@modelrelay/sdk 0.5.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +956 -59
- package/dist/index.d.cts +664 -26
- package/dist/index.d.ts +664 -26
- package/dist/index.js +921 -56
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
// src/errors.ts
|
|
2
|
+
var ErrorCodes = {
|
|
3
|
+
NOT_FOUND: "NOT_FOUND",
|
|
4
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
5
|
+
RATE_LIMIT: "RATE_LIMIT",
|
|
6
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
7
|
+
FORBIDDEN: "FORBIDDEN",
|
|
8
|
+
CONFLICT: "CONFLICT",
|
|
9
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
10
|
+
SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
|
|
11
|
+
INVALID_INPUT: "INVALID_INPUT",
|
|
12
|
+
PAYMENT_REQUIRED: "PAYMENT_REQUIRED",
|
|
13
|
+
METHOD_NOT_ALLOWED: "METHOD_NOT_ALLOWED"
|
|
14
|
+
};
|
|
2
15
|
var ModelRelayError = class extends Error {
|
|
3
16
|
constructor(message, opts) {
|
|
4
17
|
super(message);
|
|
@@ -42,6 +55,30 @@ var APIError = class extends ModelRelayError {
|
|
|
42
55
|
retries: opts.retries
|
|
43
56
|
});
|
|
44
57
|
}
|
|
58
|
+
/** Returns true if the error is a not found error. */
|
|
59
|
+
isNotFound() {
|
|
60
|
+
return this.code === ErrorCodes.NOT_FOUND;
|
|
61
|
+
}
|
|
62
|
+
/** Returns true if the error is a validation error. */
|
|
63
|
+
isValidation() {
|
|
64
|
+
return this.code === ErrorCodes.VALIDATION_ERROR || this.code === ErrorCodes.INVALID_INPUT;
|
|
65
|
+
}
|
|
66
|
+
/** Returns true if the error is a rate limit error. */
|
|
67
|
+
isRateLimit() {
|
|
68
|
+
return this.code === ErrorCodes.RATE_LIMIT;
|
|
69
|
+
}
|
|
70
|
+
/** Returns true if the error is an unauthorized error. */
|
|
71
|
+
isUnauthorized() {
|
|
72
|
+
return this.code === ErrorCodes.UNAUTHORIZED;
|
|
73
|
+
}
|
|
74
|
+
/** Returns true if the error is a forbidden error. */
|
|
75
|
+
isForbidden() {
|
|
76
|
+
return this.code === ErrorCodes.FORBIDDEN;
|
|
77
|
+
}
|
|
78
|
+
/** Returns true if the error is a service unavailable error. */
|
|
79
|
+
isUnavailable() {
|
|
80
|
+
return this.code === ErrorCodes.SERVICE_UNAVAILABLE;
|
|
81
|
+
}
|
|
45
82
|
};
|
|
46
83
|
async function parseErrorResponse(response, retries) {
|
|
47
84
|
const requestId = response.headers.get("X-ModelRelay-Chat-Request-Id") || response.headers.get("X-Request-Id") || void 0;
|
|
@@ -96,6 +133,12 @@ async function parseErrorResponse(response, retries) {
|
|
|
96
133
|
}
|
|
97
134
|
|
|
98
135
|
// src/auth.ts
|
|
136
|
+
function createApiKeyAuth(apiKey) {
|
|
137
|
+
return { apiKey };
|
|
138
|
+
}
|
|
139
|
+
function createAccessTokenAuth(accessToken) {
|
|
140
|
+
return { accessToken };
|
|
141
|
+
}
|
|
99
142
|
var AuthClient = class {
|
|
100
143
|
constructor(http, cfg) {
|
|
101
144
|
this.cachedFrontend = /* @__PURE__ */ new Map();
|
|
@@ -155,7 +198,7 @@ var AuthClient = class {
|
|
|
155
198
|
*/
|
|
156
199
|
async authForChat(customerId, overrides) {
|
|
157
200
|
if (this.accessToken) {
|
|
158
|
-
return
|
|
201
|
+
return createAccessTokenAuth(this.accessToken);
|
|
159
202
|
}
|
|
160
203
|
if (!this.apiKey) {
|
|
161
204
|
throw new ConfigError("API key or token is required");
|
|
@@ -166,21 +209,21 @@ var AuthClient = class {
|
|
|
166
209
|
deviceId: overrides?.deviceId,
|
|
167
210
|
ttlSeconds: overrides?.ttlSeconds
|
|
168
211
|
});
|
|
169
|
-
return
|
|
212
|
+
return createAccessTokenAuth(token.token);
|
|
170
213
|
}
|
|
171
|
-
return
|
|
214
|
+
return createApiKeyAuth(this.apiKey);
|
|
172
215
|
}
|
|
173
216
|
/**
|
|
174
217
|
* Billing calls accept either bearer tokens or API keys (including publishable keys).
|
|
175
218
|
*/
|
|
176
219
|
authForBilling() {
|
|
177
220
|
if (this.accessToken) {
|
|
178
|
-
return
|
|
221
|
+
return createAccessTokenAuth(this.accessToken);
|
|
179
222
|
}
|
|
180
223
|
if (!this.apiKey) {
|
|
181
224
|
throw new ConfigError("API key or token is required");
|
|
182
225
|
}
|
|
183
|
-
return
|
|
226
|
+
return createApiKeyAuth(this.apiKey);
|
|
184
227
|
}
|
|
185
228
|
};
|
|
186
229
|
function isPublishableKey(value) {
|
|
@@ -218,7 +261,7 @@ function isTokenReusable(token) {
|
|
|
218
261
|
// package.json
|
|
219
262
|
var package_default = {
|
|
220
263
|
name: "@modelrelay/sdk",
|
|
221
|
-
version: "0.
|
|
264
|
+
version: "0.14.1",
|
|
222
265
|
description: "TypeScript SDK for the ModelRelay API",
|
|
223
266
|
type: "module",
|
|
224
267
|
main: "dist/index.cjs",
|
|
@@ -252,15 +295,14 @@ var package_default = {
|
|
|
252
295
|
devDependencies: {
|
|
253
296
|
tsup: "^8.2.4",
|
|
254
297
|
typescript: "^5.6.3",
|
|
255
|
-
vitest: "^2.1.4"
|
|
298
|
+
vitest: "^2.1.4",
|
|
299
|
+
zod: "^3.23.0"
|
|
256
300
|
}
|
|
257
301
|
};
|
|
258
302
|
|
|
259
303
|
// src/types.ts
|
|
260
304
|
var SDK_VERSION = package_default.version || "0.0.0";
|
|
261
305
|
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
306
|
var DEFAULT_CLIENT_HEADER = `modelrelay-ts/${SDK_VERSION}`;
|
|
265
307
|
var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
|
|
266
308
|
var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
@@ -281,22 +323,45 @@ var StopReasons = {
|
|
|
281
323
|
var Providers = {
|
|
282
324
|
OpenAI: "openai",
|
|
283
325
|
Anthropic: "anthropic",
|
|
284
|
-
|
|
285
|
-
|
|
326
|
+
XAI: "xai",
|
|
327
|
+
GoogleAIStudio: "google-ai-studio",
|
|
286
328
|
Echo: "echo"
|
|
287
329
|
};
|
|
288
330
|
var Models = {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
331
|
+
// OpenAI models (provider-agnostic identifiers)
|
|
332
|
+
Gpt4o: "gpt-4o",
|
|
333
|
+
Gpt4oMini: "gpt-4o-mini",
|
|
334
|
+
Gpt51: "gpt-5.1",
|
|
335
|
+
// Anthropic models (provider-agnostic identifiers)
|
|
336
|
+
Claude35HaikuLatest: "claude-3-5-haiku-latest",
|
|
337
|
+
Claude35SonnetLatest: "claude-3-5-sonnet-latest",
|
|
338
|
+
ClaudeOpus45: "claude-opus-4-5",
|
|
339
|
+
Claude35Haiku: "claude-3.5-haiku",
|
|
340
|
+
// xAI / Grok models
|
|
296
341
|
Grok2: "grok-2",
|
|
297
|
-
|
|
342
|
+
Grok4_1FastNonReasoning: "grok-4-1-fast-non-reasoning",
|
|
343
|
+
Grok4_1FastReasoning: "grok-4-1-fast-reasoning",
|
|
344
|
+
// Internal echo model for testing.
|
|
298
345
|
Echo1: "echo-1"
|
|
299
346
|
};
|
|
347
|
+
function createUsage(inputTokens, outputTokens, totalTokens) {
|
|
348
|
+
return {
|
|
349
|
+
inputTokens,
|
|
350
|
+
outputTokens,
|
|
351
|
+
totalTokens: totalTokens ?? inputTokens + outputTokens
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
var ToolTypes = {
|
|
355
|
+
Function: "function",
|
|
356
|
+
Web: "web",
|
|
357
|
+
XSearch: "x_search",
|
|
358
|
+
CodeExecution: "code_execution"
|
|
359
|
+
};
|
|
360
|
+
var ToolChoiceTypes = {
|
|
361
|
+
Auto: "auto",
|
|
362
|
+
Required: "required",
|
|
363
|
+
None: "none"
|
|
364
|
+
};
|
|
300
365
|
function mergeMetrics(base, override) {
|
|
301
366
|
if (!base && !override) return void 0;
|
|
302
367
|
return {
|
|
@@ -360,6 +425,630 @@ function modelToString(value) {
|
|
|
360
425
|
return value.other?.trim() || "";
|
|
361
426
|
}
|
|
362
427
|
|
|
428
|
+
// src/tools.ts
|
|
429
|
+
function createUserMessage(content) {
|
|
430
|
+
return { role: "user", content };
|
|
431
|
+
}
|
|
432
|
+
function createAssistantMessage(content) {
|
|
433
|
+
return { role: "assistant", content };
|
|
434
|
+
}
|
|
435
|
+
function createSystemMessage(content) {
|
|
436
|
+
return { role: "system", content };
|
|
437
|
+
}
|
|
438
|
+
function createToolCall(id, name, args, type = ToolTypes.Function) {
|
|
439
|
+
return {
|
|
440
|
+
id,
|
|
441
|
+
type,
|
|
442
|
+
function: createFunctionCall(name, args)
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function createFunctionCall(name, args) {
|
|
446
|
+
return { name, arguments: args };
|
|
447
|
+
}
|
|
448
|
+
function zodToJsonSchema(schema, options = {}) {
|
|
449
|
+
const result = convertZodType(schema);
|
|
450
|
+
if (options.includeSchema) {
|
|
451
|
+
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#";
|
|
452
|
+
return { $schema: schemaVersion, ...result };
|
|
453
|
+
}
|
|
454
|
+
return result;
|
|
455
|
+
}
|
|
456
|
+
function convertZodType(schema) {
|
|
457
|
+
const def = schema._def;
|
|
458
|
+
const typeName = def.typeName;
|
|
459
|
+
switch (typeName) {
|
|
460
|
+
case "ZodString":
|
|
461
|
+
return convertZodString(def);
|
|
462
|
+
case "ZodNumber":
|
|
463
|
+
return convertZodNumber(def);
|
|
464
|
+
case "ZodBoolean":
|
|
465
|
+
return { type: "boolean" };
|
|
466
|
+
case "ZodNull":
|
|
467
|
+
return { type: "null" };
|
|
468
|
+
case "ZodArray":
|
|
469
|
+
return convertZodArray(def);
|
|
470
|
+
case "ZodObject":
|
|
471
|
+
return convertZodObject(def);
|
|
472
|
+
case "ZodEnum":
|
|
473
|
+
return convertZodEnum(def);
|
|
474
|
+
case "ZodNativeEnum":
|
|
475
|
+
return convertZodNativeEnum(def);
|
|
476
|
+
case "ZodLiteral":
|
|
477
|
+
return { const: def.value };
|
|
478
|
+
case "ZodUnion":
|
|
479
|
+
return convertZodUnion(def);
|
|
480
|
+
case "ZodOptional": {
|
|
481
|
+
const inner = convertZodType(def.innerType);
|
|
482
|
+
if (def.description && !inner.description) {
|
|
483
|
+
inner.description = def.description;
|
|
484
|
+
}
|
|
485
|
+
return inner;
|
|
486
|
+
}
|
|
487
|
+
case "ZodNullable":
|
|
488
|
+
return convertZodNullable(def);
|
|
489
|
+
case "ZodDefault":
|
|
490
|
+
return { ...convertZodType(def.innerType), default: def.defaultValue() };
|
|
491
|
+
case "ZodEffects":
|
|
492
|
+
return convertZodType(def.schema);
|
|
493
|
+
case "ZodRecord":
|
|
494
|
+
return convertZodRecord(def);
|
|
495
|
+
case "ZodTuple":
|
|
496
|
+
return convertZodTuple(def);
|
|
497
|
+
case "ZodAny":
|
|
498
|
+
case "ZodUnknown":
|
|
499
|
+
return {};
|
|
500
|
+
default:
|
|
501
|
+
return {};
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function convertZodString(def) {
|
|
505
|
+
const result = { type: "string" };
|
|
506
|
+
const checks = def.checks;
|
|
507
|
+
if (checks) {
|
|
508
|
+
for (const check of checks) {
|
|
509
|
+
switch (check.kind) {
|
|
510
|
+
case "min":
|
|
511
|
+
result.minLength = check.value;
|
|
512
|
+
break;
|
|
513
|
+
case "max":
|
|
514
|
+
result.maxLength = check.value;
|
|
515
|
+
break;
|
|
516
|
+
case "length":
|
|
517
|
+
result.minLength = check.value;
|
|
518
|
+
result.maxLength = check.value;
|
|
519
|
+
break;
|
|
520
|
+
case "email":
|
|
521
|
+
result.format = "email";
|
|
522
|
+
break;
|
|
523
|
+
case "url":
|
|
524
|
+
result.format = "uri";
|
|
525
|
+
break;
|
|
526
|
+
case "uuid":
|
|
527
|
+
result.format = "uuid";
|
|
528
|
+
break;
|
|
529
|
+
case "datetime":
|
|
530
|
+
result.format = "date-time";
|
|
531
|
+
break;
|
|
532
|
+
case "regex":
|
|
533
|
+
result.pattern = check.value.source;
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
if (def.description) {
|
|
539
|
+
result.description = def.description;
|
|
540
|
+
}
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
function convertZodNumber(def) {
|
|
544
|
+
const result = { type: "number" };
|
|
545
|
+
const checks = def.checks;
|
|
546
|
+
if (checks) {
|
|
547
|
+
for (const check of checks) {
|
|
548
|
+
switch (check.kind) {
|
|
549
|
+
case "int":
|
|
550
|
+
result.type = "integer";
|
|
551
|
+
break;
|
|
552
|
+
case "min":
|
|
553
|
+
if (check.inclusive === false) {
|
|
554
|
+
result.exclusiveMinimum = check.value;
|
|
555
|
+
} else {
|
|
556
|
+
result.minimum = check.value;
|
|
557
|
+
}
|
|
558
|
+
break;
|
|
559
|
+
case "max":
|
|
560
|
+
if (check.inclusive === false) {
|
|
561
|
+
result.exclusiveMaximum = check.value;
|
|
562
|
+
} else {
|
|
563
|
+
result.maximum = check.value;
|
|
564
|
+
}
|
|
565
|
+
break;
|
|
566
|
+
case "multipleOf":
|
|
567
|
+
result.multipleOf = check.value;
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (def.description) {
|
|
573
|
+
result.description = def.description;
|
|
574
|
+
}
|
|
575
|
+
return result;
|
|
576
|
+
}
|
|
577
|
+
function convertZodArray(def) {
|
|
578
|
+
const result = {
|
|
579
|
+
type: "array",
|
|
580
|
+
items: convertZodType(def.type)
|
|
581
|
+
};
|
|
582
|
+
if (def.minLength !== void 0 && def.minLength !== null) {
|
|
583
|
+
result.minItems = def.minLength.value;
|
|
584
|
+
}
|
|
585
|
+
if (def.maxLength !== void 0 && def.maxLength !== null) {
|
|
586
|
+
result.maxItems = def.maxLength.value;
|
|
587
|
+
}
|
|
588
|
+
if (def.description) {
|
|
589
|
+
result.description = def.description;
|
|
590
|
+
}
|
|
591
|
+
return result;
|
|
592
|
+
}
|
|
593
|
+
function convertZodObject(def) {
|
|
594
|
+
const shape = def.shape;
|
|
595
|
+
const shapeObj = typeof shape === "function" ? shape() : shape;
|
|
596
|
+
const properties = {};
|
|
597
|
+
const required = [];
|
|
598
|
+
for (const [key, value] of Object.entries(shapeObj)) {
|
|
599
|
+
properties[key] = convertZodType(value);
|
|
600
|
+
const valueDef = value._def;
|
|
601
|
+
const isOptional = valueDef.typeName === "ZodOptional" || valueDef.typeName === "ZodDefault" || valueDef.typeName === "ZodNullable" && valueDef.innerType?._def?.typeName === "ZodDefault";
|
|
602
|
+
if (!isOptional) {
|
|
603
|
+
required.push(key);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
const result = {
|
|
607
|
+
type: "object",
|
|
608
|
+
properties
|
|
609
|
+
};
|
|
610
|
+
if (required.length > 0) {
|
|
611
|
+
result.required = required;
|
|
612
|
+
}
|
|
613
|
+
if (def.description) {
|
|
614
|
+
result.description = def.description;
|
|
615
|
+
}
|
|
616
|
+
const unknownKeys = def.unknownKeys;
|
|
617
|
+
if (unknownKeys === "strict") {
|
|
618
|
+
result.additionalProperties = false;
|
|
619
|
+
}
|
|
620
|
+
return result;
|
|
621
|
+
}
|
|
622
|
+
function convertZodEnum(def) {
|
|
623
|
+
const result = {
|
|
624
|
+
type: "string",
|
|
625
|
+
enum: def.values
|
|
626
|
+
};
|
|
627
|
+
if (def.description) {
|
|
628
|
+
result.description = def.description;
|
|
629
|
+
}
|
|
630
|
+
return result;
|
|
631
|
+
}
|
|
632
|
+
function convertZodNativeEnum(def) {
|
|
633
|
+
const enumValues = def.values;
|
|
634
|
+
const values = Object.values(enumValues).filter(
|
|
635
|
+
(v) => typeof v === "string" || typeof v === "number"
|
|
636
|
+
);
|
|
637
|
+
const result = { enum: values };
|
|
638
|
+
if (def.description) {
|
|
639
|
+
result.description = def.description;
|
|
640
|
+
}
|
|
641
|
+
return result;
|
|
642
|
+
}
|
|
643
|
+
function convertZodUnion(def) {
|
|
644
|
+
const options = def.options;
|
|
645
|
+
const result = {
|
|
646
|
+
anyOf: options.map(convertZodType)
|
|
647
|
+
};
|
|
648
|
+
if (def.description) {
|
|
649
|
+
result.description = def.description;
|
|
650
|
+
}
|
|
651
|
+
return result;
|
|
652
|
+
}
|
|
653
|
+
function convertZodNullable(def) {
|
|
654
|
+
const inner = convertZodType(def.innerType);
|
|
655
|
+
return {
|
|
656
|
+
anyOf: [inner, { type: "null" }]
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
function convertZodRecord(def) {
|
|
660
|
+
const result = {
|
|
661
|
+
type: "object",
|
|
662
|
+
additionalProperties: convertZodType(def.valueType)
|
|
663
|
+
};
|
|
664
|
+
if (def.description) {
|
|
665
|
+
result.description = def.description;
|
|
666
|
+
}
|
|
667
|
+
return result;
|
|
668
|
+
}
|
|
669
|
+
function convertZodTuple(def) {
|
|
670
|
+
const items = def.items;
|
|
671
|
+
const result = {
|
|
672
|
+
type: "array",
|
|
673
|
+
items: items.map(convertZodType),
|
|
674
|
+
minItems: items.length,
|
|
675
|
+
maxItems: items.length
|
|
676
|
+
};
|
|
677
|
+
if (def.description) {
|
|
678
|
+
result.description = def.description;
|
|
679
|
+
}
|
|
680
|
+
return result;
|
|
681
|
+
}
|
|
682
|
+
function createFunctionToolFromSchema(name, description, schema, options) {
|
|
683
|
+
const jsonSchema = zodToJsonSchema(schema, options);
|
|
684
|
+
return createFunctionTool(name, description, jsonSchema);
|
|
685
|
+
}
|
|
686
|
+
function createFunctionTool(name, description, parameters) {
|
|
687
|
+
const fn = { name, description };
|
|
688
|
+
if (parameters) {
|
|
689
|
+
fn.parameters = parameters;
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
type: ToolTypes.Function,
|
|
693
|
+
function: fn
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
function createWebTool(options) {
|
|
697
|
+
return {
|
|
698
|
+
type: ToolTypes.Web,
|
|
699
|
+
web: options ? {
|
|
700
|
+
mode: options.mode,
|
|
701
|
+
allowedDomains: options.allowedDomains,
|
|
702
|
+
excludedDomains: options.excludedDomains,
|
|
703
|
+
maxUses: options.maxUses
|
|
704
|
+
} : void 0
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
function toolChoiceAuto() {
|
|
708
|
+
return { type: ToolChoiceTypes.Auto };
|
|
709
|
+
}
|
|
710
|
+
function toolChoiceRequired() {
|
|
711
|
+
return { type: ToolChoiceTypes.Required };
|
|
712
|
+
}
|
|
713
|
+
function toolChoiceNone() {
|
|
714
|
+
return { type: ToolChoiceTypes.None };
|
|
715
|
+
}
|
|
716
|
+
function hasToolCalls(response) {
|
|
717
|
+
return (response.toolCalls?.length ?? 0) > 0;
|
|
718
|
+
}
|
|
719
|
+
function firstToolCall(response) {
|
|
720
|
+
return response.toolCalls?.[0];
|
|
721
|
+
}
|
|
722
|
+
function toolResultMessage(toolCallId, result) {
|
|
723
|
+
const content = typeof result === "string" ? result : JSON.stringify(result);
|
|
724
|
+
return {
|
|
725
|
+
role: "tool",
|
|
726
|
+
content,
|
|
727
|
+
toolCallId
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
function respondToToolCall(call, result) {
|
|
731
|
+
return toolResultMessage(call.id, result);
|
|
732
|
+
}
|
|
733
|
+
function assistantMessageWithToolCalls(content, toolCalls) {
|
|
734
|
+
return {
|
|
735
|
+
role: "assistant",
|
|
736
|
+
content,
|
|
737
|
+
toolCalls
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
var ToolCallAccumulator = class {
|
|
741
|
+
constructor() {
|
|
742
|
+
this.calls = /* @__PURE__ */ new Map();
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Processes a streaming tool call delta.
|
|
746
|
+
* Returns true if this started a new tool call.
|
|
747
|
+
*/
|
|
748
|
+
processDelta(delta) {
|
|
749
|
+
const existing = this.calls.get(delta.index);
|
|
750
|
+
if (!existing) {
|
|
751
|
+
this.calls.set(delta.index, {
|
|
752
|
+
id: delta.id ?? "",
|
|
753
|
+
type: delta.type ?? ToolTypes.Function,
|
|
754
|
+
function: {
|
|
755
|
+
name: delta.function?.name ?? "",
|
|
756
|
+
arguments: delta.function?.arguments ?? ""
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
if (delta.function) {
|
|
762
|
+
if (delta.function.name) {
|
|
763
|
+
existing.function = existing.function ?? { name: "", arguments: "" };
|
|
764
|
+
existing.function.name = delta.function.name;
|
|
765
|
+
}
|
|
766
|
+
if (delta.function.arguments) {
|
|
767
|
+
existing.function = existing.function ?? { name: "", arguments: "" };
|
|
768
|
+
existing.function.arguments += delta.function.arguments;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Returns all accumulated tool calls in index order.
|
|
775
|
+
*/
|
|
776
|
+
getToolCalls() {
|
|
777
|
+
if (this.calls.size === 0) {
|
|
778
|
+
return [];
|
|
779
|
+
}
|
|
780
|
+
const maxIdx = Math.max(...this.calls.keys());
|
|
781
|
+
const result = [];
|
|
782
|
+
for (let i = 0; i <= maxIdx; i++) {
|
|
783
|
+
const call = this.calls.get(i);
|
|
784
|
+
if (call) {
|
|
785
|
+
result.push(call);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
return result;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Returns a specific tool call by index, or undefined if not found.
|
|
792
|
+
*/
|
|
793
|
+
getToolCall(index) {
|
|
794
|
+
return this.calls.get(index);
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Clears all accumulated tool calls.
|
|
798
|
+
*/
|
|
799
|
+
reset() {
|
|
800
|
+
this.calls.clear();
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
var ToolArgsError = class extends Error {
|
|
804
|
+
constructor(message, toolCallId, toolName, rawArguments) {
|
|
805
|
+
super(message);
|
|
806
|
+
this.name = "ToolArgsError";
|
|
807
|
+
this.toolCallId = toolCallId;
|
|
808
|
+
this.toolName = toolName;
|
|
809
|
+
this.rawArguments = rawArguments;
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
function parseToolArgs(call, schema) {
|
|
813
|
+
const toolName = call.function?.name ?? "unknown";
|
|
814
|
+
const rawArgs = call.function?.arguments ?? "";
|
|
815
|
+
let parsed;
|
|
816
|
+
try {
|
|
817
|
+
parsed = rawArgs ? JSON.parse(rawArgs) : {};
|
|
818
|
+
} catch (err) {
|
|
819
|
+
const message = err instanceof Error ? err.message : "Invalid JSON in arguments";
|
|
820
|
+
throw new ToolArgsError(
|
|
821
|
+
`Failed to parse arguments for tool '${toolName}': ${message}`,
|
|
822
|
+
call.id,
|
|
823
|
+
toolName,
|
|
824
|
+
rawArgs
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
try {
|
|
828
|
+
return schema.parse(parsed);
|
|
829
|
+
} catch (err) {
|
|
830
|
+
let message;
|
|
831
|
+
if (err instanceof Error) {
|
|
832
|
+
const zodErr = err;
|
|
833
|
+
if (zodErr.errors && Array.isArray(zodErr.errors)) {
|
|
834
|
+
const issues = zodErr.errors.map((e) => {
|
|
835
|
+
const path = e.path.length > 0 ? `${e.path.join(".")}: ` : "";
|
|
836
|
+
return `${path}${e.message}`;
|
|
837
|
+
}).join("; ");
|
|
838
|
+
message = issues;
|
|
839
|
+
} else {
|
|
840
|
+
message = err.message;
|
|
841
|
+
}
|
|
842
|
+
} else {
|
|
843
|
+
message = String(err);
|
|
844
|
+
}
|
|
845
|
+
throw new ToolArgsError(
|
|
846
|
+
`Invalid arguments for tool '${toolName}': ${message}`,
|
|
847
|
+
call.id,
|
|
848
|
+
toolName,
|
|
849
|
+
rawArgs
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
function tryParseToolArgs(call, schema) {
|
|
854
|
+
try {
|
|
855
|
+
const data = parseToolArgs(call, schema);
|
|
856
|
+
return { success: true, data };
|
|
857
|
+
} catch (err) {
|
|
858
|
+
if (err instanceof ToolArgsError) {
|
|
859
|
+
return { success: false, error: err };
|
|
860
|
+
}
|
|
861
|
+
const toolName = call.function?.name ?? "unknown";
|
|
862
|
+
const rawArgs = call.function?.arguments ?? "";
|
|
863
|
+
return {
|
|
864
|
+
success: false,
|
|
865
|
+
error: new ToolArgsError(
|
|
866
|
+
err instanceof Error ? err.message : String(err),
|
|
867
|
+
call.id,
|
|
868
|
+
toolName,
|
|
869
|
+
rawArgs
|
|
870
|
+
)
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function parseToolArgsRaw(call) {
|
|
875
|
+
const toolName = call.function?.name ?? "unknown";
|
|
876
|
+
const rawArgs = call.function?.arguments ?? "";
|
|
877
|
+
try {
|
|
878
|
+
return rawArgs ? JSON.parse(rawArgs) : {};
|
|
879
|
+
} catch (err) {
|
|
880
|
+
const message = err instanceof Error ? err.message : "Invalid JSON in arguments";
|
|
881
|
+
throw new ToolArgsError(
|
|
882
|
+
`Failed to parse arguments for tool '${toolName}': ${message}`,
|
|
883
|
+
call.id,
|
|
884
|
+
toolName,
|
|
885
|
+
rawArgs
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
var ToolRegistry = class {
|
|
890
|
+
constructor() {
|
|
891
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Registers a handler function for a tool name.
|
|
895
|
+
* @param name - The tool name (must match the function name in the tool definition)
|
|
896
|
+
* @param handler - Function to execute when this tool is called
|
|
897
|
+
* @returns this for chaining
|
|
898
|
+
*/
|
|
899
|
+
register(name, handler) {
|
|
900
|
+
this.handlers.set(name, handler);
|
|
901
|
+
return this;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Unregisters a tool handler.
|
|
905
|
+
* @param name - The tool name to unregister
|
|
906
|
+
* @returns true if the handler was removed, false if it didn't exist
|
|
907
|
+
*/
|
|
908
|
+
unregister(name) {
|
|
909
|
+
return this.handlers.delete(name);
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Checks if a handler is registered for the given tool name.
|
|
913
|
+
*/
|
|
914
|
+
has(name) {
|
|
915
|
+
return this.handlers.has(name);
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Returns the list of registered tool names.
|
|
919
|
+
*/
|
|
920
|
+
getRegisteredTools() {
|
|
921
|
+
return Array.from(this.handlers.keys());
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Executes a single tool call.
|
|
925
|
+
* @param call - The tool call to execute
|
|
926
|
+
* @returns The execution result
|
|
927
|
+
*/
|
|
928
|
+
async execute(call) {
|
|
929
|
+
const toolName = call.function?.name ?? "";
|
|
930
|
+
const handler = this.handlers.get(toolName);
|
|
931
|
+
if (!handler) {
|
|
932
|
+
return {
|
|
933
|
+
toolCallId: call.id,
|
|
934
|
+
toolName,
|
|
935
|
+
result: null,
|
|
936
|
+
error: `Unknown tool: '${toolName}'. Available tools: ${this.getRegisteredTools().join(", ") || "none"}`
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
let args;
|
|
940
|
+
try {
|
|
941
|
+
args = call.function?.arguments ? JSON.parse(call.function.arguments) : {};
|
|
942
|
+
} catch (err) {
|
|
943
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
944
|
+
return {
|
|
945
|
+
toolCallId: call.id,
|
|
946
|
+
toolName,
|
|
947
|
+
result: null,
|
|
948
|
+
error: `Invalid JSON in arguments: ${errorMessage}`,
|
|
949
|
+
isRetryable: true
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
try {
|
|
953
|
+
const result = await handler(args, call);
|
|
954
|
+
return {
|
|
955
|
+
toolCallId: call.id,
|
|
956
|
+
toolName,
|
|
957
|
+
result
|
|
958
|
+
};
|
|
959
|
+
} catch (err) {
|
|
960
|
+
const isRetryable = err instanceof ToolArgsError;
|
|
961
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
962
|
+
return {
|
|
963
|
+
toolCallId: call.id,
|
|
964
|
+
toolName,
|
|
965
|
+
result: null,
|
|
966
|
+
error: errorMessage,
|
|
967
|
+
isRetryable
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Executes multiple tool calls in parallel.
|
|
973
|
+
* @param calls - Array of tool calls to execute
|
|
974
|
+
* @returns Array of execution results in the same order as input
|
|
975
|
+
*/
|
|
976
|
+
async executeAll(calls) {
|
|
977
|
+
return Promise.all(calls.map((call) => this.execute(call)));
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Converts execution results to tool result messages.
|
|
981
|
+
* Useful for appending to the conversation history.
|
|
982
|
+
* @param results - Array of execution results
|
|
983
|
+
* @returns Array of ChatMessage objects with role "tool"
|
|
984
|
+
*/
|
|
985
|
+
resultsToMessages(results) {
|
|
986
|
+
return results.map((r) => {
|
|
987
|
+
const content = r.error ? `Error: ${r.error}` : typeof r.result === "string" ? r.result : JSON.stringify(r.result);
|
|
988
|
+
return toolResultMessage(r.toolCallId, content);
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
function formatToolErrorForModel(result) {
|
|
993
|
+
const lines = [
|
|
994
|
+
`Tool call error for '${result.toolName}': ${result.error}`
|
|
995
|
+
];
|
|
996
|
+
if (result.isRetryable) {
|
|
997
|
+
lines.push("");
|
|
998
|
+
lines.push("Please correct the arguments and try again.");
|
|
999
|
+
}
|
|
1000
|
+
return lines.join("\n");
|
|
1001
|
+
}
|
|
1002
|
+
function hasRetryableErrors(results) {
|
|
1003
|
+
return results.some((r) => r.error && r.isRetryable);
|
|
1004
|
+
}
|
|
1005
|
+
function getRetryableErrors(results) {
|
|
1006
|
+
return results.filter((r) => r.error && r.isRetryable);
|
|
1007
|
+
}
|
|
1008
|
+
function createRetryMessages(results) {
|
|
1009
|
+
return results.filter((r) => r.error && r.isRetryable).map((r) => toolResultMessage(r.toolCallId, formatToolErrorForModel(r)));
|
|
1010
|
+
}
|
|
1011
|
+
async function executeWithRetry(registry, toolCalls, options = {}) {
|
|
1012
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
1013
|
+
let currentCalls = toolCalls;
|
|
1014
|
+
let attempt = 0;
|
|
1015
|
+
const successfulResults = /* @__PURE__ */ new Map();
|
|
1016
|
+
while (attempt <= maxRetries) {
|
|
1017
|
+
const results = await registry.executeAll(currentCalls);
|
|
1018
|
+
for (const result of results) {
|
|
1019
|
+
if (!result.error || !result.isRetryable) {
|
|
1020
|
+
successfulResults.set(result.toolCallId, result);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
const retryableResults = getRetryableErrors(results);
|
|
1024
|
+
if (retryableResults.length === 0 || !options.onRetry) {
|
|
1025
|
+
for (const result of results) {
|
|
1026
|
+
if (result.error && result.isRetryable) {
|
|
1027
|
+
successfulResults.set(result.toolCallId, result);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return Array.from(successfulResults.values());
|
|
1031
|
+
}
|
|
1032
|
+
attempt++;
|
|
1033
|
+
if (attempt > maxRetries) {
|
|
1034
|
+
for (const result of retryableResults) {
|
|
1035
|
+
successfulResults.set(result.toolCallId, result);
|
|
1036
|
+
}
|
|
1037
|
+
return Array.from(successfulResults.values());
|
|
1038
|
+
}
|
|
1039
|
+
const errorMessages = createRetryMessages(retryableResults);
|
|
1040
|
+
const newCalls = await options.onRetry(errorMessages, attempt);
|
|
1041
|
+
if (newCalls.length === 0) {
|
|
1042
|
+
for (const result of retryableResults) {
|
|
1043
|
+
successfulResults.set(result.toolCallId, result);
|
|
1044
|
+
}
|
|
1045
|
+
return Array.from(successfulResults.values());
|
|
1046
|
+
}
|
|
1047
|
+
currentCalls = newCalls;
|
|
1048
|
+
}
|
|
1049
|
+
return Array.from(successfulResults.values());
|
|
1050
|
+
}
|
|
1051
|
+
|
|
363
1052
|
// src/chat.ts
|
|
364
1053
|
var REQUEST_ID_HEADER = "X-ModelRelay-Chat-Request-Id";
|
|
365
1054
|
var ChatClient = class {
|
|
@@ -385,16 +1074,13 @@ var ChatCompletionsClient = class {
|
|
|
385
1074
|
const stream = options.stream ?? params.stream ?? true;
|
|
386
1075
|
const metrics = mergeMetrics(this.metrics, options.metrics);
|
|
387
1076
|
const trace = mergeTrace(this.trace, options.trace);
|
|
388
|
-
const modelValue = modelToString(params.model).trim();
|
|
389
|
-
if (!modelValue) {
|
|
390
|
-
throw new ConfigError("model is required");
|
|
391
|
-
}
|
|
392
1077
|
if (!params?.messages?.length) {
|
|
393
1078
|
throw new ConfigError("at least one message is required");
|
|
394
1079
|
}
|
|
395
1080
|
if (!hasUserMessage(params.messages)) {
|
|
396
1081
|
throw new ConfigError("at least one user message is required");
|
|
397
1082
|
}
|
|
1083
|
+
validateRequestModel(params.model);
|
|
398
1084
|
const authHeaders = await this.auth.authForChat(params.customerId);
|
|
399
1085
|
const body = buildProxyBody(
|
|
400
1086
|
params,
|
|
@@ -533,7 +1219,7 @@ var ChatCompletionsStream = class {
|
|
|
533
1219
|
const context = this.enrichContext(evt);
|
|
534
1220
|
this.context = context;
|
|
535
1221
|
this.trace?.streamEvent?.({ context, event: evt });
|
|
536
|
-
if (evt.type === "message_start" || evt.type === "message_delta" || evt.type === "message_stop") {
|
|
1222
|
+
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
1223
|
this.recordFirstToken();
|
|
538
1224
|
}
|
|
539
1225
|
if (evt.type === "message_stop" && evt.usage && this.metrics?.usage) {
|
|
@@ -618,11 +1304,15 @@ function mapChatEvent(raw, requestId) {
|
|
|
618
1304
|
const model = normalizeModelId(p.model || p?.message?.model);
|
|
619
1305
|
const stopReason = normalizeStopReason(p.stop_reason);
|
|
620
1306
|
const textDelta = extractTextDelta(p);
|
|
1307
|
+
const toolCallDelta = extractToolCallDelta(p, type);
|
|
1308
|
+
const toolCalls = extractToolCalls(p, type);
|
|
621
1309
|
return {
|
|
622
1310
|
type,
|
|
623
1311
|
event: raw.event || type,
|
|
624
1312
|
data: p,
|
|
625
1313
|
textDelta,
|
|
1314
|
+
toolCallDelta,
|
|
1315
|
+
toolCalls,
|
|
626
1316
|
responseId,
|
|
627
1317
|
model,
|
|
628
1318
|
stopReason,
|
|
@@ -642,6 +1332,12 @@ function normalizeEventType(eventName, payload) {
|
|
|
642
1332
|
return "message_delta";
|
|
643
1333
|
case "message_stop":
|
|
644
1334
|
return "message_stop";
|
|
1335
|
+
case "tool_use_start":
|
|
1336
|
+
return "tool_use_start";
|
|
1337
|
+
case "tool_use_delta":
|
|
1338
|
+
return "tool_use_delta";
|
|
1339
|
+
case "tool_use_stop":
|
|
1340
|
+
return "tool_use_stop";
|
|
645
1341
|
case "ping":
|
|
646
1342
|
return "ping";
|
|
647
1343
|
default:
|
|
@@ -652,6 +1348,9 @@ function extractTextDelta(payload) {
|
|
|
652
1348
|
if (!payload || typeof payload !== "object") {
|
|
653
1349
|
return void 0;
|
|
654
1350
|
}
|
|
1351
|
+
if (typeof payload.text_delta === "string" && payload.text_delta !== "") {
|
|
1352
|
+
return payload.text_delta;
|
|
1353
|
+
}
|
|
655
1354
|
if (typeof payload.delta === "string") {
|
|
656
1355
|
return payload.delta;
|
|
657
1356
|
}
|
|
@@ -665,9 +1364,56 @@ function extractTextDelta(payload) {
|
|
|
665
1364
|
}
|
|
666
1365
|
return void 0;
|
|
667
1366
|
}
|
|
1367
|
+
function extractToolCallDelta(payload, type) {
|
|
1368
|
+
if (!payload || typeof payload !== "object") {
|
|
1369
|
+
return void 0;
|
|
1370
|
+
}
|
|
1371
|
+
if (type !== "tool_use_start" && type !== "tool_use_delta") {
|
|
1372
|
+
return void 0;
|
|
1373
|
+
}
|
|
1374
|
+
if (payload.tool_call_delta) {
|
|
1375
|
+
const d = payload.tool_call_delta;
|
|
1376
|
+
return {
|
|
1377
|
+
index: d.index ?? 0,
|
|
1378
|
+
id: d.id,
|
|
1379
|
+
type: d.type,
|
|
1380
|
+
function: d.function ? {
|
|
1381
|
+
name: d.function.name,
|
|
1382
|
+
arguments: d.function.arguments
|
|
1383
|
+
} : void 0
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
if (typeof payload.index === "number" || payload.id || payload.name) {
|
|
1387
|
+
return {
|
|
1388
|
+
index: payload.index ?? 0,
|
|
1389
|
+
id: payload.id,
|
|
1390
|
+
type: payload.tool_type,
|
|
1391
|
+
function: payload.name || payload.arguments ? {
|
|
1392
|
+
name: payload.name,
|
|
1393
|
+
arguments: payload.arguments
|
|
1394
|
+
} : void 0
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
return void 0;
|
|
1398
|
+
}
|
|
1399
|
+
function extractToolCalls(payload, type) {
|
|
1400
|
+
if (!payload || typeof payload !== "object") {
|
|
1401
|
+
return void 0;
|
|
1402
|
+
}
|
|
1403
|
+
if (type !== "tool_use_stop" && type !== "message_stop") {
|
|
1404
|
+
return void 0;
|
|
1405
|
+
}
|
|
1406
|
+
if (payload.tool_calls?.length) {
|
|
1407
|
+
return normalizeToolCalls(payload.tool_calls);
|
|
1408
|
+
}
|
|
1409
|
+
if (payload.tool_call) {
|
|
1410
|
+
return normalizeToolCalls([payload.tool_call]);
|
|
1411
|
+
}
|
|
1412
|
+
return void 0;
|
|
1413
|
+
}
|
|
668
1414
|
function normalizeChatResponse(payload, requestId) {
|
|
669
1415
|
const p = payload;
|
|
670
|
-
|
|
1416
|
+
const response = {
|
|
671
1417
|
id: p?.id,
|
|
672
1418
|
provider: normalizeProvider(p?.provider),
|
|
673
1419
|
content: Array.isArray(p?.content) ? p.content : p?.content ? [String(p.content)] : [],
|
|
@@ -676,26 +1422,51 @@ function normalizeChatResponse(payload, requestId) {
|
|
|
676
1422
|
usage: normalizeUsage(p?.usage),
|
|
677
1423
|
requestId
|
|
678
1424
|
};
|
|
1425
|
+
if (p?.tool_calls?.length) {
|
|
1426
|
+
response.toolCalls = normalizeToolCalls(p.tool_calls);
|
|
1427
|
+
}
|
|
1428
|
+
return response;
|
|
1429
|
+
}
|
|
1430
|
+
function normalizeToolCalls(toolCalls) {
|
|
1431
|
+
return toolCalls.map(
|
|
1432
|
+
(tc) => createToolCall(
|
|
1433
|
+
tc.id,
|
|
1434
|
+
tc.function?.name ?? "",
|
|
1435
|
+
tc.function?.arguments ?? "",
|
|
1436
|
+
tc.type || ToolTypes.Function
|
|
1437
|
+
)
|
|
1438
|
+
);
|
|
679
1439
|
}
|
|
680
1440
|
function normalizeUsage(payload) {
|
|
681
1441
|
if (!payload) {
|
|
682
|
-
return
|
|
1442
|
+
return createUsage(0, 0, 0);
|
|
683
1443
|
}
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
1444
|
+
const inputTokens = Number(payload.input_tokens ?? 0);
|
|
1445
|
+
const outputTokens = Number(payload.output_tokens ?? 0);
|
|
1446
|
+
const totalTokens = Number(payload.total_tokens ?? 0);
|
|
1447
|
+
return createUsage(inputTokens, outputTokens, totalTokens || void 0);
|
|
1448
|
+
}
|
|
1449
|
+
function validateRequestModel(model) {
|
|
1450
|
+
if (model === void 0 || model === null) return;
|
|
1451
|
+
const value = modelToString(model).trim();
|
|
1452
|
+
if (!value) {
|
|
1453
|
+
throw new ConfigError("model id must be a non-empty string when provided");
|
|
1454
|
+
}
|
|
1455
|
+
const knownModels = Object.values(Models);
|
|
1456
|
+
if (!knownModels.includes(value)) {
|
|
1457
|
+
throw new ConfigError(
|
|
1458
|
+
`unsupported model id "${value}". Use one of the SDK Models.* constants or omit model to use the tier's default model.`
|
|
1459
|
+
);
|
|
691
1460
|
}
|
|
692
|
-
return usage;
|
|
693
1461
|
}
|
|
694
1462
|
function buildProxyBody(params, metadata) {
|
|
1463
|
+
const modelValue = params.model ? modelToString(params.model).trim() : "";
|
|
695
1464
|
const body = {
|
|
696
|
-
model: modelToString(params.model),
|
|
697
1465
|
messages: normalizeMessages(params.messages)
|
|
698
1466
|
};
|
|
1467
|
+
if (modelValue) {
|
|
1468
|
+
body.model = modelValue;
|
|
1469
|
+
}
|
|
699
1470
|
if (typeof params.maxTokens === "number") body.max_tokens = params.maxTokens;
|
|
700
1471
|
if (params.provider) body.provider = providerToString(params.provider);
|
|
701
1472
|
if (typeof params.temperature === "number")
|
|
@@ -703,13 +1474,69 @@ function buildProxyBody(params, metadata) {
|
|
|
703
1474
|
if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;
|
|
704
1475
|
if (params.stop?.length) body.stop = params.stop;
|
|
705
1476
|
if (params.stopSequences?.length) body.stop_sequences = params.stopSequences;
|
|
1477
|
+
if (params.tools?.length) body.tools = normalizeTools(params.tools);
|
|
1478
|
+
if (params.toolChoice) body.tool_choice = normalizeToolChoice(params.toolChoice);
|
|
706
1479
|
return body;
|
|
707
1480
|
}
|
|
708
1481
|
function normalizeMessages(messages) {
|
|
709
|
-
return messages.map((msg) =>
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
1482
|
+
return messages.map((msg) => {
|
|
1483
|
+
const normalized = {
|
|
1484
|
+
role: msg.role || "user",
|
|
1485
|
+
content: msg.content
|
|
1486
|
+
};
|
|
1487
|
+
if (msg.toolCalls?.length) {
|
|
1488
|
+
normalized.tool_calls = msg.toolCalls.map((tc) => ({
|
|
1489
|
+
id: tc.id,
|
|
1490
|
+
type: tc.type,
|
|
1491
|
+
function: tc.function ? createFunctionCall(tc.function.name, tc.function.arguments) : void 0
|
|
1492
|
+
}));
|
|
1493
|
+
}
|
|
1494
|
+
if (msg.toolCallId) {
|
|
1495
|
+
normalized.tool_call_id = msg.toolCallId;
|
|
1496
|
+
}
|
|
1497
|
+
return normalized;
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
function normalizeTools(tools) {
|
|
1501
|
+
return tools.map((tool) => {
|
|
1502
|
+
const normalized = { type: tool.type };
|
|
1503
|
+
if (tool.function) {
|
|
1504
|
+
normalized.function = {
|
|
1505
|
+
name: tool.function.name,
|
|
1506
|
+
description: tool.function.description,
|
|
1507
|
+
parameters: tool.function.parameters
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
if (tool.web) {
|
|
1511
|
+
const web = {
|
|
1512
|
+
allowed_domains: tool.web.allowedDomains,
|
|
1513
|
+
excluded_domains: tool.web.excludedDomains,
|
|
1514
|
+
max_uses: tool.web.maxUses
|
|
1515
|
+
};
|
|
1516
|
+
if (tool.web.mode) {
|
|
1517
|
+
web.mode = tool.web.mode;
|
|
1518
|
+
}
|
|
1519
|
+
normalized.web = web;
|
|
1520
|
+
}
|
|
1521
|
+
if (tool.xSearch) {
|
|
1522
|
+
normalized.x_search = {
|
|
1523
|
+
allowed_handles: tool.xSearch.allowedHandles,
|
|
1524
|
+
excluded_handles: tool.xSearch.excludedHandles,
|
|
1525
|
+
from_date: tool.xSearch.fromDate,
|
|
1526
|
+
to_date: tool.xSearch.toDate
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
if (tool.codeExecution) {
|
|
1530
|
+
normalized.code_execution = {
|
|
1531
|
+
language: tool.codeExecution.language,
|
|
1532
|
+
timeout_ms: tool.codeExecution.timeoutMs
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
return normalized;
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
function normalizeToolChoice(tc) {
|
|
1539
|
+
return { type: tc.type };
|
|
713
1540
|
}
|
|
714
1541
|
function requestIdFromHeaders(headers) {
|
|
715
1542
|
return headers.get(REQUEST_ID_HEADER) || headers.get("X-Request-Id") || void 0;
|
|
@@ -734,6 +1561,10 @@ function hasUserMessage(messages) {
|
|
|
734
1561
|
}
|
|
735
1562
|
|
|
736
1563
|
// src/customers.ts
|
|
1564
|
+
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1565
|
+
function isValidEmail(email) {
|
|
1566
|
+
return EMAIL_REGEX.test(email);
|
|
1567
|
+
}
|
|
737
1568
|
var CustomersClient = class {
|
|
738
1569
|
constructor(http, cfg) {
|
|
739
1570
|
this.http = http;
|
|
@@ -768,6 +1599,12 @@ var CustomersClient = class {
|
|
|
768
1599
|
if (!request.external_id?.trim()) {
|
|
769
1600
|
throw new ConfigError("external_id is required");
|
|
770
1601
|
}
|
|
1602
|
+
if (!request.email?.trim()) {
|
|
1603
|
+
throw new ConfigError("email is required");
|
|
1604
|
+
}
|
|
1605
|
+
if (!isValidEmail(request.email)) {
|
|
1606
|
+
throw new ConfigError("invalid email format");
|
|
1607
|
+
}
|
|
771
1608
|
const response = await this.http.json("/customers", {
|
|
772
1609
|
method: "POST",
|
|
773
1610
|
body: request,
|
|
@@ -805,6 +1642,12 @@ var CustomersClient = class {
|
|
|
805
1642
|
if (!request.external_id?.trim()) {
|
|
806
1643
|
throw new ConfigError("external_id is required");
|
|
807
1644
|
}
|
|
1645
|
+
if (!request.email?.trim()) {
|
|
1646
|
+
throw new ConfigError("email is required");
|
|
1647
|
+
}
|
|
1648
|
+
if (!isValidEmail(request.email)) {
|
|
1649
|
+
throw new ConfigError("invalid email format");
|
|
1650
|
+
}
|
|
808
1651
|
const response = await this.http.json("/customers", {
|
|
809
1652
|
method: "PUT",
|
|
810
1653
|
body: request,
|
|
@@ -906,10 +1749,7 @@ var TiersClient = class {
|
|
|
906
1749
|
// src/http.ts
|
|
907
1750
|
var HTTPClient = class {
|
|
908
1751
|
constructor(cfg) {
|
|
909
|
-
const
|
|
910
|
-
const resolvedBase = normalizeBaseUrl(
|
|
911
|
-
cfg.baseUrl || baseFromEnv || DEFAULT_BASE_URL
|
|
912
|
-
);
|
|
1752
|
+
const resolvedBase = normalizeBaseUrl(cfg.baseUrl || DEFAULT_BASE_URL);
|
|
913
1753
|
if (!isValidHttpUrl(resolvedBase)) {
|
|
914
1754
|
throw new ConfigError(
|
|
915
1755
|
"baseUrl must start with http:// or https://"
|
|
@@ -1122,12 +1962,6 @@ function normalizeBaseUrl(value) {
|
|
|
1122
1962
|
function isValidHttpUrl(value) {
|
|
1123
1963
|
return /^https?:\/\//i.test(value);
|
|
1124
1964
|
}
|
|
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
1965
|
function normalizeRetryConfig(retry) {
|
|
1132
1966
|
if (retry === false) return void 0;
|
|
1133
1967
|
const cfg = retry || {};
|
|
@@ -1246,7 +2080,7 @@ var ModelRelay = class {
|
|
|
1246
2080
|
if (!cfg.key && !cfg.token) {
|
|
1247
2081
|
throw new ConfigError("Provide an API key or access token");
|
|
1248
2082
|
}
|
|
1249
|
-
this.baseUrl = resolveBaseUrl(cfg.
|
|
2083
|
+
this.baseUrl = resolveBaseUrl(cfg.baseUrl);
|
|
1250
2084
|
const http = new HTTPClient({
|
|
1251
2085
|
baseUrl: this.baseUrl,
|
|
1252
2086
|
apiKey: cfg.key,
|
|
@@ -1257,7 +2091,6 @@ var ModelRelay = class {
|
|
|
1257
2091
|
timeoutMs: cfg.timeoutMs,
|
|
1258
2092
|
retry: cfg.retry,
|
|
1259
2093
|
defaultHeaders: cfg.defaultHeaders,
|
|
1260
|
-
environment: cfg.environment,
|
|
1261
2094
|
metrics: cfg.metrics,
|
|
1262
2095
|
trace: cfg.trace
|
|
1263
2096
|
});
|
|
@@ -1280,8 +2113,8 @@ var ModelRelay = class {
|
|
|
1280
2113
|
});
|
|
1281
2114
|
}
|
|
1282
2115
|
};
|
|
1283
|
-
function resolveBaseUrl(
|
|
1284
|
-
const base = override ||
|
|
2116
|
+
function resolveBaseUrl(override) {
|
|
2117
|
+
const base = override || DEFAULT_BASE_URL;
|
|
1285
2118
|
return base.replace(/\/+$/, "");
|
|
1286
2119
|
}
|
|
1287
2120
|
export {
|
|
@@ -1295,16 +2128,39 @@ export {
|
|
|
1295
2128
|
DEFAULT_CLIENT_HEADER,
|
|
1296
2129
|
DEFAULT_CONNECT_TIMEOUT_MS,
|
|
1297
2130
|
DEFAULT_REQUEST_TIMEOUT_MS,
|
|
2131
|
+
ErrorCodes,
|
|
1298
2132
|
ModelRelay,
|
|
1299
2133
|
ModelRelayError,
|
|
1300
2134
|
Models,
|
|
1301
2135
|
Providers,
|
|
1302
|
-
SANDBOX_BASE_URL,
|
|
1303
2136
|
SDK_VERSION,
|
|
1304
|
-
STAGING_BASE_URL,
|
|
1305
2137
|
StopReasons,
|
|
1306
2138
|
TiersClient,
|
|
2139
|
+
ToolArgsError,
|
|
2140
|
+
ToolCallAccumulator,
|
|
2141
|
+
ToolChoiceTypes,
|
|
2142
|
+
ToolRegistry,
|
|
2143
|
+
ToolTypes,
|
|
1307
2144
|
TransportError,
|
|
2145
|
+
assistantMessageWithToolCalls,
|
|
2146
|
+
createAccessTokenAuth,
|
|
2147
|
+
createApiKeyAuth,
|
|
2148
|
+
createAssistantMessage,
|
|
2149
|
+
createFunctionCall,
|
|
2150
|
+
createFunctionTool,
|
|
2151
|
+
createFunctionToolFromSchema,
|
|
2152
|
+
createRetryMessages,
|
|
2153
|
+
createSystemMessage,
|
|
2154
|
+
createToolCall,
|
|
2155
|
+
createUsage,
|
|
2156
|
+
createUserMessage,
|
|
2157
|
+
createWebTool,
|
|
2158
|
+
executeWithRetry,
|
|
2159
|
+
firstToolCall,
|
|
2160
|
+
formatToolErrorForModel,
|
|
2161
|
+
getRetryableErrors,
|
|
2162
|
+
hasRetryableErrors,
|
|
2163
|
+
hasToolCalls,
|
|
1308
2164
|
isPublishableKey,
|
|
1309
2165
|
mergeMetrics,
|
|
1310
2166
|
mergeTrace,
|
|
@@ -1313,6 +2169,15 @@ export {
|
|
|
1313
2169
|
normalizeProvider,
|
|
1314
2170
|
normalizeStopReason,
|
|
1315
2171
|
parseErrorResponse,
|
|
2172
|
+
parseToolArgs,
|
|
2173
|
+
parseToolArgsRaw,
|
|
1316
2174
|
providerToString,
|
|
1317
|
-
|
|
2175
|
+
respondToToolCall,
|
|
2176
|
+
stopReasonToString,
|
|
2177
|
+
toolChoiceAuto,
|
|
2178
|
+
toolChoiceNone,
|
|
2179
|
+
toolChoiceRequired,
|
|
2180
|
+
toolResultMessage,
|
|
2181
|
+
tryParseToolArgs,
|
|
2182
|
+
zodToJsonSchema
|
|
1318
2183
|
};
|