@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.cjs
CHANGED
|
@@ -30,16 +30,39 @@ __export(index_exports, {
|
|
|
30
30
|
DEFAULT_CLIENT_HEADER: () => DEFAULT_CLIENT_HEADER,
|
|
31
31
|
DEFAULT_CONNECT_TIMEOUT_MS: () => DEFAULT_CONNECT_TIMEOUT_MS,
|
|
32
32
|
DEFAULT_REQUEST_TIMEOUT_MS: () => DEFAULT_REQUEST_TIMEOUT_MS,
|
|
33
|
+
ErrorCodes: () => ErrorCodes,
|
|
33
34
|
ModelRelay: () => ModelRelay,
|
|
34
35
|
ModelRelayError: () => ModelRelayError,
|
|
35
36
|
Models: () => Models,
|
|
36
37
|
Providers: () => Providers,
|
|
37
|
-
SANDBOX_BASE_URL: () => SANDBOX_BASE_URL,
|
|
38
38
|
SDK_VERSION: () => SDK_VERSION,
|
|
39
|
-
STAGING_BASE_URL: () => STAGING_BASE_URL,
|
|
40
39
|
StopReasons: () => StopReasons,
|
|
41
40
|
TiersClient: () => TiersClient,
|
|
41
|
+
ToolArgsError: () => ToolArgsError,
|
|
42
|
+
ToolCallAccumulator: () => ToolCallAccumulator,
|
|
43
|
+
ToolChoiceTypes: () => ToolChoiceTypes,
|
|
44
|
+
ToolRegistry: () => ToolRegistry,
|
|
45
|
+
ToolTypes: () => ToolTypes,
|
|
42
46
|
TransportError: () => TransportError,
|
|
47
|
+
assistantMessageWithToolCalls: () => assistantMessageWithToolCalls,
|
|
48
|
+
createAccessTokenAuth: () => createAccessTokenAuth,
|
|
49
|
+
createApiKeyAuth: () => createApiKeyAuth,
|
|
50
|
+
createAssistantMessage: () => createAssistantMessage,
|
|
51
|
+
createFunctionCall: () => createFunctionCall,
|
|
52
|
+
createFunctionTool: () => createFunctionTool,
|
|
53
|
+
createFunctionToolFromSchema: () => createFunctionToolFromSchema,
|
|
54
|
+
createRetryMessages: () => createRetryMessages,
|
|
55
|
+
createSystemMessage: () => createSystemMessage,
|
|
56
|
+
createToolCall: () => createToolCall,
|
|
57
|
+
createUsage: () => createUsage,
|
|
58
|
+
createUserMessage: () => createUserMessage,
|
|
59
|
+
createWebTool: () => createWebTool,
|
|
60
|
+
executeWithRetry: () => executeWithRetry,
|
|
61
|
+
firstToolCall: () => firstToolCall,
|
|
62
|
+
formatToolErrorForModel: () => formatToolErrorForModel,
|
|
63
|
+
getRetryableErrors: () => getRetryableErrors,
|
|
64
|
+
hasRetryableErrors: () => hasRetryableErrors,
|
|
65
|
+
hasToolCalls: () => hasToolCalls,
|
|
43
66
|
isPublishableKey: () => isPublishableKey,
|
|
44
67
|
mergeMetrics: () => mergeMetrics,
|
|
45
68
|
mergeTrace: () => mergeTrace,
|
|
@@ -48,12 +71,34 @@ __export(index_exports, {
|
|
|
48
71
|
normalizeProvider: () => normalizeProvider,
|
|
49
72
|
normalizeStopReason: () => normalizeStopReason,
|
|
50
73
|
parseErrorResponse: () => parseErrorResponse,
|
|
74
|
+
parseToolArgs: () => parseToolArgs,
|
|
75
|
+
parseToolArgsRaw: () => parseToolArgsRaw,
|
|
51
76
|
providerToString: () => providerToString,
|
|
52
|
-
|
|
77
|
+
respondToToolCall: () => respondToToolCall,
|
|
78
|
+
stopReasonToString: () => stopReasonToString,
|
|
79
|
+
toolChoiceAuto: () => toolChoiceAuto,
|
|
80
|
+
toolChoiceNone: () => toolChoiceNone,
|
|
81
|
+
toolChoiceRequired: () => toolChoiceRequired,
|
|
82
|
+
toolResultMessage: () => toolResultMessage,
|
|
83
|
+
tryParseToolArgs: () => tryParseToolArgs,
|
|
84
|
+
zodToJsonSchema: () => zodToJsonSchema
|
|
53
85
|
});
|
|
54
86
|
module.exports = __toCommonJS(index_exports);
|
|
55
87
|
|
|
56
88
|
// src/errors.ts
|
|
89
|
+
var ErrorCodes = {
|
|
90
|
+
NOT_FOUND: "NOT_FOUND",
|
|
91
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
92
|
+
RATE_LIMIT: "RATE_LIMIT",
|
|
93
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
94
|
+
FORBIDDEN: "FORBIDDEN",
|
|
95
|
+
CONFLICT: "CONFLICT",
|
|
96
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
97
|
+
SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
|
|
98
|
+
INVALID_INPUT: "INVALID_INPUT",
|
|
99
|
+
PAYMENT_REQUIRED: "PAYMENT_REQUIRED",
|
|
100
|
+
METHOD_NOT_ALLOWED: "METHOD_NOT_ALLOWED"
|
|
101
|
+
};
|
|
57
102
|
var ModelRelayError = class extends Error {
|
|
58
103
|
constructor(message, opts) {
|
|
59
104
|
super(message);
|
|
@@ -97,6 +142,30 @@ var APIError = class extends ModelRelayError {
|
|
|
97
142
|
retries: opts.retries
|
|
98
143
|
});
|
|
99
144
|
}
|
|
145
|
+
/** Returns true if the error is a not found error. */
|
|
146
|
+
isNotFound() {
|
|
147
|
+
return this.code === ErrorCodes.NOT_FOUND;
|
|
148
|
+
}
|
|
149
|
+
/** Returns true if the error is a validation error. */
|
|
150
|
+
isValidation() {
|
|
151
|
+
return this.code === ErrorCodes.VALIDATION_ERROR || this.code === ErrorCodes.INVALID_INPUT;
|
|
152
|
+
}
|
|
153
|
+
/** Returns true if the error is a rate limit error. */
|
|
154
|
+
isRateLimit() {
|
|
155
|
+
return this.code === ErrorCodes.RATE_LIMIT;
|
|
156
|
+
}
|
|
157
|
+
/** Returns true if the error is an unauthorized error. */
|
|
158
|
+
isUnauthorized() {
|
|
159
|
+
return this.code === ErrorCodes.UNAUTHORIZED;
|
|
160
|
+
}
|
|
161
|
+
/** Returns true if the error is a forbidden error. */
|
|
162
|
+
isForbidden() {
|
|
163
|
+
return this.code === ErrorCodes.FORBIDDEN;
|
|
164
|
+
}
|
|
165
|
+
/** Returns true if the error is a service unavailable error. */
|
|
166
|
+
isUnavailable() {
|
|
167
|
+
return this.code === ErrorCodes.SERVICE_UNAVAILABLE;
|
|
168
|
+
}
|
|
100
169
|
};
|
|
101
170
|
async function parseErrorResponse(response, retries) {
|
|
102
171
|
const requestId = response.headers.get("X-ModelRelay-Chat-Request-Id") || response.headers.get("X-Request-Id") || void 0;
|
|
@@ -151,6 +220,12 @@ async function parseErrorResponse(response, retries) {
|
|
|
151
220
|
}
|
|
152
221
|
|
|
153
222
|
// src/auth.ts
|
|
223
|
+
function createApiKeyAuth(apiKey) {
|
|
224
|
+
return { apiKey };
|
|
225
|
+
}
|
|
226
|
+
function createAccessTokenAuth(accessToken) {
|
|
227
|
+
return { accessToken };
|
|
228
|
+
}
|
|
154
229
|
var AuthClient = class {
|
|
155
230
|
constructor(http, cfg) {
|
|
156
231
|
this.cachedFrontend = /* @__PURE__ */ new Map();
|
|
@@ -210,7 +285,7 @@ var AuthClient = class {
|
|
|
210
285
|
*/
|
|
211
286
|
async authForChat(customerId, overrides) {
|
|
212
287
|
if (this.accessToken) {
|
|
213
|
-
return
|
|
288
|
+
return createAccessTokenAuth(this.accessToken);
|
|
214
289
|
}
|
|
215
290
|
if (!this.apiKey) {
|
|
216
291
|
throw new ConfigError("API key or token is required");
|
|
@@ -221,21 +296,21 @@ var AuthClient = class {
|
|
|
221
296
|
deviceId: overrides?.deviceId,
|
|
222
297
|
ttlSeconds: overrides?.ttlSeconds
|
|
223
298
|
});
|
|
224
|
-
return
|
|
299
|
+
return createAccessTokenAuth(token.token);
|
|
225
300
|
}
|
|
226
|
-
return
|
|
301
|
+
return createApiKeyAuth(this.apiKey);
|
|
227
302
|
}
|
|
228
303
|
/**
|
|
229
304
|
* Billing calls accept either bearer tokens or API keys (including publishable keys).
|
|
230
305
|
*/
|
|
231
306
|
authForBilling() {
|
|
232
307
|
if (this.accessToken) {
|
|
233
|
-
return
|
|
308
|
+
return createAccessTokenAuth(this.accessToken);
|
|
234
309
|
}
|
|
235
310
|
if (!this.apiKey) {
|
|
236
311
|
throw new ConfigError("API key or token is required");
|
|
237
312
|
}
|
|
238
|
-
return
|
|
313
|
+
return createApiKeyAuth(this.apiKey);
|
|
239
314
|
}
|
|
240
315
|
};
|
|
241
316
|
function isPublishableKey(value) {
|
|
@@ -273,7 +348,7 @@ function isTokenReusable(token) {
|
|
|
273
348
|
// package.json
|
|
274
349
|
var package_default = {
|
|
275
350
|
name: "@modelrelay/sdk",
|
|
276
|
-
version: "0.
|
|
351
|
+
version: "0.14.1",
|
|
277
352
|
description: "TypeScript SDK for the ModelRelay API",
|
|
278
353
|
type: "module",
|
|
279
354
|
main: "dist/index.cjs",
|
|
@@ -307,15 +382,14 @@ var package_default = {
|
|
|
307
382
|
devDependencies: {
|
|
308
383
|
tsup: "^8.2.4",
|
|
309
384
|
typescript: "^5.6.3",
|
|
310
|
-
vitest: "^2.1.4"
|
|
385
|
+
vitest: "^2.1.4",
|
|
386
|
+
zod: "^3.23.0"
|
|
311
387
|
}
|
|
312
388
|
};
|
|
313
389
|
|
|
314
390
|
// src/types.ts
|
|
315
391
|
var SDK_VERSION = package_default.version || "0.0.0";
|
|
316
392
|
var DEFAULT_BASE_URL = "https://api.modelrelay.ai/api/v1";
|
|
317
|
-
var STAGING_BASE_URL = "https://api-stg.modelrelay.ai/api/v1";
|
|
318
|
-
var SANDBOX_BASE_URL = "https://api.sandbox.modelrelay.ai/api/v1";
|
|
319
393
|
var DEFAULT_CLIENT_HEADER = `modelrelay-ts/${SDK_VERSION}`;
|
|
320
394
|
var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
|
|
321
395
|
var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
@@ -336,22 +410,45 @@ var StopReasons = {
|
|
|
336
410
|
var Providers = {
|
|
337
411
|
OpenAI: "openai",
|
|
338
412
|
Anthropic: "anthropic",
|
|
339
|
-
|
|
340
|
-
|
|
413
|
+
XAI: "xai",
|
|
414
|
+
GoogleAIStudio: "google-ai-studio",
|
|
341
415
|
Echo: "echo"
|
|
342
416
|
};
|
|
343
417
|
var Models = {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
418
|
+
// OpenAI models (provider-agnostic identifiers)
|
|
419
|
+
Gpt4o: "gpt-4o",
|
|
420
|
+
Gpt4oMini: "gpt-4o-mini",
|
|
421
|
+
Gpt51: "gpt-5.1",
|
|
422
|
+
// Anthropic models (provider-agnostic identifiers)
|
|
423
|
+
Claude35HaikuLatest: "claude-3-5-haiku-latest",
|
|
424
|
+
Claude35SonnetLatest: "claude-3-5-sonnet-latest",
|
|
425
|
+
ClaudeOpus45: "claude-opus-4-5",
|
|
426
|
+
Claude35Haiku: "claude-3.5-haiku",
|
|
427
|
+
// xAI / Grok models
|
|
351
428
|
Grok2: "grok-2",
|
|
352
|
-
|
|
429
|
+
Grok4_1FastNonReasoning: "grok-4-1-fast-non-reasoning",
|
|
430
|
+
Grok4_1FastReasoning: "grok-4-1-fast-reasoning",
|
|
431
|
+
// Internal echo model for testing.
|
|
353
432
|
Echo1: "echo-1"
|
|
354
433
|
};
|
|
434
|
+
function createUsage(inputTokens, outputTokens, totalTokens) {
|
|
435
|
+
return {
|
|
436
|
+
inputTokens,
|
|
437
|
+
outputTokens,
|
|
438
|
+
totalTokens: totalTokens ?? inputTokens + outputTokens
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
var ToolTypes = {
|
|
442
|
+
Function: "function",
|
|
443
|
+
Web: "web",
|
|
444
|
+
XSearch: "x_search",
|
|
445
|
+
CodeExecution: "code_execution"
|
|
446
|
+
};
|
|
447
|
+
var ToolChoiceTypes = {
|
|
448
|
+
Auto: "auto",
|
|
449
|
+
Required: "required",
|
|
450
|
+
None: "none"
|
|
451
|
+
};
|
|
355
452
|
function mergeMetrics(base, override) {
|
|
356
453
|
if (!base && !override) return void 0;
|
|
357
454
|
return {
|
|
@@ -415,6 +512,630 @@ function modelToString(value) {
|
|
|
415
512
|
return value.other?.trim() || "";
|
|
416
513
|
}
|
|
417
514
|
|
|
515
|
+
// src/tools.ts
|
|
516
|
+
function createUserMessage(content) {
|
|
517
|
+
return { role: "user", content };
|
|
518
|
+
}
|
|
519
|
+
function createAssistantMessage(content) {
|
|
520
|
+
return { role: "assistant", content };
|
|
521
|
+
}
|
|
522
|
+
function createSystemMessage(content) {
|
|
523
|
+
return { role: "system", content };
|
|
524
|
+
}
|
|
525
|
+
function createToolCall(id, name, args, type = ToolTypes.Function) {
|
|
526
|
+
return {
|
|
527
|
+
id,
|
|
528
|
+
type,
|
|
529
|
+
function: createFunctionCall(name, args)
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function createFunctionCall(name, args) {
|
|
533
|
+
return { name, arguments: args };
|
|
534
|
+
}
|
|
535
|
+
function zodToJsonSchema(schema, options = {}) {
|
|
536
|
+
const result = convertZodType(schema);
|
|
537
|
+
if (options.includeSchema) {
|
|
538
|
+
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#";
|
|
539
|
+
return { $schema: schemaVersion, ...result };
|
|
540
|
+
}
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
function convertZodType(schema) {
|
|
544
|
+
const def = schema._def;
|
|
545
|
+
const typeName = def.typeName;
|
|
546
|
+
switch (typeName) {
|
|
547
|
+
case "ZodString":
|
|
548
|
+
return convertZodString(def);
|
|
549
|
+
case "ZodNumber":
|
|
550
|
+
return convertZodNumber(def);
|
|
551
|
+
case "ZodBoolean":
|
|
552
|
+
return { type: "boolean" };
|
|
553
|
+
case "ZodNull":
|
|
554
|
+
return { type: "null" };
|
|
555
|
+
case "ZodArray":
|
|
556
|
+
return convertZodArray(def);
|
|
557
|
+
case "ZodObject":
|
|
558
|
+
return convertZodObject(def);
|
|
559
|
+
case "ZodEnum":
|
|
560
|
+
return convertZodEnum(def);
|
|
561
|
+
case "ZodNativeEnum":
|
|
562
|
+
return convertZodNativeEnum(def);
|
|
563
|
+
case "ZodLiteral":
|
|
564
|
+
return { const: def.value };
|
|
565
|
+
case "ZodUnion":
|
|
566
|
+
return convertZodUnion(def);
|
|
567
|
+
case "ZodOptional": {
|
|
568
|
+
const inner = convertZodType(def.innerType);
|
|
569
|
+
if (def.description && !inner.description) {
|
|
570
|
+
inner.description = def.description;
|
|
571
|
+
}
|
|
572
|
+
return inner;
|
|
573
|
+
}
|
|
574
|
+
case "ZodNullable":
|
|
575
|
+
return convertZodNullable(def);
|
|
576
|
+
case "ZodDefault":
|
|
577
|
+
return { ...convertZodType(def.innerType), default: def.defaultValue() };
|
|
578
|
+
case "ZodEffects":
|
|
579
|
+
return convertZodType(def.schema);
|
|
580
|
+
case "ZodRecord":
|
|
581
|
+
return convertZodRecord(def);
|
|
582
|
+
case "ZodTuple":
|
|
583
|
+
return convertZodTuple(def);
|
|
584
|
+
case "ZodAny":
|
|
585
|
+
case "ZodUnknown":
|
|
586
|
+
return {};
|
|
587
|
+
default:
|
|
588
|
+
return {};
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
function convertZodString(def) {
|
|
592
|
+
const result = { type: "string" };
|
|
593
|
+
const checks = def.checks;
|
|
594
|
+
if (checks) {
|
|
595
|
+
for (const check of checks) {
|
|
596
|
+
switch (check.kind) {
|
|
597
|
+
case "min":
|
|
598
|
+
result.minLength = check.value;
|
|
599
|
+
break;
|
|
600
|
+
case "max":
|
|
601
|
+
result.maxLength = check.value;
|
|
602
|
+
break;
|
|
603
|
+
case "length":
|
|
604
|
+
result.minLength = check.value;
|
|
605
|
+
result.maxLength = check.value;
|
|
606
|
+
break;
|
|
607
|
+
case "email":
|
|
608
|
+
result.format = "email";
|
|
609
|
+
break;
|
|
610
|
+
case "url":
|
|
611
|
+
result.format = "uri";
|
|
612
|
+
break;
|
|
613
|
+
case "uuid":
|
|
614
|
+
result.format = "uuid";
|
|
615
|
+
break;
|
|
616
|
+
case "datetime":
|
|
617
|
+
result.format = "date-time";
|
|
618
|
+
break;
|
|
619
|
+
case "regex":
|
|
620
|
+
result.pattern = check.value.source;
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (def.description) {
|
|
626
|
+
result.description = def.description;
|
|
627
|
+
}
|
|
628
|
+
return result;
|
|
629
|
+
}
|
|
630
|
+
function convertZodNumber(def) {
|
|
631
|
+
const result = { type: "number" };
|
|
632
|
+
const checks = def.checks;
|
|
633
|
+
if (checks) {
|
|
634
|
+
for (const check of checks) {
|
|
635
|
+
switch (check.kind) {
|
|
636
|
+
case "int":
|
|
637
|
+
result.type = "integer";
|
|
638
|
+
break;
|
|
639
|
+
case "min":
|
|
640
|
+
if (check.inclusive === false) {
|
|
641
|
+
result.exclusiveMinimum = check.value;
|
|
642
|
+
} else {
|
|
643
|
+
result.minimum = check.value;
|
|
644
|
+
}
|
|
645
|
+
break;
|
|
646
|
+
case "max":
|
|
647
|
+
if (check.inclusive === false) {
|
|
648
|
+
result.exclusiveMaximum = check.value;
|
|
649
|
+
} else {
|
|
650
|
+
result.maximum = check.value;
|
|
651
|
+
}
|
|
652
|
+
break;
|
|
653
|
+
case "multipleOf":
|
|
654
|
+
result.multipleOf = check.value;
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (def.description) {
|
|
660
|
+
result.description = def.description;
|
|
661
|
+
}
|
|
662
|
+
return result;
|
|
663
|
+
}
|
|
664
|
+
function convertZodArray(def) {
|
|
665
|
+
const result = {
|
|
666
|
+
type: "array",
|
|
667
|
+
items: convertZodType(def.type)
|
|
668
|
+
};
|
|
669
|
+
if (def.minLength !== void 0 && def.minLength !== null) {
|
|
670
|
+
result.minItems = def.minLength.value;
|
|
671
|
+
}
|
|
672
|
+
if (def.maxLength !== void 0 && def.maxLength !== null) {
|
|
673
|
+
result.maxItems = def.maxLength.value;
|
|
674
|
+
}
|
|
675
|
+
if (def.description) {
|
|
676
|
+
result.description = def.description;
|
|
677
|
+
}
|
|
678
|
+
return result;
|
|
679
|
+
}
|
|
680
|
+
function convertZodObject(def) {
|
|
681
|
+
const shape = def.shape;
|
|
682
|
+
const shapeObj = typeof shape === "function" ? shape() : shape;
|
|
683
|
+
const properties = {};
|
|
684
|
+
const required = [];
|
|
685
|
+
for (const [key, value] of Object.entries(shapeObj)) {
|
|
686
|
+
properties[key] = convertZodType(value);
|
|
687
|
+
const valueDef = value._def;
|
|
688
|
+
const isOptional = valueDef.typeName === "ZodOptional" || valueDef.typeName === "ZodDefault" || valueDef.typeName === "ZodNullable" && valueDef.innerType?._def?.typeName === "ZodDefault";
|
|
689
|
+
if (!isOptional) {
|
|
690
|
+
required.push(key);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
const result = {
|
|
694
|
+
type: "object",
|
|
695
|
+
properties
|
|
696
|
+
};
|
|
697
|
+
if (required.length > 0) {
|
|
698
|
+
result.required = required;
|
|
699
|
+
}
|
|
700
|
+
if (def.description) {
|
|
701
|
+
result.description = def.description;
|
|
702
|
+
}
|
|
703
|
+
const unknownKeys = def.unknownKeys;
|
|
704
|
+
if (unknownKeys === "strict") {
|
|
705
|
+
result.additionalProperties = false;
|
|
706
|
+
}
|
|
707
|
+
return result;
|
|
708
|
+
}
|
|
709
|
+
function convertZodEnum(def) {
|
|
710
|
+
const result = {
|
|
711
|
+
type: "string",
|
|
712
|
+
enum: def.values
|
|
713
|
+
};
|
|
714
|
+
if (def.description) {
|
|
715
|
+
result.description = def.description;
|
|
716
|
+
}
|
|
717
|
+
return result;
|
|
718
|
+
}
|
|
719
|
+
function convertZodNativeEnum(def) {
|
|
720
|
+
const enumValues = def.values;
|
|
721
|
+
const values = Object.values(enumValues).filter(
|
|
722
|
+
(v) => typeof v === "string" || typeof v === "number"
|
|
723
|
+
);
|
|
724
|
+
const result = { enum: values };
|
|
725
|
+
if (def.description) {
|
|
726
|
+
result.description = def.description;
|
|
727
|
+
}
|
|
728
|
+
return result;
|
|
729
|
+
}
|
|
730
|
+
function convertZodUnion(def) {
|
|
731
|
+
const options = def.options;
|
|
732
|
+
const result = {
|
|
733
|
+
anyOf: options.map(convertZodType)
|
|
734
|
+
};
|
|
735
|
+
if (def.description) {
|
|
736
|
+
result.description = def.description;
|
|
737
|
+
}
|
|
738
|
+
return result;
|
|
739
|
+
}
|
|
740
|
+
function convertZodNullable(def) {
|
|
741
|
+
const inner = convertZodType(def.innerType);
|
|
742
|
+
return {
|
|
743
|
+
anyOf: [inner, { type: "null" }]
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function convertZodRecord(def) {
|
|
747
|
+
const result = {
|
|
748
|
+
type: "object",
|
|
749
|
+
additionalProperties: convertZodType(def.valueType)
|
|
750
|
+
};
|
|
751
|
+
if (def.description) {
|
|
752
|
+
result.description = def.description;
|
|
753
|
+
}
|
|
754
|
+
return result;
|
|
755
|
+
}
|
|
756
|
+
function convertZodTuple(def) {
|
|
757
|
+
const items = def.items;
|
|
758
|
+
const result = {
|
|
759
|
+
type: "array",
|
|
760
|
+
items: items.map(convertZodType),
|
|
761
|
+
minItems: items.length,
|
|
762
|
+
maxItems: items.length
|
|
763
|
+
};
|
|
764
|
+
if (def.description) {
|
|
765
|
+
result.description = def.description;
|
|
766
|
+
}
|
|
767
|
+
return result;
|
|
768
|
+
}
|
|
769
|
+
function createFunctionToolFromSchema(name, description, schema, options) {
|
|
770
|
+
const jsonSchema = zodToJsonSchema(schema, options);
|
|
771
|
+
return createFunctionTool(name, description, jsonSchema);
|
|
772
|
+
}
|
|
773
|
+
function createFunctionTool(name, description, parameters) {
|
|
774
|
+
const fn = { name, description };
|
|
775
|
+
if (parameters) {
|
|
776
|
+
fn.parameters = parameters;
|
|
777
|
+
}
|
|
778
|
+
return {
|
|
779
|
+
type: ToolTypes.Function,
|
|
780
|
+
function: fn
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
function createWebTool(options) {
|
|
784
|
+
return {
|
|
785
|
+
type: ToolTypes.Web,
|
|
786
|
+
web: options ? {
|
|
787
|
+
mode: options.mode,
|
|
788
|
+
allowedDomains: options.allowedDomains,
|
|
789
|
+
excludedDomains: options.excludedDomains,
|
|
790
|
+
maxUses: options.maxUses
|
|
791
|
+
} : void 0
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
function toolChoiceAuto() {
|
|
795
|
+
return { type: ToolChoiceTypes.Auto };
|
|
796
|
+
}
|
|
797
|
+
function toolChoiceRequired() {
|
|
798
|
+
return { type: ToolChoiceTypes.Required };
|
|
799
|
+
}
|
|
800
|
+
function toolChoiceNone() {
|
|
801
|
+
return { type: ToolChoiceTypes.None };
|
|
802
|
+
}
|
|
803
|
+
function hasToolCalls(response) {
|
|
804
|
+
return (response.toolCalls?.length ?? 0) > 0;
|
|
805
|
+
}
|
|
806
|
+
function firstToolCall(response) {
|
|
807
|
+
return response.toolCalls?.[0];
|
|
808
|
+
}
|
|
809
|
+
function toolResultMessage(toolCallId, result) {
|
|
810
|
+
const content = typeof result === "string" ? result : JSON.stringify(result);
|
|
811
|
+
return {
|
|
812
|
+
role: "tool",
|
|
813
|
+
content,
|
|
814
|
+
toolCallId
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function respondToToolCall(call, result) {
|
|
818
|
+
return toolResultMessage(call.id, result);
|
|
819
|
+
}
|
|
820
|
+
function assistantMessageWithToolCalls(content, toolCalls) {
|
|
821
|
+
return {
|
|
822
|
+
role: "assistant",
|
|
823
|
+
content,
|
|
824
|
+
toolCalls
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
var ToolCallAccumulator = class {
|
|
828
|
+
constructor() {
|
|
829
|
+
this.calls = /* @__PURE__ */ new Map();
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Processes a streaming tool call delta.
|
|
833
|
+
* Returns true if this started a new tool call.
|
|
834
|
+
*/
|
|
835
|
+
processDelta(delta) {
|
|
836
|
+
const existing = this.calls.get(delta.index);
|
|
837
|
+
if (!existing) {
|
|
838
|
+
this.calls.set(delta.index, {
|
|
839
|
+
id: delta.id ?? "",
|
|
840
|
+
type: delta.type ?? ToolTypes.Function,
|
|
841
|
+
function: {
|
|
842
|
+
name: delta.function?.name ?? "",
|
|
843
|
+
arguments: delta.function?.arguments ?? ""
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
if (delta.function) {
|
|
849
|
+
if (delta.function.name) {
|
|
850
|
+
existing.function = existing.function ?? { name: "", arguments: "" };
|
|
851
|
+
existing.function.name = delta.function.name;
|
|
852
|
+
}
|
|
853
|
+
if (delta.function.arguments) {
|
|
854
|
+
existing.function = existing.function ?? { name: "", arguments: "" };
|
|
855
|
+
existing.function.arguments += delta.function.arguments;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Returns all accumulated tool calls in index order.
|
|
862
|
+
*/
|
|
863
|
+
getToolCalls() {
|
|
864
|
+
if (this.calls.size === 0) {
|
|
865
|
+
return [];
|
|
866
|
+
}
|
|
867
|
+
const maxIdx = Math.max(...this.calls.keys());
|
|
868
|
+
const result = [];
|
|
869
|
+
for (let i = 0; i <= maxIdx; i++) {
|
|
870
|
+
const call = this.calls.get(i);
|
|
871
|
+
if (call) {
|
|
872
|
+
result.push(call);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return result;
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Returns a specific tool call by index, or undefined if not found.
|
|
879
|
+
*/
|
|
880
|
+
getToolCall(index) {
|
|
881
|
+
return this.calls.get(index);
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Clears all accumulated tool calls.
|
|
885
|
+
*/
|
|
886
|
+
reset() {
|
|
887
|
+
this.calls.clear();
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
var ToolArgsError = class extends Error {
|
|
891
|
+
constructor(message, toolCallId, toolName, rawArguments) {
|
|
892
|
+
super(message);
|
|
893
|
+
this.name = "ToolArgsError";
|
|
894
|
+
this.toolCallId = toolCallId;
|
|
895
|
+
this.toolName = toolName;
|
|
896
|
+
this.rawArguments = rawArguments;
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
function parseToolArgs(call, schema) {
|
|
900
|
+
const toolName = call.function?.name ?? "unknown";
|
|
901
|
+
const rawArgs = call.function?.arguments ?? "";
|
|
902
|
+
let parsed;
|
|
903
|
+
try {
|
|
904
|
+
parsed = rawArgs ? JSON.parse(rawArgs) : {};
|
|
905
|
+
} catch (err) {
|
|
906
|
+
const message = err instanceof Error ? err.message : "Invalid JSON in arguments";
|
|
907
|
+
throw new ToolArgsError(
|
|
908
|
+
`Failed to parse arguments for tool '${toolName}': ${message}`,
|
|
909
|
+
call.id,
|
|
910
|
+
toolName,
|
|
911
|
+
rawArgs
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
try {
|
|
915
|
+
return schema.parse(parsed);
|
|
916
|
+
} catch (err) {
|
|
917
|
+
let message;
|
|
918
|
+
if (err instanceof Error) {
|
|
919
|
+
const zodErr = err;
|
|
920
|
+
if (zodErr.errors && Array.isArray(zodErr.errors)) {
|
|
921
|
+
const issues = zodErr.errors.map((e) => {
|
|
922
|
+
const path = e.path.length > 0 ? `${e.path.join(".")}: ` : "";
|
|
923
|
+
return `${path}${e.message}`;
|
|
924
|
+
}).join("; ");
|
|
925
|
+
message = issues;
|
|
926
|
+
} else {
|
|
927
|
+
message = err.message;
|
|
928
|
+
}
|
|
929
|
+
} else {
|
|
930
|
+
message = String(err);
|
|
931
|
+
}
|
|
932
|
+
throw new ToolArgsError(
|
|
933
|
+
`Invalid arguments for tool '${toolName}': ${message}`,
|
|
934
|
+
call.id,
|
|
935
|
+
toolName,
|
|
936
|
+
rawArgs
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
function tryParseToolArgs(call, schema) {
|
|
941
|
+
try {
|
|
942
|
+
const data = parseToolArgs(call, schema);
|
|
943
|
+
return { success: true, data };
|
|
944
|
+
} catch (err) {
|
|
945
|
+
if (err instanceof ToolArgsError) {
|
|
946
|
+
return { success: false, error: err };
|
|
947
|
+
}
|
|
948
|
+
const toolName = call.function?.name ?? "unknown";
|
|
949
|
+
const rawArgs = call.function?.arguments ?? "";
|
|
950
|
+
return {
|
|
951
|
+
success: false,
|
|
952
|
+
error: new ToolArgsError(
|
|
953
|
+
err instanceof Error ? err.message : String(err),
|
|
954
|
+
call.id,
|
|
955
|
+
toolName,
|
|
956
|
+
rawArgs
|
|
957
|
+
)
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
function parseToolArgsRaw(call) {
|
|
962
|
+
const toolName = call.function?.name ?? "unknown";
|
|
963
|
+
const rawArgs = call.function?.arguments ?? "";
|
|
964
|
+
try {
|
|
965
|
+
return rawArgs ? JSON.parse(rawArgs) : {};
|
|
966
|
+
} catch (err) {
|
|
967
|
+
const message = err instanceof Error ? err.message : "Invalid JSON in arguments";
|
|
968
|
+
throw new ToolArgsError(
|
|
969
|
+
`Failed to parse arguments for tool '${toolName}': ${message}`,
|
|
970
|
+
call.id,
|
|
971
|
+
toolName,
|
|
972
|
+
rawArgs
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
var ToolRegistry = class {
|
|
977
|
+
constructor() {
|
|
978
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Registers a handler function for a tool name.
|
|
982
|
+
* @param name - The tool name (must match the function name in the tool definition)
|
|
983
|
+
* @param handler - Function to execute when this tool is called
|
|
984
|
+
* @returns this for chaining
|
|
985
|
+
*/
|
|
986
|
+
register(name, handler) {
|
|
987
|
+
this.handlers.set(name, handler);
|
|
988
|
+
return this;
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Unregisters a tool handler.
|
|
992
|
+
* @param name - The tool name to unregister
|
|
993
|
+
* @returns true if the handler was removed, false if it didn't exist
|
|
994
|
+
*/
|
|
995
|
+
unregister(name) {
|
|
996
|
+
return this.handlers.delete(name);
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Checks if a handler is registered for the given tool name.
|
|
1000
|
+
*/
|
|
1001
|
+
has(name) {
|
|
1002
|
+
return this.handlers.has(name);
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Returns the list of registered tool names.
|
|
1006
|
+
*/
|
|
1007
|
+
getRegisteredTools() {
|
|
1008
|
+
return Array.from(this.handlers.keys());
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Executes a single tool call.
|
|
1012
|
+
* @param call - The tool call to execute
|
|
1013
|
+
* @returns The execution result
|
|
1014
|
+
*/
|
|
1015
|
+
async execute(call) {
|
|
1016
|
+
const toolName = call.function?.name ?? "";
|
|
1017
|
+
const handler = this.handlers.get(toolName);
|
|
1018
|
+
if (!handler) {
|
|
1019
|
+
return {
|
|
1020
|
+
toolCallId: call.id,
|
|
1021
|
+
toolName,
|
|
1022
|
+
result: null,
|
|
1023
|
+
error: `Unknown tool: '${toolName}'. Available tools: ${this.getRegisteredTools().join(", ") || "none"}`
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
let args;
|
|
1027
|
+
try {
|
|
1028
|
+
args = call.function?.arguments ? JSON.parse(call.function.arguments) : {};
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1031
|
+
return {
|
|
1032
|
+
toolCallId: call.id,
|
|
1033
|
+
toolName,
|
|
1034
|
+
result: null,
|
|
1035
|
+
error: `Invalid JSON in arguments: ${errorMessage}`,
|
|
1036
|
+
isRetryable: true
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
try {
|
|
1040
|
+
const result = await handler(args, call);
|
|
1041
|
+
return {
|
|
1042
|
+
toolCallId: call.id,
|
|
1043
|
+
toolName,
|
|
1044
|
+
result
|
|
1045
|
+
};
|
|
1046
|
+
} catch (err) {
|
|
1047
|
+
const isRetryable = err instanceof ToolArgsError;
|
|
1048
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1049
|
+
return {
|
|
1050
|
+
toolCallId: call.id,
|
|
1051
|
+
toolName,
|
|
1052
|
+
result: null,
|
|
1053
|
+
error: errorMessage,
|
|
1054
|
+
isRetryable
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Executes multiple tool calls in parallel.
|
|
1060
|
+
* @param calls - Array of tool calls to execute
|
|
1061
|
+
* @returns Array of execution results in the same order as input
|
|
1062
|
+
*/
|
|
1063
|
+
async executeAll(calls) {
|
|
1064
|
+
return Promise.all(calls.map((call) => this.execute(call)));
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Converts execution results to tool result messages.
|
|
1068
|
+
* Useful for appending to the conversation history.
|
|
1069
|
+
* @param results - Array of execution results
|
|
1070
|
+
* @returns Array of ChatMessage objects with role "tool"
|
|
1071
|
+
*/
|
|
1072
|
+
resultsToMessages(results) {
|
|
1073
|
+
return results.map((r) => {
|
|
1074
|
+
const content = r.error ? `Error: ${r.error}` : typeof r.result === "string" ? r.result : JSON.stringify(r.result);
|
|
1075
|
+
return toolResultMessage(r.toolCallId, content);
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
function formatToolErrorForModel(result) {
|
|
1080
|
+
const lines = [
|
|
1081
|
+
`Tool call error for '${result.toolName}': ${result.error}`
|
|
1082
|
+
];
|
|
1083
|
+
if (result.isRetryable) {
|
|
1084
|
+
lines.push("");
|
|
1085
|
+
lines.push("Please correct the arguments and try again.");
|
|
1086
|
+
}
|
|
1087
|
+
return lines.join("\n");
|
|
1088
|
+
}
|
|
1089
|
+
function hasRetryableErrors(results) {
|
|
1090
|
+
return results.some((r) => r.error && r.isRetryable);
|
|
1091
|
+
}
|
|
1092
|
+
function getRetryableErrors(results) {
|
|
1093
|
+
return results.filter((r) => r.error && r.isRetryable);
|
|
1094
|
+
}
|
|
1095
|
+
function createRetryMessages(results) {
|
|
1096
|
+
return results.filter((r) => r.error && r.isRetryable).map((r) => toolResultMessage(r.toolCallId, formatToolErrorForModel(r)));
|
|
1097
|
+
}
|
|
1098
|
+
async function executeWithRetry(registry, toolCalls, options = {}) {
|
|
1099
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
1100
|
+
let currentCalls = toolCalls;
|
|
1101
|
+
let attempt = 0;
|
|
1102
|
+
const successfulResults = /* @__PURE__ */ new Map();
|
|
1103
|
+
while (attempt <= maxRetries) {
|
|
1104
|
+
const results = await registry.executeAll(currentCalls);
|
|
1105
|
+
for (const result of results) {
|
|
1106
|
+
if (!result.error || !result.isRetryable) {
|
|
1107
|
+
successfulResults.set(result.toolCallId, result);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
const retryableResults = getRetryableErrors(results);
|
|
1111
|
+
if (retryableResults.length === 0 || !options.onRetry) {
|
|
1112
|
+
for (const result of results) {
|
|
1113
|
+
if (result.error && result.isRetryable) {
|
|
1114
|
+
successfulResults.set(result.toolCallId, result);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return Array.from(successfulResults.values());
|
|
1118
|
+
}
|
|
1119
|
+
attempt++;
|
|
1120
|
+
if (attempt > maxRetries) {
|
|
1121
|
+
for (const result of retryableResults) {
|
|
1122
|
+
successfulResults.set(result.toolCallId, result);
|
|
1123
|
+
}
|
|
1124
|
+
return Array.from(successfulResults.values());
|
|
1125
|
+
}
|
|
1126
|
+
const errorMessages = createRetryMessages(retryableResults);
|
|
1127
|
+
const newCalls = await options.onRetry(errorMessages, attempt);
|
|
1128
|
+
if (newCalls.length === 0) {
|
|
1129
|
+
for (const result of retryableResults) {
|
|
1130
|
+
successfulResults.set(result.toolCallId, result);
|
|
1131
|
+
}
|
|
1132
|
+
return Array.from(successfulResults.values());
|
|
1133
|
+
}
|
|
1134
|
+
currentCalls = newCalls;
|
|
1135
|
+
}
|
|
1136
|
+
return Array.from(successfulResults.values());
|
|
1137
|
+
}
|
|
1138
|
+
|
|
418
1139
|
// src/chat.ts
|
|
419
1140
|
var REQUEST_ID_HEADER = "X-ModelRelay-Chat-Request-Id";
|
|
420
1141
|
var ChatClient = class {
|
|
@@ -440,16 +1161,13 @@ var ChatCompletionsClient = class {
|
|
|
440
1161
|
const stream = options.stream ?? params.stream ?? true;
|
|
441
1162
|
const metrics = mergeMetrics(this.metrics, options.metrics);
|
|
442
1163
|
const trace = mergeTrace(this.trace, options.trace);
|
|
443
|
-
const modelValue = modelToString(params.model).trim();
|
|
444
|
-
if (!modelValue) {
|
|
445
|
-
throw new ConfigError("model is required");
|
|
446
|
-
}
|
|
447
1164
|
if (!params?.messages?.length) {
|
|
448
1165
|
throw new ConfigError("at least one message is required");
|
|
449
1166
|
}
|
|
450
1167
|
if (!hasUserMessage(params.messages)) {
|
|
451
1168
|
throw new ConfigError("at least one user message is required");
|
|
452
1169
|
}
|
|
1170
|
+
validateRequestModel(params.model);
|
|
453
1171
|
const authHeaders = await this.auth.authForChat(params.customerId);
|
|
454
1172
|
const body = buildProxyBody(
|
|
455
1173
|
params,
|
|
@@ -588,7 +1306,7 @@ var ChatCompletionsStream = class {
|
|
|
588
1306
|
const context = this.enrichContext(evt);
|
|
589
1307
|
this.context = context;
|
|
590
1308
|
this.trace?.streamEvent?.({ context, event: evt });
|
|
591
|
-
if (evt.type === "message_start" || evt.type === "message_delta" || evt.type === "message_stop") {
|
|
1309
|
+
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") {
|
|
592
1310
|
this.recordFirstToken();
|
|
593
1311
|
}
|
|
594
1312
|
if (evt.type === "message_stop" && evt.usage && this.metrics?.usage) {
|
|
@@ -673,11 +1391,15 @@ function mapChatEvent(raw, requestId) {
|
|
|
673
1391
|
const model = normalizeModelId(p.model || p?.message?.model);
|
|
674
1392
|
const stopReason = normalizeStopReason(p.stop_reason);
|
|
675
1393
|
const textDelta = extractTextDelta(p);
|
|
1394
|
+
const toolCallDelta = extractToolCallDelta(p, type);
|
|
1395
|
+
const toolCalls = extractToolCalls(p, type);
|
|
676
1396
|
return {
|
|
677
1397
|
type,
|
|
678
1398
|
event: raw.event || type,
|
|
679
1399
|
data: p,
|
|
680
1400
|
textDelta,
|
|
1401
|
+
toolCallDelta,
|
|
1402
|
+
toolCalls,
|
|
681
1403
|
responseId,
|
|
682
1404
|
model,
|
|
683
1405
|
stopReason,
|
|
@@ -697,6 +1419,12 @@ function normalizeEventType(eventName, payload) {
|
|
|
697
1419
|
return "message_delta";
|
|
698
1420
|
case "message_stop":
|
|
699
1421
|
return "message_stop";
|
|
1422
|
+
case "tool_use_start":
|
|
1423
|
+
return "tool_use_start";
|
|
1424
|
+
case "tool_use_delta":
|
|
1425
|
+
return "tool_use_delta";
|
|
1426
|
+
case "tool_use_stop":
|
|
1427
|
+
return "tool_use_stop";
|
|
700
1428
|
case "ping":
|
|
701
1429
|
return "ping";
|
|
702
1430
|
default:
|
|
@@ -707,6 +1435,9 @@ function extractTextDelta(payload) {
|
|
|
707
1435
|
if (!payload || typeof payload !== "object") {
|
|
708
1436
|
return void 0;
|
|
709
1437
|
}
|
|
1438
|
+
if (typeof payload.text_delta === "string" && payload.text_delta !== "") {
|
|
1439
|
+
return payload.text_delta;
|
|
1440
|
+
}
|
|
710
1441
|
if (typeof payload.delta === "string") {
|
|
711
1442
|
return payload.delta;
|
|
712
1443
|
}
|
|
@@ -720,9 +1451,56 @@ function extractTextDelta(payload) {
|
|
|
720
1451
|
}
|
|
721
1452
|
return void 0;
|
|
722
1453
|
}
|
|
1454
|
+
function extractToolCallDelta(payload, type) {
|
|
1455
|
+
if (!payload || typeof payload !== "object") {
|
|
1456
|
+
return void 0;
|
|
1457
|
+
}
|
|
1458
|
+
if (type !== "tool_use_start" && type !== "tool_use_delta") {
|
|
1459
|
+
return void 0;
|
|
1460
|
+
}
|
|
1461
|
+
if (payload.tool_call_delta) {
|
|
1462
|
+
const d = payload.tool_call_delta;
|
|
1463
|
+
return {
|
|
1464
|
+
index: d.index ?? 0,
|
|
1465
|
+
id: d.id,
|
|
1466
|
+
type: d.type,
|
|
1467
|
+
function: d.function ? {
|
|
1468
|
+
name: d.function.name,
|
|
1469
|
+
arguments: d.function.arguments
|
|
1470
|
+
} : void 0
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
if (typeof payload.index === "number" || payload.id || payload.name) {
|
|
1474
|
+
return {
|
|
1475
|
+
index: payload.index ?? 0,
|
|
1476
|
+
id: payload.id,
|
|
1477
|
+
type: payload.tool_type,
|
|
1478
|
+
function: payload.name || payload.arguments ? {
|
|
1479
|
+
name: payload.name,
|
|
1480
|
+
arguments: payload.arguments
|
|
1481
|
+
} : void 0
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
return void 0;
|
|
1485
|
+
}
|
|
1486
|
+
function extractToolCalls(payload, type) {
|
|
1487
|
+
if (!payload || typeof payload !== "object") {
|
|
1488
|
+
return void 0;
|
|
1489
|
+
}
|
|
1490
|
+
if (type !== "tool_use_stop" && type !== "message_stop") {
|
|
1491
|
+
return void 0;
|
|
1492
|
+
}
|
|
1493
|
+
if (payload.tool_calls?.length) {
|
|
1494
|
+
return normalizeToolCalls(payload.tool_calls);
|
|
1495
|
+
}
|
|
1496
|
+
if (payload.tool_call) {
|
|
1497
|
+
return normalizeToolCalls([payload.tool_call]);
|
|
1498
|
+
}
|
|
1499
|
+
return void 0;
|
|
1500
|
+
}
|
|
723
1501
|
function normalizeChatResponse(payload, requestId) {
|
|
724
1502
|
const p = payload;
|
|
725
|
-
|
|
1503
|
+
const response = {
|
|
726
1504
|
id: p?.id,
|
|
727
1505
|
provider: normalizeProvider(p?.provider),
|
|
728
1506
|
content: Array.isArray(p?.content) ? p.content : p?.content ? [String(p.content)] : [],
|
|
@@ -731,26 +1509,51 @@ function normalizeChatResponse(payload, requestId) {
|
|
|
731
1509
|
usage: normalizeUsage(p?.usage),
|
|
732
1510
|
requestId
|
|
733
1511
|
};
|
|
1512
|
+
if (p?.tool_calls?.length) {
|
|
1513
|
+
response.toolCalls = normalizeToolCalls(p.tool_calls);
|
|
1514
|
+
}
|
|
1515
|
+
return response;
|
|
1516
|
+
}
|
|
1517
|
+
function normalizeToolCalls(toolCalls) {
|
|
1518
|
+
return toolCalls.map(
|
|
1519
|
+
(tc) => createToolCall(
|
|
1520
|
+
tc.id,
|
|
1521
|
+
tc.function?.name ?? "",
|
|
1522
|
+
tc.function?.arguments ?? "",
|
|
1523
|
+
tc.type || ToolTypes.Function
|
|
1524
|
+
)
|
|
1525
|
+
);
|
|
734
1526
|
}
|
|
735
1527
|
function normalizeUsage(payload) {
|
|
736
1528
|
if (!payload) {
|
|
737
|
-
return
|
|
1529
|
+
return createUsage(0, 0, 0);
|
|
738
1530
|
}
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1531
|
+
const inputTokens = Number(payload.input_tokens ?? 0);
|
|
1532
|
+
const outputTokens = Number(payload.output_tokens ?? 0);
|
|
1533
|
+
const totalTokens = Number(payload.total_tokens ?? 0);
|
|
1534
|
+
return createUsage(inputTokens, outputTokens, totalTokens || void 0);
|
|
1535
|
+
}
|
|
1536
|
+
function validateRequestModel(model) {
|
|
1537
|
+
if (model === void 0 || model === null) return;
|
|
1538
|
+
const value = modelToString(model).trim();
|
|
1539
|
+
if (!value) {
|
|
1540
|
+
throw new ConfigError("model id must be a non-empty string when provided");
|
|
1541
|
+
}
|
|
1542
|
+
const knownModels = Object.values(Models);
|
|
1543
|
+
if (!knownModels.includes(value)) {
|
|
1544
|
+
throw new ConfigError(
|
|
1545
|
+
`unsupported model id "${value}". Use one of the SDK Models.* constants or omit model to use the tier's default model.`
|
|
1546
|
+
);
|
|
746
1547
|
}
|
|
747
|
-
return usage;
|
|
748
1548
|
}
|
|
749
1549
|
function buildProxyBody(params, metadata) {
|
|
1550
|
+
const modelValue = params.model ? modelToString(params.model).trim() : "";
|
|
750
1551
|
const body = {
|
|
751
|
-
model: modelToString(params.model),
|
|
752
1552
|
messages: normalizeMessages(params.messages)
|
|
753
1553
|
};
|
|
1554
|
+
if (modelValue) {
|
|
1555
|
+
body.model = modelValue;
|
|
1556
|
+
}
|
|
754
1557
|
if (typeof params.maxTokens === "number") body.max_tokens = params.maxTokens;
|
|
755
1558
|
if (params.provider) body.provider = providerToString(params.provider);
|
|
756
1559
|
if (typeof params.temperature === "number")
|
|
@@ -758,13 +1561,69 @@ function buildProxyBody(params, metadata) {
|
|
|
758
1561
|
if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;
|
|
759
1562
|
if (params.stop?.length) body.stop = params.stop;
|
|
760
1563
|
if (params.stopSequences?.length) body.stop_sequences = params.stopSequences;
|
|
1564
|
+
if (params.tools?.length) body.tools = normalizeTools(params.tools);
|
|
1565
|
+
if (params.toolChoice) body.tool_choice = normalizeToolChoice(params.toolChoice);
|
|
761
1566
|
return body;
|
|
762
1567
|
}
|
|
763
1568
|
function normalizeMessages(messages) {
|
|
764
|
-
return messages.map((msg) =>
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
1569
|
+
return messages.map((msg) => {
|
|
1570
|
+
const normalized = {
|
|
1571
|
+
role: msg.role || "user",
|
|
1572
|
+
content: msg.content
|
|
1573
|
+
};
|
|
1574
|
+
if (msg.toolCalls?.length) {
|
|
1575
|
+
normalized.tool_calls = msg.toolCalls.map((tc) => ({
|
|
1576
|
+
id: tc.id,
|
|
1577
|
+
type: tc.type,
|
|
1578
|
+
function: tc.function ? createFunctionCall(tc.function.name, tc.function.arguments) : void 0
|
|
1579
|
+
}));
|
|
1580
|
+
}
|
|
1581
|
+
if (msg.toolCallId) {
|
|
1582
|
+
normalized.tool_call_id = msg.toolCallId;
|
|
1583
|
+
}
|
|
1584
|
+
return normalized;
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
function normalizeTools(tools) {
|
|
1588
|
+
return tools.map((tool) => {
|
|
1589
|
+
const normalized = { type: tool.type };
|
|
1590
|
+
if (tool.function) {
|
|
1591
|
+
normalized.function = {
|
|
1592
|
+
name: tool.function.name,
|
|
1593
|
+
description: tool.function.description,
|
|
1594
|
+
parameters: tool.function.parameters
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
if (tool.web) {
|
|
1598
|
+
const web = {
|
|
1599
|
+
allowed_domains: tool.web.allowedDomains,
|
|
1600
|
+
excluded_domains: tool.web.excludedDomains,
|
|
1601
|
+
max_uses: tool.web.maxUses
|
|
1602
|
+
};
|
|
1603
|
+
if (tool.web.mode) {
|
|
1604
|
+
web.mode = tool.web.mode;
|
|
1605
|
+
}
|
|
1606
|
+
normalized.web = web;
|
|
1607
|
+
}
|
|
1608
|
+
if (tool.xSearch) {
|
|
1609
|
+
normalized.x_search = {
|
|
1610
|
+
allowed_handles: tool.xSearch.allowedHandles,
|
|
1611
|
+
excluded_handles: tool.xSearch.excludedHandles,
|
|
1612
|
+
from_date: tool.xSearch.fromDate,
|
|
1613
|
+
to_date: tool.xSearch.toDate
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
if (tool.codeExecution) {
|
|
1617
|
+
normalized.code_execution = {
|
|
1618
|
+
language: tool.codeExecution.language,
|
|
1619
|
+
timeout_ms: tool.codeExecution.timeoutMs
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
return normalized;
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
function normalizeToolChoice(tc) {
|
|
1626
|
+
return { type: tc.type };
|
|
768
1627
|
}
|
|
769
1628
|
function requestIdFromHeaders(headers) {
|
|
770
1629
|
return headers.get(REQUEST_ID_HEADER) || headers.get("X-Request-Id") || void 0;
|
|
@@ -789,6 +1648,10 @@ function hasUserMessage(messages) {
|
|
|
789
1648
|
}
|
|
790
1649
|
|
|
791
1650
|
// src/customers.ts
|
|
1651
|
+
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1652
|
+
function isValidEmail(email) {
|
|
1653
|
+
return EMAIL_REGEX.test(email);
|
|
1654
|
+
}
|
|
792
1655
|
var CustomersClient = class {
|
|
793
1656
|
constructor(http, cfg) {
|
|
794
1657
|
this.http = http;
|
|
@@ -823,6 +1686,12 @@ var CustomersClient = class {
|
|
|
823
1686
|
if (!request.external_id?.trim()) {
|
|
824
1687
|
throw new ConfigError("external_id is required");
|
|
825
1688
|
}
|
|
1689
|
+
if (!request.email?.trim()) {
|
|
1690
|
+
throw new ConfigError("email is required");
|
|
1691
|
+
}
|
|
1692
|
+
if (!isValidEmail(request.email)) {
|
|
1693
|
+
throw new ConfigError("invalid email format");
|
|
1694
|
+
}
|
|
826
1695
|
const response = await this.http.json("/customers", {
|
|
827
1696
|
method: "POST",
|
|
828
1697
|
body: request,
|
|
@@ -860,6 +1729,12 @@ var CustomersClient = class {
|
|
|
860
1729
|
if (!request.external_id?.trim()) {
|
|
861
1730
|
throw new ConfigError("external_id is required");
|
|
862
1731
|
}
|
|
1732
|
+
if (!request.email?.trim()) {
|
|
1733
|
+
throw new ConfigError("email is required");
|
|
1734
|
+
}
|
|
1735
|
+
if (!isValidEmail(request.email)) {
|
|
1736
|
+
throw new ConfigError("invalid email format");
|
|
1737
|
+
}
|
|
863
1738
|
const response = await this.http.json("/customers", {
|
|
864
1739
|
method: "PUT",
|
|
865
1740
|
body: request,
|
|
@@ -961,10 +1836,7 @@ var TiersClient = class {
|
|
|
961
1836
|
// src/http.ts
|
|
962
1837
|
var HTTPClient = class {
|
|
963
1838
|
constructor(cfg) {
|
|
964
|
-
const
|
|
965
|
-
const resolvedBase = normalizeBaseUrl(
|
|
966
|
-
cfg.baseUrl || baseFromEnv || DEFAULT_BASE_URL
|
|
967
|
-
);
|
|
1839
|
+
const resolvedBase = normalizeBaseUrl(cfg.baseUrl || DEFAULT_BASE_URL);
|
|
968
1840
|
if (!isValidHttpUrl(resolvedBase)) {
|
|
969
1841
|
throw new ConfigError(
|
|
970
1842
|
"baseUrl must start with http:// or https://"
|
|
@@ -1177,12 +2049,6 @@ function normalizeBaseUrl(value) {
|
|
|
1177
2049
|
function isValidHttpUrl(value) {
|
|
1178
2050
|
return /^https?:\/\//i.test(value);
|
|
1179
2051
|
}
|
|
1180
|
-
function baseUrlForEnvironment(env) {
|
|
1181
|
-
if (!env || env === "production") return void 0;
|
|
1182
|
-
if (env === "staging") return STAGING_BASE_URL;
|
|
1183
|
-
if (env === "sandbox") return SANDBOX_BASE_URL;
|
|
1184
|
-
return void 0;
|
|
1185
|
-
}
|
|
1186
2052
|
function normalizeRetryConfig(retry) {
|
|
1187
2053
|
if (retry === false) return void 0;
|
|
1188
2054
|
const cfg = retry || {};
|
|
@@ -1301,7 +2167,7 @@ var ModelRelay = class {
|
|
|
1301
2167
|
if (!cfg.key && !cfg.token) {
|
|
1302
2168
|
throw new ConfigError("Provide an API key or access token");
|
|
1303
2169
|
}
|
|
1304
|
-
this.baseUrl = resolveBaseUrl(cfg.
|
|
2170
|
+
this.baseUrl = resolveBaseUrl(cfg.baseUrl);
|
|
1305
2171
|
const http = new HTTPClient({
|
|
1306
2172
|
baseUrl: this.baseUrl,
|
|
1307
2173
|
apiKey: cfg.key,
|
|
@@ -1312,7 +2178,6 @@ var ModelRelay = class {
|
|
|
1312
2178
|
timeoutMs: cfg.timeoutMs,
|
|
1313
2179
|
retry: cfg.retry,
|
|
1314
2180
|
defaultHeaders: cfg.defaultHeaders,
|
|
1315
|
-
environment: cfg.environment,
|
|
1316
2181
|
metrics: cfg.metrics,
|
|
1317
2182
|
trace: cfg.trace
|
|
1318
2183
|
});
|
|
@@ -1335,8 +2200,8 @@ var ModelRelay = class {
|
|
|
1335
2200
|
});
|
|
1336
2201
|
}
|
|
1337
2202
|
};
|
|
1338
|
-
function resolveBaseUrl(
|
|
1339
|
-
const base = override ||
|
|
2203
|
+
function resolveBaseUrl(override) {
|
|
2204
|
+
const base = override || DEFAULT_BASE_URL;
|
|
1340
2205
|
return base.replace(/\/+$/, "");
|
|
1341
2206
|
}
|
|
1342
2207
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1351,16 +2216,39 @@ function resolveBaseUrl(env, override) {
|
|
|
1351
2216
|
DEFAULT_CLIENT_HEADER,
|
|
1352
2217
|
DEFAULT_CONNECT_TIMEOUT_MS,
|
|
1353
2218
|
DEFAULT_REQUEST_TIMEOUT_MS,
|
|
2219
|
+
ErrorCodes,
|
|
1354
2220
|
ModelRelay,
|
|
1355
2221
|
ModelRelayError,
|
|
1356
2222
|
Models,
|
|
1357
2223
|
Providers,
|
|
1358
|
-
SANDBOX_BASE_URL,
|
|
1359
2224
|
SDK_VERSION,
|
|
1360
|
-
STAGING_BASE_URL,
|
|
1361
2225
|
StopReasons,
|
|
1362
2226
|
TiersClient,
|
|
2227
|
+
ToolArgsError,
|
|
2228
|
+
ToolCallAccumulator,
|
|
2229
|
+
ToolChoiceTypes,
|
|
2230
|
+
ToolRegistry,
|
|
2231
|
+
ToolTypes,
|
|
1363
2232
|
TransportError,
|
|
2233
|
+
assistantMessageWithToolCalls,
|
|
2234
|
+
createAccessTokenAuth,
|
|
2235
|
+
createApiKeyAuth,
|
|
2236
|
+
createAssistantMessage,
|
|
2237
|
+
createFunctionCall,
|
|
2238
|
+
createFunctionTool,
|
|
2239
|
+
createFunctionToolFromSchema,
|
|
2240
|
+
createRetryMessages,
|
|
2241
|
+
createSystemMessage,
|
|
2242
|
+
createToolCall,
|
|
2243
|
+
createUsage,
|
|
2244
|
+
createUserMessage,
|
|
2245
|
+
createWebTool,
|
|
2246
|
+
executeWithRetry,
|
|
2247
|
+
firstToolCall,
|
|
2248
|
+
formatToolErrorForModel,
|
|
2249
|
+
getRetryableErrors,
|
|
2250
|
+
hasRetryableErrors,
|
|
2251
|
+
hasToolCalls,
|
|
1364
2252
|
isPublishableKey,
|
|
1365
2253
|
mergeMetrics,
|
|
1366
2254
|
mergeTrace,
|
|
@@ -1369,6 +2257,15 @@ function resolveBaseUrl(env, override) {
|
|
|
1369
2257
|
normalizeProvider,
|
|
1370
2258
|
normalizeStopReason,
|
|
1371
2259
|
parseErrorResponse,
|
|
2260
|
+
parseToolArgs,
|
|
2261
|
+
parseToolArgsRaw,
|
|
1372
2262
|
providerToString,
|
|
1373
|
-
|
|
2263
|
+
respondToToolCall,
|
|
2264
|
+
stopReasonToString,
|
|
2265
|
+
toolChoiceAuto,
|
|
2266
|
+
toolChoiceNone,
|
|
2267
|
+
toolChoiceRequired,
|
|
2268
|
+
toolResultMessage,
|
|
2269
|
+
tryParseToolArgs,
|
|
2270
|
+
zodToJsonSchema
|
|
1374
2271
|
});
|