@lov3kaizen/agentsea-structured 0.5.1
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/LICENSE +21 -0
- package/README.md +330 -0
- package/dist/index.d.ts +783 -0
- package/dist/index.js +3210 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3210 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { zodToJsonSchema as zodToJsonSchema$1 } from 'zod-to-json-schema';
|
|
3
|
+
import { EventEmitter } from 'eventemitter3';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __esm = (fn, res) => function __init() {
|
|
8
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
9
|
+
};
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
function zodToJsonSchema(schema) {
|
|
15
|
+
return zodToJsonSchema$1(schema, { $refStrategy: "none" });
|
|
16
|
+
}
|
|
17
|
+
function schemaToPrompt(schema, options = {}) {
|
|
18
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
19
|
+
switch (opts.format) {
|
|
20
|
+
case "json-schema":
|
|
21
|
+
return generateJsonSchemaPrompt(schema, opts);
|
|
22
|
+
case "typescript":
|
|
23
|
+
return generateTypeScriptPrompt(schema, opts);
|
|
24
|
+
case "natural":
|
|
25
|
+
return generateNaturalPrompt(schema, opts);
|
|
26
|
+
case "examples":
|
|
27
|
+
return generateExamplesPrompt(schema, opts);
|
|
28
|
+
default:
|
|
29
|
+
return generateJsonSchemaPrompt(schema, opts);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function generateJsonSchemaPrompt(schema, opts) {
|
|
33
|
+
const jsonSchema = zodToJsonSchema$1(schema, {
|
|
34
|
+
$refStrategy: "none",
|
|
35
|
+
errorMessages: true
|
|
36
|
+
});
|
|
37
|
+
const cleanedSchema = cleanJsonSchema(jsonSchema);
|
|
38
|
+
const text = [
|
|
39
|
+
"Respond with a JSON object that matches this schema:",
|
|
40
|
+
"",
|
|
41
|
+
"```json",
|
|
42
|
+
JSON.stringify(cleanedSchema, null, opts.indent),
|
|
43
|
+
"```"
|
|
44
|
+
].join("\n");
|
|
45
|
+
return {
|
|
46
|
+
text,
|
|
47
|
+
format: "json-schema",
|
|
48
|
+
jsonSchema: cleanedSchema
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function generateTypeScriptPrompt(schema, opts) {
|
|
52
|
+
const typeScript = zodToTypeScript(schema, opts);
|
|
53
|
+
const jsonSchema = zodToJsonSchema$1(schema, {
|
|
54
|
+
$refStrategy: "none"
|
|
55
|
+
});
|
|
56
|
+
const text = [
|
|
57
|
+
"Respond with a JSON object matching this TypeScript type:",
|
|
58
|
+
"",
|
|
59
|
+
"```typescript",
|
|
60
|
+
typeScript,
|
|
61
|
+
"```"
|
|
62
|
+
].join("\n");
|
|
63
|
+
return {
|
|
64
|
+
text,
|
|
65
|
+
format: "typescript",
|
|
66
|
+
jsonSchema,
|
|
67
|
+
typeScript
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function generateNaturalPrompt(schema, opts) {
|
|
71
|
+
const fieldInfos = analyzeSchema(schema, opts);
|
|
72
|
+
const jsonSchema = zodToJsonSchema$1(schema, {
|
|
73
|
+
$refStrategy: "none"
|
|
74
|
+
});
|
|
75
|
+
const lines = [
|
|
76
|
+
"Respond with a JSON object containing the following fields:",
|
|
77
|
+
""
|
|
78
|
+
];
|
|
79
|
+
for (const field of fieldInfos) {
|
|
80
|
+
const requiredStr = field.required ? "(required)" : "(optional)";
|
|
81
|
+
let line = `- ${field.name} ${requiredStr}: ${field.jsonType}`;
|
|
82
|
+
if (opts.includeDescriptions && field.description) {
|
|
83
|
+
line += ` - ${field.description}`;
|
|
84
|
+
}
|
|
85
|
+
if (opts.includeConstraints && field.constraints.length > 0) {
|
|
86
|
+
const constraintStrs = field.constraints.map((c) => c.description);
|
|
87
|
+
line += ` [${constraintStrs.join(", ")}]`;
|
|
88
|
+
}
|
|
89
|
+
lines.push(line);
|
|
90
|
+
if (field.children && field.children.length > 0) {
|
|
91
|
+
for (const child of field.children) {
|
|
92
|
+
lines.push(` - ${child.name}: ${child.jsonType}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
text: lines.join("\n"),
|
|
98
|
+
format: "natural",
|
|
99
|
+
jsonSchema
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function generateExamplesPrompt(schema, opts) {
|
|
103
|
+
const jsonSchema = zodToJsonSchema$1(schema, {
|
|
104
|
+
$refStrategy: "none"
|
|
105
|
+
});
|
|
106
|
+
const example = generateExample(jsonSchema, opts.maxDepth);
|
|
107
|
+
const text = [
|
|
108
|
+
"Respond with a JSON object following this example structure:",
|
|
109
|
+
"",
|
|
110
|
+
"```json",
|
|
111
|
+
JSON.stringify(example, null, opts.indent),
|
|
112
|
+
"```"
|
|
113
|
+
].join("\n");
|
|
114
|
+
return {
|
|
115
|
+
text,
|
|
116
|
+
format: "examples",
|
|
117
|
+
jsonSchema
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function cleanJsonSchema(schema) {
|
|
121
|
+
const cleaned = {};
|
|
122
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
123
|
+
if (key.startsWith("$") && key !== "$defs") continue;
|
|
124
|
+
if (key === "additionalProperties" && value === false) continue;
|
|
125
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
126
|
+
cleaned[key] = cleanJsonSchema(value);
|
|
127
|
+
} else if (Array.isArray(value)) {
|
|
128
|
+
cleaned[key] = value.map(
|
|
129
|
+
(item) => typeof item === "object" && item !== null ? cleanJsonSchema(item) : item
|
|
130
|
+
);
|
|
131
|
+
} else {
|
|
132
|
+
cleaned[key] = value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return cleaned;
|
|
136
|
+
}
|
|
137
|
+
function zodToTypeScript(schema, opts, depth = 0) {
|
|
138
|
+
if (depth > opts.maxDepth) return "any";
|
|
139
|
+
const indent = " ".repeat(depth);
|
|
140
|
+
const innerIndent = " ".repeat(depth + 1);
|
|
141
|
+
if (schema instanceof z.ZodObject) {
|
|
142
|
+
const shape = schema.shape;
|
|
143
|
+
const lines = ["{"];
|
|
144
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
145
|
+
const fieldSchema = value;
|
|
146
|
+
const isOptional = fieldSchema.isOptional?.() ?? false;
|
|
147
|
+
const typeStr = zodToTypeScript(fieldSchema, opts, depth + 1);
|
|
148
|
+
const optionalMarker = isOptional ? "?" : "";
|
|
149
|
+
const description = fieldSchema.description;
|
|
150
|
+
const descComment = opts.includeDescriptions && description ? ` // ${description}` : "";
|
|
151
|
+
lines.push(
|
|
152
|
+
`${innerIndent}${key}${optionalMarker}: ${typeStr};${descComment}`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
lines.push(`${indent}}`);
|
|
156
|
+
return lines.join("\n");
|
|
157
|
+
}
|
|
158
|
+
if (schema instanceof z.ZodArray) {
|
|
159
|
+
const elementType = zodToTypeScript(schema.element, opts, depth);
|
|
160
|
+
return `${elementType}[]`;
|
|
161
|
+
}
|
|
162
|
+
if (schema instanceof z.ZodString) {
|
|
163
|
+
return "string";
|
|
164
|
+
}
|
|
165
|
+
if (schema instanceof z.ZodNumber) {
|
|
166
|
+
return "number";
|
|
167
|
+
}
|
|
168
|
+
if (schema instanceof z.ZodBoolean) {
|
|
169
|
+
return "boolean";
|
|
170
|
+
}
|
|
171
|
+
if (schema instanceof z.ZodNull) {
|
|
172
|
+
return "null";
|
|
173
|
+
}
|
|
174
|
+
if (schema instanceof z.ZodUndefined) {
|
|
175
|
+
return "undefined";
|
|
176
|
+
}
|
|
177
|
+
if (schema instanceof z.ZodLiteral) {
|
|
178
|
+
const value = schema.value;
|
|
179
|
+
return typeof value === "string" ? `'${value}'` : String(value);
|
|
180
|
+
}
|
|
181
|
+
if (schema instanceof z.ZodEnum) {
|
|
182
|
+
const values = schema.options;
|
|
183
|
+
return values.map((v) => `'${v}'`).join(" | ");
|
|
184
|
+
}
|
|
185
|
+
if (schema instanceof z.ZodNativeEnum) {
|
|
186
|
+
return "enum";
|
|
187
|
+
}
|
|
188
|
+
if (schema instanceof z.ZodUnion) {
|
|
189
|
+
const options = schema.options;
|
|
190
|
+
return options.map((o) => zodToTypeScript(o, opts, depth)).join(" | ");
|
|
191
|
+
}
|
|
192
|
+
if (schema instanceof z.ZodOptional) {
|
|
193
|
+
const innerType = zodToTypeScript(schema.unwrap(), opts, depth);
|
|
194
|
+
return `${innerType} | undefined`;
|
|
195
|
+
}
|
|
196
|
+
if (schema instanceof z.ZodNullable) {
|
|
197
|
+
const innerType = zodToTypeScript(schema.unwrap(), opts, depth);
|
|
198
|
+
return `${innerType} | null`;
|
|
199
|
+
}
|
|
200
|
+
if (schema instanceof z.ZodDefault) {
|
|
201
|
+
return zodToTypeScript(schema.removeDefault(), opts, depth);
|
|
202
|
+
}
|
|
203
|
+
if (schema instanceof z.ZodRecord) {
|
|
204
|
+
const valueType = zodToTypeScript(schema.valueSchema, opts, depth);
|
|
205
|
+
return `Record<string, ${valueType}>`;
|
|
206
|
+
}
|
|
207
|
+
if (schema instanceof z.ZodTuple) {
|
|
208
|
+
const items = schema.items;
|
|
209
|
+
const types = items.map((item) => zodToTypeScript(item, opts, depth));
|
|
210
|
+
return `[${types.join(", ")}]`;
|
|
211
|
+
}
|
|
212
|
+
if (schema instanceof z.ZodDate) {
|
|
213
|
+
return "Date";
|
|
214
|
+
}
|
|
215
|
+
return "unknown";
|
|
216
|
+
}
|
|
217
|
+
function analyzeSchema(schema, opts, path = "") {
|
|
218
|
+
const fields = [];
|
|
219
|
+
if (schema instanceof z.ZodObject) {
|
|
220
|
+
const shape = schema.shape;
|
|
221
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
222
|
+
const fieldSchema = value;
|
|
223
|
+
const fieldPath = path ? `${path}.${key}` : key;
|
|
224
|
+
const info = extractFieldInfo(key, fieldSchema, fieldPath, opts);
|
|
225
|
+
fields.push(info);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return fields;
|
|
229
|
+
}
|
|
230
|
+
function extractFieldInfo(name, schema, path, opts) {
|
|
231
|
+
const constraints = extractConstraints(schema);
|
|
232
|
+
const { zodType, jsonType } = getTypeNames(schema);
|
|
233
|
+
const description = schema.description;
|
|
234
|
+
const isOptional = schema.isOptional?.() ?? false;
|
|
235
|
+
const hasDefault = schema instanceof z.ZodDefault;
|
|
236
|
+
const defaultValue = hasDefault ? schema._def.defaultValue() : void 0;
|
|
237
|
+
let children;
|
|
238
|
+
const unwrapped = unwrapSchema(schema);
|
|
239
|
+
if (unwrapped instanceof z.ZodObject) {
|
|
240
|
+
children = analyzeSchema(unwrapped, opts, path);
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
path,
|
|
244
|
+
name,
|
|
245
|
+
zodType,
|
|
246
|
+
jsonType,
|
|
247
|
+
description,
|
|
248
|
+
required: !isOptional,
|
|
249
|
+
hasDefault,
|
|
250
|
+
defaultValue,
|
|
251
|
+
constraints,
|
|
252
|
+
children
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function extractConstraints(schema) {
|
|
256
|
+
const constraints = [];
|
|
257
|
+
const def = schema._def;
|
|
258
|
+
const checks = def?.checks ?? [];
|
|
259
|
+
for (const check of checks) {
|
|
260
|
+
switch (check.kind) {
|
|
261
|
+
case "min":
|
|
262
|
+
constraints.push({
|
|
263
|
+
type: "min",
|
|
264
|
+
value: check.value,
|
|
265
|
+
description: `minimum: ${String(check.value)}`
|
|
266
|
+
});
|
|
267
|
+
break;
|
|
268
|
+
case "max":
|
|
269
|
+
constraints.push({
|
|
270
|
+
type: "max",
|
|
271
|
+
value: check.value,
|
|
272
|
+
description: `maximum: ${String(check.value)}`
|
|
273
|
+
});
|
|
274
|
+
break;
|
|
275
|
+
case "length":
|
|
276
|
+
constraints.push({
|
|
277
|
+
type: "length",
|
|
278
|
+
value: check.value,
|
|
279
|
+
description: `length: ${String(check.value)}`
|
|
280
|
+
});
|
|
281
|
+
break;
|
|
282
|
+
case "email":
|
|
283
|
+
constraints.push({
|
|
284
|
+
type: "email",
|
|
285
|
+
value: true,
|
|
286
|
+
description: "must be valid email"
|
|
287
|
+
});
|
|
288
|
+
break;
|
|
289
|
+
case "url":
|
|
290
|
+
constraints.push({
|
|
291
|
+
type: "url",
|
|
292
|
+
value: true,
|
|
293
|
+
description: "must be valid URL"
|
|
294
|
+
});
|
|
295
|
+
break;
|
|
296
|
+
case "regex":
|
|
297
|
+
constraints.push({
|
|
298
|
+
type: "regex",
|
|
299
|
+
value: check.value,
|
|
300
|
+
description: `must match pattern`
|
|
301
|
+
});
|
|
302
|
+
break;
|
|
303
|
+
case "int":
|
|
304
|
+
constraints.push({
|
|
305
|
+
type: "int",
|
|
306
|
+
value: true,
|
|
307
|
+
description: "must be integer"
|
|
308
|
+
});
|
|
309
|
+
break;
|
|
310
|
+
case "positive":
|
|
311
|
+
constraints.push({
|
|
312
|
+
type: "positive",
|
|
313
|
+
value: true,
|
|
314
|
+
description: "must be positive"
|
|
315
|
+
});
|
|
316
|
+
break;
|
|
317
|
+
case "negative":
|
|
318
|
+
constraints.push({
|
|
319
|
+
type: "negative",
|
|
320
|
+
value: true,
|
|
321
|
+
description: "must be negative"
|
|
322
|
+
});
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return constraints;
|
|
327
|
+
}
|
|
328
|
+
function getTypeNames(schema) {
|
|
329
|
+
const unwrapped = unwrapSchema(schema);
|
|
330
|
+
if (unwrapped instanceof z.ZodString)
|
|
331
|
+
return { zodType: "ZodString", jsonType: "string" };
|
|
332
|
+
if (unwrapped instanceof z.ZodNumber)
|
|
333
|
+
return { zodType: "ZodNumber", jsonType: "number" };
|
|
334
|
+
if (unwrapped instanceof z.ZodBoolean)
|
|
335
|
+
return { zodType: "ZodBoolean", jsonType: "boolean" };
|
|
336
|
+
if (unwrapped instanceof z.ZodNull)
|
|
337
|
+
return { zodType: "ZodNull", jsonType: "null" };
|
|
338
|
+
if (unwrapped instanceof z.ZodArray)
|
|
339
|
+
return { zodType: "ZodArray", jsonType: "array" };
|
|
340
|
+
if (unwrapped instanceof z.ZodObject)
|
|
341
|
+
return { zodType: "ZodObject", jsonType: "object" };
|
|
342
|
+
if (unwrapped instanceof z.ZodEnum)
|
|
343
|
+
return { zodType: "ZodEnum", jsonType: "string" };
|
|
344
|
+
if (unwrapped instanceof z.ZodLiteral) {
|
|
345
|
+
const value = unwrapped.value;
|
|
346
|
+
const type = typeof value;
|
|
347
|
+
return { zodType: "ZodLiteral", jsonType: type };
|
|
348
|
+
}
|
|
349
|
+
if (unwrapped instanceof z.ZodUnion)
|
|
350
|
+
return { zodType: "ZodUnion", jsonType: "union" };
|
|
351
|
+
if (unwrapped instanceof z.ZodRecord)
|
|
352
|
+
return { zodType: "ZodRecord", jsonType: "object" };
|
|
353
|
+
if (unwrapped instanceof z.ZodDate)
|
|
354
|
+
return { zodType: "ZodDate", jsonType: "string" };
|
|
355
|
+
return { zodType: "unknown", jsonType: "unknown" };
|
|
356
|
+
}
|
|
357
|
+
function unwrapSchema(schema) {
|
|
358
|
+
if (schema instanceof z.ZodOptional) return unwrapSchema(schema.unwrap());
|
|
359
|
+
if (schema instanceof z.ZodNullable) return unwrapSchema(schema.unwrap());
|
|
360
|
+
if (schema instanceof z.ZodDefault)
|
|
361
|
+
return unwrapSchema(schema.removeDefault());
|
|
362
|
+
return schema;
|
|
363
|
+
}
|
|
364
|
+
function generateExample(schema, maxDepth, depth = 0) {
|
|
365
|
+
if (depth > maxDepth) return null;
|
|
366
|
+
if (schema.examples && schema.examples.length > 0) {
|
|
367
|
+
return schema.examples[0];
|
|
368
|
+
}
|
|
369
|
+
if (schema.default !== void 0) {
|
|
370
|
+
return schema.default;
|
|
371
|
+
}
|
|
372
|
+
if (schema.const !== void 0) {
|
|
373
|
+
return schema.const;
|
|
374
|
+
}
|
|
375
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
376
|
+
return schema.enum[0];
|
|
377
|
+
}
|
|
378
|
+
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
379
|
+
switch (type) {
|
|
380
|
+
case "string":
|
|
381
|
+
return schema.format === "email" ? "example@email.com" : schema.format === "uri" ? "https://example.com" : "string";
|
|
382
|
+
case "number":
|
|
383
|
+
case "integer":
|
|
384
|
+
return schema.minimum ?? 0;
|
|
385
|
+
case "boolean":
|
|
386
|
+
return true;
|
|
387
|
+
case "null":
|
|
388
|
+
return null;
|
|
389
|
+
case "array":
|
|
390
|
+
if (schema.items) {
|
|
391
|
+
const itemExample = generateExample(
|
|
392
|
+
Array.isArray(schema.items) ? schema.items[0] : schema.items,
|
|
393
|
+
maxDepth,
|
|
394
|
+
depth + 1
|
|
395
|
+
);
|
|
396
|
+
return [itemExample];
|
|
397
|
+
}
|
|
398
|
+
return [];
|
|
399
|
+
case "object":
|
|
400
|
+
if (schema.properties) {
|
|
401
|
+
const obj = {};
|
|
402
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
403
|
+
obj[key] = generateExample(propSchema, maxDepth, depth + 1);
|
|
404
|
+
}
|
|
405
|
+
return obj;
|
|
406
|
+
}
|
|
407
|
+
return {};
|
|
408
|
+
default:
|
|
409
|
+
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
410
|
+
return generateExample(schema.anyOf[0], maxDepth, depth);
|
|
411
|
+
}
|
|
412
|
+
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
413
|
+
return generateExample(schema.oneOf[0], maxDepth, depth);
|
|
414
|
+
}
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
var DEFAULT_OPTIONS, SchemaPromptGenerator;
|
|
419
|
+
var init_SchemaToPrompt = __esm({
|
|
420
|
+
"src/schema/SchemaToPrompt.ts"() {
|
|
421
|
+
DEFAULT_OPTIONS = {
|
|
422
|
+
format: "json-schema",
|
|
423
|
+
includeDescriptions: true,
|
|
424
|
+
includeExamples: true,
|
|
425
|
+
includeConstraints: true,
|
|
426
|
+
maxDepth: 10,
|
|
427
|
+
indent: 2
|
|
428
|
+
};
|
|
429
|
+
SchemaPromptGenerator = {
|
|
430
|
+
toJsonSchema: (schema, options) => schemaToPrompt(schema, { ...options, format: "json-schema" }),
|
|
431
|
+
toTypeScript: (schema, options) => schemaToPrompt(schema, { ...options, format: "typescript" }),
|
|
432
|
+
toNatural: (schema, options) => schemaToPrompt(schema, { ...options, format: "natural" }),
|
|
433
|
+
toExamples: (schema, options) => schemaToPrompt(schema, { ...options, format: "examples" }),
|
|
434
|
+
generateExample
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
function validateSchema(schema, data) {
|
|
439
|
+
const result = schema.safeParse(data);
|
|
440
|
+
if (result.success) {
|
|
441
|
+
return {
|
|
442
|
+
success: true,
|
|
443
|
+
data: result.data
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
success: false,
|
|
448
|
+
errors: formatZodErrors(result.error)
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function validateSchemaOrThrow(schema, data) {
|
|
452
|
+
return schema.parse(data);
|
|
453
|
+
}
|
|
454
|
+
function validatePartial(schema, data, options = {}) {
|
|
455
|
+
const partialSchema = makePartial(schema);
|
|
456
|
+
const result = partialSchema.safeParse(data);
|
|
457
|
+
if (!result.success) {
|
|
458
|
+
return {
|
|
459
|
+
success: false,
|
|
460
|
+
errors: formatZodErrors(result.error)
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
if (options.requiredFields && options.requiredFields.length > 0) {
|
|
464
|
+
const missingFields = [];
|
|
465
|
+
for (const field of options.requiredFields) {
|
|
466
|
+
const value = getNestedValue(result.data, field);
|
|
467
|
+
if (value === void 0) {
|
|
468
|
+
missingFields.push({
|
|
469
|
+
path: field.split("."),
|
|
470
|
+
message: `Required field '${field}' is missing`,
|
|
471
|
+
expected: "value",
|
|
472
|
+
received: void 0,
|
|
473
|
+
code: "custom"
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (missingFields.length > 0) {
|
|
478
|
+
return {
|
|
479
|
+
success: false,
|
|
480
|
+
errors: missingFields
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
success: true,
|
|
486
|
+
data: result.data
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function matchesSchema(schema, data) {
|
|
490
|
+
return schema.safeParse(data).success;
|
|
491
|
+
}
|
|
492
|
+
function coerceToSchema(schema, data) {
|
|
493
|
+
const directResult = schema.safeParse(data);
|
|
494
|
+
if (directResult.success) {
|
|
495
|
+
return { success: true, data: directResult.data };
|
|
496
|
+
}
|
|
497
|
+
const coerced = coerceData(data, schema);
|
|
498
|
+
const coercedResult = schema.safeParse(coerced);
|
|
499
|
+
if (coercedResult.success) {
|
|
500
|
+
return { success: true, data: coercedResult.data };
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
success: false,
|
|
504
|
+
errors: formatZodErrors(coercedResult.error)
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function getValidationHints(schema, data) {
|
|
508
|
+
const result = schema.safeParse(data);
|
|
509
|
+
if (result.success) {
|
|
510
|
+
return [];
|
|
511
|
+
}
|
|
512
|
+
return result.error.issues.map((issue) => generateHint(issue));
|
|
513
|
+
}
|
|
514
|
+
function formatZodErrors(error) {
|
|
515
|
+
return error.issues.map((issue) => ({
|
|
516
|
+
path: issue.path.map((p) => typeof p === "number" ? p : String(p)),
|
|
517
|
+
message: issue.message,
|
|
518
|
+
expected: getExpectedFromIssue(issue),
|
|
519
|
+
received: getReceivedFromIssue(issue),
|
|
520
|
+
code: issue.code
|
|
521
|
+
}));
|
|
522
|
+
}
|
|
523
|
+
function makePartial(schema) {
|
|
524
|
+
if (schema instanceof z.ZodObject) {
|
|
525
|
+
return schema.partial();
|
|
526
|
+
}
|
|
527
|
+
if (schema instanceof z.ZodArray) {
|
|
528
|
+
return z.array(makePartial(schema.element));
|
|
529
|
+
}
|
|
530
|
+
if (schema instanceof z.ZodUnion) {
|
|
531
|
+
return z.union(
|
|
532
|
+
schema.options.map((opt) => makePartial(opt))
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
if (schema instanceof z.ZodIntersection) {
|
|
536
|
+
return z.intersection(
|
|
537
|
+
makePartial(schema._def.left),
|
|
538
|
+
makePartial(schema._def.right)
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
return schema.optional();
|
|
542
|
+
}
|
|
543
|
+
function getNestedValue(obj, path) {
|
|
544
|
+
if (typeof obj !== "object" || obj === null) {
|
|
545
|
+
return void 0;
|
|
546
|
+
}
|
|
547
|
+
const parts = path.split(".");
|
|
548
|
+
let current = obj;
|
|
549
|
+
for (const part of parts) {
|
|
550
|
+
if (typeof current !== "object" || current === null) {
|
|
551
|
+
return void 0;
|
|
552
|
+
}
|
|
553
|
+
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
|
|
554
|
+
if (arrayMatch) {
|
|
555
|
+
const [, key, index] = arrayMatch;
|
|
556
|
+
const arr = current[key];
|
|
557
|
+
if (!Array.isArray(arr)) {
|
|
558
|
+
return void 0;
|
|
559
|
+
}
|
|
560
|
+
current = arr[parseInt(index, 10)];
|
|
561
|
+
} else {
|
|
562
|
+
current = current[part];
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return current;
|
|
566
|
+
}
|
|
567
|
+
function coerceData(data, schema) {
|
|
568
|
+
if (schema instanceof z.ZodString && typeof data === "number") {
|
|
569
|
+
return String(data);
|
|
570
|
+
}
|
|
571
|
+
if (schema instanceof z.ZodNumber && typeof data === "string") {
|
|
572
|
+
const num = Number(data);
|
|
573
|
+
if (!isNaN(num)) {
|
|
574
|
+
return num;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (schema instanceof z.ZodBoolean) {
|
|
578
|
+
if (data === "true" || data === 1) return true;
|
|
579
|
+
if (data === "false" || data === 0) return false;
|
|
580
|
+
}
|
|
581
|
+
if (schema instanceof z.ZodArray && typeof data === "string") {
|
|
582
|
+
try {
|
|
583
|
+
const parsed = JSON.parse(data);
|
|
584
|
+
if (Array.isArray(parsed)) {
|
|
585
|
+
return parsed;
|
|
586
|
+
}
|
|
587
|
+
} catch {
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (schema instanceof z.ZodObject && typeof data === "string") {
|
|
591
|
+
try {
|
|
592
|
+
return JSON.parse(data);
|
|
593
|
+
} catch {
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (schema instanceof z.ZodObject && typeof data === "object" && data !== null) {
|
|
597
|
+
const objSchema = schema;
|
|
598
|
+
const shape = objSchema.shape;
|
|
599
|
+
const result = {
|
|
600
|
+
...data
|
|
601
|
+
};
|
|
602
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
603
|
+
if (key in result) {
|
|
604
|
+
result[key] = coerceData(result[key], fieldSchema);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return result;
|
|
608
|
+
}
|
|
609
|
+
return data;
|
|
610
|
+
}
|
|
611
|
+
function generateHint(issue, _data) {
|
|
612
|
+
const path = issue.path.join(".");
|
|
613
|
+
const pathPrefix = path ? `At '${path}': ` : "";
|
|
614
|
+
switch (issue.code) {
|
|
615
|
+
case "invalid_type":
|
|
616
|
+
return `${pathPrefix}Expected ${issue.expected}, but received ${issue.received}. Please provide a value of type ${issue.expected}.`;
|
|
617
|
+
case "invalid_literal":
|
|
618
|
+
return `${pathPrefix}Expected the exact value ${JSON.stringify(issue.expected)}, but received ${JSON.stringify(issue.received)}.`;
|
|
619
|
+
case "unrecognized_keys":
|
|
620
|
+
return `${pathPrefix}Unexpected keys: ${issue.keys.join(", ")}. Remove these keys or check the schema.`;
|
|
621
|
+
case "invalid_union":
|
|
622
|
+
return `${pathPrefix}Value doesn't match any of the allowed types. Check the schema for valid options.`;
|
|
623
|
+
case "invalid_enum_value":
|
|
624
|
+
return `${pathPrefix}Value must be one of: ${issue.options.join(", ")}`;
|
|
625
|
+
case "invalid_string":
|
|
626
|
+
if ("validation" in issue) {
|
|
627
|
+
const validation = issue.validation;
|
|
628
|
+
if (typeof validation === "string") {
|
|
629
|
+
return `${pathPrefix}String must be a valid ${validation}.`;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return `${pathPrefix}Invalid string format.`;
|
|
633
|
+
case "too_small": {
|
|
634
|
+
const smallIssue = issue;
|
|
635
|
+
if (smallIssue.type === "string") {
|
|
636
|
+
return `${pathPrefix}String must be at least ${smallIssue.minimum} characters.`;
|
|
637
|
+
}
|
|
638
|
+
if (smallIssue.type === "number") {
|
|
639
|
+
return `${pathPrefix}Number must be ${smallIssue.inclusive ? "at least" : "greater than"} ${smallIssue.minimum}.`;
|
|
640
|
+
}
|
|
641
|
+
if (smallIssue.type === "array") {
|
|
642
|
+
return `${pathPrefix}Array must have at least ${smallIssue.minimum} items.`;
|
|
643
|
+
}
|
|
644
|
+
return `${pathPrefix}Value is too small.`;
|
|
645
|
+
}
|
|
646
|
+
case "too_big": {
|
|
647
|
+
const bigIssue = issue;
|
|
648
|
+
if (bigIssue.type === "string") {
|
|
649
|
+
return `${pathPrefix}String must be at most ${bigIssue.maximum} characters.`;
|
|
650
|
+
}
|
|
651
|
+
if (bigIssue.type === "number") {
|
|
652
|
+
return `${pathPrefix}Number must be ${bigIssue.inclusive ? "at most" : "less than"} ${bigIssue.maximum}.`;
|
|
653
|
+
}
|
|
654
|
+
if (bigIssue.type === "array") {
|
|
655
|
+
return `${pathPrefix}Array must have at most ${bigIssue.maximum} items.`;
|
|
656
|
+
}
|
|
657
|
+
return `${pathPrefix}Value is too big.`;
|
|
658
|
+
}
|
|
659
|
+
case "custom":
|
|
660
|
+
return `${pathPrefix}${issue.message}`;
|
|
661
|
+
case "invalid_date":
|
|
662
|
+
return `${pathPrefix}Invalid date value. Provide a valid date string or Date object.`;
|
|
663
|
+
default:
|
|
664
|
+
return `${pathPrefix}${issue.message}`;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
function getExpectedFromIssue(issue) {
|
|
668
|
+
if ("expected" in issue) {
|
|
669
|
+
return String(issue.expected);
|
|
670
|
+
}
|
|
671
|
+
return void 0;
|
|
672
|
+
}
|
|
673
|
+
function getReceivedFromIssue(issue) {
|
|
674
|
+
if ("received" in issue) {
|
|
675
|
+
return issue.received;
|
|
676
|
+
}
|
|
677
|
+
return void 0;
|
|
678
|
+
}
|
|
679
|
+
var SchemaValidator;
|
|
680
|
+
var init_SchemaValidator = __esm({
|
|
681
|
+
"src/schema/SchemaValidator.ts"() {
|
|
682
|
+
SchemaValidator = {
|
|
683
|
+
validate: validateSchema,
|
|
684
|
+
validateOrThrow: validateSchemaOrThrow,
|
|
685
|
+
validatePartial,
|
|
686
|
+
matches: matchesSchema,
|
|
687
|
+
coerce: coerceToSchema,
|
|
688
|
+
getHints: getValidationHints,
|
|
689
|
+
formatErrors: formatZodErrors
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// src/streaming/IncrementalJsonParser.ts
|
|
695
|
+
function processEscapeChar(char) {
|
|
696
|
+
switch (char) {
|
|
697
|
+
case "n":
|
|
698
|
+
return "\n";
|
|
699
|
+
case "r":
|
|
700
|
+
return "\r";
|
|
701
|
+
case "t":
|
|
702
|
+
return " ";
|
|
703
|
+
case "\\":
|
|
704
|
+
return "\\";
|
|
705
|
+
case '"':
|
|
706
|
+
return '"';
|
|
707
|
+
case "/":
|
|
708
|
+
return "/";
|
|
709
|
+
case "b":
|
|
710
|
+
return "\b";
|
|
711
|
+
case "f":
|
|
712
|
+
return "\f";
|
|
713
|
+
default:
|
|
714
|
+
return char;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
function tokenizeJson(json) {
|
|
718
|
+
const tokens = [];
|
|
719
|
+
const path = [];
|
|
720
|
+
let inString = false;
|
|
721
|
+
let inEscape = false;
|
|
722
|
+
let currentString = "";
|
|
723
|
+
let currentKey = null;
|
|
724
|
+
for (let i = 0; i < json.length; i++) {
|
|
725
|
+
const char = json[i];
|
|
726
|
+
if (inString) {
|
|
727
|
+
if (inEscape) {
|
|
728
|
+
currentString += processEscapeChar(char);
|
|
729
|
+
inEscape = false;
|
|
730
|
+
} else if (char === "\\") {
|
|
731
|
+
inEscape = true;
|
|
732
|
+
} else if (char === '"') {
|
|
733
|
+
inString = false;
|
|
734
|
+
tokens.push({
|
|
735
|
+
type: currentKey === null ? "key" : "string",
|
|
736
|
+
value: currentString,
|
|
737
|
+
path: [...path],
|
|
738
|
+
complete: true
|
|
739
|
+
});
|
|
740
|
+
if (currentKey === null) {
|
|
741
|
+
currentKey = currentString;
|
|
742
|
+
}
|
|
743
|
+
currentString = "";
|
|
744
|
+
} else {
|
|
745
|
+
currentString += char;
|
|
746
|
+
}
|
|
747
|
+
} else if (/\s/.test(char)) {
|
|
748
|
+
continue;
|
|
749
|
+
} else if (char === "{") {
|
|
750
|
+
tokens.push({
|
|
751
|
+
type: "object_start",
|
|
752
|
+
value: "{",
|
|
753
|
+
path: [...path],
|
|
754
|
+
complete: true
|
|
755
|
+
});
|
|
756
|
+
} else if (char === "}") {
|
|
757
|
+
tokens.push({
|
|
758
|
+
type: "object_end",
|
|
759
|
+
value: "}",
|
|
760
|
+
path: [...path],
|
|
761
|
+
complete: true
|
|
762
|
+
});
|
|
763
|
+
path.pop();
|
|
764
|
+
currentKey = null;
|
|
765
|
+
} else if (char === "[") {
|
|
766
|
+
tokens.push({
|
|
767
|
+
type: "array_start",
|
|
768
|
+
value: "[",
|
|
769
|
+
path: [...path],
|
|
770
|
+
complete: true
|
|
771
|
+
});
|
|
772
|
+
} else if (char === "]") {
|
|
773
|
+
tokens.push({
|
|
774
|
+
type: "array_end",
|
|
775
|
+
value: "]",
|
|
776
|
+
path: [...path],
|
|
777
|
+
complete: true
|
|
778
|
+
});
|
|
779
|
+
} else if (char === ":") {
|
|
780
|
+
tokens.push({
|
|
781
|
+
type: "colon",
|
|
782
|
+
value: ":",
|
|
783
|
+
path: [...path],
|
|
784
|
+
complete: true
|
|
785
|
+
});
|
|
786
|
+
if (currentKey) {
|
|
787
|
+
path.push(currentKey);
|
|
788
|
+
}
|
|
789
|
+
} else if (char === ",") {
|
|
790
|
+
tokens.push({
|
|
791
|
+
type: "comma",
|
|
792
|
+
value: ",",
|
|
793
|
+
path: [...path],
|
|
794
|
+
complete: true
|
|
795
|
+
});
|
|
796
|
+
path.pop();
|
|
797
|
+
currentKey = null;
|
|
798
|
+
} else if (char === '"') {
|
|
799
|
+
inString = true;
|
|
800
|
+
currentString = "";
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return tokens;
|
|
804
|
+
}
|
|
805
|
+
var IncrementalJsonParser;
|
|
806
|
+
var init_IncrementalJsonParser = __esm({
|
|
807
|
+
"src/streaming/IncrementalJsonParser.ts"() {
|
|
808
|
+
IncrementalJsonParser = class {
|
|
809
|
+
state;
|
|
810
|
+
result;
|
|
811
|
+
pendingUpdates;
|
|
812
|
+
constructor() {
|
|
813
|
+
this.state = this.createInitialState();
|
|
814
|
+
this.result = {};
|
|
815
|
+
this.pendingUpdates = [];
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Feed a chunk of text to the parser
|
|
819
|
+
*/
|
|
820
|
+
feed(chunk) {
|
|
821
|
+
this.pendingUpdates = [];
|
|
822
|
+
this.state.buffer += chunk;
|
|
823
|
+
this.parse();
|
|
824
|
+
return this.pendingUpdates;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Get the current parsed result
|
|
828
|
+
*/
|
|
829
|
+
getResult() {
|
|
830
|
+
this.finalize();
|
|
831
|
+
return this.result;
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Reset the parser state
|
|
835
|
+
*/
|
|
836
|
+
reset() {
|
|
837
|
+
this.state = this.createInitialState();
|
|
838
|
+
this.result = {};
|
|
839
|
+
this.pendingUpdates = [];
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Create initial parser state
|
|
843
|
+
*/
|
|
844
|
+
createInitialState() {
|
|
845
|
+
return {
|
|
846
|
+
currentPath: [],
|
|
847
|
+
depth: 0,
|
|
848
|
+
buffer: "",
|
|
849
|
+
partial: {},
|
|
850
|
+
inString: false,
|
|
851
|
+
inEscape: false,
|
|
852
|
+
currentString: "",
|
|
853
|
+
currentKey: null,
|
|
854
|
+
containerStack: []
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Parse the current buffer
|
|
859
|
+
*/
|
|
860
|
+
parse() {
|
|
861
|
+
let i = 0;
|
|
862
|
+
let lastSuccessfulIndex = -1;
|
|
863
|
+
while (i < this.state.buffer.length) {
|
|
864
|
+
const char = this.state.buffer[i];
|
|
865
|
+
if (this.state.inString) {
|
|
866
|
+
i = this.parseStringChar(i, char);
|
|
867
|
+
lastSuccessfulIndex = i;
|
|
868
|
+
} else {
|
|
869
|
+
if (char === "t" || char === "f" || char === "n") {
|
|
870
|
+
const remaining = this.state.buffer.slice(i);
|
|
871
|
+
const isComplete = remaining.startsWith("true") || remaining.startsWith("false") || remaining.startsWith("null");
|
|
872
|
+
if (!isComplete) {
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
if (/[-\d]/.test(char)) {
|
|
877
|
+
const remaining = this.state.buffer.slice(i);
|
|
878
|
+
const match = remaining.match(/^-?\d+(\.\d+)?([eE][+-]?\d+)?/);
|
|
879
|
+
if (match) {
|
|
880
|
+
const numStr = match[0];
|
|
881
|
+
const afterNum = remaining.slice(numStr.length);
|
|
882
|
+
if (afterNum.length === 0 || afterNum.length > 0 && !/[\s,}\]]/.test(afterNum[0])) {
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
i = this.parseChar(i, char);
|
|
888
|
+
lastSuccessfulIndex = i;
|
|
889
|
+
}
|
|
890
|
+
i++;
|
|
891
|
+
}
|
|
892
|
+
if (lastSuccessfulIndex >= 0) {
|
|
893
|
+
this.state.buffer = this.state.buffer.slice(lastSuccessfulIndex + 1);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Parse a character inside a string
|
|
898
|
+
*/
|
|
899
|
+
parseStringChar(index, char) {
|
|
900
|
+
if (this.state.inEscape) {
|
|
901
|
+
this.state.currentString += this.processEscape(char);
|
|
902
|
+
this.state.inEscape = false;
|
|
903
|
+
return index;
|
|
904
|
+
}
|
|
905
|
+
if (char === "\\") {
|
|
906
|
+
this.state.inEscape = true;
|
|
907
|
+
return index;
|
|
908
|
+
}
|
|
909
|
+
if (char === '"') {
|
|
910
|
+
this.state.inString = false;
|
|
911
|
+
this.handleStringComplete();
|
|
912
|
+
return index;
|
|
913
|
+
}
|
|
914
|
+
this.state.currentString += char;
|
|
915
|
+
return index;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Parse a character outside a string
|
|
919
|
+
*/
|
|
920
|
+
parseChar(index, char) {
|
|
921
|
+
if (/\s/.test(char)) {
|
|
922
|
+
return index;
|
|
923
|
+
}
|
|
924
|
+
switch (char) {
|
|
925
|
+
case "{":
|
|
926
|
+
this.handleObjectStart();
|
|
927
|
+
break;
|
|
928
|
+
case "}":
|
|
929
|
+
this.handleObjectEnd();
|
|
930
|
+
break;
|
|
931
|
+
case "[":
|
|
932
|
+
this.handleArrayStart();
|
|
933
|
+
break;
|
|
934
|
+
case "]":
|
|
935
|
+
this.handleArrayEnd();
|
|
936
|
+
break;
|
|
937
|
+
case '"':
|
|
938
|
+
this.state.inString = true;
|
|
939
|
+
this.state.currentString = "";
|
|
940
|
+
break;
|
|
941
|
+
case ":":
|
|
942
|
+
break;
|
|
943
|
+
case ",":
|
|
944
|
+
if (this.getCurrentContainer() === "object") {
|
|
945
|
+
this.state.currentKey = null;
|
|
946
|
+
} else if (this.getCurrentContainer() === "array") {
|
|
947
|
+
const lastPath = this.state.currentPath[this.state.currentPath.length - 1];
|
|
948
|
+
if (typeof lastPath === "string" && /^\d+$/.test(lastPath)) {
|
|
949
|
+
this.state.currentPath[this.state.currentPath.length - 1] = String(
|
|
950
|
+
parseInt(lastPath, 10) + 1
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
break;
|
|
955
|
+
case "t":
|
|
956
|
+
case "f":
|
|
957
|
+
index = this.parseBoolean(index);
|
|
958
|
+
break;
|
|
959
|
+
case "n":
|
|
960
|
+
index = this.parseNull(index);
|
|
961
|
+
break;
|
|
962
|
+
default:
|
|
963
|
+
if (/[-\d]/.test(char)) {
|
|
964
|
+
index = this.parseNumber(index);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return index;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Process an escape character
|
|
971
|
+
*/
|
|
972
|
+
processEscape(char) {
|
|
973
|
+
switch (char) {
|
|
974
|
+
case "n":
|
|
975
|
+
return "\n";
|
|
976
|
+
case "r":
|
|
977
|
+
return "\r";
|
|
978
|
+
case "t":
|
|
979
|
+
return " ";
|
|
980
|
+
case "\\":
|
|
981
|
+
return "\\";
|
|
982
|
+
case '"':
|
|
983
|
+
return '"';
|
|
984
|
+
case "/":
|
|
985
|
+
return "/";
|
|
986
|
+
default:
|
|
987
|
+
return char;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Handle completion of a string
|
|
992
|
+
*/
|
|
993
|
+
handleStringComplete() {
|
|
994
|
+
const value = this.state.currentString;
|
|
995
|
+
if (this.getCurrentContainer() === "object" && this.state.currentKey === null) {
|
|
996
|
+
this.state.currentKey = value;
|
|
997
|
+
} else {
|
|
998
|
+
this.setValue(value);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Handle start of object
|
|
1003
|
+
*/
|
|
1004
|
+
handleObjectStart() {
|
|
1005
|
+
this.state.depth++;
|
|
1006
|
+
this.state.containerStack.push("object");
|
|
1007
|
+
if (this.state.currentKey !== null) {
|
|
1008
|
+
this.state.currentPath.push(this.state.currentKey);
|
|
1009
|
+
this.state.currentKey = null;
|
|
1010
|
+
}
|
|
1011
|
+
this.setNestedValue(this.result, this.state.currentPath, {});
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Handle end of object
|
|
1015
|
+
*/
|
|
1016
|
+
handleObjectEnd() {
|
|
1017
|
+
this.state.depth--;
|
|
1018
|
+
this.state.containerStack.pop();
|
|
1019
|
+
const path = [...this.state.currentPath];
|
|
1020
|
+
const value = this.getNestedValue(this.result, path);
|
|
1021
|
+
this.pendingUpdates.push({
|
|
1022
|
+
path,
|
|
1023
|
+
value,
|
|
1024
|
+
complete: true
|
|
1025
|
+
});
|
|
1026
|
+
const currentContainer = this.getCurrentContainer();
|
|
1027
|
+
if (currentContainer !== "array" && this.state.currentPath.length > 0) {
|
|
1028
|
+
this.state.currentPath.pop();
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Handle start of array
|
|
1033
|
+
*/
|
|
1034
|
+
handleArrayStart() {
|
|
1035
|
+
this.state.depth++;
|
|
1036
|
+
this.state.containerStack.push("array");
|
|
1037
|
+
if (this.state.currentKey !== null) {
|
|
1038
|
+
this.state.currentPath.push(this.state.currentKey);
|
|
1039
|
+
this.state.currentKey = null;
|
|
1040
|
+
}
|
|
1041
|
+
this.setNestedValue(this.result, this.state.currentPath, []);
|
|
1042
|
+
this.state.currentPath.push("0");
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Handle end of array
|
|
1046
|
+
*/
|
|
1047
|
+
handleArrayEnd() {
|
|
1048
|
+
this.state.depth--;
|
|
1049
|
+
this.state.containerStack.pop();
|
|
1050
|
+
this.state.currentPath.pop();
|
|
1051
|
+
const path = [...this.state.currentPath];
|
|
1052
|
+
const value = this.getNestedValue(this.result, path);
|
|
1053
|
+
this.pendingUpdates.push({
|
|
1054
|
+
path,
|
|
1055
|
+
value,
|
|
1056
|
+
complete: true
|
|
1057
|
+
});
|
|
1058
|
+
const currentContainer = this.getCurrentContainer();
|
|
1059
|
+
if (currentContainer !== "array" && this.state.currentPath.length > 0) {
|
|
1060
|
+
this.state.currentPath.pop();
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Parse a boolean value
|
|
1065
|
+
*/
|
|
1066
|
+
parseBoolean(startIndex) {
|
|
1067
|
+
const remaining = this.state.buffer.slice(startIndex);
|
|
1068
|
+
if (remaining.startsWith("true")) {
|
|
1069
|
+
this.setValue(true);
|
|
1070
|
+
return startIndex + 3;
|
|
1071
|
+
}
|
|
1072
|
+
if (remaining.startsWith("false")) {
|
|
1073
|
+
this.setValue(false);
|
|
1074
|
+
return startIndex + 4;
|
|
1075
|
+
}
|
|
1076
|
+
return startIndex;
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Parse a null value
|
|
1080
|
+
*/
|
|
1081
|
+
parseNull(startIndex) {
|
|
1082
|
+
const remaining = this.state.buffer.slice(startIndex);
|
|
1083
|
+
if (remaining.startsWith("null")) {
|
|
1084
|
+
this.setValue(null);
|
|
1085
|
+
return startIndex + 3;
|
|
1086
|
+
}
|
|
1087
|
+
return startIndex;
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Parse a number value
|
|
1091
|
+
*/
|
|
1092
|
+
parseNumber(startIndex) {
|
|
1093
|
+
const remaining = this.state.buffer.slice(startIndex);
|
|
1094
|
+
const match = remaining.match(/^-?\d+(\.\d+)?([eE][+-]?\d+)?/);
|
|
1095
|
+
if (match) {
|
|
1096
|
+
const numStr = match[0];
|
|
1097
|
+
const value = numStr.includes(".") || numStr.includes("e") || numStr.includes("E") ? parseFloat(numStr) : parseInt(numStr, 10);
|
|
1098
|
+
this.setValue(value);
|
|
1099
|
+
return startIndex + numStr.length - 1;
|
|
1100
|
+
}
|
|
1101
|
+
return startIndex;
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Set a value at the current path
|
|
1105
|
+
*/
|
|
1106
|
+
setValue(value) {
|
|
1107
|
+
let path;
|
|
1108
|
+
if (this.state.currentKey !== null) {
|
|
1109
|
+
path = [...this.state.currentPath, this.state.currentKey];
|
|
1110
|
+
this.state.currentKey = null;
|
|
1111
|
+
} else if (this.getCurrentContainer() === "array") {
|
|
1112
|
+
path = [...this.state.currentPath];
|
|
1113
|
+
} else {
|
|
1114
|
+
path = [...this.state.currentPath];
|
|
1115
|
+
}
|
|
1116
|
+
this.setNestedValue(this.result, path, value);
|
|
1117
|
+
this.pendingUpdates.push({
|
|
1118
|
+
path,
|
|
1119
|
+
value,
|
|
1120
|
+
complete: true
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Get current container type
|
|
1125
|
+
*/
|
|
1126
|
+
getCurrentContainer() {
|
|
1127
|
+
if (this.state.containerStack.length === 0) {
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
return this.state.containerStack[this.state.containerStack.length - 1];
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Set a nested value in an object
|
|
1134
|
+
*/
|
|
1135
|
+
setNestedValue(obj, path, value) {
|
|
1136
|
+
if (path.length === 0) {
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
let current = obj;
|
|
1140
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
1141
|
+
const key = path[i];
|
|
1142
|
+
if (typeof current !== "object" || current === null) {
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
const currentObj = current;
|
|
1146
|
+
if (!(key in currentObj)) {
|
|
1147
|
+
const nextKey = path[i + 1];
|
|
1148
|
+
currentObj[key] = /^\d+$/.test(nextKey) ? [] : {};
|
|
1149
|
+
}
|
|
1150
|
+
current = currentObj[key];
|
|
1151
|
+
}
|
|
1152
|
+
const lastKey = path[path.length - 1];
|
|
1153
|
+
if (typeof current === "object" && current !== null) {
|
|
1154
|
+
current[lastKey] = value;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Get a nested value from an object
|
|
1159
|
+
*/
|
|
1160
|
+
getNestedValue(obj, path) {
|
|
1161
|
+
if (path.length === 0) {
|
|
1162
|
+
return obj;
|
|
1163
|
+
}
|
|
1164
|
+
let current = obj;
|
|
1165
|
+
for (const key of path) {
|
|
1166
|
+
if (typeof current !== "object" || current === null) {
|
|
1167
|
+
return void 0;
|
|
1168
|
+
}
|
|
1169
|
+
current = current[key];
|
|
1170
|
+
}
|
|
1171
|
+
return current;
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Finalize parsing - try to complete any partial values
|
|
1175
|
+
*/
|
|
1176
|
+
finalize() {
|
|
1177
|
+
if (this.state.buffer.trim()) {
|
|
1178
|
+
try {
|
|
1179
|
+
const parsed = JSON.parse(this.state.buffer);
|
|
1180
|
+
this.result = parsed;
|
|
1181
|
+
} catch {
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
// src/streaming/StreamingExtractor.ts
|
|
1190
|
+
var StreamingExtractor_exports = {};
|
|
1191
|
+
__export(StreamingExtractor_exports, {
|
|
1192
|
+
createStreamingResult: () => createStreamingResult,
|
|
1193
|
+
getPartialState: () => getPartialState
|
|
1194
|
+
});
|
|
1195
|
+
function createStreamingResult(_client, provider, options, streamingOptions) {
|
|
1196
|
+
const emitter = new EventEmitter();
|
|
1197
|
+
const parser = new IncrementalJsonParser();
|
|
1198
|
+
let isComplete = false;
|
|
1199
|
+
const current = {};
|
|
1200
|
+
let finalResult;
|
|
1201
|
+
let error;
|
|
1202
|
+
let cancelled = false;
|
|
1203
|
+
const metadata = {
|
|
1204
|
+
totalChunks: 0,
|
|
1205
|
+
totalChars: 0,
|
|
1206
|
+
startTime: Date.now()
|
|
1207
|
+
};
|
|
1208
|
+
const streamPromise = executeStreamingExtraction();
|
|
1209
|
+
async function executeStreamingExtraction() {
|
|
1210
|
+
try {
|
|
1211
|
+
const jsonSchema = zodToJsonSchema(options.response_format);
|
|
1212
|
+
const messages = prepareMessages(
|
|
1213
|
+
options.messages,
|
|
1214
|
+
options.response_format
|
|
1215
|
+
);
|
|
1216
|
+
const stream = provider.createStreamingCompletion({
|
|
1217
|
+
model: options.model,
|
|
1218
|
+
messages,
|
|
1219
|
+
mode: "json",
|
|
1220
|
+
jsonSchema,
|
|
1221
|
+
stream: true
|
|
1222
|
+
});
|
|
1223
|
+
for await (const chunk of stream) {
|
|
1224
|
+
if (cancelled) {
|
|
1225
|
+
break;
|
|
1226
|
+
}
|
|
1227
|
+
metadata.totalChunks++;
|
|
1228
|
+
metadata.totalChars += chunk.content.length;
|
|
1229
|
+
const updates = parser.feed(chunk.content);
|
|
1230
|
+
for (const update of updates) {
|
|
1231
|
+
const fieldUpdate = {
|
|
1232
|
+
path: update.path.join("."),
|
|
1233
|
+
value: update.value,
|
|
1234
|
+
complete: update.complete,
|
|
1235
|
+
timestamp: Date.now()
|
|
1236
|
+
};
|
|
1237
|
+
setNestedValue(current, update.path, update.value);
|
|
1238
|
+
if (streamingOptions?.onFieldComplete && update.complete) {
|
|
1239
|
+
streamingOptions.onFieldComplete(fieldUpdate.path, update.value);
|
|
1240
|
+
}
|
|
1241
|
+
emitter.emit("field", {
|
|
1242
|
+
path: fieldUpdate.path,
|
|
1243
|
+
value: update.value
|
|
1244
|
+
});
|
|
1245
|
+
if (shouldEmitPartial(current, streamingOptions)) {
|
|
1246
|
+
if (streamingOptions?.onPartial) {
|
|
1247
|
+
streamingOptions.onPartial(current, update.path.join("."));
|
|
1248
|
+
}
|
|
1249
|
+
emitter.emit("partial", {
|
|
1250
|
+
data: current,
|
|
1251
|
+
path: update.path.join(".")
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
if (chunk.isFinal) {
|
|
1256
|
+
metadata.endTime = Date.now();
|
|
1257
|
+
metadata.usage = chunk.usage;
|
|
1258
|
+
break;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
const parsedResult = parser.getResult();
|
|
1262
|
+
const validation = options.response_format.safeParse(parsedResult);
|
|
1263
|
+
if (validation.success) {
|
|
1264
|
+
finalResult = validation.data;
|
|
1265
|
+
isComplete = true;
|
|
1266
|
+
emitter.emit("complete", { data: finalResult });
|
|
1267
|
+
} else {
|
|
1268
|
+
error = new Error(
|
|
1269
|
+
`Validation failed: ${validation.error.issues.map((i) => i.message).join(", ")}`
|
|
1270
|
+
);
|
|
1271
|
+
emitter.emit("error", { error });
|
|
1272
|
+
}
|
|
1273
|
+
} catch (err) {
|
|
1274
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
1275
|
+
emitter.emit("error", { error });
|
|
1276
|
+
if (streamingOptions?.onError) {
|
|
1277
|
+
streamingOptions.onError(error);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
function prepareMessages(messages, schema) {
|
|
1282
|
+
const prompt = schemaToPrompt(schema, {
|
|
1283
|
+
format: "json-schema",
|
|
1284
|
+
includeConstraints: true
|
|
1285
|
+
});
|
|
1286
|
+
const systemIndex = messages.findIndex((m) => m.role === "system");
|
|
1287
|
+
if (systemIndex >= 0) {
|
|
1288
|
+
const newMessages = [...messages];
|
|
1289
|
+
newMessages[systemIndex] = {
|
|
1290
|
+
...newMessages[systemIndex],
|
|
1291
|
+
content: `${newMessages[systemIndex].content}
|
|
1292
|
+
|
|
1293
|
+
Respond with valid JSON matching this schema:
|
|
1294
|
+
${prompt.text}`
|
|
1295
|
+
};
|
|
1296
|
+
return newMessages;
|
|
1297
|
+
}
|
|
1298
|
+
return [
|
|
1299
|
+
{
|
|
1300
|
+
role: "system",
|
|
1301
|
+
content: `Respond with valid JSON matching this schema:
|
|
1302
|
+
${prompt.text}`
|
|
1303
|
+
},
|
|
1304
|
+
...messages
|
|
1305
|
+
];
|
|
1306
|
+
}
|
|
1307
|
+
function shouldEmitPartial(partial, options2) {
|
|
1308
|
+
if (!options2?.yieldPartials) {
|
|
1309
|
+
return false;
|
|
1310
|
+
}
|
|
1311
|
+
const fieldCount = countFields(partial);
|
|
1312
|
+
const minFields = options2.minFieldsBeforeYield ?? 1;
|
|
1313
|
+
return fieldCount >= minFields;
|
|
1314
|
+
}
|
|
1315
|
+
function countFields(obj, count = 0) {
|
|
1316
|
+
if (typeof obj !== "object" || obj === null) {
|
|
1317
|
+
return count + 1;
|
|
1318
|
+
}
|
|
1319
|
+
if (Array.isArray(obj)) {
|
|
1320
|
+
return obj.reduce((c, item) => countFields(item, c), count);
|
|
1321
|
+
}
|
|
1322
|
+
return Object.values(obj).reduce(
|
|
1323
|
+
(c, value) => countFields(value, c),
|
|
1324
|
+
count
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
function setNestedValue(obj, path, value) {
|
|
1328
|
+
let current2 = obj;
|
|
1329
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
1330
|
+
const key = path[i];
|
|
1331
|
+
if (!(key in current2)) {
|
|
1332
|
+
const nextKey = path[i + 1];
|
|
1333
|
+
current2[key] = /^\d+$/.test(nextKey) ? [] : {};
|
|
1334
|
+
}
|
|
1335
|
+
current2 = current2[key];
|
|
1336
|
+
}
|
|
1337
|
+
const lastKey = path[path.length - 1];
|
|
1338
|
+
current2[lastKey] = value;
|
|
1339
|
+
}
|
|
1340
|
+
return {
|
|
1341
|
+
async *partials() {
|
|
1342
|
+
const partialQueue = [];
|
|
1343
|
+
let resolveNext = null;
|
|
1344
|
+
let done = false;
|
|
1345
|
+
const onPartial = ({ data }) => {
|
|
1346
|
+
if (resolveNext) {
|
|
1347
|
+
resolveNext({ value: { ...data }, done: false });
|
|
1348
|
+
resolveNext = null;
|
|
1349
|
+
} else {
|
|
1350
|
+
partialQueue.push({ ...data });
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
const onComplete = () => {
|
|
1354
|
+
done = true;
|
|
1355
|
+
if (resolveNext) {
|
|
1356
|
+
resolveNext({
|
|
1357
|
+
value: void 0,
|
|
1358
|
+
done: true
|
|
1359
|
+
});
|
|
1360
|
+
resolveNext = null;
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
const onError = () => {
|
|
1364
|
+
done = true;
|
|
1365
|
+
if (resolveNext) {
|
|
1366
|
+
resolveNext({
|
|
1367
|
+
value: void 0,
|
|
1368
|
+
done: true
|
|
1369
|
+
});
|
|
1370
|
+
resolveNext = null;
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
emitter.on("partial", onPartial);
|
|
1374
|
+
emitter.on("complete", onComplete);
|
|
1375
|
+
emitter.on("error", onError);
|
|
1376
|
+
try {
|
|
1377
|
+
while (!done || partialQueue.length > 0) {
|
|
1378
|
+
if (partialQueue.length > 0) {
|
|
1379
|
+
yield partialQueue.shift();
|
|
1380
|
+
} else if (!done) {
|
|
1381
|
+
const result = await new Promise((resolve) => {
|
|
1382
|
+
resolveNext = resolve;
|
|
1383
|
+
});
|
|
1384
|
+
if (!result.done) {
|
|
1385
|
+
yield result.value;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
} finally {
|
|
1390
|
+
emitter.off("partial", onPartial);
|
|
1391
|
+
emitter.off("complete", onComplete);
|
|
1392
|
+
emitter.off("error", onError);
|
|
1393
|
+
}
|
|
1394
|
+
},
|
|
1395
|
+
async *fields() {
|
|
1396
|
+
const fieldQueue = [];
|
|
1397
|
+
let resolveNext = null;
|
|
1398
|
+
let done = false;
|
|
1399
|
+
const onField = ({ path, value }) => {
|
|
1400
|
+
const update = {
|
|
1401
|
+
path,
|
|
1402
|
+
value,
|
|
1403
|
+
complete: true,
|
|
1404
|
+
timestamp: Date.now()
|
|
1405
|
+
};
|
|
1406
|
+
if (resolveNext) {
|
|
1407
|
+
resolveNext({ value: update, done: false });
|
|
1408
|
+
resolveNext = null;
|
|
1409
|
+
} else {
|
|
1410
|
+
fieldQueue.push(update);
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
const onComplete = () => {
|
|
1414
|
+
done = true;
|
|
1415
|
+
if (resolveNext) {
|
|
1416
|
+
resolveNext({
|
|
1417
|
+
value: void 0,
|
|
1418
|
+
done: true
|
|
1419
|
+
});
|
|
1420
|
+
resolveNext = null;
|
|
1421
|
+
}
|
|
1422
|
+
};
|
|
1423
|
+
const onError = () => {
|
|
1424
|
+
done = true;
|
|
1425
|
+
if (resolveNext) {
|
|
1426
|
+
resolveNext({
|
|
1427
|
+
value: void 0,
|
|
1428
|
+
done: true
|
|
1429
|
+
});
|
|
1430
|
+
resolveNext = null;
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
emitter.on("field", onField);
|
|
1434
|
+
emitter.on("complete", onComplete);
|
|
1435
|
+
emitter.on("error", onError);
|
|
1436
|
+
try {
|
|
1437
|
+
while (!done || fieldQueue.length > 0) {
|
|
1438
|
+
if (fieldQueue.length > 0) {
|
|
1439
|
+
yield fieldQueue.shift();
|
|
1440
|
+
} else if (!done) {
|
|
1441
|
+
const result = await new Promise(
|
|
1442
|
+
(resolve) => {
|
|
1443
|
+
resolveNext = resolve;
|
|
1444
|
+
}
|
|
1445
|
+
);
|
|
1446
|
+
if (!result.done) {
|
|
1447
|
+
yield result.value;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
} finally {
|
|
1452
|
+
emitter.off("field", onField);
|
|
1453
|
+
emitter.off("complete", onComplete);
|
|
1454
|
+
emitter.off("error", onError);
|
|
1455
|
+
}
|
|
1456
|
+
},
|
|
1457
|
+
async final() {
|
|
1458
|
+
await streamPromise;
|
|
1459
|
+
if (error) {
|
|
1460
|
+
throw error;
|
|
1461
|
+
}
|
|
1462
|
+
if (!finalResult) {
|
|
1463
|
+
throw new Error("Stream completed without valid result");
|
|
1464
|
+
}
|
|
1465
|
+
return finalResult;
|
|
1466
|
+
},
|
|
1467
|
+
onPartial(callback) {
|
|
1468
|
+
emitter.on("partial", ({ data, path }) => callback(data, path));
|
|
1469
|
+
},
|
|
1470
|
+
onField(callback) {
|
|
1471
|
+
emitter.on("field", ({ path, value }) => callback(path, value));
|
|
1472
|
+
},
|
|
1473
|
+
onComplete(callback) {
|
|
1474
|
+
emitter.on("complete", ({ data }) => callback(data));
|
|
1475
|
+
},
|
|
1476
|
+
onError(callback) {
|
|
1477
|
+
emitter.on("error", ({ error: error2 }) => callback(error2));
|
|
1478
|
+
},
|
|
1479
|
+
cancel() {
|
|
1480
|
+
cancelled = true;
|
|
1481
|
+
},
|
|
1482
|
+
get isComplete() {
|
|
1483
|
+
return isComplete;
|
|
1484
|
+
},
|
|
1485
|
+
get current() {
|
|
1486
|
+
return { ...current };
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
function getPartialState(schema, partial) {
|
|
1491
|
+
const fieldPaths = getAllFieldPaths(partial);
|
|
1492
|
+
const schemaFieldPaths = getSchemaFieldPaths(schema);
|
|
1493
|
+
const completedFields = fieldPaths.filter((p) => {
|
|
1494
|
+
const value = getNestedValue2(partial, p);
|
|
1495
|
+
return value !== void 0;
|
|
1496
|
+
});
|
|
1497
|
+
const inProgressFields = schemaFieldPaths.filter(
|
|
1498
|
+
(p) => !completedFields.includes(p)
|
|
1499
|
+
);
|
|
1500
|
+
const validation = validatePartial(schema, partial);
|
|
1501
|
+
return {
|
|
1502
|
+
data: partial,
|
|
1503
|
+
completedFields,
|
|
1504
|
+
inProgressFields,
|
|
1505
|
+
completionPercent: completedFields.length / schemaFieldPaths.length * 100,
|
|
1506
|
+
isValid: validation.success
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
function getAllFieldPaths(obj, prefix = "", paths = []) {
|
|
1510
|
+
if (typeof obj !== "object" || obj === null) {
|
|
1511
|
+
if (prefix) {
|
|
1512
|
+
paths.push(prefix);
|
|
1513
|
+
}
|
|
1514
|
+
return paths;
|
|
1515
|
+
}
|
|
1516
|
+
if (Array.isArray(obj)) {
|
|
1517
|
+
obj.forEach((item, index) => {
|
|
1518
|
+
getAllFieldPaths(item, `${prefix}[${index}]`, paths);
|
|
1519
|
+
});
|
|
1520
|
+
return paths;
|
|
1521
|
+
}
|
|
1522
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1523
|
+
const newPrefix = prefix ? `${prefix}.${key}` : key;
|
|
1524
|
+
getAllFieldPaths(value, newPrefix, paths);
|
|
1525
|
+
}
|
|
1526
|
+
return paths;
|
|
1527
|
+
}
|
|
1528
|
+
function getSchemaFieldPaths(schema, prefix = "") {
|
|
1529
|
+
const paths = [];
|
|
1530
|
+
if (schema instanceof z.ZodObject) {
|
|
1531
|
+
const shape = schema.shape;
|
|
1532
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
1533
|
+
const newPrefix = prefix ? `${prefix}.${key}` : key;
|
|
1534
|
+
paths.push(...getSchemaFieldPaths(fieldSchema, newPrefix));
|
|
1535
|
+
}
|
|
1536
|
+
} else if (schema instanceof z.ZodArray) {
|
|
1537
|
+
if (prefix) {
|
|
1538
|
+
paths.push(prefix);
|
|
1539
|
+
}
|
|
1540
|
+
} else {
|
|
1541
|
+
if (prefix) {
|
|
1542
|
+
paths.push(prefix);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
return paths.length > 0 ? paths : [prefix].filter(Boolean);
|
|
1546
|
+
}
|
|
1547
|
+
function getNestedValue2(obj, path) {
|
|
1548
|
+
const parts = path.replace(/\[(\d+)\]/g, ".$1").split(".");
|
|
1549
|
+
let current = obj;
|
|
1550
|
+
for (const part of parts) {
|
|
1551
|
+
if (typeof current !== "object" || current === null) {
|
|
1552
|
+
return void 0;
|
|
1553
|
+
}
|
|
1554
|
+
current = current[part];
|
|
1555
|
+
}
|
|
1556
|
+
return current;
|
|
1557
|
+
}
|
|
1558
|
+
var init_StreamingExtractor = __esm({
|
|
1559
|
+
"src/streaming/StreamingExtractor.ts"() {
|
|
1560
|
+
init_SchemaToPrompt();
|
|
1561
|
+
init_SchemaValidator();
|
|
1562
|
+
init_IncrementalJsonParser();
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
|
|
1566
|
+
// src/core/StructuredClient.ts
|
|
1567
|
+
init_SchemaToPrompt();
|
|
1568
|
+
init_SchemaValidator();
|
|
1569
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
1570
|
+
maxAttempts: 3,
|
|
1571
|
+
backoffMultiplier: 1.5,
|
|
1572
|
+
initialDelay: 1e3,
|
|
1573
|
+
maxDelay: 1e4,
|
|
1574
|
+
retryOn: ["validation_error", "parse_error"]
|
|
1575
|
+
};
|
|
1576
|
+
var StructuredClient = class extends EventEmitter {
|
|
1577
|
+
provider;
|
|
1578
|
+
config;
|
|
1579
|
+
constructor(provider, config = {}) {
|
|
1580
|
+
super();
|
|
1581
|
+
this.provider = provider;
|
|
1582
|
+
this.config = {
|
|
1583
|
+
defaultMode: config.defaultMode ?? "json",
|
|
1584
|
+
defaultRetry: { ...DEFAULT_RETRY_CONFIG, ...config.defaultRetry },
|
|
1585
|
+
enableFixHints: config.enableFixHints ?? true,
|
|
1586
|
+
validatePartials: config.validatePartials ?? false,
|
|
1587
|
+
...config
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Extract structured data from an LLM response
|
|
1592
|
+
*/
|
|
1593
|
+
async extract(options) {
|
|
1594
|
+
const requestId = this.generateRequestId();
|
|
1595
|
+
const startTime = Date.now();
|
|
1596
|
+
const attempts = [];
|
|
1597
|
+
const mode = this.resolveMode(options.mode);
|
|
1598
|
+
const retryConfig = {
|
|
1599
|
+
...DEFAULT_RETRY_CONFIG,
|
|
1600
|
+
...this.config.defaultRetry,
|
|
1601
|
+
...options.retry
|
|
1602
|
+
};
|
|
1603
|
+
this.emit("extraction:start", { requestId, mode });
|
|
1604
|
+
let lastError;
|
|
1605
|
+
let currentMode = mode;
|
|
1606
|
+
const totalTokens = {
|
|
1607
|
+
promptTokens: 0,
|
|
1608
|
+
completionTokens: 0,
|
|
1609
|
+
totalTokens: 0
|
|
1610
|
+
};
|
|
1611
|
+
for (let attempt = 1; attempt <= retryConfig.maxAttempts; attempt++) {
|
|
1612
|
+
this.emit("extraction:attempt", {
|
|
1613
|
+
requestId,
|
|
1614
|
+
attempt,
|
|
1615
|
+
mode: currentMode
|
|
1616
|
+
});
|
|
1617
|
+
try {
|
|
1618
|
+
const result = await this.executeExtraction(
|
|
1619
|
+
options,
|
|
1620
|
+
currentMode,
|
|
1621
|
+
attempts,
|
|
1622
|
+
attempt
|
|
1623
|
+
);
|
|
1624
|
+
if (result.usage) {
|
|
1625
|
+
totalTokens.promptTokens += result.usage.promptTokens;
|
|
1626
|
+
totalTokens.completionTokens += result.usage.completionTokens;
|
|
1627
|
+
totalTokens.totalTokens += result.usage.totalTokens;
|
|
1628
|
+
}
|
|
1629
|
+
const validation = validateSchema(
|
|
1630
|
+
options.response_format,
|
|
1631
|
+
result.content
|
|
1632
|
+
);
|
|
1633
|
+
if (validation.success) {
|
|
1634
|
+
const attemptRecord = {
|
|
1635
|
+
attempt,
|
|
1636
|
+
mode: currentMode,
|
|
1637
|
+
success: true,
|
|
1638
|
+
duration: Date.now() - startTime,
|
|
1639
|
+
rawResponse: result.rawResponse
|
|
1640
|
+
};
|
|
1641
|
+
attempts.push(attemptRecord);
|
|
1642
|
+
this.emit("extraction:success", {
|
|
1643
|
+
requestId,
|
|
1644
|
+
data: validation.data,
|
|
1645
|
+
attempts: attempt
|
|
1646
|
+
});
|
|
1647
|
+
return {
|
|
1648
|
+
success: true,
|
|
1649
|
+
data: validation.data,
|
|
1650
|
+
raw: result.rawResponse,
|
|
1651
|
+
metadata: this.createMetadata(
|
|
1652
|
+
startTime,
|
|
1653
|
+
attempts,
|
|
1654
|
+
totalTokens,
|
|
1655
|
+
options.model
|
|
1656
|
+
)
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
const validationErrors = validation.errors ?? [];
|
|
1660
|
+
this.emit("validation:failed", { requestId, errors: validationErrors });
|
|
1661
|
+
const hints = this.config.enableFixHints ? getValidationHints(options.response_format, result.content) : [];
|
|
1662
|
+
attempts.push({
|
|
1663
|
+
attempt,
|
|
1664
|
+
mode: currentMode,
|
|
1665
|
+
success: false,
|
|
1666
|
+
duration: Date.now() - startTime,
|
|
1667
|
+
rawResponse: result.rawResponse,
|
|
1668
|
+
error: "Validation failed",
|
|
1669
|
+
validationErrors
|
|
1670
|
+
});
|
|
1671
|
+
lastError = this.createStructuredError(
|
|
1672
|
+
"Validation failed",
|
|
1673
|
+
"VALIDATION_ERROR",
|
|
1674
|
+
attempts,
|
|
1675
|
+
validationErrors,
|
|
1676
|
+
result.content
|
|
1677
|
+
);
|
|
1678
|
+
if (attempt < retryConfig.maxAttempts && this.shouldRetry("validation_error", retryConfig, attempt)) {
|
|
1679
|
+
this.emit("extraction:retry", {
|
|
1680
|
+
requestId,
|
|
1681
|
+
attempt,
|
|
1682
|
+
reason: "validation_error",
|
|
1683
|
+
hints
|
|
1684
|
+
});
|
|
1685
|
+
if (hints.length > 0 && this.config.enableFixHints) {
|
|
1686
|
+
options = this.addFixHintsToMessages(
|
|
1687
|
+
options,
|
|
1688
|
+
hints,
|
|
1689
|
+
validationErrors
|
|
1690
|
+
);
|
|
1691
|
+
}
|
|
1692
|
+
await this.delay(this.calculateBackoff(attempt, retryConfig));
|
|
1693
|
+
if (this.shouldSwitchMode(currentMode, attempt, options.mode)) {
|
|
1694
|
+
const newMode = this.getNextMode(currentMode);
|
|
1695
|
+
this.emit("mode:switch", {
|
|
1696
|
+
requestId,
|
|
1697
|
+
from: currentMode,
|
|
1698
|
+
to: newMode
|
|
1699
|
+
});
|
|
1700
|
+
currentMode = newMode;
|
|
1701
|
+
}
|
|
1702
|
+
continue;
|
|
1703
|
+
}
|
|
1704
|
+
} catch (error) {
|
|
1705
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1706
|
+
this.emit("extraction:error", { requestId, error: err, attempt });
|
|
1707
|
+
const errorType = this.classifyError(err);
|
|
1708
|
+
attempts.push({
|
|
1709
|
+
attempt,
|
|
1710
|
+
mode: currentMode,
|
|
1711
|
+
success: false,
|
|
1712
|
+
duration: Date.now() - startTime,
|
|
1713
|
+
error: err.message
|
|
1714
|
+
});
|
|
1715
|
+
lastError = err;
|
|
1716
|
+
if (attempt < retryConfig.maxAttempts && this.shouldRetry(errorType, retryConfig, attempt)) {
|
|
1717
|
+
this.emit("extraction:retry", {
|
|
1718
|
+
requestId,
|
|
1719
|
+
attempt,
|
|
1720
|
+
reason: errorType,
|
|
1721
|
+
hints: []
|
|
1722
|
+
});
|
|
1723
|
+
await this.delay(this.calculateBackoff(attempt, retryConfig));
|
|
1724
|
+
continue;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
return {
|
|
1729
|
+
success: false,
|
|
1730
|
+
error: lastError ?? new Error("All extraction attempts failed"),
|
|
1731
|
+
raw: attempts[attempts.length - 1]?.rawResponse,
|
|
1732
|
+
metadata: this.createMetadata(
|
|
1733
|
+
startTime,
|
|
1734
|
+
attempts,
|
|
1735
|
+
totalTokens,
|
|
1736
|
+
options.model
|
|
1737
|
+
)
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Extract with streaming partial results
|
|
1742
|
+
*/
|
|
1743
|
+
async extractStream(options, streamingOptions) {
|
|
1744
|
+
const { createStreamingResult: createStreamingResult2 } = await Promise.resolve().then(() => (init_StreamingExtractor(), StreamingExtractor_exports));
|
|
1745
|
+
return createStreamingResult2(
|
|
1746
|
+
this,
|
|
1747
|
+
this.provider,
|
|
1748
|
+
options,
|
|
1749
|
+
streamingOptions
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Execute a single extraction attempt
|
|
1754
|
+
*/
|
|
1755
|
+
async executeExtraction(options, mode, _previousAttempts, _attempt) {
|
|
1756
|
+
const { model, messages } = options;
|
|
1757
|
+
const jsonSchema = zodToJsonSchema(options.response_format);
|
|
1758
|
+
let response;
|
|
1759
|
+
switch (mode) {
|
|
1760
|
+
case "json":
|
|
1761
|
+
response = await this.extractWithJsonMode(model, messages, jsonSchema);
|
|
1762
|
+
break;
|
|
1763
|
+
case "tool":
|
|
1764
|
+
response = await this.extractWithToolMode(
|
|
1765
|
+
model,
|
|
1766
|
+
messages,
|
|
1767
|
+
jsonSchema,
|
|
1768
|
+
options.response_format
|
|
1769
|
+
);
|
|
1770
|
+
break;
|
|
1771
|
+
case "prompt":
|
|
1772
|
+
response = await this.extractWithPromptMode(
|
|
1773
|
+
model,
|
|
1774
|
+
messages,
|
|
1775
|
+
options.response_format
|
|
1776
|
+
);
|
|
1777
|
+
break;
|
|
1778
|
+
case "hybrid":
|
|
1779
|
+
response = await this.extractWithJsonMode(model, messages, jsonSchema);
|
|
1780
|
+
break;
|
|
1781
|
+
default:
|
|
1782
|
+
throw new Error(`Unknown extraction mode: ${mode}`);
|
|
1783
|
+
}
|
|
1784
|
+
const content = this.parseResponse(response, mode);
|
|
1785
|
+
return {
|
|
1786
|
+
content,
|
|
1787
|
+
rawResponse: response.content,
|
|
1788
|
+
usage: response.usage
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Extract using JSON mode
|
|
1793
|
+
*/
|
|
1794
|
+
async extractWithJsonMode(model, messages, jsonSchema) {
|
|
1795
|
+
return this.provider.createCompletion({
|
|
1796
|
+
model,
|
|
1797
|
+
messages,
|
|
1798
|
+
mode: "json",
|
|
1799
|
+
jsonSchema
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Extract using tool/function calling mode
|
|
1804
|
+
*/
|
|
1805
|
+
async extractWithToolMode(model, messages, jsonSchema, _schema) {
|
|
1806
|
+
const toolDefinition = {
|
|
1807
|
+
type: "function",
|
|
1808
|
+
function: {
|
|
1809
|
+
name: "extract_data",
|
|
1810
|
+
description: "Extract structured data from the response",
|
|
1811
|
+
parameters: jsonSchema,
|
|
1812
|
+
strict: true
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
return this.provider.createCompletion({
|
|
1816
|
+
model,
|
|
1817
|
+
messages,
|
|
1818
|
+
mode: "tool",
|
|
1819
|
+
toolDefinition
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* Extract using prompt engineering mode
|
|
1824
|
+
*/
|
|
1825
|
+
async extractWithPromptMode(model, messages, schema) {
|
|
1826
|
+
const prompt = schemaToPrompt(schema, {
|
|
1827
|
+
format: "natural",
|
|
1828
|
+
includeConstraints: true,
|
|
1829
|
+
includeExamples: true
|
|
1830
|
+
});
|
|
1831
|
+
const enhancedMessages = this.addSchemaPromptToMessages(
|
|
1832
|
+
messages,
|
|
1833
|
+
prompt.text
|
|
1834
|
+
);
|
|
1835
|
+
return this.provider.createCompletion({
|
|
1836
|
+
model,
|
|
1837
|
+
messages: enhancedMessages,
|
|
1838
|
+
mode: "prompt"
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Parse the response based on mode
|
|
1843
|
+
*/
|
|
1844
|
+
parseResponse(response, mode) {
|
|
1845
|
+
if (mode === "tool" && response.toolCalls && response.toolCalls.length > 0) {
|
|
1846
|
+
try {
|
|
1847
|
+
return JSON.parse(response.toolCalls[0].arguments);
|
|
1848
|
+
} catch {
|
|
1849
|
+
throw new Error("Failed to parse tool call arguments");
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
return this.extractJsonFromContent(response.content);
|
|
1853
|
+
}
|
|
1854
|
+
/**
|
|
1855
|
+
* Extract JSON from text content
|
|
1856
|
+
*/
|
|
1857
|
+
extractJsonFromContent(content) {
|
|
1858
|
+
try {
|
|
1859
|
+
return JSON.parse(content);
|
|
1860
|
+
} catch {
|
|
1861
|
+
}
|
|
1862
|
+
const codeBlockMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
1863
|
+
if (codeBlockMatch) {
|
|
1864
|
+
try {
|
|
1865
|
+
return JSON.parse(codeBlockMatch[1].trim());
|
|
1866
|
+
} catch {
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
const jsonMatch = content.match(/(\{[\s\S]*\}|\[[\s\S]*\])/);
|
|
1870
|
+
if (jsonMatch) {
|
|
1871
|
+
try {
|
|
1872
|
+
return JSON.parse(jsonMatch[1]);
|
|
1873
|
+
} catch {
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
throw new Error("No valid JSON found in response");
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Add schema prompt to messages
|
|
1880
|
+
*/
|
|
1881
|
+
addSchemaPromptToMessages(messages, schemaPrompt) {
|
|
1882
|
+
const systemIndex = messages.findIndex((m) => m.role === "system");
|
|
1883
|
+
if (systemIndex >= 0) {
|
|
1884
|
+
const newMessages = [...messages];
|
|
1885
|
+
newMessages[systemIndex] = {
|
|
1886
|
+
...newMessages[systemIndex],
|
|
1887
|
+
content: `${newMessages[systemIndex].content}
|
|
1888
|
+
|
|
1889
|
+
${schemaPrompt}
|
|
1890
|
+
|
|
1891
|
+
Respond with valid JSON only.`
|
|
1892
|
+
};
|
|
1893
|
+
return newMessages;
|
|
1894
|
+
}
|
|
1895
|
+
return [
|
|
1896
|
+
{
|
|
1897
|
+
role: "system",
|
|
1898
|
+
content: `${schemaPrompt}
|
|
1899
|
+
|
|
1900
|
+
Respond with valid JSON only.`
|
|
1901
|
+
},
|
|
1902
|
+
...messages
|
|
1903
|
+
];
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Add fix hints to messages for retry
|
|
1907
|
+
*/
|
|
1908
|
+
addFixHintsToMessages(options, hints, errors) {
|
|
1909
|
+
const hintsText = hints.join("\n");
|
|
1910
|
+
const errorsText = errors.map((e) => `- ${e.path.join(".")}: ${e.message}`).join("\n");
|
|
1911
|
+
const fixPrompt = `Your previous response had validation errors:
|
|
1912
|
+
${errorsText}
|
|
1913
|
+
|
|
1914
|
+
Please fix these issues:
|
|
1915
|
+
${hintsText}`;
|
|
1916
|
+
const newMessages = [
|
|
1917
|
+
...options.messages,
|
|
1918
|
+
{
|
|
1919
|
+
role: "user",
|
|
1920
|
+
content: fixPrompt
|
|
1921
|
+
}
|
|
1922
|
+
];
|
|
1923
|
+
return {
|
|
1924
|
+
...options,
|
|
1925
|
+
messages: newMessages
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
/**
|
|
1929
|
+
* Resolve the extraction mode
|
|
1930
|
+
*/
|
|
1931
|
+
resolveMode(modeConfig) {
|
|
1932
|
+
if (!modeConfig) {
|
|
1933
|
+
return this.config.defaultMode ?? "json";
|
|
1934
|
+
}
|
|
1935
|
+
if (typeof modeConfig === "string") {
|
|
1936
|
+
return modeConfig;
|
|
1937
|
+
}
|
|
1938
|
+
return modeConfig.mode;
|
|
1939
|
+
}
|
|
1940
|
+
/**
|
|
1941
|
+
* Check if we should retry
|
|
1942
|
+
*/
|
|
1943
|
+
shouldRetry(errorType, config, _attempt) {
|
|
1944
|
+
return (config.retryOn ?? []).includes(
|
|
1945
|
+
errorType
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Classify an error for retry logic
|
|
1950
|
+
*/
|
|
1951
|
+
classifyError(error) {
|
|
1952
|
+
const message = error.message.toLowerCase();
|
|
1953
|
+
if (message.includes("rate limit") || message.includes("429")) {
|
|
1954
|
+
return "rate_limit";
|
|
1955
|
+
}
|
|
1956
|
+
if (message.includes("timeout")) {
|
|
1957
|
+
return "timeout";
|
|
1958
|
+
}
|
|
1959
|
+
if (message.includes("json") || message.includes("parse")) {
|
|
1960
|
+
return "parse_error";
|
|
1961
|
+
}
|
|
1962
|
+
if (message.includes("validation")) {
|
|
1963
|
+
return "validation_error";
|
|
1964
|
+
}
|
|
1965
|
+
return "unknown_error";
|
|
1966
|
+
}
|
|
1967
|
+
/**
|
|
1968
|
+
* Check if mode should be switched (for hybrid mode)
|
|
1969
|
+
*/
|
|
1970
|
+
shouldSwitchMode(_currentMode, attempt, modeConfig) {
|
|
1971
|
+
if (!modeConfig || typeof modeConfig === "string") {
|
|
1972
|
+
return false;
|
|
1973
|
+
}
|
|
1974
|
+
if (!("mode" in modeConfig) || modeConfig.mode !== "hybrid") {
|
|
1975
|
+
return false;
|
|
1976
|
+
}
|
|
1977
|
+
const hybridConfig = modeConfig;
|
|
1978
|
+
return attempt > 0 && !!hybridConfig.fallbackOrder && hybridConfig.fallbackOrder.length > 0;
|
|
1979
|
+
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Get next mode in fallback order
|
|
1982
|
+
*/
|
|
1983
|
+
getNextMode(currentMode) {
|
|
1984
|
+
const fallbackOrder = ["json", "tool", "prompt"];
|
|
1985
|
+
const currentIndex = fallbackOrder.indexOf(currentMode);
|
|
1986
|
+
if (currentIndex < fallbackOrder.length - 1) {
|
|
1987
|
+
return fallbackOrder[currentIndex + 1];
|
|
1988
|
+
}
|
|
1989
|
+
return fallbackOrder[0];
|
|
1990
|
+
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Calculate backoff delay
|
|
1993
|
+
*/
|
|
1994
|
+
calculateBackoff(attempt, config) {
|
|
1995
|
+
const delay = (config.initialDelay ?? 1e3) * Math.pow(config.backoffMultiplier ?? 1.5, attempt - 1);
|
|
1996
|
+
return Math.min(delay, config.maxDelay ?? 1e4);
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Delay helper
|
|
2000
|
+
*/
|
|
2001
|
+
delay(ms) {
|
|
2002
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2003
|
+
}
|
|
2004
|
+
/**
|
|
2005
|
+
* Generate a request ID
|
|
2006
|
+
*/
|
|
2007
|
+
generateRequestId() {
|
|
2008
|
+
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Create extraction metadata
|
|
2012
|
+
*/
|
|
2013
|
+
createMetadata(startTime, attempts, usage, model) {
|
|
2014
|
+
return {
|
|
2015
|
+
totalAttempts: attempts.length,
|
|
2016
|
+
totalDuration: Date.now() - startTime,
|
|
2017
|
+
finalMode: attempts[attempts.length - 1]?.mode ?? "json",
|
|
2018
|
+
tokenUsage: usage,
|
|
2019
|
+
attempts,
|
|
2020
|
+
model
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* Create a structured error
|
|
2025
|
+
*/
|
|
2026
|
+
createStructuredError(message, code, attempts, validationErrors, partial) {
|
|
2027
|
+
const error = new Error(message);
|
|
2028
|
+
error.name = "StructuredError";
|
|
2029
|
+
error.code = code;
|
|
2030
|
+
error.attempts = attempts;
|
|
2031
|
+
error.validationErrors = validationErrors;
|
|
2032
|
+
error.partial = partial;
|
|
2033
|
+
return error;
|
|
2034
|
+
}
|
|
2035
|
+
/**
|
|
2036
|
+
* Get provider capabilities
|
|
2037
|
+
*/
|
|
2038
|
+
getProviderCapabilities(model) {
|
|
2039
|
+
return this.provider.getCapabilities(model);
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Check if a mode is supported
|
|
2043
|
+
*/
|
|
2044
|
+
supportsMode(mode, model) {
|
|
2045
|
+
const capabilities = this.provider.getCapabilities(model);
|
|
2046
|
+
switch (mode) {
|
|
2047
|
+
case "json":
|
|
2048
|
+
return capabilities.jsonMode;
|
|
2049
|
+
case "tool":
|
|
2050
|
+
return capabilities.toolCalling;
|
|
2051
|
+
case "prompt":
|
|
2052
|
+
return true;
|
|
2053
|
+
// Always supported
|
|
2054
|
+
case "hybrid":
|
|
2055
|
+
return capabilities.jsonMode || capabilities.toolCalling;
|
|
2056
|
+
default:
|
|
2057
|
+
return false;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
function createStructuredClient(provider, config) {
|
|
2062
|
+
return new StructuredClient(provider, config);
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
// src/schema/index.ts
|
|
2066
|
+
init_SchemaToPrompt();
|
|
2067
|
+
init_SchemaValidator();
|
|
2068
|
+
|
|
2069
|
+
// src/streaming/index.ts
|
|
2070
|
+
init_StreamingExtractor();
|
|
2071
|
+
init_IncrementalJsonParser();
|
|
2072
|
+
|
|
2073
|
+
// src/providers/OpenAIAdapter.ts
|
|
2074
|
+
var MODEL_CAPABILITIES = {
|
|
2075
|
+
"gpt-4o": {
|
|
2076
|
+
jsonMode: true,
|
|
2077
|
+
strictJsonMode: true,
|
|
2078
|
+
toolCalling: true,
|
|
2079
|
+
streaming: true,
|
|
2080
|
+
systemMessages: true,
|
|
2081
|
+
maxContextWindow: 128e3,
|
|
2082
|
+
maxOutputTokens: 16384
|
|
2083
|
+
},
|
|
2084
|
+
"gpt-4o-mini": {
|
|
2085
|
+
jsonMode: true,
|
|
2086
|
+
strictJsonMode: true,
|
|
2087
|
+
toolCalling: true,
|
|
2088
|
+
streaming: true,
|
|
2089
|
+
systemMessages: true,
|
|
2090
|
+
maxContextWindow: 128e3,
|
|
2091
|
+
maxOutputTokens: 16384
|
|
2092
|
+
},
|
|
2093
|
+
"gpt-4-turbo": {
|
|
2094
|
+
jsonMode: true,
|
|
2095
|
+
strictJsonMode: false,
|
|
2096
|
+
toolCalling: true,
|
|
2097
|
+
streaming: true,
|
|
2098
|
+
systemMessages: true,
|
|
2099
|
+
maxContextWindow: 128e3,
|
|
2100
|
+
maxOutputTokens: 4096
|
|
2101
|
+
},
|
|
2102
|
+
"gpt-4": {
|
|
2103
|
+
jsonMode: true,
|
|
2104
|
+
strictJsonMode: false,
|
|
2105
|
+
toolCalling: true,
|
|
2106
|
+
streaming: true,
|
|
2107
|
+
systemMessages: true,
|
|
2108
|
+
maxContextWindow: 8192,
|
|
2109
|
+
maxOutputTokens: 4096
|
|
2110
|
+
},
|
|
2111
|
+
"gpt-3.5-turbo": {
|
|
2112
|
+
jsonMode: true,
|
|
2113
|
+
strictJsonMode: false,
|
|
2114
|
+
toolCalling: true,
|
|
2115
|
+
streaming: true,
|
|
2116
|
+
systemMessages: true,
|
|
2117
|
+
maxContextWindow: 16385,
|
|
2118
|
+
maxOutputTokens: 4096
|
|
2119
|
+
},
|
|
2120
|
+
"o1-preview": {
|
|
2121
|
+
jsonMode: false,
|
|
2122
|
+
strictJsonMode: false,
|
|
2123
|
+
toolCalling: false,
|
|
2124
|
+
streaming: false,
|
|
2125
|
+
systemMessages: false,
|
|
2126
|
+
maxContextWindow: 128e3,
|
|
2127
|
+
maxOutputTokens: 32768
|
|
2128
|
+
},
|
|
2129
|
+
"o1-mini": {
|
|
2130
|
+
jsonMode: false,
|
|
2131
|
+
strictJsonMode: false,
|
|
2132
|
+
toolCalling: false,
|
|
2133
|
+
streaming: false,
|
|
2134
|
+
systemMessages: false,
|
|
2135
|
+
maxContextWindow: 128e3,
|
|
2136
|
+
maxOutputTokens: 65536
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
var OpenAIAdapter = class {
|
|
2140
|
+
name = "openai";
|
|
2141
|
+
client;
|
|
2142
|
+
constructor(client, _options = {}) {
|
|
2143
|
+
this.client = client;
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Get capabilities for a model
|
|
2147
|
+
*/
|
|
2148
|
+
getCapabilities(model) {
|
|
2149
|
+
const base = MODEL_CAPABILITIES[model] ?? MODEL_CAPABILITIES["gpt-4o"];
|
|
2150
|
+
return {
|
|
2151
|
+
jsonMode: base.jsonMode ?? true,
|
|
2152
|
+
strictJsonMode: base.strictJsonMode ?? false,
|
|
2153
|
+
toolCalling: base.toolCalling ?? true,
|
|
2154
|
+
streaming: base.streaming ?? true,
|
|
2155
|
+
systemMessages: base.systemMessages ?? true,
|
|
2156
|
+
maxContextWindow: base.maxContextWindow,
|
|
2157
|
+
maxOutputTokens: base.maxOutputTokens
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
/**
|
|
2161
|
+
* Check if JSON mode is available
|
|
2162
|
+
*/
|
|
2163
|
+
supportsJsonMode(model) {
|
|
2164
|
+
return this.getCapabilities(model).jsonMode;
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Check if tool calling is available
|
|
2168
|
+
*/
|
|
2169
|
+
supportsToolCalling(model) {
|
|
2170
|
+
return this.getCapabilities(model).toolCalling;
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Create a completion request
|
|
2174
|
+
*/
|
|
2175
|
+
async createCompletion(request) {
|
|
2176
|
+
const params = this.buildRequestParams(request);
|
|
2177
|
+
const response = await this.client.chat.completions.create(params);
|
|
2178
|
+
return this.parseResponse(response);
|
|
2179
|
+
}
|
|
2180
|
+
/**
|
|
2181
|
+
* Create a streaming completion request
|
|
2182
|
+
*/
|
|
2183
|
+
async *createStreamingCompletion(request) {
|
|
2184
|
+
const params = this.buildRequestParams(request);
|
|
2185
|
+
params.stream = true;
|
|
2186
|
+
params.stream_options = { include_usage: true };
|
|
2187
|
+
const stream = await this.client.chat.completions.create(params);
|
|
2188
|
+
const asyncStream = stream;
|
|
2189
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
2190
|
+
for await (const chunk of asyncStream) {
|
|
2191
|
+
const choice = chunk.choices[0];
|
|
2192
|
+
if (!choice) continue;
|
|
2193
|
+
const delta = choice.delta;
|
|
2194
|
+
if (delta.tool_calls) {
|
|
2195
|
+
for (const tc of delta.tool_calls) {
|
|
2196
|
+
const existing = toolCalls.get(tc.index) ?? { arguments: "" };
|
|
2197
|
+
if (tc.id) existing.id = tc.id;
|
|
2198
|
+
if (tc.function?.name) existing.name = tc.function.name;
|
|
2199
|
+
if (tc.function?.arguments)
|
|
2200
|
+
existing.arguments += tc.function.arguments;
|
|
2201
|
+
toolCalls.set(tc.index, existing);
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
const isFinal = choice.finish_reason !== null;
|
|
2205
|
+
yield {
|
|
2206
|
+
content: delta.content ?? "",
|
|
2207
|
+
isFinal,
|
|
2208
|
+
finishReason: choice.finish_reason ?? void 0,
|
|
2209
|
+
usage: chunk.usage ? {
|
|
2210
|
+
promptTokens: chunk.usage.prompt_tokens,
|
|
2211
|
+
completionTokens: chunk.usage.completion_tokens,
|
|
2212
|
+
totalTokens: chunk.usage.total_tokens
|
|
2213
|
+
} : void 0,
|
|
2214
|
+
toolCallDeltas: delta.tool_calls?.map((tc) => ({
|
|
2215
|
+
index: tc.index,
|
|
2216
|
+
id: tc.id,
|
|
2217
|
+
name: tc.function?.name,
|
|
2218
|
+
arguments: tc.function?.arguments
|
|
2219
|
+
}))
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
/**
|
|
2224
|
+
* Format messages for OpenAI
|
|
2225
|
+
*/
|
|
2226
|
+
formatMessages(messages) {
|
|
2227
|
+
return messages.map((msg) => ({
|
|
2228
|
+
role: msg.role,
|
|
2229
|
+
content: msg.content,
|
|
2230
|
+
name: msg.name
|
|
2231
|
+
}));
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
* Format JSON schema for OpenAI
|
|
2235
|
+
*/
|
|
2236
|
+
formatJsonSchema(schema) {
|
|
2237
|
+
return {
|
|
2238
|
+
type: "json_schema",
|
|
2239
|
+
json_schema: {
|
|
2240
|
+
name: "response",
|
|
2241
|
+
schema,
|
|
2242
|
+
strict: true
|
|
2243
|
+
}
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
/**
|
|
2247
|
+
* Format tool definition for OpenAI
|
|
2248
|
+
*/
|
|
2249
|
+
formatToolDefinition(tool) {
|
|
2250
|
+
return {
|
|
2251
|
+
type: "function",
|
|
2252
|
+
function: {
|
|
2253
|
+
name: tool.function.name,
|
|
2254
|
+
description: tool.function.description,
|
|
2255
|
+
parameters: tool.function.parameters,
|
|
2256
|
+
strict: tool.function.strict
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
/**
|
|
2261
|
+
* Build request parameters
|
|
2262
|
+
*/
|
|
2263
|
+
buildRequestParams(request) {
|
|
2264
|
+
const params = {
|
|
2265
|
+
model: request.model,
|
|
2266
|
+
messages: this.formatMessages(request.messages),
|
|
2267
|
+
temperature: request.temperature,
|
|
2268
|
+
max_tokens: request.maxTokens
|
|
2269
|
+
};
|
|
2270
|
+
switch (request.mode) {
|
|
2271
|
+
case "json":
|
|
2272
|
+
if (request.jsonSchema) {
|
|
2273
|
+
const caps = this.getCapabilities(request.model);
|
|
2274
|
+
if (caps.strictJsonMode) {
|
|
2275
|
+
params.response_format = {
|
|
2276
|
+
type: "json_schema",
|
|
2277
|
+
json_schema: {
|
|
2278
|
+
name: "response",
|
|
2279
|
+
schema: request.jsonSchema,
|
|
2280
|
+
strict: true
|
|
2281
|
+
}
|
|
2282
|
+
};
|
|
2283
|
+
} else {
|
|
2284
|
+
params.response_format = { type: "json_object" };
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
break;
|
|
2288
|
+
case "tool":
|
|
2289
|
+
if (request.toolDefinition) {
|
|
2290
|
+
params.tools = [this.formatToolDefinition(request.toolDefinition)];
|
|
2291
|
+
params.tool_choice = {
|
|
2292
|
+
type: "function",
|
|
2293
|
+
function: { name: request.toolDefinition.function.name }
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
break;
|
|
2297
|
+
}
|
|
2298
|
+
if (request.options) {
|
|
2299
|
+
Object.assign(params, request.options);
|
|
2300
|
+
}
|
|
2301
|
+
return params;
|
|
2302
|
+
}
|
|
2303
|
+
/**
|
|
2304
|
+
* Parse response
|
|
2305
|
+
*/
|
|
2306
|
+
parseResponse(response) {
|
|
2307
|
+
const choice = response.choices[0];
|
|
2308
|
+
const message = choice?.message;
|
|
2309
|
+
let toolCalls;
|
|
2310
|
+
if (message?.tool_calls && message.tool_calls.length > 0) {
|
|
2311
|
+
toolCalls = message.tool_calls.map((tc) => ({
|
|
2312
|
+
id: tc.id,
|
|
2313
|
+
name: tc.function.name,
|
|
2314
|
+
arguments: tc.function.arguments
|
|
2315
|
+
}));
|
|
2316
|
+
}
|
|
2317
|
+
return {
|
|
2318
|
+
content: message?.content ?? "",
|
|
2319
|
+
usage: response.usage ? {
|
|
2320
|
+
promptTokens: response.usage.prompt_tokens,
|
|
2321
|
+
completionTokens: response.usage.completion_tokens,
|
|
2322
|
+
totalTokens: response.usage.total_tokens
|
|
2323
|
+
} : void 0,
|
|
2324
|
+
finishReason: choice?.finish_reason,
|
|
2325
|
+
toolCalls,
|
|
2326
|
+
raw: response
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
};
|
|
2330
|
+
function createOpenAIAdapter(client, options) {
|
|
2331
|
+
return new OpenAIAdapter(client, options);
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
// src/providers/AnthropicAdapter.ts
|
|
2335
|
+
var MODEL_CAPABILITIES2 = {
|
|
2336
|
+
"claude-opus-4-20250514": {
|
|
2337
|
+
jsonMode: false,
|
|
2338
|
+
strictJsonMode: false,
|
|
2339
|
+
toolCalling: true,
|
|
2340
|
+
streaming: true,
|
|
2341
|
+
systemMessages: true,
|
|
2342
|
+
maxContextWindow: 2e5,
|
|
2343
|
+
maxOutputTokens: 32768
|
|
2344
|
+
},
|
|
2345
|
+
"claude-sonnet-4-20250514": {
|
|
2346
|
+
jsonMode: false,
|
|
2347
|
+
strictJsonMode: false,
|
|
2348
|
+
toolCalling: true,
|
|
2349
|
+
streaming: true,
|
|
2350
|
+
systemMessages: true,
|
|
2351
|
+
maxContextWindow: 2e5,
|
|
2352
|
+
maxOutputTokens: 64e3
|
|
2353
|
+
},
|
|
2354
|
+
"claude-3-5-sonnet-20241022": {
|
|
2355
|
+
jsonMode: false,
|
|
2356
|
+
strictJsonMode: false,
|
|
2357
|
+
toolCalling: true,
|
|
2358
|
+
streaming: true,
|
|
2359
|
+
systemMessages: true,
|
|
2360
|
+
maxContextWindow: 2e5,
|
|
2361
|
+
maxOutputTokens: 8192
|
|
2362
|
+
},
|
|
2363
|
+
"claude-3-5-haiku-20241022": {
|
|
2364
|
+
jsonMode: false,
|
|
2365
|
+
strictJsonMode: false,
|
|
2366
|
+
toolCalling: true,
|
|
2367
|
+
streaming: true,
|
|
2368
|
+
systemMessages: true,
|
|
2369
|
+
maxContextWindow: 2e5,
|
|
2370
|
+
maxOutputTokens: 8192
|
|
2371
|
+
},
|
|
2372
|
+
"claude-3-opus-20240229": {
|
|
2373
|
+
jsonMode: false,
|
|
2374
|
+
strictJsonMode: false,
|
|
2375
|
+
toolCalling: true,
|
|
2376
|
+
streaming: true,
|
|
2377
|
+
systemMessages: true,
|
|
2378
|
+
maxContextWindow: 2e5,
|
|
2379
|
+
maxOutputTokens: 4096
|
|
2380
|
+
},
|
|
2381
|
+
"claude-3-sonnet-20240229": {
|
|
2382
|
+
jsonMode: false,
|
|
2383
|
+
strictJsonMode: false,
|
|
2384
|
+
toolCalling: true,
|
|
2385
|
+
streaming: true,
|
|
2386
|
+
systemMessages: true,
|
|
2387
|
+
maxContextWindow: 2e5,
|
|
2388
|
+
maxOutputTokens: 4096
|
|
2389
|
+
},
|
|
2390
|
+
"claude-3-haiku-20240307": {
|
|
2391
|
+
jsonMode: false,
|
|
2392
|
+
strictJsonMode: false,
|
|
2393
|
+
toolCalling: true,
|
|
2394
|
+
streaming: true,
|
|
2395
|
+
systemMessages: true,
|
|
2396
|
+
maxContextWindow: 2e5,
|
|
2397
|
+
maxOutputTokens: 4096
|
|
2398
|
+
}
|
|
2399
|
+
};
|
|
2400
|
+
var AnthropicAdapter = class {
|
|
2401
|
+
name = "anthropic";
|
|
2402
|
+
client;
|
|
2403
|
+
options;
|
|
2404
|
+
constructor(client, options = {}) {
|
|
2405
|
+
this.client = client;
|
|
2406
|
+
this.options = options;
|
|
2407
|
+
}
|
|
2408
|
+
/**
|
|
2409
|
+
* Get capabilities for a model
|
|
2410
|
+
*/
|
|
2411
|
+
getCapabilities(model) {
|
|
2412
|
+
const modelKey = Object.keys(MODEL_CAPABILITIES2).find(
|
|
2413
|
+
(key) => model.includes(key) || key.includes(model)
|
|
2414
|
+
);
|
|
2415
|
+
const base = modelKey ? MODEL_CAPABILITIES2[modelKey] : MODEL_CAPABILITIES2["claude-3-5-sonnet-20241022"];
|
|
2416
|
+
return {
|
|
2417
|
+
jsonMode: base.jsonMode ?? false,
|
|
2418
|
+
strictJsonMode: base.strictJsonMode ?? false,
|
|
2419
|
+
toolCalling: base.toolCalling ?? true,
|
|
2420
|
+
streaming: base.streaming ?? true,
|
|
2421
|
+
systemMessages: base.systemMessages ?? true,
|
|
2422
|
+
maxContextWindow: base.maxContextWindow,
|
|
2423
|
+
maxOutputTokens: base.maxOutputTokens
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
/**
|
|
2427
|
+
* Check if JSON mode is available
|
|
2428
|
+
*/
|
|
2429
|
+
supportsJsonMode(_model) {
|
|
2430
|
+
return false;
|
|
2431
|
+
}
|
|
2432
|
+
/**
|
|
2433
|
+
* Check if tool calling is available
|
|
2434
|
+
*/
|
|
2435
|
+
supportsToolCalling(model) {
|
|
2436
|
+
return this.getCapabilities(model).toolCalling;
|
|
2437
|
+
}
|
|
2438
|
+
/**
|
|
2439
|
+
* Create a completion request
|
|
2440
|
+
*/
|
|
2441
|
+
async createCompletion(request) {
|
|
2442
|
+
const params = this.buildRequestParams(request);
|
|
2443
|
+
const response = await this.client.messages.create(params);
|
|
2444
|
+
return this.parseResponse(response);
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* Create a streaming completion request
|
|
2448
|
+
*/
|
|
2449
|
+
async *createStreamingCompletion(request) {
|
|
2450
|
+
const params = this.buildRequestParams(request);
|
|
2451
|
+
params.stream = true;
|
|
2452
|
+
const stream = await this.client.messages.create(params);
|
|
2453
|
+
const asyncStream = stream;
|
|
2454
|
+
let toolCallBuffer = null;
|
|
2455
|
+
let inputTokens = 0;
|
|
2456
|
+
let outputTokens = 0;
|
|
2457
|
+
for await (const event of asyncStream) {
|
|
2458
|
+
switch (event.type) {
|
|
2459
|
+
case "message_start":
|
|
2460
|
+
if (event.message?.usage) {
|
|
2461
|
+
inputTokens = event.message.usage.input_tokens;
|
|
2462
|
+
}
|
|
2463
|
+
break;
|
|
2464
|
+
case "content_block_start":
|
|
2465
|
+
if (event.content_block?.type === "tool_use") {
|
|
2466
|
+
toolCallBuffer = {
|
|
2467
|
+
id: event.content_block.id,
|
|
2468
|
+
name: event.content_block.name,
|
|
2469
|
+
arguments: ""
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
break;
|
|
2473
|
+
case "content_block_delta":
|
|
2474
|
+
if (event.delta?.type === "text_delta" && event.delta.text) {
|
|
2475
|
+
yield {
|
|
2476
|
+
content: event.delta.text,
|
|
2477
|
+
isFinal: false
|
|
2478
|
+
};
|
|
2479
|
+
} else if (event.delta?.type === "input_json_delta" && event.delta.partial_json) {
|
|
2480
|
+
if (toolCallBuffer) {
|
|
2481
|
+
toolCallBuffer.arguments += event.delta.partial_json;
|
|
2482
|
+
}
|
|
2483
|
+
yield {
|
|
2484
|
+
content: "",
|
|
2485
|
+
isFinal: false,
|
|
2486
|
+
toolCallDeltas: toolCallBuffer ? [
|
|
2487
|
+
{
|
|
2488
|
+
index: 0,
|
|
2489
|
+
id: toolCallBuffer.id,
|
|
2490
|
+
name: toolCallBuffer.name,
|
|
2491
|
+
arguments: event.delta.partial_json
|
|
2492
|
+
}
|
|
2493
|
+
] : void 0
|
|
2494
|
+
};
|
|
2495
|
+
}
|
|
2496
|
+
break;
|
|
2497
|
+
case "message_delta":
|
|
2498
|
+
if (event.usage) {
|
|
2499
|
+
outputTokens = event.usage.output_tokens;
|
|
2500
|
+
}
|
|
2501
|
+
break;
|
|
2502
|
+
case "message_stop":
|
|
2503
|
+
yield {
|
|
2504
|
+
content: "",
|
|
2505
|
+
isFinal: true,
|
|
2506
|
+
usage: {
|
|
2507
|
+
promptTokens: inputTokens,
|
|
2508
|
+
completionTokens: outputTokens,
|
|
2509
|
+
totalTokens: inputTokens + outputTokens
|
|
2510
|
+
}
|
|
2511
|
+
};
|
|
2512
|
+
break;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
/**
|
|
2517
|
+
* Format messages for Anthropic
|
|
2518
|
+
*/
|
|
2519
|
+
formatMessages(messages) {
|
|
2520
|
+
const systemMessage = messages.find((m) => m.role === "system");
|
|
2521
|
+
const otherMessages = messages.filter((m) => m.role !== "system");
|
|
2522
|
+
return {
|
|
2523
|
+
system: systemMessage?.content,
|
|
2524
|
+
messages: otherMessages.map((msg) => ({
|
|
2525
|
+
role: msg.role === "assistant" ? "assistant" : "user",
|
|
2526
|
+
content: msg.content
|
|
2527
|
+
}))
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* Format JSON schema for Anthropic (via tool calling)
|
|
2532
|
+
*/
|
|
2533
|
+
formatJsonSchema(schema) {
|
|
2534
|
+
return {
|
|
2535
|
+
name: "extract_structured_data",
|
|
2536
|
+
description: "Extract structured data from the conversation",
|
|
2537
|
+
input_schema: schema
|
|
2538
|
+
};
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* Format tool definition for Anthropic
|
|
2542
|
+
*/
|
|
2543
|
+
formatToolDefinition(tool) {
|
|
2544
|
+
return {
|
|
2545
|
+
name: tool.function.name,
|
|
2546
|
+
description: tool.function.description,
|
|
2547
|
+
input_schema: tool.function.parameters
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
/**
|
|
2551
|
+
* Build request parameters
|
|
2552
|
+
*/
|
|
2553
|
+
buildRequestParams(request) {
|
|
2554
|
+
const formatted = this.formatMessages(request.messages);
|
|
2555
|
+
const params = {
|
|
2556
|
+
model: request.model,
|
|
2557
|
+
messages: formatted.messages,
|
|
2558
|
+
max_tokens: request.maxTokens ?? 4096
|
|
2559
|
+
};
|
|
2560
|
+
if (formatted.system) {
|
|
2561
|
+
params.system = formatted.system;
|
|
2562
|
+
}
|
|
2563
|
+
if (request.temperature !== void 0) {
|
|
2564
|
+
params.temperature = request.temperature;
|
|
2565
|
+
}
|
|
2566
|
+
switch (request.mode) {
|
|
2567
|
+
case "json":
|
|
2568
|
+
if (request.jsonSchema) {
|
|
2569
|
+
params.tools = [this.formatJsonSchema(request.jsonSchema)];
|
|
2570
|
+
params.tool_choice = {
|
|
2571
|
+
type: "tool",
|
|
2572
|
+
name: "extract_structured_data"
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
break;
|
|
2576
|
+
case "tool":
|
|
2577
|
+
if (request.toolDefinition) {
|
|
2578
|
+
params.tools = [this.formatToolDefinition(request.toolDefinition)];
|
|
2579
|
+
params.tool_choice = {
|
|
2580
|
+
type: "tool",
|
|
2581
|
+
name: request.toolDefinition.function.name
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
2584
|
+
break;
|
|
2585
|
+
case "prompt":
|
|
2586
|
+
if (params.system) {
|
|
2587
|
+
params.system = `${params.system}
|
|
2588
|
+
|
|
2589
|
+
Respond with valid JSON only.`;
|
|
2590
|
+
} else {
|
|
2591
|
+
params.system = "Respond with valid JSON only.";
|
|
2592
|
+
}
|
|
2593
|
+
break;
|
|
2594
|
+
}
|
|
2595
|
+
if (this.options.metadata) {
|
|
2596
|
+
params.metadata = this.options.metadata;
|
|
2597
|
+
}
|
|
2598
|
+
if (request.options) {
|
|
2599
|
+
Object.assign(params, request.options);
|
|
2600
|
+
}
|
|
2601
|
+
return params;
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Parse response
|
|
2605
|
+
*/
|
|
2606
|
+
parseResponse(response) {
|
|
2607
|
+
let content = "";
|
|
2608
|
+
let toolCalls;
|
|
2609
|
+
for (const block of response.content) {
|
|
2610
|
+
if (block.type === "text" && block.text) {
|
|
2611
|
+
content += block.text;
|
|
2612
|
+
} else if (block.type === "tool_use") {
|
|
2613
|
+
if (!toolCalls) {
|
|
2614
|
+
toolCalls = [];
|
|
2615
|
+
}
|
|
2616
|
+
toolCalls.push({
|
|
2617
|
+
id: block.id ?? "",
|
|
2618
|
+
name: block.name ?? "",
|
|
2619
|
+
arguments: JSON.stringify(block.input)
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
return {
|
|
2624
|
+
content,
|
|
2625
|
+
usage: {
|
|
2626
|
+
promptTokens: response.usage.input_tokens,
|
|
2627
|
+
completionTokens: response.usage.output_tokens,
|
|
2628
|
+
totalTokens: response.usage.input_tokens + response.usage.output_tokens
|
|
2629
|
+
},
|
|
2630
|
+
finishReason: response.stop_reason ?? void 0,
|
|
2631
|
+
toolCalls,
|
|
2632
|
+
raw: response
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
};
|
|
2636
|
+
function createAnthropicAdapter(client, options) {
|
|
2637
|
+
return new AnthropicAdapter(client, options);
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
// src/providers/GoogleAdapter.ts
|
|
2641
|
+
var MODEL_CAPABILITIES3 = {
|
|
2642
|
+
"gemini-2.0-flash": {
|
|
2643
|
+
jsonMode: true,
|
|
2644
|
+
strictJsonMode: true,
|
|
2645
|
+
toolCalling: true,
|
|
2646
|
+
streaming: true,
|
|
2647
|
+
systemMessages: true,
|
|
2648
|
+
maxContextWindow: 1e6,
|
|
2649
|
+
maxOutputTokens: 8192
|
|
2650
|
+
},
|
|
2651
|
+
"gemini-2.0-flash-lite": {
|
|
2652
|
+
jsonMode: true,
|
|
2653
|
+
strictJsonMode: true,
|
|
2654
|
+
toolCalling: true,
|
|
2655
|
+
streaming: true,
|
|
2656
|
+
systemMessages: true,
|
|
2657
|
+
maxContextWindow: 1e6,
|
|
2658
|
+
maxOutputTokens: 8192
|
|
2659
|
+
},
|
|
2660
|
+
"gemini-1.5-pro": {
|
|
2661
|
+
jsonMode: true,
|
|
2662
|
+
strictJsonMode: true,
|
|
2663
|
+
toolCalling: true,
|
|
2664
|
+
streaming: true,
|
|
2665
|
+
systemMessages: true,
|
|
2666
|
+
maxContextWindow: 2e6,
|
|
2667
|
+
maxOutputTokens: 8192
|
|
2668
|
+
},
|
|
2669
|
+
"gemini-1.5-flash": {
|
|
2670
|
+
jsonMode: true,
|
|
2671
|
+
strictJsonMode: true,
|
|
2672
|
+
toolCalling: true,
|
|
2673
|
+
streaming: true,
|
|
2674
|
+
systemMessages: true,
|
|
2675
|
+
maxContextWindow: 1e6,
|
|
2676
|
+
maxOutputTokens: 8192
|
|
2677
|
+
},
|
|
2678
|
+
"gemini-1.0-pro": {
|
|
2679
|
+
jsonMode: true,
|
|
2680
|
+
strictJsonMode: false,
|
|
2681
|
+
toolCalling: true,
|
|
2682
|
+
streaming: true,
|
|
2683
|
+
systemMessages: true,
|
|
2684
|
+
maxContextWindow: 32e3,
|
|
2685
|
+
maxOutputTokens: 8192
|
|
2686
|
+
}
|
|
2687
|
+
};
|
|
2688
|
+
var GoogleAdapter = class {
|
|
2689
|
+
name = "google";
|
|
2690
|
+
client;
|
|
2691
|
+
options;
|
|
2692
|
+
constructor(client, options = {}) {
|
|
2693
|
+
this.client = client;
|
|
2694
|
+
this.options = options;
|
|
2695
|
+
}
|
|
2696
|
+
/**
|
|
2697
|
+
* Get capabilities for a model
|
|
2698
|
+
*/
|
|
2699
|
+
getCapabilities(model) {
|
|
2700
|
+
const modelKey = Object.keys(MODEL_CAPABILITIES3).find(
|
|
2701
|
+
(key) => model.includes(key) || key.includes(model)
|
|
2702
|
+
);
|
|
2703
|
+
const base = modelKey ? MODEL_CAPABILITIES3[modelKey] : MODEL_CAPABILITIES3["gemini-2.0-flash"];
|
|
2704
|
+
return {
|
|
2705
|
+
jsonMode: base.jsonMode ?? true,
|
|
2706
|
+
strictJsonMode: base.strictJsonMode ?? true,
|
|
2707
|
+
toolCalling: base.toolCalling ?? true,
|
|
2708
|
+
streaming: base.streaming ?? true,
|
|
2709
|
+
systemMessages: base.systemMessages ?? true,
|
|
2710
|
+
maxContextWindow: base.maxContextWindow,
|
|
2711
|
+
maxOutputTokens: base.maxOutputTokens
|
|
2712
|
+
};
|
|
2713
|
+
}
|
|
2714
|
+
/**
|
|
2715
|
+
* Check if JSON mode is available
|
|
2716
|
+
*/
|
|
2717
|
+
supportsJsonMode(model) {
|
|
2718
|
+
return this.getCapabilities(model).jsonMode;
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Check if tool calling is available
|
|
2722
|
+
*/
|
|
2723
|
+
supportsToolCalling(model) {
|
|
2724
|
+
return this.getCapabilities(model).toolCalling;
|
|
2725
|
+
}
|
|
2726
|
+
/**
|
|
2727
|
+
* Create a completion request
|
|
2728
|
+
*/
|
|
2729
|
+
async createCompletion(request) {
|
|
2730
|
+
const model = this.getModel(request);
|
|
2731
|
+
const contents = this.formatContents(request.messages);
|
|
2732
|
+
const result = await model.generateContent({ contents });
|
|
2733
|
+
return this.parseResponse(result.response);
|
|
2734
|
+
}
|
|
2735
|
+
/**
|
|
2736
|
+
* Create a streaming completion request
|
|
2737
|
+
*/
|
|
2738
|
+
async *createStreamingCompletion(request) {
|
|
2739
|
+
const model = this.getModel(request);
|
|
2740
|
+
const contents = this.formatContents(request.messages);
|
|
2741
|
+
const streamResult = await model.generateContentStream({ contents });
|
|
2742
|
+
for await (const chunk of streamResult.stream) {
|
|
2743
|
+
const text = chunk.text();
|
|
2744
|
+
const candidate = chunk.candidates?.[0];
|
|
2745
|
+
const isFinal = candidate?.finishReason !== void 0;
|
|
2746
|
+
yield {
|
|
2747
|
+
content: text,
|
|
2748
|
+
isFinal,
|
|
2749
|
+
finishReason: candidate?.finishReason,
|
|
2750
|
+
usage: chunk.usageMetadata ? {
|
|
2751
|
+
promptTokens: chunk.usageMetadata.promptTokenCount,
|
|
2752
|
+
completionTokens: chunk.usageMetadata.candidatesTokenCount,
|
|
2753
|
+
totalTokens: chunk.usageMetadata.totalTokenCount
|
|
2754
|
+
} : void 0
|
|
2755
|
+
};
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
/**
|
|
2759
|
+
* Format messages for Google
|
|
2760
|
+
*/
|
|
2761
|
+
formatMessages(messages) {
|
|
2762
|
+
return this.formatContents(messages);
|
|
2763
|
+
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Format JSON schema for Google
|
|
2766
|
+
*/
|
|
2767
|
+
formatJsonSchema(schema) {
|
|
2768
|
+
return {
|
|
2769
|
+
responseMimeType: "application/json",
|
|
2770
|
+
responseSchema: this.convertToGoogleSchema(schema)
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2773
|
+
/**
|
|
2774
|
+
* Format tool definition for Google
|
|
2775
|
+
*/
|
|
2776
|
+
formatToolDefinition(tool) {
|
|
2777
|
+
return {
|
|
2778
|
+
functionDeclarations: [
|
|
2779
|
+
{
|
|
2780
|
+
name: tool.function.name,
|
|
2781
|
+
description: tool.function.description,
|
|
2782
|
+
parameters: this.convertToGoogleSchema(tool.function.parameters)
|
|
2783
|
+
}
|
|
2784
|
+
]
|
|
2785
|
+
};
|
|
2786
|
+
}
|
|
2787
|
+
/**
|
|
2788
|
+
* Get the generative model with configuration
|
|
2789
|
+
*/
|
|
2790
|
+
getModel(request) {
|
|
2791
|
+
const generationConfig = {};
|
|
2792
|
+
if (request.temperature !== void 0) {
|
|
2793
|
+
generationConfig.temperature = request.temperature;
|
|
2794
|
+
}
|
|
2795
|
+
if (request.maxTokens !== void 0) {
|
|
2796
|
+
generationConfig.maxOutputTokens = request.maxTokens;
|
|
2797
|
+
}
|
|
2798
|
+
let tools;
|
|
2799
|
+
let toolConfig;
|
|
2800
|
+
switch (request.mode) {
|
|
2801
|
+
case "json":
|
|
2802
|
+
if (request.jsonSchema) {
|
|
2803
|
+
generationConfig.responseMimeType = "application/json";
|
|
2804
|
+
generationConfig.responseSchema = this.convertToGoogleSchema(
|
|
2805
|
+
request.jsonSchema
|
|
2806
|
+
);
|
|
2807
|
+
}
|
|
2808
|
+
break;
|
|
2809
|
+
case "tool":
|
|
2810
|
+
if (request.toolDefinition) {
|
|
2811
|
+
tools = [
|
|
2812
|
+
{
|
|
2813
|
+
functionDeclarations: [
|
|
2814
|
+
{
|
|
2815
|
+
name: request.toolDefinition.function.name,
|
|
2816
|
+
description: request.toolDefinition.function.description,
|
|
2817
|
+
parameters: this.convertToGoogleSchema(
|
|
2818
|
+
request.toolDefinition.function.parameters
|
|
2819
|
+
)
|
|
2820
|
+
}
|
|
2821
|
+
]
|
|
2822
|
+
}
|
|
2823
|
+
];
|
|
2824
|
+
toolConfig = {
|
|
2825
|
+
functionCallingConfig: {
|
|
2826
|
+
mode: "ANY",
|
|
2827
|
+
allowedFunctionNames: [request.toolDefinition.function.name]
|
|
2828
|
+
}
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
break;
|
|
2832
|
+
}
|
|
2833
|
+
if (this.options.generationConfig) {
|
|
2834
|
+
Object.assign(generationConfig, this.options.generationConfig);
|
|
2835
|
+
}
|
|
2836
|
+
return this.client.getGenerativeModel({
|
|
2837
|
+
model: request.model,
|
|
2838
|
+
generationConfig,
|
|
2839
|
+
tools,
|
|
2840
|
+
toolConfig
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
/**
|
|
2844
|
+
* Format contents for Google API
|
|
2845
|
+
*/
|
|
2846
|
+
formatContents(messages) {
|
|
2847
|
+
const contents = [];
|
|
2848
|
+
const systemMessage = messages.find((m) => m.role === "system");
|
|
2849
|
+
const otherMessages = messages.filter((m) => m.role !== "system");
|
|
2850
|
+
if (systemMessage) {
|
|
2851
|
+
if (otherMessages.length > 0 && otherMessages[0].role === "user") {
|
|
2852
|
+
otherMessages[0] = {
|
|
2853
|
+
...otherMessages[0],
|
|
2854
|
+
content: `${systemMessage.content}
|
|
2855
|
+
|
|
2856
|
+
${otherMessages[0].content}`
|
|
2857
|
+
};
|
|
2858
|
+
} else {
|
|
2859
|
+
contents.push({
|
|
2860
|
+
role: "user",
|
|
2861
|
+
parts: [{ text: systemMessage.content }]
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
for (const msg of otherMessages) {
|
|
2866
|
+
contents.push({
|
|
2867
|
+
role: msg.role === "assistant" ? "model" : "user",
|
|
2868
|
+
parts: [{ text: msg.content }]
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
return contents;
|
|
2872
|
+
}
|
|
2873
|
+
/**
|
|
2874
|
+
* Convert JSON Schema to Google's schema format
|
|
2875
|
+
*/
|
|
2876
|
+
convertToGoogleSchema(schema) {
|
|
2877
|
+
const converted = {};
|
|
2878
|
+
if (schema.type) {
|
|
2879
|
+
converted.type = Array.isArray(schema.type) ? schema.type[0].toUpperCase() : schema.type.toUpperCase();
|
|
2880
|
+
}
|
|
2881
|
+
if (schema.description) {
|
|
2882
|
+
converted.description = schema.description;
|
|
2883
|
+
}
|
|
2884
|
+
if (schema.enum) {
|
|
2885
|
+
converted.enum = schema.enum;
|
|
2886
|
+
}
|
|
2887
|
+
if (schema.properties) {
|
|
2888
|
+
converted.properties = Object.fromEntries(
|
|
2889
|
+
Object.entries(schema.properties).map(([key, prop]) => [
|
|
2890
|
+
key,
|
|
2891
|
+
this.convertToGoogleSchema(prop)
|
|
2892
|
+
])
|
|
2893
|
+
);
|
|
2894
|
+
}
|
|
2895
|
+
if (schema.required) {
|
|
2896
|
+
converted.required = schema.required;
|
|
2897
|
+
}
|
|
2898
|
+
if (schema.items) {
|
|
2899
|
+
converted.items = this.convertToGoogleSchema(schema.items);
|
|
2900
|
+
}
|
|
2901
|
+
if (schema.minimum !== void 0) {
|
|
2902
|
+
converted.minimum = schema.minimum;
|
|
2903
|
+
}
|
|
2904
|
+
if (schema.maximum !== void 0) {
|
|
2905
|
+
converted.maximum = schema.maximum;
|
|
2906
|
+
}
|
|
2907
|
+
if (schema.minLength !== void 0) {
|
|
2908
|
+
converted.minLength = schema.minLength;
|
|
2909
|
+
}
|
|
2910
|
+
if (schema.maxLength !== void 0) {
|
|
2911
|
+
converted.maxLength = schema.maxLength;
|
|
2912
|
+
}
|
|
2913
|
+
if (schema.pattern !== void 0) {
|
|
2914
|
+
converted.pattern = schema.pattern;
|
|
2915
|
+
}
|
|
2916
|
+
return converted;
|
|
2917
|
+
}
|
|
2918
|
+
/**
|
|
2919
|
+
* Parse response
|
|
2920
|
+
*/
|
|
2921
|
+
parseResponse(response) {
|
|
2922
|
+
let content = "";
|
|
2923
|
+
let toolCalls;
|
|
2924
|
+
try {
|
|
2925
|
+
content = response.text();
|
|
2926
|
+
} catch {
|
|
2927
|
+
}
|
|
2928
|
+
const functionCalls = response.functionCalls?.();
|
|
2929
|
+
if (functionCalls && functionCalls.length > 0) {
|
|
2930
|
+
toolCalls = functionCalls.map((fc, index) => ({
|
|
2931
|
+
id: `call_${index}`,
|
|
2932
|
+
name: fc.name,
|
|
2933
|
+
arguments: JSON.stringify(fc.args)
|
|
2934
|
+
}));
|
|
2935
|
+
}
|
|
2936
|
+
if (!toolCalls && response.candidates) {
|
|
2937
|
+
for (const candidate of response.candidates) {
|
|
2938
|
+
for (const part of candidate.content.parts) {
|
|
2939
|
+
if (part.functionCall) {
|
|
2940
|
+
if (!toolCalls) {
|
|
2941
|
+
toolCalls = [];
|
|
2942
|
+
}
|
|
2943
|
+
toolCalls.push({
|
|
2944
|
+
id: `call_${toolCalls.length}`,
|
|
2945
|
+
name: part.functionCall.name,
|
|
2946
|
+
arguments: JSON.stringify(part.functionCall.args)
|
|
2947
|
+
});
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
return {
|
|
2953
|
+
content,
|
|
2954
|
+
usage: response.usageMetadata ? {
|
|
2955
|
+
promptTokens: response.usageMetadata.promptTokenCount,
|
|
2956
|
+
completionTokens: response.usageMetadata.candidatesTokenCount,
|
|
2957
|
+
totalTokens: response.usageMetadata.totalTokenCount
|
|
2958
|
+
} : void 0,
|
|
2959
|
+
finishReason: response.candidates?.[0]?.finishReason,
|
|
2960
|
+
toolCalls,
|
|
2961
|
+
raw: response
|
|
2962
|
+
};
|
|
2963
|
+
}
|
|
2964
|
+
};
|
|
2965
|
+
function createGoogleAdapter(client, options) {
|
|
2966
|
+
return new GoogleAdapter(client, options);
|
|
2967
|
+
}
|
|
2968
|
+
var StructuredProvider = class {
|
|
2969
|
+
clients;
|
|
2970
|
+
adapters;
|
|
2971
|
+
options;
|
|
2972
|
+
defaultProviderName = null;
|
|
2973
|
+
constructor(options = {}) {
|
|
2974
|
+
this.clients = /* @__PURE__ */ new Map();
|
|
2975
|
+
this.adapters = /* @__PURE__ */ new Map();
|
|
2976
|
+
this.options = {
|
|
2977
|
+
enableFixHints: true,
|
|
2978
|
+
validatePartials: false,
|
|
2979
|
+
maxRetries: 3,
|
|
2980
|
+
...options
|
|
2981
|
+
};
|
|
2982
|
+
}
|
|
2983
|
+
/**
|
|
2984
|
+
* Register a provider client
|
|
2985
|
+
*/
|
|
2986
|
+
registerProvider(name, agentSeaClient, setAsDefault = false) {
|
|
2987
|
+
const adapter = this.createAdapter(agentSeaClient);
|
|
2988
|
+
this.adapters.set(name, adapter);
|
|
2989
|
+
const client = new StructuredClient(adapter, {
|
|
2990
|
+
defaultMode: this.options.defaultMode,
|
|
2991
|
+
enableFixHints: this.options.enableFixHints,
|
|
2992
|
+
validatePartials: this.options.validatePartials,
|
|
2993
|
+
defaultRetry: {
|
|
2994
|
+
maxAttempts: this.options.maxRetries ?? 3,
|
|
2995
|
+
retryOn: ["validation_error", "parse_error"]
|
|
2996
|
+
}
|
|
2997
|
+
});
|
|
2998
|
+
this.clients.set(name, client);
|
|
2999
|
+
if (setAsDefault || !this.defaultProviderName) {
|
|
3000
|
+
this.defaultProviderName = name;
|
|
3001
|
+
}
|
|
3002
|
+
return this;
|
|
3003
|
+
}
|
|
3004
|
+
/**
|
|
3005
|
+
* Get a structured client
|
|
3006
|
+
*/
|
|
3007
|
+
getClient(name) {
|
|
3008
|
+
const providerName = name ?? this.defaultProviderName;
|
|
3009
|
+
if (!providerName) {
|
|
3010
|
+
throw new Error("No provider registered");
|
|
3011
|
+
}
|
|
3012
|
+
const client = this.clients.get(providerName);
|
|
3013
|
+
if (!client) {
|
|
3014
|
+
throw new Error(`Provider '${providerName}' not found`);
|
|
3015
|
+
}
|
|
3016
|
+
return client;
|
|
3017
|
+
}
|
|
3018
|
+
/**
|
|
3019
|
+
* Extract structured data
|
|
3020
|
+
*/
|
|
3021
|
+
async extract(schema, prompt, options) {
|
|
3022
|
+
const client = this.getClient(options?.provider);
|
|
3023
|
+
const messages = Array.isArray(prompt) ? prompt : [{ role: "user", content: prompt }];
|
|
3024
|
+
return client.extract({
|
|
3025
|
+
model: options?.model ?? this.options.defaultModel ?? "gpt-4o",
|
|
3026
|
+
messages,
|
|
3027
|
+
response_format: schema,
|
|
3028
|
+
mode: options?.mode ?? this.options.defaultMode
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
/**
|
|
3032
|
+
* Extract structured data with streaming
|
|
3033
|
+
*/
|
|
3034
|
+
extractStream(schema, prompt, options) {
|
|
3035
|
+
const client = this.getClient(options?.provider);
|
|
3036
|
+
const messages = Array.isArray(prompt) ? prompt : [{ role: "user", content: prompt }];
|
|
3037
|
+
return client.extractStream(
|
|
3038
|
+
{
|
|
3039
|
+
model: options?.model ?? this.options.defaultModel ?? "gpt-4o",
|
|
3040
|
+
messages,
|
|
3041
|
+
response_format: schema,
|
|
3042
|
+
mode: options?.mode ?? this.options.defaultMode
|
|
3043
|
+
},
|
|
3044
|
+
options?.streaming
|
|
3045
|
+
);
|
|
3046
|
+
}
|
|
3047
|
+
/**
|
|
3048
|
+
* Create a typed extractor for reuse
|
|
3049
|
+
*/
|
|
3050
|
+
createExtractor(schema, options) {
|
|
3051
|
+
return new TypedExtractor(this, schema, options);
|
|
3052
|
+
}
|
|
3053
|
+
/**
|
|
3054
|
+
* Create adapter from AgentSea client
|
|
3055
|
+
*/
|
|
3056
|
+
createAdapter(agentSeaClient) {
|
|
3057
|
+
switch (agentSeaClient.provider) {
|
|
3058
|
+
case "openai":
|
|
3059
|
+
return new OpenAIAdapter(
|
|
3060
|
+
agentSeaClient.client
|
|
3061
|
+
);
|
|
3062
|
+
case "anthropic":
|
|
3063
|
+
return new AnthropicAdapter(
|
|
3064
|
+
agentSeaClient.client
|
|
3065
|
+
);
|
|
3066
|
+
case "google":
|
|
3067
|
+
return new GoogleAdapter(
|
|
3068
|
+
agentSeaClient.client
|
|
3069
|
+
);
|
|
3070
|
+
default:
|
|
3071
|
+
throw new Error(
|
|
3072
|
+
`Unsupported provider: ${agentSeaClient.provider}`
|
|
3073
|
+
);
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
var TypedExtractor = class {
|
|
3078
|
+
constructor(provider, schema, options) {
|
|
3079
|
+
this.provider = provider;
|
|
3080
|
+
this.schema = schema;
|
|
3081
|
+
this.options = options;
|
|
3082
|
+
}
|
|
3083
|
+
/**
|
|
3084
|
+
* Extract structured data
|
|
3085
|
+
*/
|
|
3086
|
+
async extract(prompt, overrideOptions) {
|
|
3087
|
+
return this.provider.extract(this.schema, prompt, {
|
|
3088
|
+
...this.options,
|
|
3089
|
+
...overrideOptions
|
|
3090
|
+
});
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Extract with streaming
|
|
3094
|
+
*/
|
|
3095
|
+
extractStream(prompt, streamingOptions) {
|
|
3096
|
+
return this.provider.extractStream(this.schema, prompt, {
|
|
3097
|
+
...this.options,
|
|
3098
|
+
streaming: streamingOptions
|
|
3099
|
+
});
|
|
3100
|
+
}
|
|
3101
|
+
/**
|
|
3102
|
+
* Get the schema
|
|
3103
|
+
*/
|
|
3104
|
+
getSchema() {
|
|
3105
|
+
return this.schema;
|
|
3106
|
+
}
|
|
3107
|
+
};
|
|
3108
|
+
function createStructuredProvider(options) {
|
|
3109
|
+
return new StructuredProvider(options);
|
|
3110
|
+
}
|
|
3111
|
+
var Extractors = {
|
|
3112
|
+
/**
|
|
3113
|
+
* Create a list extractor
|
|
3114
|
+
*/
|
|
3115
|
+
list(itemSchema, options) {
|
|
3116
|
+
let schema = z.array(itemSchema);
|
|
3117
|
+
if (options?.minItems !== void 0) {
|
|
3118
|
+
schema = schema.min(options.minItems);
|
|
3119
|
+
}
|
|
3120
|
+
if (options?.maxItems !== void 0) {
|
|
3121
|
+
schema = schema.max(options.maxItems);
|
|
3122
|
+
}
|
|
3123
|
+
return schema;
|
|
3124
|
+
},
|
|
3125
|
+
/**
|
|
3126
|
+
* Create an entity extractor
|
|
3127
|
+
*/
|
|
3128
|
+
entity(shape) {
|
|
3129
|
+
return z.object(shape);
|
|
3130
|
+
},
|
|
3131
|
+
/**
|
|
3132
|
+
* Create a classification extractor
|
|
3133
|
+
*/
|
|
3134
|
+
classification(categories, options) {
|
|
3135
|
+
if (options?.confidence) {
|
|
3136
|
+
return z.object({
|
|
3137
|
+
category: z.enum(categories),
|
|
3138
|
+
confidence: z.number().min(0).max(1),
|
|
3139
|
+
reasoning: z.string().optional()
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
return z.object({
|
|
3143
|
+
category: z.enum(categories)
|
|
3144
|
+
});
|
|
3145
|
+
},
|
|
3146
|
+
/**
|
|
3147
|
+
* Create a sentiment analyzer
|
|
3148
|
+
*/
|
|
3149
|
+
sentiment() {
|
|
3150
|
+
return z.object({
|
|
3151
|
+
sentiment: z.enum(["positive", "negative", "neutral", "mixed"]),
|
|
3152
|
+
score: z.number().min(-1).max(1),
|
|
3153
|
+
aspects: z.array(
|
|
3154
|
+
z.object({
|
|
3155
|
+
aspect: z.string(),
|
|
3156
|
+
sentiment: z.enum(["positive", "negative", "neutral"])
|
|
3157
|
+
})
|
|
3158
|
+
).optional()
|
|
3159
|
+
});
|
|
3160
|
+
},
|
|
3161
|
+
/**
|
|
3162
|
+
* Create a key-value extractor
|
|
3163
|
+
*/
|
|
3164
|
+
keyValue() {
|
|
3165
|
+
return z.record(z.string(), z.unknown());
|
|
3166
|
+
},
|
|
3167
|
+
/**
|
|
3168
|
+
* Create a summary extractor
|
|
3169
|
+
*/
|
|
3170
|
+
summary(options) {
|
|
3171
|
+
let summarySchema = z.string();
|
|
3172
|
+
if (options?.maxLength) {
|
|
3173
|
+
summarySchema = summarySchema.max(options.maxLength);
|
|
3174
|
+
}
|
|
3175
|
+
return z.object({
|
|
3176
|
+
summary: summarySchema,
|
|
3177
|
+
keyPoints: z.array(z.string()),
|
|
3178
|
+
entities: z.array(
|
|
3179
|
+
z.object({
|
|
3180
|
+
name: z.string(),
|
|
3181
|
+
type: z.string()
|
|
3182
|
+
})
|
|
3183
|
+
).optional()
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
};
|
|
3187
|
+
|
|
3188
|
+
// src/types/core.types.ts
|
|
3189
|
+
var StructuredError = class extends Error {
|
|
3190
|
+
/** Error code */
|
|
3191
|
+
code;
|
|
3192
|
+
/** Extraction attempts */
|
|
3193
|
+
attempts;
|
|
3194
|
+
/** Last validation errors */
|
|
3195
|
+
validationErrors;
|
|
3196
|
+
/** Partial result if available */
|
|
3197
|
+
partial;
|
|
3198
|
+
constructor(message, code, attempts, validationErrors, partial) {
|
|
3199
|
+
super(message);
|
|
3200
|
+
this.name = "StructuredError";
|
|
3201
|
+
this.code = code;
|
|
3202
|
+
this.attempts = attempts;
|
|
3203
|
+
this.validationErrors = validationErrors;
|
|
3204
|
+
this.partial = partial;
|
|
3205
|
+
}
|
|
3206
|
+
};
|
|
3207
|
+
|
|
3208
|
+
export { AnthropicAdapter, Extractors, GoogleAdapter, IncrementalJsonParser, OpenAIAdapter, SchemaPromptGenerator, SchemaValidator, StructuredClient, StructuredError, StructuredProvider, TypedExtractor, analyzeSchema, coerceToSchema, createAnthropicAdapter, createGoogleAdapter, createOpenAIAdapter, createStreamingResult, createStructuredClient, createStructuredProvider, extractFieldInfo, formatZodErrors, generateExample, getPartialState, getValidationHints, matchesSchema, schemaToPrompt, tokenizeJson, validatePartial, validateSchema, validateSchemaOrThrow, zodToJsonSchema };
|
|
3209
|
+
//# sourceMappingURL=index.js.map
|
|
3210
|
+
//# sourceMappingURL=index.js.map
|