UNPKG

@firefliesai/schema-forge

Version:

Transform TypeScript classes into JSON Schema definitions with automatic support for OpenAI, Anthropic, and Google Gemini function calling (tool) formats

620 lines (619 loc) 21.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.jsonSchemaToOpenAITool = jsonSchemaToOpenAITool; exports.jsonSchemaToOpenAIResponseApiTool = jsonSchemaToOpenAIResponseApiTool; exports.jsonSchemaToOpenAIResponseFormat = jsonSchemaToOpenAIResponseFormat; exports.jsonSchemaToOpenAIResponseApiTextSchema = jsonSchemaToOpenAIResponseApiTextSchema; exports.jsonSchemaToAnthropicTool = jsonSchemaToAnthropicTool; exports.jsonSchemaToGeminiTool = jsonSchemaToGeminiTool; exports.jsonSchemaToGeminiOldTool = jsonSchemaToGeminiOldTool; exports.jsonSchemaToGeminiVertexTool = jsonSchemaToGeminiVertexTool; exports.jsonSchemaToGeminiResponseSchema = jsonSchemaToGeminiResponseSchema; exports.jsonSchemaToGeminiOldResponseSchema = jsonSchemaToGeminiOldResponseSchema; exports.jsonSchemaToGeminiVertexResponseSchema = jsonSchemaToGeminiVertexResponseSchema; exports.classToOpenAITool = classToOpenAITool; exports.classToOpenAIResponseApiTool = classToOpenAIResponseApiTool; exports.classToOpenAIResponseFormatJsonSchema = classToOpenAIResponseFormatJsonSchema; exports.classToOpenAIResponseApiTextSchema = classToOpenAIResponseApiTextSchema; exports.classToGeminiTool = classToGeminiTool; exports.classToGeminiOldTool = classToGeminiOldTool; exports.classToGeminiVertexTool = classToGeminiVertexTool; exports.classToAnthropicTool = classToAnthropicTool; exports.classToGeminiResponseSchema = classToGeminiResponseSchema; exports.classToGeminiOldResponseSchema = classToGeminiOldResponseSchema; exports.classToGeminiVertexResponseSchema = classToGeminiVertexResponseSchema; exports.openAIToolToJsonSchema = openAIToolToJsonSchema; exports.openAIResponseApiToolToJsonSchema = openAIResponseApiToolToJsonSchema; /** * LLM-specific formats for different providers */ const core_1 = require("./core"); const types_1 = require("./types"); const utils_1 = require("./utils"); /** * Converts a JSON Schema to OpenAI tool format for Chat Completions API * * @example * // Convert a JSON schema to OpenAI tool format * const schema = { * type: 'object', * properties: { name: { type: 'string' } }, * required: ['name'] * }; * * const tool = jsonSchemaToOpenAITool( * schema, * { name: 'create_user', description: 'Creates a new user' }, * { strict: true } * ); */ function jsonSchemaToOpenAITool(schema, metadata, options) { return { type: 'function', function: { name: metadata.name, ...(metadata.description && { description: metadata.description }), parameters: schema, ...(options?.strict && { strict: options.strict }), }, }; } /** * Converts a JSON Schema to OpenAI tool format for Response API * * @example * // Convert a JSON schema to OpenAI Response API tool format * const schema = { * type: 'object', * properties: { name: { type: 'string' } }, * required: ['name'] * }; * * const tool = jsonSchemaToOpenAIResponseApiTool( * schema, * { name: 'create_user', description: 'Creates a new user' }, * { strict: true } * ); */ function jsonSchemaToOpenAIResponseApiTool(schema, metadata, options) { return { type: 'function', name: metadata.name, ...(metadata.description && { description: metadata.description }), parameters: schema, strict: options.strict, }; } /** * Converts a JSON Schema to OpenAI response format for Chat Completions API * * @example * // Convert a JSON schema to OpenAI response format * const schema = { * type: 'object', * properties: { name: { type: 'string' } }, * required: ['name'] * }; * * const format = jsonSchemaToOpenAIResponseFormat( * schema, * { name: 'user_profile', description: 'User profile information' }, * { strict: true } * ); */ function jsonSchemaToOpenAIResponseFormat(schema, metadata, options) { return { type: 'json_schema', json_schema: { name: metadata.name, ...(metadata.description && { description: metadata.description }), schema: schema, ...(options?.strict && { strict: options.strict }), }, }; } /** * Converts a JSON Schema to OpenAI text format for Response API * * @example * // Convert a JSON schema to OpenAI Response API text format * const schema = { * type: 'object', * properties: { name: { type: 'string' } }, * required: ['name'] * }; * * const format = jsonSchemaToOpenAIResponseApiTextSchema( * schema, * { name: 'user_profile', description: 'User profile information' }, * { strict: true } * ); */ function jsonSchemaToOpenAIResponseApiTextSchema(schema, metadata, options) { return { type: 'json_schema', name: metadata.name, ...(metadata.description && { description: metadata.description }), schema: schema, ...(options?.strict && { strict: options.strict }), }; } /** * Converts a JSON Schema to Anthropic tool format * * @example * // Convert a JSON schema to Anthropic tool format * const schema = { * type: 'object', * properties: { name: { type: 'string' } }, * required: ['name'] * }; * * const tool = jsonSchemaToAnthropicTool( * schema, * { name: 'create_user', description: 'Creates a new user' } * ); */ function jsonSchemaToAnthropicTool(schema, metadata) { return { name: metadata.name, ...(metadata.description && { description: metadata.description }), input_schema: { type: 'object', properties: schema.properties, required: schema.required || [], }, }; } /** * Converts a JSON Schema to Gemini tool format (@google/genai) * * @example * // Convert a JSON schema to Gemini tool format * const schema = { * type: 'object', * properties: { name: { type: 'string' } }, * required: ['name'] * }; * * const tool = jsonSchemaToGeminiTool( * schema, * { name: 'create_user', description: 'Creates a new user' } * ); */ function jsonSchemaToGeminiTool(schema, metadata) { return { name: metadata.name, ...(metadata.description && { description: metadata.description }), parameters: { type: types_1.Type.OBJECT, //'OBJECT', ...(metadata.description && { description: metadata.description }), properties: schema.properties, required: schema.required || [], }, }; } /** @google/generative-ai */ function jsonSchemaToGeminiOldTool(schema, metadata) { return { name: metadata.name, ...(metadata.description && { description: metadata.description }), parameters: { type: types_1.SchemaType.OBJECT, ...(metadata.description && { description: metadata.description }), properties: schema.properties, required: schema.required || [], }, }; } /** @google-cloud/vertexai */ function jsonSchemaToGeminiVertexTool(schema, metadata) { return jsonSchemaToGeminiTool(schema, metadata); } /** * Converts a JSON Schema to Gemini response schema format (@google/genai) * * @example * // Convert a JSON schema to Gemini response schema * const schema = { * type: 'object', * properties: { name: { type: 'string' } }, * required: ['name'] * }; * * const responseSchema = jsonSchemaToGeminiResponseSchema( * schema, * { description: 'User profile information' } * ); */ function jsonSchemaToGeminiResponseSchema(schema, metadata) { return { type: types_1.Type.OBJECT, ...(metadata.description && { description: metadata.description }), properties: schema.properties, required: schema.required || [], }; } /** @google/generative-ai */ function jsonSchemaToGeminiOldResponseSchema(schema, metadata) { return { type: types_1.SchemaType.OBJECT, ...(metadata.description && { description: metadata.description }), properties: schema.properties, required: schema.required || [], }; } // @google-cloud/vertexai function jsonSchemaToGeminiVertexResponseSchema(schema, metadata) { return jsonSchemaToGeminiResponseSchema(schema, metadata); } /** * Creates an OpenAI-compatible tool function from a class * * @example * // Using options object * const tool = classToOpenAITool(UserClass, { * forStructuredOutput: true, * propertyOverrides: { * 'username': { description: 'Override description' } * } * }); * * // Use with OpenAI API: * const response = await openai.chat.completions.create({ * model: "gpt-4o-mini", * messages: [...], * tools: [tool] * }); */ function classToOpenAITool(target, options) { const classOptions = Reflect.getMetadata('jsonSchema:options', target) || {}; // Get basic JSON schema without OpenAI-specific processing let jsonSchema = (0, core_1.classToJsonSchema)(target, { propertyOverrides: options?.propertyOverrides, }); // Apply OpenAI structured output processing if requested if (options?.forStructuredOutput) { jsonSchema = (0, utils_1.prepareForOpenAIStructuredOutput)(jsonSchema, true); } // Use the helper function to convert JSON schema to OpenAI tool return jsonSchemaToOpenAITool(jsonSchema, { name: classOptions.name || '', description: classOptions.description, }, { strict: !!options?.forStructuredOutput, }); } /** * Creates an OpenAI-compatible tool function for the Response API from a class * * Note: This is for use with OpenAI's newer Response API, which has a slightly * different format than the Chat Completions API. * * @example * // Create a tool for OpenAI Response API * const tool = classToOpenAIResponseApiTool(UserClass, { * forStructuredOutput: true, * }); * * // Use with OpenAI Response API: * const response = await openai.responses.create({ * model: "gpt-4o-mini", * input: "Create a user with name John Doe", * tools: [tool] * }); */ function classToOpenAIResponseApiTool(target, options) { const classOptions = Reflect.getMetadata('jsonSchema:options', target) || {}; // For Response API, use forStructuredOutput if provided, otherwise default to true const strict = options?.forStructuredOutput ?? true; // Get basic JSON schema let jsonSchema = (0, core_1.classToJsonSchema)(target, { propertyOverrides: options?.propertyOverrides, }); // Always apply OpenAI structured output processing for Response API // with handling of optional properties if strict is true jsonSchema = (0, utils_1.prepareForOpenAIStructuredOutput)(jsonSchema, strict); // Use the helper function to convert JSON schema to OpenAI Response API tool return jsonSchemaToOpenAIResponseApiTool(jsonSchema, { name: classOptions.name || '', description: classOptions.description, }, { strict, }); } /** * Creates an OpenAI response_format compatible JSON schema from a class. * Can be used for both normal and structured outputs with OpenAI chat completions. * * @example * // For structured output (common case): * const responseFormat = classToOpenAIResponseFormatJsonSchema(UserClass, { * forStructuredOutput: true, * }); * * // Use with OpenAI API: * const completion = await openai.chat.completions.create({ * model: "gpt-4o-mini", * messages: [...], * response_format: responseFormat, * }); */ function classToOpenAIResponseFormatJsonSchema(target, options) { const classOptions = Reflect.getMetadata('jsonSchema:options', target) || {}; // Get basic JSON schema let jsonSchema = (0, core_1.classToJsonSchema)(target, { propertyOverrides: options?.propertyOverrides, }); // Apply OpenAI structured output processing if requested if (options?.forStructuredOutput) { jsonSchema = (0, utils_1.prepareForOpenAIStructuredOutput)(jsonSchema, true); } // Use forStructuredOutput to determine if strict should be true const strict = !!options?.forStructuredOutput; // Use the helper function to convert JSON schema to OpenAI response format return jsonSchemaToOpenAIResponseFormat(jsonSchema, { name: classOptions.name || '', description: classOptions.description, }, { strict, }); } /** * Creates a JSON schema for structured output with OpenAI's Response API * * This function is specifically designed for OpenAI's new Response API, which has * a slightly different format for JSON schema structured output compared to * the Chat Completions API. * * @example * // Create a response format for OpenAI Response API * const responseFormat = classToOpenAIResponseApiTextSchema (UserOutput, { * forStructuredOutput: true * }); * * // Use with OpenAI Response API: * const result = await openai.responses.create({ * model: "gpt-4o-mini", * input: "Give me user information for John Doe", * text: { * format: responseFormat * } * }); * * // Parse the response * const data = JSON.parse(result.output[0].content[0].text); */ function classToOpenAIResponseApiTextSchema(target, options) { const classOptions = Reflect.getMetadata('jsonSchema:options', target) || {}; // Get basic JSON schema let jsonSchema = (0, core_1.classToJsonSchema)(target, { propertyOverrides: options?.propertyOverrides, }); // Apply OpenAI structured output processing if requested if (options?.forStructuredOutput) { jsonSchema = (0, utils_1.prepareForOpenAIStructuredOutput)(jsonSchema, true); } // Use forStructuredOutput to determine if strict should be true const strict = !!options?.forStructuredOutput; // Use the helper function to convert JSON schema to OpenAI Response API text format return jsonSchemaToOpenAIResponseApiTextSchema(jsonSchema, { name: classOptions.name || '', description: classOptions.description, }, { strict, }); } /** * Creates a Gemini-compatible tool function from a class * * @example * const toolDeclaration = classToGeminiTool(UserClass, { * propertyOverrides: { * 'username': { description: 'Custom description' } * } * }); * * // Use with Google @google/genai: * const model = genAI.getGenerativeModel({ * model: "gemini-2.0-flash-001", * tools: { functionDeclarations: [toolDeclaration] }, * }); */ function classToGeminiTool(target, options) { const classOptions = Reflect.getMetadata('jsonSchema:options', target) || {}; // Get basic JSON schema const jsonSchema = (0, core_1.classToJsonSchema)(target, { propertyOverrides: options?.propertyOverrides, }); // Note: Gemini has broader JSON Schema support and doesn't require special processing // for structured output like OpenAI does // Use the helper function to convert JSON schema to Gemini tool format return jsonSchemaToGeminiTool(jsonSchema, { name: classOptions.name || '', description: classOptions.description, }); } /** @google/generative-ai */ function classToGeminiOldTool(target, options) { const classOptions = Reflect.getMetadata('jsonSchema:options', target) || {}; // Get basic JSON schema const jsonSchema = (0, core_1.classToJsonSchema)(target, { propertyOverrides: options?.propertyOverrides, }); // Note: Gemini has broader JSON Schema support and doesn't require special processing // for structured output like OpenAI does // Use the helper function to convert JSON schema to Gemini tool format return jsonSchemaToGeminiOldTool(jsonSchema, { name: classOptions.name || '', description: classOptions.description, }); } // @google-cloud/vertexai function classToGeminiVertexTool(target, options) { return classToGeminiTool(target, options); } /** * Creates an Anthropic-compatible tool function from a class * * @example * const tool = classToAnthropicTool(UserClass, { * propertyOverrides: { * 'username': { description: 'Custom description' } * } * }); * * // Use with Anthropic API: * const message = await anthropic.messages.create({ * model: "claude-3-7-sonnet-20250219", * max_tokens: 1000, * messages: [...], * tools: [tool], * }); */ function classToAnthropicTool(target, options) { const classOptions = Reflect.getMetadata('jsonSchema:options', target) || {}; const jsonSchema = (0, core_1.classToJsonSchema)(target, options); // Use the helper function to convert JSON schema to Anthropic tool format return jsonSchemaToAnthropicTool(jsonSchema, { name: classOptions.name || '', description: classOptions.description, }); } /** * Creates a Gemini response schema for structured output * * Note: Gemini's structured output supports top-level arrays unlike OpenAI. * This function always generates an object-type schema (from a class), * but Gemini also supports array schemas like: * ``` * { * type: "ARRAY", * items: { * type: "OBJECT", * properties: {...} * } * } * ``` * If you need an array schema, you would need to manually wrap the result * or create a dedicated helper function. * * @example * const responseSchema = classToGeminiResponseSchema(RecipeClass, { * propertyOverrides: { * 'ingredients': { description: 'List of recipe ingredients' } * } * }); * * // Use with Google @google/genai: * const response = await gemini.models.generateContent({ * model: 'gemini-2.0-flash-001', * contents: userMessage, * config: { * responseMimeType: 'application/json', * responseSchema: responseSchema, * }, * }); */ function classToGeminiResponseSchema(target, options) { const classOptions = Reflect.getMetadata('jsonSchema:options', target) || {}; // Get basic JSON schema const jsonSchema = (0, core_1.classToJsonSchema)(target, { propertyOverrides: options?.propertyOverrides, }); // Note: Gemini has broader JSON Schema support and doesn't require special processing // for structured output like OpenAI does // Use the helper function to convert JSON schema to Gemini response schema format return jsonSchemaToGeminiResponseSchema(jsonSchema, { description: classOptions.description, }); } // @google/generative-ai function classToGeminiOldResponseSchema(target, options) { const classOptions = Reflect.getMetadata('jsonSchema:options', target) || {}; // Get basic JSON schema const jsonSchema = (0, core_1.classToJsonSchema)(target, { propertyOverrides: options?.propertyOverrides, }); // Note: Gemini has broader JSON Schema support and doesn't require special processing // for structured output like OpenAI does // Use the helper function to convert JSON schema to Gemini response schema format return jsonSchemaToGeminiOldResponseSchema(jsonSchema, { description: classOptions.description, }); } /** @google-cloud/vertexai */ function classToGeminiVertexResponseSchema(target, options) { return classToGeminiResponseSchema(target, options); } /** * Extracts JSON Schema and metadata from an OpenAI Chat Completions API tool format * * This function is useful when you want to convert an existing OpenAI tool definition * to another LLM format (e.g., Anthropic or Gemini) or when you need to extract * the JSON Schema for other purposes. * * @example * // Extract JSON Schema from an OpenAI tool * const openaiTool = { * type: 'function', * function: { * name: 'get_user', * description: 'Get user information', * parameters: { * type: 'object', * properties: { id: { type: 'string' } }, * required: ['id'] * } * } * }; * * const { schema, metadata } = openAIToolToJsonSchema(openaiTool); * * // Convert to Anthropic format * const anthropicTool = jsonSchemaToAnthropicTool(schema, metadata); */ function openAIToolToJsonSchema(openAITool) { return { schema: openAITool.function.parameters, metadata: { name: openAITool.function.name, ...(openAITool.function.description && { description: openAITool.function.description }), }, }; } /** * Extracts JSON Schema and metadata from an OpenAI Response API tool format * * This function is useful when you want to convert an existing OpenAI Response API tool * definition to another LLM format (e.g., Anthropic or Gemini) or when you need to extract * the JSON Schema for other purposes. * * @example * // Extract JSON Schema from an OpenAI Response API tool * const responseApiTool = { * type: 'function', * name: 'get_user', * description: 'Get user information', * parameters: { * type: 'object', * properties: { id: { type: 'string' } }, * required: ['id'] * }, * strict: true * }; * * const { schema, metadata } = openAIResponseApiToolToJsonSchema(responseApiTool); * * // Convert to Gemini format * const geminiTool = jsonSchemaToGeminiTool(schema, metadata); */ function openAIResponseApiToolToJsonSchema(openAITool) { return { schema: openAITool.parameters, metadata: { name: openAITool.name, ...(openAITool.description && { description: openAITool.description }), }, }; }