Skip to content

Latest commit

 

History

History
1878 lines (1496 loc) · 50.5 KB

File metadata and controls

1878 lines (1496 loc) · 50.5 KB

LICENSE

MIT License

Copyright (c) 2024 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.


README.md

# New LangGraph.js Project

[![CI](https://github.com/langchain-ai/new-langgraphjs-project/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/langchain-ai/new-langgraphjs-project/actions/workflows/unit-tests.yml)
[![Integration Tests](https://github.com/langchain-ai/new-langgraphjs-project/actions/workflows/integration-tests.yml/badge.svg)](https://github.com/langchain-ai/new-langgraphjs-project/actions/workflows/integration-tests.yml)
[![Open in - LangGraph Studio](https://img.shields.io/badge/Open_in-LangGraph_Studio-00324d.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NS4zMzMiIGhlaWdodD0iODUuMzMzIiB2ZXJzaW9uPSIxLjAiIHZpZXdCb3g9IjAgMCA2NCA2NCI+PHBhdGggZD0iTTEzIDcuOGMtNi4zIDMuMS03LjEgNi4zLTYuOCAyNS43LjQgMjQuNi4zIDI0LjUgMjUuOSAyNC41QzU3LjUgNTggNTggNTcuNSA1OCAzMi4zIDU4IDcuMyA1Ni43IDYgMzIgNmMtMTIuOCAwLTE2LjEuMy0xOSAxLjhtMzcuNiAxNi42YzIuOCAyLjggMy40IDQuMiAzLjQgNy42cy0uNiA0LjgtMy40IDcuNkw0Ny4yIDQzSDE2LjhsLTMuNC0zLjRjLTQuOC00LjgtNC44LTEwLjQgMC0xNS4ybDMuNC0zLjRoMzAuNHoiLz48cGF0aCBkPSJNMTguOSAyNS42Yy0xLjEgMS4zLTEgMS43LjQgMi41LjkuNiAxLjcgMS44IDEuNyAyLjcgMCAxIC43IDIuOCAxLjYgNC4xIDEuNCAxLjkgMS40IDIuNS4zIDMuMi0xIC42LS42LjkgMS40LjkgMS41IDAgMi43LS41IDIuNy0xIDAtLjYgMS4xLS44IDIuNi0uNGwyLjYuNy0xLjgtMi45Yy01LjktOS4zLTkuNC0xMi4zLTExLjUtOS44TTM5IDI2YzAgMS4xLS45IDIuNS0yIDMuMi0yLjQgMS41LTIuNiAzLjQtLjUgNC4yLjguMyAyIDEuNyAyLjUgMy4xLjYgMS41IDEuNCAyLjMgMiAyIDEuNS0uOSAxLjItMy41LS40LTMuNS0yLjEgMC0yLjgtMi44LS44LTMuMyAxLjYtLjQgMS42LS41IDAtLjYtMS4xLS4xLTEuNS0uNi0xLjItMS42LjctMS43IDMuMy0yLjEgMy41LS41LjEuNS4yIDEuNi4zIDIuMiAwIC43LjkgMS40IDEuOSAxLjYgMi4xLjQgMi4zLTIuMy4yLTMuMi0uOC0uMy0yLTEuNy0yLjUtMy4xLTEuMS0zLTMtMy4zLTMtLjUiLz48L3N2Zz4=)](https://langgraph-studio.vercel.app/templates/open?githubUrl=https://github.com/langchain-ai/new-langgraphjs-project)

This template demonstrates a simple chatbot implemented using [LangGraph.js](https://github.com/langchain-ai/langgraphjs), designed for [LangGraph Studio](https://github.com/langchain-ai/langgraph-studio). The chatbot maintains persistent chat memory, allowing for coherent conversations across multiple interactions.

![Graph view in LangGraph studio UI](./static/studio.png)

The core logic, defined in `src/agent/graph.ts`, showcases a straightforward chatbot that responds to user queries while maintaining context from previous messages.

## What it does

The simple chatbot:

1. Takes a user **message** as input
2. Maintains a history of the conversation
3. Returns a placeholder response, updating the conversation history

This template provides a foundation that can be easily customized and extended to create more complex conversational agents.

## Getting Started

Assuming you have already [installed LangGraph Studio](https://github.com/langchain-ai/langgraph-studio?tab=readme-ov-file#download), to set up:

1. Create a `.env` file. This template does not require any environment variables by default, but you will likely want to add some when customizing.

```bash
cp .env.example .env
  1. Open the folder in LangGraph Studio!
  2. Customize the code as needed.

How to customize

  1. Add an LLM call: You can select and install a chat model wrapper from the LangChain.js ecosystem, or use LangGraph.js without LangChain.js.
  2. Extend the graph: The core logic of the chatbot is defined in graph.ts. You can modify this file to add new nodes, edges, or change the flow of the conversation.

You can also extend this template by:

  • Adding custom tools or functions to enhance the chatbot's capabilities.
  • Implementing additional logic for handling specific types of user queries or tasks.
  • Add retrieval-augmented generation (RAG) capabilities by integrating external APIs or databases to provide more customized responses.

Development

While iterating on your graph, you can edit past state and rerun your app from previous states to debug specific nodes. Local changes will be automatically applied via hot reload. Try experimenting with:

  • Modifying the system prompt to give your chatbot a unique personality.
  • Adding new nodes to the graph for more complex conversation flows.
  • Implementing conditional logic to handle different types of user inputs.

Follow-up requests will be appended to the same thread. You can create an entirely new thread, clearing previous history, using the + button in the top right.

For more advanced features and examples, refer to the LangGraph.js documentation. These resources can help you adapt this template for your specific use case and build more sophisticated conversational agents.

LangGraph Studio also integrates with LangSmith for more in-depth tracing and collaboration with teammates, allowing you to analyze and optimize your chatbot's performance.

solgent


---

# condense.config.json

{ "ignoreFolders": [ "/build", "/temp", "/logs", "node_modules", "/dist", "/coverage", "/.git", "/.idea", "/.vscode", "/vendor", "/bin", "/obj", "/out", "/target", "/cache", "/tmp", "package-lock.json", "yarn.lock" ] }


---

# jest.config.js

export default { preset: "ts-jest/presets/default-esm", moduleNameMapper: { "^(\.{1,2}/.*)\.js$": "$1", }, transform: { "^.+\.tsx?$": [ "ts-jest", { useESM: true, }, ], }, extensionsToTreatAsEsm: [".ts"], setupFiles: ["dotenv/config"], passWithNoTests: true, testTimeout: 20_000, };


---

# langgraph.json

{ "node_version": "20", "graphs": { "agent": "./src/index.ts:graph" }, "env": ".env", "dependencies": ["."] }


---

# package.json

{ "name": "example-graph", "version": "0.0.1", "description": "A starter template for creating a LangGraph workflow.", "packageManager": "yarn@1.22.22", "main": "my_app/graph.ts", "author": "Your Name", "license": "MIT", "private": true, "type": "module", "scripts": { "build": "tsc", "clean": "rm -rf dist", "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --testPathPattern=\.test\.ts$ --testPathIgnorePatterns=\.int\.test\.ts$", "test:int": "node --experimental-vm-modules node_modules/jest/bin/jest.js --testPathPattern=\.int\.test\.ts$", "format": "prettier --write .", "lint": "eslint src", "format:check": "prettier --check .", "lint:langgraph-json": "node scripts/checkLanggraphPaths.js", "lint:all": "yarn lint & yarn lint:langgraph-json & yarn format:check", "test:all": "yarn test && yarn test:int && yarn lint:langgraph" }, "dependencies": { "@langchain/anthropic": "^0.3.9", "@langchain/community": "^0.3.19", "@langchain/core": "^0.3.2", "@langchain/langgraph": "^0.2.5", "@langchain/openai": "^0.3.14", "@langchain/xai": "^0.0.1", "@solana/web3.js": "^1.95.8", "fuels": "^0.62.0", "solana-agent-kit": "^1.1.0" }, "devDependencies": { "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.9.1", "@tsconfig/recommended": "^1.0.7", "@types/jest": "^29.5.0", "@typescript-eslint/eslint-plugin": "^5.59.8", "@typescript-eslint/parser": "^5.59.8", "dotenv": "^16.4.5", "eslint": "^8.41.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-no-instanceof": "^1.0.1", "eslint-plugin-prettier": "^4.2.1", "jest": "^29.7.0", "prettier": "^3.3.3", "repo-condenser": "^1.0.2", "ts-jest": "^29.1.0", "typescript": "^5.3.3" } }


---

# scripts/checkLanggraphPaths.js

import fs from "fs"; import path from "path"; import { fileURLToPath } from "url";

// Function to check if a file exists function fileExists(filePath) { return fs.existsSync(filePath); }

// Function to check if an object is exported from a file function isObjectExported(filePath, objectName) { try { const fileContent = fs.readFileSync(filePath, "utf8"); const exportRegex = new RegExp( export\\s+(?:const|let|var)\\s+${objectName}\\s*=|export\\s+\\{[^}]*\\b${objectName}\\b[^}]*\\}, ); return exportRegex.test(fileContent); } catch (error) { console.error(Error reading file ${filePath}: ${error.message}); return false; } }

// Main function to check langgraph.json function checkLanggraphPaths() { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const langgraphPath = path.join(__dirname, "..", "langgraph.json");

if (!fileExists(langgraphPath)) { console.error("langgraph.json not found in the root directory"); process.exit(1); }

try { const langgraphContent = JSON.parse(fs.readFileSync(langgraphPath, "utf8")); const graphs = langgraphContent.graphs;

if (!graphs || typeof graphs !== "object") {
  console.error('Invalid or missing "graphs" object in langgraph.json');
  process.exit(1);
}

let hasError = false;

for (const [key, value] of Object.entries(graphs)) {
  const [filePath, objectName] = value.split(":");
  const fullPath = path.join(__dirname, "..", filePath);

  if (!fileExists(fullPath)) {
    console.error(`File not found: ${fullPath}`);
    hasError = true;
    continue;
  }

  if (!isObjectExported(fullPath, objectName)) {
    console.error(
      `Object "${objectName}" is not exported from ${fullPath}`,
    );
    hasError = true;
  }
}

if (hasError) {
  process.exit(1);
} else {
  console.log(
    "All paths in langgraph.json are valid and objects are exported correctly.",
  );
}

} catch (error) { console.error(Error parsing langgraph.json: ${error.message}); process.exit(1); } }

checkLanggraphPaths();


---

# src/agent/cheif.ts

import "dotenv/config"; import { RunnableSequence } from "@langchain/core/runnables"; import { solanaAgentState } from "../utils/state.js"; import { prompt, parser } from "../prompts/chief.js"; import { gptModel } from "../utils/model.js";

const chain = RunnableSequence.from([prompt, gptModel, parser]);

export const cheifNode = async (state: typeof solanaAgentState.State) => { const { messages } = state;

const result = await chain.invoke({ formatInstructions: parser.getFormatInstructions(), messages: messages, });

const { isTechnicalQuery, isDefiQuery, isGeneralQuestion } = result;

return { isGeneralQuestion, isDefiQuery, isTechnicalQuery }; };

// const example = "how do i add a transaction in a solana smart contract?";

// const result = await chain.invoke({ // messages: example, // formatInstructions: parser.getFormatInstructions(), // });

// console.log({ result });


---

# src/agent/defi/defiManager.ts

import "dotenv/config"; import { RunnableSequence } from "@langchain/core/runnables"; import { tokenList } from "../../helpers/tokens.js"; import { gptMiniModel, gptModel } from "../../utils/model.js"; import { solanaAgentState } from "../../utils/state.js"; import { defiPrompt, defiParser } from "../../prompts/defi/defiManager.js";

const chain = RunnableSequence.from([ { messages: (input) => input.messages, formatInstructions: () => defiParser.getFormatInstructions(), }, defiPrompt, gptModel, defiParser, ]);

export const defiNode = async (state: typeof solanaAgentState.State) => { const { messages } = state; const tokenListStringified = JSON.stringify(tokenList);

const result = await chain.invoke({ messages: messages, token_list: tokenListStringified, });

// Update the state with the classification result return { defiOptions: { isSwap: result.isSwap, isTransfer: result.isTransfer, isStaking: result.isStaking, isTokenLaunch: result.isTokenLaunch, isNFTLaunch: result.isNFTLaunch, isPumpFun: result.isPumpFun, }, }; };


---

# src/agent/defi/defiToolNode.ts

import { ToolNode } from "@langchain/langgraph/prebuilt"; import { solana_transfer } from "../../tools/defi/transfer.js"; import { swap_token } from "../../tools/defi/swap.js"; import { mint_shit_coin } from "../../tools/defi/pumpFun.js";

export const defiTools = [solana_transfer, swap_token, mint_shit_coin];

export const defiToolsNode = new ToolNode(defiTools);

export const transferSwapTools = [solana_transfer, swap_token]; export const transferSwapToolsNode = new ToolNode(transferSwapTools);

export const pumpfunTools = [mint_shit_coin]; export const pumpfunToolsNode = new ToolNode(pumpfunTools);

export const lendingTools = []; //todo export const lendingToolsNode = new ToolNode(lendingTools);

export const nftTools = []; //todo export const nftToolsNode = new ToolNode(nftTools);


---

# src/agent/defi/lendingAgent.ts

import "dotenv/config"; import { RunnableSequence } from "@langchain/core/runnables"; import { tokenList } from "../../helpers/tokens.js"; import { gptModel } from "../../utils/model.js"; import { solanaAgentState } from "../../utils/state.js"; import { lendingPrompt } from "../../prompts/defi/lending.js"; import { lendingTools } from "../../tools/defi/lendTokens.js"; import { createReactAgent } from "@langchain/langgraph/prebuilt";

const lendingGraph = createReactAgent({ llm: gptModel, tools: lendingTools, stateModifier: lendingPrompt, });

export const lendingNode = async (state: typeof solanaAgentState.State) => { const { messages } = state; const tokenListStringified = JSON.stringify(tokenList);

const result = await lendingGraph.invoke({ messages: messages, token_list: tokenListStringified, });

return { messages: [...result.messages] }; };


---

# src/agent/defi/nftAgent.ts


---

# src/agent/defi/pumpfunAgent.ts

import "dotenv/config"; import { gptModel } from "../../utils/model.js"; import { solanaAgentState } from "../../utils/state.js"; import { pumpfunPrompt } from "../../prompts/defi/pumpfun.js"; import { pumpfunTools } from "./defiToolNode.js"; import { createReactAgent } from "@langchain/langgraph/prebuilt";

const pumpfunGraph = createReactAgent({ llm: gptModel, tools: pumpfunTools, stateModifier: pumpfunPrompt, });

export const pumpfunNode = async (state: typeof solanaAgentState.State) => { const { messages } = state;

const result = await pumpfunGraph.invoke({ messages: messages, });

return { messages: [...result.messages] }; };

// const result = await pumpfunGraph.invoke({ // messages: // "Create a token called $MCAT with the description 'The first feline-powered cryptocurrency that promises to take your investments to the moon!' and the image prompt 'A cartoon cat wearing an astronaut helmet, sitting on the moon with Earth visible in the background, digital art style'", // });

// console.log({ result });


---

# src/agent/defi/transferSwapAgent.ts

import "dotenv/config"; import { tokenList } from "../../helpers/tokens.js"; import { gptModel } from "../../utils/model.js"; import { solanaAgentState } from "../../utils/state.js"; import { transferSwapPrompt } from "../../prompts/defi/transferSwap.js"; import { transferSwapTools } from "./defiToolNode.js"; import { createReactAgent } from "@langchain/langgraph/prebuilt";

export const transferSwapNode = async ( state: typeof solanaAgentState.State, ) => { const { messages } = state; const tokenListStringified = JSON.stringify(tokenList);

const result = await transferSwapGraph.invoke({ messages: messages, token_list: tokenListStringified, });

return { messages: [...result.messages] }; };

const transferSwapGraph = createReactAgent({ llm: gptModel, tools: transferSwapTools, stateModifier: transferSwapPrompt, });


---

# src/agent/generalist.ts

import { createReactAgent } from "@langchain/langgraph/prebuilt"; import { solanaAgentState } from "../utils/state.js"; import { gptMiniModel } from "../utils/model.js"; import { webSearchTool } from "../tools/search.js";

const reactGraph = createReactAgent({ llm: gptMiniModel, tools: [webSearchTool], });

export const generalistNode = async (state: typeof solanaAgentState.State) => { const { messages } = state;

const result = await reactGraph.invoke({ messages });

return { messages: [...result.messages] }; };

// const result = await reactGraph.invoke({ // messages: "what is the meaning of life", // });

// console.log(result.messages);


---

# src/agent/techRetreiver.ts


---

# src/examples/index.ts

import { HumanMessage } from "@langchain/core/messages";

export const swapMessage = { messages: [new HumanMessage("swap 1 USDC to SOL")], };

export const transferMessage = { messages: [ new HumanMessage( "trensfer 1000 bonk to address DZbJiJ2Uiwe3g53KBhJZ4ftdcUJGaVZNyp1ua2saguXC ", ), ], };

export const generalMessage = { messages: [new HumanMessage("who is the richest person in mexico? ")], };

export const pumpFun = { messages: [ new HumanMessage( "mint a meme coin on pump fun called catsAreGods with the ticker $CATGOD and the image of cat with an aura ", ), ], };


---

# src/helpers/tokens.ts

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", }, ];


---

# src/index.ts

import { StateGraph } from "@langchain/langgraph"; import { solanaAgentState } from "./utils/state.js"; import { defiNode } from "./agent/defi/defiManager.js"; import { transferSwapNode } from "./agent/defi/transferSwapAgent.js"; import { START, END } from "@langchain/langgraph"; import { chiefRouter } from "./router/index.js"; import { cheifNode } from "./agent/cheif.js"; import { generalistNode } from "./agent/generalist.js"; import { HumanMessage } from "@langchain/core/messages"; import { defiTeamRouter } from "./router/defi.js"; import { pumpfunNode } from "./agent/defi/pumpfunAgent.js"; import { lendingNode } from "./agent/defi/lendingAgent.js";

const workflow = new StateGraph(solanaAgentState) .addNode("defiManager", defiNode) .addNode("chief", cheifNode) .addNode("generalist", generalistNode) .addNode("transferSwap", transferSwapNode) .addNode("pumpFun", pumpfunNode) .addNode("lending", lendingNode) .addEdge(START, "chief") .addEdge("generalist", END) .addConditionalEdges("chief", chiefRouter) .addConditionalEdges("defiManager", defiTeamRouter) .addEdge("transferSwap", END) .addEdge("pumpFun", END) .addEdge("lending", END);

export const graph = workflow.compile();

export const transferMessage = { // messages: [new HumanMessage("launch a meme coi based on cats on pump.fun")], };

const result = await graph.invoke(transferMessage);

console.log(result);


---

# src/prompts/chief.ts

import { StructuredOutputParser } from "@langchain/core/output_parsers"; import { z } from "zod"; import { PromptTemplate } from "@langchain/core/prompts";

export const parser = StructuredOutputParser.fromZodSchema( z.object({ isTechnicalQuery: z .boolean() .describe("Query requires technical expertise"), isDefiQuery: z .boolean() .describe("Query is related to DeFi or financial protocols"), isGeneralQuestion: z.boolean().describe("Query is about basic concepts"), }), );

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 the most appropriate team(s) for handling the query

Format your response according to:

{formatInstructions}

Teams and their specialties:
- TECHNICAL: Handles technical implementation, coding, and infrastructure questions regarding Solana
- DEFI: Manages DeFi, trading,lending, staking and financial protocol actions like swapping , trading, creating NFTs , creating shitcoins, memecoins on Pump.Fun, staking/lending on Jupiter, lending/staking on Lulo etc
- GENERAL: Handles basic blockchain concepts and general information

Classification Guidelines:
- Technical queries include: code, smart contracts, implementation details
- DeFi queries include: trading, liquidity, yields, financial protocols
- Current affairs include: recent updates, news, network changes
- General queries include: basic concepts, terminology, blockchain fundamentals

\n {messages} \n
`,

);


---

# src/prompts/defi/defiManager.ts

import { PromptTemplate } from "@langchain/core/prompts"; import { StructuredOutputParser } from "@langchain/core/output_parsers"; import { z } from "zod";

export const defiParser = StructuredOutputParser.fromZodSchema( z.object({ isSwap: z .boolean() .describe("Query is related to token swapping or exchange"), isTransfer: z .boolean() .describe("Query is about transferring tokens or assets"), isStaking: z .boolean() .describe( "Query involves staking, lending, or yield farming (including Lulo platform operations)", ), isTokenLaunch: z .boolean() .describe("Query is about launching new tokens or cryptocurrencies"), isNFTLaunch: z .boolean() .describe("Query is about creating or launching NFTs"), isPumpFun: z .boolean() .describe("Query is specifically about Pump.Fun platform operations"), }), );

export const defiPrompt = PromptTemplate.fromTemplate( ` You are a DeFi Operations Classifier for a blockchain agent network. Your role is to: 1. Analyze incoming DeFi-related queries 2. Determine the SINGLE most relevant DeFi operation being requested 3. Set ONLY ONE category to true, all others must be false

Format your response according to:
{formatInstructions}

DeFi Operations Categories (Choose ONLY ONE):
- SWAP: Token swapping, exchange, or trading operations
- TRANSFER: Moving tokens or assets between addresses
- STAKING: Staking tokens, yield farming, or liquidity provision
- TOKEN_LAUNCH: Creating new tokens, memecoins, or cryptocurrencies
- NFT_LAUNCH: Creating or launching NFT collections
- PUMP_FUN: Operations specific to Pump.Fun platform

Detailed Classification Guidelines (Select Most Relevant ONE):
- Swap queries include: 
  * Token exchanges on DEXes
  * Trading pair inquiries
  * Price impact calculations
  * Slippage settings
  * DEX aggregator usage

- Transfer queries include:
  * Sending tokens between wallets
  * Cross-chain bridge operations
  * Asset migration
  * Batch transfers
  * Gas fee considerations

- Staking queries include:
  * Yield farming strategies
  * Liquidity pool participation
  * Staking reward calculations
  * Unstaking procedures
  * Lock-up periods
  * Lulo lending operations
  * Lulo staking activities
  * Lending market participation
  * Borrowing and lending rates
  * Collateral management

- Token Launch queries include:
  * Token creation and deployment
  * Tokenomics design
  * Initial supply settings
  * Token distribution plans
  * Smart contract deployment

- NFT Launch queries include:
  * Collection creation
  * Metadata setup
  * Minting processes
  * Royalty configurations
  * Marketplace listings

- Pump.Fun specific queries include:
  * Memecoin creation on Pump.Fun
  * Platform-specific features
  * Pump.Fun token launches
  * Platform fees and requirements
  * Pump.Fun ecosystem interactions

IMPORTANT: Choose ONLY ONE category. Set that category to true and all others to false.
If a query could fit multiple categories, choose the most specific and relevant one.
Priority order for ambiguous cases:
1. Pump.Fun (if platform-specific)
2. Token/NFT Launch (if about creation)
3. Swap/Transfer/Staking (based on main action)

\n {messages} \n
`,

);


---

# src/prompts/defi/lending.ts

import { ChatPromptTemplate, MessagesPlaceholder, } from "@langchain/core/prompts";

export const lendingPrompt = ChatPromptTemplate.fromMessages([ [ "system", `You are a DeFi tool calling agent that executes staking and lending operations using available tools. Your role is to understand user requests and call the appropriate tools.

Available Tools:
1. stake_sol_jup(amount: number) 
   - For staking SOL on Jupiter protocol
   - Returns jupSOL tokens

2. lend_usdc_lulo(amount: number) 
   - For lending USDC on Lulo protocol
   - Returns transaction signature

Tool Selection Logic:
- If user wants to stake SOL → Use stake_sol_jup
- If user wants to lend USDC → Use lend_usdc_lulo

Required Information to Call Tools:
- For stake_sol_jup:
  * Amount of SOL to stake (in SOL units)

- For lend_usdc_lulo:
  * Amount of USDC to lend (in USDC units)

Always:
1. Identify the required tool based on user input
2. Extract necessary parameters
3. Call appropriate tool with required parameters
4. Do not provide explanations or additional information
5. Only respond with tool calls`,

], new MessagesPlaceholder("messages"), ]);


---

# src/prompts/defi/nft.ts

import { PromptTemplate } from "@langchain/core/prompts"; import { StructuredOutputParser } from "langchain/output_parsers"; import { z } from "zod";

const nftSchema = z.object({ collection: z.string().describe("The NFT collection name or address"), action: z .enum(["buy", "sell", "mint"]) .describe("The action to perform with the NFT"), price: z.number().describe("The price in SOL"), quantity: z.number().describe("Number of NFTs to buy/sell/mint"), });

export const nftParser = StructuredOutputParser.fromZodSchema(nftSchema);

export const nftPrompt = PromptTemplate.fromTemplate(` You are a Solana NFT specialist assistant. Analyze the user's request and provide structured information for NFT operations.

User messages: {messages}

${nftParser.getFormatInstructions()} `);


---

# src/prompts/defi/pumpfun.ts

import { ChatPromptTemplate, MessagesPlaceholder, } from "@langchain/core/prompts";

export const pumpfunPrompt = ChatPromptTemplate.fromMessages([ [ "system", `You are an expert agent specialized in creating meme tokens and shitcoins on the Pump.Fun platform on Solana. You help users launch their creative token ideas with style and humor.

When creating a new token, you need:
1. Token Name: A creative and memorable name for the token
2. Token Ticker: A unique symbol (usually 3-6 characters)
3. Description: A fun and engaging description of the token's concept
4. Image Prompt: A detailed prompt to generate an appealing token image

Guidelines for token creation:
- Token names should be catchy and memorable
- Tickers should be related to the token name
- Descriptions should be entertaining and engaging
- Image prompts should be specific and creative

Example format:
Token Name: "Moon Cats"
Ticker: $MCAT
Description: "The first feline-powered cryptocurrency that promises to take your investments to the moon!"
Image Prompt: "A cartoon cat wearing an astronaut helmet, sitting on the moon with Earth visible in the background, digital art style"

Remember:
- Keep the tone fun and lighthearted
- Ensure all required fields are provided
- Generate engaging visual concepts
- Make each token unique and memorable`,

], new MessagesPlaceholder("messages"), ]);


---

# src/prompts/defi/transferSwap.ts

import { ChatPromptTemplate, MessagesPlaceholder, } from "@langchain/core/prompts"; import { tokenList } from "../../helpers/tokens.js";

// 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)`,

], new MessagesPlaceholder("messages"), ]);


---

# src/router/defi.ts

import { AIMessage } from "@langchain/core/messages"; import { END } from "@langchain/langgraph"; import { solanaAgentState } from "../utils/state.js";

export const defiTeamRouter = (state: typeof solanaAgentState.State) => { if (state.defiOptions.isSwap) { return "transferSwap"; } if (state.defiOptions.isPumpFun) { return "pumpFun"; } if (state.defiOptions.isStaking) { return "lending"; } else { return END; } };


---

# src/router/index.ts

import { END } from "@langchain/langgraph"; import { solanaAgentState } from "../utils/state.js"; import { initializeAgentExecutorWithOptions } from "langchain/agents";

export const chiefRouter = async (state: typeof solanaAgentState.State) => { const { isDefiQuery, isGeneralQuestion, isTechnicalQuery } = state;

if (isDefiQuery) { return "defiManager"; } else if (isGeneralQuestion) { return "generalist"; } else { return END; } };


---

# src/tools/defi/lendTokens.ts

import { tool } from "@langchain/core/tools"; import { agent } from "../../utils/agent.js"; import { z } from "zod"; import { getLendingDetails } from "solana-agent-kit/dist/tools/lend.js"; import { PublicKey } from "@solana/web3.js"; import { VersionedTransaction } from "@solana/web3.js"; import { SolanaAgentKit } from "solana-agent-kit";

export interface LuloAccountDetailsResponse { totalValue: number; interestEarned: number; realtimeApy: number; settings: { owner: string; allowedProtocols: string | null; homebase: string | null; minimumRate: string; }; }

// export const lend_tokens = tool( // async ({ asset, action, amount }) => { // try { // // Convert asset string to PublicKey // const assetPublicKey = new PublicKey(asset);

// if (action === "lend") { // const result = await lendAsset( // agent, // assetPublicKey, // amount, // process.env.LULO_API_KEY, // );

// console.log("Lending transaction:", result); // return result; // } else { // // For borrowing (to be implemented) // throw new Error("Borrowing not yet implemented"); // } // } catch (e) { // throw new Error(e as string); // } // }, // { // name: "lend_tokens", // description: "Lend or borrow tokens using the Lulo protocol", // schema: z.object({ // asset: z // .string() // .describe("The mint address of the token to lend or borrow"), // action: z // .enum(["lend", "borrow"]) // .describe("Whether to lend or borrow the asset"), // amount: z.number().describe("The amount of tokens to lend or borrow"), // }), // }, // );

// export const get_lending_info = tool( // async () => { // try { // const result = await getLendingDetails(agent, process.env.LULO_API_KEY);

// console.log("Lending details:", result); // return result; // } catch (e) { // throw new Error(e as string); // } // }, // { // name: "get_lending_info", // description: "Get current lending account details and positions", // schema: z.object({}), // }, // );

export const stake_sol_jup = tool( async ({ amount }) => { try { const result = await stakeWithJup(agent, amount);

  console.log("Jupiter SOL staking transaction:", result);
  return result;
} catch (e) {
  throw new Error(e as string);
}

}, { name: "stake_sol_jup", description: "Stake SOL tokens using Jupiter's liquid staking protocol", schema: z.object({ amount: z .number() .describe("The amount of SOL tokens to stake (in SOL units)"), }), }, );

export async function stakeWithJup( agent: SolanaAgentKit, amount: number, ): Promise { try { const res = await fetch( https://worker.jup.ag/blinks/swap/So11111111111111111111111111111111111111112/jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v/${amount}, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ account: agent.wallet.publicKey.toBase58(), }), }, );

const data = await res.json();

const txn = VersionedTransaction.deserialize(
  Buffer.from(data.transaction, "base64"),
);

const { blockhash } = await agent.connection.getLatestBlockhash();
txn.message.recentBlockhash = blockhash;

// Sign and send transaction
txn.sign([agent.wallet]);
const signature = await agent.connection.sendTransaction(txn, {
  preflightCommitment: "confirmed",
  maxRetries: 3,
});

const latestBlockhash = await agent.connection.getLatestBlockhash();
await agent.connection.confirmTransaction({
  signature,
  blockhash: latestBlockhash.blockhash,
  lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
});

return signature;

} catch (error: any) { console.error(error); throw new Error(jupSOL staking failed: ${error.message}); } }

/**

  • Lend tokens for yields using Lulo

  • @param agent SolanaAgentKit instance

  • @param amount Amount of USDC to lend

  • @returns Transaction signature */ export async function lendAsset( agent: SolanaAgentKit, amount: number, ): Promise { try { const response = await fetch( https://blink.lulo.fi/actions?amount=${amount}&symbol=USDC, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ account: agent.wallet.publicKey.toBase58(), }), }, );

    const data = await response.json();

    // Deserialize the transaction const luloTxn = VersionedTransaction.deserialize( Buffer.from(data.transaction, "base64"), );

    // Get a recent blockhash and set it const { blockhash } = await agent.connection.getLatestBlockhash(); luloTxn.message.recentBlockhash = blockhash;

    // Sign and send transaction luloTxn.sign([agent.wallet]);

    const signature = await agent.connection.sendTransaction(luloTxn, { preflightCommitment: "confirmed", maxRetries: 3, });

    // Wait for confirmation using the latest strategy const latestBlockhash = await agent.connection.getLatestBlockhash(); await agent.connection.confirmTransaction({ signature, blockhash: latestBlockhash.blockhash, lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, });

    return signature; } catch (error: any) { throw new Error(Lending failed: ${error.message}); } }

export const lend_usdc_lulo = tool( async ({ amount }) => { try { const result = await lendAsset(agent, amount); console.log("USDC lending transaction:", result); return result; } catch (e) { throw new Error(e as string); } }, { name: "lend_usdc_lulo", description: "Lend USDC tokens using the Lulo protocol for yield generation", schema: z.object({ amount: z .number() .describe("The amount of USDC tokens to lend (in USDC units)"), }), }, );

export const lendingTools = [stake_sol_jup, lend_usdc_lulo];


---

# src/tools/defi/mintNFT.ts

import { SolanaMintNFTTool } from "solana-agent-kit/dist/langchain/index.js";


---

# src/tools/defi/pumpFun.ts

import { tool } from "@langchain/core/tools"; import { agent } from "../../utils/agent.js"; import { z } from "zod"; import { launchPumpFunToken } from "solana-agent-kit/dist/tools/launch_pumpfun_token.js"; import { dalleModel } from "../../utils/model.js";

export const mint_shit_coin = tool( async ({ tokenName, tokenTicker, description, imagePrompt }) => { try { const image = await dalleModel.images.generate({ prompt: imagePrompt, n: 1, size: "1024x1024", });

  const imageUrl = image.data[0].url;

  console.log(imageUrl);

  const result = await launchPumpFunToken(
    agent,
    tokenName,
    tokenTicker,
    description,
    imageUrl!,
  );

  console.log(result);
  return result;
} catch (e) {
  throw new Error(e as string);
}

},

{ name: "mint_shit_coin", description: "create a new token using the launch_pumpfun_token tool", schema: z.object({ tokenName: z.string().describe("The name of the token to be created"), tokenTicker: z .string() .describe("The ticker symbol of the token to be created"), description: z .string() .describe("A description of the token to be created"), imagePrompt: z .string() .describe("The prompt for the image to be generated"), }), }, );


---

# src/tools/defi/swap.ts

import { tool } from "@langchain/core/tools"; import { agent } from "../../utils/agent.js"; import { JUP_API } from "solana-agent-kit/dist/constants/index.js"; import { VersionedTransaction } from "@solana/web3.js"; import { z } from "zod";

export const swap_token = tool( async ({ outputMint, inputAmount, inputMint, inputDecimal }) => { const slippageBps = 300;

try {
  // Get quote for the swap
  console.log(
    inputMint.toString(),
    outputMint.toString(),
    inputAmount,
    slippageBps,
  );
  const quoteResponse = await (
    await fetch(
      `${JUP_API}/quote?` +
        `inputMint=${inputMint.toString()}` +
        `&outputMint=${outputMint.toString()}` +
        `&amount=${inputAmount * 10 ** inputDecimal}` +
        `&slippageBps=${slippageBps}` +
        `&onlyDirectRoutes=true` +
        `&maxAccounts=20`,
    )
  ).json();

  // Get serialized transaction
  const { swapTransaction } = await (
    await fetch("https://quote-api.jup.ag/v6/swap", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        quoteResponse,
        userPublicKey: agent.wallet_address.toString(),
        wrapAndUnwrapSol: true,
        dynamicComputeUnitLimit: true,
        prioritizationFeeLamports: "auto",
      }),
    })
  ).json();
  // Deserialize transaction
  const swapTransactionBuf = Buffer.from(swapTransaction, "base64");

  const transaction = VersionedTransaction.deserialize(swapTransactionBuf);
  // Sign and send transaction
  transaction.sign([agent.wallet]);
  const signature = await agent.connection.sendTransaction(transaction);

  return signature;
} catch (e) {
  throw new Error(e as string);
}

},

{ name: "jupiter_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"), }), }, );


---

# src/tools/defi/transfer.ts

import { PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js"; import { SystemProgram, Transaction, TransactionInstruction } from "@solana/web3.js"; import { getAssociatedTokenAddress, createTransferInstruction, getMint, createAssociatedTokenAccountInstruction, getAccount } from "@solana/spl-token"; import { SolanaAgentKit } from "solana-agent-kit"; import {z} from 'zod' import { tool } from "@langchain/core/tools" import { agent } from "../../utils/agent.js";

export const solana_transfer = tool(async ( { to, amount,mint }) => { const toAddress = new PublicKey(to); const mintAddress= new PublicKey(mint)

try {
    const result = await transferFunction(
        agent,
        toAddress,
        amount,
        mintAddress
      );

console.log({result}) return result

}catch(e){
    throw new Error(e as string);
}

},{

name:"solana_transfer",
description:"call to transfer tokens from network to the other",
schema:z.object({
    to:z.string().describe("the destination address"),
    amount:z.number(),
    mint:z.string().describe("the mint address of the token that is being transferred")
})

})

const transferFunction=async( agent: SolanaAgentKit, to: PublicKey, amount: number, mint?: PublicKey )=>{ try { if (!mint) { // Transfer native SOL const balance = await agent.connection.getBalance(agent.wallet_address); const transferAmount = amount * LAMPORTS_PER_SOL;

    if (balance < transferAmount) {
      throw new Error('Insufficient SOL balance');
    }

    const transaction = new Transaction().add(
      SystemProgram.transfer({
        fromPubkey: agent.wallet_address,
        toPubkey: to,
        lamports: transferAmount
      })
    );

    return await agent.connection.sendTransaction(
      transaction,
      [agent.wallet]
    );
  } else {
    // Transfer SPL token
    const fromAta = await getAssociatedTokenAddress(mint, agent.wallet_address);
    const toAta = await getAssociatedTokenAddress(mint, to);
    
    // Get mint info to determine decimals
    const mintInfo = await getMint(agent.connection, mint);
    const adjustedAmount = amount * Math.pow(10, mintInfo.decimals);

    // Check sender's token balance
    try {
      const fromAccount = await getAccount(agent.connection, fromAta);
      if (Number(fromAccount.amount) < adjustedAmount) {
        throw new Error('Insufficient token balance');
      }
    } catch (error) {
      throw new Error('Source token account does not exist or is invalid');
    }

    // Check if recipient's token account exists
    const instructions: TransactionInstruction[] = [];
    
    try {
      await getAccount(agent.connection, toAta);
    } catch (error) {
      // If the account doesn't exist, add instruction to create it
      instructions.push(
        createAssociatedTokenAccountInstruction(
          agent.wallet_address, // payer
          toAta,               // ata
          to,                  // owner
          mint                 // mint
        )
      );
    }

    // Add transfer instruction
    instructions.push(
      createTransferInstruction(
        fromAta,
        toAta,
        agent.wallet_address,
        adjustedAmount
      )
    );

    const transaction = new Transaction().add(...instructions);

    // Send transaction
    return await agent.connection.sendTransaction(
      transaction,
      [agent.wallet]
    );
  }
} catch (error: any) {
  throw new Error(`Transfer failed: ${error.message}`);
}

}


---

# src/tools/fuel/index.ts

// import { Provider, Wallet } from "fuels";

// const ADDRESS = // "0x767caf5b08eba21c561078a4d5be09bbd7f16b9eca22699a61f1edd9e456126f";

// // const provider = await Provider.create( // // "https://testnet.fuel.network/v1/graphql", // // ); // // const wallet = Wallet.fromAddress(ADDRESS as string, provider);

// // // const { balances } = await wallet.getBalances();

// // console.log("Balances", balances);

// const agent = new FuelAgent();

// const result = await agent.invoke("send 100 coins to 0xA074e7a6A28a3bB47308126a1dD071DfF4aa73b4BaBA85B5493B0Cd891E024cF");

// console.log(result);


---

# src/tools/search.ts

import { TavilySearchResults } from "@langchain/community/tools/tavily_search";

export const webSearchTool = new TavilySearchResults({ maxResults: 2 });


---

# src/utils/agent.ts

import { SolanaAgentKit } from "solana-agent-kit";

export const agent = new SolanaAgentKit( process.env.SOLANA_PRIVATE_KEY!, process.env.SOLANA_MAINNET_RPC, process.env.OPENAI_API_KEY! );


---

# src/utils/model.ts

import { ChatOpenAI } from "@langchain/openai"; import { ChatAnthropic } from "@langchain/anthropic"; import { ChatXAI } from "@langchain/xai"; import OpenAI from "openai"; import "dotenv/config";

export const gptMiniModel = new ChatOpenAI({ model: "gpt-4o-mini", apiKey: process.env.OPENAI_API_KEY, });

export const gptModel = new ChatOpenAI({ model: "gpt-4o", apiKey: process.env.OPENAI_API_KEY, });

export const claudeSonnetModel = new ChatAnthropic({ model: "claude-3-5-sonnet-20241022", apiKey: process.env.ANTHROPIC_API_KEY, });

export const xAIModel = new ChatXAI({ model: "grok-beta", apiKey: process.env.XAI_API_KEY, });

export const dalleModel = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, });

const url = await dalleModel.images .generate({ prompt: "a cat as a image for a meme coin", n: 1, size: "1024x1024", }) .then((res) => res.data[0].url);

console.log(url);


---

# src/utils/state.ts

import { BaseMessage } from "@langchain/core/messages"; import { Annotation } from "@langchain/langgraph"; import { BaseMessageLike } from "@langchain/core/messages"; import { messagesStateReducer } from "@langchain/langgraph";

export enum Blockchain { SOLANA = "solana", BASE = "base", APTOS = "aptos", }

// Separate DeFi options interface for better organization export interface DefiOptions { isSwap: boolean; isTransfer: boolean; isStaking: boolean; isTokenLaunch: boolean; isNFTLaunch: boolean; isPumpFun: boolean; }

// Define the state using Annotation.Root export const solanaAgentState = Annotation.Root({ messages: Annotation<BaseMessage[]>({ reducer: messagesStateReducer, default: () => [], }),

input: Annotation({ reducer: (x, y) => y ?? x ?? "", default: () => "", }),

blockchain: Annotation({ reducer: (x, y) => y ?? x ?? Blockchain.SOLANA, default: () => Blockchain.SOLANA, }),

isDefiQuery: Annotation({ reducer: (x, y) => y ?? x ?? false, default: () => false, }),

isTechnicalQuery: Annotation({ reducer: (x, y) => y ?? x ?? false, default: () => false, }),

isGeneralQuestion: Annotation({ reducer: (x, y) => y ?? x ?? false, default: () => false, }),

defiOptions: Annotation({ reducer: (x, y) => ({ ...x, ...y, }), default: () => ({ isSwap: false, isTransfer: false, isStaking: false, isTokenLaunch: false, isNFTLaunch: false, isPumpFun: false, }), }), });


---

# todo.txt

SETUP THE ARCHITECTURE

  • figure out all the agents and the architecture
    • defi tools
    • retrieve all the docs
    • search through all trending coins on birdseye
    • get information about the top traders etc

---

# tsconfig.json

{ "extends": "@tsconfig/recommended", "compilerOptions": { "target": "ES2021", "lib": ["ES2021", "ES2022.Object", "DOM"], "module": "NodeNext", "moduleResolution": "nodenext", "esModuleInterop": true, "noImplicitReturns": true, "declaration": true, "useDefineForClassFields": true,

"allowJs": true,
"outDir": "dist",
"types": ["jest", "node"],
"resolveJsonModule": true

}, "include": ["/*.ts", "/*.js"], "exclude": ["node_modules", "dist"] }


---