(obj: any)
| 1486 | } |
| 1487 | |
| 1488 | const sanitizeGemini = (obj: any): any => { |
| 1489 | if (obj === null || typeof obj !== "object") { |
| 1490 | return obj |
| 1491 | } |
| 1492 | |
| 1493 | if (Array.isArray(obj)) { |
| 1494 | return obj.map(sanitizeGemini) |
| 1495 | } |
| 1496 | |
| 1497 | const result: any = {} |
| 1498 | for (const [key, value] of Object.entries(obj)) { |
| 1499 | if (key === "enum" && Array.isArray(value)) { |
| 1500 | // Convert all enum values to strings |
| 1501 | result[key] = value.map((v) => String(v)) |
| 1502 | // If we have integer type with enum, change type to string |
| 1503 | if (result.type === "integer" || result.type === "number") { |
| 1504 | result.type = "string" |
| 1505 | } |
| 1506 | } else if (typeof value === "object" && value !== null) { |
| 1507 | result[key] = sanitizeGemini(value) |
| 1508 | } else { |
| 1509 | result[key] = value |
| 1510 | } |
| 1511 | } |
| 1512 | |
| 1513 | // Gemini requires a single `type`, not a JSON Schema type array such as |
| 1514 | // `["number","string"]` (emitted by some MCP servers). Plain `@ai-sdk/google` |
| 1515 | // rewrites these into an `anyOf` of single-type schemas, but OpenAI-compatible |
| 1516 | // transports (e.g. GitHub Copilot proxying to Gemini) forward them verbatim |
| 1517 | // and the backend rejects the array form. Mirror the SDK: split non-null |
| 1518 | // types into `anyOf`, and lift `null` into `nullable`. |
| 1519 | if (Array.isArray(result.type)) { |
| 1520 | const hasNull = result.type.includes("null") |
| 1521 | const nonNull = result.type.filter((entry: unknown) => entry !== "null") |
| 1522 | if (nonNull.length === 0) { |
| 1523 | result.type = "null" |
| 1524 | } else { |
| 1525 | delete result.type |
| 1526 | result.anyOf = nonNull.map((entry: unknown) => ({ type: entry })) |
| 1527 | if (hasNull) result.nullable = true |
| 1528 | } |
| 1529 | } |
| 1530 | |
| 1531 | // Filter required array to only include fields that exist in properties |
| 1532 | if (result.type === "object" && result.properties && Array.isArray(result.required)) { |
| 1533 | result.required = result.required.filter((field: any) => field in result.properties) |
| 1534 | } |
| 1535 | |
| 1536 | if (result.type === "array" && !hasCombiner(result)) { |
| 1537 | if (result.items == null) { |
| 1538 | result.items = {} |
| 1539 | } |
| 1540 | // Ensure items has a type only when it's still schema-empty. |
| 1541 | if (isPlainObject(result.items) && !hasSchemaIntent(result.items)) { |
| 1542 | result.items.type = "string" |
| 1543 | } |
| 1544 | } |
| 1545 |
no test coverage detected