import type { TSchema } from "typebox"; import type { ModelCompatConfig } from "../config/types.models.js"; import { stripUnsupportedSchemaKeywords } from "../plugin-sdk/provider-tools.js"; import { resolveUnsupportedToolSchemaKeywords } from "../plugins/provider-model-compat.js"; import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; import { cleanSchemaForGemini } from "./schema/clean-for-gemini.js";
// Provider quirks: // - Gemini rejects several JSON Schema keywords, so we scrub those. // - OpenAI rejects function tool schemas unless the *top-level* is `type: "object"`. // (TypeBox root unions compile to `{ anyOf: [...] }` without `type`). // - Anthropic expects full JSON Schema draft 2020-12 compliance. // - xAI rejects validation-constraint keywords (minLength, maxLength, etc.) outright. // // Normalize once here so callers can always pass `tools` through unchanged. const normalizedProvider = normalizeLowercaseStringOrEmpty(options?.modelProvider); const isGeminiProvider =
normalizedProvider.includes("google") || normalizedProvider.includes("gemini"); const isAnthropicProvider = normalizedProvider.includes("anthropic"); const unsupportedToolSchemaKeywords = resolveUnsupportedToolSchemaKeywords(options?.modelCompat);
function applyProviderCleaning(s: unknown): TSchema { if (isGeminiProvider && !isAnthropicProvider) { return cleanSchemaForGemini(s);
} if (unsupportedToolSchemaKeywords.size > 0) { return stripUnsupportedSchemaKeywords(s, unsupportedToolSchemaKeywords) as TSchema;
} return s as TSchema;
}
if (!flattenableVariantKey) { if (isTrulyEmptySchema(schemaRecord)) { // Handle the proven MCP no-parameter case: a truly empty schema object. return applyProviderCleaning({ type: "object", properties: {} });
} if (conditionalKey === "allOf") { // Top-level `allOf` is not safely flattenable with the same heuristics we // use for unions. Keep it explicit rather than silently rewriting it. return applyProviderCleaning(schema);
} return applyProviderCleaning(schema);
} const variants = schemaRecord[flattenableVariantKey] as unknown[]; const mergedProperties: Record<string, unknown> = {}; const requiredCounts = new Map<string, number>();
let objectVariants = 0;
for (const entry of variants) { if (!entry || typeof entry !== "object") { continue;
} const props = (entry as { properties?: unknown }).properties; if (!props || typeof props !== "object") { continue;
}
objectVariants += 1; for (const [key, value] of Object.entries(props as Record<string, unknown>)) { if (!(key in mergedProperties)) {
mergedProperties[key] = value; continue;
}
mergedProperties[key] = mergePropertySchemas(mergedProperties[key], value);
} const required = Array.isArray((entry as { required?: unknown }).required)
? (entry as { required: unknown[] }).required
: []; for (const key of required) { if (typeof key !== "string") { continue;
}
requiredCounts.set(key, (requiredCounts.get(key) ?? 0) + 1);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.