UNPKG

@unified-llm/core

Version:

Unified LLM interface (in-memory).

217 lines 6.99 kB
/** * ResponseFormat class for unified structured output interface * Provides a consistent way to define structured output across different LLM providers */ export class ResponseFormat { constructor(config) { // Validate name format for OpenAI compatibility if (!/^[a-zA-Z0-9_-]+$/.test(config.name)) { throw new Error(`Invalid response format name: "${config.name}". ` + 'Name must contain only letters, numbers, underscores, and hyphens (pattern: ^[a-zA-Z0-9_-]+$)'); } this.config = config; } /** * Convert to OpenAI's response_format structure */ toOpenAI() { var _a; // OpenAI requires additionalProperties: false for strict mode const schema = this.ensureAdditionalPropertiesFalse(this.config.schema); return { type: 'json_schema', json_schema: { name: this.config.name, description: this.config.description, schema, strict: (_a = this.config.strict) !== null && _a !== void 0 ? _a : true } }; } /** * Convert to Google's responseSchema structure */ toGoogle() { return { responseMimeType: 'application/json', responseSchema: this.convertToGoogleSchema(this.config.schema) }; } /** * Convert to Anthropic's format using prompt engineering */ toAnthropic() { const schemaString = JSON.stringify(this.config.schema, null, 2); return { type: 'json_object', schema: this.config.schema, promptSuffix: `\n\nAnalyze this feedback and output in JSON format with ${schemaString}` }; } /** * Get the raw unified format */ toUnified() { return { type: 'json_object', schema: this.config.schema }; } /** * Add response format instruction to messages (for Anthropic) */ addRequestSuffix(messages) { const schemaString = JSON.stringify(this.config.schema, null, 2); const promptSuffix = `\n\nAnalyze this feedback and output in JSON format with ${schemaString}`; const result = [...messages]; let lastUserMessageIndex = -1; for (let i = result.length - 1; i >= 0; i--) { if (result[i].role === 'user') { lastUserMessageIndex = i; break; } } if (lastUserMessageIndex !== -1) { const lastMessage = result[lastUserMessageIndex]; const lastTextContent = lastMessage.content.find((c) => c.type === 'text'); if (lastTextContent) { result[lastUserMessageIndex] = { ...lastMessage, content: lastMessage.content.map((c) => c.type === 'text' && c === lastTextContent ? { ...c, text: c.text + promptSuffix } : c) }; } } return result; } /** * Convert JSON Schema to Google's Schema format */ convertToGoogleSchema(schema) { const converted = { type: this.mapToGoogleType(schema.type) }; if (schema.description) { converted.description = schema.description; } if (schema.type === 'object' && schema.properties) { converted.properties = {}; for (const [key, value] of Object.entries(schema.properties)) { converted.properties[key] = this.convertToGoogleSchema(value); } if (schema.required) { converted.required = schema.required; } } if (schema.type === 'array' && schema.items) { converted.items = this.convertToGoogleSchema(schema.items); } if (schema.enum) { converted.enum = schema.enum; } return converted; } mapToGoogleType(type) { const typeMap = { 'object': 'OBJECT', 'array': 'ARRAY', 'string': 'STRING', 'number': 'NUMBER', 'boolean': 'BOOLEAN', 'null': 'NULL' }; return typeMap[type] || 'STRING'; } /** * Ensure all object schemas have additionalProperties: false for OpenAI compatibility */ ensureAdditionalPropertiesFalse(schema) { const result = { ...schema }; if (schema.type === 'object') { // Set additionalProperties to false if not explicitly set if (result.additionalProperties === undefined) { result.additionalProperties = false; } // Recursively apply to nested object properties if (schema.properties) { result.properties = {}; for (const [key, value] of Object.entries(schema.properties)) { result.properties[key] = this.ensureAdditionalPropertiesFalse(value); } } } // Handle array items if (schema.type === 'array' && schema.items) { result.items = this.ensureAdditionalPropertiesFalse(schema.items); } return result; } } /** * Helper function to create a ResponseFormat instance */ export function createResponseFormat(config) { return new ResponseFormat(config); } /** * Pre-defined response format templates */ export const ResponseFormats = { /** * Simple key-value pair response */ keyValue: (keys) => new ResponseFormat({ name: 'key_value_response', schema: { type: 'object', properties: keys.reduce((acc, key) => ({ ...acc, [key]: { type: 'string' } }), {}), required: keys, additionalProperties: false }, strict: true }), /** * List of items */ list: (itemSchema) => new ResponseFormat({ name: 'list_response', schema: { type: 'object', properties: { items: { type: 'array', items: itemSchema } }, required: ['items'], additionalProperties: false }, strict: true }), /** * Classification response */ classification: (categories) => new ResponseFormat({ name: 'classification_response', schema: { type: 'object', properties: { category: { type: 'string', enum: categories }, confidence: { type: 'number' } }, required: ['category', 'confidence'], additionalProperties: false }, strict: true }) }; //# sourceMappingURL=response-format.js.map