@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
257 lines • 10.6 kB
JavaScript
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