Agents: Fix Gemini schema compatibility and robust model discovery
This commit is contained in:
@@ -62,8 +62,14 @@ export async function loadModelCatalog(params?: {
|
||||
typeof entry?.reasoning === "boolean" ? entry.reasoning : undefined;
|
||||
models.push({ id, name, provider, contextWindow, reasoning });
|
||||
}
|
||||
|
||||
if (models.length === 0) {
|
||||
// If we found nothing, don't cache this result so we can try again.
|
||||
modelCatalogPromise = null;
|
||||
}
|
||||
} catch {
|
||||
// Leave models empty on discovery errors.
|
||||
// Leave models empty on discovery errors and don't cache.
|
||||
modelCatalogPromise = null;
|
||||
}
|
||||
|
||||
return models.sort((a, b) => {
|
||||
|
||||
@@ -141,13 +141,80 @@ function mergePropertySchemas(existing: unknown, incoming: unknown): unknown {
|
||||
return existing;
|
||||
}
|
||||
|
||||
function cleanSchemaForGemini(schema: unknown): unknown {
|
||||
if (!schema || typeof schema !== "object") return schema;
|
||||
if (Array.isArray(schema)) return schema.map(cleanSchemaForGemini);
|
||||
|
||||
const obj = schema as Record<string, unknown>;
|
||||
const hasAnyOf = "anyOf" in obj && Array.isArray(obj.anyOf);
|
||||
const hasConst = "const" in obj;
|
||||
const cleaned: Record<string, unknown> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
// Skip unsupported schema features for Gemini:
|
||||
// - patternProperties: not in OpenAPI 3.0 subset
|
||||
// - const: convert to enum with single value instead
|
||||
if (key === "patternProperties") {
|
||||
// Gemini doesn't support patternProperties - skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert const to enum (Gemini doesn't support const)
|
||||
if (key === "const") {
|
||||
cleaned.enum = [value];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip 'type' if we have 'anyOf' — Gemini doesn't allow both
|
||||
if (key === "type" && hasAnyOf) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key === "properties" && value && typeof value === "object") {
|
||||
// Recursively clean nested properties
|
||||
const props = value as Record<string, unknown>;
|
||||
cleaned[key] = Object.fromEntries(
|
||||
Object.entries(props).map(([k, v]) => [k, cleanSchemaForGemini(v)])
|
||||
);
|
||||
} else if (key === "items" && value && typeof value === "object") {
|
||||
// Recursively clean array items schema
|
||||
cleaned[key] = cleanSchemaForGemini(value);
|
||||
} else if (key === "anyOf" && Array.isArray(value)) {
|
||||
// Clean each anyOf variant
|
||||
cleaned[key] = value.map(v => cleanSchemaForGemini(v));
|
||||
} else if (key === "oneOf" && Array.isArray(value)) {
|
||||
// Clean each oneOf variant
|
||||
cleaned[key] = value.map(v => cleanSchemaForGemini(v));
|
||||
} else if (key === "allOf" && Array.isArray(value)) {
|
||||
// Clean each allOf variant
|
||||
cleaned[key] = value.map(v => cleanSchemaForGemini(v));
|
||||
} else if (key === "additionalProperties" && value && typeof value === "object") {
|
||||
// Recursively clean additionalProperties schema
|
||||
cleaned[key] = cleanSchemaForGemini(value);
|
||||
} else {
|
||||
cleaned[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
function normalizeToolParameters(tool: AnyAgentTool): AnyAgentTool {
|
||||
const schema =
|
||||
tool.parameters && typeof tool.parameters === "object"
|
||||
? (tool.parameters as Record<string, unknown>)
|
||||
: undefined;
|
||||
if (!schema) return tool;
|
||||
if ("type" in schema && "properties" in schema) return tool;
|
||||
|
||||
// If schema already has type + properties (no top-level anyOf to merge),
|
||||
// still clean it for Gemini compatibility
|
||||
if ("type" in schema && "properties" in schema && !Array.isArray(schema.anyOf)) {
|
||||
return {
|
||||
...tool,
|
||||
parameters: cleanSchemaForGemini(schema),
|
||||
};
|
||||
}
|
||||
|
||||
if (!Array.isArray(schema.anyOf)) return tool;
|
||||
const mergedProperties: Record<string, unknown> = {};
|
||||
const requiredCounts = new Map<string, number>();
|
||||
@@ -191,10 +258,11 @@ function normalizeToolParameters(tool: AnyAgentTool): AnyAgentTool {
|
||||
.map(([key]) => key)
|
||||
: undefined;
|
||||
|
||||
const { anyOf: _unusedAnyOf, ...restSchema } = schema;
|
||||
return {
|
||||
...tool,
|
||||
parameters: {
|
||||
...schema,
|
||||
parameters: cleanSchemaForGemini({
|
||||
...restSchema,
|
||||
type: "object",
|
||||
properties:
|
||||
Object.keys(mergedProperties).length > 0
|
||||
@@ -205,7 +273,7 @@ function normalizeToolParameters(tool: AnyAgentTool): AnyAgentTool {
|
||||
: {}),
|
||||
additionalProperties:
|
||||
"additionalProperties" in schema ? schema.additionalProperties : true,
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user