Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Vercel AI SDK framework support #227

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
779 changes: 778 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
"workspaces": [
"typescript/agentkit",
"typescript/framework-extensions/langchain",
"typescript/framework-extensions/vercel-ai-sdk",
"typescript/examples/langchain-cdp-chatbot",
"typescript/examples/langchain-twitter-chatbot",
"typescript/examples/langchain-farcaster-chatbot"
"typescript/examples/langchain-farcaster-chatbot",
"typescript/examples/vercel-ai-sdk-cdp-chatbot"
],
"packageManager": "[email protected]",
"scripts": {
Expand Down
3 changes: 3 additions & 0 deletions typescript/examples/vercel-ai-sdk-cdp-chatbot/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CDP_API_KEY_NAME="" # Place your CDP API key name in between the quotes
CDP_API_KEY_PRIVATE_KEY="" # Place your CDP API key private key in between the quotes
OPENAI_API_KEY="" # Place your OpenAI API key in between the quotes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"parser": "@typescript-eslint/parser",
"extends": ["../../../.eslintrc.base.json"]
}
30 changes: 30 additions & 0 deletions typescript/examples/vercel-ai-sdk-cdp-chatbot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# CDP Vercel AI SDK Example - Chatbot

This example demonstrates how to use the CDP Vercel AI SDK to create a chatbot that can interact with the Coinbase Developer Platform.

## Setup

1. Install dependencies:
```bash
npm install
```

2. Set up environment variables:
```bash
export CDP_API_KEY_NAME=<your-api-key-name>
export CDP_API_KEY_PRIVATE_KEY=$'<your-private-key>'
export OPENAI_API_KEY=<your-openai-api-key>
export NETWORK_ID=base-sepolia # Optional: Defaults to base-sepolia
```

3. Run the chatbot:
```bash
npm start
```

## Usage

The chatbot can:
- Execute CDP operations through natural language
- Handle streaming responses
- Use all available CDP tools through the Vercel AI SDK
195 changes: 195 additions & 0 deletions typescript/examples/vercel-ai-sdk-cdp-chatbot/chatbot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { AgentKit, cdpApiActionProvider, erc721ActionProvider, pythActionProvider, walletActionProvider, CdpWalletProvider } from "@coinbase/agentkit";
import { getVercelAITools } from "@coinbase/agentkit-vercel-ai-sdk";
import { openai } from "@ai-sdk/openai";
import { generateId, Message, streamText, ToolSet } from "ai";
import * as dotenv from "dotenv";
import * as readline from "readline";
import * as fs from "fs";

dotenv.config();

/**
* Validates that required environment variables are set
*
* @throws {Error} - If required environment variables are missing
* @returns {void}
*/
function validateEnvironment(): void {
const missingVars: string[] = [];

// Check required variables
const requiredVars = ["OPENAI_API_KEY", "CDP_API_KEY_NAME", "CDP_API_KEY_PRIVATE_KEY"];
requiredVars.forEach(varName => {
if (!process.env[varName]) {
missingVars.push(varName);
}
});

// Exit if any required variables are missing
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);
}

// Warn about optional NETWORK_ID
if (!process.env.NETWORK_ID) {
console.warn("Warning: NETWORK_ID not set, defaulting to base-sepolia testnet");
}
}

// Add this right after imports and before any other code
validateEnvironment();

// Configure a file to persist the agent's CDP MPC Wallet Data
const WALLET_DATA_FILE = "wallet_data.txt";

const system = `You are a helpful agent that can interact onchain using the Coinbase Developer Platform AgentKit. You are
empowered to interact onchain using your tools. If you ever need funds, you can request them from the
faucet if you are on network ID 'base-sepolia'. If not, you can provide your wallet details and request
funds from the user. Before executing your first action, get the wallet details to see what network
you're on. 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 CDP SDK + Agentkit, recommend they go to
docs.cdp.coinbase.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 CDP Agentkit and Vercel AI SDK tools
*
* @returns Object containing initialized tools
* @throws Error if initialization fails
*/
async function initializeAgent() {
try {

let walletDataStr: string | null = null;

// Read existing wallet data if available
if (fs.existsSync(WALLET_DATA_FILE)) {
try {
walletDataStr = fs.readFileSync(WALLET_DATA_FILE, "utf8");
} catch (error) {
console.error("Error reading wallet data:", error);
// Continue without wallet data
}
}

// Configure CDP Wallet Provider
const config = {
apiKeyName: process.env.CDP_API_KEY_NAME,
apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY?.replace(/\\n/g, "\n"),
cdpWalletData: walletDataStr || undefined,
networkId: process.env.NETWORK_ID || "base-sepolia",
};

const walletProvider = await CdpWalletProvider.configureWithWallet(config);

// Initialize action providers
const cdp = cdpApiActionProvider({
apiKeyName: process.env.CDP_API_KEY_NAME,
apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY,
});
const erc721 = erc721ActionProvider();
const pyth = pythActionProvider();
const wallet = walletActionProvider();

const agentKit = await AgentKit.from({
walletProvider,
actionProviders: [cdp, erc721, pyth, wallet],
});

// Log available actions
const actions = agentKit.getActions();
for (const action of actions) {
console.log(`Available action: ${action.name}`);
}

const tools = getVercelAITools(agentKit);
return { tools };
} catch (error) {
console.error("Failed to initialize agent:", error);
throw error;
}
}

/**
* Run the chatbot in interactive mode
*
* @param tools - Record of Vercel AI SDK tools from AgentKit
* @returns Promise that resolves when chat session ends
*/
async function runChatMode(tools: ToolSet) {
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));

const messages: Message[] = [];
let running = true;

try {
while (running) {
const userInput = await question("\nPrompt: ");

if (userInput.toLowerCase() === "exit") {
running = false;
continue;
}

messages.push({ id: generateId(), role: "user", content: userInput });

const stream = streamText({
model: openai("gpt-4-turbo-preview"),
messages,
tools,
system,
maxSteps: 10,
});

let assistantMessage = "";
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
assistantMessage += chunk;
}
console.log("\n-------------------");

messages.push({ id: generateId(), role: "assistant", content: assistantMessage });
}
} catch (error) {
console.error("Error:", error);
} finally {
rl.close();
}
}

/**
* Main entry point for the chatbot application
* Initializes the agent and starts chat mode
*
* @throws Error if initialization or chat mode fails
*/
async function main() {
try {
const { tools } = await initializeAgent();
await runChatMode(tools);
} catch (error) {
console.error("Error:", error);
process.exit(1);
}
}

if (require.main === module) {
console.log("Starting Agent...");
main().catch(error => {
console.error("Fatal error:", error);
process.exit(1);
});
}
28 changes: 28 additions & 0 deletions typescript/examples/vercel-ai-sdk-cdp-chatbot/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "@coinbase/cdp-vercel-ai-sdk-chatbot-example",
"description": "CDP Agentkit Vercel AI 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": {
"@ai-sdk/openai": "^1.1.9",
"@coinbase/agentkit": "^0.1.0",
"@coinbase/agentkit-vercel-ai-sdk": "^0.0.1",
"ai": "^4.1.16",
"dotenv": "^16.4.5",
"tsx": "^4.7.1",
"zod": "^3.22.4"
},
"devDependencies": {
"nodemon": "^3.1.0",
"ts-node": "^10.9.2"
}
}
9 changes: 9 additions & 0 deletions typescript/examples/vercel-ai-sdk-cdp-chatbot/tsconfig.json
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"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"parser": "@typescript-eslint/parser",
"extends": ["../../../.eslintrc.base.json"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
docs/
dist/
coverage/
.github/
src/client
**/**/*.json
*.md
11 changes: 11 additions & 0 deletions typescript/framework-extensions/vercel-ai-sdk/.prettierrc
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"
}
9 changes: 9 additions & 0 deletions typescript/framework-extensions/vercel-ai-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# CDP AgentKit.js Vercel AI SDK Extension Changelog

## [0.0.1] - 2024-03-XX

### Added

- Initial release of the CDP AgentKit.js Vercel AI SDK Extension.
- Integration with Vercel AI SDK for streaming responses
- Basic toolkit setup and configuration
47 changes: 47 additions & 0 deletions typescript/framework-extensions/vercel-ai-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# AgentKit Extension - Vercel AI SDK Toolkit

Vercel AI SDK extension of AgentKit. Enables agentic workflows to interact with onchain actions.

## Setup

### Prerequisites

- [CDP API Key](https://portal.cdp.coinbase.com/access/api)
- Node.js 18 or higher

### Installation

```bash
npm install @coinbase/agentkit-vercel-ai-sdk
```

### Environment Setup

Set the following environment variables:

```bash
export CDP_API_KEY_NAME=<your-api-key-name>
export CDP_API_KEY_PRIVATE_KEY=$'<your-private-key>'
export NETWORK_ID=base-sepolia # Optional: Defaults to base-sepolia
```

## Usage

### Basic Setup

```typescript
import { getVercelAITools } from "@coinbase/agentkit-vercel-ai-sdk";
import { AgentKit } from "@coinbase/agentkit";

// Initialize CDP AgentKit
const agentkit = await AgentKit.configureWithWallet();

// Get available tools
const tools = await getVercelAITools(agentkit);
```

The toolkit provides access to all CDP AgentKit tools in a Vercel AI SDK compatible format.

## Contributing

See [CONTRIBUTING.md](../../CONTRIBUTING.md) for detailed setup instructions and contribution guidelines.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const baseConfig = require("../../../jest.config.base.cjs");

module.exports = {
...baseConfig,
coveragePathIgnorePatterns: ["node_modules", "dist", "docs", "index.ts"],
coverageThreshold: {},
};
Loading
Loading