Skip to content

Commit

Permalink
chore: relocate typescript monorepo
Browse files Browse the repository at this point in the history
  • Loading branch information
0xRAG committed Feb 4, 2025
1 parent b006ddd commit 425a648
Show file tree
Hide file tree
Showing 24 changed files with 802 additions and 500 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,16 @@ jobs:

lint-typescript:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./typescript
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "18"
cache: "npm"
cache-dependency-path: ./typescript/package-lock.json
# Install dependencies in parent directory first
- run: npm install
# formatting/lint in working directory
Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ jobs:
working-directory: ./python/cdp-agentkit-core
strategy:
matrix:
python: ['3.10', '3.11', '3.12']
python: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3

Expand Down Expand Up @@ -47,8 +47,8 @@ jobs:
working-directory: ./python/cdp-langchain
strategy:
matrix:
python: ['3.10', '3.11', '3.12']
python: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3

Expand Down Expand Up @@ -85,8 +85,8 @@ jobs:
working-directory: ./python/twitter-langchain
strategy:
matrix:
python: ['3.10', '3.11', '3.12']
python: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3

Expand Down Expand Up @@ -121,12 +121,16 @@ jobs:
strategy:
matrix:
node-version: ["18", "20"]
defaults:
run:
working-directory: ./typescript
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
cache-dependency-path: ./typescript/package-lock.json
- name: Install and test AgentKit.js
run: |
npm i
Expand Down
246 changes: 1 addition & 245 deletions CONTRIBUTING-TYPESCRIPT.md
Original file line number Diff line number Diff line change
@@ -1,247 +1,3 @@
# TypeScript Development Guide

This guide covers TypeScript-specific setup and development for AgentKit.

## Contents

- [Development Setup](#development-setup)
- [Adding an Action Provider](#adding-an-action-provider)
- [Adding a Wallet Provider](#adding-a-wallet-provider)
- [Integrating into an AI Agent Framework](#integrating-into-an-ai-agent-framework)
- [Testing](#testing)
- [Code Style](#code-style)

## Development Setup

AgentKit uses Node.js v18.x or higher and npm 8.x or higher.

You can run the following commands in your terminal to check your local Node.js and npm versions:

```bash
node --version
npm --version
```

If the versions are not correct or you don't have Node.js or npm installed, download through [nvm](https://github.com/nvm-sh/nvm).

Once you have these installed, make sure you install the project dependencies by running `npm install` from the root of the repository.

## Adding an Action Provider

An Action is an interface for an AI Agent to interact with the real world: any TypeScript function that you can think of can be used by an Agent via an Action! Actions are grouped by Action Providers, which are classes that contain a collection of actions along with configuration and helper functions.

Action Components:

1. **Name**: The name of the action. This is used to identify the action when it is added to an Agent.

2. **Description**: A description that helps the AI understand when and how to use the action. It's important to describe the inputs and outputs of the action and include examples. Additionally, think about what inputs can be removed entirely and fetched or inferred by the LLM, so that users don't have to manually provide them.

3. **Input Schema**: Define the input parameters using [Zod](https://zod.dev/) schemas. Zod is used to validate the inputs to the action and to generate a JSON schema that can be used by the LLM to understand the inputs.

4. **Invocation Function**: The actual logic that executes the action. This function receives as input the wallet that the Agent has access to, and as you'll see in the walkthrough below, we can use this wallet to invoke an onchain contract! For more information on contract invocations using a CDP wallet, see [here](https://docs.cdp.coinbase.com/cdp-sdk/docs/onchain-interactions#smart-contract-interactions).

In practice, action providers are housed in `typescript/agentkit/src/action-providers` and generally grouped by the type of action they are and the configuration they require . For example, actions related to interacting with social platforms such as X (Twitter) are housed in `typescript/agentkit/src/action-providers/twitter`. When adding a new action, check if there is an existing action provider for the type of action you are adding and add your new action to the appropriate folder.

Here's the structure of the action providers directory:

```
./typescript/agentkit/src
└── action-providers
└── pyth
├── pythActionProvider.ts
├── schemas.ts
└── pythActionProvider.test.ts
└── ...
```

Once you decide which folder to add your action to, go ahead and create a new file there to house your action, then read through the following sections to learn how to implement your action. For a complete example of an action provider, see [erc721ActionProvider.ts](https://github.com/coinbase/agentkit/blob/master/typescript/agentkit/src/action-providers/erc721/erc721ActionProvider.ts).

### Crafting a good description prompt

The description prompt is used by the LLM to understand when and how to use the action. It's important to be as specific as possible in describing the inputs and outputs of the action and include examples. Take the Mint NFT prompt for example:

```typescript
// src/action-providers/erc721/erc721ActionProvider.ts

@CreateAction({
name: "mint",
description: `
This tool will mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation.
It takes the contract address of the NFT onchain and the destination address onchain that will receive the NFT as inputs.
Do not use the contract address as the destination address. If you are unsure of the destination address, please ask the user before proceeding.
`,
schema: MintSchema,
})
```

* The prompt disambuguates the type of NFT by specifying "ERC-721"
* The prompt specifies that the destination address should not be the contract address
* The prompt specifies that the LLM should ask the user for the destination address if it is unsure
* Think about the best UX: if a contract address from a known list of addresses is required, you can instruct the LLM to use another action to get the list of addresses and prompt the user to choose an address from that list. For example, consider a DeFi action that allows a user to withdraw funds from a liquidity provider position. This action would take a contract address, so it would be valuable to have another action that can pull a list of addresses representing the user's positions. You can then instruct the LLM via the prompt to use that action in the case that no contract address is provided.

### Defining the input schema

The input schema is used to validate the inputs to the action and to generate a JSON schema that can be used by the LLM to understand the inputs. For TypeScript, we use [Zod](https://zod.dev/) to define the input schema. For example, the Mint NFT input schema is defined as follows:

```typescript
// src/action-providers/erc721/schemas.ts

const MintSchema = z
.object({
contractAddress: z.string().describe("The contract address of the NFT to mint"),
destination: z.string().describe("The destination address that will receive the NFT"),
})
.strip()
.describe("Instructions for minting an NFT");
```

This says that the input schema has two fields: `contractAddress` and `destination`. The `contractAddress` field is required and must be a string. The `destination` field is required and must be a string. For more information on Zod, see the [Zod documentation](https://zod.dev/).

### Implementing the action provider

```typescript
// src/action-providers/erc721/erc721ActionProvider.ts

export class Erc721ActionProvider extends ActionProvider {
constructor() {
super("erc721", []);
}

supportsNetwork = (network: Network) => network.protocolFamily === "evm";
}

export const erc721ActionProvider = () => new Erc721ActionProvider();
```

### Implementing the action

Now we need to implement the actual function that the AI will call when using your action. Actions are defined as instance methods on the action provider class with the `@CreateAction` decorator. The function receives as input the wallet provider that the Agent has access to, along with the inputs defined in the input schema, and it must return a string. This return value is used by the LLM to understand the result of the action, which in turn will generate a user-facing response. Here's an example of the Mint NFT implementation instance method:

```typescript
// src/action-providers/erc721/erc721ActionProvider.ts

export class Erc721ActionProvider extends ActionProvider {
constructor() {
super("erc721", []);
}

@CreateAction({
name: "mint",
description: `
This tool will mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation.
It takes the contract address of the NFT onchain and the destination address onchain that will receive the NFT as inputs.
Do not use the contract address as the destination address. If you are unsure of the destination address, please ask the user before proceeding.
`,
schema: MintSchema,
})
async mint(walletProvider: EvmWalletProvider, args: z.infer<typeof MintSchema>): Promise<string> {
try {
const data = encodeFunctionData({
abi: ERC721_ABI,
functionName: "mint",
args: [args.destination as Hex, 1n],
});

const hash = await walletProvider.sendTransaction({
to: args.contractAddress as `0x${string}`,
data,
});

await walletProvider.waitForTransactionReceipt(hash);

return `Successfully minted NFT ${args.contractAddress} to ${args.destination}`;
} catch (error) {
return `Error minting NFT ${args.contractAddress} to ${args.destination}: ${error}`;
}
}

supportsNetwork = (network: Network) => network.protocolFamily === "evm";
}

```

Notice the return value contains useful information for the user, such as the transaction hash and link. It's important to include this information in the return value so that the user can easily see the result of the action.

This class is then exported out of [typescript/agentkit/src/action-providers/erc721/index.ts](https://github.com/coinbase/agentkit/blob/master/typescript/agentkit/src/action-providers/erc721/index.ts) so that is is consumable by users of the `@coinbase/agentkit` package.

### Testing the action provider

There are two forms of testing you should do: unit testing and manual end-to-end testing.

To add a unit test for your action provider, add a file to your action provider folder post-fixing it with `.test.ts`. For an example, see [pythActionProvider.test.ts](https://github.com/coinbase/agentkit/blob/master/typescript/agentkit/src/action-providers/pyth/pythActionProvider.test.ts).

You can then run the unit tests with the following command:
```bash
cd typescript/agentkit
npm test
```

For instructions on manual end-to-end testing, see the [Testing](#testing) section.

Check out the [Testing](#testing) section to learn how to manually test your new action provider.

## Adding a Wallet Provider

Wallet providers give an agent access to a wallet. AgentKit currently supports the following wallet providers:

EVM:
- [CdpWalletProvider](https://github.com/coinbase/agentkit/blob/master/typescript/agentkit/src/wallet-providers/cdpWalletProvider.ts)
- [ViemWalletProvider](https://github.com/coinbase/agentkit/blob/master/typescript/agentkit/src/wallet-providers/viemWalletProvider.ts)

### Adding a new EVM wallet provider

The EVM Wallet Providers are housed in `typescript/agentkit/src/wallet-providers`. EVM Wallet Providers extend `EvmWalletProvider` which is an abstract class that conforms to [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193). To add a new EVM wallet provider, create a new file in the `wallet-providers` directory and implement a class that extends `EvmWalletProvider`.

### Adding a new non-EVM wallet provider

Non-EVM Wallet Providers are housed in `typescript/agentkit/src/wallet-providers`. Non-EVM Wallet Providers extend `WalletProvider` which is an abstract class that provides a core set of wallet functionality. To add a new non-EVM wallet provider, create a new file in the `wallet-providers` directory and implement a class that extends `WalletProvider`.

## Integrating into an AI Agent Framework

Actions are necessary building blocks powering onchain AI applications, but they're just one piece of the puzzle. To make them truly useful, they must be integrated into an AI Agent framework such as [LangChain](https://www.langchain.com/) or [Eliza](https://elizaos.github.io/eliza/), among others.

Integrations into AI Agent frameworks are specific to the framework itself, so we can't go into specific implementation details here, but we can offer up some examples and tips.
- Check out how [AgentKit actions are mapped into LangChain Tools](https://github.com/coinbase/agentkit/blob/master/typescript/agentkit-langchain/src/index.ts)
- Check out how [AgentKit Actions are mapped into Eliza Actions](https://github.com/elizaOS/eliza/blob/develop/packages/plugin-agentkit/src/actions.ts#L31)

## Testing

### Local Testing

A good way to test new actions locally is by using the chatbot example in `typescript/examples/langchain-cdp-chatbot`. See the [chatbot README](https://github.com/coinbase/agentkit/blob/master/typescript/examples/langchain-cdp-chatbot/README.md) for instructions on setting up and running the chatbot.

The flow is:

1. Make your change as described in the [Adding an Agentic Action](#adding-an-agentic-action) section
2. From root, run `npm run build && npm i`
3. In `typescript/examples/langchain-cdp-chatbot`, run `npm run start`
4. You can now interact with your new action via the chatbot!

### Running Tests

From the package you are working in, you can run:

```bash
npm test
```

For example, to run all tests in the `@coinbase/agentkit` package, you can run:
```bash
cd typescript/agentkit
npm test
```

## Code Style

We use ESLint and Prettier for linting and formatting. Run:

```bash
# Format code
npm run format

# Lint code
npm run lint

# Fix linting issues
npm run lint:fix
```
See: [typescript/CONTRIBUTING.md](./typescript/CONTRIBUTING.md)
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 425a648

Please sign in to comment.