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:
2026-10-15 17:58:48 -04:00
parent 719370c8f7
commit 6081e3e325
1743 changed files with 205460 additions and 131705 deletions

View File

@@ -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;