n8n
Version:
n8n Workflow Automation Tool
120 lines • 4.21 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.discoverStructuredOutputSchema = discoverStructuredOutputSchema;
exports.conformContentToSchema = conformContentToSchema;
exports.applyStructuredOutputConformance = applyStructuredOutputConformance;
function isRecord(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function looksLikeJsonSchema(value) {
return (isRecord(value) &&
('properties' in value || 'additionalProperties' in value || value.type === 'object'));
}
function contentToText(content) {
if (typeof content === 'string')
return content;
if (Array.isArray(content)) {
return content
.map((part) => typeof part === 'string'
? part
: isRecord(part) && typeof part.text === 'string'
? part.text
: '')
.join('\n');
}
return '';
}
const FENCED_JSON = /```(?:json)?\s*([\s\S]*?)```/gi;
function discoverStructuredOutputSchema(body) {
if (!isRecord(body))
return undefined;
const text = body.text;
if (isRecord(text) && isRecord(text.format) && looksLikeJsonSchema(text.format.schema)) {
return text.format.schema;
}
const responseFormat = body.response_format;
if (isRecord(responseFormat) &&
isRecord(responseFormat.json_schema) &&
looksLikeJsonSchema(responseFormat.json_schema.schema)) {
return responseFormat.json_schema.schema;
}
const items = Array.isArray(body.input)
? body.input
: Array.isArray(body.messages)
? body.messages
: [];
let found;
for (const item of items) {
if (!isRecord(item))
continue;
const itemText = contentToText(item.content);
if (!itemText.includes('```'))
continue;
FENCED_JSON.lastIndex = 0;
let match;
while ((match = FENCED_JSON.exec(itemText)) !== null) {
try {
const parsed = JSON.parse(match[1].trim());
if (looksLikeJsonSchema(parsed))
found = parsed;
}
catch {
}
}
}
return found;
}
function schemaProperties(schema) {
return isRecord(schema) && isRecord(schema.properties) ? schema.properties : undefined;
}
function isStrictObject(schema) {
return isRecord(schema) && schema.additionalProperties === false;
}
function pruneToSchema(value, schema) {
if (isRecord(value)) {
const props = schemaProperties(schema);
if (!props)
return value;
const strict = isStrictObject(schema);
const out = {};
for (const [key, val] of Object.entries(value)) {
const declared = key in props;
if (strict && !declared)
continue;
out[key] = declared ? pruneToSchema(val, props[key]) : val;
}
return out;
}
if (Array.isArray(value) && isRecord(schema) && schema.type === 'array') {
return value.map((element) => pruneToSchema(element, schema.items));
}
return value;
}
function conformContentToSchema(content, schema) {
if (!schema)
return content;
const fenced = /^```(?:json)?\s*([\s\S]*?)```$/i.exec(content.trim());
const jsonText = fenced ? fenced[1].trim() : content.trim();
let parsed;
try {
parsed = JSON.parse(jsonText);
}
catch {
return content;
}
if (isRecord(parsed) && isStrictObject(schema)) {
const keys = Object.keys(parsed);
const props = schemaProperties(schema) ?? {};
const soleKey = keys[0];
if (keys.length === 1 && !(soleKey in props) && isRecord(parsed[soleKey])) {
parsed = parsed[soleKey];
}
}
const conformed = pruneToSchema(parsed, schema);
const serialized = JSON.stringify(conformed);
return fenced ? `\`\`\`json\n${serialized}\n\`\`\`` : serialized;
}
function applyStructuredOutputConformance(content, body) {
return conformContentToSchema(content, discoverStructuredOutputSchema(body));
}
//# sourceMappingURL=structured-output-conformance.js.map