@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.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.4.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;
@@ -863,13 +996,50 @@ var CustomersClient = class {
863
996
  }
864
997
  };
865
998
 
999
+ // src/tiers.ts
1000
+ var TiersClient = class {
1001
+ constructor(http, cfg) {
1002
+ this.http = http;
1003
+ this.apiKey = cfg.apiKey;
1004
+ }
1005
+ ensureApiKey() {
1006
+ if (!this.apiKey || !this.apiKey.startsWith("mr_pk_") && !this.apiKey.startsWith("mr_sk_")) {
1007
+ throw new ConfigError(
1008
+ "API key (mr_pk_* or mr_sk_*) required for tier operations"
1009
+ );
1010
+ }
1011
+ }
1012
+ /**
1013
+ * List all tiers in the project.
1014
+ */
1015
+ async list() {
1016
+ this.ensureApiKey();
1017
+ const response = await this.http.json("/tiers", {
1018
+ method: "GET",
1019
+ apiKey: this.apiKey
1020
+ });
1021
+ return response.tiers;
1022
+ }
1023
+ /**
1024
+ * Get a tier by ID.
1025
+ */
1026
+ async get(tierId) {
1027
+ this.ensureApiKey();
1028
+ if (!tierId?.trim()) {
1029
+ throw new ConfigError("tierId is required");
1030
+ }
1031
+ const response = await this.http.json(`/tiers/${tierId}`, {
1032
+ method: "GET",
1033
+ apiKey: this.apiKey
1034
+ });
1035
+ return response.tier;
1036
+ }
1037
+ };
1038
+
866
1039
  // src/http.ts
867
1040
  var HTTPClient = class {
868
1041
  constructor(cfg) {
869
- const baseFromEnv = baseUrlForEnvironment(cfg.environment);
870
- const resolvedBase = normalizeBaseUrl(
871
- cfg.baseUrl || baseFromEnv || DEFAULT_BASE_URL
872
- );
1042
+ const resolvedBase = normalizeBaseUrl(cfg.baseUrl || DEFAULT_BASE_URL);
873
1043
  if (!isValidHttpUrl(resolvedBase)) {
874
1044
  throw new ConfigError(
875
1045
  "baseUrl must start with http:// or https://"
@@ -1082,12 +1252,6 @@ function normalizeBaseUrl(value) {
1082
1252
  function isValidHttpUrl(value) {
1083
1253
  return /^https?:\/\//i.test(value);
1084
1254
  }
1085
- function baseUrlForEnvironment(env) {
1086
- if (!env || env === "production") return void 0;
1087
- if (env === "staging") return STAGING_BASE_URL;
1088
- if (env === "sandbox") return SANDBOX_BASE_URL;
1089
- return void 0;
1090
- }
1091
1255
  function normalizeRetryConfig(retry) {
1092
1256
  if (retry === false) return void 0;
1093
1257
  const cfg = retry || {};
@@ -1199,6 +1363,610 @@ function withRequestId(context, headers) {
1199
1363
  return { ...context, requestId };
1200
1364
  }
1201
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
+
1202
1970
  // src/index.ts
1203
1971
  var ModelRelay = class {
1204
1972
  constructor(options) {
@@ -1206,7 +1974,7 @@ var ModelRelay = class {
1206
1974
  if (!cfg.key && !cfg.token) {
1207
1975
  throw new ConfigError("Provide an API key or access token");
1208
1976
  }
1209
- this.baseUrl = resolveBaseUrl(cfg.environment, cfg.baseUrl);
1977
+ this.baseUrl = resolveBaseUrl(cfg.baseUrl);
1210
1978
  const http = new HTTPClient({
1211
1979
  baseUrl: this.baseUrl,
1212
1980
  apiKey: cfg.key,
@@ -1217,7 +1985,6 @@ var ModelRelay = class {
1217
1985
  timeoutMs: cfg.timeoutMs,
1218
1986
  retry: cfg.retry,
1219
1987
  defaultHeaders: cfg.defaultHeaders,
1220
- environment: cfg.environment,
1221
1988
  metrics: cfg.metrics,
1222
1989
  trace: cfg.trace
1223
1990
  });
@@ -1235,10 +2002,13 @@ var ModelRelay = class {
1235
2002
  this.customers = new CustomersClient(http, {
1236
2003
  apiKey: cfg.key
1237
2004
  });
2005
+ this.tiers = new TiersClient(http, {
2006
+ apiKey: cfg.key
2007
+ });
1238
2008
  }
1239
2009
  };
1240
- function resolveBaseUrl(env, override) {
1241
- 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;
1242
2012
  return base.replace(/\/+$/, "");
1243
2013
  }
1244
2014
  export {
@@ -1256,11 +2026,26 @@ export {
1256
2026
  ModelRelayError,
1257
2027
  Models,
1258
2028
  Providers,
1259
- SANDBOX_BASE_URL,
1260
2029
  SDK_VERSION,
1261
- STAGING_BASE_URL,
1262
2030
  StopReasons,
2031
+ TiersClient,
2032
+ ToolArgsError,
2033
+ ToolCallAccumulator,
2034
+ ToolChoiceTypes,
2035
+ ToolRegistry,
2036
+ ToolTypes,
1263
2037
  TransportError,
2038
+ assistantMessageWithToolCalls,
2039
+ createFunctionTool,
2040
+ createFunctionToolFromSchema,
2041
+ createRetryMessages,
2042
+ createWebSearchTool,
2043
+ executeWithRetry,
2044
+ firstToolCall,
2045
+ formatToolErrorForModel,
2046
+ getRetryableErrors,
2047
+ hasRetryableErrors,
2048
+ hasToolCalls,
1264
2049
  isPublishableKey,
1265
2050
  mergeMetrics,
1266
2051
  mergeTrace,
@@ -1269,6 +2054,15 @@ export {
1269
2054
  normalizeProvider,
1270
2055
  normalizeStopReason,
1271
2056
  parseErrorResponse,
2057
+ parseToolArgs,
2058
+ parseToolArgsRaw,
1272
2059
  providerToString,
1273
- stopReasonToString
2060
+ respondToToolCall,
2061
+ stopReasonToString,
2062
+ toolChoiceAuto,
2063
+ toolChoiceNone,
2064
+ toolChoiceRequired,
2065
+ toolResultMessage,
2066
+ tryParseToolArgs,
2067
+ zodToJsonSchema
1274
2068
  };