From 65c3b99ad2f8dc00be59958efaa9598ecc3ce18f Mon Sep 17 00:00:00 2001 From: Eli Front Date: Tue, 1 Jul 2025 15:21:18 -0400 Subject: [PATCH 1/3] feat: add `maxOutputTokens` option --- src/runWithTools.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/runWithTools.ts b/src/runWithTools.ts index ff38249..588a9f7 100644 --- a/src/runWithTools.ts +++ b/src/runWithTools.ts @@ -22,6 +22,7 @@ import { AiTextGenerationToolInputWithFunction } from "./types"; * @param {number} [config.maxRecursiveToolRuns=0] - The maximum number of recursive tool runs to perform. * @param {boolean} [config.strictValidation=false] - Whether to perform strict validation (using zod) of the arguments passed to the tools. * @param {boolean} [config.verbose=false] - Whether to enable verbose logging. + * @param {number} [config.maxOutputTokens] - Maximum number of output tokens to generate. * @param {(tools: AiTextGenerationToolInputWithFunction[], ai: Ai, model: BaseAiTextGenerationModels, messages: RoleScopedChatInput[]) => Promise} [config.trimFunction] - Use a trim function to trim down the number of tools given to the AI for a given task. You can also use this alongside `autoTrimTools`, which uses an extra AI.run call to cut down on the input tokens of the tool call based on the tool's names. * * @returns {Promise} The final response in the same format as the AI.run call. @@ -48,7 +49,8 @@ export const runWithTools = async ( strictValidation?: boolean; /** Whether to enable verbose logging. */ verbose?: boolean; - + /** Maximum number of output tokens to generate. */ + maxOutputTokens?: number; /** Automatically decides the best tools to use for a given task. */ trimFunction?: ( tools: AiTextGenerationToolInputWithFunction[], @@ -127,6 +129,7 @@ export const runWithTools = async ( messages: messages, stream: false, tools: tools, + max_tokens: config.maxOutputTokens, })) as { response?: string; tool_calls?: { @@ -213,7 +216,7 @@ export const runWithTools = async ( Logger.error( `Function for tool ${toolCallObjectJson.name} is undefined`, ); - return response + return response } }); From 3ef7dbb369d0913130f266e8e31ef305ba1427cf Mon Sep 17 00:00:00 2001 From: Eli Front Date: Tue, 1 Jul 2025 15:45:34 -0400 Subject: [PATCH 2/3] fix: build errors and setup pnpm --- package.json | 3 +- pnpm-lock.yaml | 324 ++++++++++++++++++++++++++++++++++++++++++++ src/runWithTools.ts | 22 ++- src/utils.ts | 4 +- 4 files changed, 336 insertions(+), 17 deletions(-) create mode 100644 pnpm-lock.yaml diff --git a/package.json b/package.json index d16d414..7afd6d7 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,8 @@ ], "main": "dist/index.js", "types": "dist/index.d.ts", - "dependencies": {}, "devDependencies": { - "@cloudflare/workers-types": "^4.20240620.0", + "@cloudflare/workers-types": "^4.20250701.0", "@types/json-schema": "^7.0.15", "@types/node": "^20.14.8", "esbuild": "^0.21.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..a2fd865 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,324 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@cloudflare/workers-types': + specifier: ^4.20250701.0 + version: 4.20250701.0 + '@types/json-schema': + specifier: ^7.0.15 + version: 7.0.15 + '@types/node': + specifier: ^20.14.8 + version: 20.19.3 + esbuild: + specifier: ^0.21.5 + version: 0.21.5 + prettier: + specifier: ^3.3.2 + version: 3.6.2 + typescript: + specifier: ^5.5.2 + version: 5.8.3 + yaml: + specifier: ^2.4.5 + version: 2.8.0 + zod: + specifier: ^3.23.8 + version: 3.25.67 + +packages: + + '@cloudflare/workers-types@4.20250701.0': + resolution: {integrity: sha512-q1bHwe5P7FGy9RkLYOY1kwoZrqUe2Q6XhCPscaxzQc0N7+2pwIZzZzY5iMTTfvmf65UNsadoVxuF+vPVXoAkkQ==} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@20.19.3': + resolution: {integrity: sha512-Zeb0nYmBhczT5Fq2/Yrt5TuF8rfdlFmPG+pUVGd8SdkRGNF1RfUFyzcjj86r6L2ZSRwWuBTcsykiG6Ichbu6HA==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + + zod@3.25.67: + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} + +snapshots: + + '@cloudflare/workers-types@4.20250701.0': {} + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@types/json-schema@7.0.15': {} + + '@types/node@20.19.3': + dependencies: + undici-types: 6.21.0 + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + prettier@3.6.2: {} + + typescript@5.8.3: {} + + undici-types@6.21.0: {} + + yaml@2.8.0: {} + + zod@3.25.67: {} diff --git a/src/runWithTools.ts b/src/runWithTools.ts index 588a9f7..a8013ff 100644 --- a/src/runWithTools.ts +++ b/src/runWithTools.ts @@ -2,10 +2,8 @@ import { Logger } from "./logger"; import { validateArgsWithZod } from "./utils"; import { Ai, - AiTextGenerationInput, - AiTextGenerationOutput, - BaseAiTextGenerationModels, RoleScopedChatInput, + AiModels, } from "@cloudflare/workers-types"; import { AiTextGenerationToolInputWithFunction } from "./types"; @@ -13,7 +11,7 @@ import { AiTextGenerationToolInputWithFunction } from "./types"; * Runs a set of tools on a given input and returns the final response in the same format as the AI.run call. * * @param {Ai} ai - The AI instance to use for the run. - * @param {BaseAiTextGenerationModels} model - The function calling model to use for the run. We recommend using `@hf/nousresearch/hermes-2-pro-mistral-7b`, `llama-3` or equivalent model that's suited for function calling. + * @param {keyof AiModels} model - The function calling model to use for the run. We recommend using `@hf/nousresearch/hermes-2-pro-mistral-7b`, `llama-3` or equivalent model that's suited for function calling. * @param {Object} input - The input for the runWithTools call. * @param {RoleScopedChatInput[]} input.messages - The messages to be sent to the AI. * @param {AiTextGenerationToolInputWithFunction[]} input.tools - The tools to be used. You can also pass a function along with each tool that will automatically run the tool with the arguments passed to the function. The function arguments are type-checked against your tool's parameters, so you can get autocomplete and type checking in your IDE. @@ -23,7 +21,7 @@ import { AiTextGenerationToolInputWithFunction } from "./types"; * @param {boolean} [config.strictValidation=false] - Whether to perform strict validation (using zod) of the arguments passed to the tools. * @param {boolean} [config.verbose=false] - Whether to enable verbose logging. * @param {number} [config.maxOutputTokens] - Maximum number of output tokens to generate. - * @param {(tools: AiTextGenerationToolInputWithFunction[], ai: Ai, model: BaseAiTextGenerationModels, messages: RoleScopedChatInput[]) => Promise} [config.trimFunction] - Use a trim function to trim down the number of tools given to the AI for a given task. You can also use this alongside `autoTrimTools`, which uses an extra AI.run call to cut down on the input tokens of the tool call based on the tool's names. + * @param {(tools: AiTextGenerationToolInputWithFunction[], ai: Ai, model: keyof AiModels, messages: RoleScopedChatInput[]) => Promise} [config.trimFunction] - Use a trim function to trim down the number of tools given to the AI for a given task. You can also use this alongside `autoTrimTools`, which uses an extra AI.run call to cut down on the input tokens of the tool call based on the tool's names. * * @returns {Promise} The final response in the same format as the AI.run call. */ @@ -31,7 +29,7 @@ export const runWithTools = async ( /** The AI instance to use for the run. */ ai: Ai, /** The function calling model to use for the run. We recommend using `@hf/nousresearch/hermes-2-pro-mistral-7b`, `llama-3` or equivalent model that's suited for function calling. */ - model: BaseAiTextGenerationModels, + model: keyof AiModels, /** The input for the runWithTools call. */ input: { /** The messages to be sent to the AI. */ @@ -55,11 +53,11 @@ export const runWithTools = async ( trimFunction?: ( tools: AiTextGenerationToolInputWithFunction[], ai: Ai, - model: BaseAiTextGenerationModels, + model: keyof AiModels, messages: RoleScopedChatInput[], ) => Promise; } = {}, -): Promise => { +): Promise> => { // Destructure config with default values const { streamFinalResponse = false, @@ -68,7 +66,7 @@ export const runWithTools = async ( trimFunction = async ( tools: AiTextGenerationToolInputWithFunction[], ai: Ai, - model: BaseAiTextGenerationModels, + model: keyof AiModels, messages: RoleScopedChatInput[], ) => tools as AiTextGenerationToolInputWithFunction[], strictValidation = false, @@ -114,11 +112,11 @@ export const runWithTools = async ( maxRecursiveToolRuns, }: { ai: Ai; - model: BaseAiTextGenerationModels; + model: keyof AiModels; messages: RoleScopedChatInput[]; streamFinalResponse: boolean; maxRecursiveToolRuns: number; - }): Promise { + }): Promise> { try { Logger.info("Starting AI.run call"); Logger.info("Messages", JSON.stringify(messages, null, 2)); @@ -200,7 +198,6 @@ export const runWithTools = async ( messages.push({ role: "tool", content: JSON.stringify(result), - // @ts-expect-error workerd types name: selectedTool.name, }); } catch (error) { @@ -208,7 +205,6 @@ export const runWithTools = async ( messages.push({ role: "tool", content: `Error executing tool ${selectedTool.name}: ${(error as Error).message}`, - // @ts-expect-error workerd types name: selectedTool.name, }); } diff --git a/src/utils.ts b/src/utils.ts index 789973b..6b7adc3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,7 +6,7 @@ import { Logger } from "./logger"; import { AiTextGenerationToolInputWithFunction } from "./types"; import { Ai, - BaseAiTextGenerationModels, + AiModels, RoleScopedChatInput, } from "@cloudflare/workers-types"; @@ -92,7 +92,7 @@ export function validateArgsWithZod( export async function autoTrimTools( tools: AiTextGenerationToolInputWithFunction[], ai: Ai, - model: BaseAiTextGenerationModels, + model: keyof AiModels, messages: RoleScopedChatInput[], ) { let returnedTools = tools; From 104c884293a142a25346b69d0b376ae093ed09bc Mon Sep 17 00:00:00 2001 From: Eli Front Date: Tue, 1 Jul 2025 15:54:57 -0400 Subject: [PATCH 3/3] feat: add a prepare script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7afd6d7..64a4c98 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "author": "Dhravya Shah ", "scripts": { "build": "tsc && esbuild src/index.ts --bundle --outfile=dist/index.js --sourcemap --format=esm", - "format": "prettier --write ." + "format": "prettier --write .", + "prepare": "npm run build" }, "files": [ "dist/*"