



















@@ -1,7 +1,10 @@
11import type { TSchema } from "typebox";
22import type { ModelCompatConfig } from "../config/types.models.js";
33import { stripUnsupportedSchemaKeywords } from "../plugin-sdk/provider-tools.js";
4-import { resolveUnsupportedToolSchemaKeywords } from "../plugins/provider-model-compat.js";
4+import {
5+resolveUnsupportedToolSchemaKeywords,
6+shouldOmitEmptyArrayItems,
7+} from "../plugins/provider-model-compat.js";
58import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
69import { cleanSchemaForGemini } from "./schema/clean-for-gemini.js";
710@@ -224,6 +227,88 @@ function normalizeArraySchemasMissingItems(schema: unknown): unknown {
224227return changed ? nextSchema : schema;
225228}
226229230+function schemaAllowsArrayType(schema: Record<string, unknown>): boolean {
231+const type = schema.type;
232+return type === "array" || (Array.isArray(type) && type.includes("array"));
233+}
234+235+const ARRAY_ITEMS_SCHEMA_OBJECT_KEYS = new Set([
236+"additionalProperties",
237+"contains",
238+"else",
239+"if",
240+"items",
241+"not",
242+"propertyNames",
243+"then",
244+]);
245+246+const ARRAY_ITEMS_SCHEMA_ARRAY_KEYS = new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
247+248+const ARRAY_ITEMS_SCHEMA_MAP_KEYS = new Set([
249+"$defs",
250+"definitions",
251+"dependentSchemas",
252+"patternProperties",
253+"properties",
254+]);
255+256+function stripEmptyArrayItemsFromArraySchemas(schema: unknown): unknown {
257+if (Array.isArray(schema)) {
258+let changed = false;
259+const entries = schema.map((entry) => {
260+const next = stripEmptyArrayItemsFromArraySchemas(entry);
261+changed ||= next !== entry;
262+return next;
263+});
264+return changed ? entries : schema;
265+}
266+if (!isSchemaRecord(schema)) {
267+return schema;
268+}
269+270+let changed = false;
271+const entries = Object.entries(schema).flatMap(([key, value]) => {
272+if (
273+key === "items" &&
274+schemaAllowsArrayType(schema) &&
275+isSchemaRecord(value) &&
276+isTrulyEmptySchema(value)
277+) {
278+changed = true;
279+return [];
280+}
281+282+if (ARRAY_ITEMS_SCHEMA_OBJECT_KEYS.has(key)) {
283+const next = stripEmptyArrayItemsFromArraySchemas(value);
284+changed ||= next !== value;
285+return [[key, next] as const];
286+}
287+288+if (ARRAY_ITEMS_SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
289+const next = stripEmptyArrayItemsFromArraySchemas(value);
290+changed ||= next !== value;
291+return [[key, next] as const];
292+}
293+294+if (ARRAY_ITEMS_SCHEMA_MAP_KEYS.has(key) && isSchemaRecord(value)) {
295+let mapChanged = false;
296+const next = Object.fromEntries(
297+Object.entries(value).map(([entryKey, entryValue]) => {
298+const entryNext = stripEmptyArrayItemsFromArraySchemas(entryValue);
299+mapChanged ||= entryNext !== entryValue;
300+return [entryKey, entryNext] as const;
301+}),
302+);
303+changed ||= mapChanged;
304+return [[key, mapChanged ? next : value] as const];
305+}
306+307+return [[key, value] as const];
308+});
309+return changed ? Object.fromEntries(entries) : schema;
310+}
311+227312export function normalizeToolParameterSchema(
228313schema: unknown,
229314options?: { modelProvider?: string; modelId?: string; modelCompat?: ModelCompatConfig },
@@ -247,16 +332,23 @@ export function normalizeToolParameterSchema(
247332normalizedProvider.includes("google") || normalizedProvider.includes("gemini");
248333const isAnthropicProvider = normalizedProvider.includes("anthropic");
249334const unsupportedToolSchemaKeywords = resolveUnsupportedToolSchemaKeywords(options?.modelCompat);
335+const omitEmptyArrayItems = shouldOmitEmptyArrayItems(options?.modelCompat);
250336251337function applyProviderCleaning(s: unknown): TSchema {
252338const normalizedSchema = normalizeArraySchemasMissingItems(s);
339+const arrayItemsCompatibleSchema = omitEmptyArrayItems
340+ ? stripEmptyArrayItemsFromArraySchemas(normalizedSchema)
341+ : normalizedSchema;
253342if (isGeminiProvider && !isAnthropicProvider) {
254-return cleanSchemaForGemini(normalizedSchema);
343+return cleanSchemaForGemini(arrayItemsCompatibleSchema);
255344}
256345if (unsupportedToolSchemaKeywords.size > 0) {
257-return stripUnsupportedSchemaKeywords(normalizedSchema, unsupportedToolSchemaKeywords) as TSchema;
346+return stripUnsupportedSchemaKeywords(
347+arrayItemsCompatibleSchema,
348+unsupportedToolSchemaKeywords,
349+) as TSchema;
258350}
259-return normalizedSchema as TSchema;
351+return arrayItemsCompatibleSchema as TSchema;
260352}
261353262354const conditionalKey = getTopLevelConditionalKey(schemaRecord);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。