chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
@@ -9,6 +9,9 @@ interface JsonSchemaObject {
|
||||
|
||||
class UnsupportedStrictJsonSchemaError extends Error {}
|
||||
|
||||
/** Returns true when a provider's strict mode rejects this schema keyword with this value. */
|
||||
export type UnsupportedStrictSchemaKeywordCheck = (key: string, value: unknown) => boolean;
|
||||
|
||||
const UNSUPPORTED_STRICT_SCHEMA_KEYS = [
|
||||
"$ref",
|
||||
"$defs",
|
||||
@@ -50,7 +53,7 @@ function schemaAllowsNull(schema: unknown): boolean {
|
||||
return Array.isArray(schema.anyOf) && schema.anyOf.some((variant) => schemaAllowsNull(variant));
|
||||
}
|
||||
|
||||
function makeJsonSchemaNodeStrict(schema: unknown): void {
|
||||
function makeJsonSchemaNodeStrict(schema: unknown, isUnsupportedKeyword?: UnsupportedStrictSchemaKeywordCheck): void {
|
||||
if (!isJsonSchemaObject(schema)) {
|
||||
throw new UnsupportedStrictJsonSchemaError("boolean schemas are unsupported");
|
||||
}
|
||||
@@ -59,6 +62,13 @@ function makeJsonSchemaNodeStrict(schema: unknown): void {
|
||||
throw new UnsupportedStrictJsonSchemaError(`${key} schemas are unsupported`);
|
||||
}
|
||||
}
|
||||
if (isUnsupportedKeyword) {
|
||||
for (const [key, value] of Object.entries(schema)) {
|
||||
if (isUnsupportedKeyword(key, value)) {
|
||||
throw new UnsupportedStrictJsonSchemaError(`${key}: ${JSON.stringify(value)} is unsupported`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (schema.anyOf !== undefined) {
|
||||
if (!Array.isArray(schema.anyOf) || schema.anyOf.length === 0) {
|
||||
@@ -68,7 +78,7 @@ function makeJsonSchemaNodeStrict(schema: unknown): void {
|
||||
if (isStructuredSchema(variant)) {
|
||||
throw new UnsupportedStrictJsonSchemaError("object and array unions are unsupported");
|
||||
}
|
||||
makeJsonSchemaNodeStrict(variant);
|
||||
makeJsonSchemaNodeStrict(variant, isUnsupportedKeyword);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +86,7 @@ function makeJsonSchemaNodeStrict(schema: unknown): void {
|
||||
if (Array.isArray(schema.items)) {
|
||||
throw new UnsupportedStrictJsonSchemaError("tuple schemas are unsupported");
|
||||
}
|
||||
makeJsonSchemaNodeStrict(schema.items);
|
||||
makeJsonSchemaNodeStrict(schema.items, isUnsupportedKeyword);
|
||||
}
|
||||
|
||||
const isObjectSchema = schema.type === "object";
|
||||
@@ -104,7 +114,7 @@ function makeJsonSchemaNodeStrict(schema: unknown): void {
|
||||
throw new UnsupportedStrictJsonSchemaError("required contains an unknown property");
|
||||
}
|
||||
for (const [key, property] of Object.entries(properties)) {
|
||||
makeJsonSchemaNodeStrict(property);
|
||||
makeJsonSchemaNodeStrict(property, isUnsupportedKeyword);
|
||||
if (!required.has(key) && !schemaAllowsNull(property)) {
|
||||
properties[key] = { anyOf: [property, { type: "null" }] };
|
||||
}
|
||||
@@ -114,12 +124,15 @@ function makeJsonSchemaNodeStrict(schema: unknown): void {
|
||||
}
|
||||
|
||||
/** Convert a tool schema to the strict subset expected by provider constrained sampling. */
|
||||
export function makeStrictJsonSchema(schema: Tool["parameters"]): Record<string, unknown> {
|
||||
export function makeStrictJsonSchema(
|
||||
schema: Tool["parameters"],
|
||||
isUnsupportedKeyword?: UnsupportedStrictSchemaKeywordCheck,
|
||||
): Record<string, unknown> {
|
||||
const cloned: unknown = structuredClone(schema);
|
||||
if (!isJsonSchemaObject(cloned)) {
|
||||
throw new UnsupportedStrictJsonSchemaError("root schema must have type object");
|
||||
}
|
||||
makeJsonSchemaNodeStrict(cloned);
|
||||
makeJsonSchemaNodeStrict(cloned, isUnsupportedKeyword);
|
||||
if (cloned.type !== "object") {
|
||||
throw new UnsupportedStrictJsonSchemaError("root schema must have type object");
|
||||
}
|
||||
@@ -205,13 +218,21 @@ function inferGrammarInputProperty(tool: Tool): string {
|
||||
return inputProperty;
|
||||
}
|
||||
|
||||
export function resolveJsonSchemaStrictSampling(tool: Tool, supportsStrictMode: boolean): boolean | undefined {
|
||||
/**
|
||||
* Decide whether a JSON-schema tool is sent in strict mode. `isUnsupportedKeyword` lets a provider
|
||||
* reject extra keywords its strict mode does not accept, so "prefer" tools fall back to non-strict.
|
||||
*/
|
||||
export function resolveJsonSchemaStrictSampling(
|
||||
tool: Tool,
|
||||
supportsStrictMode: boolean,
|
||||
isUnsupportedKeyword?: UnsupportedStrictSchemaKeywordCheck,
|
||||
): boolean | undefined {
|
||||
const config = tool.constrainedSampling;
|
||||
if (!config || config.type !== "json_schema") return undefined;
|
||||
|
||||
if (supportsStrictMode) {
|
||||
try {
|
||||
makeStrictJsonSchema(tool.parameters);
|
||||
makeStrictJsonSchema(tool.parameters, isUnsupportedKeyword);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (!(error instanceof UnsupportedStrictJsonSchemaError)) throw error;
|
||||
|
||||
Reference in New Issue
Block a user