Merge branch 'main' into feature/webui

This commit is contained in:
Wha1eChai
2026-01-10 04:46:30 +08:00
committed by GitHub
7 changed files with 350 additions and 18 deletions

View File

@@ -9,7 +9,7 @@ import {
isThinkingModel
} from '../constants.js';
import { convertContentToParts, convertRole } from './content-converter.js';
import { sanitizeSchema, cleanSchemaForGemini } from './schema-sanitizer.js';
import { sanitizeSchema, cleanSchema } from './schema-sanitizer.js';
import {
restoreThinkingSignatures,
removeTrailingThinkingBlocks,
@@ -210,10 +210,11 @@ export function convertAnthropicToGoogle(anthropicRequest) {
// Sanitize schema for general compatibility
let parameters = sanitizeSchema(schema);
// For Gemini models, apply additional cleaning for VALIDATED mode
if (isGeminiModel) {
parameters = cleanSchemaForGemini(parameters);
}
// Apply Google-format cleaning for ALL models since they all go through
// Cloud Code API which validates schemas using Google's protobuf format.
// This fixes issue #82: /compact command fails with schema transformation error
// "Proto field is not repeating, cannot start list" for Claude models.
parameters = cleanSchema(parameters);
return {
name: String(name).replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 64),

View File

@@ -564,6 +564,27 @@ export function sanitizeSchema(schema) {
return sanitized;
}
/**
* Convert JSON Schema type names to Google's Protobuf-style uppercase type names.
* Google's Generative AI API expects uppercase types: STRING, OBJECT, ARRAY, etc.
*
* @param {string} type - JSON Schema type name (lowercase)
* @returns {string} Google-format type name (uppercase)
*/
function toGoogleType(type) {
if (!type || typeof type !== 'string') return type;
const typeMap = {
'string': 'STRING',
'number': 'NUMBER',
'integer': 'INTEGER',
'boolean': 'BOOLEAN',
'array': 'ARRAY',
'object': 'OBJECT',
'null': 'STRING' // Fallback for null type
};
return typeMap[type.toLowerCase()] || type.toUpperCase();
}
/**
* Cleans JSON schema for Gemini API compatibility.
* Uses a multi-phase pipeline matching opencode-antigravity-auth approach.
@@ -571,9 +592,9 @@ export function sanitizeSchema(schema) {
* @param {Object} schema - The JSON schema to clean
* @returns {Object} Cleaned schema safe for Gemini API
*/
export function cleanSchemaForGemini(schema) {
export function cleanSchema(schema) {
if (!schema || typeof schema !== 'object') return schema;
if (Array.isArray(schema)) return schema.map(cleanSchemaForGemini);
if (Array.isArray(schema)) return schema.map(cleanSchema);
// Phase 1: Convert $refs to hints
let result = convertRefsToHints(schema);
@@ -620,16 +641,16 @@ export function cleanSchemaForGemini(schema) {
if (result.properties && typeof result.properties === 'object') {
const newProps = {};
for (const [key, value] of Object.entries(result.properties)) {
newProps[key] = cleanSchemaForGemini(value);
newProps[key] = cleanSchema(value);
}
result.properties = newProps;
}
if (result.items) {
if (Array.isArray(result.items)) {
result.items = result.items.map(cleanSchemaForGemini);
result.items = result.items.map(cleanSchema);
} else if (typeof result.items === 'object') {
result.items = cleanSchemaForGemini(result.items);
result.items = cleanSchema(result.items);
}
}
@@ -642,5 +663,11 @@ export function cleanSchemaForGemini(schema) {
}
}
// Phase 5: Convert type to Google's uppercase format (STRING, OBJECT, ARRAY, etc.)
// Only convert at current level - nested types already converted by recursive cleanSchema calls
if (result.type && typeof result.type === 'string') {
result.type = toGoogleType(result.type);
}
return result;
}