@modelrelay/sdk 0.4.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.cjs CHANGED
@@ -34,11 +34,26 @@ __export(index_exports, {
34
34
  ModelRelayError: () => ModelRelayError,
35
35
  Models: () => Models,
36
36
  Providers: () => Providers,
37
- SANDBOX_BASE_URL: () => SANDBOX_BASE_URL,
38
37
  SDK_VERSION: () => SDK_VERSION,
39
- STAGING_BASE_URL: () => STAGING_BASE_URL,
40
38
  StopReasons: () => StopReasons,
39
+ TiersClient: () => TiersClient,
40
+ ToolArgsError: () => ToolArgsError,
41
+ ToolCallAccumulator: () => ToolCallAccumulator,
42
+ ToolChoiceTypes: () => ToolChoiceTypes,
43
+ ToolRegistry: () => ToolRegistry,
44
+ ToolTypes: () => ToolTypes,
41
45
  TransportError: () => TransportError,
46
+ assistantMessageWithToolCalls: () => assistantMessageWithToolCalls,
47
+ createFunctionTool: () => createFunctionTool,
48
+ createFunctionToolFromSchema: () => createFunctionToolFromSchema,
49
+ createRetryMessages: () => createRetryMessages,
50
+ createWebSearchTool: () => createWebSearchTool,
51
+ executeWithRetry: () => executeWithRetry,
52
+ firstToolCall: () => firstToolCall,
53
+ formatToolErrorForModel: () => formatToolErrorForModel,
54
+ getRetryableErrors: () => getRetryableErrors,
55
+ hasRetryableErrors: () => hasRetryableErrors,
56
+ hasToolCalls: () => hasToolCalls,
42
57
  isPublishableKey: () => isPublishableKey,
43
58
  mergeMetrics: () => mergeMetrics,
44
59
  mergeTrace: () => mergeTrace,
@@ -47,8 +62,17 @@ __export(index_exports, {
47
62
  normalizeProvider: () => normalizeProvider,
48
63
  normalizeStopReason: () => normalizeStopReason,
49
64
  parseErrorResponse: () => parseErrorResponse,
65
+ parseToolArgs: () => parseToolArgs,
66
+ parseToolArgsRaw: () => parseToolArgsRaw,
50
67
  providerToString: () => providerToString,
51
- stopReasonToString: () => stopReasonToString
68
+ respondToToolCall: () => respondToToolCall,
69
+ stopReasonToString: () => stopReasonToString,
70
+ toolChoiceAuto: () => toolChoiceAuto,
71
+ toolChoiceNone: () => toolChoiceNone,
72
+ toolChoiceRequired: () => toolChoiceRequired,
73
+ toolResultMessage: () => toolResultMessage,
74
+ tryParseToolArgs: () => tryParseToolArgs,
75
+ zodToJsonSchema: () => zodToJsonSchema
52
76
  });
53
77
  module.exports = __toCommonJS(index_exports);
54
78
 
@@ -272,7 +296,7 @@ function isTokenReusable(token) {
272
296
  // package.json
273
297
  var package_default = {
274
298
  name: "@modelrelay/sdk",
275
- version: "0.4.0",
299
+ version: "0.7.0",
276
300
  description: "TypeScript SDK for the ModelRelay API",
277
301
  type: "module",
278
302
  main: "dist/index.cjs",
@@ -306,15 +330,14 @@ var package_default = {
306
330
  devDependencies: {
307
331
  tsup: "^8.2.4",
308
332
  typescript: "^5.6.3",
309
- vitest: "^2.1.4"
333
+ vitest: "^2.1.4",
334
+ zod: "^3.23.0"
310
335
  }
311
336
  };
312
337
 
313
338
  // src/types.ts
314
339
  var SDK_VERSION = package_default.version || "0.0.0";
315
340
  var DEFAULT_BASE_URL = "https://api.modelrelay.ai/api/v1";
316
- var STAGING_BASE_URL = "https://api-stg.modelrelay.ai/api/v1";
317
- var SANDBOX_BASE_URL = "https://api.sandbox.modelrelay.ai/api/v1";
318
341
  var DEFAULT_CLIENT_HEADER = `modelrelay-ts/${SDK_VERSION}`;
319
342
  var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
320
343
  var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
@@ -336,7 +359,6 @@ var Providers = {
336
359
  OpenAI: "openai",
337
360
  Anthropic: "anthropic",
338
361
  Grok: "grok",
339
- OpenRouter: "openrouter",
340
362
  Echo: "echo"
341
363
  };
342
364
  var Models = {
@@ -346,11 +368,23 @@ var Models = {
346
368
  AnthropicClaude35HaikuLatest: "anthropic/claude-3-5-haiku-latest",
347
369
  AnthropicClaude35SonnetLatest: "anthropic/claude-3-5-sonnet-latest",
348
370
  AnthropicClaudeOpus45: "anthropic/claude-opus-4-5-20251101",
349
- OpenRouterClaude35Haiku: "anthropic/claude-3.5-haiku",
371
+ AnthropicClaude35Haiku: "anthropic/claude-3.5-haiku",
350
372
  Grok2: "grok-2",
351
- Grok4Fast: "grok-4-fast",
373
+ Grok4_1FastNonReasoning: "grok-4-1-fast-non-reasoning",
374
+ Grok4_1FastReasoning: "grok-4-1-fast-reasoning",
352
375
  Echo1: "echo-1"
353
376
  };
377
+ var ToolTypes = {
378
+ Function: "function",
379
+ WebSearch: "web_search",
380
+ XSearch: "x_search",
381
+ CodeExecution: "code_execution"
382
+ };
383
+ var ToolChoiceTypes = {
384
+ Auto: "auto",
385
+ Required: "required",
386
+ None: "none"
387
+ };
354
388
  function mergeMetrics(base, override) {
355
389
  if (!base && !override) return void 0;
356
390
  return {
@@ -587,7 +621,7 @@ var ChatCompletionsStream = class {
587
621
  const context = this.enrichContext(evt);
588
622
  this.context = context;
589
623
  this.trace?.streamEvent?.({ context, event: evt });
590
- if (evt.type === "message_start" || evt.type === "message_delta" || evt.type === "message_stop") {
624
+ 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") {
591
625
  this.recordFirstToken();
592
626
  }
593
627
  if (evt.type === "message_stop" && evt.usage && this.metrics?.usage) {
@@ -672,11 +706,15 @@ function mapChatEvent(raw, requestId) {
672
706
  const model = normalizeModelId(p.model || p?.message?.model);
673
707
  const stopReason = normalizeStopReason(p.stop_reason);
674
708
  const textDelta = extractTextDelta(p);
709
+ const toolCallDelta = extractToolCallDelta(p, type);
710
+ const toolCalls = extractToolCalls(p, type);
675
711
  return {
676
712
  type,
677
713
  event: raw.event || type,
678
714
  data: p,
679
715
  textDelta,
716
+ toolCallDelta,
717
+ toolCalls,
680
718
  responseId,
681
719
  model,
682
720
  stopReason,
@@ -696,6 +734,12 @@ function normalizeEventType(eventName, payload) {
696
734
  return "message_delta";
697
735
  case "message_stop":
698
736
  return "message_stop";
737
+ case "tool_use_start":
738
+ return "tool_use_start";
739
+ case "tool_use_delta":
740
+ return "tool_use_delta";
741
+ case "tool_use_stop":
742
+ return "tool_use_stop";
699
743
  case "ping":
700
744
  return "ping";
701
745
  default:
@@ -706,6 +750,9 @@ function extractTextDelta(payload) {
706
750
  if (!payload || typeof payload !== "object") {
707
751
  return void 0;
708
752
  }
753
+ if (typeof payload.text_delta === "string" && payload.text_delta !== "") {
754
+ return payload.text_delta;
755
+ }
709
756
  if (typeof payload.delta === "string") {
710
757
  return payload.delta;
711
758
  }
@@ -719,9 +766,56 @@ function extractTextDelta(payload) {
719
766
  }
720
767
  return void 0;
721
768
  }
769
+ function extractToolCallDelta(payload, type) {
770
+ if (!payload || typeof payload !== "object") {
771
+ return void 0;
772
+ }
773
+ if (type !== "tool_use_start" && type !== "tool_use_delta") {
774
+ return void 0;
775
+ }
776
+ if (payload.tool_call_delta) {
777
+ const d = payload.tool_call_delta;
778
+ return {
779
+ index: d.index ?? 0,
780
+ id: d.id,
781
+ type: d.type,
782
+ function: d.function ? {
783
+ name: d.function.name,
784
+ arguments: d.function.arguments
785
+ } : void 0
786
+ };
787
+ }
788
+ if (typeof payload.index === "number" || payload.id || payload.name) {
789
+ return {
790
+ index: payload.index ?? 0,
791
+ id: payload.id,
792
+ type: payload.tool_type,
793
+ function: payload.name || payload.arguments ? {
794
+ name: payload.name,
795
+ arguments: payload.arguments
796
+ } : void 0
797
+ };
798
+ }
799
+ return void 0;
800
+ }
801
+ function extractToolCalls(payload, type) {
802
+ if (!payload || typeof payload !== "object") {
803
+ return void 0;
804
+ }
805
+ if (type !== "tool_use_stop" && type !== "message_stop") {
806
+ return void 0;
807
+ }
808
+ if (payload.tool_calls?.length) {
809
+ return normalizeToolCalls(payload.tool_calls);
810
+ }
811
+ if (payload.tool_call) {
812
+ return normalizeToolCalls([payload.tool_call]);
813
+ }
814
+ return void 0;
815
+ }
722
816
  function normalizeChatResponse(payload, requestId) {
723
817
  const p = payload;
724
- return {
818
+ const response = {
725
819
  id: p?.id,
726
820
  provider: normalizeProvider(p?.provider),
727
821
  content: Array.isArray(p?.content) ? p.content : p?.content ? [String(p.content)] : [],
@@ -730,6 +824,17 @@ function normalizeChatResponse(payload, requestId) {
730
824
  usage: normalizeUsage(p?.usage),
731
825
  requestId
732
826
  };
827
+ if (p?.tool_calls?.length) {
828
+ response.toolCalls = normalizeToolCalls(p.tool_calls);
829
+ }
830
+ return response;
831
+ }
832
+ function normalizeToolCalls(toolCalls) {
833
+ return toolCalls.map((tc) => ({
834
+ id: tc.id,
835
+ type: tc.type || ToolTypes.Function,
836
+ function: tc.function ? { name: tc.function.name, arguments: tc.function.arguments } : void 0
837
+ }));
733
838
  }
734
839
  function normalizeUsage(payload) {
735
840
  if (!payload) {
@@ -757,13 +862,65 @@ function buildProxyBody(params, metadata) {
757
862
  if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;
758
863
  if (params.stop?.length) body.stop = params.stop;
759
864
  if (params.stopSequences?.length) body.stop_sequences = params.stopSequences;
865
+ if (params.tools?.length) body.tools = normalizeTools(params.tools);
866
+ if (params.toolChoice) body.tool_choice = normalizeToolChoice(params.toolChoice);
760
867
  return body;
761
868
  }
762
869
  function normalizeMessages(messages) {
763
- return messages.map((msg) => ({
764
- role: msg.role || "user",
765
- content: msg.content
766
- }));
870
+ return messages.map((msg) => {
871
+ const normalized = {
872
+ role: msg.role || "user",
873
+ content: msg.content
874
+ };
875
+ if (msg.toolCalls?.length) {
876
+ normalized.tool_calls = msg.toolCalls.map((tc) => ({
877
+ id: tc.id,
878
+ type: tc.type,
879
+ function: tc.function ? { name: tc.function.name, arguments: tc.function.arguments } : void 0
880
+ }));
881
+ }
882
+ if (msg.toolCallId) {
883
+ normalized.tool_call_id = msg.toolCallId;
884
+ }
885
+ return normalized;
886
+ });
887
+ }
888
+ function normalizeTools(tools) {
889
+ return tools.map((tool) => {
890
+ const normalized = { type: tool.type };
891
+ if (tool.function) {
892
+ normalized.function = {
893
+ name: tool.function.name,
894
+ description: tool.function.description,
895
+ parameters: tool.function.parameters
896
+ };
897
+ }
898
+ if (tool.webSearch) {
899
+ normalized.web_search = {
900
+ allowed_domains: tool.webSearch.allowedDomains,
901
+ excluded_domains: tool.webSearch.excludedDomains,
902
+ max_uses: tool.webSearch.maxUses
903
+ };
904
+ }
905
+ if (tool.xSearch) {
906
+ normalized.x_search = {
907
+ allowed_handles: tool.xSearch.allowedHandles,
908
+ excluded_handles: tool.xSearch.excludedHandles,
909
+ from_date: tool.xSearch.fromDate,
910
+ to_date: tool.xSearch.toDate
911
+ };
912
+ }
913
+ if (tool.codeExecution) {
914
+ normalized.code_execution = {
915
+ language: tool.codeExecution.language,
916
+ timeout_ms: tool.codeExecution.timeoutMs
917
+ };
918
+ }
919
+ return normalized;
920
+ });
921
+ }
922
+ function normalizeToolChoice(tc) {
923
+ return { type: tc.type };
767
924
  }
768
925
  function requestIdFromHeaders(headers) {
769
926
  return headers.get(REQUEST_ID_HEADER) || headers.get("X-Request-Id") || void 0;
@@ -917,13 +1074,50 @@ var CustomersClient = class {
917
1074
  }
918
1075
  };
919
1076
 
1077
+ // src/tiers.ts
1078
+ var TiersClient = class {
1079
+ constructor(http, cfg) {
1080
+ this.http = http;
1081
+ this.apiKey = cfg.apiKey;
1082
+ }
1083
+ ensureApiKey() {
1084
+ if (!this.apiKey || !this.apiKey.startsWith("mr_pk_") && !this.apiKey.startsWith("mr_sk_")) {
1085
+ throw new ConfigError(
1086
+ "API key (mr_pk_* or mr_sk_*) required for tier operations"
1087
+ );
1088
+ }
1089
+ }
1090
+ /**
1091
+ * List all tiers in the project.
1092
+ */
1093
+ async list() {
1094
+ this.ensureApiKey();
1095
+ const response = await this.http.json("/tiers", {
1096
+ method: "GET",
1097
+ apiKey: this.apiKey
1098
+ });
1099
+ return response.tiers;
1100
+ }
1101
+ /**
1102
+ * Get a tier by ID.
1103
+ */
1104
+ async get(tierId) {
1105
+ this.ensureApiKey();
1106
+ if (!tierId?.trim()) {
1107
+ throw new ConfigError("tierId is required");
1108
+ }
1109
+ const response = await this.http.json(`/tiers/${tierId}`, {
1110
+ method: "GET",
1111
+ apiKey: this.apiKey
1112
+ });
1113
+ return response.tier;
1114
+ }
1115
+ };
1116
+
920
1117
  // src/http.ts
921
1118
  var HTTPClient = class {
922
1119
  constructor(cfg) {
923
- const baseFromEnv = baseUrlForEnvironment(cfg.environment);
924
- const resolvedBase = normalizeBaseUrl(
925
- cfg.baseUrl || baseFromEnv || DEFAULT_BASE_URL
926
- );
1120
+ const resolvedBase = normalizeBaseUrl(cfg.baseUrl || DEFAULT_BASE_URL);
927
1121
  if (!isValidHttpUrl(resolvedBase)) {
928
1122
  throw new ConfigError(
929
1123
  "baseUrl must start with http:// or https://"
@@ -1136,12 +1330,6 @@ function normalizeBaseUrl(value) {
1136
1330
  function isValidHttpUrl(value) {
1137
1331
  return /^https?:\/\//i.test(value);
1138
1332
  }
1139
- function baseUrlForEnvironment(env) {
1140
- if (!env || env === "production") return void 0;
1141
- if (env === "staging") return STAGING_BASE_URL;
1142
- if (env === "sandbox") return SANDBOX_BASE_URL;
1143
- return void 0;
1144
- }
1145
1333
  function normalizeRetryConfig(retry) {
1146
1334
  if (retry === false) return void 0;
1147
1335
  const cfg = retry || {};
@@ -1253,6 +1441,610 @@ function withRequestId(context, headers) {
1253
1441
  return { ...context, requestId };
1254
1442
  }
1255
1443
 
1444
+ // src/tools.ts
1445
+ function zodToJsonSchema(schema, options = {}) {
1446
+ const result = convertZodType(schema);
1447
+ if (options.includeSchema) {
1448
+ 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#";
1449
+ return { $schema: schemaVersion, ...result };
1450
+ }
1451
+ return result;
1452
+ }
1453
+ function convertZodType(schema) {
1454
+ const def = schema._def;
1455
+ const typeName = def.typeName;
1456
+ switch (typeName) {
1457
+ case "ZodString":
1458
+ return convertZodString(def);
1459
+ case "ZodNumber":
1460
+ return convertZodNumber(def);
1461
+ case "ZodBoolean":
1462
+ return { type: "boolean" };
1463
+ case "ZodNull":
1464
+ return { type: "null" };
1465
+ case "ZodArray":
1466
+ return convertZodArray(def);
1467
+ case "ZodObject":
1468
+ return convertZodObject(def);
1469
+ case "ZodEnum":
1470
+ return convertZodEnum(def);
1471
+ case "ZodNativeEnum":
1472
+ return convertZodNativeEnum(def);
1473
+ case "ZodLiteral":
1474
+ return { const: def.value };
1475
+ case "ZodUnion":
1476
+ return convertZodUnion(def);
1477
+ case "ZodOptional": {
1478
+ const inner = convertZodType(def.innerType);
1479
+ if (def.description && !inner.description) {
1480
+ inner.description = def.description;
1481
+ }
1482
+ return inner;
1483
+ }
1484
+ case "ZodNullable":
1485
+ return convertZodNullable(def);
1486
+ case "ZodDefault":
1487
+ return { ...convertZodType(def.innerType), default: def.defaultValue() };
1488
+ case "ZodEffects":
1489
+ return convertZodType(def.schema);
1490
+ case "ZodRecord":
1491
+ return convertZodRecord(def);
1492
+ case "ZodTuple":
1493
+ return convertZodTuple(def);
1494
+ case "ZodAny":
1495
+ case "ZodUnknown":
1496
+ return {};
1497
+ default:
1498
+ return {};
1499
+ }
1500
+ }
1501
+ function convertZodString(def) {
1502
+ const result = { type: "string" };
1503
+ const checks = def.checks;
1504
+ if (checks) {
1505
+ for (const check of checks) {
1506
+ switch (check.kind) {
1507
+ case "min":
1508
+ result.minLength = check.value;
1509
+ break;
1510
+ case "max":
1511
+ result.maxLength = check.value;
1512
+ break;
1513
+ case "length":
1514
+ result.minLength = check.value;
1515
+ result.maxLength = check.value;
1516
+ break;
1517
+ case "email":
1518
+ result.format = "email";
1519
+ break;
1520
+ case "url":
1521
+ result.format = "uri";
1522
+ break;
1523
+ case "uuid":
1524
+ result.format = "uuid";
1525
+ break;
1526
+ case "datetime":
1527
+ result.format = "date-time";
1528
+ break;
1529
+ case "regex":
1530
+ result.pattern = check.value.source;
1531
+ break;
1532
+ }
1533
+ }
1534
+ }
1535
+ if (def.description) {
1536
+ result.description = def.description;
1537
+ }
1538
+ return result;
1539
+ }
1540
+ function convertZodNumber(def) {
1541
+ const result = { type: "number" };
1542
+ const checks = def.checks;
1543
+ if (checks) {
1544
+ for (const check of checks) {
1545
+ switch (check.kind) {
1546
+ case "int":
1547
+ result.type = "integer";
1548
+ break;
1549
+ case "min":
1550
+ if (check.inclusive === false) {
1551
+ result.exclusiveMinimum = check.value;
1552
+ } else {
1553
+ result.minimum = check.value;
1554
+ }
1555
+ break;
1556
+ case "max":
1557
+ if (check.inclusive === false) {
1558
+ result.exclusiveMaximum = check.value;
1559
+ } else {
1560
+ result.maximum = check.value;
1561
+ }
1562
+ break;
1563
+ case "multipleOf":
1564
+ result.multipleOf = check.value;
1565
+ break;
1566
+ }
1567
+ }
1568
+ }
1569
+ if (def.description) {
1570
+ result.description = def.description;
1571
+ }
1572
+ return result;
1573
+ }
1574
+ function convertZodArray(def) {
1575
+ const result = {
1576
+ type: "array",
1577
+ items: convertZodType(def.type)
1578
+ };
1579
+ if (def.minLength !== void 0 && def.minLength !== null) {
1580
+ result.minItems = def.minLength.value;
1581
+ }
1582
+ if (def.maxLength !== void 0 && def.maxLength !== null) {
1583
+ result.maxItems = def.maxLength.value;
1584
+ }
1585
+ if (def.description) {
1586
+ result.description = def.description;
1587
+ }
1588
+ return result;
1589
+ }
1590
+ function convertZodObject(def) {
1591
+ const shape = def.shape;
1592
+ const shapeObj = typeof shape === "function" ? shape() : shape;
1593
+ const properties = {};
1594
+ const required = [];
1595
+ for (const [key, value] of Object.entries(shapeObj)) {
1596
+ properties[key] = convertZodType(value);
1597
+ const valueDef = value._def;
1598
+ const isOptional = valueDef.typeName === "ZodOptional" || valueDef.typeName === "ZodDefault" || valueDef.typeName === "ZodNullable" && valueDef.innerType?._def?.typeName === "ZodDefault";
1599
+ if (!isOptional) {
1600
+ required.push(key);
1601
+ }
1602
+ }
1603
+ const result = {
1604
+ type: "object",
1605
+ properties
1606
+ };
1607
+ if (required.length > 0) {
1608
+ result.required = required;
1609
+ }
1610
+ if (def.description) {
1611
+ result.description = def.description;
1612
+ }
1613
+ const unknownKeys = def.unknownKeys;
1614
+ if (unknownKeys === "strict") {
1615
+ result.additionalProperties = false;
1616
+ }
1617
+ return result;
1618
+ }
1619
+ function convertZodEnum(def) {
1620
+ const result = {
1621
+ type: "string",
1622
+ enum: def.values
1623
+ };
1624
+ if (def.description) {
1625
+ result.description = def.description;
1626
+ }
1627
+ return result;
1628
+ }
1629
+ function convertZodNativeEnum(def) {
1630
+ const enumValues = def.values;
1631
+ const values = Object.values(enumValues).filter(
1632
+ (v) => typeof v === "string" || typeof v === "number"
1633
+ );
1634
+ const result = { enum: values };
1635
+ if (def.description) {
1636
+ result.description = def.description;
1637
+ }
1638
+ return result;
1639
+ }
1640
+ function convertZodUnion(def) {
1641
+ const options = def.options;
1642
+ const result = {
1643
+ anyOf: options.map(convertZodType)
1644
+ };
1645
+ if (def.description) {
1646
+ result.description = def.description;
1647
+ }
1648
+ return result;
1649
+ }
1650
+ function convertZodNullable(def) {
1651
+ const inner = convertZodType(def.innerType);
1652
+ return {
1653
+ anyOf: [inner, { type: "null" }]
1654
+ };
1655
+ }
1656
+ function convertZodRecord(def) {
1657
+ const result = {
1658
+ type: "object",
1659
+ additionalProperties: convertZodType(def.valueType)
1660
+ };
1661
+ if (def.description) {
1662
+ result.description = def.description;
1663
+ }
1664
+ return result;
1665
+ }
1666
+ function convertZodTuple(def) {
1667
+ const items = def.items;
1668
+ const result = {
1669
+ type: "array",
1670
+ items: items.map(convertZodType),
1671
+ minItems: items.length,
1672
+ maxItems: items.length
1673
+ };
1674
+ if (def.description) {
1675
+ result.description = def.description;
1676
+ }
1677
+ return result;
1678
+ }
1679
+ function createFunctionToolFromSchema(name, description, schema, options) {
1680
+ const jsonSchema = zodToJsonSchema(schema, options);
1681
+ return createFunctionTool(name, description, jsonSchema);
1682
+ }
1683
+ function createFunctionTool(name, description, parameters) {
1684
+ const fn = { name, description };
1685
+ if (parameters) {
1686
+ fn.parameters = parameters;
1687
+ }
1688
+ return {
1689
+ type: ToolTypes.Function,
1690
+ function: fn
1691
+ };
1692
+ }
1693
+ function createWebSearchTool(options) {
1694
+ return {
1695
+ type: ToolTypes.WebSearch,
1696
+ webSearch: options ? {
1697
+ allowedDomains: options.allowedDomains,
1698
+ excludedDomains: options.excludedDomains,
1699
+ maxUses: options.maxUses
1700
+ } : void 0
1701
+ };
1702
+ }
1703
+ function toolChoiceAuto() {
1704
+ return { type: ToolChoiceTypes.Auto };
1705
+ }
1706
+ function toolChoiceRequired() {
1707
+ return { type: ToolChoiceTypes.Required };
1708
+ }
1709
+ function toolChoiceNone() {
1710
+ return { type: ToolChoiceTypes.None };
1711
+ }
1712
+ function hasToolCalls(response) {
1713
+ return (response.toolCalls?.length ?? 0) > 0;
1714
+ }
1715
+ function firstToolCall(response) {
1716
+ return response.toolCalls?.[0];
1717
+ }
1718
+ function toolResultMessage(toolCallId, result) {
1719
+ const content = typeof result === "string" ? result : JSON.stringify(result);
1720
+ return {
1721
+ role: "tool",
1722
+ content,
1723
+ toolCallId
1724
+ };
1725
+ }
1726
+ function respondToToolCall(call, result) {
1727
+ return toolResultMessage(call.id, result);
1728
+ }
1729
+ function assistantMessageWithToolCalls(content, toolCalls) {
1730
+ return {
1731
+ role: "assistant",
1732
+ content,
1733
+ toolCalls
1734
+ };
1735
+ }
1736
+ var ToolCallAccumulator = class {
1737
+ constructor() {
1738
+ this.calls = /* @__PURE__ */ new Map();
1739
+ }
1740
+ /**
1741
+ * Processes a streaming tool call delta.
1742
+ * Returns true if this started a new tool call.
1743
+ */
1744
+ processDelta(delta) {
1745
+ const existing = this.calls.get(delta.index);
1746
+ if (!existing) {
1747
+ this.calls.set(delta.index, {
1748
+ id: delta.id ?? "",
1749
+ type: delta.type ?? ToolTypes.Function,
1750
+ function: {
1751
+ name: delta.function?.name ?? "",
1752
+ arguments: delta.function?.arguments ?? ""
1753
+ }
1754
+ });
1755
+ return true;
1756
+ }
1757
+ if (delta.function) {
1758
+ if (delta.function.name) {
1759
+ existing.function = existing.function ?? { name: "", arguments: "" };
1760
+ existing.function.name = delta.function.name;
1761
+ }
1762
+ if (delta.function.arguments) {
1763
+ existing.function = existing.function ?? { name: "", arguments: "" };
1764
+ existing.function.arguments += delta.function.arguments;
1765
+ }
1766
+ }
1767
+ return false;
1768
+ }
1769
+ /**
1770
+ * Returns all accumulated tool calls in index order.
1771
+ */
1772
+ getToolCalls() {
1773
+ if (this.calls.size === 0) {
1774
+ return [];
1775
+ }
1776
+ const maxIdx = Math.max(...this.calls.keys());
1777
+ const result = [];
1778
+ for (let i = 0; i <= maxIdx; i++) {
1779
+ const call = this.calls.get(i);
1780
+ if (call) {
1781
+ result.push(call);
1782
+ }
1783
+ }
1784
+ return result;
1785
+ }
1786
+ /**
1787
+ * Returns a specific tool call by index, or undefined if not found.
1788
+ */
1789
+ getToolCall(index) {
1790
+ return this.calls.get(index);
1791
+ }
1792
+ /**
1793
+ * Clears all accumulated tool calls.
1794
+ */
1795
+ reset() {
1796
+ this.calls.clear();
1797
+ }
1798
+ };
1799
+ var ToolArgsError = class extends Error {
1800
+ constructor(message, toolCallId, toolName, rawArguments) {
1801
+ super(message);
1802
+ this.name = "ToolArgsError";
1803
+ this.toolCallId = toolCallId;
1804
+ this.toolName = toolName;
1805
+ this.rawArguments = rawArguments;
1806
+ }
1807
+ };
1808
+ function parseToolArgs(call, schema) {
1809
+ const toolName = call.function?.name ?? "unknown";
1810
+ const rawArgs = call.function?.arguments ?? "";
1811
+ let parsed;
1812
+ try {
1813
+ parsed = rawArgs ? JSON.parse(rawArgs) : {};
1814
+ } catch (err) {
1815
+ const message = err instanceof Error ? err.message : "Invalid JSON in arguments";
1816
+ throw new ToolArgsError(
1817
+ `Failed to parse arguments for tool '${toolName}': ${message}`,
1818
+ call.id,
1819
+ toolName,
1820
+ rawArgs
1821
+ );
1822
+ }
1823
+ try {
1824
+ return schema.parse(parsed);
1825
+ } catch (err) {
1826
+ let message;
1827
+ if (err instanceof Error) {
1828
+ const zodErr = err;
1829
+ if (zodErr.errors && Array.isArray(zodErr.errors)) {
1830
+ const issues = zodErr.errors.map((e) => {
1831
+ const path = e.path.length > 0 ? `${e.path.join(".")}: ` : "";
1832
+ return `${path}${e.message}`;
1833
+ }).join("; ");
1834
+ message = issues;
1835
+ } else {
1836
+ message = err.message;
1837
+ }
1838
+ } else {
1839
+ message = String(err);
1840
+ }
1841
+ throw new ToolArgsError(
1842
+ `Invalid arguments for tool '${toolName}': ${message}`,
1843
+ call.id,
1844
+ toolName,
1845
+ rawArgs
1846
+ );
1847
+ }
1848
+ }
1849
+ function tryParseToolArgs(call, schema) {
1850
+ try {
1851
+ const data = parseToolArgs(call, schema);
1852
+ return { success: true, data };
1853
+ } catch (err) {
1854
+ if (err instanceof ToolArgsError) {
1855
+ return { success: false, error: err };
1856
+ }
1857
+ const toolName = call.function?.name ?? "unknown";
1858
+ const rawArgs = call.function?.arguments ?? "";
1859
+ return {
1860
+ success: false,
1861
+ error: new ToolArgsError(
1862
+ err instanceof Error ? err.message : String(err),
1863
+ call.id,
1864
+ toolName,
1865
+ rawArgs
1866
+ )
1867
+ };
1868
+ }
1869
+ }
1870
+ function parseToolArgsRaw(call) {
1871
+ const toolName = call.function?.name ?? "unknown";
1872
+ const rawArgs = call.function?.arguments ?? "";
1873
+ try {
1874
+ return rawArgs ? JSON.parse(rawArgs) : {};
1875
+ } catch (err) {
1876
+ const message = err instanceof Error ? err.message : "Invalid JSON in arguments";
1877
+ throw new ToolArgsError(
1878
+ `Failed to parse arguments for tool '${toolName}': ${message}`,
1879
+ call.id,
1880
+ toolName,
1881
+ rawArgs
1882
+ );
1883
+ }
1884
+ }
1885
+ var ToolRegistry = class {
1886
+ constructor() {
1887
+ this.handlers = /* @__PURE__ */ new Map();
1888
+ }
1889
+ /**
1890
+ * Registers a handler function for a tool name.
1891
+ * @param name - The tool name (must match the function name in the tool definition)
1892
+ * @param handler - Function to execute when this tool is called
1893
+ * @returns this for chaining
1894
+ */
1895
+ register(name, handler) {
1896
+ this.handlers.set(name, handler);
1897
+ return this;
1898
+ }
1899
+ /**
1900
+ * Unregisters a tool handler.
1901
+ * @param name - The tool name to unregister
1902
+ * @returns true if the handler was removed, false if it didn't exist
1903
+ */
1904
+ unregister(name) {
1905
+ return this.handlers.delete(name);
1906
+ }
1907
+ /**
1908
+ * Checks if a handler is registered for the given tool name.
1909
+ */
1910
+ has(name) {
1911
+ return this.handlers.has(name);
1912
+ }
1913
+ /**
1914
+ * Returns the list of registered tool names.
1915
+ */
1916
+ getRegisteredTools() {
1917
+ return Array.from(this.handlers.keys());
1918
+ }
1919
+ /**
1920
+ * Executes a single tool call.
1921
+ * @param call - The tool call to execute
1922
+ * @returns The execution result
1923
+ */
1924
+ async execute(call) {
1925
+ const toolName = call.function?.name ?? "";
1926
+ const handler = this.handlers.get(toolName);
1927
+ if (!handler) {
1928
+ return {
1929
+ toolCallId: call.id,
1930
+ toolName,
1931
+ result: null,
1932
+ error: `Unknown tool: '${toolName}'. Available tools: ${this.getRegisteredTools().join(", ") || "none"}`
1933
+ };
1934
+ }
1935
+ let args;
1936
+ try {
1937
+ args = call.function?.arguments ? JSON.parse(call.function.arguments) : {};
1938
+ } catch (err) {
1939
+ const errorMessage = err instanceof Error ? err.message : String(err);
1940
+ return {
1941
+ toolCallId: call.id,
1942
+ toolName,
1943
+ result: null,
1944
+ error: `Invalid JSON in arguments: ${errorMessage}`,
1945
+ isRetryable: true
1946
+ };
1947
+ }
1948
+ try {
1949
+ const result = await handler(args, call);
1950
+ return {
1951
+ toolCallId: call.id,
1952
+ toolName,
1953
+ result
1954
+ };
1955
+ } catch (err) {
1956
+ const isRetryable = err instanceof ToolArgsError;
1957
+ const errorMessage = err instanceof Error ? err.message : String(err);
1958
+ return {
1959
+ toolCallId: call.id,
1960
+ toolName,
1961
+ result: null,
1962
+ error: errorMessage,
1963
+ isRetryable
1964
+ };
1965
+ }
1966
+ }
1967
+ /**
1968
+ * Executes multiple tool calls in parallel.
1969
+ * @param calls - Array of tool calls to execute
1970
+ * @returns Array of execution results in the same order as input
1971
+ */
1972
+ async executeAll(calls) {
1973
+ return Promise.all(calls.map((call) => this.execute(call)));
1974
+ }
1975
+ /**
1976
+ * Converts execution results to tool result messages.
1977
+ * Useful for appending to the conversation history.
1978
+ * @param results - Array of execution results
1979
+ * @returns Array of ChatMessage objects with role "tool"
1980
+ */
1981
+ resultsToMessages(results) {
1982
+ return results.map((r) => {
1983
+ const content = r.error ? `Error: ${r.error}` : typeof r.result === "string" ? r.result : JSON.stringify(r.result);
1984
+ return toolResultMessage(r.toolCallId, content);
1985
+ });
1986
+ }
1987
+ };
1988
+ function formatToolErrorForModel(result) {
1989
+ const lines = [
1990
+ `Tool call error for '${result.toolName}': ${result.error}`
1991
+ ];
1992
+ if (result.isRetryable) {
1993
+ lines.push("");
1994
+ lines.push("Please correct the arguments and try again.");
1995
+ }
1996
+ return lines.join("\n");
1997
+ }
1998
+ function hasRetryableErrors(results) {
1999
+ return results.some((r) => r.error && r.isRetryable);
2000
+ }
2001
+ function getRetryableErrors(results) {
2002
+ return results.filter((r) => r.error && r.isRetryable);
2003
+ }
2004
+ function createRetryMessages(results) {
2005
+ return results.filter((r) => r.error && r.isRetryable).map((r) => toolResultMessage(r.toolCallId, formatToolErrorForModel(r)));
2006
+ }
2007
+ async function executeWithRetry(registry, toolCalls, options = {}) {
2008
+ const maxRetries = options.maxRetries ?? 2;
2009
+ let currentCalls = toolCalls;
2010
+ let attempt = 0;
2011
+ const successfulResults = /* @__PURE__ */ new Map();
2012
+ while (attempt <= maxRetries) {
2013
+ const results = await registry.executeAll(currentCalls);
2014
+ for (const result of results) {
2015
+ if (!result.error || !result.isRetryable) {
2016
+ successfulResults.set(result.toolCallId, result);
2017
+ }
2018
+ }
2019
+ const retryableResults = getRetryableErrors(results);
2020
+ if (retryableResults.length === 0 || !options.onRetry) {
2021
+ for (const result of results) {
2022
+ if (result.error && result.isRetryable) {
2023
+ successfulResults.set(result.toolCallId, result);
2024
+ }
2025
+ }
2026
+ return Array.from(successfulResults.values());
2027
+ }
2028
+ attempt++;
2029
+ if (attempt > maxRetries) {
2030
+ for (const result of retryableResults) {
2031
+ successfulResults.set(result.toolCallId, result);
2032
+ }
2033
+ return Array.from(successfulResults.values());
2034
+ }
2035
+ const errorMessages = createRetryMessages(retryableResults);
2036
+ const newCalls = await options.onRetry(errorMessages, attempt);
2037
+ if (newCalls.length === 0) {
2038
+ for (const result of retryableResults) {
2039
+ successfulResults.set(result.toolCallId, result);
2040
+ }
2041
+ return Array.from(successfulResults.values());
2042
+ }
2043
+ currentCalls = newCalls;
2044
+ }
2045
+ return Array.from(successfulResults.values());
2046
+ }
2047
+
1256
2048
  // src/index.ts
1257
2049
  var ModelRelay = class {
1258
2050
  constructor(options) {
@@ -1260,7 +2052,7 @@ var ModelRelay = class {
1260
2052
  if (!cfg.key && !cfg.token) {
1261
2053
  throw new ConfigError("Provide an API key or access token");
1262
2054
  }
1263
- this.baseUrl = resolveBaseUrl(cfg.environment, cfg.baseUrl);
2055
+ this.baseUrl = resolveBaseUrl(cfg.baseUrl);
1264
2056
  const http = new HTTPClient({
1265
2057
  baseUrl: this.baseUrl,
1266
2058
  apiKey: cfg.key,
@@ -1271,7 +2063,6 @@ var ModelRelay = class {
1271
2063
  timeoutMs: cfg.timeoutMs,
1272
2064
  retry: cfg.retry,
1273
2065
  defaultHeaders: cfg.defaultHeaders,
1274
- environment: cfg.environment,
1275
2066
  metrics: cfg.metrics,
1276
2067
  trace: cfg.trace
1277
2068
  });
@@ -1289,10 +2080,13 @@ var ModelRelay = class {
1289
2080
  this.customers = new CustomersClient(http, {
1290
2081
  apiKey: cfg.key
1291
2082
  });
2083
+ this.tiers = new TiersClient(http, {
2084
+ apiKey: cfg.key
2085
+ });
1292
2086
  }
1293
2087
  };
1294
- function resolveBaseUrl(env, override) {
1295
- const base = override || (env === "staging" ? STAGING_BASE_URL : env === "sandbox" ? SANDBOX_BASE_URL : DEFAULT_BASE_URL);
2088
+ function resolveBaseUrl(override) {
2089
+ const base = override || DEFAULT_BASE_URL;
1296
2090
  return base.replace(/\/+$/, "");
1297
2091
  }
1298
2092
  // Annotate the CommonJS export names for ESM import in node:
@@ -1311,11 +2105,26 @@ function resolveBaseUrl(env, override) {
1311
2105
  ModelRelayError,
1312
2106
  Models,
1313
2107
  Providers,
1314
- SANDBOX_BASE_URL,
1315
2108
  SDK_VERSION,
1316
- STAGING_BASE_URL,
1317
2109
  StopReasons,
2110
+ TiersClient,
2111
+ ToolArgsError,
2112
+ ToolCallAccumulator,
2113
+ ToolChoiceTypes,
2114
+ ToolRegistry,
2115
+ ToolTypes,
1318
2116
  TransportError,
2117
+ assistantMessageWithToolCalls,
2118
+ createFunctionTool,
2119
+ createFunctionToolFromSchema,
2120
+ createRetryMessages,
2121
+ createWebSearchTool,
2122
+ executeWithRetry,
2123
+ firstToolCall,
2124
+ formatToolErrorForModel,
2125
+ getRetryableErrors,
2126
+ hasRetryableErrors,
2127
+ hasToolCalls,
1319
2128
  isPublishableKey,
1320
2129
  mergeMetrics,
1321
2130
  mergeTrace,
@@ -1324,6 +2133,15 @@ function resolveBaseUrl(env, override) {
1324
2133
  normalizeProvider,
1325
2134
  normalizeStopReason,
1326
2135
  parseErrorResponse,
2136
+ parseToolArgs,
2137
+ parseToolArgsRaw,
1327
2138
  providerToString,
1328
- stopReasonToString
2139
+ respondToToolCall,
2140
+ stopReasonToString,
2141
+ toolChoiceAuto,
2142
+ toolChoiceNone,
2143
+ toolChoiceRequired,
2144
+ toolResultMessage,
2145
+ tryParseToolArgs,
2146
+ zodToJsonSchema
1329
2147
  });