-
Notifications
You must be signed in to change notification settings - Fork 281
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bffcb86
commit 3bb28b8
Showing
10 changed files
with
1,074 additions
and
1,793 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
AGENT_FID= | ||
CDP_API_KEY_NAME= | ||
CDP_API_KEY_PRIVATE_KEY= | ||
OPENAI_API_KEY= | ||
NEYNAR_API_KEY= | ||
NEYNAR_MANAGED_SIGNER= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"parser": "@typescript-eslint/parser", | ||
"extends": ["../../../.eslintrc.base.json"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
docs/ | ||
dist/ | ||
coverage/ | ||
.github/ | ||
src/client | ||
**/**/*.json | ||
*.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"tabWidth": 2, | ||
"useTabs": false, | ||
"semi": true, | ||
"singleQuote": false, | ||
"trailingComma": "all", | ||
"bracketSpacing": true, | ||
"arrowParens": "avoid", | ||
"printWidth": 100, | ||
"proseWrap": "never" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# CDP Agentkit Farcaster Langchain Extension Examples - Chatbot Typescript | ||
|
||
This example demonstrates an agent setup as a terminal style chatbot with access to Farcaster API actions. | ||
|
||
## Ask the chatbot to engage in the onchain ecosystem! | ||
- "Please send a cast for me to Farcaster" | ||
|
||
## Requirements | ||
|
||
- Node.js 18+ | ||
- [CDP API Key](https://portal.cdp.coinbase.com/access/api) | ||
- [OpenAI API Key](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key) | ||
- [Farcaster API Keys via Neynar](https://dev.neynar.com/) | ||
|
||
### Farcaster Application Setup | ||
1. Visit the Neynar [Developer Portal](https://dev.neynar.com/) | ||
2. Navigate to your application | ||
3. Copy the API key. | ||
4. Set the copied value in .env as your `NEYNAR_API_KEY` | ||
5. Return to the Neynar [Developer Portal](https://dev.neynar.com/) | ||
6. Navigate to your application | ||
7. Follow the instructions to get a signer UUID by either creating an agent or logging into Farcaster with an existing account. | ||
8. Copy the signer UUID. | ||
9. Set the copied UUID value in .env as your `NEYNAR_MANAGED_SIGNER`. | ||
10. Set the AGENT_FID value to your Farcaster profile FID, this can be found through your profile about modal. | ||
|
||
### Checking Node Version | ||
|
||
Before using the example, ensure that you have the correct version of Node.js installed. The example requires Node.js 18 or higher. You can check your Node version by running: | ||
|
||
```bash | ||
node --version | ||
npm --version | ||
``` | ||
|
||
## Installation | ||
|
||
```bash | ||
npm install | ||
``` | ||
|
||
## Run the Chatbot | ||
|
||
Ensure the following vars are set in .env: | ||
``` | ||
AGENT_FID= | ||
CDP_API_KEY_NAME= | ||
CDP_API_KEY_PRIVATE_KEY= | ||
OPENAI_API_KEY= | ||
NEYNAR_API_KEY= | ||
NEYNAR_MANAGER_SIGNER= | ||
``` | ||
|
||
```bash | ||
npm start | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
import { getLangChainTools } from "@coinbase/cdp-langchain"; | ||
import { MemorySaver } from "@langchain/langgraph"; | ||
import { createReactAgent } from "@langchain/langgraph/prebuilt"; | ||
import { ChatOpenAI } from "@langchain/openai"; | ||
import { HumanMessage } from "@langchain/core/messages"; | ||
import * as dotenv from "dotenv"; | ||
import * as readline from "readline"; | ||
|
||
import { AgentKit, farcasterActionProvider } from "@coinbase/cdp-agentkit-core"; | ||
|
||
dotenv.config(); | ||
|
||
const modifier = ` | ||
You are a helpful agent that can interact with the Farcaster API using the Coinbase Developer Platform Farcaster Agentkit. | ||
You are empowered to interact with Farcaster using your tools. | ||
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 Farcaster API + Agentkit. | ||
Recommend they go to https://docs.neynar.com/ for more information. | ||
Be concise and helpful with your responses. | ||
Refrain from restating your tools' descriptions unless it is explicitly requested. | ||
`; | ||
|
||
/** | ||
* Initialize the agent with Farcaster Agentkit | ||
* | ||
* @returns Agent executor and config | ||
*/ | ||
async function initialize() { | ||
// Initialize LLM | ||
const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); | ||
|
||
// Initialize Farcaster action provider | ||
const farcaster = farcasterActionProvider(); | ||
|
||
if (!process.env.CDP_API_KEY_NAME) { | ||
throw new Error("CDP_API_KEY_NAME is not set"); | ||
} | ||
|
||
if (!process.env.CDP_API_KEY_PRIVATE_KEY) { | ||
throw new Error("CDP_API_KEY_PRIVATE_KEY is not set"); | ||
} | ||
|
||
// Initialize AgentKit | ||
const agentKit = await AgentKit.from({ | ||
cdpApiKeyName: process.env.CDP_API_KEY_NAME, | ||
cdpApiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY.replace(/\\n/g, "\n"), | ||
actionProviders: [farcaster], | ||
}); | ||
|
||
// Retrieve actions | ||
const actions = agentKit.getActions(); | ||
for (const action of actions) { | ||
console.log(action.name); | ||
} | ||
|
||
// Retrieve tools | ||
const tools = await getLangChainTools(agentKit); | ||
|
||
// Store buffered conversation history in memory | ||
const memory = new MemorySaver(); | ||
|
||
// React Agent config | ||
const agentConfig = { configurable: { thread_id: "Twitter Agentkit Chatbot Example!" } }; | ||
|
||
// Create React Agent using the LLM and Twitter (X) tools | ||
const agent = createReactAgent({ | ||
llm, | ||
tools, | ||
checkpointSaver: memory, | ||
messageModifier: modifier, | ||
}); | ||
|
||
return { agent, config: agentConfig }; | ||
} | ||
|
||
/** | ||
* Run the agent autonomously with specified intervals | ||
* | ||
* @param agent - The agent executor | ||
* @param config - Agent configuration | ||
* @param interval - Time interval between actions in seconds | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
async function runAutonomousMode(agent: any, config: any, interval = 10) { | ||
console.log("Starting autonomous mode..."); | ||
|
||
// eslint-disable-next-line no-constant-condition | ||
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); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Run the agent interactively based on user input | ||
* | ||
* @param agent - The agent executor | ||
* @param config - Agent configuration | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
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 { | ||
// eslint-disable-next-line no-constant-condition | ||
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(); | ||
} | ||
} | ||
|
||
/** | ||
* Choose whether to run in autonomous or chat mode based on user input | ||
* | ||
* @returns Selected mode | ||
*/ | ||
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)); | ||
|
||
// eslint-disable-next-line no-constant-condition | ||
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(); | ||
|
||
if (choice === "1" || choice === "chat") { | ||
rl.close(); | ||
return "chat"; | ||
} else if (choice === "2" || choice === "auto") { | ||
rl.close(); | ||
return "auto"; | ||
} | ||
console.log("Invalid choice. Please try again."); | ||
} | ||
} | ||
|
||
/** | ||
* Start the chatbot agent | ||
*/ | ||
async function main() { | ||
try { | ||
const { agent, config } = await initialize(); | ||
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) { | ||
console.log("Starting Agent..."); | ||
main().catch(error => { | ||
console.error("Fatal error:", error); | ||
process.exit(1); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "@coinbase/cdp-langchain-farcaster-chatbot-example", | ||
"description": "Farcaster CDP Agentkit Node.js SDK Chatbot Example", | ||
"version": "1.0.0", | ||
"author": "Coinbase Inc.", | ||
"license": "Apache-2.0", | ||
"scripts": { | ||
"start": "NODE_OPTIONS='--no-warnings' ts-node ./chatbot.ts", | ||
"dev": "nodemon ./chatbot.ts", | ||
"lint": "eslint -c .eslintrc.json \"*.ts\"", | ||
"lint:fix": "eslint -c .eslintrc.json \"*.ts\" --fix", | ||
"format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"", | ||
"format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"" | ||
}, | ||
"dependencies": { | ||
"@coinbase/cdp-agentkit-core": "^0.0.14", | ||
"@coinbase/cdp-langchain": "^0.0.15", | ||
"@langchain/langgraph": "^0.2.21", | ||
"@langchain/openai": "^0.3.14", | ||
"@langchain/core": "^0.3.19", | ||
"dotenv": "^16.4.5", | ||
"zod": "^3.22.4" | ||
}, | ||
"devDependencies": { | ||
"nodemon": "^3.1.0", | ||
"ts-node": "^10.9.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"extends": "../../../tsconfig.base.json", | ||
"compilerOptions": { | ||
"preserveSymlinks": true, | ||
"outDir": "./dist", | ||
"rootDir": "." | ||
}, | ||
"include": ["*.ts"] | ||
} |
Oops, something went wrong.