@modelrelay/sdk 0.5.0 → 0.7.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.js CHANGED
@@ -218,7 +218,7 @@ function isTokenReusable(token) {
218
218
  // package.json
219
219
  var package_default = {
220
220
  name: "@modelrelay/sdk",
221
- version: "0.5.0",
221
+ version: "0.7.0",
222
222
  description: "TypeScript SDK for the ModelRelay API",
223
223
  type: "module",
224
224
  main: "dist/index.cjs",
@@ -252,15 +252,14 @@ var package_default = {
252
252
  devDependencies: {
253
253
  tsup: "^8.2.4",
254
254
  typescript: "^5.6.3",
255
- vitest: "^2.1.4"
255
+ vitest: "^2.1.4",
256
+ zod: "^3.23.0"
256
257
  }
257
258
  };
258
259
 
259
260
  // src/types.ts
260
261
  var SDK_VERSION = package_default.version || "0.0.0";
261
262
  var DEFAULT_BASE_URL = "https://api.modelrelay.ai/api/v1";
262
- var STAGING_BASE_URL = "https://api-stg.modelrelay.ai/api/v1";
263
- var SANDBOX_BASE_URL = "https://api.sandbox.modelrelay.ai/api/v1";
264
263
  var DEFAULT_CLIENT_HEADER = `modelrelay-ts/${SDK_VERSION}`;
265
264
  var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
266
265
  var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
@@ -282,7 +281,6 @@ var Providers = {
282
281
  OpenAI: "openai",
283
282
  Anthropic: "anthropic",
284
283
  Grok: "grok",
285
- OpenRouter: "openrouter",
286
284
  Echo: "echo"
287
285
  };
288
286
  var Models = {
@@ -292,11 +290,23 @@ var Models = {
292
290
  AnthropicClaude35HaikuLatest: "anthropic/claude-3-5-haiku-latest",
293
291
  AnthropicClaude35SonnetLatest: "anthropic/claude-3-5-sonnet-latest",
294
292
  AnthropicClaudeOpus45: "anthropic/claude-opus-4-5-20251101",
295
- OpenRouterClaude35Haiku: "anthropic/claude-3.5-haiku",
293
+ AnthropicClaude35Haiku: "anthropic/claude-3.5-haiku",
296
294
  Grok2: "grok-2",
297
- Grok4Fast: "grok-4-fast",
295
+ Grok4_1FastNonReasoning: "grok-4-1-fast-non-reasoning",
296
+ Grok4_1FastReasoning: "grok-4-1-fast-reasoning",
298
297
  Echo1: "echo-1"
299
298
  };
299
+ var ToolTypes = {
300
+ Function: "function",
301
+ WebSearch: "web_search",
302
+ XSearch: "x_search",
303
+ CodeExecution: "code_execution"
304
+ };
305
+ var ToolChoiceTypes = {
306
+ Auto: "auto",
307
+ Required: "required",
308
+ None: "none"
309
+ };
300
310
  function mergeMetrics(base, override) {
301
311
  if (!base && !override) return void 0;
302
312
  return {
@@ -533,7 +543,7 @@ var ChatCompletionsStream = class {
533
543
  const context = this.enrichContext(evt);
534
544
  this.context = context;
535
545
  this.trace?.streamEvent?.({ context, event: evt });
536
- if (evt.type === "message_start" || evt.type === "message_delta" || evt.type === "message_stop") {
546
+ if (evt.type === "message_start" || evt.type === "message_delta" || evt.type === "message_stop" || evt.type === "tool_use_start" || evt.type === "tool_use_delta" || evt.type === "tool_use_stop") {
537
547
  this.recordFirstToken();
538
548
  }
539
549
  if (evt.type === "message_stop" && evt.usage && this.metrics?.usage) {
@@ -618,11 +628,15 @@ function mapChatEvent(raw, requestId) {
618
628
  const model = normalizeModelId(p.model || p?.message?.model);
619
629
  const stopReason = normalizeStopReason(p.stop_reason);
620
630
  const textDelta = extractTextDelta(p);
631
+ const toolCallDelta = extractToolCallDelta(p, type);
632
+ const toolCalls = extractToolCalls(p, type);
621
633
  return {
622
634
  type,
623
635
  event: raw.event || type,
624
636
  data: p,
625
637
  textDelta,
638
+ toolCallDelta,
639
+ toolCalls,
626
640
  responseId,
627
641
  model,
628
642
  stopReason,
@@ -642,6 +656,12 @@ function normalizeEventType(eventName, payload) {
642
656
  return "message_delta";
643
657
  case "message_stop":
644
658
  return "message_stop";
659
+ case "tool_use_start":
660
+ return "tool_use_start";
661
+ case "tool_use_delta":
662
+ return "tool_use_delta";
663
+ case "tool_use_stop":
664
+ return "tool_use_stop";
645
665
  case "ping":
646
666
  return "ping";
647
667
  default:
@@ -652,6 +672,9 @@ function extractTextDelta(payload) {
652
672
  if (!payload || typeof payload !== "object") {
653
673
  return void 0;
654
674
  }
675
+ if (typeof payload.text_delta === "string" && payload.text_delta !== "") {
676
+ return payload.text_delta;
677
+ }
655
678
  if (typeof payload.delta === "string") {
656
679
  return payload.delta;
657
680
  }
@@ -665,9 +688,56 @@ function extractTextDelta(payload) {
665
688
  }
666
689
  return void 0;
667
690
  }
691
+ function extractToolCallDelta(payload, type) {
692
+ if (!payload || typeof payload !== "object") {
693
+ return void 0;
694
+ }
695
+ if (type !== "tool_use_start" && type !== "tool_use_delta") {
696
+ return void 0;
697
+ }
698
+ if (payload.tool_call_delta) {
699
+ const d = payload.tool_call_delta;
700
+ return {
701
+ index: d.index ?? 0,
702
+ id: d.id,
703
+ type: d.type,
704
+ function: d.function ? {
705
+ name: d.function.name,
706
+ arguments: d.function.arguments
707
+ } : void 0
708
+ };
709
+ }
710
+ if (typeof payload.index === "number" || payload.id || payload.name) {
711
+ return {
712
+ index: payload.index ?? 0,
713
+ id: payload.id,
714
+ type: payload.tool_type,
715
+ function: payload.name || payload.arguments ? {
716
+ name: payload.name,
717
+ arguments: payload.arguments
718
+ } : void 0
719
+ };
720
+ }
721
+ return void 0;
722
+ }
723
+ function extractToolCalls(payload, type) {
724
+ if (!payload || typeof payload !== "object") {
725
+ return void 0;
726
+ }
727
+ if (type !== "tool_use_stop" && type !== "message_stop") {
728
+ return void 0;
729
+ }
730
+ if (payload.tool_calls?.length) {
731
+ return normalizeToolCalls(payload.tool_calls);
732
+ }
733
+ if (payload.tool_call) {
734
+ return normalizeToolCalls([payload.tool_call]);
735
+ }
736
+ return void 0;
737
+ }
668
738
  function normalizeChatResponse(payload, requestId) {
669
739
  const p = payload;
670
- return {
740
+ const response = {
671
741
  id: p?.id,
672
742
  provider: normalizeProvider(p?.provider),
673
743
  content: Array.isArray(p?.content) ? p.content : p?.content ? [String(p.content)] : [],
@@ -676,6 +746,17 @@ function normalizeChatResponse(payload, requestId) {
676
746
  usage: normalizeUsage(p?.usage),
677
747
  requestId
678
748
  };
749
+ if (p?.tool_calls?.length) {
750
+ response.toolCalls = normalizeToolCalls(p.tool_calls);
751
+ }
752
+ return response;
753
+ }
754
+ function normalizeToolCalls(toolCalls) {
755
+ return toolCalls.map((tc) => ({
756
+ id: tc.id,
757
+ type: tc.type || ToolTypes.Function,
758
+ function: tc.function ? { name: tc.function.name, arguments: tc.function.arguments } : void 0
759
+ }));
679
760
  }
680
761
  function normalizeUsage(payload) {
681
762
  if (!payload) {
@@ -703,13 +784,65 @@ function buildProxyBody(params, metadata) {
703
784
  if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;
704
785
  if (params.stop?.length) body.stop = params.stop;
705
786
  if (params.stopSequences?.length) body.stop_sequences = params.stopSequences;
787
+ if (params.tools?.length) body.tools = normalizeTools(params.tools);
788
+ if (params.toolChoice) body.tool_choice = normalizeToolChoice(params.toolChoice);
706
789
  return body;
707
790
  }
708
791
  function normalizeMessages(messages) {
709
- return messages.map((msg) => ({
710
- role: msg.role || "user",
711
- content: msg.content
712
- }));
792
+ return messages.map((msg) => {
793
+ const normalized = {
794
+ role: msg.role || "user",
795
+ content: msg.content
796
+ };
797
+ if (msg.toolCalls?.length) {
798
+ normalized.tool_calls = msg.toolCalls.map((tc) => ({
799
+ id: tc.id,
800
+ type: tc.type,
801
+ function: tc.function ? { name: tc.function.name, arguments: tc.function.arguments } : void 0
802
+ }));
803
+ }
804
+ if (msg.toolCallId) {
805
+ normalized.tool_call_id = msg.toolCallId;
806
+ }
807
+ return normalized;
808
+ });
809
+ }
810
+ function normalizeTools(tools) {
811
+ return tools.map((tool) => {
812
+ const normalized = { type: tool.type };
813
+ if (tool.function) {
814
+ normalized.function = {
815
+ name: tool.function.name,
816
+ description: tool.function.description,
817
+ parameters: tool.function.parameters
818
+ };
819
+ }
820
+ if (tool.webSearch) {
821
+ normalized.web_search = {
822
+ allowed_domains: tool.webSearch.allowedDomains,
823
+ excluded_domains: tool.webSearch.excludedDomains,
824
+ max_uses: tool.webSearch.maxUses
825
+ };
826
+ }
827
+ if (tool.xSearch) {
828
+ normalized.x_search = {
829
+ allowed_handles: tool.xSearch.allowedHandles,
830
+ excluded_handles: tool.xSearch.excludedHandles,
831
+ from_date: tool.xSearch.fromDate,
832
+ to_date: tool.xSearch.toDate
833
+ };
834
+ }
835
+ if (tool.codeExecution) {
836
+ normalized.code_execution = {
837
+ language: tool.codeExecution.language,
838
+ timeout_ms: tool.codeExecution.timeoutMs
839
+ };
840
+ }
841
+ return normalized;
842
+ });
843
+ }
844
+ function normalizeToolChoice(tc) {
845
+ return { type: tc.type };
713
846
  }
714
847
  function requestIdFromHeaders(headers) {
715
848
  return headers.get(REQUEST_ID_HEADER) || headers.get("X-Request-Id") || void 0;
@@ -906,10 +1039,7 @@ var TiersClient = class {
906
1039
  // src/http.ts
907
1040
  var HTTPClient = class {
908
1041
  constructor(cfg) {
909
- const baseFromEnv = baseUrlForEnvironment(cfg.environment);
910
- const resolvedBase = normalizeBaseUrl(
911
- cfg.baseUrl || baseFromEnv || DEFAULT_BASE_URL
912
- );
1042
+ const resolvedBase = normalizeBaseUrl(cfg.baseUrl || DEFAULT_BASE_URL);
913
1043
  if (!isValidHttpUrl(resolvedBase)) {
914
1044
  throw new ConfigError(
915
1045
  "baseUrl must start with http:// or https://"
@@ -1122,12 +1252,6 @@ function normalizeBaseUrl(value) {
1122
1252
  function isValidHttpUrl(value) {
1123
1253
  return /^https?:\/\//i.test(value);
1124
1254
  }
1125
- function baseUrlForEnvironment(env) {
1126
- if (!env || env === "production") return void 0;
1127
- if (env === "staging") return STAGING_BASE_URL;
1128
- if (env === "sandbox") return SANDBOX_BASE_URL;
1129
- return void 0;
1130
- }
1131
1255
  function normalizeRetryConfig(retry) {
1132
1256
  if (retry === false) return void 0;
1133
1257
  const cfg = retry || {};
@@ -1239,6 +1363,610 @@ function withRequestId(context, headers) {
1239
1363
  return { ...context, requestId };
1240
1364
  }
1241
1365
 
1366
+ // src/tools.ts
1367
+ function zodToJsonSchema(schema, options = {}) {
1368
+ const result = convertZodType(schema);
1369
+ if (options.includeSchema) {
1370
+ const schemaVersion = options.target === "draft-04" ? "http://json-schema.org/draft-04/schema#" : options.target === "draft-2019-09" ? "https://json-schema.org/draft/2019-09/schema" : options.target === "draft-2020-12" ? "https://json-schema.org/draft/2020-12/schema" : "http://json-schema.org/draft-07/schema#";
1371
+ return { $schema: schemaVersion, ...result };
1372
+ }
1373
+ return result;
1374
+ }
1375
+ function convertZodType(schema) {
1376
+ const def = schema._def;
1377
+ const typeName = def.typeName;
1378
+ switch (typeName) {
1379
+ case "ZodString":
1380
+ return convertZodString(def);
1381
+ case "ZodNumber":
1382
+ return convertZodNumber(def);
1383
+ case "ZodBoolean":
1384
+ return { type: "boolean" };
1385
+ case "ZodNull":
1386
+ return { type: "null" };
1387
+ case "ZodArray":
1388
+ return convertZodArray(def);
1389
+ case "ZodObject":
1390
+ return convertZodObject(def);
1391
+ case "ZodEnum":
1392
+ return convertZodEnum(def);
1393
+ case "ZodNativeEnum":
1394
+ return convertZodNativeEnum(def);
1395
+ case "ZodLiteral":
1396
+ return { const: def.value };
1397
+ case "ZodUnion":
1398
+ return convertZodUnion(def);
1399
+ case "ZodOptional": {
1400
+ const inner = convertZodType(def.innerType);
1401
+ if (def.description && !inner.description) {
1402
+ inner.description = def.description;
1403
+ }
1404
+ return inner;
1405
+ }
1406
+ case "ZodNullable":
1407
+ return convertZodNullable(def);
1408
+ case "ZodDefault":
1409
+ return { ...convertZodType(def.innerType), default: def.defaultValue() };
1410
+ case "ZodEffects":
1411
+ return convertZodType(def.schema);
1412
+ case "ZodRecord":
1413
+ return convertZodRecord(def);
1414
+ case "ZodTuple":
1415
+ return convertZodTuple(def);
1416
+ case "ZodAny":
1417
+ case "ZodUnknown":
1418
+ return {};
1419
+ default:
1420
+ return {};
1421
+ }
1422
+ }
1423
+ function convertZodString(def) {
1424
+ const result = { type: "string" };
1425
+ const checks = def.checks;
1426
+ if (checks) {
1427
+ for (const check of checks) {
1428
+ switch (check.kind) {
1429
+ case "min":
1430
+ result.minLength = check.value;
1431
+ break;
1432
+ case "max":
1433
+ result.maxLength = check.value;
1434
+ break;
1435
+ case "length":
1436
+ result.minLength = check.value;
1437
+ result.maxLength = check.value;
1438
+ break;
1439
+ case "email":
1440
+ result.format = "email";
1441
+ break;
1442
+ case "url":
1443
+ result.format = "uri";
1444
+ break;
1445
+ case "uuid":
1446
+ result.format = "uuid";
1447
+ break;
1448
+ case "datetime":
1449
+ result.format = "date-time";
1450
+ break;
1451
+ case "regex":
1452
+ result.pattern = check.value.source;
1453
+ break;
1454
+ }
1455
+ }
1456
+ }
1457
+ if (def.description) {
1458
+ result.description = def.description;
1459
+ }
1460
+ return result;
1461
+ }
1462
+ function convertZodNumber(def) {
1463
+ const result = { type: "number" };
1464
+ const checks = def.checks;
1465
+ if (checks) {
1466
+ for (const check of checks) {
1467
+ switch (check.kind) {
1468
+ case "int":
1469
+ result.type = "integer";
1470
+ break;
1471
+ case "min":
1472
+ if (check.inclusive === false) {
1473
+ result.exclusiveMinimum = check.value;
1474
+ } else {
1475
+ result.minimum = check.value;
1476
+ }
1477
+ break;
1478
+ case "max":
1479
+ if (check.inclusive === false) {
1480
+ result.exclusiveMaximum = check.value;
1481
+ } else {
1482
+ result.maximum = check.value;
1483
+ }
1484
+ break;
1485
+ case "multipleOf":
1486
+ result.multipleOf = check.value;
1487
+ break;
1488
+ }
1489
+ }
1490
+ }
1491
+ if (def.description) {
1492
+ result.description = def.description;
1493
+ }
1494
+ return result;
1495
+ }
1496
+ function convertZodArray(def) {
1497
+ const result = {
1498
+ type: "array",
1499
+ items: convertZodType(def.type)
1500
+ };
1501
+ if (def.minLength !== void 0 && def.minLength !== null) {
1502
+ result.minItems = def.minLength.value;
1503
+ }
1504
+ if (def.maxLength !== void 0 && def.maxLength !== null) {
1505
+ result.maxItems = def.maxLength.value;
1506
+ }
1507
+ if (def.description) {
1508
+ result.description = def.description;
1509
+ }
1510
+ return result;
1511
+ }
1512
+ function convertZodObject(def) {
1513
+ const shape = def.shape;
1514
+ const shapeObj = typeof shape === "function" ? shape() : shape;
1515
+ const properties = {};
1516
+ const required = [];
1517
+ for (const [key, value] of Object.entries(shapeObj)) {
1518
+ properties[key] = convertZodType(value);
1519
+ const valueDef = value._def;
1520
+ const isOptional = valueDef.typeName === "ZodOptional" || valueDef.typeName === "ZodDefault" || valueDef.typeName === "ZodNullable" && valueDef.innerType?._def?.typeName === "ZodDefault";
1521
+ if (!isOptional) {
1522
+ required.push(key);
1523
+ }
1524
+ }
1525
+ const result = {
1526
+ type: "object",
1527
+ properties
1528
+ };
1529
+ if (required.length > 0) {
1530
+ result.required = required;
1531
+ }
1532
+ if (def.description) {
1533
+ result.description = def.description;
1534
+ }
1535
+ const unknownKeys = def.unknownKeys;
1536
+ if (unknownKeys === "strict") {
1537
+ result.additionalProperties = false;
1538
+ }
1539
+ return result;
1540
+ }
1541
+ function convertZodEnum(def) {
1542
+ const result = {
1543
+ type: "string",
1544
+ enum: def.values
1545
+ };
1546
+ if (def.description) {
1547
+ result.description = def.description;
1548
+ }
1549
+ return result;
1550
+ }
1551
+ function convertZodNativeEnum(def) {
1552
+ const enumValues = def.values;
1553
+ const values = Object.values(enumValues).filter(
1554
+ (v) => typeof v === "string" || typeof v === "number"
1555
+ );
1556
+ const result = { enum: values };
1557
+ if (def.description) {
1558
+ result.description = def.description;
1559
+ }
1560
+ return result;
1561
+ }
1562
+ function convertZodUnion(def) {
1563
+ const options = def.options;
1564
+ const result = {
1565
+ anyOf: options.map(convertZodType)
1566
+ };
1567
+ if (def.description) {
1568
+ result.description = def.description;
1569
+ }
1570
+ return result;
1571
+ }
1572
+ function convertZodNullable(def) {
1573
+ const inner = convertZodType(def.innerType);
1574
+ return {
1575
+ anyOf: [inner, { type: "null" }]
1576
+ };
1577
+ }
1578
+ function convertZodRecord(def) {
1579
+ const result = {
1580
+ type: "object",
1581
+ additionalProperties: convertZodType(def.valueType)
1582
+ };
1583
+ if (def.description) {
1584
+ result.description = def.description;
1585
+ }
1586
+ return result;
1587
+ }
1588
+ function convertZodTuple(def) {
1589
+ const items = def.items;
1590
+ const result = {
1591
+ type: "array",
1592
+ items: items.map(convertZodType),
1593
+ minItems: items.length,
1594
+ maxItems: items.length
1595
+ };
1596
+ if (def.description) {
1597
+ result.description = def.description;
1598
+ }
1599
+ return result;
1600
+ }
1601
+ function createFunctionToolFromSchema(name, description, schema, options) {
1602
+ const jsonSchema = zodToJsonSchema(schema, options);
1603
+ return createFunctionTool(name, description, jsonSchema);
1604
+ }
1605
+ function createFunctionTool(name, description, parameters) {
1606
+ const fn = { name, description };
1607
+ if (parameters) {
1608
+ fn.parameters = parameters;
1609
+ }
1610
+ return {
1611
+ type: ToolTypes.Function,
1612
+ function: fn
1613
+ };
1614
+ }
1615
+ function createWebSearchTool(options) {
1616
+ return {
1617
+ type: ToolTypes.WebSearch,
1618
+ webSearch: options ? {
1619
+ allowedDomains: options.allowedDomains,
1620
+ excludedDomains: options.excludedDomains,
1621
+ maxUses: options.maxUses
1622
+ } : void 0
1623
+ };
1624
+ }
1625
+ function toolChoiceAuto() {
1626
+ return { type: ToolChoiceTypes.Auto };
1627
+ }
1628
+ function toolChoiceRequired() {
1629
+ return { type: ToolChoiceTypes.Required };
1630
+ }
1631
+ function toolChoiceNone() {
1632
+ return { type: ToolChoiceTypes.None };
1633
+ }
1634
+ function hasToolCalls(response) {
1635
+ return (response.toolCalls?.length ?? 0) > 0;
1636
+ }
1637
+ function firstToolCall(response) {
1638
+ return response.toolCalls?.[0];
1639
+ }
1640
+ function toolResultMessage(toolCallId, result) {
1641
+ const content = typeof result === "string" ? result : JSON.stringify(result);
1642
+ return {
1643
+ role: "tool",
1644
+ content,
1645
+ toolCallId
1646
+ };
1647
+ }
1648
+ function respondToToolCall(call, result) {
1649
+ return toolResultMessage(call.id, result);
1650
+ }
1651
+ function assistantMessageWithToolCalls(content, toolCalls) {
1652
+ return {
1653
+ role: "assistant",
1654
+ content,
1655
+ toolCalls
1656
+ };
1657
+ }
1658
+ var ToolCallAccumulator = class {
1659
+ constructor() {
1660
+ this.calls = /* @__PURE__ */ new Map();
1661
+ }
1662
+ /**
1663
+ * Processes a streaming tool call delta.
1664
+ * Returns true if this started a new tool call.
1665
+ */
1666
+ processDelta(delta) {
1667
+ const existing = this.calls.get(delta.index);
1668
+ if (!existing) {
1669
+ this.calls.set(delta.index, {
1670
+ id: delta.id ?? "",
1671
+ type: delta.type ?? ToolTypes.Function,
1672
+ function: {
1673
+ name: delta.function?.name ?? "",
1674
+ arguments: delta.function?.arguments ?? ""
1675
+ }
1676
+ });
1677
+ return true;
1678
+ }
1679
+ if (delta.function) {
1680
+ if (delta.function.name) {
1681
+ existing.function = existing.function ?? { name: "", arguments: "" };
1682
+ existing.function.name = delta.function.name;
1683
+ }
1684
+ if (delta.function.arguments) {
1685
+ existing.function = existing.function ?? { name: "", arguments: "" };
1686
+ existing.function.arguments += delta.function.arguments;
1687
+ }
1688
+ }
1689
+ return false;
1690
+ }
1691
+ /**
1692
+ * Returns all accumulated tool calls in index order.
1693
+ */
1694
+ getToolCalls() {
1695
+ if (this.calls.size === 0) {
1696
+ return [];
1697
+ }
1698
+ const maxIdx = Math.max(...this.calls.keys());
1699
+ const result = [];
1700
+ for (let i = 0; i <= maxIdx; i++) {
1701
+ const call = this.calls.get(i);
1702
+ if (call) {
1703
+ result.push(call);
1704
+ }
1705
+ }
1706
+ return result;
1707
+ }
1708
+ /**
1709
+ * Returns a specific tool call by index, or undefined if not found.
1710
+ */
1711
+ getToolCall(index) {
1712
+ return this.calls.get(index);
1713
+ }
1714
+ /**
1715
+ * Clears all accumulated tool calls.
1716
+ */
1717
+ reset() {
1718
+ this.calls.clear();
1719
+ }
1720
+ };
1721
+ var ToolArgsError = class extends Error {
1722
+ constructor(message, toolCallId, toolName, rawArguments) {
1723
+ super(message);
1724
+ this.name = "ToolArgsError";
1725
+ this.toolCallId = toolCallId;
1726
+ this.toolName = toolName;
1727
+ this.rawArguments = rawArguments;
1728
+ }
1729
+ };
1730
+ function parseToolArgs(call, schema) {
1731
+ const toolName = call.function?.name ?? "unknown";
1732
+ const rawArgs = call.function?.arguments ?? "";
1733
+ let parsed;
1734
+ try {
1735
+ parsed = rawArgs ? JSON.parse(rawArgs) : {};
1736
+ } catch (err) {
1737
+ const message = err instanceof Error ? err.message : "Invalid JSON in arguments";
1738
+ throw new ToolArgsError(
1739
+ `Failed to parse arguments for tool '${toolName}': ${message}`,
1740
+ call.id,
1741
+ toolName,
1742
+ rawArgs
1743
+ );
1744
+ }
1745
+ try {
1746
+ return schema.parse(parsed);
1747
+ } catch (err) {
1748
+ let message;
1749
+ if (err instanceof Error) {
1750
+ const zodErr = err;
1751
+ if (zodErr.errors && Array.isArray(zodErr.errors)) {
1752
+ const issues = zodErr.errors.map((e) => {
1753
+ const path = e.path.length > 0 ? `${e.path.join(".")}: ` : "";
1754
+ return `${path}${e.message}`;
1755
+ }).join("; ");
1756
+ message = issues;
1757
+ } else {
1758
+ message = err.message;
1759
+ }
1760
+ } else {
1761
+ message = String(err);
1762
+ }
1763
+ throw new ToolArgsError(
1764
+ `Invalid arguments for tool '${toolName}': ${message}`,
1765
+ call.id,
1766
+ toolName,
1767
+ rawArgs
1768
+ );
1769
+ }
1770
+ }
1771
+ function tryParseToolArgs(call, schema) {
1772
+ try {
1773
+ const data = parseToolArgs(call, schema);
1774
+ return { success: true, data };
1775
+ } catch (err) {
1776
+ if (err instanceof ToolArgsError) {
1777
+ return { success: false, error: err };
1778
+ }
1779
+ const toolName = call.function?.name ?? "unknown";
1780
+ const rawArgs = call.function?.arguments ?? "";
1781
+ return {
1782
+ success: false,
1783
+ error: new ToolArgsError(
1784
+ err instanceof Error ? err.message : String(err),
1785
+ call.id,
1786
+ toolName,
1787
+ rawArgs
1788
+ )
1789
+ };
1790
+ }
1791
+ }
1792
+ function parseToolArgsRaw(call) {
1793
+ const toolName = call.function?.name ?? "unknown";
1794
+ const rawArgs = call.function?.arguments ?? "";
1795
+ try {
1796
+ return rawArgs ? JSON.parse(rawArgs) : {};
1797
+ } catch (err) {
1798
+ const message = err instanceof Error ? err.message : "Invalid JSON in arguments";
1799
+ throw new ToolArgsError(
1800
+ `Failed to parse arguments for tool '${toolName}': ${message}`,
1801
+ call.id,
1802
+ toolName,
1803
+ rawArgs
1804
+ );
1805
+ }
1806
+ }
1807
+ var ToolRegistry = class {
1808
+ constructor() {
1809
+ this.handlers = /* @__PURE__ */ new Map();
1810
+ }
1811
+ /**
1812
+ * Registers a handler function for a tool name.
1813
+ * @param name - The tool name (must match the function name in the tool definition)
1814
+ * @param handler - Function to execute when this tool is called
1815
+ * @returns this for chaining
1816
+ */
1817
+ register(name, handler) {
1818
+ this.handlers.set(name, handler);
1819
+ return this;
1820
+ }
1821
+ /**
1822
+ * Unregisters a tool handler.
1823
+ * @param name - The tool name to unregister
1824
+ * @returns true if the handler was removed, false if it didn't exist
1825
+ */
1826
+ unregister(name) {
1827
+ return this.handlers.delete(name);
1828
+ }
1829
+ /**
1830
+ * Checks if a handler is registered for the given tool name.
1831
+ */
1832
+ has(name) {
1833
+ return this.handlers.has(name);
1834
+ }
1835
+ /**
1836
+ * Returns the list of registered tool names.
1837
+ */
1838
+ getRegisteredTools() {
1839
+ return Array.from(this.handlers.keys());
1840
+ }
1841
+ /**
1842
+ * Executes a single tool call.
1843
+ * @param call - The tool call to execute
1844
+ * @returns The execution result
1845
+ */
1846
+ async execute(call) {
1847
+ const toolName = call.function?.name ?? "";
1848
+ const handler = this.handlers.get(toolName);
1849
+ if (!handler) {
1850
+ return {
1851
+ toolCallId: call.id,
1852
+ toolName,
1853
+ result: null,
1854
+ error: `Unknown tool: '${toolName}'. Available tools: ${this.getRegisteredTools().join(", ") || "none"}`
1855
+ };
1856
+ }
1857
+ let args;
1858
+ try {
1859
+ args = call.function?.arguments ? JSON.parse(call.function.arguments) : {};
1860
+ } catch (err) {
1861
+ const errorMessage = err instanceof Error ? err.message : String(err);
1862
+ return {
1863
+ toolCallId: call.id,
1864
+ toolName,
1865
+ result: null,
1866
+ error: `Invalid JSON in arguments: ${errorMessage}`,
1867
+ isRetryable: true
1868
+ };
1869
+ }
1870
+ try {
1871
+ const result = await handler(args, call);
1872
+ return {
1873
+ toolCallId: call.id,
1874
+ toolName,
1875
+ result
1876
+ };
1877
+ } catch (err) {
1878
+ const isRetryable = err instanceof ToolArgsError;
1879
+ const errorMessage = err instanceof Error ? err.message : String(err);
1880
+ return {
1881
+ toolCallId: call.id,
1882
+ toolName,
1883
+ result: null,
1884
+ error: errorMessage,
1885
+ isRetryable
1886
+ };
1887
+ }
1888
+ }
1889
+ /**
1890
+ * Executes multiple tool calls in parallel.
1891
+ * @param calls - Array of tool calls to execute
1892
+ * @returns Array of execution results in the same order as input
1893
+ */
1894
+ async executeAll(calls) {
1895
+ return Promise.all(calls.map((call) => this.execute(call)));
1896
+ }
1897
+ /**
1898
+ * Converts execution results to tool result messages.
1899
+ * Useful for appending to the conversation history.
1900
+ * @param results - Array of execution results
1901
+ * @returns Array of ChatMessage objects with role "tool"
1902
+ */
1903
+ resultsToMessages(results) {
1904
+ return results.map((r) => {
1905
+ const content = r.error ? `Error: ${r.error}` : typeof r.result === "string" ? r.result : JSON.stringify(r.result);
1906
+ return toolResultMessage(r.toolCallId, content);
1907
+ });
1908
+ }
1909
+ };
1910
+ function formatToolErrorForModel(result) {
1911
+ const lines = [
1912
+ `Tool call error for '${result.toolName}': ${result.error}`
1913
+ ];
1914
+ if (result.isRetryable) {
1915
+ lines.push("");
1916
+ lines.push("Please correct the arguments and try again.");
1917
+ }
1918
+ return lines.join("\n");
1919
+ }
1920
+ function hasRetryableErrors(results) {
1921
+ return results.some((r) => r.error && r.isRetryable);
1922
+ }
1923
+ function getRetryableErrors(results) {
1924
+ return results.filter((r) => r.error && r.isRetryable);
1925
+ }
1926
+ function createRetryMessages(results) {
1927
+ return results.filter((r) => r.error && r.isRetryable).map((r) => toolResultMessage(r.toolCallId, formatToolErrorForModel(r)));
1928
+ }
1929
+ async function executeWithRetry(registry, toolCalls, options = {}) {
1930
+ const maxRetries = options.maxRetries ?? 2;
1931
+ let currentCalls = toolCalls;
1932
+ let attempt = 0;
1933
+ const successfulResults = /* @__PURE__ */ new Map();
1934
+ while (attempt <= maxRetries) {
1935
+ const results = await registry.executeAll(currentCalls);
1936
+ for (const result of results) {
1937
+ if (!result.error || !result.isRetryable) {
1938
+ successfulResults.set(result.toolCallId, result);
1939
+ }
1940
+ }
1941
+ const retryableResults = getRetryableErrors(results);
1942
+ if (retryableResults.length === 0 || !options.onRetry) {
1943
+ for (const result of results) {
1944
+ if (result.error && result.isRetryable) {
1945
+ successfulResults.set(result.toolCallId, result);
1946
+ }
1947
+ }
1948
+ return Array.from(successfulResults.values());
1949
+ }
1950
+ attempt++;
1951
+ if (attempt > maxRetries) {
1952
+ for (const result of retryableResults) {
1953
+ successfulResults.set(result.toolCallId, result);
1954
+ }
1955
+ return Array.from(successfulResults.values());
1956
+ }
1957
+ const errorMessages = createRetryMessages(retryableResults);
1958
+ const newCalls = await options.onRetry(errorMessages, attempt);
1959
+ if (newCalls.length === 0) {
1960
+ for (const result of retryableResults) {
1961
+ successfulResults.set(result.toolCallId, result);
1962
+ }
1963
+ return Array.from(successfulResults.values());
1964
+ }
1965
+ currentCalls = newCalls;
1966
+ }
1967
+ return Array.from(successfulResults.values());
1968
+ }
1969
+
1242
1970
  // src/index.ts
1243
1971
  var ModelRelay = class {
1244
1972
  constructor(options) {
@@ -1246,7 +1974,7 @@ var ModelRelay = class {
1246
1974
  if (!cfg.key && !cfg.token) {
1247
1975
  throw new ConfigError("Provide an API key or access token");
1248
1976
  }
1249
- this.baseUrl = resolveBaseUrl(cfg.environment, cfg.baseUrl);
1977
+ this.baseUrl = resolveBaseUrl(cfg.baseUrl);
1250
1978
  const http = new HTTPClient({
1251
1979
  baseUrl: this.baseUrl,
1252
1980
  apiKey: cfg.key,
@@ -1257,7 +1985,6 @@ var ModelRelay = class {
1257
1985
  timeoutMs: cfg.timeoutMs,
1258
1986
  retry: cfg.retry,
1259
1987
  defaultHeaders: cfg.defaultHeaders,
1260
- environment: cfg.environment,
1261
1988
  metrics: cfg.metrics,
1262
1989
  trace: cfg.trace
1263
1990
  });
@@ -1280,8 +2007,8 @@ var ModelRelay = class {
1280
2007
  });
1281
2008
  }
1282
2009
  };
1283
- function resolveBaseUrl(env, override) {
1284
- const base = override || (env === "staging" ? STAGING_BASE_URL : env === "sandbox" ? SANDBOX_BASE_URL : DEFAULT_BASE_URL);
2010
+ function resolveBaseUrl(override) {
2011
+ const base = override || DEFAULT_BASE_URL;
1285
2012
  return base.replace(/\/+$/, "");
1286
2013
  }
1287
2014
  export {
@@ -1299,12 +2026,26 @@ export {
1299
2026
  ModelRelayError,
1300
2027
  Models,
1301
2028
  Providers,
1302
- SANDBOX_BASE_URL,
1303
2029
  SDK_VERSION,
1304
- STAGING_BASE_URL,
1305
2030
  StopReasons,
1306
2031
  TiersClient,
2032
+ ToolArgsError,
2033
+ ToolCallAccumulator,
2034
+ ToolChoiceTypes,
2035
+ ToolRegistry,
2036
+ ToolTypes,
1307
2037
  TransportError,
2038
+ assistantMessageWithToolCalls,
2039
+ createFunctionTool,
2040
+ createFunctionToolFromSchema,
2041
+ createRetryMessages,
2042
+ createWebSearchTool,
2043
+ executeWithRetry,
2044
+ firstToolCall,
2045
+ formatToolErrorForModel,
2046
+ getRetryableErrors,
2047
+ hasRetryableErrors,
2048
+ hasToolCalls,
1308
2049
  isPublishableKey,
1309
2050
  mergeMetrics,
1310
2051
  mergeTrace,
@@ -1313,6 +2054,15 @@ export {
1313
2054
  normalizeProvider,
1314
2055
  normalizeStopReason,
1315
2056
  parseErrorResponse,
2057
+ parseToolArgs,
2058
+ parseToolArgsRaw,
1316
2059
  providerToString,
1317
- stopReasonToString
2060
+ respondToToolCall,
2061
+ stopReasonToString,
2062
+ toolChoiceAuto,
2063
+ toolChoiceNone,
2064
+ toolChoiceRequired,
2065
+ toolResultMessage,
2066
+ tryParseToolArgs,
2067
+ zodToJsonSchema
1318
2068
  };