mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 08:17:02 +00:00
Give array unions an unrestricted item schema and validate converted tools/list schemas for both advertised profiles. Keep client constraints separate from JSON Schema validity. Refs: #3838, #3921, #3842 Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Co-authored-by: 冰水泡枸杞 <xingzihai@users.noreply.github.com>
75 lines
2.5 KiB
TypeScript
75 lines
2.5 KiB
TypeScript
import {
|
|
schemaArrays,
|
|
schemaChildren,
|
|
schemaMaps,
|
|
} from "../../src/contracts/inputSchemaPresentation.js";
|
|
|
|
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
|
|
/** VS Code Copilot rejects falsy `items` for any type that admits arrays. */
|
|
export const arrayItemSchemaError = (
|
|
schema: unknown,
|
|
path: string,
|
|
): string | undefined => {
|
|
if (!isRecord(schema)) return undefined;
|
|
const admitsArrays =
|
|
schema.type === "array" ||
|
|
(Array.isArray(schema.type) && schema.type.includes("array"));
|
|
if (admitsArrays && !schema.items)
|
|
return `${path}: tool parameters array type must have items`;
|
|
for (const [key, value] of Object.entries(schema)) {
|
|
const children =
|
|
schemaMaps.has(key) && isRecord(value)
|
|
? Object.entries(value).map(
|
|
([name, child]) => [`${key}.${name}`, child] as const,
|
|
)
|
|
: (schemaArrays.has(key) || schemaChildren.has(key)) &&
|
|
Array.isArray(value)
|
|
? value.map(
|
|
(child: unknown, index) => [`${key}.${index}`, child] as const,
|
|
)
|
|
: schemaChildren.has(key)
|
|
? [[key, value] as const]
|
|
: [];
|
|
for (const [suffix, child] of children) {
|
|
const error = arrayItemSchemaError(child, `${path}.${suffix}`);
|
|
if (error !== undefined) return error;
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
/** Reject Gemini-incompatible boolean literals in advertised input positions. */
|
|
export const booleanLiteralSchemaError = (
|
|
schema: unknown,
|
|
path: string,
|
|
): string | undefined => {
|
|
if (!isRecord(schema)) return undefined;
|
|
if (typeof schema.const === "boolean") return `${path}.const is boolean`;
|
|
if (
|
|
Array.isArray(schema.enum) &&
|
|
schema.enum.some((value) => typeof value === "boolean")
|
|
)
|
|
return `${path}.enum contains a boolean`;
|
|
for (const [key, value] of Object.entries(schema)) {
|
|
const children =
|
|
schemaMaps.has(key) && isRecord(value)
|
|
? Object.entries(value).map(
|
|
([name, child]) => [`${key}.${name}`, child] as const,
|
|
)
|
|
: schemaArrays.has(key) && Array.isArray(value)
|
|
? value.map(
|
|
(child: unknown, index) => [`${key}[${index}]`, child] as const,
|
|
)
|
|
: schemaChildren.has(key)
|
|
? [[key, value] as const]
|
|
: [];
|
|
for (const [suffix, child] of children) {
|
|
const error = booleanLiteralSchemaError(child, `${path}.${suffix}`);
|
|
if (error !== undefined) return error;
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|