(model: Provider.Model, schema: JSONSchema7)
| 1417 | } |
| 1418 | |
| 1419 | export function schema(model: Provider.Model, schema: JSONSchema7): JSONSchema7 { |
| 1420 | /* |
| 1421 | if (["openai", "azure"].includes(providerID)) { |
| 1422 | if (schema.type === "object" && schema.properties) { |
| 1423 | for (const [key, value] of Object.entries(schema.properties)) { |
| 1424 | if (schema.required?.includes(key)) continue |
| 1425 | schema.properties[key] = { |
| 1426 | anyOf: [ |
| 1427 | value as JSONSchema.JSONSchema, |
| 1428 | { |
| 1429 | type: "null", |
| 1430 | }, |
| 1431 | ], |
| 1432 | } |
| 1433 | } |
| 1434 | } |
| 1435 | } |
| 1436 | */ |
| 1437 | |
| 1438 | if (model.api.npm === "@ai-sdk/openai" || model.api.npm === "@ai-sdk/azure") { |
| 1439 | schema = sanitizeOpenAISchema(schema) as JSONSchema7 |
| 1440 | // Codex also applies lossy compaction above 4 KB; defer that until OpenCode needs the same schema budget. |
| 1441 | } |
| 1442 | |
| 1443 | if (model.providerID === "moonshotai" || model.api.id.toLowerCase().includes("kimi")) { |
| 1444 | const sanitizeMoonshot = (obj: unknown): unknown => { |
| 1445 | if (obj === null || typeof obj !== "object") return obj |
| 1446 | if (Array.isArray(obj)) return obj.map(sanitizeMoonshot) |
| 1447 | // Moonshot expands $ref before validation and rejects sibling keywords like description on the same node. |
| 1448 | if ("$ref" in obj && typeof obj.$ref === "string") return { $ref: obj.$ref } |
| 1449 | const result = Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, sanitizeMoonshot(value)])) |
| 1450 | // MFJS does not support tuple-style `items` arrays; it requires one schema object for all array items. |
| 1451 | if (Array.isArray(result.items)) result.items = result.items[0] ?? {} |
| 1452 | return result |
| 1453 | } |
| 1454 | |
| 1455 | const sanitized = sanitizeMoonshot(schema) |
| 1456 | if (typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized)) { |
| 1457 | schema = sanitized |
| 1458 | } |
| 1459 | } |
| 1460 | |
| 1461 | // Convert integer enums to string enums for Google/Gemini |
| 1462 | if (model.providerID === "google" || model.api.id.includes("gemini")) { |
| 1463 | const isPlainObject = (node: unknown): node is Record<string, any> => |
| 1464 | typeof node === "object" && node !== null && !Array.isArray(node) |
| 1465 | const hasCombiner = (node: unknown) => |
| 1466 | isPlainObject(node) && (Array.isArray(node.anyOf) || Array.isArray(node.oneOf) || Array.isArray(node.allOf)) |
| 1467 | const hasSchemaIntent = (node: unknown) => { |
| 1468 | if (!isPlainObject(node)) return false |
| 1469 | if (hasCombiner(node)) return true |
| 1470 | return [ |
| 1471 | "type", |
| 1472 | "properties", |
| 1473 | "items", |
| 1474 | "prefixItems", |
| 1475 | "enum", |
| 1476 | "const", |
nothing calls this directly
no test coverage detected