-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #773 from ai16z/shaw/add-goat
Integrate goat plugin
- Loading branch information
Showing
12 changed files
with
418 additions
and
197 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 |
---|---|---|
|
@@ -86,6 +86,7 @@ HEURIST_IMAGE_MODEL= | |
|
||
# EVM | ||
EVM_PRIVATE_KEY= | ||
EVM_PROVIDER_URL= | ||
|
||
# Solana | ||
SOLANA_PRIVATE_KEY= | ||
|
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
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
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,12 @@ | ||
# Goat Plugin | ||
Example plugin setup of how you can integrate [Goat](https://ohmygoat.dev/) tools and plugins with Eliza. | ||
|
||
Adds onchain capabilities to your agent to send and check balances of ETH and USDC. Add all the capabilities you need by adding more plugins! | ||
|
||
## Setup | ||
1. Configure your wallet (key pair, smart wallet, etc. see all available wallets at [https://ohmygoat.dev/wallets](https://ohmygoat.dev/wallets)) | ||
2. Add the plugins you need (uniswap, zora, polymarket, etc. see all available plugins at [https://ohmygoat.dev/chains-wallets-plugins](https://ohmygoat.dev/chains-wallets-plugins)) | ||
3. Select a chain (see all available chains at [https://ohmygoat.dev/chains](https://ohmygoat.dev/chains)) | ||
4. Import and add the plugin to your Eliza agent | ||
5. Build the project | ||
6. Add the necessary environment variables to set up your wallet and plugins |
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,21 @@ | ||
{ | ||
"name": "@ai16z/plugin-goat", | ||
"version": "0.0.1", | ||
"main": "dist/index.js", | ||
"type": "module", | ||
"types": "dist/index.d.ts", | ||
"dependencies": { | ||
"@ai16z/eliza": "workspace:*", | ||
"@goat-sdk/core": "0.3.8", | ||
"@goat-sdk/plugin-erc20": "0.1.6", | ||
"@goat-sdk/wallet-viem": "0.1.3", | ||
"tsup": "^8.3.5", | ||
"viem": "^2.21.45" | ||
}, | ||
"scripts": { | ||
"build": "tsup --format esm --dts" | ||
}, | ||
"peerDependencies": { | ||
"whatwg-url": "7.1.0" | ||
} | ||
} |
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,200 @@ | ||
import { | ||
type WalletClient, | ||
type Plugin, | ||
getDeferredTools, | ||
addParametersToDescription, | ||
type ChainForWalletClient, | ||
type DeferredTool, | ||
} from "@goat-sdk/core"; | ||
import { | ||
type Action, | ||
generateText, | ||
type HandlerCallback, | ||
type IAgentRuntime, | ||
type Memory, | ||
ModelClass, | ||
type State, | ||
composeContext, | ||
generateObjectV2, | ||
} from "@ai16z/eliza"; | ||
|
||
type GetOnChainActionsParams<TWalletClient extends WalletClient> = { | ||
chain: ChainForWalletClient<TWalletClient>; | ||
getWalletClient: (runtime: IAgentRuntime) => Promise<TWalletClient>; | ||
plugins: Plugin<TWalletClient>[]; | ||
supportsSmartWallets?: boolean; | ||
}; | ||
|
||
/** | ||
* Get all the on chain actions for the given wallet client and plugins | ||
* | ||
* @param params | ||
* @returns | ||
*/ | ||
export async function getOnChainActions<TWalletClient extends WalletClient>({ | ||
getWalletClient, | ||
plugins, | ||
chain, | ||
supportsSmartWallets, | ||
}: GetOnChainActionsParams<TWalletClient>): Promise<Action[]> { | ||
const tools = await getDeferredTools<TWalletClient>({ | ||
plugins, | ||
wordForTool: "action", | ||
chain, | ||
supportsSmartWallets, | ||
}); | ||
|
||
return tools | ||
.map((action) => ({ | ||
...action, | ||
name: action.name.toUpperCase(), | ||
})) | ||
.map((tool) => createAction(tool, getWalletClient)); | ||
} | ||
|
||
function createAction<TWalletClient extends WalletClient>( | ||
tool: DeferredTool<TWalletClient>, | ||
getWalletClient: (runtime: IAgentRuntime) => Promise<TWalletClient> | ||
): Action { | ||
return { | ||
name: tool.name, | ||
similes: [], | ||
description: tool.description, | ||
validate: async () => true, | ||
handler: async ( | ||
runtime: IAgentRuntime, | ||
message: Memory, | ||
state: State | undefined, | ||
options?: Record<string, unknown>, | ||
callback?: HandlerCallback | ||
): Promise<boolean> => { | ||
try { | ||
const walletClient = await getWalletClient(runtime); | ||
let currentState = | ||
state ?? (await runtime.composeState(message)); | ||
currentState = | ||
await runtime.updateRecentMessageState(currentState); | ||
|
||
const parameterContext = composeParameterContext( | ||
tool, | ||
currentState | ||
); | ||
const parameters = await generateParameters( | ||
runtime, | ||
parameterContext, | ||
tool | ||
); | ||
|
||
const parsedParameters = tool.parameters.safeParse(parameters); | ||
if (!parsedParameters.success) { | ||
callback?.({ | ||
text: `Invalid parameters for action ${tool.name}: ${parsedParameters.error.message}`, | ||
content: { error: parsedParameters.error.message }, | ||
}); | ||
return false; | ||
} | ||
|
||
const result = await tool.method( | ||
walletClient, | ||
parsedParameters.data | ||
); | ||
const responseContext = composeResponseContext( | ||
tool, | ||
result, | ||
currentState | ||
); | ||
const response = await generateResponse( | ||
runtime, | ||
responseContext | ||
); | ||
|
||
callback?.({ text: response, content: result }); | ||
return true; | ||
} catch (error) { | ||
const errorMessage = | ||
error instanceof Error ? error.message : String(error); | ||
callback?.({ | ||
text: `Error executing action ${tool.name}: ${errorMessage}`, | ||
content: { error: errorMessage }, | ||
}); | ||
return false; | ||
} | ||
}, | ||
examples: [], | ||
}; | ||
} | ||
|
||
function composeParameterContext<TWalletClient extends WalletClient>( | ||
tool: DeferredTool<TWalletClient>, | ||
state: State | ||
): string { | ||
const contextTemplate = `{{recentMessages}} | ||
Given the recent messages, extract the following information for the action "${tool.name}": | ||
${addParametersToDescription("", tool.parameters)} | ||
`; | ||
return composeContext({ state, template: contextTemplate }); | ||
} | ||
|
||
async function generateParameters<TWalletClient extends WalletClient>( | ||
runtime: IAgentRuntime, | ||
context: string, | ||
tool: DeferredTool<TWalletClient> | ||
): Promise<unknown> { | ||
const { object } = await generateObjectV2({ | ||
runtime, | ||
context, | ||
modelClass: ModelClass.SMALL, | ||
schema: tool.parameters, | ||
}); | ||
|
||
return object; | ||
} | ||
|
||
function composeResponseContext<TWalletClient extends WalletClient>( | ||
tool: DeferredTool<TWalletClient>, | ||
result: unknown, | ||
state: State | ||
): string { | ||
const responseTemplate = ` | ||
# Action Examples | ||
{{actionExamples}} | ||
(Action examples are for reference only. Do not use the information from them in your response.) | ||
# Knowledge | ||
{{knowledge}} | ||
# Task: Generate dialog and actions for the character {{agentName}}. | ||
About {{agentName}}: | ||
{{bio}} | ||
{{lore}} | ||
{{providers}} | ||
{{attachments}} | ||
# Capabilities | ||
Note that {{agentName}} is capable of reading/seeing/hearing various forms of media, including images, videos, audio, plaintext and PDFs. Recent attachments have been included above under the "Attachments" section. | ||
The action "${tool.name}" was executed successfully. | ||
Here is the result: | ||
${JSON.stringify(result)} | ||
{{actions}} | ||
Respond to the message knowing that the action was successful and these were the previous messages: | ||
{{recentMessages}} | ||
`; | ||
return composeContext({ state, template: responseTemplate }); | ||
} | ||
|
||
async function generateResponse( | ||
runtime: IAgentRuntime, | ||
context: string | ||
): Promise<string> { | ||
return generateText({ | ||
runtime, | ||
context, | ||
modelClass: ModelClass.SMALL, | ||
}); | ||
} |
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,27 @@ | ||
import type { Plugin } from '@ai16z/eliza' | ||
import { getOnChainActions } from './actions'; | ||
import { erc20, USDC } from '@goat-sdk/plugin-erc20'; | ||
import { chain, getWalletClient, walletProvider } from './provider'; | ||
import { sendETH } from '@goat-sdk/core'; | ||
|
||
export const goatPlugin: Plugin = { | ||
name: "[GOAT] Onchain Actions", | ||
description: "Base integration plugin", | ||
providers: [walletProvider], | ||
evaluators: [], | ||
services: [], | ||
actions: [ | ||
...(await getOnChainActions({ | ||
getWalletClient, | ||
// Add plugins here based on what actions you want to use | ||
// See all available plugins at https://ohmygoat.dev/chains-wallets-plugins#plugins | ||
plugins: [sendETH(), erc20({ tokens: [USDC] })], | ||
chain: { | ||
type: "evm", | ||
id: chain.id, | ||
}, | ||
})), | ||
], | ||
}; | ||
|
||
export default goatPlugin |
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,54 @@ | ||
import { createWalletClient, http } from "viem"; | ||
import { privateKeyToAccount } from "viem/accounts"; | ||
import { base } from "viem/chains"; | ||
|
||
import { Memory, Provider, State, type IAgentRuntime } from "@ai16z/eliza"; | ||
import { viem } from "@goat-sdk/wallet-viem"; | ||
|
||
|
||
// Add the chain you want to use, remember to update also | ||
// the EVM_PROVIDER_URL to the correct one for the chain | ||
export const chain = base; | ||
|
||
/** | ||
* Create a wallet client for the given runtime. | ||
* | ||
* You can change it to use a different wallet client such as Crossmint smart wallets or others. | ||
* | ||
* See all available wallet clients at https://ohmygoat.dev/wallets | ||
* | ||
* @param runtime | ||
* @returns Wallet client | ||
*/ | ||
export async function getWalletClient(runtime: IAgentRuntime) { | ||
const privateKey = runtime.getSetting("EVM_PRIVATE_KEY"); | ||
if (!privateKey) throw new Error("EVM_PRIVATE_KEY not configured"); | ||
|
||
const provider = runtime.getSetting("EVM_PROVIDER_URL"); | ||
if (!provider) throw new Error("EVM_PROVIDER_URL not configured"); | ||
|
||
const walletClient = createWalletClient({ | ||
account: privateKeyToAccount(privateKey as `0x${string}`), | ||
chain: chain, | ||
transport: http(provider), | ||
}); | ||
return viem(walletClient); | ||
} | ||
|
||
export const walletProvider: Provider = { | ||
async get( | ||
runtime: IAgentRuntime, | ||
message: Memory, | ||
state?: State | ||
): Promise<string | null> { | ||
try { | ||
const walletClient = await getWalletClient(runtime); | ||
const address = walletClient.getAddress(); | ||
const balance = await walletClient.balanceOf(address); | ||
return `EVM Wallet Address: ${address}\nBalance: ${balance} ETH`; | ||
} catch (error) { | ||
console.error("Error in EVM wallet provider:", error); | ||
return null; | ||
} | ||
}, | ||
}; |
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 @@ | ||
{ | ||
"extends": "../core/tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "dist", | ||
"rootDir": "./src", | ||
"declaration": true | ||
}, | ||
"include": [ | ||
"src" | ||
] | ||
} |
Oops, something went wrong.