From ef54d182936f1836c512de440a54baf564ffdd43 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 6 Aug 2024 22:33:54 -0400 Subject: [PATCH] Demonstrate Function Calling & Structured Outputs with Zod --- README.md | 2 +- package-lock.json | 13 ++++++- package.json | 3 +- test/fixtures.js | 1 + test/fixtures/creativeAssistant.js | 57 ++++++++++++++++++++++++++++++ test/uat/arrayOfTools.test.js | 13 +++++++ 6 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/creativeAssistant.js create mode 100644 test/uat/arrayOfTools.test.js diff --git a/README.md b/README.md index 75890d2..dfad011 100644 --- a/README.md +++ b/README.md @@ -245,7 +245,7 @@ class ProductsTool extends Tool { OpenAI's Assistants API introduces a new resource called [Threads](https://platform.openai.com/docs/assistants/how-it-works/managing-threads-and-messages) which messages & files are stored within. Essentially, threads are a managed context window (memory) for your agents. Creating a new thread with Experts.js is as easy as: ```javascript -const thread = Thread.create(); +const thread = await Thread.create(); console.log(thread.id) // thread_abc123 ``` diff --git a/package-lock.json b/package-lock.json index e8fd583..5f7ee0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,8 @@ "file-type": "^19.0.0", "jest": "^29.7.0", "jest-environment-node": "^29.7.0", - "p-queue": "^7.4.1" + "p-queue": "^7.4.1", + "zod": "^3.23.8" } }, "node_modules/@ampproject/remapping": { @@ -4135,6 +4136,16 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index d282240..913b3bc 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "file-type": "^19.0.0", "jest": "^29.7.0", "jest-environment-node": "^29.7.0", - "p-queue": "^7.4.1" + "p-queue": "^7.4.1", + "zod": "^3.23.8" }, "repository": { "type": "git", diff --git a/test/fixtures.js b/test/fixtures.js index 84502f4..7bf6d54 100644 --- a/test/fixtures.js +++ b/test/fixtures.js @@ -13,3 +13,4 @@ export { AccountsTool, } from "./fixtures/accountsAssistant.js"; export { HtmlAssistant } from "./fixtures/htmlAssistant.js"; +export { CreativeAssistant } from "./fixtures/creativeAssistant.js"; diff --git a/test/fixtures/creativeAssistant.js b/test/fixtures/creativeAssistant.js new file mode 100644 index 0000000..d156ca7 --- /dev/null +++ b/test/fixtures/creativeAssistant.js @@ -0,0 +1,57 @@ +import { helperName } from "../helpers.js"; +import { Tool } from "../../src/experts/tool.js"; +import { Assistant } from "../../src/experts/assistant.js"; +import { z } from "zod"; +import { zodFunction, zodResponseFormat } from "openai/helpers/zod"; + +class IdeogramTool extends Tool { + constructor() { + super({ + name: helperName("IdeogramTool"), + model: "gpt-4o-2024-08-06", + instructions: + "Turn an ideaogram concept into an Ideogram prompt. Very short paragraph.", + temperature: 0.1, + asyncRun: true, + parentsTools: [ + zodFunction({ + name: "ideogram", + description: "Submit a single concept to cerate an ideogram prompt", + parameters: z.object({ + concept: z.object({ + concept: z + .string() + .describe("Brief concept in a single sentence."), + thinking: z + .string() + .describe("Your detailed thinking behind the concept."), + }), + }), + }), + ], + response_format: zodResponseFormat( + z.object({ prompt: z.string() }), + "prompt" + ), + }); + } +} + +class CreativeAssistant extends Assistant { + constructor() { + super({ + name: helperName("CreativeAssistant"), + model: "gpt-4o-2024-08-06", + instructions: + "Take a topic and use your ideogram tool to create three concepts.", + temperature: 0.1, + response_format: zodResponseFormat( + z.object({ prompts: z.array(z.object({ prompt: z.string() })) }), + "prompt" + ), + }); + this.addAssistantTool(IdeogramTool); + } +} + +export { CreativeAssistant }; diff --git a/test/uat/arrayOfTools.test.js b/test/uat/arrayOfTools.test.js new file mode 100644 index 0000000..dd4709a --- /dev/null +++ b/test/uat/arrayOfTools.test.js @@ -0,0 +1,13 @@ +import { helperThreadID } from "../helpers.js"; +import { CreativeAssistant } from "../fixtures.js"; + +// TODO: Allow these tool calls to be async with a unique thread. + +test("can call 3 tools in sequence", async () => { + const threadID = await helperThreadID(); + const assistant = await CreativeAssistant.create(); + await assistant.ask( + "RouteLLM: What it is and what you should know.", + threadID + ); +}, 60000);