diff --git a/package.json b/package.json index 8048b4e751..938819873f 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "start:service:all": "pnpm --filter \"@ai16z/agent\" start:service:all --isRoot", "stop:service:all": "pnpm --filter \"@ai16z/agent\" stop:service:all", "start": "pnpm --filter \"@ai16z/agent\" start --isRoot", + "start:client": "pnpm --dir client start --isRoot", "dev": "bash ./scripts/dev.sh", "lint": "pnpm --dir packages/core lint && pnpm --dir packages/agent lint", "prettier-check": "npx prettier --check .", diff --git a/packages/client-discord/src/voice.ts b/packages/client-discord/src/voice.ts index 0977ee0e59..e2abc0927e 100644 --- a/packages/client-discord/src/voice.ts +++ b/packages/client-discord/src/voice.ts @@ -31,12 +31,11 @@ import { ITranscriptionService, Memory, ModelClass, - Service, ServiceType, State, UUID, } from "@ai16z/eliza"; -import { stringToUuid } from "@ai16z/eliza/src/uuid.ts"; +import { stringToUuid } from "@ai16z/eliza"; export function getWavHeader( audioLength: number, diff --git a/packages/client-twitter/src/base.ts b/packages/client-twitter/src/base.ts index 59293769cc..1a910c3769 100644 --- a/packages/client-twitter/src/base.ts +++ b/packages/client-twitter/src/base.ts @@ -207,7 +207,7 @@ export class ClientBase extends EventEmitter { ); await this.setCookiesFromArray(cookiesArray); } else { - console.log("Cookies file path:", cookiesFilePath); + elizaLogger.debug("Cookies file path:", cookiesFilePath); if (fs.existsSync(cookiesFilePath)) { const cookiesArray = JSON.parse( fs.readFileSync(cookiesFilePath, "utf-8") diff --git a/packages/client-twitter/src/interactions.ts b/packages/client-twitter/src/interactions.ts index 0f8b307d5c..f6e7e2dff3 100644 --- a/packages/client-twitter/src/interactions.ts +++ b/packages/client-twitter/src/interactions.ts @@ -1,6 +1,6 @@ import { SearchMode, Tweet } from "agent-twitter-client"; import fs from "fs"; -import { composeContext } from "@ai16z/eliza"; +import { composeContext, elizaLogger } from "@ai16z/eliza"; import { generateMessageResponse, generateShouldRespond } from "@ai16z/eliza"; import { messageCompletionFooter, shouldRespondFooter } from "@ai16z/eliza"; import { @@ -199,7 +199,7 @@ export class TwitterInteractionClient extends ClientBase { console.log("skipping tweet with no text", tweet.id); return { text: "", action: "IGNORE" }; } - console.log("handling tweet", tweet.id); + elizaLogger.log("handling tweet", tweet.id); const formatTweet = (tweet: Tweet) => { return ` ID: ${tweet.id} From: ${tweet.name} (@${tweet.username}) @@ -267,8 +267,6 @@ export class TwitterInteractionClient extends ClientBase { this.saveRequestMessage(message, state); } - console.log("composeState done"); - const shouldRespondContext = composeContext({ state, template: diff --git a/packages/core/src/embedding.ts b/packages/core/src/embedding.ts index ae9c069b25..4c775d3946 100644 --- a/packages/core/src/embedding.ts +++ b/packages/core/src/embedding.ts @@ -1,7 +1,5 @@ - import path from "node:path"; - import { models } from "./models.ts"; import { IAgentRuntime, ModelProviderName, ModelClass } from "./types.ts"; import fs from "fs"; @@ -29,8 +27,6 @@ async function getRemoteEmbedding( // Construct full URL const fullUrl = `${baseEndpoint}/embeddings`; - //console.log("Calling embedding API at:", fullUrl); // Debug log - const requestOptions = { method: "POST", headers: { @@ -52,7 +48,7 @@ async function getRemoteEmbedding( const response = await fetch(fullUrl, requestOptions); if (!response.ok) { - console.error("API Response:", await response.text()); // Debug log + elizaLogger.error("API Response:", await response.text()); // Debug log throw new Error( `Embedding API Error: ${response.status} ${response.statusText}` ); @@ -65,7 +61,7 @@ async function getRemoteEmbedding( const data: EmbeddingResponse = await response.json(); return data?.data?.[0].embedding; } catch (e) { - console.error("Full error details:", e); + elizaLogger.error("Full error details:", e); throw e; } } @@ -176,7 +172,7 @@ export async function retrieveCachedEmbedding( input: string ) { if (!input) { - console.log("No input to retrieve cached embedding for"); + elizaLogger.log("No input to retrieve cached embedding for"); return null; } diff --git a/packages/core/src/memory.ts b/packages/core/src/memory.ts index 70c61f3305..350f65c54a 100644 --- a/packages/core/src/memory.ts +++ b/packages/core/src/memory.ts @@ -1,4 +1,5 @@ import { embed } from "./embedding.ts"; +import elizaLogger from "./logger.ts"; import { IAgentRuntime, IMemoryManager, @@ -160,7 +161,7 @@ export class MemoryManager implements IMemoryManager { await this.runtime.databaseAdapter.getMemoryById(memory.id); if (existingMessage) { - // console.log("Memory already exists, skipping"); + elizaLogger.debug("Memory already exists, skipping"); return; } await this.runtime.databaseAdapter.createMemory( diff --git a/packages/core/src/runtime.ts b/packages/core/src/runtime.ts index 730cf08af8..c972e4462a 100644 --- a/packages/core/src/runtime.ts +++ b/packages/core/src/runtime.ts @@ -131,8 +131,6 @@ export class AgentRuntime implements IAgentRuntime { services: Map = new Map(); memoryManagers: Map = new Map(); - logging: boolean = false; - registerMemoryManager(manager: IMemoryManager): void { if (!manager.tableName) { throw new Error("Memory manager must have a tableName"); @@ -428,7 +426,6 @@ export class AgentRuntime implements IAgentRuntime { state?: State, callback?: HandlerCallback ): Promise { - console.log("Processing actions", responses); if (!responses[0].content?.action) { elizaLogger.warn("No action found in the response content."); return; @@ -499,7 +496,7 @@ export class AgentRuntime implements IAgentRuntime { async evaluate(message: Memory, state?: State, didRespond?: boolean) { const evaluatorPromises = this.evaluators.map( async (evaluator: Evaluator) => { - console.log("Evaluating", evaluator.name); + elizaLogger.log("Evaluating", evaluator.name); if (!evaluator.handler) { return null; } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 278b1948df..ec821e52c9 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -670,3 +670,9 @@ export enum ServiceType { SPEECH_GENERATION = "speech_generation", PDF = "pdf", } + +export enum LoggingLevel { + DEBUG = "debug", + VERBOSE = "verbose", + NONE = "none", +} diff --git a/packages/plugin-starknet/readme.md b/packages/plugin-starknet/readme.md index 4f01bc33e8..799d6592ab 100644 --- a/packages/plugin-starknet/readme.md +++ b/packages/plugin-starknet/readme.md @@ -10,3 +10,8 @@ Reuse providers and utilities from the existing actions where possible. Add more 1. Add the action to the `actions` directory. Try to follow the naming convention of the other actions. 2. Export the action in the `index.ts` file. + +## TODO: + +1. Ekubo DCA +2. Unruggable diff --git a/packages/plugin-starknet/src/actions/swap.ts b/packages/plugin-starknet/src/actions/swap.ts index 9e93d5d968..e6b47d0c16 100644 --- a/packages/plugin-starknet/src/actions/swap.ts +++ b/packages/plugin-starknet/src/actions/swap.ts @@ -17,13 +17,46 @@ import { import { getStarknetAccount, validateSettings } from "../utils/index.ts"; +interface SwapContent { + sellTokenAddress: string; + buyTokenAddress: string; + sellAmount: string; +} + +export function isSwapContent(content: SwapContent): content is SwapContent { + // Validate types + const validTypes = + typeof content.sellTokenAddress === "string" && + typeof content.buyTokenAddress === "string" && + typeof content.sellAmount === "string"; + if (!validTypes) { + return false; + } + + // Validate addresses (must be 32-bytes long with 0x prefix) + const validAddresses = + content.sellTokenAddress.startsWith("0x") && + content.sellTokenAddress.length === 66 && + content.buyTokenAddress.startsWith("0x") && + content.buyTokenAddress.length === 66; + + return validAddresses; +} + const swapTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. +These are known addresses you will get asked to swap, use these addresses for sellTokenAddress and buyTokenAddress: +- BROTHER/brother/$brother: 0x03b405a98c9e795d427fe82cdeeeed803f221b52471e3a757574a2b4180793ee +- BTC/btc: 0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac +- ETH/eth: 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 +- STRK/strk: 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d +- LORDS/lords: 0x0124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49 + Example response: \`\`\`json { "sellTokenAddress": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", - "buyTokenAddress": "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8", + "buyTokenAddress": "0x124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49", "sellAmount": "1000000000000000000" } \`\`\` @@ -48,7 +81,8 @@ export const executeSwap: Action = { validate: async (runtime: IAgentRuntime, message: Memory) => { return validateSettings(runtime); }, - description: "Perform a token swap using Avnu.", + description: + "Perform a token swap on starknet. Use this action when a user asks you to swap tokens anything.", handler: async ( runtime: IAgentRuntime, message: Memory, @@ -75,6 +109,11 @@ export const executeSwap: Action = { console.log("Response:", response); + if (!isSwapContent(response)) { + callback?.({ text: "Invalid swap content, please try again." }); + return false; + } + try { // Get quote const quoteParams: QuoteRequest = { @@ -105,7 +144,7 @@ export const executeSwap: Action = { return true; } catch (error) { console.error("Error during token swap:", error); - callback?.({ text: `Error during swap: ${error.message}` }); + callback?.({ text: `Error during swap:` }); return false; } }, @@ -114,19 +153,41 @@ export const executeSwap: Action = { { user: "{{user1}}", content: { - text: "Swap 1 ETH for USDC on Starknet", + text: "Swap 10 ETH for LORDS", + }, + }, + { + user: "{{agent}}", + content: { + text: "Ok, I'll swap 10 ETH for LORDS", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Swap 100 $lords on starknet", }, }, { - user: "{{user2}}", + user: "{{agent}}", + content: { + text: "Ok, I'll swap 100 $lords on starknet", + }, + }, + ], + [ + { + user: "{{user1}}", content: { - text: "Buy LORDS on Starknet", + text: "Swap 0.5 BTC for LORDS", }, }, { - user: "{{user2}}", + user: "{{agent}}", content: { - text: "Executing swap...", + text: "Ok, I'll swap 0.5 BTC for LORDS", }, }, ], diff --git a/packages/plugin-starknet/src/actions/takeOrder.ts b/packages/plugin-starknet/src/actions/takeOrder.ts index c737e21a1a..c79d00f9f4 100644 --- a/packages/plugin-starknet/src/actions/takeOrder.ts +++ b/packages/plugin-starknet/src/actions/takeOrder.ts @@ -20,23 +20,7 @@ interface Order { price: number; } -const take_order: Action = { - name: "TAKE_STARKNET_ORDER", - similes: ["BUY_STARKNET_ORDER", "PLACE_STARKNET_ORDER"], - description: - "Records a starknet buy order based on the user's conviction level.", - examples: [], - validate: async (runtime: IAgentRuntime, message: Memory) => { - const text = (message.content as Content).text; - // Check if the message contains a ticker symbol - const tickerRegex = /\b[A-Z]{1,5}\b/g; - return tickerRegex.test(text); - }, - handler: async (runtime: IAgentRuntime, message: Memory) => { - const text = (message.content as Content).text; - const userId = message.userId; - - const template = ` +const template = ` // CLAUDE TODO: Put the usual conversation context here @@ -52,6 +36,21 @@ Determine if the user is trying to shill the ticker. if they are, respond with e // - contractAddress: string `; +const take_order: Action = { + name: "TAKE_STARKNET_ORDER", + similes: ["BUY_STARKNET_ORDER", "PLACE_STARKNET_ORDER"], + description: + "Records a starknet buy order based on the user's conviction level.", + validate: async (runtime: IAgentRuntime, message: Memory) => { + const text = (message.content as Content).text; + // Check if the message contains a ticker symbol + const tickerRegex = /\b[A-Z]{1,5}\b/g; + return tickerRegex.test(text); + }, + handler: async (runtime: IAgentRuntime, message: Memory) => { + const text = (message.content as Content).text; + const userId = message.userId; + let ticker, contractAddress; // TODO: @@ -130,6 +129,7 @@ Determine if the user is trying to shill the ticker. if they are, respond with e text: `Recorded a ${conviction} conviction buy order for ${ticker} (${contractAddress}) with an amount of ${buyAmount} at the price of ${currentPrice}.`, }; }, -}; + examples: [] as ActionExample[][], +} as Action; export default take_order; diff --git a/packages/plugin-starknet/src/actions/transfer.ts b/packages/plugin-starknet/src/actions/transfer.ts index f993484227..5c064e8431 100644 --- a/packages/plugin-starknet/src/actions/transfer.ts +++ b/packages/plugin-starknet/src/actions/transfer.ts @@ -161,79 +161,41 @@ export default { { user: "{{user1}}", content: { - text: "Send 69 STRK to 0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", + text: "Send 10 ETH to 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", }, }, { - user: "{{user2}}", + user: "{{agent}}", content: { - text: "Transfer to 0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF 0.01 ETH", - }, - }, - { - user: "{{user3}}", - content: { - text: "Please send 100 STRK tokens to 0x789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345", - }, - }, - { - user: "{{user4}}", - content: { - text: "I'd like to transfer 0.5 ETH to 0xABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF01234567", - }, - }, - { - user: "{{user5}}", - content: { - text: "Can you send 25 STRK to wallet 0x456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012", + text: "I'll transfer 10 ETH to that address right away. Let me process that for you.", }, }, + ], + [ { user: "{{user1}}", content: { - text: "Transfer 1.5 ETH -> 0x123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF", - }, - }, - { - user: "{{user2}}", - content: { - text: "Send 42.42 STRK tokens to address 0xDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789", - }, - }, - { - user: "{{user3}}", - content: { - text: "Could you transfer 0.1 ETH to this address: 0xEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABC", - }, - }, - { - user: "{{user4}}", - content: { - text: "I am requesting 777 STRK to be sent to this address: 0x0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF", + text: "Can you transfer 50 LORDS tokens to 0x0124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49?", }, }, { - user: "{{user5}}", + user: "{{agent}}", content: { - text: "I really think i need 100 lords to be sent to this address: 0xBCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789A", + text: "Executing transfer of 50 LORDS tokens to the specified address. One moment please.", }, }, + ], + [ { user: "{{user1}}", content: { - text: "I am requesting 100 lords to be sent to this address: 0xCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789AB", - }, - }, - { - user: "{{user2}}", - content: { - text: "Transfer lords to 0x90ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF01234567", + text: "Please send 0.5 BTC to 0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac", }, }, { - user: "{{user3}}", + user: "{{agent}}", content: { - text: "The repair of the squire blobert kingdom kitchen has been completed. Could you please send 100 LORDS tokens to 0x0277eE04e3f82D4E805Ab0e2044C53fB6d61ABd00a2a7f44B78410e9b43E1344", + text: "Got it, initiating transfer of 0.5 BTC to the provided address. I'll confirm once it's complete.", }, }, ], diff --git a/packages/plugin-starknet/src/index.ts b/packages/plugin-starknet/src/index.ts index 8d2891c198..5ed9e48db7 100644 --- a/packages/plugin-starknet/src/index.ts +++ b/packages/plugin-starknet/src/index.ts @@ -20,7 +20,7 @@ export const PROVIDER_CONFIG = { export const starknetPlugin: Plugin = { name: "starknet", description: "Starknet Plugin for Eliza", - actions: [transfer], + actions: [transfer, executeSwap], evaluators: [], providers: [], }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06568d960d..3f38759380 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15825,7 +15825,7 @@ snapshots: '@octokit/request-error': 3.0.3 '@octokit/types': 9.3.2 is-plain-object: 5.0.0 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.6.7(encoding@0.1.13) universal-user-agent: 6.0.1 transitivePeerDependencies: - encoding @@ -19959,7 +19959,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.4 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -22066,7 +22066,7 @@ snapshots: log-symbols@4.1.0: dependencies: - chalk: 4.1.2 + chalk: 4.1.0 is-unicode-supported: 0.1.0 log-symbols@6.0.0: diff --git a/scripts/start.sh b/scripts/start.sh index a46889fc5d..808a2c0a31 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -40,6 +40,10 @@ if ! pnpm start; then exit 1 fi +# Start client +echo -e "\033[1mStarting client...\033[0m" +pnpm start:client + # Open webpage echo -e "\033[1mOpening webpage...\033[0m" if command -v xdg-open &> /dev/null; then