UNPKG

@unified-llm/core

Version:

Unified LLM interface (in-memory).

130 lines 4.51 kB
const GEMINI_JSON_SCHEMA_ALLOWED_KEYS = new Set([ "$id", "$defs", "$ref", "$anchor", "type", "format", "title", "description", "enum", "items", "prefixItems", "minItems", "maxItems", "minimum", "maximum", "anyOf", "oneOf", "properties", "additionalProperties", "required", "propertyOrdering", // Geminiの非標準キー ]); function isPlainObject(v) { return !!v && typeof v === "object" && !Array.isArray(v); } /** * OpenAIのJSON SchemaをGeminiのサブセットに寄せる(落ちにくくする) * - 未対応キーを落とす * - definitions -> $defs に寄せる * - $ref がある sub-schema は “$系” 以外を落とす(Gemini doc の制約に合わせる) * - object には propertyOrdering を自動付与(特に Gemini 2.0 対策) */ export function coerceJsonSchemaForGemini(input) { const walk = (node) => { if (Array.isArray(node)) return node.map(walk); if (!isPlainObject(node)) return node; // definitions -> $defs const defs = isPlainObject(node.$defs) ? node.$defs : isPlainObject(node.definitions) ? node.definitions : undefined; // $ref があるなら “$系”以外を落とす(Geminiの制約) if (typeof node.$ref === "string" && node.$ref.length > 0) { const out = {}; if (typeof node.$id === "string") out.$id = node.$id; if (typeof node.$anchor === "string") out.$anchor = node.$anchor; if (defs) { const coercedDefs = {}; for (const [k, v] of Object.entries(defs)) coercedDefs[k] = walk(v); out.$defs = coercedDefs; } out.$ref = node.$ref; return out; } const out = {}; if (defs) { const coercedDefs = {}; for (const [k, v] of Object.entries(defs)) coercedDefs[k] = walk(v); out.$defs = coercedDefs; } for (const [k, v] of Object.entries(node)) { if (k === "definitions") continue; // $defsへ寄せたので捨てる if (!GEMINI_JSON_SCHEMA_ALLOWED_KEYS.has(k)) continue; switch (k) { case "properties": { if (!isPlainObject(v)) break; const props = {}; for (const [pk, pv] of Object.entries(v)) props[pk] = walk(pv); out.properties = props; break; } case "items": { // items が配列で来る(旧仕様)ことがあるので prefixItems に寄せる if (Array.isArray(v)) { out.prefixItems = v.map(walk); } else { out.items = walk(v); } break; } case "prefixItems": { if (Array.isArray(v)) out.prefixItems = v.map(walk); break; } case "anyOf": case "oneOf": { if (Array.isArray(v)) out[k] = v.map(walk); break; } case "additionalProperties": { // boolean or schema if (typeof v === "boolean") out.additionalProperties = v; else out.additionalProperties = walk(v); break; } default: out[k] = walk(v); } } // object の場合 propertyOrdering を自動付与(Gemini 2.0 の “要求”に寄せる) const typeVal = out.type; const isObjectType = typeVal === "object" || (Array.isArray(typeVal) && typeVal.includes("object")); if (isObjectType && !Array.isArray(out.propertyOrdering) && isPlainObject(out.properties)) { out.propertyOrdering = Object.keys(out.properties); } return out; }; return walk(input); } //# sourceMappingURL=coerceJsonSchemaForGemini.js.map