@modelrelay/sdk 0.24.0 → 0.27.0

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.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.24.0",
440
+ version: "0.27.0",
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]() {
@@ -1785,11 +2123,13 @@ var StructuredJSONStream = class {
1785
2123
  if (rawType === "completion") {
1786
2124
  this.sawTerminal = true;
1787
2125
  }
2126
+ const completeFieldsArray = Array.isArray(obj.complete_fields) ? obj.complete_fields.filter((f) => typeof f === "string") : [];
1788
2127
  const event = {
1789
2128
  type: rawType,
1790
2129
  // biome-ignore lint/suspicious/noExplicitAny: payload is untyped json
1791
2130
  payload: obj.payload,
1792
- requestId: this.requestId
2131
+ requestId: this.requestId,
2132
+ completeFields: new Set(completeFieldsArray)
1793
2133
  };
1794
2134
  return event;
1795
2135
  }
@@ -1872,7 +2212,7 @@ function mapChatEvent(raw, requestId) {
1872
2212
  if (raw.data) {
1873
2213
  try {
1874
2214
  parsed = JSON.parse(raw.data);
1875
- } catch {
2215
+ } catch (err) {
1876
2216
  parsed = raw.data;
1877
2217
  }
1878
2218
  }
@@ -2785,6 +3125,8 @@ function resolveBaseUrl(override) {
2785
3125
  ResponseFormatTypes,
2786
3126
  SDK_VERSION,
2787
3127
  StopReasons,
3128
+ StructuredDecodeError,
3129
+ StructuredExhaustedError,
2788
3130
  StructuredJSONStream,
2789
3131
  TiersClient,
2790
3132
  ToolArgsError,
@@ -2807,6 +3149,7 @@ function resolveBaseUrl(override) {
2807
3149
  createUsage,
2808
3150
  createUserMessage,
2809
3151
  createWebTool,
3152
+ defaultRetryHandler,
2810
3153
  executeWithRetry,
2811
3154
  firstToolCall,
2812
3155
  formatToolErrorForModel,
@@ -2827,11 +3170,13 @@ function resolveBaseUrl(override) {
2827
3170
  parseToolArgs,
2828
3171
  parseToolArgsRaw,
2829
3172
  respondToToolCall,
3173
+ responseFormatFromZod,
2830
3174
  stopReasonToString,
2831
3175
  toolChoiceAuto,
2832
3176
  toolChoiceNone,
2833
3177
  toolChoiceRequired,
2834
3178
  toolResultMessage,
2835
3179
  tryParseToolArgs,
3180
+ validateWithZod,
2836
3181
  zodToJsonSchema
2837
3182
  });