Merge branch 'main' into gib-bounty-sol756670
@@ -1,3 +1,5 @@
|
||||
OPENAI_API_KEY=
|
||||
RPC_URL=
|
||||
SOLANA_PRIVATE_KEY=
|
||||
JUPITER_REFERRAL_ACCOUNT=
|
||||
JUPITER_FEE_BPS=
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["test/**/*", "src/utils/keypair.ts"],
|
||||
"files": ["test/**/*", "src/utils/keypair.ts", "examples/**/*"],
|
||||
"rules": {
|
||||
"no-console": "off"
|
||||
}
|
||||
|
||||
39
README.md
@@ -2,8 +2,6 @@
|
||||
|
||||
# Solana Agent Kit
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -25,6 +23,10 @@ An open-source toolkit for connecting AI agents to Solana protocols. Now, any ag
|
||||
|
||||
Anyone - whether an SF-based AI researcher or a crypto-native builder - can bring their AI agents trained with any model and seamlessly integrate with Solana.
|
||||
|
||||
|
||||
[](https://replit.com/@sendaifun/Solana-Agent-Kit)
|
||||
> Replit template created by [Arpit Singh](https://github.com/The-x-35)
|
||||
|
||||
## 🔧 Core Blockchain Features
|
||||
|
||||
- **Token Operations**
|
||||
@@ -45,7 +47,8 @@ Anyone - whether an SF-based AI researcher or a crypto-native builder - can brin
|
||||
- Launch on Pump via PumpPortal
|
||||
- Raydium pool creation (CPMM, CLMM, AMMv4)
|
||||
- Orca Whirlpool integration
|
||||
- Meteora Dynamic AMM, DLMM Pool, and Alpga Vault
|
||||
- Manifest market creation, and limit orders
|
||||
- Meteora Dynamic AMM, DLMM Pool, and Alpha Vault
|
||||
- Openbook market creation
|
||||
- Register and Resolve SNS
|
||||
- Jito Bundles
|
||||
@@ -53,7 +56,7 @@ Anyone - whether an SF-based AI researcher or a crypto-native builder - can brin
|
||||
- Register/resolve Alldomains
|
||||
|
||||
- **Solana Blinks**
|
||||
- Lending by Lulon (Best APR for USDC)
|
||||
- Lending by Lulo (Best APR for USDC)
|
||||
- Send Arcade Games
|
||||
- JupSOL staking
|
||||
|
||||
@@ -202,6 +205,23 @@ const price = await agent.pythFetchPrice(
|
||||
console.log("Price in BTC/USD:", price);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### LangGraph Multi-Agent System
|
||||
|
||||
The repository includes an advanced example of building a multi-agent system using LangGraph and Solana Agent Kit. Located in `examples/agent-kit-langgraph`, this example demonstrates:
|
||||
|
||||
- Multi-agent architecture using LangGraph's StateGraph
|
||||
- Specialized agents for different tasks:
|
||||
- General purpose agent for basic queries
|
||||
- Transfer/Swap agent for transaction operations
|
||||
- Read agent for blockchain data queries
|
||||
- Manager agent for routing and orchestration
|
||||
- Fully typed TypeScript implementation
|
||||
- Environment-based configuration
|
||||
|
||||
Check out the [LangGraph example](examples/agent-kit-langgraph) for a complete implementation of an advanced Solana agent system.
|
||||
|
||||
## Dependencies
|
||||
|
||||
The toolkit relies on several key Solana and Metaplex libraries:
|
||||
@@ -220,6 +240,17 @@ The toolkit relies on several key Solana and Metaplex libraries:
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines on how to contribute to this project.
|
||||
|
||||
## Contributors
|
||||
|
||||
<a href="https://github.com/sendaifun/solana-agent-kit/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=sendaifun/solana-agent-kit" />
|
||||
</a>
|
||||
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#sendaifun/solana-agent-kit&Date)
|
||||
|
||||
## License
|
||||
|
||||
Apache-2 License
|
||||
|
||||
63
SECURITY.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We take the security of our software seriously. If you believe you have found a security vulnerability, please report it to us following these guidelines:
|
||||
|
||||
### Reporting Process
|
||||
|
||||
1. **DO NOT** create a public GitHub issue for the vulnerability
|
||||
2. Email your findings to:
|
||||
- Primary: aryan@sendai.fun
|
||||
- Secondary: dev@sendai.fun
|
||||
|
||||
### What to Include
|
||||
|
||||
Please include the following information in your report:
|
||||
|
||||
- A clear description of the vulnerability
|
||||
- Steps to reproduce the issue
|
||||
- Affected versions
|
||||
- Any potential impacts
|
||||
- Optional: Suggested fixes or mitigations
|
||||
|
||||
### Response Timeline
|
||||
|
||||
- We will acknowledge receipt of your vulnerability report within 48 hours
|
||||
- We aim to send a more detailed response within 5 business days
|
||||
- We will keep you informed of our progress throughout the process
|
||||
|
||||
### Security Updates
|
||||
|
||||
Security updates will be released as soon as possible after we have confirmed and fixed the vulnerability. Updates will be published through:
|
||||
|
||||
- GitHub releases
|
||||
- Security advisories
|
||||
- Email notifications to affected parties (if applicable)
|
||||
|
||||
## Supported Versions
|
||||
|
||||
As an open-source project under the Apache 2.0 license, we focus our security updates on the latest stable release. While you're free to use any version as per the Apache 2.0 license terms, we strongly recommend using the most recent version for the best security posture.
|
||||
|
||||
| Version | Security Updates |
|
||||
| ------- | --------------- |
|
||||
| Latest Release | ✅ Active |
|
||||
| Previous Releases | ⚠️ Use at your own risk |
|
||||
|
||||
Note: The Apache 2.0 license comes with NO WARRANTIES or CONDITIONS of any kind, either express or implied. Users are responsible for their own security assessment when using any version of this software.
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
When using this software, please follow these security best practices:
|
||||
- Keep your private keys secure and never share them
|
||||
- Regularly update to the latest version
|
||||
- Review transaction details before signing
|
||||
- Use appropriate access controls in production environments
|
||||
|
||||
## Bug Bounty Program
|
||||
|
||||
Currently, we do not offer a bug bounty program. However, we greatly appreciate responsible disclosure of security vulnerabilities.
|
||||
|
||||
## License
|
||||
|
||||
This security policy is part of our project licensed under [Apache 2.0](LICENSE).
|
||||
1
docs/assets/hierarchy.js
Normal file
@@ -0,0 +1 @@
|
||||
window.hierarchyData = "eJyrVirKzy8pVrKKjtVRKkpNy0lNLsnMzytWsqqurQUAmx4Kpg=="
|
||||
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 12 KiB |
@@ -1 +1 @@
|
||||
window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE42TwU7DMAyG3yXniooJJtTbRNUDDJigN8QhCi6JltpR40hMaO+OtiFYWebtkos/f7asP69fiuGTVaVeyGvUsw9AvnesChU0W1Up43WMEMtx/cJy71Whlg7fVXU5uVkXv6Zb8h4MO8IagqdVD7jnc8gwdNpALHPgWDy5nmbFT2HzxhPWH0pUDqCZhrxoV5PaG2BjF4Mz8AwxEEbImg4xSXqXgmMYWloC1pp1VvkfkoTz5GlmDCXkGlg7H8Vtj+PSkAeH/Hf6x6YVZxylpRGL1Icu4VwnNFbUZ8lT6ibh9pxSuDKcqF2xPTMjeVSSm00+YfcvWyK/t3GXcHvYWB5AY+P0av32DUTvPWMEBAAA"
|
||||
window.navigationData = "eJyNlMFKw0AQht9lz8Fg0SK5FUMFrVpqbuJhXSfNks3ssjuLFum7S1PRxm6mXnKZb74/DD/7/CkIPkgU4skaiXK2BqQ7TSITTlIjCqGMDAFCPpyfNdQZkYlW45sozidX2+zHdG2NAUXaYgnO2E0HeODTSOBrqSDkKXAonlxOk+JHt/uGE9Zvildirdcjnt2IXfYgyfr09n7Grc+BVLP0WsEKgrMYIGk6xjjpjX59t77t46GSoV3BuHoM5gJuo9MEvrItYClJJsV/IU64iMbOlLIRqQSS2gT2HOM4F3KvkX6L8TCv2IxRmotYxs7VERcyompYfZI8pZ5H7M/JVT/BsdoNNf8sYRrl5Kov1f7VqKw1B39cR+wPG/IjaGicXmxfvgDluqEc"
|
||||
@@ -1,6 +1,9 @@
|
||||
<!DOCTYPE html><html class="default" lang="en"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>solana-agent-kit</title><meta name="description" content="Documentation for solana-agent-kit"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script defer src="assets/main.js"></script><script async src="assets/icons.js" id="tsd-icons-script"></script><script async src="assets/search.js" id="tsd-search-script"></script><script async src="assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><div class="table-cell" id="tsd-search" data-base="."><div class="field"><label for="tsd-search-field" class="tsd-widget tsd-toolbar-icon search no-caption"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><use href="assets/icons.svg#icon-search"></use></svg></label><input type="text" id="tsd-search-field" aria-label="Search"/></div><div class="field"><div id="tsd-toolbar-links"></div></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="index.html" class="title">solana-agent-kit</a></div><div class="table-cell" id="tsd-widgets"><a href="#" class="tsd-widget tsd-toolbar-icon menu no-caption" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><use href="assets/icons.svg#icon-menu"></use></svg></a></div></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><h1>solana-agent-kit</h1></div><div class="tsd-panel tsd-typography"><div align="center">
|
||||
<a id="md:solana-agent-kit" class="tsd-anchor"></a><h1 class="tsd-anchor-link">Solana Agent Kit<a href="#md:solana-agent-kit" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h1></div>
|
||||
<p><img src="https://github.com/user-attachments/assets/cfa380f6-79d9-474d-9852-3e1976c6de70" alt="Solana Agent Kit Cover 1 (3)"></p>
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="."><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>solana-agent-kit</title><meta name="description" content="Documentation for solana-agent-kit"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script defer src="assets/main.js"></script><script async src="assets/icons.js" id="tsd-icons-script"></script><script async src="assets/search.js" id="tsd-search-script"></script><script async src="assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><div class="table-cell" id="tsd-search"><div class="field"><label for="tsd-search-field" class="tsd-widget tsd-toolbar-icon search no-caption"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><use href="assets/icons.svg#icon-search"></use></svg></label><input type="text" id="tsd-search-field" aria-label="Search"/></div><div class="field"><div id="tsd-toolbar-links"></div></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="index.html" class="title">solana-agent-kit</a></div><div class="table-cell" id="tsd-widgets"><a href="#" class="tsd-widget tsd-toolbar-icon menu no-caption" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><use href="assets/icons.svg#icon-menu"></use></svg></a></div></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><h1>solana-agent-kit</h1></div><div class="tsd-panel tsd-typography"><div align="center">
|
||||
<a id="solana-agent-kit" class="tsd-anchor"></a><h1 class="tsd-anchor-link">Solana Agent Kit<a href="#solana-agent-kit" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h1><p><img src="https://github.com/user-attachments/assets/cfa380f6-79d9-474d-9852-3e1976c6de70" alt="Solana Agent Kit Cover 1 (3)"></p>
|
||||
<p><img src="https://img.shields.io/npm/dm/solana-agent-kit?style=for-the-badge" alt="NPM Downloads">
|
||||
<img src="https://img.shields.io/github/forks/sendaifun/solana-agent-kit?style=for-the-badge" alt="GitHub forks">
|
||||
<img src="https://img.shields.io/github/license/sendaifun/solana-agent-kit?style=for-the-badge" alt="GitHub License"></p>
|
||||
</div>
|
||||
<p>An open-source toolkit for connecting AI agents to Solana protocols. Now, any agent, using any model can autonomously perform 15+ Solana actions:</p>
|
||||
<ul>
|
||||
<li>Trade tokens</li>
|
||||
@@ -12,7 +15,11 @@
|
||||
<li>And more...</li>
|
||||
</ul>
|
||||
<p>Anyone - whether an SF-based AI researcher or a crypto-native builder - can bring their AI agents trained with any model and seamlessly integrate with Solana.</p>
|
||||
<a id="md:🔧-core-blockchain-features" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🔧 Core Blockchain Features<a href="#md:🔧-core-blockchain-features" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
|
||||
<p><a href="https://replit.com/@sendaifun/Solana-Agent-Kit"><img src="https://replit.com/badge/github/sendaifun/solana-agent-kit" alt="Run on Repl.it"></a></p>
|
||||
<blockquote>
|
||||
<p>Replit template created by <a href="https://github.com/The-x-35">Arpit Singh</a></p>
|
||||
</blockquote>
|
||||
<a id="🔧-core-blockchain-features" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🔧 Core Blockchain Features<a href="#🔧-core-blockchain-features" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
|
||||
<li>
|
||||
<p><strong>Token Operations</strong></p>
|
||||
<ul>
|
||||
@@ -38,23 +45,32 @@
|
||||
<li>Jupiter Exchange swaps</li>
|
||||
<li>Launch on Pump via PumpPortal</li>
|
||||
<li>Raydium pool creation (CPMM, CLMM, AMMv4)</li>
|
||||
<li>Orca whirlpool integration</li>
|
||||
<li>Meteora Dynamic AMM, DLMM Pool, and Alpga Vault</li>
|
||||
<li>Orca Whirlpool integration</li>
|
||||
<li>Manifest market creation, and limit orders</li>
|
||||
<li>Meteora Dynamic AMM, DLMM Pool, and Alpha Vault</li>
|
||||
<li>Openbook market creation</li>
|
||||
<li>Register and Resolve SNS</li>
|
||||
<li>Jito Bundles</li>
|
||||
<li>Pyth Price feeds for fetching Asset Prices</li>
|
||||
<li>Register/resolve Alldomains</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Solana Blinks</strong></p>
|
||||
<ul>
|
||||
<li>Lending by Lulo</li>
|
||||
<li>Lending by Lulo (Best APR for USDC)</li>
|
||||
<li>Send Arcade Games</li>
|
||||
<li>JupSOL staking</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Non-Financial Actions</strong></p>
|
||||
<ul>
|
||||
<li>Gib Work for registering bounties</li>
|
||||
</ul>
|
||||
<a id="md:🤖-ai-integration-features" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🤖 AI Integration Features<a href="#md:🤖-ai-integration-features" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
|
||||
</li>
|
||||
</ul>
|
||||
<a id="🤖-ai-integration-features" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🤖 AI Integration Features<a href="#🤖-ai-integration-features" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
|
||||
<li>
|
||||
<p><strong>LangChain Integration</strong></p>
|
||||
<ul>
|
||||
@@ -83,34 +99,49 @@
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<a id="md:📦-installation" class="tsd-anchor"></a><h2 class="tsd-anchor-link">📦 Installation<a href="#md:📦-installation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="bash"><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">install</span><span class="hl-1"> </span><span class="hl-2">solana-agent-kit</span>
|
||||
<a id="📦-installation" class="tsd-anchor"></a><h2 class="tsd-anchor-link">📦 Installation<a href="#📦-installation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="bash"><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">install</span><span class="hl-1"> </span><span class="hl-2">solana-agent-kit</span>
|
||||
</code><button type="button">Copy</button></pre>
|
||||
|
||||
<a id="md:quick-start" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Quick Start<a href="#md:quick-start" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="typescript"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">SolanaAgentKit</span><span class="hl-1">, </span><span class="hl-4">createSolanaTools</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">"solana-agent-kit"</span><span class="hl-1">;</span><br/><br/><span class="hl-5">// Initialize with private key and optional RPC URL</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">agent</span><span class="hl-1"> = </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">SolanaAgentKit</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">"your-wallet-private-key-as-base58"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">"https://api.mainnet-beta.solana.com"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">"your-openai-api-key"</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-5">// Create LangChain tools</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">tools</span><span class="hl-1"> = </span><span class="hl-0">createSolanaTools</span><span class="hl-1">(</span><span class="hl-4">agent</span><span class="hl-1">);</span>
|
||||
<a id="quick-start" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Quick Start<a href="#quick-start" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="typescript"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">SolanaAgentKit</span><span class="hl-1">, </span><span class="hl-4">createSolanaTools</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">"solana-agent-kit"</span><span class="hl-1">;</span><br/><br/><span class="hl-5">// Initialize with private key and optional RPC URL</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">agent</span><span class="hl-1"> = </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">SolanaAgentKit</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">"your-wallet-private-key-as-base58"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">"https://api.mainnet-beta.solana.com"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">"your-openai-api-key"</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-5">// Create LangChain tools</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">tools</span><span class="hl-1"> = </span><span class="hl-0">createSolanaTools</span><span class="hl-1">(</span><span class="hl-4">agent</span><span class="hl-1">);</span>
|
||||
</code><button type="button">Copy</button></pre>
|
||||
|
||||
<a id="md:usage-examples" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Usage Examples<a href="#md:usage-examples" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><a id="md:deploy-a-new-token" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Deploy a New Token<a href="#md:deploy-a-new-token" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">result</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">deployToken</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">"my ai token"</span><span class="hl-1">, </span><span class="hl-5">// name</span><br/><span class="hl-1"> </span><span class="hl-2">"uri"</span><span class="hl-1">, </span><span class="hl-5">// uri</span><br/><span class="hl-1"> </span><span class="hl-2">"token"</span><span class="hl-1">, </span><span class="hl-5">// symbol</span><br/><span class="hl-1"> </span><span class="hl-8">9</span><span class="hl-1">, </span><span class="hl-5">// decimals</span><br/><span class="hl-1"> </span><span class="hl-8">1000000</span><span class="hl-1"> </span><span class="hl-5">// initial supply</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-4">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><span class="hl-2">"Token Mint Address:"</span><span class="hl-1">, </span><span class="hl-4">result</span><span class="hl-1">.</span><span class="hl-4">mint</span><span class="hl-1">.</span><span class="hl-0">toString</span><span class="hl-1">());</span>
|
||||
<a id="usage-examples" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Usage Examples<a href="#usage-examples" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><a id="deploy-a-new-token" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Deploy a New Token<a href="#deploy-a-new-token" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">result</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">deployToken</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">"my ai token"</span><span class="hl-1">, </span><span class="hl-5">// name</span><br/><span class="hl-1"> </span><span class="hl-2">"uri"</span><span class="hl-1">, </span><span class="hl-5">// uri</span><br/><span class="hl-1"> </span><span class="hl-2">"token"</span><span class="hl-1">, </span><span class="hl-5">// symbol</span><br/><span class="hl-1"> </span><span class="hl-8">9</span><span class="hl-1">, </span><span class="hl-5">// decimals</span><br/><span class="hl-1"> </span><span class="hl-8">1000000</span><span class="hl-1"> </span><span class="hl-5">// initial supply</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-4">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><span class="hl-2">"Token Mint Address:"</span><span class="hl-1">, </span><span class="hl-4">result</span><span class="hl-1">.</span><span class="hl-4">mint</span><span class="hl-1">.</span><span class="hl-0">toString</span><span class="hl-1">());</span>
|
||||
</code><button type="button">Copy</button></pre>
|
||||
|
||||
<a id="md:create-nft-collection" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Create NFT Collection<a href="#md:create-nft-collection" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">collection</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">deployCollection</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-4">name:</span><span class="hl-1"> </span><span class="hl-2">"My NFT Collection"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">uri:</span><span class="hl-1"> </span><span class="hl-2">"https://arweave.net/metadata.json"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">royaltyBasisPoints:</span><span class="hl-1"> </span><span class="hl-8">500</span><span class="hl-1">, </span><span class="hl-5">// 5%</span><br/><span class="hl-1"> </span><span class="hl-4">creators:</span><span class="hl-1"> [</span><br/><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">address:</span><span class="hl-1"> </span><span class="hl-2">"creator-wallet-address"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">percentage:</span><span class="hl-1"> </span><span class="hl-8">100</span><span class="hl-1">,</span><br/><span class="hl-1"> },</span><br/><span class="hl-1"> ],</span><br/><span class="hl-1">});</span>
|
||||
<a id="create-nft-collection" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Create NFT Collection<a href="#create-nft-collection" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">collection</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">deployCollection</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-4">name:</span><span class="hl-1"> </span><span class="hl-2">"My NFT Collection"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">uri:</span><span class="hl-1"> </span><span class="hl-2">"https://arweave.net/metadata.json"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">royaltyBasisPoints:</span><span class="hl-1"> </span><span class="hl-8">500</span><span class="hl-1">, </span><span class="hl-5">// 5%</span><br/><span class="hl-1"> </span><span class="hl-4">creators:</span><span class="hl-1"> [</span><br/><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">address:</span><span class="hl-1"> </span><span class="hl-2">"creator-wallet-address"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">percentage:</span><span class="hl-1"> </span><span class="hl-8">100</span><span class="hl-1">,</span><br/><span class="hl-1"> },</span><br/><span class="hl-1"> ],</span><br/><span class="hl-1">});</span>
|
||||
</code><button type="button">Copy</button></pre>
|
||||
|
||||
<a id="md:swap-tokens" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Swap Tokens<a href="#md:swap-tokens" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">PublicKey</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">"@solana/web3.js"</span><span class="hl-1">;</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">signature</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">trade</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">PublicKey</span><span class="hl-1">(</span><span class="hl-2">"target-token-mint"</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-8">100</span><span class="hl-1">, </span><span class="hl-5">// amount</span><br/><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">PublicKey</span><span class="hl-1">(</span><span class="hl-2">"source-token-mint"</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-8">300</span><span class="hl-1"> </span><span class="hl-5">// 3% slippage</span><br/><span class="hl-1">);</span>
|
||||
<a id="swap-tokens" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Swap Tokens<a href="#swap-tokens" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">PublicKey</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">"@solana/web3.js"</span><span class="hl-1">;</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">signature</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">trade</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">PublicKey</span><span class="hl-1">(</span><span class="hl-2">"target-token-mint"</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-8">100</span><span class="hl-1">, </span><span class="hl-5">// amount</span><br/><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">PublicKey</span><span class="hl-1">(</span><span class="hl-2">"source-token-mint"</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-8">300</span><span class="hl-1"> </span><span class="hl-5">// 3% slippage</span><br/><span class="hl-1">);</span>
|
||||
</code><button type="button">Copy</button></pre>
|
||||
|
||||
<a id="md:lend-tokens" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Lend Tokens<a href="#md:lend-tokens" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">PublicKey</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">"@solana/web3.js"</span><span class="hl-1">;</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">signature</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">lendAssets</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-8">100</span><span class="hl-1"> </span><span class="hl-5">// amount of USDC to lend</span><br/><span class="hl-1">);</span>
|
||||
<a id="lend-tokens" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Lend Tokens<a href="#lend-tokens" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">PublicKey</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">"@solana/web3.js"</span><span class="hl-1">;</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">signature</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">lendAssets</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-8">100</span><span class="hl-1"> </span><span class="hl-5">// amount of USDC to lend</span><br/><span class="hl-1">);</span>
|
||||
</code><button type="button">Copy</button></pre>
|
||||
|
||||
<a id="md:stake-sol" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Stake SOL<a href="#md:stake-sol" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">signature</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">stake</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-8">1</span><span class="hl-1"> </span><span class="hl-5">// amount in SOL to stake</span><br/><span class="hl-1">);</span>
|
||||
<a id="stake-sol" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Stake SOL<a href="#stake-sol" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">signature</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">stake</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-8">1</span><span class="hl-1"> </span><span class="hl-5">// amount in SOL to stake</span><br/><span class="hl-1">);</span>
|
||||
</code><button type="button">Copy</button></pre>
|
||||
|
||||
<a id="md:send-an-spl-token-airdrop-via-zk-compression" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Send an SPL Token Airdrop via ZK Compression<a href="#md:send-an-spl-token-airdrop-via-zk-compression" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">PublicKey</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">"@solana/web3.js"</span><span class="hl-1">;</span><br/><br/><span class="hl-1">(</span><span class="hl-6">async</span><span class="hl-1"> () </span><span class="hl-6">=></span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">"~Airdrop cost estimate:"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-0">getAirdropCostEstimate</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-8">1000</span><span class="hl-1">, </span><span class="hl-5">// recipients</span><br/><span class="hl-1"> </span><span class="hl-8">30_000</span><span class="hl-1"> </span><span class="hl-5">// priority fee in lamports</span><br/><span class="hl-1"> )</span><br/><span class="hl-1"> );</span><br/><br/><span class="hl-1"> </span><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">signature</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">sendCompressedAirdrop</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">PublicKey</span><span class="hl-1">(</span><span class="hl-2">"JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"</span><span class="hl-1">), </span><span class="hl-5">// mint</span><br/><span class="hl-1"> </span><span class="hl-8">42</span><span class="hl-1">, </span><span class="hl-5">// amount per recipient</span><br/><span class="hl-1"> [</span><br/><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">PublicKey</span><span class="hl-1">(</span><span class="hl-2">"1nc1nerator11111111111111111111111111111111"</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-5">// ... add more recipients</span><br/><span class="hl-1"> ],</span><br/><span class="hl-1"> </span><span class="hl-8">30_000</span><span class="hl-1"> </span><span class="hl-5">// priority fee in lamports</span><br/><span class="hl-1"> );</span><br/><span class="hl-1">})();</span>
|
||||
<a id="send-an-spl-token-airdrop-via-zk-compression" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Send an SPL Token Airdrop via ZK Compression<a href="#send-an-spl-token-airdrop-via-zk-compression" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">PublicKey</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">"@solana/web3.js"</span><span class="hl-1">;</span><br/><br/><span class="hl-1">(</span><span class="hl-6">async</span><span class="hl-1"> () </span><span class="hl-6">=></span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">"~Airdrop cost estimate:"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-0">getAirdropCostEstimate</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-8">1000</span><span class="hl-1">, </span><span class="hl-5">// recipients</span><br/><span class="hl-1"> </span><span class="hl-8">30_000</span><span class="hl-1"> </span><span class="hl-5">// priority fee in lamports</span><br/><span class="hl-1"> )</span><br/><span class="hl-1"> );</span><br/><br/><span class="hl-1"> </span><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">signature</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">sendCompressedAirdrop</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">PublicKey</span><span class="hl-1">(</span><span class="hl-2">"JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"</span><span class="hl-1">), </span><span class="hl-5">// mint</span><br/><span class="hl-1"> </span><span class="hl-8">42</span><span class="hl-1">, </span><span class="hl-5">// amount per recipient</span><br/><span class="hl-1"> [</span><br/><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">PublicKey</span><span class="hl-1">(</span><span class="hl-2">"1nc1nerator11111111111111111111111111111111"</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-5">// ... add more recipients</span><br/><span class="hl-1"> ],</span><br/><span class="hl-1"> </span><span class="hl-8">30_000</span><span class="hl-1"> </span><span class="hl-5">// priority fee in lamports</span><br/><span class="hl-1"> );</span><br/><span class="hl-1">})();</span>
|
||||
</code><button type="button">Copy</button></pre>
|
||||
|
||||
<a id="md:fetch-price-data-from-pyth" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Fetch Price Data from Pyth<a href="#md:fetch-price-data-from-pyth" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">price</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">pythFetchPrice</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">"0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-4">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><span class="hl-2">"Price in BTC/USD:"</span><span class="hl-1">, </span><span class="hl-4">price</span><span class="hl-1">);</span>
|
||||
<a id="fetch-price-data-from-pyth" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Fetch Price Data from Pyth<a href="#fetch-price-data-from-pyth" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">price</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">pythFetchPrice</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">"0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-4">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><span class="hl-2">"Price in BTC/USD:"</span><span class="hl-1">, </span><span class="hl-4">price</span><span class="hl-1">);</span>
|
||||
</code><button type="button">Copy</button></pre>
|
||||
|
||||
<a id="md:dependencies" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Dependencies<a href="#md:dependencies" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>The toolkit relies on several key Solana and Metaplex libraries:</p>
|
||||
<a id="examples" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Examples<a href="#examples" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><a id="langgraph-multi-agent-system" class="tsd-anchor"></a><h3 class="tsd-anchor-link">LangGraph Multi-Agent System<a href="#langgraph-multi-agent-system" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><p>The repository includes an advanced example of building a multi-agent system using LangGraph and Solana Agent Kit. Located in <code>examples/agent-kit-langgraph</code>, this example demonstrates:</p>
|
||||
<ul>
|
||||
<li>Multi-agent architecture using LangGraph's StateGraph</li>
|
||||
<li>Specialized agents for different tasks:
|
||||
<ul>
|
||||
<li>General purpose agent for basic queries</li>
|
||||
<li>Transfer/Swap agent for transaction operations</li>
|
||||
<li>Read agent for blockchain data queries</li>
|
||||
<li>Manager agent for routing and orchestration</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Fully typed TypeScript implementation</li>
|
||||
<li>Environment-based configuration</li>
|
||||
</ul>
|
||||
<p>Check out the <a href="examples/agent-kit-langgraph">LangGraph example</a> for a complete implementation of an advanced Solana agent system.</p>
|
||||
<a id="dependencies" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Dependencies<a href="#dependencies" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>The toolkit relies on several key Solana and Metaplex libraries:</p>
|
||||
<ul>
|
||||
<li>@solana/web3.js</li>
|
||||
<li>@solana/spl-token</li>
|
||||
@@ -121,8 +152,12 @@
|
||||
<li>@lightprotocol/stateless.js</li>
|
||||
<li>@pythnetwork/price-service-client</li>
|
||||
</ul>
|
||||
<a id="md:contributing" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Contributing<a href="#md:contributing" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
<a id="contributing" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Contributing<a href="#contributing" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
Refer to <a href="media/CONTRIBUTING.md">CONTRIBUTING.md</a> for detailed guidelines on how to contribute to this project.</p>
|
||||
<a id="md:license" class="tsd-anchor"></a><h2 class="tsd-anchor-link">License<a href="#md:license" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>MIT License</p>
|
||||
<a id="md:security" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Security<a href="#md:security" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>This toolkit handles private keys and transactions. Always ensure you're using it in a secure environment and never share your private keys.</p>
|
||||
</div></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none"><use href="assets/icons.svg#icon-chevronDown"></use></svg>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-external" name="external"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>External</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none"><use href="assets/icons.svg#icon-chevronDown"></use></svg>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#md:solana-agent-kit"><span>Solana <wbr/>Agent <wbr/>Kit</span></a><ul><li><a href="#md:🔧-core-blockchain-features"><span>🔧 <wbr/>Core <wbr/>Blockchain <wbr/>Features</span></a></li><li><a href="#md:🤖-ai-integration-features"><span>🤖 AI <wbr/>Integration <wbr/>Features</span></a></li><li><a href="#md:📦-installation"><span>📦 <wbr/>Installation</span></a></li><li><a href="#md:quick-start"><span>Quick <wbr/>Start</span></a></li><li><a href="#md:usage-examples"><span>Usage <wbr/>Examples</span></a></li><li><ul><li><a href="#md:deploy-a-new-token"><span>Deploy a <wbr/>New <wbr/>Token</span></a></li><li><a href="#md:create-nft-collection"><span>Create NFT <wbr/>Collection</span></a></li><li><a href="#md:swap-tokens"><span>Swap <wbr/>Tokens</span></a></li><li><a href="#md:lend-tokens"><span>Lend <wbr/>Tokens</span></a></li><li><a href="#md:stake-sol"><span>Stake SOL</span></a></li><li><a href="#md:send-an-spl-token-airdrop-via-zk-compression"><span>Send an SPL <wbr/>Token <wbr/>Airdrop via ZK <wbr/>Compression</span></a></li><li><a href="#md:fetch-price-data-from-pyth"><span>Fetch <wbr/>Price <wbr/>Data from <wbr/>Pyth</span></a></li></ul></li><li><a href="#md:dependencies"><span>Dependencies</span></a></li><li><a href="#md:contributing"><span>Contributing</span></a></li><li><a href="#md:license"><span>License</span></a></li><li><a href="#md:security"><span>Security</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html" class="current"><svg class="tsd-kind-icon" viewBox="0 0 24 24"><use href="assets/icons.svg#icon-1"></use></svg><span>solana-agent-kit</span></a><ul class="tsd-small-nested-navigation" id="tsd-nav-container" data-base="."><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html>
|
||||
<a id="contributors" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Contributors<a href="#contributors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><a href="https://github.com/sendaifun/solana-agent-kit/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=sendaifun/solana-agent-kit" />
|
||||
</a>
|
||||
<a id="star-history" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Star History<a href="#star-history" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p><a href="https://star-history.com/#sendaifun/solana-agent-kit&Date"><img src="https://api.star-history.com/svg?repos=sendaifun/solana-agent-kit&type=Date" alt="Star History Chart"></a></p>
|
||||
<a id="license" class="tsd-anchor"></a><h2 class="tsd-anchor-link">License<a href="#license" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Apache-2 License</p>
|
||||
<a id="security" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Security<a href="#security" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>This toolkit handles private keys and transactions. Always ensure you're using it in a secure environment and never share your private keys.</p>
|
||||
</div></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none"><use href="assets/icons.svg#icon-chevronDown"></use></svg>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-external" name="external"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>External</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none"><use href="assets/icons.svg#icon-chevronDown"></use></svg>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#solana-agent-kit"><span>Solana <wbr/>Agent <wbr/>Kit</span></a><ul><li><a href="#🔧-core-blockchain-features"><span>🔧 <wbr/>Core <wbr/>Blockchain <wbr/>Features</span></a></li><li><a href="#🤖-ai-integration-features"><span>🤖 AI <wbr/>Integration <wbr/>Features</span></a></li><li><a href="#📦-installation"><span>📦 <wbr/>Installation</span></a></li><li><a href="#quick-start"><span>Quick <wbr/>Start</span></a></li><li><a href="#usage-examples"><span>Usage <wbr/>Examples</span></a></li><li><ul><li><a href="#deploy-a-new-token"><span>Deploy a <wbr/>New <wbr/>Token</span></a></li><li><a href="#create-nft-collection"><span>Create NFT <wbr/>Collection</span></a></li><li><a href="#swap-tokens"><span>Swap <wbr/>Tokens</span></a></li><li><a href="#lend-tokens"><span>Lend <wbr/>Tokens</span></a></li><li><a href="#stake-sol"><span>Stake SOL</span></a></li><li><a href="#send-an-spl-token-airdrop-via-zk-compression"><span>Send an SPL <wbr/>Token <wbr/>Airdrop via ZK <wbr/>Compression</span></a></li><li><a href="#fetch-price-data-from-pyth"><span>Fetch <wbr/>Price <wbr/>Data from <wbr/>Pyth</span></a></li></ul></li><li><a href="#examples"><span>Examples</span></a></li><li><ul><li><a href="#langgraph-multi-agent-system"><span>Lang<wbr/>Graph <wbr/>Multi-<wbr/>Agent <wbr/>System</span></a></li></ul></li><li><a href="#dependencies"><span>Dependencies</span></a></li><li><a href="#contributing"><span>Contributing</span></a></li><li><a href="#contributors"><span>Contributors</span></a></li><li><a href="#star-history"><span>Star <wbr/>History</span></a></li><li><a href="#license"><span>License</span></a></li><li><a href="#security"><span>Security</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html">solana-agent-kit</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html>
|
||||
|
||||
4
docs/interfaces/Config.html
Normal file
4
docs/interfaces/GibworkCreateTaskReponse.html
Normal file
4
examples/agent-kit-langgraph/.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
OPENAI_API_KEY=
|
||||
RPC_URL=
|
||||
SOLANA_PRIVATE_KEY=
|
||||
TAVILY_API_KEY= #Optional: for search functionality
|
||||
11
examples/agent-kit-langgraph/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
package-lock.json
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
build/
|
||||
87
examples/agent-kit-langgraph/README.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Agent Kit LangGraph Example
|
||||
|
||||
This example demonstrates how to build an advanced Solana agent using LangGraph and the Solana Agent Kit. It showcases a multi-agent system that can handle various Solana-related tasks through a directed workflow.
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- Multi-agent architecture using LangGraph's StateGraph
|
||||
- Specialized agents for different tasks:
|
||||
- General purpose agent for basic queries (with optional Tavily search integration)
|
||||
- Transfer/Swap agent for transaction operations
|
||||
- Read agent for blockchain data queries
|
||||
- Manager agent for routing and orchestration
|
||||
- Environment-based configuration
|
||||
- TypeScript implementation with full type safety
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js (v16 or higher)
|
||||
- pnpm package manager
|
||||
- Solana development environment
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone the repository and navigate to the example directory:
|
||||
```bash
|
||||
cd examples/agent-kit-langgraph
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. Configure environment variables:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Edit the `.env` file with your configuration:
|
||||
- Add your OpenAI API key
|
||||
- Add your Tavily API key (optional, enables web search capabilities)
|
||||
- Configure any other required environment variables
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── agents/ # Individual agent implementations
|
||||
├── helper/ # Helper utilities and examples
|
||||
├── prompts/ # Agent prompts and templates
|
||||
├── tools/ # Custom tools for agents
|
||||
└── utils/ # Utility functions and configurations
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
To run the example:
|
||||
|
||||
```bash
|
||||
pnpm dev src/index.ts
|
||||
```
|
||||
|
||||
The example demonstrates a workflow where:
|
||||
1. The manager agent receives the initial query
|
||||
2. Based on the query type, it routes to the appropriate specialized agent:
|
||||
- General queries → Generalist Agent
|
||||
- Transfer/Swap operations → TransferSwap Agent
|
||||
- Blockchain data queries → Read Agent
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `@langchain/community`: LangChain community tools and utilities
|
||||
- Includes Tavily search integration for enhanced query responses
|
||||
- `@langchain/core`: Core LangChain functionality
|
||||
- `@langchain/langgraph`: Graph-based agent workflows
|
||||
- `solana-agent-kit`: Solana Agent Kit for blockchain interactions
|
||||
- `zod`: Runtime type checking
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
## License
|
||||
|
||||
ISC License
|
||||
BIN
examples/agent-kit-langgraph/assets/architecture.png
Normal file
|
After Width: | Height: | Size: 213 KiB |
9
examples/agent-kit-langgraph/langgraph.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"node_version": "20",
|
||||
"dockerfile_lines": [],
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"solanaAgent": "./src/index.ts:graph"
|
||||
},
|
||||
"env": ".env"
|
||||
}
|
||||
31
examples/agent-kit-langgraph/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "agent-kit-langgraph",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "tsx"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@langchain/community": "^0.3.20",
|
||||
"@langchain/core": "^0.3.26",
|
||||
"@langchain/langgraph": "^0.2.36",
|
||||
"dotenv": "^16.4.7",
|
||||
"solana-agent-kit": "^1.3.0",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"ts-node": {
|
||||
"esm": true,
|
||||
"experimentalSpecifierResolution": "node"
|
||||
}
|
||||
}
|
||||
4462
examples/agent-kit-langgraph/pnpm-lock.yaml
generated
Normal file
25
examples/agent-kit-langgraph/src/agents/generalAgent.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
||||
import { gpt4o } from "../utils/model";
|
||||
import { solanaAgentState } from "../utils/state";
|
||||
import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
|
||||
|
||||
// Initialize tools array
|
||||
const searchTools = [];
|
||||
|
||||
// Only add Tavily search if API key is available
|
||||
if (process.env.TAVILY_API_KEY) {
|
||||
searchTools.push(new TavilySearchResults());
|
||||
}
|
||||
|
||||
const generalAgent = createReactAgent({
|
||||
llm: gpt4o,
|
||||
tools: searchTools,
|
||||
});
|
||||
|
||||
export const generalistNode = async (state: typeof solanaAgentState.State) => {
|
||||
const { messages } = state;
|
||||
|
||||
const result = await generalAgent.invoke({ messages });
|
||||
|
||||
return { messages: [...result.messages] };
|
||||
};
|
||||
23
examples/agent-kit-langgraph/src/agents/manager.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { prompt, parser } from "../prompts/manager";
|
||||
import { RunnableSequence } from "@langchain/core/runnables";
|
||||
import { solanaAgentState } from "../utils/state";
|
||||
import { gpt4o } from "../utils/model";
|
||||
|
||||
const chain = RunnableSequence.from([prompt, gpt4o, parser]);
|
||||
|
||||
export const managerNode = async (state: typeof solanaAgentState.State) => {
|
||||
const { messages } = state;
|
||||
|
||||
const result = await chain.invoke({
|
||||
formatInstructions: parser.getFormatInstructions(),
|
||||
messages: messages,
|
||||
});
|
||||
|
||||
const { isSolanaReadQuery, isSolanaWriteQuery, isGeneralQuery } = result;
|
||||
|
||||
return {
|
||||
isSolanaReadQuery,
|
||||
isSolanaWriteQuery,
|
||||
isGeneralQuery,
|
||||
};
|
||||
};
|
||||
21
examples/agent-kit-langgraph/src/agents/readAgent.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
||||
import { gpt4o } from "../utils/model";
|
||||
import { solanaAgentState } from "../utils/state";
|
||||
import { agentKit } from "../utils/solanaAgent";
|
||||
import {
|
||||
SolanaBalanceTool,
|
||||
SolanaFetchPriceTool,
|
||||
} from "solana-agent-kit/dist/langchain";
|
||||
|
||||
const readAgent = createReactAgent({
|
||||
llm: gpt4o,
|
||||
tools: [new SolanaBalanceTool(agentKit), new SolanaFetchPriceTool(agentKit)],
|
||||
});
|
||||
|
||||
export const readNode = async (state: typeof solanaAgentState.State) => {
|
||||
const { messages } = state;
|
||||
|
||||
const result = await readAgent.invoke({ messages });
|
||||
|
||||
return { messages: [...result.messages] };
|
||||
};
|
||||
25
examples/agent-kit-langgraph/src/agents/transferOrSwap.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { gpt4o } from "../utils/model";
|
||||
import { agentKit } from "../utils/solanaAgent";
|
||||
import { solanaAgentState } from "../utils/state";
|
||||
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
||||
import { SolanaTransferTool } from "solana-agent-kit/dist/langchain";
|
||||
import { transferSwapPrompt } from "../prompts/transferSwap";
|
||||
import { swapTool } from "../tools/swap";
|
||||
|
||||
const transferOrSwapAgent = createReactAgent({
|
||||
stateModifier: transferSwapPrompt,
|
||||
llm: gpt4o,
|
||||
tools: [new SolanaTransferTool(agentKit), swapTool],
|
||||
});
|
||||
|
||||
export const transferSwapNode = async (
|
||||
state: typeof solanaAgentState.State,
|
||||
) => {
|
||||
const { messages } = state;
|
||||
|
||||
const result = await transferOrSwapAgent.invoke({
|
||||
messages,
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
9
examples/agent-kit-langgraph/src/helper/examples.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { HumanMessage } from "@langchain/core/messages";
|
||||
|
||||
export const generalQuestion = [
|
||||
new HumanMessage("Who is the president of Ecuador?"),
|
||||
];
|
||||
|
||||
export const solanaReadQuery = [new HumanMessage("what is the price of SOL")];
|
||||
|
||||
export const solanaWriteQuery = [new HumanMessage("swap 0.1 usdc to sol")];
|
||||
50
examples/agent-kit-langgraph/src/helper/tokenList.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
export const tokenList = [
|
||||
{
|
||||
name: "USDC",
|
||||
ticker: "USDC",
|
||||
decimal: 6,
|
||||
mintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
||||
},
|
||||
{
|
||||
name: "USDT",
|
||||
ticker: "USDT",
|
||||
decimal: 6,
|
||||
mintAddress: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
|
||||
},
|
||||
{
|
||||
name: "USDS",
|
||||
ticker: "USDS",
|
||||
decimal: 6,
|
||||
mintAddress: "USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA",
|
||||
},
|
||||
{
|
||||
name: "SOL",
|
||||
ticker: "SOL",
|
||||
decimal: 9,
|
||||
mintAddress: "So11111111111111111111111111111111111111112",
|
||||
},
|
||||
{
|
||||
name: "jitoSOL",
|
||||
ticker: "jitoSOL",
|
||||
decimal: 9,
|
||||
mintAddress: "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn",
|
||||
},
|
||||
{
|
||||
name: "bSOL",
|
||||
ticker: "bSOL",
|
||||
decimal: 9,
|
||||
mintAddress: "bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1",
|
||||
},
|
||||
{
|
||||
name: "mSOL",
|
||||
ticker: "mSOL",
|
||||
decimal: 9,
|
||||
mintAddress: "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So",
|
||||
},
|
||||
{
|
||||
name: "BONK",
|
||||
ticker: "BONK",
|
||||
decimal: 9,
|
||||
mintAddress: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
|
||||
},
|
||||
];
|
||||
28
examples/agent-kit-langgraph/src/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { StateGraph } from "@langchain/langgraph";
|
||||
import { solanaAgentState } from "./utils/state";
|
||||
import { generalistNode } from "./agents/generalAgent";
|
||||
import { transferSwapNode } from "./agents/transferOrSwap";
|
||||
import { managerNode } from "./agents/manager";
|
||||
import { readNode } from "./agents/readAgent";
|
||||
import { START, END } from "@langchain/langgraph";
|
||||
import { managerRouter } from "./utils/route";
|
||||
import { HumanMessage } from "@langchain/core/messages";
|
||||
|
||||
const workflow = new StateGraph(solanaAgentState)
|
||||
.addNode("generalist", generalistNode)
|
||||
.addNode("manager", managerNode)
|
||||
.addNode("transferSwap", transferSwapNode)
|
||||
.addNode("read", readNode)
|
||||
.addEdge(START, "manager")
|
||||
.addConditionalEdges("manager", managerRouter)
|
||||
.addEdge("generalist", END)
|
||||
.addEdge("transferSwap", END)
|
||||
.addEdge("read", END);
|
||||
|
||||
export const graph = workflow.compile();
|
||||
|
||||
const result = await graph.invoke({
|
||||
messages: [new HumanMessage("what is the price of SOL")],
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
48
examples/agent-kit-langgraph/src/prompts/manager.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { StructuredOutputParser } from "@langchain/core/output_parsers";
|
||||
import { z } from "zod";
|
||||
import { PromptTemplate } from "@langchain/core/prompts";
|
||||
|
||||
export const parser = StructuredOutputParser.fromZodSchema(
|
||||
z.object({
|
||||
isSolanaReadQuery: z
|
||||
.boolean()
|
||||
.describe("Query requires reading data from Solana blockchain"),
|
||||
isSolanaWriteQuery: z
|
||||
.boolean()
|
||||
.describe("Query requires writing/modifying data on Solana blockchain"),
|
||||
isGeneralQuery: z
|
||||
.boolean()
|
||||
.describe("Query is about non-blockchain topics"),
|
||||
}),
|
||||
);
|
||||
|
||||
export const prompt = PromptTemplate.fromTemplate(
|
||||
`
|
||||
You are the Chief Routing Officer for a multi-blockchain agent network. Your role is to:
|
||||
1. Analyze and classify incoming queries
|
||||
2. Determine if the query requires Solana read operations, write operations, or is general
|
||||
|
||||
Format your response according to:
|
||||
{formatInstructions}
|
||||
|
||||
Classification Guidelines:
|
||||
- Solana Read Operations include:
|
||||
* Checking account balances
|
||||
* Viewing NFT metadata
|
||||
* Getting program data
|
||||
* Querying transaction history
|
||||
* Checking token prices or holdings
|
||||
- Solana Write Operations include:
|
||||
* Creating or updating programs
|
||||
* Sending tokens or SOL
|
||||
* Minting NFTs
|
||||
* Creating accounts
|
||||
* Any transaction that modifies blockchain state
|
||||
- General queries include:
|
||||
* Non-blockchain topics
|
||||
* Internet searches
|
||||
* General knowledge questions
|
||||
|
||||
\n {messages} \n
|
||||
`,
|
||||
);
|
||||
43
examples/agent-kit-langgraph/src/prompts/transferSwap.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
ChatPromptTemplate,
|
||||
MessagesPlaceholder,
|
||||
} from "@langchain/core/prompts";
|
||||
import { tokenList } from "../helper/tokenList";
|
||||
|
||||
// Convert token list to a more readable format for the prompt
|
||||
const formattedTokenList = tokenList
|
||||
.map(
|
||||
(token) =>
|
||||
`- ${token.name} (${token.ticker}) - Decimals: ${token.decimal} - Address: ${token.mintAddress}`,
|
||||
)
|
||||
.join("\n ");
|
||||
|
||||
export const transferSwapPrompt = ChatPromptTemplate.fromMessages([
|
||||
[
|
||||
"system",
|
||||
`You are an agent that is an expert in Solana transactions, specialized in token transfers and swaps. You can execute these transactions using the available tools based on user input.
|
||||
|
||||
When processing token amounts:
|
||||
1. Use EXACTLY the decimal amount specified by the user without any modifications
|
||||
2. Do not round or adjust the numbers
|
||||
3. Maintain precise decimal places as provided in the user input
|
||||
|
||||
For transfers:
|
||||
- User must specify the token, amount, and recipient address
|
||||
- The same token will be used for input and output
|
||||
|
||||
For swaps:
|
||||
- User must specify the input token, output token, and amount to swap
|
||||
- Input and output tokens must be different
|
||||
- Select tokens from this list of supported tokens:
|
||||
|
||||
${formattedTokenList}
|
||||
|
||||
Example amounts:
|
||||
If you say "0.01 SOL", I will use exactly 0.01 (not 0.010 or 0.0100)
|
||||
If you say "1.234 USDC", I will use exactly 1.234 (not 1.23 or 1.2340)
|
||||
For swaps, have the slippage be 200 bps
|
||||
`,
|
||||
],
|
||||
new MessagesPlaceholder("messages"),
|
||||
]);
|
||||
45
examples/agent-kit-langgraph/src/tools/swap.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { agentKit } from "../utils/solanaAgent";
|
||||
import { tool } from "@langchain/core/tools";
|
||||
import { z } from "zod";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const swapTool = tool(
|
||||
async ({ outputMint, inputAmount, inputMint, inputDecimal }) => {
|
||||
try {
|
||||
const inputAmountWithDecimals = inputAmount * 10 ** inputDecimal;
|
||||
const outputMintAddress = new PublicKey(outputMint);
|
||||
const inputMintAddress = new PublicKey(inputMint);
|
||||
|
||||
console.log(inputAmountWithDecimals, outputMintAddress, inputMintAddress);
|
||||
const tx = await agentKit.trade(
|
||||
outputMintAddress,
|
||||
inputAmountWithDecimals,
|
||||
inputMintAddress,
|
||||
200,
|
||||
);
|
||||
return tx;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return "error";
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "swap",
|
||||
description:
|
||||
"call to swap/trade tokens from one token to the other using Jupiter exchange",
|
||||
schema: z.object({
|
||||
outputMint: z
|
||||
.string()
|
||||
.describe("The mint address of destination token to be swapped to"),
|
||||
inputAmount: z
|
||||
.number()
|
||||
.describe(
|
||||
"the input amount of the token to be swapped without adding any decimals",
|
||||
),
|
||||
inputMint: z.string().describe("The mint address of the origin token "),
|
||||
inputDecimal: z
|
||||
.number()
|
||||
.describe("The decimal of the input token that is being traded"),
|
||||
}),
|
||||
},
|
||||
);
|
||||
12
examples/agent-kit-langgraph/src/utils/model.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ChatOpenAI } from "@langchain/openai";
|
||||
import "dotenv/config";
|
||||
|
||||
export const gpt4o = new ChatOpenAI({
|
||||
modelName: "gpt-4o",
|
||||
apiKey: process.env.OPENAI_API_KEY!,
|
||||
});
|
||||
|
||||
export const gpt4oMini = new ChatOpenAI({
|
||||
modelName: "gpt-4o-mini",
|
||||
apiKey: process.env.OPENAI_API_KEY!,
|
||||
});
|
||||
16
examples/agent-kit-langgraph/src/utils/route.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { solanaAgentState } from "./state";
|
||||
import { END } from "@langchain/langgraph";
|
||||
|
||||
export const managerRouter = (state: typeof solanaAgentState.State) => {
|
||||
const { isSolanaReadQuery, isSolanaWriteQuery, isGeneralQuery } = state;
|
||||
|
||||
if (isGeneralQuery) {
|
||||
return "generalist";
|
||||
} else if (isSolanaWriteQuery) {
|
||||
return "transferSwap";
|
||||
} else if (isSolanaReadQuery) {
|
||||
return "read";
|
||||
} else {
|
||||
return END;
|
||||
}
|
||||
};
|
||||
9
examples/agent-kit-langgraph/src/utils/solanaAgent.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
|
||||
|
||||
export const agentKit = new SolanaAgentKit(
|
||||
process.env.SOLANA_PRIVATE_KEY!,
|
||||
process.env.RPC_URL!,
|
||||
process.env.OPENAI_API_KEY!,
|
||||
);
|
||||
|
||||
export const solanaTools = createSolanaTools(agentKit);
|
||||
25
examples/agent-kit-langgraph/src/utils/state.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Annotation } from "@langchain/langgraph";
|
||||
import { BaseMessage } from "@langchain/core/messages";
|
||||
import { messagesStateReducer } from "@langchain/langgraph";
|
||||
|
||||
export const solanaAgentState = Annotation.Root({
|
||||
messages: Annotation<BaseMessage[]>({
|
||||
reducer: messagesStateReducer,
|
||||
default: () => [],
|
||||
}),
|
||||
|
||||
isSolanaReadQuery: Annotation<boolean>({
|
||||
reducer: (x, y) => y ?? x ?? false,
|
||||
default: () => false,
|
||||
}),
|
||||
|
||||
isSolanaWriteQuery: Annotation<boolean>({
|
||||
reducer: (x, y) => y ?? x ?? false,
|
||||
default: () => false,
|
||||
}),
|
||||
|
||||
isGeneralQuery: Annotation<boolean>({
|
||||
reducer: (x, y) => y ?? x ?? false,
|
||||
default: () => false,
|
||||
}),
|
||||
});
|
||||
12
examples/agent-kit-langgraph/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"module": "es2022",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
4
examples/agent-kit-nextjs-langchain/.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
LANGCHAIN_CALLBACKS_BACKGROUND=false
|
||||
OPENAI_API_KEY=
|
||||
RPC_URL=
|
||||
SOLANA_PRIVATE_KEY=
|
||||
3
examples/agent-kit-nextjs-langchain/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
43
examples/agent-kit-nextjs-langchain/.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.env
|
||||
1
examples/agent-kit-nextjs-langchain/.prettierrc.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
21
examples/agent-kit-nextjs-langchain/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 LangChain
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
56
examples/agent-kit-nextjs-langchain/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# SolanaAgentKit 🦜️🔗 LangChain + Next.js Starter Template
|
||||
|
||||
[](https://codespaces.new/michaelessiet/solana-agent-nextjs-starter-langchain)
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fsendaifun%2Fsolana-agent-kit%2Ftree%2Fmain%2Fexamples%2Fagent-kit-nextjs-langchain&env=OPENAI_API_KEY,RPC_URL,SOLANA_PRIVATE_KEY&project-name=solana-agent-kit&repository-name=sak-yourprojectname)
|
||||
|
||||
This template scaffolds a SolanaAgentKit + LangChain.js + Next.js starter app.
|
||||
|
||||
The agent uses [LangGraph.js](https://langchain-ai.github.io/langgraphjs/), LangChain's framework for building agentic workflows. They use preconfigured helper functions to minimize boilerplate, but you can replace them with custom graphs as desired.
|
||||
|
||||

|
||||
|
||||
It's free-tier friendly too! Check out the [bundle size stats below](#-bundle-size).
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
First, clone this repo and download it locally.
|
||||
|
||||
Next, you'll need to set up environment variables in your repo's `.env.local` file. Copy the `.env.example` file to `.env.local`.
|
||||
To start, you'll just need to add your OpenAI API key, Solana RPC URL and wallet private key in base 58 string form.
|
||||
|
||||
Next, install the required packages using your preferred package manager (e.g. `pnpm`).
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
Now you're ready to run the development server:
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result! Ask the bot something and you'll see a streamed response:
|
||||
|
||||

|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
Backend logic lives in `app/api/chat/route.ts`. From here, you can change the prompt and model, or add other modules and logic.
|
||||
|
||||
## 📚 Learn More
|
||||
|
||||
To learn more about what you can do with SolanaAgentKit and LangChain.js, check out the docs here:
|
||||
|
||||
- https://github.com/sendaifun/solana-agent-kit
|
||||
- https://js.langchain.com/docs/
|
||||
|
||||
## ▲ Deploy on Vercel
|
||||
|
||||
When ready, you can deploy your app on the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme).
|
||||
|
||||
Check out the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
|
||||
## Thank You!
|
||||
|
||||
Thanks for reading! If you have any questions or comments, please drop an issue on this repo or reach out to us on [X](https://x.com/sendaifun)
|
||||
71
examples/agent-kit-nextjs-langchain/app/api/chat/route.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { ChatOpenAI } from "@langchain/openai";
|
||||
import { MemorySaver } from "@langchain/langgraph";
|
||||
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
||||
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
|
||||
|
||||
const llm = new ChatOpenAI({
|
||||
temperature: 0.7,
|
||||
model: "gpt-4o-mini",
|
||||
});
|
||||
|
||||
const solanaAgent = new SolanaAgentKit(
|
||||
process.env.SOLANA_PRIVATE_KEY!,
|
||||
process.env.RPC_URL,
|
||||
process.env.OPENAI_API_KEY!,
|
||||
);
|
||||
|
||||
const tools = createSolanaTools(solanaAgent);
|
||||
const memory = new MemorySaver();
|
||||
|
||||
const agent = createReactAgent({
|
||||
llm,
|
||||
tools,
|
||||
checkpointSaver: memory,
|
||||
messageModifier: `
|
||||
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
|
||||
empowered to interact onchain using your tools. If you ever need funds, you can request them from the
|
||||
faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX
|
||||
(internal) HTTP error code, ask the user to try again later. If someone asks you to do something you
|
||||
can't do with your currently available tools, you must say so, and encourage them to implement it
|
||||
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information. Be
|
||||
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested.
|
||||
`,
|
||||
});
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const body = await req.json();
|
||||
const messages = body.messages ?? [];
|
||||
|
||||
const eventStream = agent.streamEvents(
|
||||
{
|
||||
messages,
|
||||
},
|
||||
{
|
||||
version: "v2",
|
||||
configurable: {
|
||||
thread_id: "Solana Agent Kit!",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
const transformStream = new ReadableStream({
|
||||
async start(controller) {
|
||||
for await (const { event, data } of eventStream) {
|
||||
if (event === "on_chat_model_stream") {
|
||||
if (!!data.chunk.content) {
|
||||
controller.enqueue(textEncoder.encode(data.chunk.content));
|
||||
}
|
||||
}
|
||||
}
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(transformStream);
|
||||
} catch (e: any) {
|
||||
return NextResponse.json({ error: e.message }, { status: e.status ?? 500 });
|
||||
}
|
||||
}
|
||||
33
examples/agent-kit-nextjs-langchain/app/globals.css
Normal file
@@ -0,0 +1,33 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
color: #f8f8f8;
|
||||
background: #131318;
|
||||
}
|
||||
|
||||
body input,
|
||||
body textarea {
|
||||
color: black;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #2d7bd4;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #ffa500;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 4px;
|
||||
}
|
||||
45
examples/agent-kit-nextjs-langchain/app/layout.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import "./globals.css";
|
||||
import { Public_Sans } from "next/font/google";
|
||||
|
||||
const publicSans = Public_Sans({ subsets: ["latin"] });
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>SolanaAgentKit + LangChain + Next.js Template</title>
|
||||
<link rel="shortcut icon" href="/images/favicon.ico" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Starter template showing how to use SolanaAgentKit with Langchain in Next.js projects."
|
||||
/>
|
||||
<meta
|
||||
property="og:title"
|
||||
content="SolanaAgentKit + LangChain + Next.js Template"
|
||||
/>
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Starter template showing how to use SolanaAgentKit with LangChain in Next.js projects."
|
||||
/>
|
||||
<meta property="og:image" content="/images/title-card.png" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta
|
||||
name="twitter:title"
|
||||
content="SolanaAgentKit + LangChain + Next.js Template"
|
||||
/>
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="Starter template showing how to use SolanaAgentKit with LangChain in Next.js projects."
|
||||
/>
|
||||
<meta name="twitter:image" content="/images/title-card.png" />
|
||||
</head>
|
||||
<body className={publicSans.className}>
|
||||
<div className="flex flex-col p-4 md:p-12 h-[100vh]">{children}</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
75
examples/agent-kit-nextjs-langchain/app/page.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { ChatWindow } from "@/components/ChatWindow";
|
||||
|
||||
export default function Home() {
|
||||
const InfoCard = (
|
||||
<div className="p-4 md:p-8 rounded bg-[#25252d] w-full max-h-[85%] overflow-hidden">
|
||||
<h1 className="text-3xl md:text-4xl mb-4">
|
||||
SolanaAgentKit + LangChain.js 🦜🔗 + Next.js
|
||||
</h1>
|
||||
<ul>
|
||||
<li className="text-l">
|
||||
🤝
|
||||
<span className="ml-2">
|
||||
This template showcases a simple agent chatbot using{" "}
|
||||
<a href="https://https://www.solanaagentkit.xyz/">SolanaAgentKit</a>
|
||||
{", "}
|
||||
<a href="https://js.langchain.com/" target="_blank">
|
||||
LangChain.js
|
||||
</a>{" "}
|
||||
and the Vercel{" "}
|
||||
<a href="https://sdk.vercel.ai/docs" target="_blank">
|
||||
AI SDK
|
||||
</a>{" "}
|
||||
in a{" "}
|
||||
<a href="https://nextjs.org/" target="_blank">
|
||||
Next.js
|
||||
</a>{" "}
|
||||
project.
|
||||
</span>
|
||||
</li>
|
||||
<li className="hidden text-l md:block">
|
||||
💻
|
||||
<span className="ml-2">
|
||||
You can find the prompt and model logic for this use-case in{" "}
|
||||
<code>app/api/chat/route.ts</code>.
|
||||
</span>
|
||||
</li>
|
||||
<li className="hidden text-l md:block">
|
||||
🎨
|
||||
<span className="ml-2">
|
||||
The main frontend logic is found in <code>app/page.tsx</code>.
|
||||
</span>
|
||||
</li>
|
||||
<li className="text-l">
|
||||
🐙
|
||||
<span className="ml-2">
|
||||
This template is open source - you can see the source code and
|
||||
deploy your own version{" "}
|
||||
<a
|
||||
href="https://github.com/michaelessiet/solana-agent-nextjs-starter-langchain"
|
||||
target="_blank"
|
||||
>
|
||||
from the GitHub repo
|
||||
</a>
|
||||
!
|
||||
</span>
|
||||
</li>
|
||||
<li className="text-l">
|
||||
👇
|
||||
<span className="ml-2">
|
||||
Try asking e.g. <code>What is my wallet address?</code> below!
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<ChatWindow
|
||||
endpoint="api/chat"
|
||||
emoji="🤖"
|
||||
titleText="Solana agent"
|
||||
placeholder="I'm your friendly Solana agent! Ask me anything..."
|
||||
emptyStateComponent={InfoCard}
|
||||
></ChatWindow>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import markdownToHtml from "@/utils/markdownToHTML";
|
||||
import type { Message } from "ai/react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export function ChatMessageBubble(props: {
|
||||
message: Message;
|
||||
aiEmoji?: string;
|
||||
sources: any[];
|
||||
}) {
|
||||
const colorClassName =
|
||||
props.message.role === "user" ? "bg-sky-600" : "bg-slate-50 text-black";
|
||||
const alignmentClassName =
|
||||
props.message.role === "user" ? "ml-auto" : "mr-auto";
|
||||
const prefix = props.message.role === "user" ? "🧑" : props.aiEmoji;
|
||||
|
||||
const content = useMemo(() => {
|
||||
return markdownToHtml(props.message.content);
|
||||
}, [props.message.content]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${alignmentClassName} ${colorClassName} rounded px-4 py-2 max-w-[80%] mb-8 flex`}
|
||||
>
|
||||
<div className="mr-2">{prefix}</div>
|
||||
<div className="flex flex-col">
|
||||
<div
|
||||
className="prose prose-lg max-w-none"
|
||||
dangerouslySetInnerHTML={{ __html: content }}
|
||||
></div>
|
||||
{props.sources && props.sources.length ? (
|
||||
<>
|
||||
<code className="mt-4 mr-auto bg-slate-600 px-2 py-1 rounded">
|
||||
<h2>🔍 Sources:</h2>
|
||||
</code>
|
||||
<code className="mt-1 mr-2 bg-slate-600 px-2 py-1 rounded text-xs">
|
||||
{props.sources?.map((source, i) => (
|
||||
<div className="mt-2" key={"source:" + i}>
|
||||
{i + 1}. "{source.pageContent}"
|
||||
{source.metadata?.loc?.lines !== undefined ? (
|
||||
<div>
|
||||
<br />
|
||||
Lines {source.metadata?.loc?.lines?.from} to{" "}
|
||||
{source.metadata?.loc?.lines?.to}
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</code>
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
248
examples/agent-kit-nextjs-langchain/components/ChatWindow.tsx
Normal file
@@ -0,0 +1,248 @@
|
||||
"use client";
|
||||
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
|
||||
import { Message } from "ai";
|
||||
import { useChat } from "ai/react";
|
||||
import { useRef, useState, ReactElement } from "react";
|
||||
import type { FormEvent } from "react";
|
||||
|
||||
import { ChatMessageBubble } from "@/components/ChatMessageBubble";
|
||||
import { IntermediateStep } from "./IntermediateStep";
|
||||
|
||||
export function ChatWindow(props: {
|
||||
endpoint: string;
|
||||
emptyStateComponent: ReactElement;
|
||||
placeholder?: string;
|
||||
titleText?: string;
|
||||
emoji?: string;
|
||||
showIntermediateStepsToggle?: boolean;
|
||||
}) {
|
||||
const messageContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const {
|
||||
endpoint,
|
||||
emptyStateComponent,
|
||||
placeholder,
|
||||
titleText = "An LLM",
|
||||
showIntermediateStepsToggle,
|
||||
emoji,
|
||||
} = props;
|
||||
|
||||
const [showIntermediateSteps, setShowIntermediateSteps] = useState(false);
|
||||
const [intermediateStepsLoading, setIntermediateStepsLoading] =
|
||||
useState(false);
|
||||
const intemediateStepsToggle = showIntermediateStepsToggle && (
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="show_intermediate_steps"
|
||||
name="show_intermediate_steps"
|
||||
checked={showIntermediateSteps}
|
||||
onChange={(e) => setShowIntermediateSteps(e.target.checked)}
|
||||
></input>
|
||||
<label htmlFor="show_intermediate_steps"> Show intermediate steps</label>
|
||||
</div>
|
||||
);
|
||||
|
||||
const [sourcesForMessages, setSourcesForMessages] = useState<
|
||||
Record<string, any>
|
||||
>({});
|
||||
|
||||
const {
|
||||
messages,
|
||||
input,
|
||||
setInput,
|
||||
handleInputChange,
|
||||
handleSubmit,
|
||||
isLoading: chatEndpointIsLoading,
|
||||
setMessages,
|
||||
} = useChat({
|
||||
api: endpoint,
|
||||
onResponse(response) {
|
||||
const sourcesHeader = response.headers.get("x-sources");
|
||||
const sources = sourcesHeader
|
||||
? JSON.parse(Buffer.from(sourcesHeader, "base64").toString("utf8"))
|
||||
: [];
|
||||
const messageIndexHeader = response.headers.get("x-message-index");
|
||||
if (sources.length && messageIndexHeader !== null) {
|
||||
setSourcesForMessages({
|
||||
...sourcesForMessages,
|
||||
[messageIndexHeader]: sources,
|
||||
});
|
||||
}
|
||||
},
|
||||
streamMode: "text",
|
||||
onError: (e) => {
|
||||
toast(e.message, {
|
||||
theme: "dark",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
async function sendMessage(e: FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
if (messageContainerRef.current) {
|
||||
messageContainerRef.current.classList.add("grow");
|
||||
}
|
||||
if (!messages.length) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
}
|
||||
if (chatEndpointIsLoading ?? intermediateStepsLoading) {
|
||||
return;
|
||||
}
|
||||
if (!showIntermediateSteps) {
|
||||
handleSubmit(e);
|
||||
// Some extra work to show intermediate steps properly
|
||||
} else {
|
||||
setIntermediateStepsLoading(true);
|
||||
setInput("");
|
||||
const messagesWithUserReply = messages.concat({
|
||||
id: messages.length.toString(),
|
||||
content: input,
|
||||
role: "user",
|
||||
});
|
||||
setMessages(messagesWithUserReply);
|
||||
const response = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
messages: messagesWithUserReply,
|
||||
show_intermediate_steps: true,
|
||||
}),
|
||||
});
|
||||
const json = await response.json();
|
||||
setIntermediateStepsLoading(false);
|
||||
if (response.status === 200) {
|
||||
const responseMessages: Message[] = json.messages;
|
||||
// Represent intermediate steps as system messages for display purposes
|
||||
// TODO: Add proper support for tool messages
|
||||
const toolCallMessages = responseMessages.filter(
|
||||
(responseMessage: Message) => {
|
||||
return (
|
||||
(responseMessage.role === "assistant" &&
|
||||
!!responseMessage.tool_calls?.length) ||
|
||||
responseMessage.role === "tool"
|
||||
);
|
||||
},
|
||||
);
|
||||
const intermediateStepMessages = [];
|
||||
for (let i = 0; i < toolCallMessages.length; i += 2) {
|
||||
const aiMessage = toolCallMessages[i];
|
||||
const toolMessage = toolCallMessages[i + 1];
|
||||
intermediateStepMessages.push({
|
||||
id: (messagesWithUserReply.length + i / 2).toString(),
|
||||
role: "system" as const,
|
||||
content: JSON.stringify({
|
||||
action: aiMessage.tool_calls?.[0],
|
||||
observation: toolMessage.content,
|
||||
}),
|
||||
});
|
||||
}
|
||||
const newMessages = messagesWithUserReply;
|
||||
for (const message of intermediateStepMessages) {
|
||||
newMessages.push(message);
|
||||
setMessages([...newMessages]);
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, 1000 + Math.random() * 1000),
|
||||
);
|
||||
}
|
||||
setMessages([
|
||||
...newMessages,
|
||||
{
|
||||
id: newMessages.length.toString(),
|
||||
content: responseMessages[responseMessages.length - 1].content,
|
||||
role: "assistant",
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
if (json.error) {
|
||||
toast(json.error, {
|
||||
theme: "dark",
|
||||
});
|
||||
throw new Error(json.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col items-center p-4 md:p-8 rounded grow overflow-hidden ${messages.length > 0 ? "border" : ""}`}
|
||||
>
|
||||
<h2 className={`${messages.length > 0 ? "" : "hidden"} text-2xl`}>
|
||||
{emoji} {titleText}
|
||||
</h2>
|
||||
{messages.length === 0 ? emptyStateComponent : ""}
|
||||
<div
|
||||
className="flex flex-col-reverse w-full mb-4 overflow-auto transition-[flex-grow] ease-in-out"
|
||||
ref={messageContainerRef}
|
||||
>
|
||||
{messages.length > 0
|
||||
? [...messages].reverse().map((m, i) => {
|
||||
const sourceKey = (messages.length - 1 - i).toString();
|
||||
return m.role === "system" ? (
|
||||
<IntermediateStep key={m.id} message={m}></IntermediateStep>
|
||||
) : (
|
||||
<ChatMessageBubble
|
||||
key={m.id}
|
||||
message={m}
|
||||
aiEmoji={emoji}
|
||||
sources={sourcesForMessages[sourceKey]}
|
||||
></ChatMessageBubble>
|
||||
);
|
||||
})
|
||||
: ""}
|
||||
</div>
|
||||
|
||||
<form onSubmit={sendMessage} className="flex w-full flex-col">
|
||||
<div className="flex">{intemediateStepsToggle}</div>
|
||||
<div className="flex w-full mt-4">
|
||||
<input
|
||||
className="grow mr-8 p-4 rounded"
|
||||
value={input}
|
||||
placeholder={placeholder ?? "What's it like to be a pirate?"}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="shrink-0 px-8 py-4 bg-sky-600 rounded w-28"
|
||||
>
|
||||
<div
|
||||
role="status"
|
||||
className={`${chatEndpointIsLoading || intermediateStepsLoading ? "" : "hidden"} flex justify-center`}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="w-6 h-6 text-white animate-spin dark:text-white fill-sky-800"
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
fill="currentFill"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Loading...</span>
|
||||
</div>
|
||||
<span
|
||||
className={
|
||||
chatEndpointIsLoading || intermediateStepsLoading
|
||||
? "hidden"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
Send
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<ToastContainer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { useState } from "react";
|
||||
import type { Message } from "ai/react";
|
||||
|
||||
export function IntermediateStep(props: { message: Message }) {
|
||||
const parsedInput = JSON.parse(props.message.content);
|
||||
const action = parsedInput.action;
|
||||
const observation = parsedInput.observation;
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
return (
|
||||
<div
|
||||
className={`ml-auto bg-green-600 rounded px-4 py-2 max-w-[80%] mb-8 whitespace-pre-wrap flex flex-col cursor-pointer`}
|
||||
>
|
||||
<div
|
||||
className={`text-right ${expanded ? "w-full" : ""}`}
|
||||
onClick={(e) => setExpanded(!expanded)}
|
||||
>
|
||||
<code className="mr-2 bg-slate-600 px-2 py-1 rounded hover:text-blue-600">
|
||||
🛠 <b>{action.name}</b>
|
||||
</code>
|
||||
<span className={expanded ? "hidden" : ""}>🔽</span>
|
||||
<span className={expanded ? "" : "hidden"}>🔼</span>
|
||||
</div>
|
||||
<div
|
||||
className={`overflow-hidden max-h-[0px] transition-[max-height] ease-in-out ${expanded ? "max-h-[360px]" : ""}`}
|
||||
>
|
||||
<div
|
||||
className={`bg-slate-600 rounded p-4 mt-1 max-w-0 ${expanded ? "max-w-full" : "transition-[max-width] delay-100"}`}
|
||||
>
|
||||
<code
|
||||
className={`opacity-0 max-h-[100px] overflow-auto transition ease-in-out delay-150 ${expanded ? "opacity-100" : ""}`}
|
||||
>
|
||||
Tool Input:
|
||||
<br></br>
|
||||
<br></br>
|
||||
{JSON.stringify(action.args)}
|
||||
</code>
|
||||
</div>
|
||||
<div
|
||||
className={`bg-slate-600 rounded p-4 mt-1 max-w-0 ${expanded ? "max-w-full" : "transition-[max-width] delay-100"}`}
|
||||
>
|
||||
<code
|
||||
className={`opacity-0 max-h-[260px] overflow-auto transition ease-in-out delay-150 ${expanded ? "opacity-100" : ""}`}
|
||||
>
|
||||
{observation}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
540
examples/agent-kit-nextjs-langchain/data/DefaultRetrievalText.ts
Normal file
@@ -0,0 +1,540 @@
|
||||
export default `# QA and Chat over Documents
|
||||
|
||||
Chat and Question-Answering (QA) over \`data\` are popular LLM use-cases.
|
||||
|
||||
\`data\` can include many things, including:
|
||||
|
||||
* \`Unstructured data\` (e.g., PDFs)
|
||||
* \`Structured data\` (e.g., SQL)
|
||||
* \`Code\` (e.g., Python)
|
||||
|
||||
Below we will review Chat and QA on \`Unstructured data\`.
|
||||
|
||||

|
||||
|
||||
\`Unstructured data\` can be loaded from many sources.
|
||||
|
||||
Check out the [document loader integrations here](/docs/modules/data_connection/document_loaders/) to browse the set of supported loaders.
|
||||
|
||||
Each loader returns data as a LangChain \`Document\`.
|
||||
|
||||
\`Documents\` are turned into a Chat or QA app following the general steps below:
|
||||
|
||||
* \`Splitting\`: [Text splitters](/docs/modules/data_connection/document_transformers/) break \`Documents\` into splits of specified size
|
||||
* \`Storage\`: Storage (e.g., often a [vectorstore](/docs/modules/data_connection/vectorstores/)) will house [and often embed](https://www.pinecone.io/learn/vector-embeddings/) the splits
|
||||
* \`Retrieval\`: The app retrieves splits from storage (e.g., often [with similar embeddings](https://www.pinecone.io/learn/k-nearest-neighbor/) to the input question)
|
||||
* \`Output\`: An [LLM](/docs/modules/model_io/models/llms/) produces an answer using a prompt that includes the question and the retrieved splits
|
||||
|
||||

|
||||
|
||||
## Quickstart
|
||||
|
||||
Let's load this [blog post](https://lilianweng.github.io/posts/2023-06-23-agent/) on agents as an example \`Document\`.
|
||||
|
||||
We'll have a QA app in a few lines of code.
|
||||
|
||||
First, set environment variables and install packages required for the guide:
|
||||
|
||||
\`\`\`shell
|
||||
> yarn add cheerio
|
||||
# Or load env vars in your preferred way:
|
||||
> export OPENAI_API_KEY="..."
|
||||
\`\`\`
|
||||
|
||||
## 1. Loading, Splitting, Storage
|
||||
|
||||
### 1.1 Getting started
|
||||
|
||||
Specify a \`Document\` loader.
|
||||
|
||||
\`\`\`typescript
|
||||
// Document loader
|
||||
import { CheerioWebBaseLoader } from "langchain/document_loaders/web/cheerio";
|
||||
|
||||
const loader = new CheerioWebBaseLoader(
|
||||
"https://lilianweng.github.io/posts/2023-06-23-agent/"
|
||||
);
|
||||
const data = await loader.load();
|
||||
\`\`\`
|
||||
|
||||
Split the \`Document\` into chunks for embedding and vector storage.
|
||||
|
||||
|
||||
\`\`\`typescript
|
||||
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
|
||||
|
||||
const textSplitter = new RecursiveCharacterTextSplitter({
|
||||
chunkSize: 500,
|
||||
chunkOverlap: 0,
|
||||
});
|
||||
|
||||
const splitDocs = await textSplitter.splitDocuments(data);
|
||||
\`\`\`
|
||||
|
||||
Embed and store the splits in a vector database (for demo purposes we use an unoptimized, in-memory example but you can [browse integrations here](/docs/modules/data_connection/vectorstores/integrations/)):
|
||||
|
||||
|
||||
\`\`\`typescript
|
||||
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
|
||||
import { MemoryVectorStore } from "langchain/vectorstores/memory";
|
||||
|
||||
const embeddings = new OpenAIEmbeddings();
|
||||
|
||||
const vectorStore = await MemoryVectorStore.fromDocuments(splitDocs, embeddings);
|
||||
\`\`\`
|
||||
|
||||
Here are the three pieces together:
|
||||
|
||||

|
||||
|
||||
### 1.2 Going Deeper
|
||||
|
||||
#### 1.2.1 Integrations
|
||||
|
||||
\`Document Loaders\`
|
||||
|
||||
* Browse document loader integrations [here](/docs/modules/data_connection/document_loaders/).
|
||||
|
||||
* See further documentation on loaders [here](/docs/modules/data_connection/document_loaders/).
|
||||
|
||||
\`Document Transformers\`
|
||||
|
||||
* All can ingest loaded \`Documents\` and process them (e.g., split).
|
||||
|
||||
* See further documentation on transformers [here](/docs/modules/data_connection/document_transformers/).
|
||||
|
||||
\`Vectorstores\`
|
||||
|
||||
* Browse vectorstore integrations [here](/docs/modules/data_connection/vectorstores/integrations/).
|
||||
|
||||
* See further documentation on vectorstores [here](/docs/modules/data_connection/vectorstores/).
|
||||
|
||||
## 2. Retrieval
|
||||
|
||||
### 2.1 Getting started
|
||||
|
||||
Retrieve [relevant splits](https://www.pinecone.io/learn/what-is-similarity-search/) for any question using \`similarity_search\`.
|
||||
|
||||
|
||||
\`\`\`typescript
|
||||
const relevantDocs = await vectorStore.similaritySearch("What is task decomposition?");
|
||||
|
||||
console.log(relevantDocs.length);
|
||||
|
||||
// 4
|
||||
\`\`\`
|
||||
|
||||
|
||||
### 2.2 Going Deeper
|
||||
|
||||
#### 2.2.1 Retrieval
|
||||
|
||||
Vectorstores are commonly used for retrieval.
|
||||
|
||||
But, they are not the only option.
|
||||
|
||||
For example, SVMs (see thread [here](https://twitter.com/karpathy/status/1647025230546886658?s=20)) can also be used.
|
||||
|
||||
LangChain [has many retrievers and retrieval methods](/docs/modules/data_connection/retrievers/) including, but not limited to, vectorstores.
|
||||
|
||||
All retrievers implement some common methods, such as \`getRelevantDocuments()\`.
|
||||
|
||||
|
||||
## 3. QA
|
||||
|
||||
### 3.1 Getting started
|
||||
|
||||
Distill the retrieved documents into an answer using an LLM (e.g., \`gpt-3.5-turbo\`) with \`RetrievalQA\` chain.
|
||||
|
||||
|
||||
\`\`\`typescript
|
||||
import { RetrievalQAChain } from "langchain/chains";
|
||||
import { ChatOpenAI } from "langchain/chat_models/openai";
|
||||
|
||||
const model = new ChatOpenAI({ model: "gpt-3.5-turbo" });
|
||||
const chain = RetrievalQAChain.fromLLM(model, vectorstore.asRetriever());
|
||||
|
||||
const response = await chain.call({
|
||||
query: "What is task decomposition?"
|
||||
});
|
||||
console.log(response);
|
||||
|
||||
/*
|
||||
{
|
||||
text: 'Task decomposition refers to the process of breaking down a larger task into smaller, more manageable subgoals. By decomposing a task, it becomes easier for an agent or system to handle complex tasks efficiently. Task decomposition can be done through various methods such as using prompting or task-specific instructions, or through human inputs. It helps in planning and organizing the steps required to complete a task effectively.'
|
||||
}
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
### 3.2 Going Deeper
|
||||
|
||||
#### 3.2.1 Integrations
|
||||
|
||||
\`LLMs\`
|
||||
|
||||
* Browse LLM integrations and further documentation [here](/docs/modules/model_io/models/).
|
||||
|
||||
#### 3.2.2 Customizing the prompt
|
||||
|
||||
The prompt in \`RetrievalQA\` chain can be customized as follows.
|
||||
|
||||
|
||||
\`\`\`typescript
|
||||
import { RetrievalQAChain } from "langchain/chains";
|
||||
import { ChatOpenAI } from "langchain/chat_models/openai";
|
||||
import { PromptTemplate } from "langchain/prompts";
|
||||
|
||||
const model = new ChatOpenAI({ model: "gpt-3.5-turbo" });
|
||||
|
||||
const template = \`Use the following pieces of context to answer the question at the end.
|
||||
If you don't know the answer, just say that you don't know, don't try to make up an answer.
|
||||
Use three sentences maximum and keep the answer as concise as possible.
|
||||
Always say "thanks for asking!" at the end of the answer.
|
||||
{context}
|
||||
Question: {question}
|
||||
Helpful Answer:\`;
|
||||
|
||||
const chain = RetrievalQAChain.fromLLM(model, vectorstore.asRetriever(), {
|
||||
prompt: PromptTemplate.fromTemplate(template),
|
||||
});
|
||||
|
||||
const response = await chain.call({
|
||||
query: "What is task decomposition?"
|
||||
});
|
||||
|
||||
console.log(response);
|
||||
|
||||
/*
|
||||
{
|
||||
text: 'Task decomposition is the process of breaking down a large task into smaller, more manageable subgoals. This allows for efficient handling of complex tasks and aids in planning and organizing the steps needed to achieve the overall goal. Thanks for asking!'
|
||||
}
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
|
||||
#### 3.2.3 Returning source documents
|
||||
|
||||
The full set of retrieved documents used for answer distillation can be returned using \`return_source_documents=True\`.
|
||||
|
||||
|
||||
\`\`\`typescript
|
||||
import { RetrievalQAChain } from "langchain/chains";
|
||||
import { ChatOpenAI } from "langchain/chat_models/openai";
|
||||
|
||||
const model = new ChatOpenAI({ model: "gpt-3.5-turbo" });
|
||||
|
||||
const chain = RetrievalQAChain.fromLLM(model, vectorstore.asRetriever(), {
|
||||
returnSourceDocuments: true
|
||||
});
|
||||
|
||||
const response = await chain.call({
|
||||
query: "What is task decomposition?"
|
||||
});
|
||||
|
||||
console.log(response.sourceDocuments[0]);
|
||||
|
||||
/*
|
||||
Document {
|
||||
pageContent: 'Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outline." for writing a novel, or (3) with human inputs.',
|
||||
metadata: [Object]
|
||||
}
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
|
||||
#### 3.2.4 Customizing retrieved docs in the LLM prompt
|
||||
|
||||
Retrieved documents can be fed to an LLM for answer distillation in a few different ways.
|
||||
|
||||
\`stuff\`, \`refine\`, and \`map-reduce\` chains for passing documents to an LLM prompt are well summarized [here](/docs/modules/chains/document/).
|
||||
|
||||
\`stuff\` is commonly used because it simply "stuffs" all retrieved documents into the prompt.
|
||||
|
||||
The [loadQAChain](/docs/modules/chains/document/) methods are easy ways to pass documents to an LLM using these various approaches.
|
||||
|
||||
|
||||
\`\`\`typescript
|
||||
import { loadQAStuffChain } from "langchain/chains";
|
||||
|
||||
const stuffChain = loadQAStuffChain(model);
|
||||
|
||||
const stuffResult = await stuffChain.call({
|
||||
input_documents: relevantDocs,
|
||||
question: "What is task decomposition
|
||||
});
|
||||
|
||||
console.log(stuffResult);
|
||||
/*
|
||||
{
|
||||
text: 'Task decomposition is the process of breaking down a large task into smaller, more manageable subgoals or steps. This allows for efficient handling of complex tasks by focusing on one subgoal at a time. Task decomposition can be done through various methods such as using simple prompting, task-specific instructions, or human inputs.'
|
||||
}
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
## 4. Chat
|
||||
|
||||
### 4.1 Getting started
|
||||
|
||||
To keep chat history, we use a variant of the previous chain called a \`ConversationalRetrievalQAChain\`.
|
||||
First, specify a \`Memory buffer\` to track the conversation inputs / outputs.
|
||||
|
||||
|
||||
\`\`\`typescript
|
||||
import { ConversationalRetrievalQAChain } from "langchain/chains";
|
||||
import { BufferMemory } from "langchain/memory";
|
||||
import { ChatOpenAI } from "langchain/chat_models/openai";
|
||||
|
||||
const memory = new BufferMemory({
|
||||
memoryKey: "chat_history",
|
||||
returnMessages: true,
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
Next, we initialize and call the chain:
|
||||
|
||||
\`\`\`typescript
|
||||
const model = new ChatOpenAI({ model: "gpt-3.5-turbo" });
|
||||
const chain = ConversationalRetrievalQAChain.fromLLM(model, vectorstore.asRetriever(), {
|
||||
memory
|
||||
});
|
||||
|
||||
const result = await chain.call({
|
||||
question: "What are some of the main ideas in self-reflection?"
|
||||
});
|
||||
console.log(result);
|
||||
|
||||
/*
|
||||
{
|
||||
text: 'Some main ideas in self-reflection include:\n' +
|
||||
'\n' +
|
||||
'1. Iterative Improvement: Self-reflection allows autonomous agents to improve by continuously refining past action decisions and correcting mistakes.\n' +
|
||||
'\n' +
|
||||
'2. Trial and Error: Self-reflection plays a crucial role in real-world tasks where trial and error are inevitable. It helps agents learn from failed trajectories and make adjustments for future actions.\n' +
|
||||
'\n' +
|
||||
'3. Constructive Criticism: Agents engage in constructive self-criticism of their big-picture behavior to identify areas for improvement.\n' +
|
||||
'\n' +
|
||||
'4. Decision and Strategy Refinement: Reflection on past decisions and strategies enables agents to refine their approach and make more informed choices.\n' +
|
||||
'\n' +
|
||||
'5. Efficiency and Optimization: Self-reflection encourages agents to be smart and efficient in their actions, aiming to complete tasks in the least number of steps.\n' +
|
||||
'\n' +
|
||||
'These ideas highlight the importance of self-reflection in enhancing performance and guiding future actions.'
|
||||
}
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
|
||||
The \`Memory buffer\` has context to resolve \`"it"\` ("self-reflection") in the below question.
|
||||
|
||||
|
||||
\`\`\`typescript
|
||||
const followupResult = await chain.call({
|
||||
question: "How does the Reflexion paper handle it?"
|
||||
});
|
||||
console.log(followupResult);
|
||||
|
||||
/*
|
||||
{
|
||||
text: "The Reflexion paper introduces a framework that equips agents with dynamic memory and self-reflection capabilities to improve their reasoning skills. The approach involves showing the agent two-shot examples, where each example consists of a failed trajectory and an ideal reflection on how to guide future changes in the agent's plan. These reflections are then added to the agent's working memory as context for querying a language model. The agent uses this self-reflection information to make decisions on whether to start a new trial or continue with the current plan."
|
||||
}
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
|
||||
### 4.2 Going deeper
|
||||
|
||||
The [documentation](/docs/modules/chains/popular/chat_vector_db) on \`ConversationalRetrievalQAChain\` offers a few extensions, such as streaming and source documents.
|
||||
|
||||
|
||||
# Conversational Retrieval Agents
|
||||
|
||||
This is an agent specifically optimized for doing retrieval when necessary while holding a conversation and being able
|
||||
to answer questions based on previous dialogue in the conversation.
|
||||
|
||||
To start, we will set up the retriever we want to use, then turn it into a retriever tool. Next, we will use the high-level constructor for this type of agent.
|
||||
Finally, we will walk through how to construct a conversational retrieval agent from components.
|
||||
|
||||
## The Retriever
|
||||
|
||||
To start, we need a retriever to use! The code here is mostly just example code. Feel free to use your own retriever and skip to the next section on creating a retriever tool.
|
||||
|
||||
\`\`\`typescript
|
||||
import { FaissStore } from "langchain/vectorstores/faiss";
|
||||
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
|
||||
import { TextLoader } from "langchain/document_loaders/fs/text";
|
||||
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
|
||||
|
||||
const loader = new TextLoader("state_of_the_union.txt");
|
||||
const docs = await loader.load();
|
||||
const splitter = new RecursiveCharacterTextSplitter({
|
||||
chunkSize: 1000,
|
||||
chunkOverlap: 0
|
||||
});
|
||||
|
||||
const texts = await splitter.splitDocuments(docs);
|
||||
|
||||
const vectorStore = await FaissStore.fromDocuments(texts, new OpenAIEmbeddings());
|
||||
|
||||
const retriever = vectorStore.asRetriever();
|
||||
\`\`\`
|
||||
|
||||
## Retriever Tool
|
||||
|
||||
Now we need to create a tool for our retriever. The main things we need to pass in are a \`name\` for the retriever as well as a \`description\`. These will both be used by the language model, so they should be informative.
|
||||
|
||||
\`\`\`typescript
|
||||
import { createRetrieverTool } from "langchain/agents/toolkits";
|
||||
|
||||
const tool = createRetrieverTool(retriever, {
|
||||
name: "search_state_of_union",
|
||||
description: "Searches and returns documents regarding the state-of-the-union.",
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
## Agent Constructor
|
||||
|
||||
Here, we will use the high level \`create_conversational_retrieval_agent\` API to construct the agent.
|
||||
Notice that beside the list of tools, the only thing we need to pass in is a language model to use.
|
||||
|
||||
Under the hood, this agent is using the OpenAIFunctionsAgent, so we need to use an ChatOpenAI model.
|
||||
|
||||
\`\`\`typescript
|
||||
import { createConversationalRetrievalAgent } from "langchain/agents/toolkits";
|
||||
import { ChatOpenAI } from "langchain/chat_models/openai";
|
||||
|
||||
const model = new ChatOpenAI({
|
||||
temperature: 0,
|
||||
});
|
||||
|
||||
const executor = await createConversationalRetrievalAgent(model, [tool], {
|
||||
verbose: true,
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
We can now try it out!
|
||||
|
||||
\`\`\`typescript
|
||||
const result = await executor.call({
|
||||
input: "Hi, I'm Bob!"
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
||||
/*
|
||||
{
|
||||
output: 'Hello Bob! How can I assist you today?',
|
||||
intermediateSteps: []
|
||||
}
|
||||
*/
|
||||
|
||||
const result2 = await executor.call({
|
||||
input: "What's my name?"
|
||||
});
|
||||
|
||||
console.log(result2);
|
||||
|
||||
/*
|
||||
{ output: 'Your name is Bob.', intermediateSteps: [] }
|
||||
*/
|
||||
|
||||
const result3 = await executor.call({
|
||||
input: "What did the president say about Ketanji Brown Jackson in the most recent state of the union?"
|
||||
});
|
||||
|
||||
console.log(result3);
|
||||
|
||||
/*
|
||||
{
|
||||
output: "In the most recent state of the union, President Biden mentioned Ketanji Brown Jackson. He nominated her as a Circuit Court of Appeals judge and described her as one of the nation's top legal minds who will continue Justice Breyer's legacy of excellence. He mentioned that she has received a broad range of support, including from the Fraternal Order of Police and former judges appointed by Democrats and Republicans.",
|
||||
intermediateSteps: [
|
||||
{...}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
const result4 = await executor.call({
|
||||
input: "How long ago did he nominate her?"
|
||||
});
|
||||
|
||||
console.log(result4);
|
||||
|
||||
/*
|
||||
{
|
||||
output: 'President Biden nominated Ketanji Brown Jackson four days before the most recent state of the union address.',
|
||||
intermediateSteps: []
|
||||
}
|
||||
*/
|
||||
\`\`\`
|
||||
|
||||
Note that for the final call, the agent used previously retrieved information to answer the query and did not need to call the tool again!
|
||||
|
||||
Here's a trace showing how the agent fetches documents to answer the question with the retrieval tool:
|
||||
|
||||
https://smith.langchain.com/public/1e2b1887-ca44-4210-913b-a69c1b8a8e7e/r
|
||||
|
||||
## Creating from components
|
||||
|
||||
What actually is going on underneath the hood? Let's take a look so we can understand how to modify things going forward.
|
||||
|
||||
### Memory
|
||||
|
||||
In this example, we want the agent to remember not only previous conversations, but also previous intermediate steps.
|
||||
For that, we can use \`OpenAIAgentTokenBufferMemory\`. Note that if you want to change whether the agent remembers intermediate steps,
|
||||
how the long the retained buffer is, or anything like that you should change this part.
|
||||
|
||||
\`\`\`typescript
|
||||
import { OpenAIAgentTokenBufferMemory } from "langchain/agents/toolkits";
|
||||
|
||||
const memory = new OpenAIAgentTokenBufferMemory({
|
||||
llm: model,
|
||||
memoryKey: "chat_history",
|
||||
outputKey: "output"
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
You should make sure \`memoryKey\` is set to \`"chat_history"\` and \`outputKey\` is set to \`"output"\` for the OpenAI functions agent.
|
||||
This memory also has \`returnMessages\` set to \`true\` by default.
|
||||
|
||||
You can also load messages from prior conversations into this memory by initializing it with a pre-loaded chat history:
|
||||
|
||||
\`\`\`typescript
|
||||
import { ChatOpenAI } from "langchain/chat_models/openai";
|
||||
import { OpenAIAgentTokenBufferMemory } from "langchain/agents/toolkits";
|
||||
import { HumanMessage, AIMessage } from "langchain/schema";
|
||||
import { ChatMessageHistory } from "langchain/memory";
|
||||
|
||||
const previousMessages = [
|
||||
new HumanMessage("My name is Bob"),
|
||||
new AIMessage("Nice to meet you, Bob!"),
|
||||
];
|
||||
|
||||
const chatHistory = new ChatMessageHistory(previousMessages);
|
||||
|
||||
const memory = new OpenAIAgentTokenBufferMemory({
|
||||
llm: new ChatOpenAI({}),
|
||||
memoryKey: "chat_history",
|
||||
outputKey: "output",
|
||||
chatHistory,
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Agent executor
|
||||
|
||||
We can recreate the agent executor directly with the \`initializeAgentExecutorWithOptions\` method.
|
||||
This allows us to customize the agent's system message by passing in a \`prefix\` into \`agentArgs\`.
|
||||
Importantly, we must pass in \`return_intermediate_steps: true\` since we are recording that with our memory object.
|
||||
|
||||
\`\`\`typescript
|
||||
import { initializeAgentExecutorWithOptions } from "langchain/agents";
|
||||
|
||||
const executor = await initializeAgentExecutorWithOptions(tools, llm, {
|
||||
agentType: "openai-functions",
|
||||
memory,
|
||||
returnIntermediateSteps: true,
|
||||
agentArgs: {
|
||||
prefix:
|
||||
prefix ??
|
||||
\`Do your best to answer the questions. Feel free to use any tools available to look up relevant information, only if necessary.\`,
|
||||
},
|
||||
});
|
||||
\`\`\`
|
||||
`;
|
||||
4
examples/agent-kit-nextjs-langchain/next.config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
})
|
||||
module.exports = withBundleAnalyzer({})
|
||||
47
examples/agent-kit-nextjs-langchain/package.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "langchain-nextjs-template",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"format": "prettier --write \"app\""
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"dependencies": {
|
||||
"@langchain/community": "^0.3.11",
|
||||
"@langchain/core": "^0.3.17",
|
||||
"@langchain/langgraph": "^0.2.20",
|
||||
"@langchain/openai": "^0.3.11",
|
||||
"@next/bundle-analyzer": "^13.4.19",
|
||||
"@supabase/supabase-js": "^2.32.0",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@types/node": "20.12.12",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"ai": "^3.1.12",
|
||||
"autoprefixer": "10.4.14",
|
||||
"eslint": "8.46.0",
|
||||
"eslint-config-next": "13.4.12",
|
||||
"isomorphic-dompurify": "^2.19.0",
|
||||
"langchain": "^0.3.5",
|
||||
"marked": "^15.0.4",
|
||||
"next": "^14.2.3",
|
||||
"postcss": "8.4.27",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-toastify": "^9.1.3",
|
||||
"solana-agent-kit": "^1.3.0",
|
||||
"tailwindcss": "3.3.3",
|
||||
"typescript": "5.1.6",
|
||||
"zod": "^3.22.3",
|
||||
"zod-to-json-schema": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "3.0.0"
|
||||
}
|
||||
}
|
||||
8019
examples/agent-kit-nextjs-langchain/pnpm-lock.yaml
generated
Normal file
6
examples/agent-kit-nextjs-langchain/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
BIN
examples/agent-kit-nextjs-langchain/public/images/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
examples/agent-kit-nextjs-langchain/public/images/og-image.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
examples/agent-kit-nextjs-langchain/public/images/title-card.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
18
examples/agent-kit-nextjs-langchain/tailwind.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
|
||||
"gradient-conic":
|
||||
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require("@tailwindcss/typography")],
|
||||
};
|
||||
28
examples/agent-kit-nextjs-langchain/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
30
examples/agent-kit-nextjs-langchain/utils/markdownToHTML.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { marked } from "marked";
|
||||
import DOMPurify from "isomorphic-dompurify";
|
||||
|
||||
interface MarkedOptions {
|
||||
gfm: boolean;
|
||||
breaks: boolean;
|
||||
headerIds: boolean;
|
||||
mangle: false;
|
||||
highlight?: (code: string, lang: string) => string;
|
||||
}
|
||||
|
||||
// Configure marked options
|
||||
const markedOptions: MarkedOptions = {
|
||||
gfm: true, // GitHub Flavored Markdown
|
||||
breaks: true, // Convert \n to <br>
|
||||
headerIds: true, // Add ids to headers
|
||||
mangle: false, // Don't escape HTML
|
||||
highlight: function (code: string, lang: string): string {
|
||||
// You can add syntax highlighting here if needed
|
||||
return code;
|
||||
},
|
||||
};
|
||||
|
||||
marked.setOptions(markedOptions);
|
||||
|
||||
// Basic markdown to HTML conversion with sanitization
|
||||
export default function markdownToHtml(markdown: string) {
|
||||
const rawHtml = marked.parse(markdown);
|
||||
return DOMPurify.sanitize(rawHtml as string);
|
||||
}
|
||||
4
examples/persistent-agent/.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
OPENAI_API_KEY=
|
||||
RPC_URL=
|
||||
SOLANA_PRIVATE_KEY=
|
||||
POSTGRES_DB_URL=
|
||||
66
examples/persistent-agent/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Persistent Agent with PostgreSQL
|
||||
|
||||
This example showcases a persistent agent that retains memory across sessions using a PostgreSQL database. It ensures that the agent can remember previous conversations even after being restarted, enhancing the user experience in applications requiring long-term context retention.
|
||||
[Reference](https://langchain-ai.github.io/langgraphjs/reference/classes/checkpoint_postgres.PostgresSaver.html)
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Persistent Memory**: The agent stores chat history in a PostgreSQL database, allowing it to remember past interactions across sessions.
|
||||
- **Seamless Integration**: Designed to integrate smoothly with existing setups.
|
||||
- **Scalable Solution**: Ideal for applications requiring long-term memory capabilities.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To use this feature, ensure you have the following:
|
||||
|
||||
1. **PostgreSQL Database URL**: Create and host ur PostgreSQL databse and enter the URL. It will be of the format "postgresql://user:password@localhost:5432/db"
|
||||
|
||||
## Without persistence
|
||||
```
|
||||
Available modes:
|
||||
1. chat
|
||||
- Interactive chat mode
|
||||
2. auto
|
||||
- Autonomous action mode
|
||||
Choose a mode (enter number or name: 1
|
||||
Starting chat mode... Type 'exit' to end.
|
||||
Prompt: i am arpit
|
||||
Hello Arpit! How can I assist you today?
|
||||
Prompt: ^С
|
||||
® arpitsingh Mac persistent-agent & ts-node index.ts
|
||||
Starting Agent...
|
||||
Available modes:
|
||||
1. chat
|
||||
- Interactive chat mode
|
||||
2. auto
|
||||
- Autonomous action mode
|
||||
Choose a mode (enter number or name): 1
|
||||
Starting chat mode... Type 'exit' to end.
|
||||
Prompt: do u know my name
|
||||
I don't know your name yet. If you'd like, you can share it.
|
||||
```
|
||||
## With persistence
|
||||
```
|
||||
Available modes:
|
||||
1. chat
|
||||
- Interactive chat mode
|
||||
2. auto
|
||||
- Autonomous action mode
|
||||
Choose a mode (enter number or name: 1
|
||||
Starting chat mode... Type 'exit' to end.
|
||||
Prompt: i am arpit
|
||||
Hello Arpit! How can I assist you today?
|
||||
Prompt: ^С
|
||||
® arpitsingh Mac persistent-agent & ts-node index.ts
|
||||
Starting Agent...
|
||||
Available modes:
|
||||
1. chat
|
||||
- Interactive chat mode
|
||||
2. auto
|
||||
- Autonomous action mode
|
||||
Choose a mode (enter number or name): 1
|
||||
Starting chat mode... Type 'exit' to end.
|
||||
Prompt: do u know my name
|
||||
Yes, you mentioned that your name is Arpit. How can I help you today?
|
||||
```
|
||||
|
||||
223
examples/persistent-agent/index.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
|
||||
import { HumanMessage } from "@langchain/core/messages";
|
||||
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
||||
import { ChatOpenAI } from "@langchain/openai";
|
||||
import * as dotenv from "dotenv";
|
||||
import * as fs from "fs";
|
||||
import * as readline from "readline";
|
||||
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
|
||||
dotenv.config();
|
||||
|
||||
const checkpointer = PostgresSaver.fromConnString(process.env.POSTGRES_DB_URL!);
|
||||
|
||||
function validateEnvironment(): void {
|
||||
const missingVars: string[] = [];
|
||||
const requiredVars = [
|
||||
"OPENAI_API_KEY",
|
||||
"RPC_URL",
|
||||
"SOLANA_PRIVATE_KEY",
|
||||
"POSTGRES_DB_URL",
|
||||
];
|
||||
|
||||
requiredVars.forEach((varName) => {
|
||||
if (!process.env[varName]) {
|
||||
missingVars.push(varName);
|
||||
}
|
||||
});
|
||||
|
||||
if (missingVars.length > 0) {
|
||||
console.error("Error: Required environment variables are not set");
|
||||
missingVars.forEach((varName) => {
|
||||
console.error(`${varName}=your_${varName.toLowerCase()}_here`);
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
validateEnvironment();
|
||||
|
||||
const WALLET_DATA_FILE = "wallet_data.txt";
|
||||
|
||||
async function initializeAgent() {
|
||||
try {
|
||||
const llm = new ChatOpenAI({
|
||||
modelName: "gpt-4o-mini",
|
||||
temperature: 0.7,
|
||||
});
|
||||
|
||||
let walletDataStr: string | null = null;
|
||||
|
||||
if (fs.existsSync(WALLET_DATA_FILE)) {
|
||||
try {
|
||||
walletDataStr = fs.readFileSync(WALLET_DATA_FILE, "utf8");
|
||||
} catch (error) {
|
||||
console.error("Error reading wallet data:", error);
|
||||
}
|
||||
}
|
||||
|
||||
const solanaAgent = new SolanaAgentKit(
|
||||
process.env.SOLANA_PRIVATE_KEY!,
|
||||
process.env.RPC_URL,
|
||||
process.env.OPENAI_API_KEY!,
|
||||
);
|
||||
|
||||
const tools = createSolanaTools(solanaAgent);
|
||||
await checkpointer.setup();
|
||||
const config = { configurable: { thread_id: "Solana Agent Kit!" } };
|
||||
|
||||
const agent = createReactAgent({
|
||||
llm,
|
||||
tools,
|
||||
checkpointSaver: checkpointer,
|
||||
messageModifier: `
|
||||
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
|
||||
empowered to interact onchain using your tools. If you ever need funds, you can request them from the
|
||||
faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX
|
||||
(internal) HTTP error code, ask the user to try again later. If someone asks you to do something you
|
||||
can't do with your currently available tools, you must say so, and encourage them to implement it
|
||||
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information. Be
|
||||
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested.
|
||||
`,
|
||||
});
|
||||
|
||||
if (walletDataStr) {
|
||||
fs.writeFileSync(WALLET_DATA_FILE, walletDataStr);
|
||||
}
|
||||
|
||||
return { agent, config };
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize agent:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function runAutonomousMode(agent: any, config: any, interval = 10) {
|
||||
console.log("Starting autonomous mode...");
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
const thought =
|
||||
"Be creative and do something interesting on the blockchain. " +
|
||||
"Choose an action or set of actions and execute it that highlights your abilities.";
|
||||
|
||||
const stream = await agent.stream(
|
||||
{ messages: [new HumanMessage(thought)] },
|
||||
config,
|
||||
);
|
||||
|
||||
for await (const chunk of stream) {
|
||||
if ("agent" in chunk) {
|
||||
console.log(chunk.agent.messages[0].content);
|
||||
} else if ("tools" in chunk) {
|
||||
console.log(chunk.tools.messages[0].content);
|
||||
}
|
||||
console.log("-------------------");
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error("Error:", error.message);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function runChatMode(agent: any, config: any) {
|
||||
console.log("Starting chat mode... Type 'exit' to end.");
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
const question = (prompt: string): Promise<string> =>
|
||||
new Promise((resolve) => rl.question(prompt, resolve));
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const userInput = await question("\nPrompt: ");
|
||||
|
||||
if (userInput.toLowerCase() === "exit") {
|
||||
break;
|
||||
}
|
||||
|
||||
const stream = await agent.stream(
|
||||
{ messages: [new HumanMessage(userInput)] },
|
||||
config,
|
||||
);
|
||||
|
||||
for await (const chunk of stream) {
|
||||
if ("agent" in chunk) {
|
||||
console.log(chunk.agent.messages[0].content);
|
||||
} else if ("tools" in chunk) {
|
||||
console.log(chunk.tools.messages[0].content);
|
||||
}
|
||||
console.log("-------------------");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error("Error:", error.message);
|
||||
}
|
||||
process.exit(1);
|
||||
} finally {
|
||||
rl.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function chooseMode(): Promise<"chat" | "auto"> {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
const question = (prompt: string): Promise<string> =>
|
||||
new Promise((resolve) => rl.question(prompt, resolve));
|
||||
|
||||
while (true) {
|
||||
console.log("\nAvailable modes:");
|
||||
console.log("1. chat - Interactive chat mode");
|
||||
console.log("2. auto - Autonomous action mode");
|
||||
|
||||
const choice = (await question("\nChoose a mode (enter number or name): "))
|
||||
.toLowerCase()
|
||||
.trim();
|
||||
|
||||
rl.close();
|
||||
|
||||
if (choice === "1" || choice === "chat") {
|
||||
return "chat";
|
||||
} else if (choice === "2" || choice === "auto") {
|
||||
return "auto";
|
||||
}
|
||||
console.log("Invalid choice. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
console.log("Starting Agent...");
|
||||
const { agent, config } = await initializeAgent();
|
||||
const mode = await chooseMode();
|
||||
|
||||
if (mode === "chat") {
|
||||
await runChatMode(agent, config);
|
||||
} else {
|
||||
await runAutonomousMode(agent, config);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error("Error:", error.message);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch((error) => {
|
||||
console.error("Fatal error:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
16
examples/persistent-agent/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "persistance-agent",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@langchain/langgraph-checkpoint-postgres": "^0.0.2",
|
||||
"solana-agent-kit": "^1.3.0"
|
||||
}
|
||||
}
|
||||
3095
examples/persistent-agent/pnpm-lock.yaml
generated
Normal file
4
examples/tg-bot-starter/.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
OPENAI_API_KEY=
|
||||
RPC_URL=
|
||||
SOLANA_PRIVATE_KEY=
|
||||
TELEGRAM_BOT_TOKEN=
|
||||
41
examples/tg-bot-starter/.gitignore
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
24
examples/tg-bot-starter/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Telegram Bot Starter with Solana Agent Kit
|
||||
|
||||
This example showcases how we can make a telegram bot with the Solana Agent Kit by Send AI.
|
||||
|
||||
## Quick Deploy
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fsendaifun%2Fsolana-agent-kit%2Ftree%2Fmain%2Fexamples%2Ftg-bot-starter&env=OPENAI_API_KEY,RPC_URL,SOLANA_PRIVATE_KEY,TELEGRAM_BOT_TOKEN&project-name=solana-agent-kit&repository-name=sak-yourprojectname)
|
||||
|
||||
## How to get the telegram bot token
|
||||
|
||||
You can check [here](https://help.zoho.com/portal/en/kb/desk/support-channels/instant-messaging/telegram/articles/telegram-integration-with-zoho-desk#How_to_find_a_token_for_an_existing_Telegram_Bot) how you can obtain a bot token for your telegram bot.
|
||||
|
||||
## How to setup the project
|
||||
|
||||
- Set env variables
|
||||
- Run ``` pnpm install ```
|
||||
- Run ``` pnpm run dev ```
|
||||
- Run ``` ngrok http 3000 ```
|
||||
- With the URL you got from ngrok, where your bot is hosted at https://yourUrl.app/api/bot
|
||||
- Set the webhook by using this command ``` curl https://api.telegram.org/bot<telegram_bot_token>/setWebhook?url=https://<your-deployment-url>.app/api/bot ``` or simply clicking on that link.
|
||||
- You can host it on Vercel too as we have used NextJs in this.
|
||||
- Once the URL is set successfully, you will see this ``` {"ok":true,"result":true,"description":"Webhook was set"} ```
|
||||
|
||||
Done!!! Congratulations you just hosted Solana Agent Kit on a Telegram bot.
|
||||
|
||||
7
examples/tg-bot-starter/next.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
31
examples/tg-bot-starter/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "tg-bot-starter",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@langchain/core": "^0.3.26",
|
||||
"@langchain/langgraph": "^0.2.36",
|
||||
"@langchain/openai": "^0.3.16",
|
||||
"grammy": "^1.33.0",
|
||||
"messages": "link:@langchain/core/messages",
|
||||
"next": "15.1.3",
|
||||
"prebuilt": "link:@langchain/langgraph/prebuilt",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"solana-agent-kit": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
4296
examples/tg-bot-starter/pnpm-lock.yaml
generated
Normal file
8
examples/tg-bot-starter/postcss.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
1
examples/tg-bot-starter/public/file.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
1
examples/tg-bot-starter/public/globe.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
examples/tg-bot-starter/public/next.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
examples/tg-bot-starter/public/vercel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 128 B |
1
examples/tg-bot-starter/public/window.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||
|
After Width: | Height: | Size: 385 B |
103
examples/tg-bot-starter/src/app/api/bot/route.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
export const dynamic = "force-dynamic";
|
||||
export const fetchCache = "force-no-store";
|
||||
export const maxDuration = 300;
|
||||
|
||||
import { Bot, webhookCallback } from "grammy";
|
||||
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
|
||||
import { ChatOpenAI } from "@langchain/openai";
|
||||
import { MemorySaver } from "@langchain/langgraph";
|
||||
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
||||
import { HumanMessage } from "@langchain/core/messages";
|
||||
|
||||
const token = process.env.TELEGRAM_BOT_TOKEN;
|
||||
if (!token) {
|
||||
throw new Error("TELEGRAM_BOT_TOKEN environment variable not found.");
|
||||
}
|
||||
const bot = new Bot(token);
|
||||
|
||||
async function initializeAgent(userId: string) {
|
||||
try {
|
||||
const llm = new ChatOpenAI({
|
||||
modelName: "gpt-4o-mini",
|
||||
temperature: 0.7,
|
||||
});
|
||||
|
||||
const solanaKit = new SolanaAgentKit(
|
||||
process.env.SOLANA_PRIVATE_KEY!,
|
||||
process.env.RPC_URL,
|
||||
process.env.OPENAI_API_KEY!,
|
||||
);
|
||||
|
||||
const tools = createSolanaTools(solanaKit);
|
||||
const memory = new MemorySaver();
|
||||
const config = { configurable: { thread_id: userId } };
|
||||
const agent = createReactAgent({
|
||||
llm,
|
||||
tools,
|
||||
checkpointSaver: memory,
|
||||
messageModifier: `
|
||||
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
|
||||
empowered to interact onchain using your tools. If you ever need funds, you can request them from the
|
||||
faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX
|
||||
(internal) HTTP error code, ask the user to try again later. If someone asks you to do something you
|
||||
can't do with your currently available tools, you must say so, and encourage them to implement it
|
||||
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information. Be
|
||||
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested.
|
||||
`,
|
||||
});
|
||||
return { agent, config };
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize agent:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// Telegram bot handler
|
||||
bot.on("message:text", async (ctx: any) => {
|
||||
const userId = ctx.from?.id.toString();
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
const { agent, config } = await initializeAgent(userId);
|
||||
const stream = await agent.stream(
|
||||
{ messages: [new HumanMessage(ctx.message.text)] },
|
||||
config,
|
||||
);
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error("Timeout")), 20000),
|
||||
);
|
||||
try {
|
||||
for await (const chunk of (await Promise.race([
|
||||
stream,
|
||||
timeoutPromise,
|
||||
])) as AsyncIterable<{ agent?: any; tools?: any }>) {
|
||||
if ("agent" in chunk) {
|
||||
if (chunk.agent.messages[0].content) {
|
||||
await ctx.reply(String(chunk.agent.messages[0].content));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.message === "Timeout") {
|
||||
await ctx.reply(
|
||||
"I'm sorry, the operation took too long and timed out. Please try again.",
|
||||
);
|
||||
} else {
|
||||
console.error("Error processing stream:", error);
|
||||
await ctx.reply(
|
||||
"I'm sorry, an error occurred while processing your request.",
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Export webhook handler
|
||||
export const POST = async (req: Request) => {
|
||||
// Mark the function as a background function for Vercel
|
||||
const headers = new Headers();
|
||||
headers.set("x-vercel-background", "true");
|
||||
|
||||
const handler = webhookCallback(bot, "std/http"); // Use the correct callback
|
||||
|
||||
// Handle the incoming webhook request
|
||||
return handler(req);
|
||||
};
|
||||
BIN
examples/tg-bot-starter/src/app/favicon.ico
Normal file
|
After Width: | Height: | Size: 25 KiB |
21
examples/tg-bot-starter/src/app/globals.css
Normal file
@@ -0,0 +1,21 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
34
examples/tg-bot-starter/src/app/layout.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
101
examples/tg-bot-starter/src/app/page.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
||||
src/app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>Save and see your changes instantly.</li>
|
||||
</ol>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
18
examples/tg-bot-starter/tailwind.config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
export default {
|
||||
content: [
|
||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
background: "var(--background)",
|
||||
foreground: "var(--foreground)",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
} satisfies Config;
|
||||
27
examples/tg-bot-starter/tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -18,7 +18,7 @@ Extending the **Solana Agent Kit** with custom tools allows you to add specializ
|
||||
Create a new TypeScript file in the `src/tools/` directory for your tool (e.g., `custom_tool.ts`).
|
||||
|
||||
### 2. Implement the Tool Class
|
||||
|
||||
> `src/langchain/index.ts`
|
||||
```typescript:src/langchain/index.ts
|
||||
import { Tool } from "langchain/tools";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
@@ -51,7 +51,7 @@ export class CustomTool extends Tool {
|
||||
```
|
||||
|
||||
### 3. Add Supporting Functions to SolanaAgentKit
|
||||
|
||||
> `src/agent/index.ts`
|
||||
```typescript:src/agent/index.ts
|
||||
export class SolanaAgentKit {
|
||||
// ... existing code ...
|
||||
@@ -64,7 +64,7 @@ export class SolanaAgentKit {
|
||||
```
|
||||
|
||||
### 4. Export the Tool
|
||||
|
||||
> `src/tools/index.ts`
|
||||
```typescript:src/tools/index.ts
|
||||
export * from "./request_faucet_funds";
|
||||
export * from "./deploy_token";
|
||||
@@ -72,7 +72,7 @@ export * from "./custom_tool"; // Add your new tool
|
||||
```
|
||||
|
||||
### 5. Integrate with Agent
|
||||
|
||||
> `src/langchain/index.ts`
|
||||
```typescript:src/langchain/index.ts
|
||||
import { CustomTool } from "../tools/custom_tool";
|
||||
|
||||
@@ -104,6 +104,10 @@ if (customTool) {
|
||||
const result = await customTool._call("your-input");
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
// or alternatively
|
||||
const result = await agent.customFunction("your-input"); // assuming you have a `customFunction` method in SolanaAgentKit
|
||||
console.log(result);
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
@@ -117,7 +121,7 @@ if (customTool) {
|
||||
## Example: Token Price Fetching Tool
|
||||
|
||||
Here's a complete example of implementing a tool to fetch token prices:
|
||||
|
||||
> `src/tools/fetch_token_price.ts`
|
||||
```typescript:src/tools/fetch_token_price.ts
|
||||
import { Tool } from "langchain/tools";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
@@ -150,7 +154,7 @@ export class FetchTokenPriceTool extends Tool {
|
||||
```
|
||||
|
||||
Add the supporting function to SolanaAgentKit:
|
||||
|
||||
> `src/agent/index.ts`
|
||||
```typescript:src/agent/index.ts
|
||||
export class SolanaAgentKit {
|
||||
async getTokenPrice(tokenSymbol: string): Promise<number> {
|
||||
@@ -170,6 +174,21 @@ export class SolanaAgentKit {
|
||||
}
|
||||
```
|
||||
|
||||
Then it can be used as such:
|
||||
|
||||
```typescript
|
||||
import { SolanaAgentKit } from "solana-agent-kit";
|
||||
|
||||
const agent = new SolanaAgentKit(
|
||||
"your-wallet-private-key-as-base58",
|
||||
"https://api.mainnet-beta.solana.com",
|
||||
"your-openai-api-key"
|
||||
);
|
||||
|
||||
const result = await agent.getTokenPrice("SOL");
|
||||
console.log(result);
|
||||
```
|
||||
|
||||
## Need Help?
|
||||
|
||||
If you encounter any issues while implementing your custom tool:
|
||||
|
||||
@@ -49,12 +49,12 @@ The project includes a test script located at `test/index.ts`. To execute the te
|
||||
|
||||
### Token Deployment
|
||||
```typescript
|
||||
import { deploy_token } from "solana-agent-kit";
|
||||
import { SolanaAgentKit } from "solana-agent-kit";
|
||||
|
||||
const result = await deploy_token(
|
||||
agent,
|
||||
9, // decimals
|
||||
1000000 // initial supply
|
||||
const agent = new SolanaAgentKit("your_private_key");
|
||||
|
||||
const result = await agent.deployToken(
|
||||
9, // decimals
|
||||
);
|
||||
|
||||
console.log("Token Mint Address:", result.mint.toString());
|
||||
@@ -62,9 +62,11 @@ console.log("Token Mint Address:", result.mint.toString());
|
||||
|
||||
### NFT Collection Creation
|
||||
```typescript
|
||||
import { deploy_collection } from "solana-agent-kit";
|
||||
import { SolanaAgentKit } from "solana-agent-kit";
|
||||
|
||||
const collection = await deploy_collection(agent, {
|
||||
const agent = new SolanaAgentKit("your_private_key");
|
||||
|
||||
const collection = await agent.deployCollection({
|
||||
name: "My NFT Collection",
|
||||
uri: "https://arweave.net/metadata.json",
|
||||
royaltyBasisPoints: 500, // 5%
|
||||
|
||||
35
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "solana-agent-kit",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.2",
|
||||
"description": "connect any ai agents to solana protocols",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -14,7 +14,7 @@
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\""
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=23.1.0",
|
||||
"node": ">=22.0.0",
|
||||
"pnpm": ">=8.0.0"
|
||||
},
|
||||
"keywords": [],
|
||||
@@ -22,11 +22,12 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@bonfida/spl-name-service": "^3.0.7",
|
||||
"@cks-systems/manifest-sdk": "^0.1.73",
|
||||
"@coral-xyz/anchor": "0.29",
|
||||
"@langchain/core": "^0.3.18",
|
||||
"@langchain/core": "^0.3.26",
|
||||
"@langchain/groq": "^0.1.2",
|
||||
"@langchain/langgraph": "^0.2.27",
|
||||
"@langchain/openai": "^0.3.13",
|
||||
"@langchain/langgraph": "^0.2.36",
|
||||
"@langchain/openai": "^0.3.16",
|
||||
"@lightprotocol/compressed-token": "^0.17.1",
|
||||
"@lightprotocol/stateless.js": "^0.17.1",
|
||||
"@metaplex-foundation/mpl-core": "^1.1.1",
|
||||
@@ -41,28 +42,30 @@
|
||||
"@pythnetwork/price-service-client": "^1.9.0",
|
||||
"@raydium-io/raydium-sdk-v2": "0.1.95-alpha",
|
||||
"@solana/spl-token": "^0.4.9",
|
||||
"@solana/web3.js": "^1.95.4",
|
||||
"@tensor-oss/tensorswap-sdk": "^4.5.0",
|
||||
"@solana/web3.js": "^1.98.0",
|
||||
"@tiplink/api": "^0.3.1",
|
||||
"bn.js": "^5.2.1",
|
||||
"bs58": "^6.0.0",
|
||||
"chai": "^5.1.2",
|
||||
"decimal.js": "^10.4.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"form-data": "^4.0.1",
|
||||
"langchain": "^0.3.6",
|
||||
"openai": "^4.75.0",
|
||||
"typedoc": "^0.26.11",
|
||||
"langchain": "^0.3.8",
|
||||
"openai": "^4.77.0",
|
||||
"typedoc": "^0.27.6",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^5.1.5",
|
||||
"@types/bn.js": "^5.1.6",
|
||||
"@types/chai": "^5.0.1",
|
||||
"@types/node": "^22.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"@types/node": "^22.10.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.18.2",
|
||||
"@typescript-eslint/parser": "^8.18.2",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"prettier": "^3.2.5",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"prettier": "^3.4.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.7.2"
|
||||
}
|
||||
|
||||