diff --git a/auto-agents-framework/.env.sample b/auto-agents-framework/.env.sample index 252a7bf9..f78f26db 100644 --- a/auto-agents-framework/.env.sample +++ b/auto-agents-framework/.env.sample @@ -15,9 +15,10 @@ RESPONSE_INTERVAL_MINUTES=26 POST_INTERVAL_MINUTES=30 # LLM Configuration -LARGE_LLM_MODEL= -SMALL_LLM_MODEL= OPENAI_API_KEY= +ANTHROPIC_API_KEY= +LLAMA_API_URL= +# Config the models and sizes in src/config/llm.ts # AutoDrive Configuration AUTO_DRIVE_API_KEY= diff --git a/auto-agents-framework/package.json b/auto-agents-framework/package.json index 463b4a0a..14e7d4a9 100644 --- a/auto-agents-framework/package.json +++ b/auto-agents-framework/package.json @@ -17,9 +17,11 @@ "dependencies": { "@autonomys/auto-dag-data": "1.2.1", "@autonomys/auto-drive": "1.2.1", + "@langchain/anthropic": "^0.3.11", "@langchain/community": "0.3.20", - "@langchain/core": "0.3.19", + "@langchain/core": "^0.3.27", "@langchain/langgraph": "0.2.36", + "@langchain/ollama": "^0.1.4", "@langchain/openai": "0.3.16", "agent-twitter-client": "0.0.18", "dotenv": "^16.3.1", diff --git a/auto-agents-framework/src/agents/workflows/kol/types.ts b/auto-agents-framework/src/agents/workflows/kol/types.ts index 73a28cfb..4d9e7585 100644 --- a/auto-agents-framework/src/agents/workflows/kol/types.ts +++ b/auto-agents-framework/src/agents/workflows/kol/types.ts @@ -4,6 +4,8 @@ import { z } from 'zod'; import { Tweet, TwitterApi } from '../../../services/twitter/types.js'; import { ToolNode } from '@langchain/langgraph/prebuilt'; import { ChatOpenAI } from '@langchain/openai'; +import { ChatAnthropic } from '@langchain/anthropic'; +import { ChatOllama } from '@langchain/ollama'; import { Runnable } from '@langchain/core/runnables'; import { engagementSchema, responseSchema, skippedEngagementSchema, dsnTweet } from './schemas.js'; import { ChatPromptTemplate } from '@langchain/core/prompts'; @@ -17,10 +19,10 @@ export type WorkflowConfig = Readonly<{ twitterApi: TwitterApi; toolNode: ToolNode; llms: Readonly<{ - decision: ChatOpenAI; - analyze: ChatOpenAI; - generation: Runnable; - response: Runnable; + decision: ChatOpenAI | ChatAnthropic | ChatOllama; + analyze: ChatOpenAI | ChatAnthropic | ChatOllama; + generation: ChatOpenAI | ChatAnthropic | ChatOllama; + response: ChatOpenAI | ChatAnthropic | ChatOllama; }>; prompts: Readonly<{ engagementPrompt: ChatPromptTemplate; diff --git a/auto-agents-framework/src/agents/workflows/kol/workflow.ts b/auto-agents-framework/src/agents/workflows/kol/workflow.ts index 4b520c7d..0ed1283a 100644 --- a/auto-agents-framework/src/agents/workflows/kol/workflow.ts +++ b/auto-agents-framework/src/agents/workflows/kol/workflow.ts @@ -13,6 +13,7 @@ import { Tweet } from '../../../services/twitter/types.js'; import { trendSchema, summarySchema } from './schemas.js'; import { z } from 'zod'; import { createPrompts } from './prompts.js'; +import { LLMFactory } from '../../../services/llm/factory.js'; export const logger = createLogger('agent-workflow'); @@ -62,7 +63,7 @@ export const State = Annotation.Root({ const createWorkflowConfig = async (characterFile: string): Promise => { const { USERNAME, PASSWORD, COOKIES_PATH } = config.twitterConfig; - const { LARGE_LLM_MODEL, SMALL_LLM_MODEL } = config.llmConfig; + const { nodes } = config.llmConfig; const twitterApi = await createTwitterApi(USERNAME, PASSWORD, COOKIES_PATH); const { tools } = createTools(twitterApi); @@ -74,22 +75,10 @@ const createWorkflowConfig = async (characterFile: string): Promise { POST_INTERVAL_MS: (Number(process.env.POST_INTERVAL_MINUTES) || 90) * 60 * 1000, }, llmConfig: { - LARGE_LLM_MODEL: process.env.LARGE_LLM_MODEL || 'gpt-4o', - SMALL_LLM_MODEL: process.env.SMALL_LLM_MODEL || 'gpt-4o-mini', + configuration: { + large: { + provider: llmConfig.configuration.large.provider, + model: llmConfig.configuration.large.model, + }, + small: { + provider: llmConfig.configuration.small.provider, + model: llmConfig.configuration.small.model, + }, + }, + nodes: { + decision: llmConfig.nodes.decision, + analyze: llmConfig.nodes.analyze, + generation: llmConfig.nodes.generation, + response: llmConfig.nodes.response, + }, OPENAI_API_KEY: process.env.OPENAI_API_KEY || '', + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY || '', + LLAMA_API_URL: process.env.LLAMA_API_URL || '', }, autoDriveConfig: { AUTO_DRIVE_API_KEY: process.env.AUTO_DRIVE_API_KEY, diff --git a/auto-agents-framework/src/config/llm.ts b/auto-agents-framework/src/config/llm.ts new file mode 100644 index 00000000..9299c198 --- /dev/null +++ b/auto-agents-framework/src/config/llm.ts @@ -0,0 +1,32 @@ +import { LLMNodeConfiguration, LLMSize, LLMProvider, llmModels } from '../services/llm/types.js'; + +export const llmConfig = { + configuration: { + large: { + provider: LLMProvider.ANTHROPIC, + model: llmModels.large.anthropic.claude35sonnet, + }, + small: { + provider: LLMProvider.OPENAI, + model: llmModels.small.openai.gpt_4o_mini, + }, + }, + nodes: { + decision: { + size: LLMSize.SMALL, + temperature: 0.2, + } as LLMNodeConfiguration, + analyze: { + size: LLMSize.LARGE, + temperature: 0.5, + } as LLMNodeConfiguration, + generation: { + size: LLMSize.LARGE, + temperature: 0.8, + } as LLMNodeConfiguration, + response: { + size: LLMSize.SMALL, + temperature: 0.8, + } as LLMNodeConfiguration, + }, +}; diff --git a/auto-agents-framework/src/config/schema.ts b/auto-agents-framework/src/config/schema.ts index 778a0740..11dddc86 100644 --- a/auto-agents-framework/src/config/schema.ts +++ b/auto-agents-framework/src/config/schema.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { LLMSize, LLMProvider } from '../services/llm/types.js'; const twitterConfigSchema = z.object({ USERNAME: z.string().min(1, 'Twitter username is required'), @@ -16,11 +17,76 @@ const twitterConfigSchema = z.object({ POST_INTERVAL_MS: z.number().int().positive(), }); -const llmConfigSchema = z.object({ - LARGE_LLM_MODEL: z.string().min(1), - SMALL_LLM_MODEL: z.string().min(1), - OPENAI_API_KEY: z.string().min(1, 'OpenAI API key is required'), -}); +const llmConfigSchema = z + .object({ + configuration: z.object({ + large: z.object({ + provider: z.nativeEnum(LLMProvider), + model: z.string(), + }), + small: z.object({ + provider: z.nativeEnum(LLMProvider), + model: z.string(), + }), + }), + nodes: z.object({ + decision: z.object({ + size: z.nativeEnum(LLMSize), + temperature: z.number(), + }), + analyze: z.object({ + size: z.nativeEnum(LLMSize), + temperature: z.number(), + }), + generation: z.object({ + size: z.nativeEnum(LLMSize), + temperature: z.number(), + }), + response: z.object({ + size: z.nativeEnum(LLMSize), + temperature: z.number(), + }), + }), + OPENAI_API_KEY: z.string(), + ANTHROPIC_API_KEY: z.string(), + LLAMA_API_URL: z.string(), + }) + .superRefine((data, ctx) => { + const providers = new Set([ + data.nodes.decision.size === LLMSize.LARGE + ? data.configuration.large.provider + : data.configuration.small.provider, + data.nodes.analyze.size === LLMSize.LARGE + ? data.configuration.large.provider + : data.configuration.small.provider, + data.nodes.generation.size === LLMSize.LARGE + ? data.configuration.large.provider + : data.configuration.small.provider, + data.nodes.response.size === LLMSize.LARGE + ? data.configuration.large.provider + : data.configuration.small.provider, + ]); + + const missingConfigs = []; + + if (providers.has(LLMProvider.OPENAI) && !data.OPENAI_API_KEY) { + missingConfigs.push('OpenAI API key'); + } + if (providers.has(LLMProvider.ANTHROPIC) && !data.ANTHROPIC_API_KEY) { + missingConfigs.push('Anthropic API key'); + } + if (providers.has(LLMProvider.OLLAMA) && !data.LLAMA_API_URL) { + missingConfigs.push('Llama API URL'); + } + + if (missingConfigs.length > 0) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: `Missing required configs: ${missingConfigs.join(', ')}`, + path: ['llmConfig'], + }); + } + }); const autoDriveConfigSchema = z.object({ AUTO_DRIVE_API_KEY: z.string().optional(), diff --git a/auto-agents-framework/src/services/llm/factory.ts b/auto-agents-framework/src/services/llm/factory.ts new file mode 100644 index 00000000..d9168411 --- /dev/null +++ b/auto-agents-framework/src/services/llm/factory.ts @@ -0,0 +1,38 @@ +import { ChatOpenAI } from '@langchain/openai'; +import { ChatAnthropic } from '@langchain/anthropic'; +import { ChatOllama } from '@langchain/ollama'; +import { LLMProvider, LLMConfiguration, LLMNodeConfiguration } from './types.js'; +import { llmConfig } from '../../config/llm.js'; +import { config as appConfig } from '../../config/index.js'; + +export class LLMFactory { + static createModel(node: LLMNodeConfiguration) { + const cfg = llmConfig.configuration[node.size]; + return this.createModelFromConfig(cfg, node.temperature); + } + + static createModelFromConfig(config: LLMConfiguration, temperature: number) { + switch (config.provider) { + case LLMProvider.OPENAI: + return new ChatOpenAI({ + apiKey: appConfig.llmConfig.OPENAI_API_KEY, + model: config.model, + temperature, + }); + case LLMProvider.ANTHROPIC: + return new ChatAnthropic({ + apiKey: appConfig.llmConfig.ANTHROPIC_API_KEY, + model: config.model, + temperature, + }); + case LLMProvider.OLLAMA: + return new ChatOllama({ + baseUrl: appConfig.llmConfig.LLAMA_API_URL, + model: config.model, + temperature, + }); + default: + throw new Error(`Unsupported LLM provider: ${config.provider}`); + } + } +} diff --git a/auto-agents-framework/src/services/llm/types.ts b/auto-agents-framework/src/services/llm/types.ts new file mode 100644 index 00000000..51ee46f7 --- /dev/null +++ b/auto-agents-framework/src/services/llm/types.ts @@ -0,0 +1,47 @@ +export enum LLMProvider { + OPENAI = 'openai', + ANTHROPIC = 'anthropic', + OLLAMA = 'ollama', +} + +export type LLMConfiguration = { + provider: LLMProvider; + model: string; +}; + +export enum LLMSize { + SMALL = 'small', + LARGE = 'large', +} + +export type LLMNodeConfiguration = { + size: LLMSize; + temperature: number; +}; + +export const llmModels = { + large: { + openai: { + gpt4o: 'gpt-4o', + }, + anthropic: { + claude35sonnet: 'claude-3-5-sonnet-latest', + }, + //placeholder + ollama: { + llama3: 'llama3.1', + }, + }, + small: { + openai: { + gpt_4o_mini: 'gpt-4o-mini', + }, + anthropic: { + claude35haiku: 'claude-3-5-haiku-latest', + }, + //placeholder + ollama: { + llama3: 'llama3.1', + }, + }, +}; diff --git a/auto-agents-framework/yarn.lock b/auto-agents-framework/yarn.lock index 8e88dedb..a8c01d94 100644 --- a/auto-agents-framework/yarn.lock +++ b/auto-agents-framework/yarn.lock @@ -7,6 +7,19 @@ resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== +"@anthropic-ai/sdk@^0.32.1": + version "0.32.1" + resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.32.1.tgz#d22c8ebae2adccc59d78fb416e89de337ff09014" + integrity sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + "@autonomys/auto-dag-data@1.2.1", "@autonomys/auto-dag-data@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@autonomys/auto-dag-data/-/auto-dag-data-1.2.1.tgz#45042895f4e4083486e94de80f2025ee95ec800e" @@ -60,6 +73,11 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" +"@cfworker/json-schema@^4.0.2": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.0.3.tgz#30fc6b72322c74ceb27014a779de0ccdfa0dfb9e" + integrity sha512-ZykIcDTVv5UNmKWSTLAs3VukO6NDJkkSKxrgUTDPBkAlORVT3H9n5DbRjRl8xIotklscHdbLIa0b9+y3mQq73g== + "@colors/colors@1.6.0", "@colors/colors@^1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" @@ -208,6 +226,16 @@ dependencies: lodash "^4.17.21" +"@langchain/anthropic@^0.3.11": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@langchain/anthropic/-/anthropic-0.3.11.tgz#57277bb4bd7c624eb9039dc10cc36de9ecfeb91c" + integrity sha512-rYjDZjMwVQ+cYeJd9IoSESdkkG8fc0m3siGRYKNy6qgYMnqCz8sUPKBanXwbZAs6wvspPCGgNK9WONfaCeX97A== + dependencies: + "@anthropic-ai/sdk" "^0.32.1" + fast-xml-parser "^4.4.1" + zod "^3.22.4" + zod-to-json-schema "^3.22.4" + "@langchain/community@0.3.20": version "0.3.20" resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.3.20.tgz#1c2dc6ab2837fe3d52c83c0d1602ed5ba54bfa8d" @@ -224,16 +252,17 @@ zod "^3.22.3" zod-to-json-schema "^3.22.5" -"@langchain/core@0.3.19": - version "0.3.19" - resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.19.tgz#8e2038dbb7cb70264d892cf9c3b506125ee68d77" - integrity sha512-pJVOAHShefu1SRO8uhzUs0Pexah/Ib66WETLMScIC2w9vXlpwQy3DzXJPJ5X7ixry9N666jYO5cHtM2Z1DnQIQ== +"@langchain/core@^0.3.27": + version "0.3.27" + resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.27.tgz#b8e75bd4f122b18a423f3905df0e9872205a1a44" + integrity sha512-jtJKbJWB1NPU1YvtrExOB2rumvUFgkJwlWGxyjSIV9A6zcLVmUbcZGV8fCSuXgl5bbzOIQLJ1xcLYQmbW9TkTg== dependencies: + "@cfworker/json-schema" "^4.0.2" ansi-styles "^5.0.0" camelcase "6" decamelize "1.2.0" js-tiktoken "^1.0.12" - langsmith "^0.2.0" + langsmith "^0.2.8" mustache "^4.2.0" p-queue "^6.6.2" p-retry "4" @@ -268,6 +297,14 @@ uuid "^10.0.0" zod "^3.23.8" +"@langchain/ollama@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@langchain/ollama/-/ollama-0.1.4.tgz#3796a7b8a41000ef04ff1e5271a61eea5c16835f" + integrity sha512-olHPViUurGcmOI3IbhIGK/EJ7QxDlZru4j98V269PiEFTIVlciRULltgI/t3voHYTdvB8R+HV8pMo/Y3UVzvzA== + dependencies: + ollama "^0.5.9" + uuid "^10.0.0" + "@langchain/openai@0.3.16", "@langchain/openai@>=0.1.0 <0.4.0", "@langchain/openai@>=0.2.0 <0.4.0": version "0.3.16" resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.3.16.tgz#bc1ebf67e88d5e5f8320d1df4f01ec3151279101" @@ -942,6 +979,13 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-xml-parser@^4.4.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz#a7e665ff79b7919100a5202f23984b6150f9b31e" + integrity sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w== + dependencies: + strnum "^1.0.5" + fecha@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" @@ -1235,7 +1279,7 @@ kuler@^2.0.0: zod "^3.22.4" zod-to-json-schema "^3.22.3" -langsmith@^0.2.0, langsmith@^0.2.8: +langsmith@^0.2.8: version "0.2.14" resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.2.14.tgz#9a186af45730b1e14bd448ac34a7928b633ea6ae" integrity sha512-ClAuAgSf3m9miMYotLEaZKQyKdaWlfjhebCuYco8bc6g72dU2VwTg31Bv4YINBq7EH2i1cMwbOiJxbOXPqjGig== @@ -1398,6 +1442,13 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +ollama@^0.5.9: + version "0.5.11" + resolved "https://registry.yarnpkg.com/ollama/-/ollama-0.5.11.tgz#208e4476a6f010c20d24c83896223bfa660ec236" + integrity sha512-lDAKcpmBU3VAOGF05NcQipHNKTdpKfAHpZ7bjCsElkUkmX7SNZImi6lwIxz/l1zQtLq0S3wuLneRuiXxX2KIew== + dependencies: + whatwg-fetch "^3.6.20" + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1717,6 +1768,11 @@ strip-json-comments@^3.1.0: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -1907,6 +1963,11 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== +whatwg-fetch@^3.6.20: + version "3.6.20" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -1971,7 +2032,7 @@ yaml@^2.2.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== -zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.5, zod-to-json-schema@^3.24.1: +zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.4, zod-to-json-schema@^3.22.5, zod-to-json-schema@^3.24.1: version "3.24.1" resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz#f08c6725091aadabffa820ba8d50c7ab527f227a" integrity sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==