@modelrelay/sdk 0.24.0 → 0.25.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/README.md +149 -0
- package/dist/index.cjs +347 -4
- package/dist/index.d.cts +967 -728
- package/dist/index.d.ts +967 -728
- package/dist/index.js +342 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -205,6 +205,155 @@ const final = await stream.collect();
|
|
|
205
205
|
console.log(final.items.length);
|
|
206
206
|
```
|
|
207
207
|
|
|
208
|
+
### Type-safe structured outputs with Zod schemas
|
|
209
|
+
|
|
210
|
+
For automatic schema generation and validation, use `structured()` with Zod:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import { ModelRelay } from "@modelrelay/sdk";
|
|
214
|
+
import { z } from "zod";
|
|
215
|
+
|
|
216
|
+
const mr = new ModelRelay({ key: "mr_sk_..." });
|
|
217
|
+
|
|
218
|
+
// Define your output type with Zod
|
|
219
|
+
const PersonSchema = z.object({
|
|
220
|
+
name: z.string(),
|
|
221
|
+
age: z.number(),
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// structured() auto-generates JSON schema and validates responses
|
|
225
|
+
const result = await mr.chat.completions.structured(
|
|
226
|
+
PersonSchema,
|
|
227
|
+
{
|
|
228
|
+
model: "claude-sonnet-4-20250514",
|
|
229
|
+
messages: [{ role: "user", content: "Extract: John Doe is 30 years old" }],
|
|
230
|
+
},
|
|
231
|
+
{ maxRetries: 2 } // Retry on validation failures
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
console.log(`Name: ${result.value.name}, Age: ${result.value.age}`);
|
|
235
|
+
console.log(`Succeeded on attempt ${result.attempts}`);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Schema features
|
|
239
|
+
|
|
240
|
+
Zod schemas map to JSON Schema properties:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const StatusSchema = z.object({
|
|
244
|
+
// Required string field
|
|
245
|
+
code: z.string(),
|
|
246
|
+
|
|
247
|
+
// Optional field (not in "required" array)
|
|
248
|
+
notes: z.string().optional(),
|
|
249
|
+
|
|
250
|
+
// Description for documentation
|
|
251
|
+
email: z.string().email().describe("User's email address"),
|
|
252
|
+
|
|
253
|
+
// Enum constraint
|
|
254
|
+
priority: z.enum(["low", "medium", "high"]),
|
|
255
|
+
|
|
256
|
+
// Nested objects are fully supported
|
|
257
|
+
address: z.object({
|
|
258
|
+
city: z.string(),
|
|
259
|
+
country: z.string(),
|
|
260
|
+
}),
|
|
261
|
+
|
|
262
|
+
// Arrays
|
|
263
|
+
tags: z.array(z.string()),
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
#### Handling validation errors
|
|
268
|
+
|
|
269
|
+
When validation fails after all retries:
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
import { StructuredExhaustedError } from "@modelrelay/sdk";
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const result = await mr.chat.completions.structured(
|
|
276
|
+
PersonSchema,
|
|
277
|
+
{ model: "claude-sonnet-4-20250514", messages },
|
|
278
|
+
{ maxRetries: 2 }
|
|
279
|
+
);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
if (err instanceof StructuredExhaustedError) {
|
|
282
|
+
console.log(`Failed after ${err.allAttempts.length} attempts`);
|
|
283
|
+
for (const attempt of err.allAttempts) {
|
|
284
|
+
console.log(`Attempt ${attempt.attempt}: ${attempt.rawJson}`);
|
|
285
|
+
if (attempt.error.kind === "validation" && attempt.error.issues) {
|
|
286
|
+
for (const issue of attempt.error.issues) {
|
|
287
|
+
console.log(` - ${issue.path ?? "root"}: ${issue.message}`);
|
|
288
|
+
}
|
|
289
|
+
} else if (attempt.error.kind === "decode") {
|
|
290
|
+
console.log(` Decode error: ${attempt.error.message}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
#### Custom retry handlers
|
|
298
|
+
|
|
299
|
+
Customize retry behavior:
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
import type { RetryHandler } from "@modelrelay/sdk";
|
|
303
|
+
|
|
304
|
+
const customHandler: RetryHandler = {
|
|
305
|
+
onValidationError(attempt, rawJson, error, messages) {
|
|
306
|
+
if (attempt >= 3) {
|
|
307
|
+
return null; // Stop retrying
|
|
308
|
+
}
|
|
309
|
+
return [
|
|
310
|
+
{
|
|
311
|
+
role: "user",
|
|
312
|
+
content: `Invalid response. Issues: ${JSON.stringify(error.issues)}. Try again.`,
|
|
313
|
+
},
|
|
314
|
+
];
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const result = await mr.chat.completions.structured(
|
|
319
|
+
PersonSchema,
|
|
320
|
+
{ model: "claude-sonnet-4-20250514", messages },
|
|
321
|
+
{ maxRetries: 3, retryHandler: customHandler }
|
|
322
|
+
);
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
#### Streaming structured outputs
|
|
326
|
+
|
|
327
|
+
For streaming with Zod schema (no retries):
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
const stream = await mr.chat.completions.streamStructured(
|
|
331
|
+
PersonSchema,
|
|
332
|
+
{
|
|
333
|
+
model: "claude-sonnet-4-20250514",
|
|
334
|
+
messages: [{ role: "user", content: "Extract: Jane, 25" }],
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
for await (const evt of stream) {
|
|
339
|
+
if (evt.type === "completion") {
|
|
340
|
+
console.log("Final:", evt.payload);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Customer-attributed structured outputs
|
|
346
|
+
|
|
347
|
+
Works with customer-attributed requests too:
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
const result = await mr.chat.forCustomer("customer-123").structured(
|
|
351
|
+
PersonSchema,
|
|
352
|
+
{ messages: [{ role: "user", content: "Extract: John, 30" }] },
|
|
353
|
+
{ maxRetries: 2 }
|
|
354
|
+
);
|
|
355
|
+
```
|
|
356
|
+
|
|
208
357
|
### Telemetry & metrics hooks
|
|
209
358
|
|
|
210
359
|
Provide lightweight callbacks to observe latency and usage without extra deps:
|
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,8 @@ __export(index_exports, {
|
|
|
38
38
|
ResponseFormatTypes: () => ResponseFormatTypes,
|
|
39
39
|
SDK_VERSION: () => SDK_VERSION,
|
|
40
40
|
StopReasons: () => StopReasons,
|
|
41
|
+
StructuredDecodeError: () => StructuredDecodeError,
|
|
42
|
+
StructuredExhaustedError: () => StructuredExhaustedError,
|
|
41
43
|
StructuredJSONStream: () => StructuredJSONStream,
|
|
42
44
|
TiersClient: () => TiersClient,
|
|
43
45
|
ToolArgsError: () => ToolArgsError,
|
|
@@ -60,6 +62,7 @@ __export(index_exports, {
|
|
|
60
62
|
createUsage: () => createUsage,
|
|
61
63
|
createUserMessage: () => createUserMessage,
|
|
62
64
|
createWebTool: () => createWebTool,
|
|
65
|
+
defaultRetryHandler: () => defaultRetryHandler,
|
|
63
66
|
executeWithRetry: () => executeWithRetry,
|
|
64
67
|
firstToolCall: () => firstToolCall,
|
|
65
68
|
formatToolErrorForModel: () => formatToolErrorForModel,
|
|
@@ -80,12 +83,14 @@ __export(index_exports, {
|
|
|
80
83
|
parseToolArgs: () => parseToolArgs,
|
|
81
84
|
parseToolArgsRaw: () => parseToolArgsRaw,
|
|
82
85
|
respondToToolCall: () => respondToToolCall,
|
|
86
|
+
responseFormatFromZod: () => responseFormatFromZod,
|
|
83
87
|
stopReasonToString: () => stopReasonToString,
|
|
84
88
|
toolChoiceAuto: () => toolChoiceAuto,
|
|
85
89
|
toolChoiceNone: () => toolChoiceNone,
|
|
86
90
|
toolChoiceRequired: () => toolChoiceRequired,
|
|
87
91
|
toolResultMessage: () => toolResultMessage,
|
|
88
92
|
tryParseToolArgs: () => tryParseToolArgs,
|
|
93
|
+
validateWithZod: () => validateWithZod,
|
|
89
94
|
zodToJsonSchema: () => zodToJsonSchema
|
|
90
95
|
});
|
|
91
96
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -432,7 +437,7 @@ function isTokenReusable(token) {
|
|
|
432
437
|
// package.json
|
|
433
438
|
var package_default = {
|
|
434
439
|
name: "@modelrelay/sdk",
|
|
435
|
-
version: "0.
|
|
440
|
+
version: "0.25.1",
|
|
436
441
|
description: "TypeScript SDK for the ModelRelay API",
|
|
437
442
|
type: "module",
|
|
438
443
|
main: "dist/index.cjs",
|
|
@@ -1192,6 +1197,58 @@ async function executeWithRetry(registry, toolCalls, options = {}) {
|
|
|
1192
1197
|
return Array.from(successfulResults.values());
|
|
1193
1198
|
}
|
|
1194
1199
|
|
|
1200
|
+
// src/structured.ts
|
|
1201
|
+
var StructuredDecodeError = class extends Error {
|
|
1202
|
+
constructor(message, rawJson, attempt) {
|
|
1203
|
+
super(`structured output decode error (attempt ${attempt}): ${message}`);
|
|
1204
|
+
this.name = "StructuredDecodeError";
|
|
1205
|
+
this.rawJson = rawJson;
|
|
1206
|
+
this.attempt = attempt;
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
var StructuredExhaustedError = class extends Error {
|
|
1210
|
+
constructor(lastRawJson, allAttempts, finalError) {
|
|
1211
|
+
const errorMsg = finalError.kind === "decode" ? finalError.message : finalError.issues.map((i) => i.message).join("; ");
|
|
1212
|
+
super(
|
|
1213
|
+
`structured output failed after ${allAttempts.length} attempts: ${errorMsg}`
|
|
1214
|
+
);
|
|
1215
|
+
this.name = "StructuredExhaustedError";
|
|
1216
|
+
this.lastRawJson = lastRawJson;
|
|
1217
|
+
this.allAttempts = allAttempts;
|
|
1218
|
+
this.finalError = finalError;
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
var defaultRetryHandler = {
|
|
1222
|
+
onValidationError(_attempt, _rawJson, error, _originalMessages) {
|
|
1223
|
+
const errorMsg = error.kind === "decode" ? error.message : error.issues.map((i) => `${i.path ?? ""}: ${i.message}`).join("; ");
|
|
1224
|
+
return [
|
|
1225
|
+
{
|
|
1226
|
+
role: "user",
|
|
1227
|
+
content: `The previous response did not match the expected schema. Error: ${errorMsg}. Please provide a response that matches the schema exactly.`
|
|
1228
|
+
}
|
|
1229
|
+
];
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
function responseFormatFromZod(schema, name = "response") {
|
|
1233
|
+
const jsonSchema = zodToJsonSchema(schema);
|
|
1234
|
+
return {
|
|
1235
|
+
type: "json_schema",
|
|
1236
|
+
json_schema: {
|
|
1237
|
+
name,
|
|
1238
|
+
schema: jsonSchema,
|
|
1239
|
+
strict: true
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
function validateWithZod(schema, data) {
|
|
1244
|
+
const result = schema.safeParse(data);
|
|
1245
|
+
if (result.success) {
|
|
1246
|
+
return { success: true, data: result.data };
|
|
1247
|
+
}
|
|
1248
|
+
const errorMsg = result.error && typeof result.error === "object" && "message" in result.error ? String(result.error.message) : "validation failed";
|
|
1249
|
+
return { success: false, error: errorMsg };
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1195
1252
|
// src/chat.ts
|
|
1196
1253
|
var CUSTOMER_ID_HEADER = "X-ModelRelay-Customer-Id";
|
|
1197
1254
|
var REQUEST_ID_HEADER = "X-ModelRelay-Chat-Request-Id";
|
|
@@ -1389,6 +1446,158 @@ var ChatCompletionsClient = class {
|
|
|
1389
1446
|
trace
|
|
1390
1447
|
);
|
|
1391
1448
|
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Send a structured output request with a Zod schema.
|
|
1451
|
+
*
|
|
1452
|
+
* Auto-generates JSON schema from the Zod schema, validates the response,
|
|
1453
|
+
* and retries on validation failure if configured.
|
|
1454
|
+
*
|
|
1455
|
+
* @param schema - A Zod schema defining the expected response structure
|
|
1456
|
+
* @param params - Chat completion parameters (excluding responseFormat)
|
|
1457
|
+
* @param options - Request options including retry configuration
|
|
1458
|
+
* @returns A typed result with the parsed value
|
|
1459
|
+
*
|
|
1460
|
+
* @example
|
|
1461
|
+
* ```typescript
|
|
1462
|
+
* import { z } from 'zod';
|
|
1463
|
+
*
|
|
1464
|
+
* const PersonSchema = z.object({
|
|
1465
|
+
* name: z.string(),
|
|
1466
|
+
* age: z.number(),
|
|
1467
|
+
* });
|
|
1468
|
+
*
|
|
1469
|
+
* const result = await client.chat.completions.structured(
|
|
1470
|
+
* PersonSchema,
|
|
1471
|
+
* { model: "claude-sonnet-4-20250514", messages: [...] },
|
|
1472
|
+
* { maxRetries: 2 }
|
|
1473
|
+
* );
|
|
1474
|
+
* ```
|
|
1475
|
+
*/
|
|
1476
|
+
async structured(schema, params, options = {}) {
|
|
1477
|
+
const {
|
|
1478
|
+
maxRetries = 0,
|
|
1479
|
+
retryHandler = defaultRetryHandler,
|
|
1480
|
+
schemaName,
|
|
1481
|
+
...requestOptions
|
|
1482
|
+
} = options;
|
|
1483
|
+
const responseFormat = responseFormatFromZod(schema, schemaName);
|
|
1484
|
+
const fullParams = {
|
|
1485
|
+
...params,
|
|
1486
|
+
responseFormat,
|
|
1487
|
+
stream: false
|
|
1488
|
+
};
|
|
1489
|
+
let messages = [...params.messages];
|
|
1490
|
+
const attempts = [];
|
|
1491
|
+
const maxAttempts = maxRetries + 1;
|
|
1492
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1493
|
+
const response = await this.create(
|
|
1494
|
+
{ ...fullParams, messages },
|
|
1495
|
+
{ ...requestOptions, stream: false }
|
|
1496
|
+
);
|
|
1497
|
+
const rawJson = response.content.join("");
|
|
1498
|
+
const requestId = response.requestId;
|
|
1499
|
+
try {
|
|
1500
|
+
const parsed = JSON.parse(rawJson);
|
|
1501
|
+
const validated = validateWithZod(schema, parsed);
|
|
1502
|
+
if (validated.success) {
|
|
1503
|
+
return {
|
|
1504
|
+
value: validated.data,
|
|
1505
|
+
attempts: attempt,
|
|
1506
|
+
requestId
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
const error = {
|
|
1510
|
+
kind: "validation",
|
|
1511
|
+
issues: [{ message: validated.error }]
|
|
1512
|
+
};
|
|
1513
|
+
attempts.push({ attempt, rawJson, error });
|
|
1514
|
+
if (attempt >= maxAttempts) {
|
|
1515
|
+
throw new StructuredExhaustedError(rawJson, attempts, error);
|
|
1516
|
+
}
|
|
1517
|
+
const retryMessages = retryHandler.onValidationError(
|
|
1518
|
+
attempt,
|
|
1519
|
+
rawJson,
|
|
1520
|
+
error,
|
|
1521
|
+
params.messages
|
|
1522
|
+
);
|
|
1523
|
+
if (!retryMessages) {
|
|
1524
|
+
throw new StructuredExhaustedError(rawJson, attempts, error);
|
|
1525
|
+
}
|
|
1526
|
+
messages = [
|
|
1527
|
+
...params.messages,
|
|
1528
|
+
{ role: "assistant", content: rawJson },
|
|
1529
|
+
...retryMessages
|
|
1530
|
+
];
|
|
1531
|
+
} catch (e) {
|
|
1532
|
+
if (e instanceof StructuredExhaustedError) {
|
|
1533
|
+
throw e;
|
|
1534
|
+
}
|
|
1535
|
+
const error = {
|
|
1536
|
+
kind: "decode",
|
|
1537
|
+
message: e instanceof Error ? e.message : String(e)
|
|
1538
|
+
};
|
|
1539
|
+
attempts.push({ attempt, rawJson, error });
|
|
1540
|
+
if (attempt >= maxAttempts) {
|
|
1541
|
+
throw new StructuredExhaustedError(rawJson, attempts, error);
|
|
1542
|
+
}
|
|
1543
|
+
const retryMessages = retryHandler.onValidationError(
|
|
1544
|
+
attempt,
|
|
1545
|
+
rawJson,
|
|
1546
|
+
error,
|
|
1547
|
+
params.messages
|
|
1548
|
+
);
|
|
1549
|
+
if (!retryMessages) {
|
|
1550
|
+
throw new StructuredExhaustedError(rawJson, attempts, error);
|
|
1551
|
+
}
|
|
1552
|
+
messages = [
|
|
1553
|
+
...params.messages,
|
|
1554
|
+
{ role: "assistant", content: rawJson },
|
|
1555
|
+
...retryMessages
|
|
1556
|
+
];
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
throw new Error(
|
|
1560
|
+
`Internal error: structured output loop exited unexpectedly after ${maxAttempts} attempts (this is a bug, please report it)`
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Stream structured output with a Zod schema.
|
|
1565
|
+
*
|
|
1566
|
+
* Auto-generates JSON schema from the Zod schema. Note that streaming
|
|
1567
|
+
* does not support retries - for retry behavior, use `structured()`.
|
|
1568
|
+
*
|
|
1569
|
+
* @param schema - A Zod schema defining the expected response structure
|
|
1570
|
+
* @param params - Chat completion parameters (excluding responseFormat)
|
|
1571
|
+
* @param options - Request options
|
|
1572
|
+
* @returns A structured JSON stream
|
|
1573
|
+
*
|
|
1574
|
+
* @example
|
|
1575
|
+
* ```typescript
|
|
1576
|
+
* import { z } from 'zod';
|
|
1577
|
+
*
|
|
1578
|
+
* const PersonSchema = z.object({
|
|
1579
|
+
* name: z.string(),
|
|
1580
|
+
* age: z.number(),
|
|
1581
|
+
* });
|
|
1582
|
+
*
|
|
1583
|
+
* const stream = await client.chat.completions.streamStructured(
|
|
1584
|
+
* PersonSchema,
|
|
1585
|
+
* { model: "claude-sonnet-4-20250514", messages: [...] },
|
|
1586
|
+
* );
|
|
1587
|
+
*
|
|
1588
|
+
* for await (const event of stream) {
|
|
1589
|
+
* console.log(event.type, event.payload);
|
|
1590
|
+
* }
|
|
1591
|
+
* ```
|
|
1592
|
+
*/
|
|
1593
|
+
async streamStructured(schema, params, options = {}) {
|
|
1594
|
+
const { schemaName, ...requestOptions } = options;
|
|
1595
|
+
const responseFormat = responseFormatFromZod(schema, schemaName);
|
|
1596
|
+
return this.streamJSON(
|
|
1597
|
+
{ ...params, responseFormat },
|
|
1598
|
+
requestOptions
|
|
1599
|
+
);
|
|
1600
|
+
}
|
|
1392
1601
|
};
|
|
1393
1602
|
var CustomerChatClient = class {
|
|
1394
1603
|
constructor(http, auth, customerId, defaultMetadata, metrics, trace) {
|
|
@@ -1553,6 +1762,123 @@ var CustomerChatClient = class {
|
|
|
1553
1762
|
trace
|
|
1554
1763
|
);
|
|
1555
1764
|
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Send a structured output request with a Zod schema for customer-attributed calls.
|
|
1767
|
+
*
|
|
1768
|
+
* Auto-generates JSON schema from the Zod schema, validates the response,
|
|
1769
|
+
* and retries on validation failure if configured.
|
|
1770
|
+
*
|
|
1771
|
+
* @param schema - A Zod schema defining the expected response structure
|
|
1772
|
+
* @param params - Customer chat parameters (excluding responseFormat)
|
|
1773
|
+
* @param options - Request options including retry configuration
|
|
1774
|
+
* @returns A typed result with the parsed value
|
|
1775
|
+
*/
|
|
1776
|
+
async structured(schema, params, options = {}) {
|
|
1777
|
+
const {
|
|
1778
|
+
maxRetries = 0,
|
|
1779
|
+
retryHandler = defaultRetryHandler,
|
|
1780
|
+
schemaName,
|
|
1781
|
+
...requestOptions
|
|
1782
|
+
} = options;
|
|
1783
|
+
const responseFormat = responseFormatFromZod(schema, schemaName);
|
|
1784
|
+
const fullParams = {
|
|
1785
|
+
...params,
|
|
1786
|
+
responseFormat,
|
|
1787
|
+
stream: false
|
|
1788
|
+
};
|
|
1789
|
+
let messages = [...params.messages];
|
|
1790
|
+
const attempts = [];
|
|
1791
|
+
const maxAttempts = maxRetries + 1;
|
|
1792
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1793
|
+
const response = await this.create(
|
|
1794
|
+
{ ...fullParams, messages },
|
|
1795
|
+
{ ...requestOptions, stream: false }
|
|
1796
|
+
);
|
|
1797
|
+
const rawJson = response.content.join("");
|
|
1798
|
+
const requestId = response.requestId;
|
|
1799
|
+
try {
|
|
1800
|
+
const parsed = JSON.parse(rawJson);
|
|
1801
|
+
const validated = validateWithZod(schema, parsed);
|
|
1802
|
+
if (validated.success) {
|
|
1803
|
+
return {
|
|
1804
|
+
value: validated.data,
|
|
1805
|
+
attempts: attempt,
|
|
1806
|
+
requestId
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
const error = {
|
|
1810
|
+
kind: "validation",
|
|
1811
|
+
issues: [{ message: validated.error }]
|
|
1812
|
+
};
|
|
1813
|
+
attempts.push({ attempt, rawJson, error });
|
|
1814
|
+
if (attempt >= maxAttempts) {
|
|
1815
|
+
throw new StructuredExhaustedError(rawJson, attempts, error);
|
|
1816
|
+
}
|
|
1817
|
+
const retryMessages = retryHandler.onValidationError(
|
|
1818
|
+
attempt,
|
|
1819
|
+
rawJson,
|
|
1820
|
+
error,
|
|
1821
|
+
params.messages
|
|
1822
|
+
);
|
|
1823
|
+
if (!retryMessages) {
|
|
1824
|
+
throw new StructuredExhaustedError(rawJson, attempts, error);
|
|
1825
|
+
}
|
|
1826
|
+
messages = [
|
|
1827
|
+
...params.messages,
|
|
1828
|
+
{ role: "assistant", content: rawJson },
|
|
1829
|
+
...retryMessages
|
|
1830
|
+
];
|
|
1831
|
+
} catch (e) {
|
|
1832
|
+
if (e instanceof StructuredExhaustedError) {
|
|
1833
|
+
throw e;
|
|
1834
|
+
}
|
|
1835
|
+
const error = {
|
|
1836
|
+
kind: "decode",
|
|
1837
|
+
message: e instanceof Error ? e.message : String(e)
|
|
1838
|
+
};
|
|
1839
|
+
attempts.push({ attempt, rawJson, error });
|
|
1840
|
+
if (attempt >= maxAttempts) {
|
|
1841
|
+
throw new StructuredExhaustedError(rawJson, attempts, error);
|
|
1842
|
+
}
|
|
1843
|
+
const retryMessages = retryHandler.onValidationError(
|
|
1844
|
+
attempt,
|
|
1845
|
+
rawJson,
|
|
1846
|
+
error,
|
|
1847
|
+
params.messages
|
|
1848
|
+
);
|
|
1849
|
+
if (!retryMessages) {
|
|
1850
|
+
throw new StructuredExhaustedError(rawJson, attempts, error);
|
|
1851
|
+
}
|
|
1852
|
+
messages = [
|
|
1853
|
+
...params.messages,
|
|
1854
|
+
{ role: "assistant", content: rawJson },
|
|
1855
|
+
...retryMessages
|
|
1856
|
+
];
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
throw new Error(
|
|
1860
|
+
`Internal error: structured output loop exited unexpectedly after ${maxAttempts} attempts (this is a bug, please report it)`
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Stream structured output with a Zod schema for customer-attributed calls.
|
|
1865
|
+
*
|
|
1866
|
+
* Auto-generates JSON schema from the Zod schema. Note that streaming
|
|
1867
|
+
* does not support retries - for retry behavior, use `structured()`.
|
|
1868
|
+
*
|
|
1869
|
+
* @param schema - A Zod schema defining the expected response structure
|
|
1870
|
+
* @param params - Customer chat parameters (excluding responseFormat)
|
|
1871
|
+
* @param options - Request options
|
|
1872
|
+
* @returns A structured JSON stream
|
|
1873
|
+
*/
|
|
1874
|
+
async streamStructured(schema, params, options = {}) {
|
|
1875
|
+
const { schemaName, ...requestOptions } = options;
|
|
1876
|
+
const responseFormat = responseFormatFromZod(schema, schemaName);
|
|
1877
|
+
return this.streamJSON(
|
|
1878
|
+
{ ...params, responseFormat },
|
|
1879
|
+
requestOptions
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1556
1882
|
};
|
|
1557
1883
|
var ChatCompletionsStream = class {
|
|
1558
1884
|
constructor(response, requestId, context, metrics, trace) {
|
|
@@ -1572,7 +1898,13 @@ var ChatCompletionsStream = class {
|
|
|
1572
1898
|
this.closed = true;
|
|
1573
1899
|
try {
|
|
1574
1900
|
await this.response.body?.cancel(reason);
|
|
1575
|
-
} catch {
|
|
1901
|
+
} catch (err) {
|
|
1902
|
+
if (this.trace?.streamError) {
|
|
1903
|
+
this.trace.streamError({
|
|
1904
|
+
context: this.context,
|
|
1905
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1576
1908
|
}
|
|
1577
1909
|
}
|
|
1578
1910
|
async *[Symbol.asyncIterator]() {
|
|
@@ -1671,7 +2003,13 @@ var StructuredJSONStream = class {
|
|
|
1671
2003
|
this.closed = true;
|
|
1672
2004
|
try {
|
|
1673
2005
|
await this.response.body?.cancel(reason);
|
|
1674
|
-
} catch {
|
|
2006
|
+
} catch (err) {
|
|
2007
|
+
if (this.trace?.streamError) {
|
|
2008
|
+
this.trace.streamError({
|
|
2009
|
+
context: this.context,
|
|
2010
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
1675
2013
|
}
|
|
1676
2014
|
}
|
|
1677
2015
|
async *[Symbol.asyncIterator]() {
|
|
@@ -1872,7 +2210,7 @@ function mapChatEvent(raw, requestId) {
|
|
|
1872
2210
|
if (raw.data) {
|
|
1873
2211
|
try {
|
|
1874
2212
|
parsed = JSON.parse(raw.data);
|
|
1875
|
-
} catch {
|
|
2213
|
+
} catch (err) {
|
|
1876
2214
|
parsed = raw.data;
|
|
1877
2215
|
}
|
|
1878
2216
|
}
|
|
@@ -2785,6 +3123,8 @@ function resolveBaseUrl(override) {
|
|
|
2785
3123
|
ResponseFormatTypes,
|
|
2786
3124
|
SDK_VERSION,
|
|
2787
3125
|
StopReasons,
|
|
3126
|
+
StructuredDecodeError,
|
|
3127
|
+
StructuredExhaustedError,
|
|
2788
3128
|
StructuredJSONStream,
|
|
2789
3129
|
TiersClient,
|
|
2790
3130
|
ToolArgsError,
|
|
@@ -2807,6 +3147,7 @@ function resolveBaseUrl(override) {
|
|
|
2807
3147
|
createUsage,
|
|
2808
3148
|
createUserMessage,
|
|
2809
3149
|
createWebTool,
|
|
3150
|
+
defaultRetryHandler,
|
|
2810
3151
|
executeWithRetry,
|
|
2811
3152
|
firstToolCall,
|
|
2812
3153
|
formatToolErrorForModel,
|
|
@@ -2827,11 +3168,13 @@ function resolveBaseUrl(override) {
|
|
|
2827
3168
|
parseToolArgs,
|
|
2828
3169
|
parseToolArgsRaw,
|
|
2829
3170
|
respondToToolCall,
|
|
3171
|
+
responseFormatFromZod,
|
|
2830
3172
|
stopReasonToString,
|
|
2831
3173
|
toolChoiceAuto,
|
|
2832
3174
|
toolChoiceNone,
|
|
2833
3175
|
toolChoiceRequired,
|
|
2834
3176
|
toolResultMessage,
|
|
2835
3177
|
tryParseToolArgs,
|
|
3178
|
+
validateWithZod,
|
|
2836
3179
|
zodToJsonSchema
|
|
2837
3180
|
});
|