




















@@ -309,14 +309,184 @@ function stripEmptyArrayItemsFromArraySchemas(schema: unknown): unknown {
309309return changed ? Object.fromEntries(entries) : schema;
310310}
311311312+type SchemaDefs = {
313+$defs: Map<string, unknown>;
314+definitions: Map<string, unknown>;
315+};
316+317+function copySchemaMeta(from: Record<string, unknown>, to: Record<string, unknown>): void {
318+for (const key of ["title", "description", "default"] as const) {
319+if (key in from && from[key] !== undefined) {
320+to[key] = from[key];
321+}
322+}
323+}
324+325+function extendSchemaDefs(
326+defs: SchemaDefs | undefined,
327+schema: Record<string, unknown>,
328+): SchemaDefs | undefined {
329+const defsEntry =
330+schema.$defs && typeof schema.$defs === "object" && !Array.isArray(schema.$defs)
331+ ? (schema.$defs as Record<string, unknown>)
332+ : undefined;
333+const legacyDefsEntry =
334+schema.definitions &&
335+typeof schema.definitions === "object" &&
336+!Array.isArray(schema.definitions)
337+ ? (schema.definitions as Record<string, unknown>)
338+ : undefined;
339+340+if (!defsEntry && !legacyDefsEntry) {
341+return defs;
342+}
343+344+const next: SchemaDefs = defs
345+ ? {
346+$defs: new Map(defs.$defs),
347+definitions: new Map(defs.definitions),
348+}
349+ : {
350+$defs: new Map<string, unknown>(),
351+definitions: new Map<string, unknown>(),
352+};
353+if (defsEntry) {
354+for (const [key, value] of Object.entries(defsEntry)) {
355+next.$defs.set(key, value);
356+}
357+}
358+if (legacyDefsEntry) {
359+for (const [key, value] of Object.entries(legacyDefsEntry)) {
360+next.definitions.set(key, value);
361+}
362+}
363+return next;
364+}
365+366+function decodeJsonPointerSegment(segment: string): string {
367+return segment.replaceAll("~1", "/").replaceAll("~0", "~");
368+}
369+370+function resolveJsonPointerPath(value: unknown, segments: string[]): unknown {
371+let current = value;
372+for (const segment of segments) {
373+if (!current || typeof current !== "object") {
374+return undefined;
375+}
376+const key = decodeJsonPointerSegment(segment);
377+if (Array.isArray(current)) {
378+const index = Number(key);
379+if (!Number.isInteger(index) || index < 0 || index >= current.length) {
380+return undefined;
381+}
382+current = current[index];
383+continue;
384+}
385+const record = current as Record<string, unknown>;
386+if (!Object.prototype.hasOwnProperty.call(record, key)) {
387+return undefined;
388+}
389+current = record[key];
390+}
391+return current;
392+}
393+394+function tryResolveLocalRef(ref: string, defs: SchemaDefs | undefined): unknown {
395+if (!defs) {
396+return undefined;
397+}
398+const match = ref.match(/^#\/(\$defs|definitions)\/([^/]+)(?:\/(.*))?$/);
399+if (!match) {
400+return undefined;
401+}
402+const namespace = match[1] === "$defs" ? defs.$defs : defs.definitions;
403+const name = decodeJsonPointerSegment(match[2] ?? "");
404+const resolved = name ? namespace.get(name) : undefined;
405+if (resolved === undefined) {
406+return undefined;
407+}
408+const remainingPath = match[3] ? match[3].split("/") : [];
409+return resolveJsonPointerPath(resolved, remainingPath);
410+}
411+412+function inlineLocalSchemaRefsWithDefs(
413+schema: unknown,
414+defs: SchemaDefs | undefined,
415+refStack: Set<string> | undefined,
416+state: { unresolvedLocalRefs: boolean },
417+): unknown {
418+if (!schema || typeof schema !== "object") {
419+return schema;
420+}
421+if (Array.isArray(schema)) {
422+return schema.map((entry) => inlineLocalSchemaRefsWithDefs(entry, defs, refStack, state));
423+}
424+425+const obj = schema as Record<string, unknown>;
426+const nextDefs = extendSchemaDefs(defs, obj);
427+const refValue = typeof obj.$ref === "string" ? obj.$ref : undefined;
428+429+if (refValue) {
430+if (refStack?.has(refValue)) {
431+return {};
432+}
433+const resolved = tryResolveLocalRef(refValue, nextDefs);
434+if (resolved === undefined) {
435+if (refValue.startsWith("#/")) {
436+state.unresolvedLocalRefs = true;
437+}
438+return { ...obj };
439+}
440+const nextRefStack = refStack ? new Set(refStack) : new Set<string>();
441+nextRefStack.add(refValue);
442+const inlined = inlineLocalSchemaRefsWithDefs(resolved, nextDefs, nextRefStack, state);
443+if (!inlined || typeof inlined !== "object" || Array.isArray(inlined)) {
444+return inlined;
445+}
446+const result: Record<string, unknown> = { ...(inlined as Record<string, unknown>) };
447+copySchemaMeta(obj, result);
448+return result;
449+}
450+451+const result: Record<string, unknown> = {};
452+for (const [key, value] of Object.entries(obj)) {
453+if (key === "$defs" || key === "definitions") {
454+continue;
455+}
456+result[key] = inlineLocalSchemaRefsWithDefs(value, nextDefs, refStack, state);
457+}
458+if (state.unresolvedLocalRefs) {
459+if ("$defs" in obj) {
460+result.$defs = obj.$defs;
461+}
462+if ("definitions" in obj) {
463+result.definitions = obj.definitions;
464+}
465+}
466+return result;
467+}
468+469+export function inlineLocalToolSchemaRefs(schema: unknown): TSchema {
470+if (!schema || typeof schema !== "object") {
471+return schema as TSchema;
472+}
473+const defs = extendSchemaDefs(undefined, schema as Record<string, unknown>);
474+return inlineLocalSchemaRefsWithDefs(schema, defs, undefined, {
475+unresolvedLocalRefs: false,
476+}) as TSchema;
477+}
478+312479export function normalizeToolParameterSchema(
313480schema: unknown,
314481options?: { modelProvider?: string; modelId?: string; modelCompat?: ModelCompatConfig },
315482): TSchema {
483+const inlinedSchema = inlineLocalToolSchemaRefs(schema);
316484const schemaRecord =
317-schema && typeof schema === "object" ? (schema as Record<string, unknown>) : undefined;
485+inlinedSchema && typeof inlinedSchema === "object"
486+ ? (inlinedSchema as Record<string, unknown>)
487+ : undefined;
318488if (!schemaRecord) {
319-return schema as TSchema;
489+return inlinedSchema;
320490}
321491322492// Provider quirks:
@@ -378,9 +548,9 @@ export function normalizeToolParameterSchema(
378548if (conditionalKey === "allOf") {
379549// Top-level `allOf` is not safely flattenable with the same heuristics we
380550// use for unions. Keep it explicit rather than silently rewriting it.
381-return applyProviderCleaning(schema);
551+return applyProviderCleaning(inlinedSchema);
382552}
383-return applyProviderCleaning(schema);
553+return applyProviderCleaning(inlinedSchema);
384554}
385555const variants = schemaRecord[flattenableVariantKey] as unknown[];
386556const mergedProperties: Record<string, unknown> = {};
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。