@caupulican/pi-ai 0.81.11 → 0.81.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-registry.d.ts.map +1 -1
- package/dist/api-registry.js +17 -1
- package/dist/api-registry.js.map +1 -1
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +27 -16
- package/dist/env-api-keys.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/models.generated.d.ts +120 -0
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +124 -4
- package/dist/models.generated.js.map +1 -1
- package/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.js +39 -6
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/google-shared.d.ts +13 -4
- package/dist/providers/google-shared.d.ts.map +1 -1
- package/dist/providers/google-shared.js +132 -11
- package/dist/providers/google-shared.js.map +1 -1
- package/dist/providers/google-vertex.d.ts.map +1 -1
- package/dist/providers/google-vertex.js +16 -98
- package/dist/providers/google-vertex.js.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +16 -116
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/openai-completions.d.ts +2 -1
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +56 -22
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/dist/providers/openai-responses-shared.js +34 -15
- package/dist/providers/openai-responses-shared.js.map +1 -1
- package/dist/providers/transform-messages.d.ts.map +1 -1
- package/dist/providers/transform-messages.js +25 -6
- package/dist/providers/transform-messages.js.map +1 -1
- package/dist/stream.d.ts +2 -1
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +79 -2
- package/dist/stream.js.map +1 -1
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/json-parse.d.ts +5 -0
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +9 -13
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/tool-names.d.ts +7 -0
- package/dist/utils/tool-names.d.ts.map +1 -0
- package/dist/utils/tool-names.js +46 -0
- package/dist/utils/tool-names.js.map +1 -0
- package/dist/utils/tool-repair/analyzer.d.ts +45 -0
- package/dist/utils/tool-repair/analyzer.d.ts.map +1 -0
- package/dist/utils/tool-repair/analyzer.js +255 -0
- package/dist/utils/tool-repair/analyzer.js.map +1 -0
- package/dist/utils/tool-repair/registry.d.ts +32 -0
- package/dist/utils/tool-repair/registry.d.ts.map +1 -0
- package/dist/utils/tool-repair/registry.js +129 -0
- package/dist/utils/tool-repair/registry.js.map +1 -0
- package/dist/utils/tool-repair/repairer.d.ts +16 -0
- package/dist/utils/tool-repair/repairer.d.ts.map +1 -0
- package/dist/utils/tool-repair/repairer.js +254 -0
- package/dist/utils/tool-repair/repairer.js.map +1 -0
- package/dist/utils/tool-repair/replay.d.ts +35 -0
- package/dist/utils/tool-repair/replay.d.ts.map +1 -0
- package/dist/utils/tool-repair/replay.js +200 -0
- package/dist/utils/tool-repair/replay.js.map +1 -0
- package/dist/utils/tool-repair/text-protocol.d.ts +16 -0
- package/dist/utils/tool-repair/text-protocol.d.ts.map +1 -0
- package/dist/utils/tool-repair/text-protocol.js +198 -0
- package/dist/utils/tool-repair/text-protocol.js.map +1 -0
- package/dist/utils/validation.d.ts +47 -4
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +300 -217
- package/dist/utils/validation.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { Compile } from "typebox/compile";
|
|
2
|
+
import { analyzeToolArgumentErrors } from "./analyzer.js";
|
|
3
|
+
import { TOOL_REPAIR_MODE_NAMES } from "./registry.js";
|
|
4
|
+
import { repairToolArguments } from "./repairer.js";
|
|
5
|
+
function splitReplayPath(path) {
|
|
6
|
+
return path === "root" ? [] : path.split(".").filter((segment) => segment.length > 0);
|
|
7
|
+
}
|
|
8
|
+
function schemaForExpectedType(expectedType) {
|
|
9
|
+
if (!expectedType || expectedType === "unknown")
|
|
10
|
+
return { type: "null" };
|
|
11
|
+
if (expectedType === "enum")
|
|
12
|
+
return { enum: ["expected"] };
|
|
13
|
+
const types = expectedType.split("|").filter((type) => type.length > 0);
|
|
14
|
+
return { type: types.length <= 1 ? (types[0] ?? "null") : types };
|
|
15
|
+
}
|
|
16
|
+
function isNumericSegment(segment) {
|
|
17
|
+
return /^\d+$/.test(segment);
|
|
18
|
+
}
|
|
19
|
+
function ensureObjectProperty(schema, key) {
|
|
20
|
+
schema.type = "object";
|
|
21
|
+
schema.properties ??= {};
|
|
22
|
+
schema.required ??= [];
|
|
23
|
+
if (!schema.required.includes(key))
|
|
24
|
+
schema.required.push(key);
|
|
25
|
+
schema.properties[key] ??= { type: "object", properties: {}, required: [] };
|
|
26
|
+
return schema.properties[key];
|
|
27
|
+
}
|
|
28
|
+
function ensureArrayItems(schema) {
|
|
29
|
+
schema.type = "array";
|
|
30
|
+
schema.items ??= { type: "object", properties: {}, required: [] };
|
|
31
|
+
return schema.items;
|
|
32
|
+
}
|
|
33
|
+
function setSchemaAtPath(root, path, leaf) {
|
|
34
|
+
if (path.length === 0) {
|
|
35
|
+
Object.assign(root, leaf);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let cursor = root;
|
|
39
|
+
for (let index = 0; index < path.length; index++) {
|
|
40
|
+
const segment = path[index];
|
|
41
|
+
const isLast = index === path.length - 1;
|
|
42
|
+
if (isNumericSegment(segment)) {
|
|
43
|
+
cursor = isLast ? Object.assign(ensureArrayItems(cursor), leaf) : ensureArrayItems(cursor);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (isLast) {
|
|
47
|
+
cursor.type = "object";
|
|
48
|
+
cursor.properties ??= {};
|
|
49
|
+
cursor.required ??= [];
|
|
50
|
+
if (!cursor.required.includes(segment))
|
|
51
|
+
cursor.required.push(segment);
|
|
52
|
+
cursor.properties[segment] = leaf;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const next = path[index + 1];
|
|
56
|
+
cursor = isNumericSegment(next)
|
|
57
|
+
? ensureArrayItems(ensureObjectProperty(cursor, segment))
|
|
58
|
+
: ensureObjectProperty(cursor, segment);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function sampleValue(receivedType, expectedType) {
|
|
62
|
+
if (receivedType === "missing")
|
|
63
|
+
return undefined;
|
|
64
|
+
if (receivedType === "null")
|
|
65
|
+
return null;
|
|
66
|
+
if (receivedType === "array")
|
|
67
|
+
return [];
|
|
68
|
+
if (receivedType === "object")
|
|
69
|
+
return {};
|
|
70
|
+
if (receivedType === "boolean")
|
|
71
|
+
return true;
|
|
72
|
+
if (receivedType === "number" || receivedType === "integer")
|
|
73
|
+
return 1;
|
|
74
|
+
if (receivedType === "string") {
|
|
75
|
+
if (expectedType?.includes("number") || expectedType?.includes("integer"))
|
|
76
|
+
return "not-a-number";
|
|
77
|
+
if (expectedType?.includes("boolean"))
|
|
78
|
+
return "not-a-boolean";
|
|
79
|
+
if (expectedType?.includes("array") || expectedType?.includes("object"))
|
|
80
|
+
return "not-json";
|
|
81
|
+
return "not-a-valid-replay-value";
|
|
82
|
+
}
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
function ensureArgsObject(target, key) {
|
|
86
|
+
const current = target[key];
|
|
87
|
+
if (typeof current === "object" && current !== null && !Array.isArray(current))
|
|
88
|
+
return current;
|
|
89
|
+
const next = {};
|
|
90
|
+
target[key] = next;
|
|
91
|
+
return next;
|
|
92
|
+
}
|
|
93
|
+
function ensureArgsArray(target, key) {
|
|
94
|
+
const current = target[key];
|
|
95
|
+
if (Array.isArray(current))
|
|
96
|
+
return current;
|
|
97
|
+
const next = [];
|
|
98
|
+
target[key] = next;
|
|
99
|
+
return next;
|
|
100
|
+
}
|
|
101
|
+
function setArgumentAtPath(root, path, value) {
|
|
102
|
+
if (value === undefined)
|
|
103
|
+
return;
|
|
104
|
+
if (path.length === 0)
|
|
105
|
+
return;
|
|
106
|
+
let objectCursor = root;
|
|
107
|
+
let arrayCursor;
|
|
108
|
+
for (let index = 0; index < path.length; index++) {
|
|
109
|
+
const segment = path[index];
|
|
110
|
+
const isLast = index === path.length - 1;
|
|
111
|
+
if (arrayCursor) {
|
|
112
|
+
const arrayIndex = Number(segment);
|
|
113
|
+
if (!Number.isInteger(arrayIndex))
|
|
114
|
+
return;
|
|
115
|
+
if (isLast) {
|
|
116
|
+
arrayCursor[arrayIndex] = value;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const next = path[index + 1];
|
|
120
|
+
if (isNumericSegment(next)) {
|
|
121
|
+
const existing = arrayCursor[arrayIndex];
|
|
122
|
+
const nextArray = Array.isArray(existing) ? existing : [];
|
|
123
|
+
arrayCursor[arrayIndex] = nextArray;
|
|
124
|
+
arrayCursor = nextArray;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const existing = arrayCursor[arrayIndex];
|
|
128
|
+
const nextObject = typeof existing === "object" && existing !== null && !Array.isArray(existing)
|
|
129
|
+
? existing
|
|
130
|
+
: {};
|
|
131
|
+
arrayCursor[arrayIndex] = nextObject;
|
|
132
|
+
objectCursor = nextObject;
|
|
133
|
+
arrayCursor = undefined;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (isLast) {
|
|
137
|
+
objectCursor[segment] = value;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const next = path[index + 1];
|
|
141
|
+
if (isNumericSegment(next)) {
|
|
142
|
+
arrayCursor = ensureArgsArray(objectCursor, segment);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
objectCursor = ensureArgsObject(objectCursor, segment);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function buildFixture(record) {
|
|
150
|
+
const parameters = { type: "object", properties: {}, required: [] };
|
|
151
|
+
const args = {};
|
|
152
|
+
for (const shape of record.shape) {
|
|
153
|
+
const path = splitReplayPath(shape.path);
|
|
154
|
+
setSchemaAtPath(parameters, path, schemaForExpectedType(shape.expectedType));
|
|
155
|
+
setArgumentAtPath(args, path, sampleValue(shape.receivedType, shape.expectedType));
|
|
156
|
+
}
|
|
157
|
+
return { tool: record.tool, parameters: parameters, arguments: args };
|
|
158
|
+
}
|
|
159
|
+
function validationErrorsForFixture(fixture) {
|
|
160
|
+
const validator = Compile(fixture.parameters);
|
|
161
|
+
return [...validator.Errors(fixture.arguments)].map((error) => ({
|
|
162
|
+
instancePath: error.instancePath,
|
|
163
|
+
keyword: error.keyword,
|
|
164
|
+
message: error.message,
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
const KNOWN_FAILURE_MODES = new Set([...TOOL_REPAIR_MODE_NAMES, "other"]);
|
|
168
|
+
function normalizedFailureModes(modes) {
|
|
169
|
+
const unique = [...new Set(modes ?? [])]
|
|
170
|
+
.map((mode) => (KNOWN_FAILURE_MODES.has(mode) ? mode : "other"))
|
|
171
|
+
.sort();
|
|
172
|
+
return unique.length > 0 ? unique : ["other"];
|
|
173
|
+
}
|
|
174
|
+
export function isToolRepairCorpusRecord(value) {
|
|
175
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
176
|
+
return false;
|
|
177
|
+
const record = value;
|
|
178
|
+
return record.kind === "tool_validation" && typeof record.tool === "string" && Array.isArray(record.shape);
|
|
179
|
+
}
|
|
180
|
+
export function replayToolRepairCorpus(records) {
|
|
181
|
+
return records.map((record, index) => {
|
|
182
|
+
const fixture = buildFixture(record);
|
|
183
|
+
const errors = validationErrorsForFixture(fixture);
|
|
184
|
+
const analyzerModes = analyzeToolArgumentErrors(fixture.tool, fixture.parameters, fixture.arguments, errors).flatMap((issue) => issue.modes);
|
|
185
|
+
const classifiedModes = analyzerModes.length > 0 ? normalizedFailureModes(analyzerModes) : normalizedFailureModes(record.failureModes);
|
|
186
|
+
const validator = Compile(fixture.parameters);
|
|
187
|
+
const repaired = repairToolArguments(fixture.tool, fixture.parameters, fixture.arguments, errors, (candidate) => validator.Check(candidate));
|
|
188
|
+
return {
|
|
189
|
+
record: index + 1,
|
|
190
|
+
provider: record.provider,
|
|
191
|
+
modelId: record.modelId,
|
|
192
|
+
tool: record.tool,
|
|
193
|
+
classifiedModes,
|
|
194
|
+
outcome: repaired ? "would-repair" : "would-bounce",
|
|
195
|
+
repairsApplied: repaired?.repairsApplied ?? [],
|
|
196
|
+
fixture,
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../../src/utils/tool-repair/replay.ts"],"names":[],"mappings":"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","sourcesContent":["import { Compile } from \"typebox/compile\";\nimport type { Tool } from \"../../types.ts\";\nimport { analyzeToolArgumentErrors, type ValidationErrorLike } from \"./analyzer.ts\";\nimport { TOOL_REPAIR_MODE_NAMES, type ToolRepairFailureModeName, type ToolRepairModeName } from \"./registry.ts\";\nimport { repairToolArguments } from \"./repairer.ts\";\n\ninterface ReplayJsonSchemaObject {\n\ttype?: string | string[];\n\tproperties?: Record<string, ReplayJsonSchemaObject>;\n\trequired?: string[];\n\titems?: ReplayJsonSchemaObject;\n\tenum?: unknown[];\n\tadditionalProperties?: boolean;\n}\n\nexport interface ToolRepairCorpusShapeEntry {\n\tpath: string;\n\texpectedType?: string;\n\treceivedType: string;\n\tkeyword?: string;\n}\n\nexport interface ToolRepairCorpusRecord {\n\tkind: \"tool_validation\";\n\tprovider?: string;\n\tmodelId?: string;\n\ttool: string;\n\tfailureModes?: readonly string[];\n\tshape: readonly ToolRepairCorpusShapeEntry[];\n\terrorKeywords?: readonly string[];\n}\n\nexport interface ToolRepairReplayFixture {\n\ttool: string;\n\tparameters: Tool[\"parameters\"];\n\targuments: Record<string, unknown>;\n}\n\nexport interface ToolRepairReplayResult {\n\trecord: number;\n\tprovider?: string;\n\tmodelId?: string;\n\ttool: string;\n\tclassifiedModes: ToolRepairFailureModeName[];\n\toutcome: \"would-repair\" | \"would-bounce\";\n\trepairsApplied: ToolRepairModeName[];\n\tfixture: ToolRepairReplayFixture;\n}\n\nfunction splitReplayPath(path: string): string[] {\n\treturn path === \"root\" ? [] : path.split(\".\").filter((segment) => segment.length > 0);\n}\n\nfunction schemaForExpectedType(expectedType: string | undefined): ReplayJsonSchemaObject {\n\tif (!expectedType || expectedType === \"unknown\") return { type: \"null\" };\n\tif (expectedType === \"enum\") return { enum: [\"expected\"] };\n\tconst types = expectedType.split(\"|\").filter((type) => type.length > 0);\n\treturn { type: types.length <= 1 ? (types[0] ?? \"null\") : types };\n}\n\nfunction isNumericSegment(segment: string): boolean {\n\treturn /^\\d+$/.test(segment);\n}\n\nfunction ensureObjectProperty(schema: ReplayJsonSchemaObject, key: string): ReplayJsonSchemaObject {\n\tschema.type = \"object\";\n\tschema.properties ??= {};\n\tschema.required ??= [];\n\tif (!schema.required.includes(key)) schema.required.push(key);\n\tschema.properties[key] ??= { type: \"object\", properties: {}, required: [] };\n\treturn schema.properties[key];\n}\n\nfunction ensureArrayItems(schema: ReplayJsonSchemaObject): ReplayJsonSchemaObject {\n\tschema.type = \"array\";\n\tschema.items ??= { type: \"object\", properties: {}, required: [] };\n\treturn schema.items;\n}\n\nfunction setSchemaAtPath(root: ReplayJsonSchemaObject, path: readonly string[], leaf: ReplayJsonSchemaObject): void {\n\tif (path.length === 0) {\n\t\tObject.assign(root, leaf);\n\t\treturn;\n\t}\n\n\tlet cursor = root;\n\tfor (let index = 0; index < path.length; index++) {\n\t\tconst segment = path[index]!;\n\t\tconst isLast = index === path.length - 1;\n\t\tif (isNumericSegment(segment)) {\n\t\t\tcursor = isLast ? Object.assign(ensureArrayItems(cursor), leaf) : ensureArrayItems(cursor);\n\t\t\tcontinue;\n\t\t}\n\t\tif (isLast) {\n\t\t\tcursor.type = \"object\";\n\t\t\tcursor.properties ??= {};\n\t\t\tcursor.required ??= [];\n\t\t\tif (!cursor.required.includes(segment)) cursor.required.push(segment);\n\t\t\tcursor.properties[segment] = leaf;\n\t\t\tcontinue;\n\t\t}\n\t\tconst next = path[index + 1]!;\n\t\tcursor = isNumericSegment(next)\n\t\t\t? ensureArrayItems(ensureObjectProperty(cursor, segment))\n\t\t\t: ensureObjectProperty(cursor, segment);\n\t}\n}\n\nfunction sampleValue(receivedType: string, expectedType: string | undefined): unknown {\n\tif (receivedType === \"missing\") return undefined;\n\tif (receivedType === \"null\") return null;\n\tif (receivedType === \"array\") return [];\n\tif (receivedType === \"object\") return {};\n\tif (receivedType === \"boolean\") return true;\n\tif (receivedType === \"number\" || receivedType === \"integer\") return 1;\n\tif (receivedType === \"string\") {\n\t\tif (expectedType?.includes(\"number\") || expectedType?.includes(\"integer\")) return \"not-a-number\";\n\t\tif (expectedType?.includes(\"boolean\")) return \"not-a-boolean\";\n\t\tif (expectedType?.includes(\"array\") || expectedType?.includes(\"object\")) return \"not-json\";\n\t\treturn \"not-a-valid-replay-value\";\n\t}\n\treturn {};\n}\n\nfunction ensureArgsObject(target: Record<string, unknown>, key: string): Record<string, unknown> {\n\tconst current = target[key];\n\tif (typeof current === \"object\" && current !== null && !Array.isArray(current))\n\t\treturn current as Record<string, unknown>;\n\tconst next: Record<string, unknown> = {};\n\ttarget[key] = next;\n\treturn next;\n}\n\nfunction ensureArgsArray(target: Record<string, unknown>, key: string): unknown[] {\n\tconst current = target[key];\n\tif (Array.isArray(current)) return current;\n\tconst next: unknown[] = [];\n\ttarget[key] = next;\n\treturn next;\n}\n\nfunction setArgumentAtPath(root: Record<string, unknown>, path: readonly string[], value: unknown): void {\n\tif (value === undefined) return;\n\tif (path.length === 0) return;\n\tlet objectCursor = root;\n\tlet arrayCursor: unknown[] | undefined;\n\tfor (let index = 0; index < path.length; index++) {\n\t\tconst segment = path[index]!;\n\t\tconst isLast = index === path.length - 1;\n\t\tif (arrayCursor) {\n\t\t\tconst arrayIndex = Number(segment);\n\t\t\tif (!Number.isInteger(arrayIndex)) return;\n\t\t\tif (isLast) {\n\t\t\t\tarrayCursor[arrayIndex] = value;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst next = path[index + 1]!;\n\t\t\tif (isNumericSegment(next)) {\n\t\t\t\tconst existing = arrayCursor[arrayIndex];\n\t\t\t\tconst nextArray = Array.isArray(existing) ? existing : [];\n\t\t\t\tarrayCursor[arrayIndex] = nextArray;\n\t\t\t\tarrayCursor = nextArray;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = arrayCursor[arrayIndex];\n\t\t\tconst nextObject =\n\t\t\t\ttypeof existing === \"object\" && existing !== null && !Array.isArray(existing)\n\t\t\t\t\t? (existing as Record<string, unknown>)\n\t\t\t\t\t: {};\n\t\t\tarrayCursor[arrayIndex] = nextObject;\n\t\t\tobjectCursor = nextObject;\n\t\t\tarrayCursor = undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isLast) {\n\t\t\tobjectCursor[segment] = value;\n\t\t\treturn;\n\t\t}\n\t\tconst next = path[index + 1]!;\n\t\tif (isNumericSegment(next)) {\n\t\t\tarrayCursor = ensureArgsArray(objectCursor, segment);\n\t\t} else {\n\t\t\tobjectCursor = ensureArgsObject(objectCursor, segment);\n\t\t}\n\t}\n}\n\nfunction buildFixture(record: ToolRepairCorpusRecord): ToolRepairReplayFixture {\n\tconst parameters: ReplayJsonSchemaObject = { type: \"object\", properties: {}, required: [] };\n\tconst args: Record<string, unknown> = {};\n\tfor (const shape of record.shape) {\n\t\tconst path = splitReplayPath(shape.path);\n\t\tsetSchemaAtPath(parameters, path, schemaForExpectedType(shape.expectedType));\n\t\tsetArgumentAtPath(args, path, sampleValue(shape.receivedType, shape.expectedType));\n\t}\n\treturn { tool: record.tool, parameters: parameters as Tool[\"parameters\"], arguments: args };\n}\n\nfunction validationErrorsForFixture(fixture: ToolRepairReplayFixture): ValidationErrorLike[] {\n\tconst validator = Compile(fixture.parameters);\n\treturn [...validator.Errors(fixture.arguments)].map((error) => ({\n\t\tinstancePath: error.instancePath,\n\t\tkeyword: error.keyword,\n\t\tmessage: error.message,\n\t}));\n}\n\nconst KNOWN_FAILURE_MODES = new Set<string>([...TOOL_REPAIR_MODE_NAMES, \"other\"]);\n\nfunction normalizedFailureModes(modes: readonly string[] | undefined): ToolRepairFailureModeName[] {\n\tconst unique = [...new Set(modes ?? [])]\n\t\t.map((mode) => (KNOWN_FAILURE_MODES.has(mode) ? mode : \"other\"))\n\t\t.sort() as ToolRepairFailureModeName[];\n\treturn unique.length > 0 ? unique : [\"other\"];\n}\n\nexport function isToolRepairCorpusRecord(value: unknown): value is ToolRepairCorpusRecord {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n\tconst record = value as { kind?: unknown; tool?: unknown; shape?: unknown };\n\treturn record.kind === \"tool_validation\" && typeof record.tool === \"string\" && Array.isArray(record.shape);\n}\n\nexport function replayToolRepairCorpus(records: readonly ToolRepairCorpusRecord[]): ToolRepairReplayResult[] {\n\treturn records.map((record, index) => {\n\t\tconst fixture = buildFixture(record);\n\t\tconst errors = validationErrorsForFixture(fixture);\n\t\tconst analyzerModes = analyzeToolArgumentErrors(\n\t\t\tfixture.tool,\n\t\t\tfixture.parameters,\n\t\t\tfixture.arguments,\n\t\t\terrors,\n\t\t).flatMap((issue) => issue.modes);\n\t\tconst classifiedModes =\n\t\t\tanalyzerModes.length > 0 ? normalizedFailureModes(analyzerModes) : normalizedFailureModes(record.failureModes);\n\t\tconst validator = Compile(fixture.parameters);\n\t\tconst repaired = repairToolArguments(fixture.tool, fixture.parameters, fixture.arguments, errors, (candidate) =>\n\t\t\tvalidator.Check(candidate),\n\t\t);\n\t\treturn {\n\t\t\trecord: index + 1,\n\t\t\tprovider: record.provider,\n\t\t\tmodelId: record.modelId,\n\t\t\ttool: record.tool,\n\t\t\tclassifiedModes,\n\t\t\toutcome: repaired ? \"would-repair\" : \"would-bounce\",\n\t\t\trepairsApplied: repaired?.repairsApplied ?? [],\n\t\t\tfixture,\n\t\t};\n\t});\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Tool, ToolCall } from "../../types.ts";
|
|
2
|
+
export type TextToolProtocolParseFailure = "overlap" | "mixed-prose" | "unrecognized";
|
|
3
|
+
export interface ParsedTextToolCalls {
|
|
4
|
+
calls: ToolCall[];
|
|
5
|
+
text: string;
|
|
6
|
+
attempted: boolean;
|
|
7
|
+
failure?: TextToolProtocolParseFailure;
|
|
8
|
+
}
|
|
9
|
+
export type TextToolProtocolVariant = "tool-tag" | "tool-call" | "fenced-json";
|
|
10
|
+
export interface TextToolProtocolOptions {
|
|
11
|
+
variant?: TextToolProtocolVariant;
|
|
12
|
+
}
|
|
13
|
+
export declare function normalizeTextToolProtocolOptions(option: boolean | TextToolProtocolOptions | undefined): TextToolProtocolOptions | undefined;
|
|
14
|
+
export declare function generateTextToolProtocolPrimer(tools: readonly Tool[], options?: TextToolProtocolOptions): string;
|
|
15
|
+
export declare function parseTextToolCalls(text: string, knownTools: readonly Tool[]): ParsedTextToolCalls;
|
|
16
|
+
//# sourceMappingURL=text-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-protocol.d.ts","sourceRoot":"","sources":["../../../src/utils/tool-repair/text-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,MAAM,4BAA4B,GAAG,SAAS,GAAG,aAAa,GAAG,cAAc,CAAC;AAEtF,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,4BAA4B,CAAC;CACvC;AAED,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;AAE/E,MAAM,WAAW,uBAAuB;IACvC,OAAO,CAAC,EAAE,uBAAuB,CAAC;CAClC;AAgLD,wBAAgB,gCAAgC,CAC/C,MAAM,EAAE,OAAO,GAAG,uBAAuB,GAAG,SAAS,GACnD,uBAAuB,GAAG,SAAS,CAIrC;AAQD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,MAAM,CAehH;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,IAAI,EAAE,GAAG,mBAAmB,CAajG","sourcesContent":["import type { Tool, ToolCall } from \"../../types.ts\";\n\nexport type TextToolProtocolParseFailure = \"overlap\" | \"mixed-prose\" | \"unrecognized\";\n\nexport interface ParsedTextToolCalls {\n\tcalls: ToolCall[];\n\ttext: string;\n\tattempted: boolean;\n\tfailure?: TextToolProtocolParseFailure;\n}\n\nexport type TextToolProtocolVariant = \"tool-tag\" | \"tool-call\" | \"fenced-json\";\n\nexport interface TextToolProtocolOptions {\n\tvariant?: TextToolProtocolVariant;\n}\n\ntype EnvelopeKind = \"pi_call\" | \"tool_call\" | \"fenced_json\";\n\ninterface EnvelopeMatch {\n\tkind: EnvelopeKind;\n\tstart: number;\n\tend: number;\n\tname?: string;\n\tbody: string;\n}\n\nconst MAX_SCHEMA_CHARS = 600;\nconst DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT: TextToolProtocolVariant = \"tool-tag\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction compactJson(value: unknown, maxChars = MAX_SCHEMA_CHARS): string {\n\tconst json = JSON.stringify(value);\n\tif (json.length <= maxChars) return json;\n\treturn `${json.slice(0, maxChars - 1)}…`;\n}\n\nfunction escapeAttribute(value: string): string {\n\treturn value.replace(/&/g, \"&\").replace(/\"/g, \""\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction unescapeAttribute(value: string): string {\n\treturn value\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/&/g, \"&\");\n}\n\nfunction knownToolNames(tools: readonly Tool[]): Set<string> {\n\treturn new Set(tools.map((tool) => tool.name));\n}\n\nfunction findToolEnvelopes(text: string): EnvelopeMatch[] {\n\tconst matches: EnvelopeMatch[] = [];\n\tconst piCallEnvelope = /<pi:call\\s+name=\"([^\"]+)\"\\s*>([\\s\\S]*?)<\\/pi:call>/g;\n\tfor (const match of text.matchAll(piCallEnvelope)) {\n\t\tmatches.push({\n\t\t\tkind: \"pi_call\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tname: unescapeAttribute(match[1] ?? \"\"),\n\t\t\tbody: match[2] ?? \"\",\n\t\t});\n\t}\n\n\tconst toolCallEnvelope = /<tool_call>([\\s\\S]*?)<\\/tool_call>/g;\n\tfor (const match of text.matchAll(toolCallEnvelope)) {\n\t\tmatches.push({\n\t\t\tkind: \"tool_call\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tbody: match[1] ?? \"\",\n\t\t});\n\t}\n\n\tconst fence = /```(?:tool|tool_call)\\s*\\n([\\s\\S]*?)\\n?```/gi;\n\tfor (const match of text.matchAll(fence)) {\n\t\tmatches.push({\n\t\t\tkind: \"fenced_json\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tbody: match[1] ?? \"\",\n\t\t});\n\t}\n\n\treturn matches.sort((a, b) => a.start - b.start);\n}\n\nfunction hasOverlap(matches: readonly EnvelopeMatch[]): boolean {\n\tlet previousEnd = -1;\n\tfor (const match of matches) {\n\t\tif (match.start < previousEnd) return true;\n\t\tpreviousEnd = match.end;\n\t}\n\treturn false;\n}\n\nfunction remainingText(text: string, matches: readonly EnvelopeMatch[]): string {\n\tlet cursor = 0;\n\tconst pieces: string[] = [];\n\tfor (const match of matches) {\n\t\tpieces.push(text.slice(cursor, match.start));\n\t\tcursor = match.end;\n\t}\n\tpieces.push(text.slice(cursor));\n\treturn pieces.join(\"\");\n}\n\nfunction coerceArguments(value: unknown): {\n\targuments: Record<string, unknown>;\n\trawArguments?: Record<string, unknown>;\n} {\n\tif (isRecord(value)) return { arguments: value };\n\treturn { arguments: value as Record<string, unknown>, rawArguments: { value } };\n}\n\nfunction parseJsonValue(raw: string): { ok: true; value: unknown } | { ok: false } {\n\ttry {\n\t\treturn { ok: true, value: JSON.parse(raw) as unknown };\n\t} catch {\n\t\treturn { ok: false };\n\t}\n}\n\nfunction parseJsonObject(raw: string): Record<string, unknown> | undefined {\n\tconst parsed = parseJsonValue(raw);\n\treturn parsed.ok && isRecord(parsed.value) ? parsed.value : undefined;\n}\n\nfunction textToolErrorMessage(name: string, names: readonly string[]): string | undefined {\n\tif (names.includes(name)) return undefined;\n\treturn `Unknown tool \"${name}\". Valid tools: ${names.join(\", \")}.`;\n}\n\nfunction extractNameFromMalformedJson(raw: string): string | undefined {\n\tconst match = /\"(?:name|tool)\"\\s*:\\s*\"([^\"]+)\"/.exec(raw);\n\treturn match?.[1];\n}\n\nfunction parsePiCallEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (!match.name) return undefined;\n\tconst parsed = parseJsonValue(match.body);\n\tconst args = parsed.ok ? coerceArguments(parsed.value) : coerceArguments(match.body.trim());\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: match.name,\n\t\targuments: args.arguments,\n\t\trawArguments: parsed.ok ? args.rawArguments : { text: match.body.trim() },\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(match.name, names),\n\t};\n}\n\nfunction parseEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (match.kind === \"pi_call\") return parsePiCallEnvelope(match, names, index);\n\n\tconst parsed = parseJsonObject(match.body);\n\tif (!parsed) {\n\t\tconst name = extractNameFromMalformedJson(match.body);\n\t\tif (!name) return undefined;\n\t\treturn {\n\t\t\ttype: \"toolCall\",\n\t\t\tid: `text-tool-${index}`,\n\t\t\tname,\n\t\t\targuments: match.body.trim() as unknown as Record<string, unknown>,\n\t\t\trawArguments: { text: match.body.trim() },\n\t\t\tsource: \"text-protocol\",\n\t\t\terrorMessage: textToolErrorMessage(name, names),\n\t\t};\n\t}\n\n\tconst nameValue = parsed.name ?? parsed.tool;\n\tif (typeof nameValue !== \"string\") return undefined;\n\tconst argsValue = parsed.arguments ?? parsed.args ?? {};\n\tconst args = coerceArguments(argsValue);\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: nameValue,\n\t\targuments: args.arguments,\n\t\trawArguments: args.rawArguments,\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(nameValue, names),\n\t};\n}\n\nexport function normalizeTextToolProtocolOptions(\n\toption: boolean | TextToolProtocolOptions | undefined,\n): TextToolProtocolOptions | undefined {\n\tif (!option) return undefined;\n\tif (option === true) return { variant: DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };\n\treturn { variant: option.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };\n}\n\nfunction formatVariantEnvelope(variant: TextToolProtocolVariant, toolName: string, argsJson: string): string {\n\tif (variant === \"tool-call\") return `<tool_call>{\"name\":\"${toolName}\",\"arguments\":${argsJson}}</tool_call>`;\n\tif (variant === \"fenced-json\") return `\\`\\`\\`tool_call\\n{\"name\":\"${toolName}\",\"arguments\":${argsJson}}\\n\\`\\`\\``;\n\treturn `<pi:call name=\"${escapeAttribute(toolName)}\">${argsJson}</pi:call>`;\n}\n\nexport function generateTextToolProtocolPrimer(tools: readonly Tool[], options?: TextToolProtocolOptions): string {\n\tif (tools.length === 0) return \"\";\n\tconst variant = options?.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT;\n\tconst lines = [\n\t\t\"Text tool-call protocol is enabled.\",\n\t\t\"When calling tools, output only one or more envelopes and no prose:\",\n\t\tformatVariantEnvelope(variant, \"TOOL_NAME\", '{\"argument\":\"value\"}'),\n\t\t\"Available tools:\",\n\t];\n\tfor (const tool of tools) {\n\t\tlines.push(`- ${escapeAttribute(tool.name)}: ${tool.description}; args schema ${compactJson(tool.parameters)}`);\n\t}\n\tconst exampleTool = tools[0];\n\tlines.push(\"Examples:\", formatVariantEnvelope(variant, exampleTool.name, \"{}\"));\n\treturn lines.join(\"\\n\");\n}\n\nexport function parseTextToolCalls(text: string, knownTools: readonly Tool[]): ParsedTextToolCalls {\n\tif (knownTools.length === 0) return { calls: [], text, attempted: false };\n\tconst matches = findToolEnvelopes(text);\n\tif (matches.length === 0) return { calls: [], text, attempted: false };\n\tif (hasOverlap(matches)) return { calls: [], text, attempted: true, failure: \"overlap\" };\n\tconst remainder = remainingText(text, matches);\n\tconst names = [...knownToolNames(knownTools)];\n\tconst calls = matches\n\t\t.map((match, index) => parseEnvelope(match, names, index + 1))\n\t\t.filter((call): call is ToolCall => call !== undefined);\n\treturn calls.length > 0\n\t\t? { calls, text: remainder.trim(), attempted: true }\n\t\t: { calls: [], text, attempted: true, failure: \"unrecognized\" };\n}\n"]}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
const MAX_SCHEMA_CHARS = 600;
|
|
2
|
+
const DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT = "tool-tag";
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function compactJson(value, maxChars = MAX_SCHEMA_CHARS) {
|
|
7
|
+
const json = JSON.stringify(value);
|
|
8
|
+
if (json.length <= maxChars)
|
|
9
|
+
return json;
|
|
10
|
+
return `${json.slice(0, maxChars - 1)}…`;
|
|
11
|
+
}
|
|
12
|
+
function escapeAttribute(value) {
|
|
13
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
14
|
+
}
|
|
15
|
+
function unescapeAttribute(value) {
|
|
16
|
+
return value
|
|
17
|
+
.replace(/"/g, '"')
|
|
18
|
+
.replace(/</g, "<")
|
|
19
|
+
.replace(/>/g, ">")
|
|
20
|
+
.replace(/&/g, "&");
|
|
21
|
+
}
|
|
22
|
+
function knownToolNames(tools) {
|
|
23
|
+
return new Set(tools.map((tool) => tool.name));
|
|
24
|
+
}
|
|
25
|
+
function findToolEnvelopes(text) {
|
|
26
|
+
const matches = [];
|
|
27
|
+
const piCallEnvelope = /<pi:call\s+name="([^"]+)"\s*>([\s\S]*?)<\/pi:call>/g;
|
|
28
|
+
for (const match of text.matchAll(piCallEnvelope)) {
|
|
29
|
+
matches.push({
|
|
30
|
+
kind: "pi_call",
|
|
31
|
+
start: match.index,
|
|
32
|
+
end: match.index + match[0].length,
|
|
33
|
+
name: unescapeAttribute(match[1] ?? ""),
|
|
34
|
+
body: match[2] ?? "",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const toolCallEnvelope = /<tool_call>([\s\S]*?)<\/tool_call>/g;
|
|
38
|
+
for (const match of text.matchAll(toolCallEnvelope)) {
|
|
39
|
+
matches.push({
|
|
40
|
+
kind: "tool_call",
|
|
41
|
+
start: match.index,
|
|
42
|
+
end: match.index + match[0].length,
|
|
43
|
+
body: match[1] ?? "",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const fence = /```(?:tool|tool_call)\s*\n([\s\S]*?)\n?```/gi;
|
|
47
|
+
for (const match of text.matchAll(fence)) {
|
|
48
|
+
matches.push({
|
|
49
|
+
kind: "fenced_json",
|
|
50
|
+
start: match.index,
|
|
51
|
+
end: match.index + match[0].length,
|
|
52
|
+
body: match[1] ?? "",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return matches.sort((a, b) => a.start - b.start);
|
|
56
|
+
}
|
|
57
|
+
function hasOverlap(matches) {
|
|
58
|
+
let previousEnd = -1;
|
|
59
|
+
for (const match of matches) {
|
|
60
|
+
if (match.start < previousEnd)
|
|
61
|
+
return true;
|
|
62
|
+
previousEnd = match.end;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
function remainingText(text, matches) {
|
|
67
|
+
let cursor = 0;
|
|
68
|
+
const pieces = [];
|
|
69
|
+
for (const match of matches) {
|
|
70
|
+
pieces.push(text.slice(cursor, match.start));
|
|
71
|
+
cursor = match.end;
|
|
72
|
+
}
|
|
73
|
+
pieces.push(text.slice(cursor));
|
|
74
|
+
return pieces.join("");
|
|
75
|
+
}
|
|
76
|
+
function coerceArguments(value) {
|
|
77
|
+
if (isRecord(value))
|
|
78
|
+
return { arguments: value };
|
|
79
|
+
return { arguments: value, rawArguments: { value } };
|
|
80
|
+
}
|
|
81
|
+
function parseJsonValue(raw) {
|
|
82
|
+
try {
|
|
83
|
+
return { ok: true, value: JSON.parse(raw) };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return { ok: false };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function parseJsonObject(raw) {
|
|
90
|
+
const parsed = parseJsonValue(raw);
|
|
91
|
+
return parsed.ok && isRecord(parsed.value) ? parsed.value : undefined;
|
|
92
|
+
}
|
|
93
|
+
function textToolErrorMessage(name, names) {
|
|
94
|
+
if (names.includes(name))
|
|
95
|
+
return undefined;
|
|
96
|
+
return `Unknown tool "${name}". Valid tools: ${names.join(", ")}.`;
|
|
97
|
+
}
|
|
98
|
+
function extractNameFromMalformedJson(raw) {
|
|
99
|
+
const match = /"(?:name|tool)"\s*:\s*"([^"]+)"/.exec(raw);
|
|
100
|
+
return match?.[1];
|
|
101
|
+
}
|
|
102
|
+
function parsePiCallEnvelope(match, names, index) {
|
|
103
|
+
if (!match.name)
|
|
104
|
+
return undefined;
|
|
105
|
+
const parsed = parseJsonValue(match.body);
|
|
106
|
+
const args = parsed.ok ? coerceArguments(parsed.value) : coerceArguments(match.body.trim());
|
|
107
|
+
return {
|
|
108
|
+
type: "toolCall",
|
|
109
|
+
id: `text-tool-${index}`,
|
|
110
|
+
name: match.name,
|
|
111
|
+
arguments: args.arguments,
|
|
112
|
+
rawArguments: parsed.ok ? args.rawArguments : { text: match.body.trim() },
|
|
113
|
+
source: "text-protocol",
|
|
114
|
+
errorMessage: textToolErrorMessage(match.name, names),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function parseEnvelope(match, names, index) {
|
|
118
|
+
if (match.kind === "pi_call")
|
|
119
|
+
return parsePiCallEnvelope(match, names, index);
|
|
120
|
+
const parsed = parseJsonObject(match.body);
|
|
121
|
+
if (!parsed) {
|
|
122
|
+
const name = extractNameFromMalformedJson(match.body);
|
|
123
|
+
if (!name)
|
|
124
|
+
return undefined;
|
|
125
|
+
return {
|
|
126
|
+
type: "toolCall",
|
|
127
|
+
id: `text-tool-${index}`,
|
|
128
|
+
name,
|
|
129
|
+
arguments: match.body.trim(),
|
|
130
|
+
rawArguments: { text: match.body.trim() },
|
|
131
|
+
source: "text-protocol",
|
|
132
|
+
errorMessage: textToolErrorMessage(name, names),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const nameValue = parsed.name ?? parsed.tool;
|
|
136
|
+
if (typeof nameValue !== "string")
|
|
137
|
+
return undefined;
|
|
138
|
+
const argsValue = parsed.arguments ?? parsed.args ?? {};
|
|
139
|
+
const args = coerceArguments(argsValue);
|
|
140
|
+
return {
|
|
141
|
+
type: "toolCall",
|
|
142
|
+
id: `text-tool-${index}`,
|
|
143
|
+
name: nameValue,
|
|
144
|
+
arguments: args.arguments,
|
|
145
|
+
rawArguments: args.rawArguments,
|
|
146
|
+
source: "text-protocol",
|
|
147
|
+
errorMessage: textToolErrorMessage(nameValue, names),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
export function normalizeTextToolProtocolOptions(option) {
|
|
151
|
+
if (!option)
|
|
152
|
+
return undefined;
|
|
153
|
+
if (option === true)
|
|
154
|
+
return { variant: DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };
|
|
155
|
+
return { variant: option.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };
|
|
156
|
+
}
|
|
157
|
+
function formatVariantEnvelope(variant, toolName, argsJson) {
|
|
158
|
+
if (variant === "tool-call")
|
|
159
|
+
return `<tool_call>{"name":"${toolName}","arguments":${argsJson}}</tool_call>`;
|
|
160
|
+
if (variant === "fenced-json")
|
|
161
|
+
return `\`\`\`tool_call\n{"name":"${toolName}","arguments":${argsJson}}\n\`\`\``;
|
|
162
|
+
return `<pi:call name="${escapeAttribute(toolName)}">${argsJson}</pi:call>`;
|
|
163
|
+
}
|
|
164
|
+
export function generateTextToolProtocolPrimer(tools, options) {
|
|
165
|
+
if (tools.length === 0)
|
|
166
|
+
return "";
|
|
167
|
+
const variant = options?.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT;
|
|
168
|
+
const lines = [
|
|
169
|
+
"Text tool-call protocol is enabled.",
|
|
170
|
+
"When calling tools, output only one or more envelopes and no prose:",
|
|
171
|
+
formatVariantEnvelope(variant, "TOOL_NAME", '{"argument":"value"}'),
|
|
172
|
+
"Available tools:",
|
|
173
|
+
];
|
|
174
|
+
for (const tool of tools) {
|
|
175
|
+
lines.push(`- ${escapeAttribute(tool.name)}: ${tool.description}; args schema ${compactJson(tool.parameters)}`);
|
|
176
|
+
}
|
|
177
|
+
const exampleTool = tools[0];
|
|
178
|
+
lines.push("Examples:", formatVariantEnvelope(variant, exampleTool.name, "{}"));
|
|
179
|
+
return lines.join("\n");
|
|
180
|
+
}
|
|
181
|
+
export function parseTextToolCalls(text, knownTools) {
|
|
182
|
+
if (knownTools.length === 0)
|
|
183
|
+
return { calls: [], text, attempted: false };
|
|
184
|
+
const matches = findToolEnvelopes(text);
|
|
185
|
+
if (matches.length === 0)
|
|
186
|
+
return { calls: [], text, attempted: false };
|
|
187
|
+
if (hasOverlap(matches))
|
|
188
|
+
return { calls: [], text, attempted: true, failure: "overlap" };
|
|
189
|
+
const remainder = remainingText(text, matches);
|
|
190
|
+
const names = [...knownToolNames(knownTools)];
|
|
191
|
+
const calls = matches
|
|
192
|
+
.map((match, index) => parseEnvelope(match, names, index + 1))
|
|
193
|
+
.filter((call) => call !== undefined);
|
|
194
|
+
return calls.length > 0
|
|
195
|
+
? { calls, text: remainder.trim(), attempted: true }
|
|
196
|
+
: { calls: [], text, attempted: true, failure: "unrecognized" };
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=text-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-protocol.js","sourceRoot":"","sources":["../../../src/utils/tool-repair/text-protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Tool, ToolCall } from \"../../types.ts\";\n\nexport type TextToolProtocolParseFailure = \"overlap\" | \"mixed-prose\" | \"unrecognized\";\n\nexport interface ParsedTextToolCalls {\n\tcalls: ToolCall[];\n\ttext: string;\n\tattempted: boolean;\n\tfailure?: TextToolProtocolParseFailure;\n}\n\nexport type TextToolProtocolVariant = \"tool-tag\" | \"tool-call\" | \"fenced-json\";\n\nexport interface TextToolProtocolOptions {\n\tvariant?: TextToolProtocolVariant;\n}\n\ntype EnvelopeKind = \"pi_call\" | \"tool_call\" | \"fenced_json\";\n\ninterface EnvelopeMatch {\n\tkind: EnvelopeKind;\n\tstart: number;\n\tend: number;\n\tname?: string;\n\tbody: string;\n}\n\nconst MAX_SCHEMA_CHARS = 600;\nconst DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT: TextToolProtocolVariant = \"tool-tag\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction compactJson(value: unknown, maxChars = MAX_SCHEMA_CHARS): string {\n\tconst json = JSON.stringify(value);\n\tif (json.length <= maxChars) return json;\n\treturn `${json.slice(0, maxChars - 1)}…`;\n}\n\nfunction escapeAttribute(value: string): string {\n\treturn value.replace(/&/g, \"&\").replace(/\"/g, \""\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction unescapeAttribute(value: string): string {\n\treturn value\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/&/g, \"&\");\n}\n\nfunction knownToolNames(tools: readonly Tool[]): Set<string> {\n\treturn new Set(tools.map((tool) => tool.name));\n}\n\nfunction findToolEnvelopes(text: string): EnvelopeMatch[] {\n\tconst matches: EnvelopeMatch[] = [];\n\tconst piCallEnvelope = /<pi:call\\s+name=\"([^\"]+)\"\\s*>([\\s\\S]*?)<\\/pi:call>/g;\n\tfor (const match of text.matchAll(piCallEnvelope)) {\n\t\tmatches.push({\n\t\t\tkind: \"pi_call\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tname: unescapeAttribute(match[1] ?? \"\"),\n\t\t\tbody: match[2] ?? \"\",\n\t\t});\n\t}\n\n\tconst toolCallEnvelope = /<tool_call>([\\s\\S]*?)<\\/tool_call>/g;\n\tfor (const match of text.matchAll(toolCallEnvelope)) {\n\t\tmatches.push({\n\t\t\tkind: \"tool_call\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tbody: match[1] ?? \"\",\n\t\t});\n\t}\n\n\tconst fence = /```(?:tool|tool_call)\\s*\\n([\\s\\S]*?)\\n?```/gi;\n\tfor (const match of text.matchAll(fence)) {\n\t\tmatches.push({\n\t\t\tkind: \"fenced_json\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tbody: match[1] ?? \"\",\n\t\t});\n\t}\n\n\treturn matches.sort((a, b) => a.start - b.start);\n}\n\nfunction hasOverlap(matches: readonly EnvelopeMatch[]): boolean {\n\tlet previousEnd = -1;\n\tfor (const match of matches) {\n\t\tif (match.start < previousEnd) return true;\n\t\tpreviousEnd = match.end;\n\t}\n\treturn false;\n}\n\nfunction remainingText(text: string, matches: readonly EnvelopeMatch[]): string {\n\tlet cursor = 0;\n\tconst pieces: string[] = [];\n\tfor (const match of matches) {\n\t\tpieces.push(text.slice(cursor, match.start));\n\t\tcursor = match.end;\n\t}\n\tpieces.push(text.slice(cursor));\n\treturn pieces.join(\"\");\n}\n\nfunction coerceArguments(value: unknown): {\n\targuments: Record<string, unknown>;\n\trawArguments?: Record<string, unknown>;\n} {\n\tif (isRecord(value)) return { arguments: value };\n\treturn { arguments: value as Record<string, unknown>, rawArguments: { value } };\n}\n\nfunction parseJsonValue(raw: string): { ok: true; value: unknown } | { ok: false } {\n\ttry {\n\t\treturn { ok: true, value: JSON.parse(raw) as unknown };\n\t} catch {\n\t\treturn { ok: false };\n\t}\n}\n\nfunction parseJsonObject(raw: string): Record<string, unknown> | undefined {\n\tconst parsed = parseJsonValue(raw);\n\treturn parsed.ok && isRecord(parsed.value) ? parsed.value : undefined;\n}\n\nfunction textToolErrorMessage(name: string, names: readonly string[]): string | undefined {\n\tif (names.includes(name)) return undefined;\n\treturn `Unknown tool \"${name}\". Valid tools: ${names.join(\", \")}.`;\n}\n\nfunction extractNameFromMalformedJson(raw: string): string | undefined {\n\tconst match = /\"(?:name|tool)\"\\s*:\\s*\"([^\"]+)\"/.exec(raw);\n\treturn match?.[1];\n}\n\nfunction parsePiCallEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (!match.name) return undefined;\n\tconst parsed = parseJsonValue(match.body);\n\tconst args = parsed.ok ? coerceArguments(parsed.value) : coerceArguments(match.body.trim());\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: match.name,\n\t\targuments: args.arguments,\n\t\trawArguments: parsed.ok ? args.rawArguments : { text: match.body.trim() },\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(match.name, names),\n\t};\n}\n\nfunction parseEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (match.kind === \"pi_call\") return parsePiCallEnvelope(match, names, index);\n\n\tconst parsed = parseJsonObject(match.body);\n\tif (!parsed) {\n\t\tconst name = extractNameFromMalformedJson(match.body);\n\t\tif (!name) return undefined;\n\t\treturn {\n\t\t\ttype: \"toolCall\",\n\t\t\tid: `text-tool-${index}`,\n\t\t\tname,\n\t\t\targuments: match.body.trim() as unknown as Record<string, unknown>,\n\t\t\trawArguments: { text: match.body.trim() },\n\t\t\tsource: \"text-protocol\",\n\t\t\terrorMessage: textToolErrorMessage(name, names),\n\t\t};\n\t}\n\n\tconst nameValue = parsed.name ?? parsed.tool;\n\tif (typeof nameValue !== \"string\") return undefined;\n\tconst argsValue = parsed.arguments ?? parsed.args ?? {};\n\tconst args = coerceArguments(argsValue);\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: nameValue,\n\t\targuments: args.arguments,\n\t\trawArguments: args.rawArguments,\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(nameValue, names),\n\t};\n}\n\nexport function normalizeTextToolProtocolOptions(\n\toption: boolean | TextToolProtocolOptions | undefined,\n): TextToolProtocolOptions | undefined {\n\tif (!option) return undefined;\n\tif (option === true) return { variant: DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };\n\treturn { variant: option.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };\n}\n\nfunction formatVariantEnvelope(variant: TextToolProtocolVariant, toolName: string, argsJson: string): string {\n\tif (variant === \"tool-call\") return `<tool_call>{\"name\":\"${toolName}\",\"arguments\":${argsJson}}</tool_call>`;\n\tif (variant === \"fenced-json\") return `\\`\\`\\`tool_call\\n{\"name\":\"${toolName}\",\"arguments\":${argsJson}}\\n\\`\\`\\``;\n\treturn `<pi:call name=\"${escapeAttribute(toolName)}\">${argsJson}</pi:call>`;\n}\n\nexport function generateTextToolProtocolPrimer(tools: readonly Tool[], options?: TextToolProtocolOptions): string {\n\tif (tools.length === 0) return \"\";\n\tconst variant = options?.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT;\n\tconst lines = [\n\t\t\"Text tool-call protocol is enabled.\",\n\t\t\"When calling tools, output only one or more envelopes and no prose:\",\n\t\tformatVariantEnvelope(variant, \"TOOL_NAME\", '{\"argument\":\"value\"}'),\n\t\t\"Available tools:\",\n\t];\n\tfor (const tool of tools) {\n\t\tlines.push(`- ${escapeAttribute(tool.name)}: ${tool.description}; args schema ${compactJson(tool.parameters)}`);\n\t}\n\tconst exampleTool = tools[0];\n\tlines.push(\"Examples:\", formatVariantEnvelope(variant, exampleTool.name, \"{}\"));\n\treturn lines.join(\"\\n\");\n}\n\nexport function parseTextToolCalls(text: string, knownTools: readonly Tool[]): ParsedTextToolCalls {\n\tif (knownTools.length === 0) return { calls: [], text, attempted: false };\n\tconst matches = findToolEnvelopes(text);\n\tif (matches.length === 0) return { calls: [], text, attempted: false };\n\tif (hasOverlap(matches)) return { calls: [], text, attempted: true, failure: \"overlap\" };\n\tconst remainder = remainingText(text, matches);\n\tconst names = [...knownToolNames(knownTools)];\n\tconst calls = matches\n\t\t.map((match, index) => parseEnvelope(match, names, index + 1))\n\t\t.filter((call): call is ToolCall => call !== undefined);\n\treturn calls.length > 0\n\t\t? { calls, text: remainder.trim(), attempted: true }\n\t\t: { calls: [], text, attempted: true, failure: \"unrecognized\" };\n}\n"]}
|
|
@@ -1,4 +1,42 @@
|
|
|
1
1
|
import type { Tool, ToolCall } from "../types.ts";
|
|
2
|
+
import { type ToolRepairFailureModeName, type ToolRepairModeName } from "./tool-repair/registry.ts";
|
|
3
|
+
export type ToolArgumentValidationOutcome = "clean" | "repaired" | "bounced";
|
|
4
|
+
export type ToolArgumentTeachState = "none" | "note" | "rule";
|
|
5
|
+
export type ToolArgumentExecutionOutcome = "not_run" | "succeeded" | "failed";
|
|
6
|
+
export interface ToolArgumentFailureShapeEntry {
|
|
7
|
+
path: string;
|
|
8
|
+
expectedType: string;
|
|
9
|
+
receivedType: string;
|
|
10
|
+
keyword?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ToolArgumentValidationTelemetryEvent {
|
|
13
|
+
outcome: ToolArgumentValidationOutcome;
|
|
14
|
+
provider?: string;
|
|
15
|
+
model?: string;
|
|
16
|
+
tool: string;
|
|
17
|
+
failureModes: ToolRepairFailureModeName[];
|
|
18
|
+
repairsApplied: ToolRepairModeName[];
|
|
19
|
+
failureShape?: ToolArgumentFailureShapeEntry[];
|
|
20
|
+
errorKeywords?: string[];
|
|
21
|
+
taught: ToolArgumentTeachState;
|
|
22
|
+
executionOutcome: ToolArgumentExecutionOutcome;
|
|
23
|
+
}
|
|
24
|
+
export interface ToolArgumentValidationOptions {
|
|
25
|
+
model?: string;
|
|
26
|
+
provider?: string;
|
|
27
|
+
telemetry?: (event: ToolArgumentValidationTelemetryEvent) => void;
|
|
28
|
+
repairEnabled?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare class ToolArgumentValidationError extends Error {
|
|
31
|
+
readonly toolName: string;
|
|
32
|
+
readonly signature: string;
|
|
33
|
+
readonly enrichment: string;
|
|
34
|
+
constructor(message: string, options: {
|
|
35
|
+
toolName: string;
|
|
36
|
+
signature: string;
|
|
37
|
+
enrichment: string;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
2
40
|
/**
|
|
3
41
|
* Finds a tool by name and validates the tool call arguments against its TypeBox schema
|
|
4
42
|
* @param tools Array of tool definitions
|
|
@@ -6,13 +44,18 @@ import type { Tool, ToolCall } from "../types.ts";
|
|
|
6
44
|
* @returns The validated arguments
|
|
7
45
|
* @throws Error if tool is not found or validation fails
|
|
8
46
|
*/
|
|
9
|
-
export declare function validateToolCall(tools: Tool[], toolCall: ToolCall):
|
|
47
|
+
export declare function validateToolCall(tools: Tool[], toolCall: ToolCall, options?: ToolArgumentValidationOptions): Record<string, unknown>;
|
|
10
48
|
/**
|
|
11
|
-
* Validates tool call arguments against the tool's TypeBox schema
|
|
49
|
+
* Validates tool call arguments against the tool's TypeBox schema.
|
|
50
|
+
*
|
|
51
|
+
* The hot path is validate-first and allocation-free: valid arguments return the exact
|
|
52
|
+
* argument object emitted by the model. Only invalid calls enter the deterministic
|
|
53
|
+
* repair layer, which applies named, guard-checked shape repairs on a clone.
|
|
54
|
+
*
|
|
12
55
|
* @param tool The tool definition with TypeBox schema
|
|
13
56
|
* @param toolCall The tool call from the LLM
|
|
14
|
-
* @returns The validated (and potentially
|
|
57
|
+
* @returns The validated (and potentially repaired) arguments
|
|
15
58
|
* @throws Error with formatted message if validation fails
|
|
16
59
|
*/
|
|
17
|
-
export declare function validateToolArguments(tool: Tool, toolCall: ToolCall):
|
|
60
|
+
export declare function validateToolArguments(tool: Tool, toolCall: ToolCall, options?: ToolArgumentValidationOptions): Record<string, unknown>;
|
|
18
61
|
//# sourceMappingURL=validation.d.ts.map
|