UNPKG

@openai/agents-core

Version:

The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.

257 lines • 10.6 kB
import { zodResponsesFunction, zodTextFormat } from 'openai/helpers/zod'; import { UserError } from "../errors.mjs"; import { isZodObject } from "./typeGuards.mjs"; import { zodJsonSchemaCompat, zodJsonSchemaCompatForOpenAIStrict, assertLosslessOpenAIStrictZodSchemaConversion, hasJsonSchemaObjectShape, mergeJsonSchemaDescriptions, } from "./zodJsonSchemaCompat.mjs"; import { asZodType } from "./zodCompat.mjs"; import { prepareOpenAIStrictToolSchema, stripStrictNullsForZodSchema, toOpenAIStrictToolSchema, } from "./strictToolSchema.mjs"; import { assertStandardSchemaObjectRoot, hasStandardSchemaMarker, isStandardSchemaWithJSON, standardSchemaToJsonSchema, validateStandardSchema, unsupportedStandardSchemaError, } from "./standardSchema.mjs"; const zodResponsesFunctionCompat = zodResponsesFunction; // The `.schema` payload is all we need, so a lightweight signature keeps the compiler happy. const zodTextFormatCompat = zodTextFormat; // openai/helpers/zod cannot emit strict schemas for every Zod runtime // (notably Zod v4), so we delegate to a small local converter living in // zodJsonSchemaCompat.ts whenever its output is missing the required JSON Schema bits. function buildJsonSchemaFromZod(inputType) { return zodJsonSchemaCompat(inputType); } /** * Convert a string to a function tool name by replacing spaces with underscores and * non-alphanumeric characters with underscores. * @param name - The name of the tool. * @returns The function tool name. */ export function toFunctionToolName(name) { // Replace spaces with underscores name = name.replace(/\s/g, '_'); // Replace non-alphanumeric characters with underscores name = name.replace(/[^a-zA-Z0-9]/g, '_'); // Ensure the name is not empty if (name.length === 0) { throw new Error('Tool name cannot be empty'); } return name; } /** * Get the schema and parser from an input type. Supported Standard Schema and Zod values are * converted to JSON Schema and retain runtime validation. Plain JSON Schema values use JSON.parse. * @param inputType - The input type to get the schema and parser from. * @param name - The name of the tool. * @returns The schema and parser. */ export function getSchemaAndParserFromInputType(inputType, name, options = {}) { const parser = (input) => JSON.parse(input); if (isZodObject(inputType)) { const useFallback = (originalError) => { const strictFallback = options.strict ? zodJsonSchemaCompatForOpenAIStrict(inputType) : undefined; if (strictFallback?.loweredObjectIntersection || strictFallback?.loweredPrimitiveIntersection) { assertLosslessOpenAIStrictZodSchemaConversion(strictFallback); } const fallbackSchema = options.strict ? strictFallback?.schema : buildJsonSchemaFromZod(inputType); if (fallbackSchema) { return { schema: options.strict ? toOpenAIStrictToolSchema(fallbackSchema) : fallbackSchema, parser: (rawInput) => options.strict ? inputType.parse(stripStrictNullsForZodSchema(inputType, JSON.parse(rawInput))) : inputType.parse(JSON.parse(rawInput)), }; } const errorMessage = originalError instanceof Error ? ` Upstream helper error: ${originalError.message}` : ''; throw new UserError(`Unable to convert the provided Zod schema to JSON Schema. Ensure that the \`zod\` package is available at runtime or provide a JSON schema object instead.${errorMessage}`); }; let formattedFunction; try { formattedFunction = zodResponsesFunctionCompat({ name, parameters: asZodType(inputType), function: () => { }, // empty function here to satisfy the OpenAI helper description: '', }); } catch (error) { return useFallback(error); } if (hasJsonSchemaObjectShape(formattedFunction.parameters)) { const strictFallback = options.strict ? zodJsonSchemaCompatForOpenAIStrict(inputType) : undefined; const fallbackSchema = strictFallback?.schema ?? buildJsonSchemaFromZod(inputType); if (fallbackSchema) { mergeJsonSchemaDescriptions(formattedFunction.parameters, fallbackSchema); } const upstreamSchema = formattedFunction.parameters; const strictSchemaSource = strictFallback?.loweredObjectIntersection || (strictFallback?.loweredPrimitiveIntersection && containsJsonSchemaAllOf(upstreamSchema)) ? useLosslessStrictFallback(strictFallback) : upstreamSchema; return { schema: options.strict ? toOpenAIStrictToolSchema(strictSchemaSource) : formattedFunction.parameters, parser: options.strict ? (rawInput) => inputType.parse(stripStrictNullsForZodSchema(inputType, JSON.parse(rawInput))) : formattedFunction.$parseRaw, }; } return useFallback(); } else if (isStandardSchemaWithJSON(inputType)) { if (!options.strict) { throw new UserError('Strict mode is required for Standard Schema parameters'); } const inputSchema = standardSchemaToJsonSchema(inputType, 'input'); assertStandardSchemaObjectRoot(inputSchema, 'Tool parameter'); const preparedSchema = prepareOpenAIStrictToolSchema(inputSchema); return { schema: preparedSchema.schema, parser: (rawInput) => validateStandardSchema(inputType, preparedSchema.normalizeInput(JSON.parse(rawInput))), }; } else if (hasStandardSchemaMarker(inputType)) { throw unsupportedStandardSchemaError(); } else if (typeof inputType === 'object' && inputType !== null) { const jsonSchema = inputType; const preparedSchema = options.strict ? prepareOpenAIStrictToolSchema(jsonSchema) : undefined; return { schema: preparedSchema?.schema ?? jsonSchema, parser: preparedSchema ? (rawInput) => preparedSchema.normalizeInput(JSON.parse(rawInput)) : parser, }; } throw new UserError('Input type is not a supported Standard Schema or a valid JSON schema'); } function useLosslessStrictFallback(conversion) { assertLosslessOpenAIStrictZodSchemaConversion(conversion); return conversion.schema; } function containsJsonSchemaAllOf(value, visited = new WeakSet()) { if (typeof value !== 'object' || value === null || Array.isArray(value) || visited.has(value)) { return false; } visited.add(value); const schema = value; if (Array.isArray(schema.allOf)) { return true; } for (const key of [ 'additionalItems', 'additionalProperties', 'contains', 'contentSchema', 'else', 'if', 'items', 'not', 'propertyNames', 'then', 'unevaluatedItems', 'unevaluatedProperties', ]) { const nested = schema[key]; if (Array.isArray(nested)) { if (nested.some((entry) => containsJsonSchemaAllOf(entry, visited))) { return true; } } else if (containsJsonSchemaAllOf(nested, visited)) { return true; } } for (const key of ['anyOf', 'oneOf', 'prefixItems']) { const nested = schema[key]; if (Array.isArray(nested) && nested.some((entry) => containsJsonSchemaAllOf(entry, visited))) { return true; } } for (const key of [ '$defs', 'definitions', 'dependencies', 'dependentSchemas', 'patternProperties', 'properties', ]) { const nested = schema[key]; if (typeof nested === 'object' && nested !== null && !Array.isArray(nested) && Object.values(nested).some((entry) => containsJsonSchemaAllOf(entry, visited))) { return true; } } return false; } /** * Converts the agent output type provided to a serializable version */ export function convertAgentOutputTypeToSerializable(outputType) { if (outputType === 'text') { return 'text'; } if (isZodObject(outputType)) { const useFallback = (existing, originalError) => { const fallbackSchema = buildJsonSchemaFromZod(outputType); if (fallbackSchema) { return { type: existing?.type ?? 'json_schema', name: existing?.name ?? 'output', strict: existing?.strict ?? false, schema: fallbackSchema, }; } const errorMessage = originalError instanceof Error ? ` Upstream helper error: ${originalError.message}` : ''; throw new UserError(`Unable to convert the provided Zod schema to JSON Schema. Ensure that the \`zod\` package is available at runtime or provide a JSON schema object instead.${errorMessage}`); }; let output; try { output = zodTextFormatCompat(asZodType(outputType), 'output'); } catch (error) { return useFallback(undefined, error); } if (hasJsonSchemaObjectShape(output.schema)) { return { type: output.type, name: output.name, strict: output.strict || false, schema: output.schema, }; } return useFallback(output); } if (isStandardSchemaWithJSON(outputType)) { const schema = standardSchemaToJsonSchema(outputType, 'input'); assertStandardSchemaObjectRoot(schema, 'Agent output'); return { type: 'json_schema', name: 'output', strict: true, schema: prepareOpenAIStrictToolSchema(schema) .schema, }; } if (hasStandardSchemaMarker(outputType)) { throw unsupportedStandardSchemaError(); } return outputType; } //# sourceMappingURL=tools.mjs.map