@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/dist/index.js CHANGED
@@ -340,7 +340,7 @@ function isTokenReusable(token) {
340
340
  // package.json
341
341
  var package_default = {
342
342
  name: "@modelrelay/sdk",
343
- version: "0.24.0",
343
+ version: "0.25.1",
344
344
  description: "TypeScript SDK for the ModelRelay API",
345
345
  type: "module",
346
346
  main: "dist/index.cjs",
@@ -1100,6 +1100,58 @@ async function executeWithRetry(registry, toolCalls, options = {}) {
1100
1100
  return Array.from(successfulResults.values());
1101
1101
  }
1102
1102
 
1103
+ // src/structured.ts
1104
+ var StructuredDecodeError = class extends Error {
1105
+ constructor(message, rawJson, attempt) {
1106
+ super(`structured output decode error (attempt ${attempt}): ${message}`);
1107
+ this.name = "StructuredDecodeError";
1108
+ this.rawJson = rawJson;
1109
+ this.attempt = attempt;
1110
+ }
1111
+ };
1112
+ var StructuredExhaustedError = class extends Error {
1113
+ constructor(lastRawJson, allAttempts, finalError) {
1114
+ const errorMsg = finalError.kind === "decode" ? finalError.message : finalError.issues.map((i) => i.message).join("; ");
1115
+ super(
1116
+ `structured output failed after ${allAttempts.length} attempts: ${errorMsg}`
1117
+ );
1118
+ this.name = "StructuredExhaustedError";
1119
+ this.lastRawJson = lastRawJson;
1120
+ this.allAttempts = allAttempts;
1121
+ this.finalError = finalError;
1122
+ }
1123
+ };
1124
+ var defaultRetryHandler = {
1125
+ onValidationError(_attempt, _rawJson, error, _originalMessages) {
1126
+ const errorMsg = error.kind === "decode" ? error.message : error.issues.map((i) => `${i.path ?? ""}: ${i.message}`).join("; ");
1127
+ return [
1128
+ {
1129
+ role: "user",
1130
+ content: `The previous response did not match the expected schema. Error: ${errorMsg}. Please provide a response that matches the schema exactly.`
1131
+ }
1132
+ ];
1133
+ }
1134
+ };
1135
+ function responseFormatFromZod(schema, name = "response") {
1136
+ const jsonSchema = zodToJsonSchema(schema);
1137
+ return {
1138
+ type: "json_schema",
1139
+ json_schema: {
1140
+ name,
1141
+ schema: jsonSchema,
1142
+ strict: true
1143
+ }
1144
+ };
1145
+ }
1146
+ function validateWithZod(schema, data) {
1147
+ const result = schema.safeParse(data);
1148
+ if (result.success) {
1149
+ return { success: true, data: result.data };
1150
+ }
1151
+ const errorMsg = result.error && typeof result.error === "object" && "message" in result.error ? String(result.error.message) : "validation failed";
1152
+ return { success: false, error: errorMsg };
1153
+ }
1154
+
1103
1155
  // src/chat.ts
1104
1156
  var CUSTOMER_ID_HEADER = "X-ModelRelay-Customer-Id";
1105
1157
  var REQUEST_ID_HEADER = "X-ModelRelay-Chat-Request-Id";
@@ -1297,6 +1349,158 @@ var ChatCompletionsClient = class {
1297
1349
  trace
1298
1350
  );
1299
1351
  }
1352
+ /**
1353
+ * Send a structured output request with a Zod schema.
1354
+ *
1355
+ * Auto-generates JSON schema from the Zod schema, validates the response,
1356
+ * and retries on validation failure if configured.
1357
+ *
1358
+ * @param schema - A Zod schema defining the expected response structure
1359
+ * @param params - Chat completion parameters (excluding responseFormat)
1360
+ * @param options - Request options including retry configuration
1361
+ * @returns A typed result with the parsed value
1362
+ *
1363
+ * @example
1364
+ * ```typescript
1365
+ * import { z } from 'zod';
1366
+ *
1367
+ * const PersonSchema = z.object({
1368
+ * name: z.string(),
1369
+ * age: z.number(),
1370
+ * });
1371
+ *
1372
+ * const result = await client.chat.completions.structured(
1373
+ * PersonSchema,
1374
+ * { model: "claude-sonnet-4-20250514", messages: [...] },
1375
+ * { maxRetries: 2 }
1376
+ * );
1377
+ * ```
1378
+ */
1379
+ async structured(schema, params, options = {}) {
1380
+ const {
1381
+ maxRetries = 0,
1382
+ retryHandler = defaultRetryHandler,
1383
+ schemaName,
1384
+ ...requestOptions
1385
+ } = options;
1386
+ const responseFormat = responseFormatFromZod(schema, schemaName);
1387
+ const fullParams = {
1388
+ ...params,
1389
+ responseFormat,
1390
+ stream: false
1391
+ };
1392
+ let messages = [...params.messages];
1393
+ const attempts = [];
1394
+ const maxAttempts = maxRetries + 1;
1395
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1396
+ const response = await this.create(
1397
+ { ...fullParams, messages },
1398
+ { ...requestOptions, stream: false }
1399
+ );
1400
+ const rawJson = response.content.join("");
1401
+ const requestId = response.requestId;
1402
+ try {
1403
+ const parsed = JSON.parse(rawJson);
1404
+ const validated = validateWithZod(schema, parsed);
1405
+ if (validated.success) {
1406
+ return {
1407
+ value: validated.data,
1408
+ attempts: attempt,
1409
+ requestId
1410
+ };
1411
+ }
1412
+ const error = {
1413
+ kind: "validation",
1414
+ issues: [{ message: validated.error }]
1415
+ };
1416
+ attempts.push({ attempt, rawJson, error });
1417
+ if (attempt >= maxAttempts) {
1418
+ throw new StructuredExhaustedError(rawJson, attempts, error);
1419
+ }
1420
+ const retryMessages = retryHandler.onValidationError(
1421
+ attempt,
1422
+ rawJson,
1423
+ error,
1424
+ params.messages
1425
+ );
1426
+ if (!retryMessages) {
1427
+ throw new StructuredExhaustedError(rawJson, attempts, error);
1428
+ }
1429
+ messages = [
1430
+ ...params.messages,
1431
+ { role: "assistant", content: rawJson },
1432
+ ...retryMessages
1433
+ ];
1434
+ } catch (e) {
1435
+ if (e instanceof StructuredExhaustedError) {
1436
+ throw e;
1437
+ }
1438
+ const error = {
1439
+ kind: "decode",
1440
+ message: e instanceof Error ? e.message : String(e)
1441
+ };
1442
+ attempts.push({ attempt, rawJson, error });
1443
+ if (attempt >= maxAttempts) {
1444
+ throw new StructuredExhaustedError(rawJson, attempts, error);
1445
+ }
1446
+ const retryMessages = retryHandler.onValidationError(
1447
+ attempt,
1448
+ rawJson,
1449
+ error,
1450
+ params.messages
1451
+ );
1452
+ if (!retryMessages) {
1453
+ throw new StructuredExhaustedError(rawJson, attempts, error);
1454
+ }
1455
+ messages = [
1456
+ ...params.messages,
1457
+ { role: "assistant", content: rawJson },
1458
+ ...retryMessages
1459
+ ];
1460
+ }
1461
+ }
1462
+ throw new Error(
1463
+ `Internal error: structured output loop exited unexpectedly after ${maxAttempts} attempts (this is a bug, please report it)`
1464
+ );
1465
+ }
1466
+ /**
1467
+ * Stream structured output with a Zod schema.
1468
+ *
1469
+ * Auto-generates JSON schema from the Zod schema. Note that streaming
1470
+ * does not support retries - for retry behavior, use `structured()`.
1471
+ *
1472
+ * @param schema - A Zod schema defining the expected response structure
1473
+ * @param params - Chat completion parameters (excluding responseFormat)
1474
+ * @param options - Request options
1475
+ * @returns A structured JSON stream
1476
+ *
1477
+ * @example
1478
+ * ```typescript
1479
+ * import { z } from 'zod';
1480
+ *
1481
+ * const PersonSchema = z.object({
1482
+ * name: z.string(),
1483
+ * age: z.number(),
1484
+ * });
1485
+ *
1486
+ * const stream = await client.chat.completions.streamStructured(
1487
+ * PersonSchema,
1488
+ * { model: "claude-sonnet-4-20250514", messages: [...] },
1489
+ * );
1490
+ *
1491
+ * for await (const event of stream) {
1492
+ * console.log(event.type, event.payload);
1493
+ * }
1494
+ * ```
1495
+ */
1496
+ async streamStructured(schema, params, options = {}) {
1497
+ const { schemaName, ...requestOptions } = options;
1498
+ const responseFormat = responseFormatFromZod(schema, schemaName);
1499
+ return this.streamJSON(
1500
+ { ...params, responseFormat },
1501
+ requestOptions
1502
+ );
1503
+ }
1300
1504
  };
1301
1505
  var CustomerChatClient = class {
1302
1506
  constructor(http, auth, customerId, defaultMetadata, metrics, trace) {
@@ -1461,6 +1665,123 @@ var CustomerChatClient = class {
1461
1665
  trace
1462
1666
  );
1463
1667
  }
1668
+ /**
1669
+ * Send a structured output request with a Zod schema for customer-attributed calls.
1670
+ *
1671
+ * Auto-generates JSON schema from the Zod schema, validates the response,
1672
+ * and retries on validation failure if configured.
1673
+ *
1674
+ * @param schema - A Zod schema defining the expected response structure
1675
+ * @param params - Customer chat parameters (excluding responseFormat)
1676
+ * @param options - Request options including retry configuration
1677
+ * @returns A typed result with the parsed value
1678
+ */
1679
+ async structured(schema, params, options = {}) {
1680
+ const {
1681
+ maxRetries = 0,
1682
+ retryHandler = defaultRetryHandler,
1683
+ schemaName,
1684
+ ...requestOptions
1685
+ } = options;
1686
+ const responseFormat = responseFormatFromZod(schema, schemaName);
1687
+ const fullParams = {
1688
+ ...params,
1689
+ responseFormat,
1690
+ stream: false
1691
+ };
1692
+ let messages = [...params.messages];
1693
+ const attempts = [];
1694
+ const maxAttempts = maxRetries + 1;
1695
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1696
+ const response = await this.create(
1697
+ { ...fullParams, messages },
1698
+ { ...requestOptions, stream: false }
1699
+ );
1700
+ const rawJson = response.content.join("");
1701
+ const requestId = response.requestId;
1702
+ try {
1703
+ const parsed = JSON.parse(rawJson);
1704
+ const validated = validateWithZod(schema, parsed);
1705
+ if (validated.success) {
1706
+ return {
1707
+ value: validated.data,
1708
+ attempts: attempt,
1709
+ requestId
1710
+ };
1711
+ }
1712
+ const error = {
1713
+ kind: "validation",
1714
+ issues: [{ message: validated.error }]
1715
+ };
1716
+ attempts.push({ attempt, rawJson, error });
1717
+ if (attempt >= maxAttempts) {
1718
+ throw new StructuredExhaustedError(rawJson, attempts, error);
1719
+ }
1720
+ const retryMessages = retryHandler.onValidationError(
1721
+ attempt,
1722
+ rawJson,
1723
+ error,
1724
+ params.messages
1725
+ );
1726
+ if (!retryMessages) {
1727
+ throw new StructuredExhaustedError(rawJson, attempts, error);
1728
+ }
1729
+ messages = [
1730
+ ...params.messages,
1731
+ { role: "assistant", content: rawJson },
1732
+ ...retryMessages
1733
+ ];
1734
+ } catch (e) {
1735
+ if (e instanceof StructuredExhaustedError) {
1736
+ throw e;
1737
+ }
1738
+ const error = {
1739
+ kind: "decode",
1740
+ message: e instanceof Error ? e.message : String(e)
1741
+ };
1742
+ attempts.push({ attempt, rawJson, error });
1743
+ if (attempt >= maxAttempts) {
1744
+ throw new StructuredExhaustedError(rawJson, attempts, error);
1745
+ }
1746
+ const retryMessages = retryHandler.onValidationError(
1747
+ attempt,
1748
+ rawJson,
1749
+ error,
1750
+ params.messages
1751
+ );
1752
+ if (!retryMessages) {
1753
+ throw new StructuredExhaustedError(rawJson, attempts, error);
1754
+ }
1755
+ messages = [
1756
+ ...params.messages,
1757
+ { role: "assistant", content: rawJson },
1758
+ ...retryMessages
1759
+ ];
1760
+ }
1761
+ }
1762
+ throw new Error(
1763
+ `Internal error: structured output loop exited unexpectedly after ${maxAttempts} attempts (this is a bug, please report it)`
1764
+ );
1765
+ }
1766
+ /**
1767
+ * Stream structured output with a Zod schema for customer-attributed calls.
1768
+ *
1769
+ * Auto-generates JSON schema from the Zod schema. Note that streaming
1770
+ * does not support retries - for retry behavior, use `structured()`.
1771
+ *
1772
+ * @param schema - A Zod schema defining the expected response structure
1773
+ * @param params - Customer chat parameters (excluding responseFormat)
1774
+ * @param options - Request options
1775
+ * @returns A structured JSON stream
1776
+ */
1777
+ async streamStructured(schema, params, options = {}) {
1778
+ const { schemaName, ...requestOptions } = options;
1779
+ const responseFormat = responseFormatFromZod(schema, schemaName);
1780
+ return this.streamJSON(
1781
+ { ...params, responseFormat },
1782
+ requestOptions
1783
+ );
1784
+ }
1464
1785
  };
1465
1786
  var ChatCompletionsStream = class {
1466
1787
  constructor(response, requestId, context, metrics, trace) {
@@ -1480,7 +1801,13 @@ var ChatCompletionsStream = class {
1480
1801
  this.closed = true;
1481
1802
  try {
1482
1803
  await this.response.body?.cancel(reason);
1483
- } catch {
1804
+ } catch (err) {
1805
+ if (this.trace?.streamError) {
1806
+ this.trace.streamError({
1807
+ context: this.context,
1808
+ error: err instanceof Error ? err : new Error(String(err))
1809
+ });
1810
+ }
1484
1811
  }
1485
1812
  }
1486
1813
  async *[Symbol.asyncIterator]() {
@@ -1579,7 +1906,13 @@ var StructuredJSONStream = class {
1579
1906
  this.closed = true;
1580
1907
  try {
1581
1908
  await this.response.body?.cancel(reason);
1582
- } catch {
1909
+ } catch (err) {
1910
+ if (this.trace?.streamError) {
1911
+ this.trace.streamError({
1912
+ context: this.context,
1913
+ error: err instanceof Error ? err : new Error(String(err))
1914
+ });
1915
+ }
1583
1916
  }
1584
1917
  }
1585
1918
  async *[Symbol.asyncIterator]() {
@@ -1780,7 +2113,7 @@ function mapChatEvent(raw, requestId) {
1780
2113
  if (raw.data) {
1781
2114
  try {
1782
2115
  parsed = JSON.parse(raw.data);
1783
- } catch {
2116
+ } catch (err) {
1784
2117
  parsed = raw.data;
1785
2118
  }
1786
2119
  }
@@ -2692,6 +3025,8 @@ export {
2692
3025
  ResponseFormatTypes,
2693
3026
  SDK_VERSION,
2694
3027
  StopReasons,
3028
+ StructuredDecodeError,
3029
+ StructuredExhaustedError,
2695
3030
  StructuredJSONStream,
2696
3031
  TiersClient,
2697
3032
  ToolArgsError,
@@ -2714,6 +3049,7 @@ export {
2714
3049
  createUsage,
2715
3050
  createUserMessage,
2716
3051
  createWebTool,
3052
+ defaultRetryHandler,
2717
3053
  executeWithRetry,
2718
3054
  firstToolCall,
2719
3055
  formatToolErrorForModel,
@@ -2734,11 +3070,13 @@ export {
2734
3070
  parseToolArgs,
2735
3071
  parseToolArgsRaw,
2736
3072
  respondToToolCall,
3073
+ responseFormatFromZod,
2737
3074
  stopReasonToString,
2738
3075
  toolChoiceAuto,
2739
3076
  toolChoiceNone,
2740
3077
  toolChoiceRequired,
2741
3078
  toolResultMessage,
2742
3079
  tryParseToolArgs,
3080
+ validateWithZod,
2743
3081
  zodToJsonSchema
2744
3082
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelrelay/sdk",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "description": "TypeScript SDK for the ModelRelay API",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",