zod-to-openai-tool
Version:
Easily create tools from zod schemas to use with OpenAI Assistants and Chat Completions
130 lines (127 loc) • 5.57 kB
TypeScript
import { OpenAI } from 'openai';
import { z } from 'zod';
interface Steps<T = void, Omitted extends string = never> {
/**
* Adds a schema for the tool. This will be used to validate the input and to infer the type of the input in the `run()` function.
* @param s The schema for the input. Must be a `z.object({})`
* @returns A tool with the input schema set.
*/
input<S extends z.AnyZodObject>(schema: S): Omit<Steps<z.infer<S>, Omitted | "input">, "input" | Omitted> & InternalTool;
/**
* The function to run when the model calls the tool. This is the only required builder step.
* @param args The arguments for the `run()` function.
* The type of the arguments will be inferred from the input schema. If there is no input schema, the type will be `void`.
* @returns A tool with the `run()` function set.
*/
run(func: (input: T extends void ? never : T) => unknown): Omit<Steps<T, Omitted | "run">, "run" | "input" | Omitted> & InternalTool;
/**
* Adds a description to the tool. This will be provided to the model to aid in understanding the tool.
* @param d The description of the tool as a string.
* It is good to explain what data the tool returns and what it does here.
* @returns A tool with the description set.
*/
describe(description: string): Omit<Steps<T, Omitted | "describe">, Omitted | "describe"> & InternalTool;
}
type CheckHasSetRun<T> = T extends {
run: any;
} ? never : T;
interface Data {
func: (input: any) => unknown;
schema: z.AnyZodObject;
description: string | undefined;
}
interface InternalTool {
_data: Data;
_parameters: OpenAI.Beta.FunctionTool["function"]["parameters"];
}
type OpenAIBuiltInTool = OpenAI.Beta.Assistant["tools"][number];
type Tool<T = void, O extends string = never> = Steps<T, O> & InternalTool;
/**
* Creates a tool for use with openai assistants
* @example
* ```ts
* const getWeather = t
* .input(
* z.object({
* city: z.string(),
* }))
* .describe("Gets the weather")
* .run(async ({ city }) => ({
* weather: "sunny",
* }));
* ```
*/
declare const t: Steps<void> & {
/**
* Alias to the `file_search` tool
* @see https://platform.openai.com/docs/assistants/tools/knowledge-retrieval
*/
fileSearch: OpenAI.Beta.FileSearchTool;
/**
* Alias to the `code_interpreter` tool
* @see https://platform.openai.com/docs/assistants/tools/code-interpreter
*/
codeInterpreter: OpenAI.Beta.CodeInterpreterTool;
};
/**
*
* @param tools An object containing tools created with `t.run()`. Name them using the key.
* @param onError A function that will be called when a tool throws an error. The error will be passed as the first argument.
* If this function returns a value, that value will be used as the output of the tool.
* If you do not provide a function, the error will be stringified and sent to the assistant.
* If the function returns `undefined` or `null`, the error will be sent to the assistant.
* @returns An object containing the tools and a function to process actions.
* @example
* ```ts
* const { t, processAssistantActions, processChatActions } = createTools({
* getWeather, // These are created with `t.run()` and `t.input()`, see the example for `t`
* exponential,
* });
*
* // Then use them like this:
* const assistant = await openai.beta.assistants.create({
* tools,
* //...
* });
* ```
*/
declare function createTools<T>(tools: {
[K in keyof T]: InternalTool & CheckHasSetRun<T[K]>;
}, onError?: (error: unknown) => any): {
tools: (OpenAI.Beta.Assistants.FunctionTool & OpenAI.Chat.Completions.ChatCompletionTool)[];
/**
* Process the actions from the chat completion.
* @param data The tool calls generated from the chat completion. (`message.tool_calls`)
* @returns The message which should be sent with the messages to generate the result based on the tool calls
*/
processChatActions(data?: OpenAI.Chat.ChatCompletionMessageToolCall[]): Promise<OpenAI.Chat.Completions.ChatCompletionToolMessageParam[]>;
/**
* Process the actions from the assistant run.
* @param data The tool calls generated from the assistant run. (`run.required_action.submit_tool_outputs.tool_calls`)
* @returns The tool outputs which should be sent to `runs.submitToolOutputs()` to continue the run.
*/
processAssistantActions(data?: OpenAI.Beta.Threads.Runs.RequiredActionFunctionToolCall[]): Promise<OpenAI.Beta.Threads.Runs.RunSubmitToolOutputsParams.ToolOutput[]>;
};
type CreateToolsOutput = ReturnType<typeof createTools>;
type AnyTool = ReturnType<typeof createTools> | OpenAIBuiltInTool;
/**
* Combine multiple tools into one object that can be used with an assistant.
* @param tools All tools to combine. You can provide tools created with `createTools()` or built in tools from the OpenAI API (CodeInterpreter and Retrieval).
* @returns The same object as `createTools()`, but with all tools combined.
* @see https://platform.openai.com/docs/assistants/tools - for more information on the OpenAI API tools.
* @example
* ```ts
* const { tools, processAssistantActions } = combineTools(
* createTools({
* getWeather,
* exponential,
* }),
* { type: "code_interpreter" },
* { type: "retrieval" },
* );
* ```
*/
declare function combineTools(...tools: AnyTool[]): Omit<CreateToolsOutput, "tools"> & {
tools: OpenAIBuiltInTool[];
};
export { type Tool, combineTools, createTools, t };