@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
JavaScript
"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 }),
},
};
}