diff --git a/examples/ai-sdk/weather.ts b/examples/ai-sdk/weather.ts index 35b60a8..0ef7ae4 100644 --- a/examples/ai-sdk/weather.ts +++ b/examples/ai-sdk/weather.ts @@ -15,7 +15,7 @@ async function main() { tools: createAISDKTools(weather), toolChoice: 'required', temperature: 0, - system: 'You are a weather assistant. Be as concise as possible.', + system: 'You are a helpful assistant. Be as concise as possible.', prompt: 'What is the weather in San Francisco?' }) diff --git a/examples/dexter/weather.ts b/examples/dexter/weather.ts index 78ac72d..511ee30 100644 --- a/examples/dexter/weather.ts +++ b/examples/dexter/weather.ts @@ -12,7 +12,7 @@ async function main() { const runner = createAIRunner({ chatModel: new ChatModel({ params: { model: 'gpt-4o', temperature: 0 } }), functions: createDexterFunctions(weather), - systemMessage: 'You are a weather assistant. Be as concise as possible.' + systemMessage: 'You are a helpful assistant. Be as concise as possible.' }) const result = await runner('What is the weather in San Francisco?') diff --git a/examples/genkit/weather.ts b/examples/genkit/weather.ts index 6371592..7f6557f 100644 --- a/examples/genkit/weather.ts +++ b/examples/genkit/weather.ts @@ -23,7 +23,7 @@ async function main() { role: 'system', content: [ { - text: 'You are a weather assistant. Be as concise as possible.' + text: 'You are a helpful assistant. Be as concise as possible.' } ] } diff --git a/examples/langchain/weather.ts b/examples/langchain/weather.ts index 7508173..112c90a 100644 --- a/examples/langchain/weather.ts +++ b/examples/langchain/weather.ts @@ -16,7 +16,7 @@ async function main() { llm: new ChatOpenAI({ model: 'gpt-4o', temperature: 0 }), tools, prompt: ChatPromptTemplate.fromMessages([ - ['system', 'You are a weather assistant. Be as concise as possible.'], + ['system', 'You are a helpful assistant. Be as concise as possible.'], ['placeholder', '{chat_history}'], ['human', '{input}'], ['placeholder', '{agent_scratchpad}'] diff --git a/examples/openai/weather.ts b/examples/openai/weather.ts new file mode 100644 index 0000000..39ac02d --- /dev/null +++ b/examples/openai/weather.ts @@ -0,0 +1,57 @@ +#!/usr/bin/env node +import 'dotenv/config' + +import OpenAI from 'openai' +import { default as assert } from 'tiny-invariant' + +import { WeatherClient } from '../../src/index.js' + +async function main() { + const weather = new WeatherClient() + const openai = new OpenAI() + + const messages: OpenAI.ChatCompletionMessageParam[] = [ + { + role: 'system', + content: 'You are a helpful assistant. Be as concise as possible.' + }, + { role: 'user', content: 'What is the weather in San Francisco?' } + ] + + const res0 = await openai.chat.completions.create({ + messages, + model: 'gpt-4o', + temperature: 0, + tools: weather.tools.specs, + tool_choice: 'required' + }) + const message0 = res0.choices[0]?.message! + console.log(JSON.stringify(message0, null, 2)) + assert(message0.role === 'assistant') + assert(message0.tool_calls?.[0]?.function?.name === 'get_current_weather') + + const getCurrentWeather = weather.tools.get('get_current_weather')!.function + assert(getCurrentWeather) + + const toolParams = message0.tool_calls[0].function.arguments + assert(typeof toolParams === 'string') + const toolResult = await getCurrentWeather(toolParams) + + messages.push(message0) + messages.push({ + role: 'tool', + tool_call_id: message0.tool_calls[0].id, + content: JSON.stringify(toolResult) + }) + + const res1 = await openai.chat.completions.create({ + messages, + model: 'gpt-4o', + temperature: 0, + tools: weather.tools.specs + }) + const message1 = res1.choices[0].message + console.log(JSON.stringify(message1, null, 2)) +} + +await main() diff --git a/examples/package.json b/examples/package.json index 89b28cf..5e90477 100644 --- a/examples/package.json +++ b/examples/package.json @@ -28,12 +28,14 @@ "@dexaai/dexter": "^2.0.3", "@genkit-ai/ai": "^0.5.2", "@genkit-ai/core": "^0.5.2", + "@instructor-ai/instructor": "^1.3.0", "@langchain/core": "^0.2.5", "@langchain/openai": "^0.1.1", "ai": "^3.1.22", "dotenv": "^16.4.5", "genkitx-openai": "^0.9.0", "langchain": "^0.2.4", + "openai": "^4.47.3", "zod": "^3.23.3" } } diff --git a/package.json b/package.json index 5c38bb5..ba061f3 100644 --- a/package.json +++ b/package.json @@ -72,11 +72,12 @@ }, "devDependencies": { "@dexaai/dexter": "^2.0.3", - "@fisch0920/eslint-config": "^1.3.1", + "@fisch0920/eslint-config": "^1.3.3", "@genkit-ai/ai": "^0.5.2", + "@instructor-ai/instructor": "^1.3.0", "@langchain/core": "^0.2.5", "@total-typescript/ts-reset": "^0.5.1", - "@types/node": "^20.12.7", + "@types/node": "^20.13.0", "ai": "^3.1.22", "del-cli": "^5.1.0", "dotenv": "^16.4.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f199db3..5f1eacd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,11 +46,14 @@ importers: specifier: ^2.0.3 version: 2.1.0 '@fisch0920/eslint-config': - specifier: ^1.3.1 - version: 1.3.2(eslint@8.57.0)(typescript@5.4.5) + specifier: ^1.3.3 + version: 1.3.3(eslint@8.57.0)(typescript@5.4.5) '@genkit-ai/ai': specifier: ^0.5.2 version: 0.5.2 + '@instructor-ai/instructor': + specifier: ^1.3.0 + version: 1.3.0(openai@4.47.3)(zod@3.23.8) '@langchain/core': specifier: ^0.2.5 version: 0.2.5(langchain@0.2.4(openai@4.47.3))(openai@4.47.3) @@ -58,7 +61,7 @@ importers: specifier: ^0.5.1 version: 0.5.1 '@types/node': - specifier: ^20.12.7 + specifier: ^20.13.0 version: 20.13.0 ai: specifier: ^3.1.22 @@ -126,6 +129,9 @@ importers: '@genkit-ai/core': specifier: ^0.5.2 version: 0.5.2 + '@instructor-ai/instructor': + specifier: ^1.3.0 + version: 1.3.0(openai@4.47.3)(zod@3.23.8) '@langchain/core': specifier: ^0.2.5 version: 0.2.5(langchain@0.2.4(axios@1.7.2)(ignore@5.3.1)(openai@4.47.3))(openai@4.47.3) @@ -144,6 +150,9 @@ importers: langchain: specifier: ^0.2.4 version: 0.2.4(axios@1.7.2)(ignore@5.3.1)(openai@4.47.3) + openai: + specifier: ^4.47.3 + version: 4.47.3 zod: specifier: ^3.23.3 version: 3.23.8 @@ -369,8 +378,8 @@ packages: '@fastify/deepmerge@1.3.0': resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==} - '@fisch0920/eslint-config@1.3.2': - resolution: {integrity: sha512-CUpMxCPWzoxvWFlmvH0OthooRlenqrezrVVleKEZv+NbibwgT4phyZadBLYsF8aB7Et2OoEzk4LKg8Xsdu5spA==} + '@fisch0920/eslint-config@1.3.3': + resolution: {integrity: sha512-BhjKXkVVSmdj1M60rAbJMaM1yFUFMlOeaWNmxOL4SNgLF2TR+LFNlcUkVH7bb2A5sq4BEc+2POBCCDXwKOlbUg==} engines: {node: '>=18'} peerDependencies: typescript: ^5.0.0 @@ -405,6 +414,12 @@ packages: resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} engines: {node: '>=18'} + '@instructor-ai/instructor@1.3.0': + resolution: {integrity: sha512-nzadwGSkiVLcK3NAVeinPUrA6KwV5mkyWDGTXLMR8eXKbzlUwmLwQ+PTabPn2hCR+LtQ+7MvtuoR2acSTXgy6Q==} + peerDependencies: + openai: '>=4.28.0' + zod: '>=3.22.4' + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1265,8 +1280,8 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} - caniuse-lite@1.0.30001625: - resolution: {integrity: sha512-4KE9N2gcRH+HQhpeiRZXd+1niLB/XNLAhSy4z7fI8EzcbcPoAqjNInxVHTiTwWfTIV4w096XG8OtCOCQQKPv3w==} + caniuse-lite@1.0.30001626: + resolution: {integrity: sha512-JRW7kAH8PFJzoPCJhLSHgDgKg5348hsQ68aqb+slnzuB5QFERv846oA/mRChmlLAOdEDeOkRn3ynb1gSFnjt3w==} chai@5.1.1: resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} @@ -3508,6 +3523,9 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} + ramda@0.29.1: + resolution: {integrity: sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -3719,6 +3737,11 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + schema-stream@3.1.0: + resolution: {integrity: sha512-R4PoSFJnMORRGJ5i5BTHRO2Ed3Lf2h8DMofHd5XBU4ZE0lEBCTGRqOXBurjTEO2QntPSYAhv93jLt8PFMqDEPw==} + peerDependencies: + zod: 3.22.4 + scoped-regex@3.0.0: resolution: {integrity: sha512-yEsN6TuxZhZ1Tl9iB81frTNS292m0I/IG7+w8lTvfcJQP2x3vnpOoevjBoE3Np5A6KnZM2+RtVenihj9t6NiYg==} engines: {node: '>=12'} @@ -4458,6 +4481,12 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} + zod-stream@1.0.3: + resolution: {integrity: sha512-HxK/PZ0faOMgI1Pgjhcwin22StvlwiaYo6R7jWCT3jOFcPa4wGyFLVpeC5TwmpT6YlZhNJfDoB0qJoa+rQLYmQ==} + peerDependencies: + openai: '>=4.24.1' + zod: '>=3.22.4' + zod-to-json-schema@3.22.5: resolution: {integrity: sha512-+akaPo6a0zpVCCseDed504KBJUQpEW5QZw7RMneNmKw+fGaML1Z9tUNLnHHAC8x6dzVRO1eB2oEMyZRnuBZg7Q==} peerDependencies: @@ -4468,6 +4497,12 @@ packages: peerDependencies: zod: ^3.23.3 + zod-validation-error@2.1.0: + resolution: {integrity: sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + zod-validation-error@3.3.0: resolution: {integrity: sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==} engines: {node: '>=18.0.0'} @@ -4650,7 +4685,7 @@ snapshots: '@fastify/deepmerge@1.3.0': {} - '@fisch0920/eslint-config@1.3.2(eslint@8.57.0)(typescript@5.4.5)': + '@fisch0920/eslint-config@1.3.3(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@rushstack/eslint-patch': 1.10.3 '@typescript-eslint/eslint-plugin': 7.11.0(@typescript-eslint/parser@7.11.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) @@ -4731,6 +4766,13 @@ snapshots: '@inquirer/figures@1.0.3': {} + '@instructor-ai/instructor@1.3.0(openai@4.47.3)(zod@3.23.8)': + dependencies: + openai: 4.47.3 + zod: 3.23.8 + zod-stream: 1.0.3(openai@4.47.3)(zod@3.23.8) + zod-validation-error: 2.1.0(zod@3.23.8) + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -5666,7 +5708,7 @@ snapshots: browserslist@4.23.0: dependencies: - caniuse-lite: 1.0.30001625 + caniuse-lite: 1.0.30001626 electron-to-chromium: 1.4.788 node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.0) @@ -5724,7 +5766,7 @@ snapshots: camelcase@7.0.1: {} - caniuse-lite@1.0.30001625: {} + caniuse-lite@1.0.30001626: {} chai@5.1.1: dependencies: @@ -8041,6 +8083,8 @@ snapshots: quick-lru@5.1.1: {} + ramda@0.29.1: {} + range-parser@1.2.1: {} raw-body@2.5.2: @@ -8291,6 +8335,11 @@ snapshots: safer-buffer@2.1.2: {} + schema-stream@3.1.0(zod@3.23.8): + dependencies: + ramda: 0.29.1 + zod: 3.23.8 + scoped-regex@3.0.0: {} secure-json-parse@2.7.0: {} @@ -9070,6 +9119,13 @@ snapshots: yocto-queue@1.0.0: {} + zod-stream@1.0.3(openai@4.47.3)(zod@3.23.8): + dependencies: + openai: 4.47.3 + schema-stream: 3.1.0(zod@3.23.8) + zod: 3.23.8 + zod-to-json-schema: 3.23.0(zod@3.23.8) + zod-to-json-schema@3.22.5(zod@3.23.8): dependencies: zod: 3.23.8 @@ -9078,6 +9134,10 @@ snapshots: dependencies: zod: 3.23.8 + zod-validation-error@2.1.0(zod@3.23.8): + dependencies: + zod: 3.23.8 + zod-validation-error@3.3.0(zod@3.23.8): dependencies: zod: 3.23.8 diff --git a/readme.md b/readme.md index 97655a4..6732ff2 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,7 @@ Discuss on Twitter

-# Walter +# Agentic **Coming soon** @@ -44,15 +44,11 @@ ## TODO -- core - - company schema - - person schema - - database - - move out to a separate project - - agentic - - walter +- rename this repo to agentic +- change license to MIT - sdks - instructor-js + - TODO - services - wolfram alpha - midjourney diff --git a/src/ai-function-set.ts b/src/ai-function-set.ts index 6dfdf20..c011e68 100644 --- a/src/ai-function-set.ts +++ b/src/ai-function-set.ts @@ -58,6 +58,17 @@ export class AIFunctionSet implements Iterable { return [...this.entries].map(fn) } + get specs(): types.AIFunctionSpec[] { + return this.map((fn) => fn.spec) + } + + get toolSpecs(): types.AIToolSpec[] { + return this.map((fn) => ({ + type: 'function' as const, + function: fn.spec + })) + } + get entries(): IterableIterator { return this._map.values() } diff --git a/src/ai-tool-set.ts b/src/ai-tool-set.ts index 0748aa2..d7b21c6 100644 --- a/src/ai-tool-set.ts +++ b/src/ai-tool-set.ts @@ -62,6 +62,14 @@ export class AIToolSet implements Iterable { return [...this.entries].map(fn) } + get functionSpecs(): types.AIFunctionSpec[] { + return this.map((fn) => fn.function.spec) + } + + get specs(): types.AIToolSpec[] { + return this.map((fn) => fn.spec) + } + get entries(): IterableIterator { return this._map.values() } diff --git a/src/types.ts b/src/types.ts index 49becde..17a5215 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,7 +34,9 @@ export type AIFunctionImpl = Omit< export interface AIFunction< InputSchema extends z.ZodObject = z.ZodObject, Return = any -> extends AIFunctionImpl { +> { + (input: string | Msg): MaybePromise + /** The Zod schema for the arguments string. */ inputSchema: InputSchema diff --git a/tsconfig.json b/tsconfig.json index 4f2456e..c47b9c4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,5 +25,8 @@ "outDir": "dist", "sourceMap": true }, - "include": ["src"] + "include": ["src"], + "ts-node": { + "transpileOnly": true + } }