@modelrelay/sdk 8.1.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-AQJ4VKNC.js +1199 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +1 -1
- package/dist/tools-QkA_kWul.d.cts +1042 -0
- package/dist/tools-QkA_kWul.d.ts +1042 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1199 @@
|
|
|
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
|
+
};
|
|
15
|
+
var ModelRelayError = class extends Error {
|
|
16
|
+
constructor(message, opts) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = this.constructor.name;
|
|
19
|
+
this.category = opts.category;
|
|
20
|
+
this.status = opts.status;
|
|
21
|
+
this.code = opts.code;
|
|
22
|
+
this.requestId = opts.requestId;
|
|
23
|
+
this.fields = opts.fields;
|
|
24
|
+
this.data = opts.data;
|
|
25
|
+
this.retries = opts.retries;
|
|
26
|
+
this.cause = opts.cause;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var ConfigError = class extends ModelRelayError {
|
|
30
|
+
constructor(message, data) {
|
|
31
|
+
super(message, { category: "config", status: 400, data });
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var TransportError = class extends ModelRelayError {
|
|
35
|
+
constructor(message, opts) {
|
|
36
|
+
super(message, {
|
|
37
|
+
category: "transport",
|
|
38
|
+
status: opts.kind === "timeout" ? 408 : 0,
|
|
39
|
+
retries: opts.retries,
|
|
40
|
+
cause: opts.cause,
|
|
41
|
+
data: opts.cause
|
|
42
|
+
});
|
|
43
|
+
this.kind = opts.kind;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var StreamProtocolError = class extends TransportError {
|
|
47
|
+
constructor(opts) {
|
|
48
|
+
const got = opts.receivedContentType?.trim() || "<missing>";
|
|
49
|
+
super(`expected NDJSON stream (${opts.expectedContentType}), got Content-Type ${got}`, {
|
|
50
|
+
kind: "request"
|
|
51
|
+
});
|
|
52
|
+
this.expectedContentType = opts.expectedContentType;
|
|
53
|
+
this.receivedContentType = opts.receivedContentType?.trim() || void 0;
|
|
54
|
+
this.status = opts.status;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var StreamTimeoutError = class extends ModelRelayError {
|
|
58
|
+
constructor(streamKind, timeoutMs) {
|
|
59
|
+
const label = streamKind === "ttft" ? "TTFT" : streamKind === "idle" ? "idle" : "total";
|
|
60
|
+
super(`stream ${label} timeout after ${timeoutMs}ms`, { category: "transport", status: 408 });
|
|
61
|
+
this.kind = streamKind;
|
|
62
|
+
this.timeoutMs = timeoutMs;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var APIError = class extends ModelRelayError {
|
|
66
|
+
constructor(message, opts) {
|
|
67
|
+
super(message, {
|
|
68
|
+
category: "api",
|
|
69
|
+
status: opts.status,
|
|
70
|
+
code: opts.code,
|
|
71
|
+
requestId: opts.requestId,
|
|
72
|
+
fields: opts.fields,
|
|
73
|
+
data: opts.data,
|
|
74
|
+
retries: opts.retries
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/** Returns true if the error is a not found error. */
|
|
78
|
+
isNotFound() {
|
|
79
|
+
return this.code === ErrorCodes.NOT_FOUND;
|
|
80
|
+
}
|
|
81
|
+
/** Returns true if the error is a validation error. */
|
|
82
|
+
isValidation() {
|
|
83
|
+
return this.code === ErrorCodes.VALIDATION_ERROR || this.code === ErrorCodes.INVALID_INPUT;
|
|
84
|
+
}
|
|
85
|
+
/** Returns true if the error is a rate limit error. */
|
|
86
|
+
isRateLimit() {
|
|
87
|
+
return this.code === ErrorCodes.RATE_LIMIT;
|
|
88
|
+
}
|
|
89
|
+
/** Returns true if the error is an unauthorized error. */
|
|
90
|
+
isUnauthorized() {
|
|
91
|
+
return this.code === ErrorCodes.UNAUTHORIZED;
|
|
92
|
+
}
|
|
93
|
+
/** Returns true if the error is a forbidden error. */
|
|
94
|
+
isForbidden() {
|
|
95
|
+
return this.code === ErrorCodes.FORBIDDEN;
|
|
96
|
+
}
|
|
97
|
+
/** Returns true if the error is a service unavailable error. */
|
|
98
|
+
isUnavailable() {
|
|
99
|
+
return this.code === ErrorCodes.SERVICE_UNAVAILABLE;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var WorkflowValidationError = class extends ModelRelayError {
|
|
103
|
+
constructor(opts) {
|
|
104
|
+
const msg = opts.issues.length === 0 ? "workflow validation error" : opts.issues[0]?.message || "workflow validation error";
|
|
105
|
+
super(msg, {
|
|
106
|
+
category: "api",
|
|
107
|
+
status: opts.status,
|
|
108
|
+
requestId: opts.requestId,
|
|
109
|
+
data: opts.data,
|
|
110
|
+
retries: opts.retries
|
|
111
|
+
});
|
|
112
|
+
this.issues = opts.issues;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var ToolArgumentError = class extends ModelRelayError {
|
|
116
|
+
constructor(opts) {
|
|
117
|
+
super(opts.message, {
|
|
118
|
+
category: "config",
|
|
119
|
+
status: 400,
|
|
120
|
+
cause: opts.cause
|
|
121
|
+
});
|
|
122
|
+
this.toolCallId = opts.toolCallId;
|
|
123
|
+
this.toolName = opts.toolName;
|
|
124
|
+
this.rawArguments = opts.rawArguments;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var PathEscapeError = class extends ModelRelayError {
|
|
128
|
+
constructor(opts) {
|
|
129
|
+
super(`path escapes sandbox: ${opts.requestedPath}`, {
|
|
130
|
+
category: "config",
|
|
131
|
+
status: 403
|
|
132
|
+
});
|
|
133
|
+
this.requestedPath = opts.requestedPath;
|
|
134
|
+
this.resolvedPath = opts.resolvedPath;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
var AgentMaxTurnsError = class extends ModelRelayError {
|
|
138
|
+
constructor(maxTurns) {
|
|
139
|
+
super(`agent exceeded maximum turns (${maxTurns}) without completing`, {
|
|
140
|
+
category: "config",
|
|
141
|
+
status: 400
|
|
142
|
+
});
|
|
143
|
+
this.maxTurns = maxTurns;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
async function parseErrorResponse(response, retries) {
|
|
147
|
+
const requestId = response.headers.get("X-ModelRelay-Request-Id") || response.headers.get("X-Request-Id") || void 0;
|
|
148
|
+
const fallbackMessage = response.statusText || "Request failed";
|
|
149
|
+
const status = response.status || 500;
|
|
150
|
+
let bodyText = "";
|
|
151
|
+
let bodyReadErr;
|
|
152
|
+
try {
|
|
153
|
+
bodyText = await response.text();
|
|
154
|
+
} catch (err) {
|
|
155
|
+
bodyReadErr = err;
|
|
156
|
+
}
|
|
157
|
+
if (!bodyText) {
|
|
158
|
+
return new APIError(fallbackMessage, {
|
|
159
|
+
status,
|
|
160
|
+
requestId,
|
|
161
|
+
retries,
|
|
162
|
+
data: bodyReadErr ? {
|
|
163
|
+
body_read_error: bodyReadErr instanceof Error ? bodyReadErr.message : String(bodyReadErr)
|
|
164
|
+
} : void 0
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const parsed = JSON.parse(bodyText);
|
|
169
|
+
const parsedObj = typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
170
|
+
const issues = Array.isArray(parsedObj?.issues) ? parsedObj?.issues : null;
|
|
171
|
+
if (status === 400 && issues && issues.length > 0) {
|
|
172
|
+
const normalized = [];
|
|
173
|
+
for (const raw of issues) {
|
|
174
|
+
if (!raw || typeof raw !== "object") continue;
|
|
175
|
+
const obj = raw;
|
|
176
|
+
const code = typeof obj.code === "string" ? obj.code : "";
|
|
177
|
+
const path = typeof obj.path === "string" ? obj.path : "";
|
|
178
|
+
const message = typeof obj.message === "string" ? obj.message : "";
|
|
179
|
+
if (!code || !path || !message) continue;
|
|
180
|
+
normalized.push({ code, path, message });
|
|
181
|
+
}
|
|
182
|
+
if (normalized.length > 0) {
|
|
183
|
+
return new WorkflowValidationError({
|
|
184
|
+
status,
|
|
185
|
+
requestId,
|
|
186
|
+
issues: normalized,
|
|
187
|
+
retries,
|
|
188
|
+
data: parsed
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (parsedObj?.error && typeof parsedObj.error === "object") {
|
|
193
|
+
const errPayload = parsedObj.error;
|
|
194
|
+
const message = errPayload?.message || fallbackMessage;
|
|
195
|
+
const code = errPayload?.code || void 0;
|
|
196
|
+
const fields = Array.isArray(errPayload?.fields) ? errPayload?.fields : void 0;
|
|
197
|
+
const parsedStatus = typeof errPayload?.status === "number" ? errPayload.status : status;
|
|
198
|
+
return new APIError(message, {
|
|
199
|
+
status: parsedStatus,
|
|
200
|
+
code,
|
|
201
|
+
fields,
|
|
202
|
+
requestId: parsedObj?.request_id || parsedObj?.requestId || requestId,
|
|
203
|
+
data: parsed,
|
|
204
|
+
retries
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (parsedObj?.message || parsedObj?.code) {
|
|
208
|
+
const message = parsedObj.message || fallbackMessage;
|
|
209
|
+
return new APIError(message, {
|
|
210
|
+
status,
|
|
211
|
+
code: parsedObj.code,
|
|
212
|
+
fields: parsedObj.fields,
|
|
213
|
+
requestId: parsedObj?.request_id || parsedObj?.requestId || requestId,
|
|
214
|
+
data: parsed,
|
|
215
|
+
retries
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return new APIError(fallbackMessage, {
|
|
219
|
+
status,
|
|
220
|
+
requestId,
|
|
221
|
+
data: parsed,
|
|
222
|
+
retries
|
|
223
|
+
});
|
|
224
|
+
} catch {
|
|
225
|
+
return new APIError(bodyText, { status, requestId, retries });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// package.json
|
|
230
|
+
var package_default = {
|
|
231
|
+
name: "@modelrelay/sdk",
|
|
232
|
+
version: "9.0.0",
|
|
233
|
+
description: "TypeScript SDK for the ModelRelay API",
|
|
234
|
+
type: "module",
|
|
235
|
+
main: "dist/index.cjs",
|
|
236
|
+
module: "dist/index.js",
|
|
237
|
+
types: "dist/index.d.ts",
|
|
238
|
+
exports: {
|
|
239
|
+
".": {
|
|
240
|
+
types: "./dist/index.d.ts",
|
|
241
|
+
import: "./dist/index.js",
|
|
242
|
+
require: "./dist/index.cjs"
|
|
243
|
+
},
|
|
244
|
+
"./billing": {
|
|
245
|
+
types: "./dist/billing/index.d.ts",
|
|
246
|
+
import: "./dist/billing/index.js",
|
|
247
|
+
require: "./dist/billing/index.cjs"
|
|
248
|
+
},
|
|
249
|
+
"./node": {
|
|
250
|
+
types: "./dist/node.d.ts",
|
|
251
|
+
import: "./dist/node.js",
|
|
252
|
+
require: "./dist/node.cjs"
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
publishConfig: {
|
|
256
|
+
access: "public"
|
|
257
|
+
},
|
|
258
|
+
files: [
|
|
259
|
+
"dist"
|
|
260
|
+
],
|
|
261
|
+
scripts: {
|
|
262
|
+
build: "tsup src/index.ts src/node.ts src/billing/index.ts --format esm,cjs --dts --external playwright",
|
|
263
|
+
dev: "tsup src/index.ts src/node.ts src/billing/index.ts --format esm,cjs --dts --watch",
|
|
264
|
+
lint: "tsc --noEmit --project tsconfig.lint.json",
|
|
265
|
+
test: "vitest run",
|
|
266
|
+
"generate:types": "openapi-typescript ../../api/openapi/api.json -o src/generated/api.ts"
|
|
267
|
+
},
|
|
268
|
+
keywords: [
|
|
269
|
+
"modelrelay",
|
|
270
|
+
"llm",
|
|
271
|
+
"sdk",
|
|
272
|
+
"typescript"
|
|
273
|
+
],
|
|
274
|
+
author: "Shane Vitarana",
|
|
275
|
+
license: "Apache-2.0",
|
|
276
|
+
dependencies: {
|
|
277
|
+
"fast-json-patch": "^3.1.1",
|
|
278
|
+
zod: "^3.25.76"
|
|
279
|
+
},
|
|
280
|
+
peerDependencies: {
|
|
281
|
+
"better-sqlite3": "^9.6.0",
|
|
282
|
+
playwright: ">=1.40.0"
|
|
283
|
+
},
|
|
284
|
+
peerDependenciesMeta: {
|
|
285
|
+
"better-sqlite3": {
|
|
286
|
+
optional: true
|
|
287
|
+
},
|
|
288
|
+
playwright: {
|
|
289
|
+
optional: true
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
devDependencies: {
|
|
293
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
294
|
+
"@types/node": "^25.0.3",
|
|
295
|
+
"openapi-typescript": "^7.10.1",
|
|
296
|
+
playwright: "^1.57.0",
|
|
297
|
+
tsup: "^8.5.1",
|
|
298
|
+
typescript: "^5.9.3",
|
|
299
|
+
vitest: "^2.1.9"
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// src/types.ts
|
|
304
|
+
var SDK_VERSION = package_default.version || "0.0.0";
|
|
305
|
+
var DEFAULT_BASE_URL = "https://api.modelrelay.ai/api/v1";
|
|
306
|
+
var DEFAULT_CLIENT_HEADER = `modelrelay-ts/${SDK_VERSION}`;
|
|
307
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
|
|
308
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
309
|
+
var StopReasons = {
|
|
310
|
+
Completed: "completed",
|
|
311
|
+
Stop: "stop",
|
|
312
|
+
StopSequence: "stop_sequence",
|
|
313
|
+
EndTurn: "end_turn",
|
|
314
|
+
MaxTokens: "max_tokens",
|
|
315
|
+
MaxLength: "max_len",
|
|
316
|
+
MaxContext: "max_context",
|
|
317
|
+
ToolCalls: "tool_calls",
|
|
318
|
+
TimeLimit: "time_limit",
|
|
319
|
+
ContentFilter: "content_filter",
|
|
320
|
+
Incomplete: "incomplete",
|
|
321
|
+
Unknown: "unknown"
|
|
322
|
+
};
|
|
323
|
+
function asProviderId(value) {
|
|
324
|
+
return value;
|
|
325
|
+
}
|
|
326
|
+
function asModelId(value) {
|
|
327
|
+
return value;
|
|
328
|
+
}
|
|
329
|
+
function asTierCode(value) {
|
|
330
|
+
return value;
|
|
331
|
+
}
|
|
332
|
+
var SubscriptionStatuses = {
|
|
333
|
+
Active: "active",
|
|
334
|
+
Trialing: "trialing",
|
|
335
|
+
PastDue: "past_due",
|
|
336
|
+
Canceled: "canceled",
|
|
337
|
+
Unpaid: "unpaid",
|
|
338
|
+
Incomplete: "incomplete",
|
|
339
|
+
IncompleteExpired: "incomplete_expired",
|
|
340
|
+
Paused: "paused"
|
|
341
|
+
};
|
|
342
|
+
var BillingProviders = {
|
|
343
|
+
Stripe: "stripe",
|
|
344
|
+
Crypto: "crypto",
|
|
345
|
+
AppStore: "app_store",
|
|
346
|
+
External: "external"
|
|
347
|
+
};
|
|
348
|
+
function createUsage(inputTokens, outputTokens, totalTokens) {
|
|
349
|
+
return {
|
|
350
|
+
inputTokens,
|
|
351
|
+
outputTokens,
|
|
352
|
+
totalTokens: totalTokens ?? inputTokens + outputTokens
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
var MessageRoles = {
|
|
356
|
+
User: "user",
|
|
357
|
+
Assistant: "assistant",
|
|
358
|
+
System: "system",
|
|
359
|
+
Tool: "tool"
|
|
360
|
+
};
|
|
361
|
+
var ContentPartTypes = {
|
|
362
|
+
Text: "text"
|
|
363
|
+
};
|
|
364
|
+
var InputItemTypes = {
|
|
365
|
+
Message: "message"
|
|
366
|
+
};
|
|
367
|
+
var OutputItemTypes = {
|
|
368
|
+
Message: "message"
|
|
369
|
+
};
|
|
370
|
+
var ToolTypes = {
|
|
371
|
+
Function: "function",
|
|
372
|
+
XSearch: "x_search",
|
|
373
|
+
CodeExecution: "code_execution"
|
|
374
|
+
};
|
|
375
|
+
var ToolChoiceTypes = {
|
|
376
|
+
Auto: "auto",
|
|
377
|
+
Required: "required",
|
|
378
|
+
None: "none"
|
|
379
|
+
};
|
|
380
|
+
var OutputFormatTypes = {
|
|
381
|
+
Text: "text",
|
|
382
|
+
JsonSchema: "json_schema"
|
|
383
|
+
};
|
|
384
|
+
function mergeMetrics(base, override) {
|
|
385
|
+
if (!base && !override) return void 0;
|
|
386
|
+
return {
|
|
387
|
+
...base || {},
|
|
388
|
+
...override || {}
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function mergeTrace(base, override) {
|
|
392
|
+
if (!base && !override) return void 0;
|
|
393
|
+
return {
|
|
394
|
+
...base || {},
|
|
395
|
+
...override || {}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function normalizeStopReason(value) {
|
|
399
|
+
if (value === void 0 || value === null) return void 0;
|
|
400
|
+
const str = String(value).trim();
|
|
401
|
+
const lower = str.toLowerCase();
|
|
402
|
+
for (const reason of Object.values(StopReasons)) {
|
|
403
|
+
if (lower === reason) return reason;
|
|
404
|
+
}
|
|
405
|
+
switch (lower) {
|
|
406
|
+
case "length":
|
|
407
|
+
return StopReasons.MaxLength;
|
|
408
|
+
default:
|
|
409
|
+
return { other: str };
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function stopReasonToString(value) {
|
|
413
|
+
if (!value) return void 0;
|
|
414
|
+
if (typeof value === "string") return value;
|
|
415
|
+
return value.other?.trim() || void 0;
|
|
416
|
+
}
|
|
417
|
+
function normalizeModelId(value) {
|
|
418
|
+
if (value === void 0 || value === null) return void 0;
|
|
419
|
+
const str = String(value).trim();
|
|
420
|
+
if (!str) return void 0;
|
|
421
|
+
return str;
|
|
422
|
+
}
|
|
423
|
+
function modelToString(value) {
|
|
424
|
+
return String(value).trim();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// src/tools.ts
|
|
428
|
+
function createUserMessage(content) {
|
|
429
|
+
return {
|
|
430
|
+
type: "message",
|
|
431
|
+
role: "user",
|
|
432
|
+
content: [{ type: "text", text: content }]
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function createAssistantMessage(content) {
|
|
436
|
+
return {
|
|
437
|
+
type: "message",
|
|
438
|
+
role: "assistant",
|
|
439
|
+
content: [{ type: "text", text: content }]
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function createSystemMessage(content) {
|
|
443
|
+
return {
|
|
444
|
+
type: "message",
|
|
445
|
+
role: "system",
|
|
446
|
+
content: [{ type: "text", text: content }]
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function createToolCall(id, name, args, type = ToolTypes.Function) {
|
|
450
|
+
return {
|
|
451
|
+
id,
|
|
452
|
+
type,
|
|
453
|
+
function: createFunctionCall(name, args)
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
function createFunctionCall(name, args) {
|
|
457
|
+
return { name, arguments: args };
|
|
458
|
+
}
|
|
459
|
+
function zodToJsonSchema(schema, options = {}) {
|
|
460
|
+
const result = convertZodType(schema);
|
|
461
|
+
if (options.includeSchema) {
|
|
462
|
+
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#";
|
|
463
|
+
return { $schema: schemaVersion, ...result };
|
|
464
|
+
}
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
function convertZodType(schema) {
|
|
468
|
+
const def = schema._def;
|
|
469
|
+
const typeName = def.typeName;
|
|
470
|
+
switch (typeName) {
|
|
471
|
+
case "ZodString":
|
|
472
|
+
return convertZodString(def);
|
|
473
|
+
case "ZodNumber":
|
|
474
|
+
return convertZodNumber(def);
|
|
475
|
+
case "ZodBoolean":
|
|
476
|
+
return { type: "boolean" };
|
|
477
|
+
case "ZodNull":
|
|
478
|
+
return { type: "null" };
|
|
479
|
+
case "ZodArray":
|
|
480
|
+
return convertZodArray(def);
|
|
481
|
+
case "ZodObject":
|
|
482
|
+
return convertZodObject(def);
|
|
483
|
+
case "ZodEnum":
|
|
484
|
+
return convertZodEnum(def);
|
|
485
|
+
case "ZodNativeEnum":
|
|
486
|
+
return convertZodNativeEnum(def);
|
|
487
|
+
case "ZodLiteral":
|
|
488
|
+
return { const: def.value };
|
|
489
|
+
case "ZodUnion":
|
|
490
|
+
return convertZodUnion(def);
|
|
491
|
+
case "ZodOptional": {
|
|
492
|
+
const inner = convertZodType(def.innerType);
|
|
493
|
+
if (def.description && !inner.description) {
|
|
494
|
+
inner.description = def.description;
|
|
495
|
+
}
|
|
496
|
+
return inner;
|
|
497
|
+
}
|
|
498
|
+
case "ZodNullable":
|
|
499
|
+
return convertZodNullable(def);
|
|
500
|
+
case "ZodDefault":
|
|
501
|
+
return {
|
|
502
|
+
...convertZodType(def.innerType),
|
|
503
|
+
default: def.defaultValue()
|
|
504
|
+
};
|
|
505
|
+
case "ZodEffects":
|
|
506
|
+
return convertZodType(def.schema);
|
|
507
|
+
case "ZodRecord":
|
|
508
|
+
return convertZodRecord(def);
|
|
509
|
+
case "ZodTuple":
|
|
510
|
+
return convertZodTuple(def);
|
|
511
|
+
case "ZodAny":
|
|
512
|
+
case "ZodUnknown":
|
|
513
|
+
return {};
|
|
514
|
+
default:
|
|
515
|
+
throw new ConfigError(
|
|
516
|
+
`sdk: unsupported Zod schema type ${JSON.stringify(typeName)}; pass JSON Schema directly or use a full converter like zod-to-json-schema`,
|
|
517
|
+
{ typeName }
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
function convertZodString(def) {
|
|
522
|
+
const result = { type: "string" };
|
|
523
|
+
const checks = def.checks;
|
|
524
|
+
if (checks) {
|
|
525
|
+
for (const check of checks) {
|
|
526
|
+
switch (check.kind) {
|
|
527
|
+
case "min":
|
|
528
|
+
result.minLength = check.value;
|
|
529
|
+
break;
|
|
530
|
+
case "max":
|
|
531
|
+
result.maxLength = check.value;
|
|
532
|
+
break;
|
|
533
|
+
case "length":
|
|
534
|
+
result.minLength = check.value;
|
|
535
|
+
result.maxLength = check.value;
|
|
536
|
+
break;
|
|
537
|
+
case "email":
|
|
538
|
+
result.format = "email";
|
|
539
|
+
break;
|
|
540
|
+
case "url":
|
|
541
|
+
result.format = "uri";
|
|
542
|
+
break;
|
|
543
|
+
case "uuid":
|
|
544
|
+
result.format = "uuid";
|
|
545
|
+
break;
|
|
546
|
+
case "datetime":
|
|
547
|
+
result.format = "date-time";
|
|
548
|
+
break;
|
|
549
|
+
case "regex":
|
|
550
|
+
result.pattern = check.value.source;
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (def.description) {
|
|
556
|
+
result.description = def.description;
|
|
557
|
+
}
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
function convertZodNumber(def) {
|
|
561
|
+
const result = { type: "number" };
|
|
562
|
+
const checks = def.checks;
|
|
563
|
+
if (checks) {
|
|
564
|
+
for (const check of checks) {
|
|
565
|
+
switch (check.kind) {
|
|
566
|
+
case "int":
|
|
567
|
+
result.type = "integer";
|
|
568
|
+
break;
|
|
569
|
+
case "min":
|
|
570
|
+
if (check.inclusive === false) {
|
|
571
|
+
result.exclusiveMinimum = check.value;
|
|
572
|
+
} else {
|
|
573
|
+
result.minimum = check.value;
|
|
574
|
+
}
|
|
575
|
+
break;
|
|
576
|
+
case "max":
|
|
577
|
+
if (check.inclusive === false) {
|
|
578
|
+
result.exclusiveMaximum = check.value;
|
|
579
|
+
} else {
|
|
580
|
+
result.maximum = check.value;
|
|
581
|
+
}
|
|
582
|
+
break;
|
|
583
|
+
case "multipleOf":
|
|
584
|
+
result.multipleOf = check.value;
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (def.description) {
|
|
590
|
+
result.description = def.description;
|
|
591
|
+
}
|
|
592
|
+
return result;
|
|
593
|
+
}
|
|
594
|
+
function convertZodArray(def) {
|
|
595
|
+
const result = {
|
|
596
|
+
type: "array",
|
|
597
|
+
items: convertZodType(def.type)
|
|
598
|
+
};
|
|
599
|
+
if (def.minLength !== void 0 && def.minLength !== null) {
|
|
600
|
+
result.minItems = def.minLength.value;
|
|
601
|
+
}
|
|
602
|
+
if (def.maxLength !== void 0 && def.maxLength !== null) {
|
|
603
|
+
result.maxItems = def.maxLength.value;
|
|
604
|
+
}
|
|
605
|
+
if (def.description) {
|
|
606
|
+
result.description = def.description;
|
|
607
|
+
}
|
|
608
|
+
return result;
|
|
609
|
+
}
|
|
610
|
+
function convertZodObject(def) {
|
|
611
|
+
const shape = def.shape;
|
|
612
|
+
const shapeObj = typeof shape === "function" ? shape() : shape;
|
|
613
|
+
const properties = {};
|
|
614
|
+
const required = [];
|
|
615
|
+
for (const [key, value] of Object.entries(shapeObj)) {
|
|
616
|
+
properties[key] = convertZodType(value);
|
|
617
|
+
const valueDef = value._def;
|
|
618
|
+
const isOptional = valueDef.typeName === "ZodOptional" || valueDef.typeName === "ZodDefault" || valueDef.typeName === "ZodNullable" && valueDef.innerType?._def?.typeName === "ZodDefault";
|
|
619
|
+
if (!isOptional) {
|
|
620
|
+
required.push(key);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
const result = {
|
|
624
|
+
type: "object",
|
|
625
|
+
properties
|
|
626
|
+
};
|
|
627
|
+
if (required.length > 0) {
|
|
628
|
+
result.required = required;
|
|
629
|
+
}
|
|
630
|
+
if (def.description) {
|
|
631
|
+
result.description = def.description;
|
|
632
|
+
}
|
|
633
|
+
const unknownKeys = def.unknownKeys;
|
|
634
|
+
if (unknownKeys === "strict") {
|
|
635
|
+
result.additionalProperties = false;
|
|
636
|
+
}
|
|
637
|
+
return result;
|
|
638
|
+
}
|
|
639
|
+
function convertZodEnum(def) {
|
|
640
|
+
const result = {
|
|
641
|
+
type: "string",
|
|
642
|
+
enum: def.values
|
|
643
|
+
};
|
|
644
|
+
if (def.description) {
|
|
645
|
+
result.description = def.description;
|
|
646
|
+
}
|
|
647
|
+
return result;
|
|
648
|
+
}
|
|
649
|
+
function convertZodNativeEnum(def) {
|
|
650
|
+
const enumValues = def.values;
|
|
651
|
+
const values = Object.values(enumValues).filter(
|
|
652
|
+
(v) => typeof v === "string" || typeof v === "number"
|
|
653
|
+
);
|
|
654
|
+
const result = { enum: values };
|
|
655
|
+
if (def.description) {
|
|
656
|
+
result.description = def.description;
|
|
657
|
+
}
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
function convertZodUnion(def) {
|
|
661
|
+
const options = def.options;
|
|
662
|
+
const result = {
|
|
663
|
+
anyOf: options.map(convertZodType)
|
|
664
|
+
};
|
|
665
|
+
if (def.description) {
|
|
666
|
+
result.description = def.description;
|
|
667
|
+
}
|
|
668
|
+
return result;
|
|
669
|
+
}
|
|
670
|
+
function convertZodNullable(def) {
|
|
671
|
+
const inner = convertZodType(def.innerType);
|
|
672
|
+
return {
|
|
673
|
+
anyOf: [inner, { type: "null" }]
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
function convertZodRecord(def) {
|
|
677
|
+
const result = {
|
|
678
|
+
type: "object",
|
|
679
|
+
additionalProperties: convertZodType(def.valueType)
|
|
680
|
+
};
|
|
681
|
+
if (def.description) {
|
|
682
|
+
result.description = def.description;
|
|
683
|
+
}
|
|
684
|
+
return result;
|
|
685
|
+
}
|
|
686
|
+
function convertZodTuple(def) {
|
|
687
|
+
const items = def.items;
|
|
688
|
+
const result = {
|
|
689
|
+
type: "array",
|
|
690
|
+
items: items.map(convertZodType),
|
|
691
|
+
minItems: items.length,
|
|
692
|
+
maxItems: items.length
|
|
693
|
+
};
|
|
694
|
+
if (def.description) {
|
|
695
|
+
result.description = def.description;
|
|
696
|
+
}
|
|
697
|
+
return result;
|
|
698
|
+
}
|
|
699
|
+
function createFunctionToolFromSchema(name, description, schema, options) {
|
|
700
|
+
const jsonSchema = zodToJsonSchema(schema, options);
|
|
701
|
+
return createFunctionTool(name, description, jsonSchema);
|
|
702
|
+
}
|
|
703
|
+
function createTypedTool(def) {
|
|
704
|
+
const jsonSchema = zodToJsonSchema(def.parameters, def.options);
|
|
705
|
+
const tool = createFunctionTool(def.name, def.description, jsonSchema);
|
|
706
|
+
Object.defineProperty(tool, "_schema", {
|
|
707
|
+
value: def.parameters,
|
|
708
|
+
enumerable: false
|
|
709
|
+
});
|
|
710
|
+
return tool;
|
|
711
|
+
}
|
|
712
|
+
function createFunctionTool(name, description, parameters) {
|
|
713
|
+
const fn = { name, description };
|
|
714
|
+
if (parameters) {
|
|
715
|
+
fn.parameters = parameters;
|
|
716
|
+
}
|
|
717
|
+
return {
|
|
718
|
+
type: ToolTypes.Function,
|
|
719
|
+
function: fn
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function toolChoiceAuto() {
|
|
723
|
+
return { type: ToolChoiceTypes.Auto };
|
|
724
|
+
}
|
|
725
|
+
function toolChoiceRequired() {
|
|
726
|
+
return { type: ToolChoiceTypes.Required };
|
|
727
|
+
}
|
|
728
|
+
function toolChoiceNone() {
|
|
729
|
+
return { type: ToolChoiceTypes.None };
|
|
730
|
+
}
|
|
731
|
+
function hasToolCalls(response) {
|
|
732
|
+
for (const item of response.output || []) {
|
|
733
|
+
if (item?.toolCalls?.length) return true;
|
|
734
|
+
}
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
function firstToolCall(response) {
|
|
738
|
+
for (const item of response.output || []) {
|
|
739
|
+
const call = item?.toolCalls?.[0];
|
|
740
|
+
if (call) return call;
|
|
741
|
+
}
|
|
742
|
+
return void 0;
|
|
743
|
+
}
|
|
744
|
+
function toolResultMessage(toolCallId, result) {
|
|
745
|
+
const content = typeof result === "string" ? result : JSON.stringify(result);
|
|
746
|
+
return {
|
|
747
|
+
type: "message",
|
|
748
|
+
role: "tool",
|
|
749
|
+
toolCallId,
|
|
750
|
+
content: [{ type: "text", text: content }]
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function respondToToolCall(call, result) {
|
|
754
|
+
return toolResultMessage(call.id, result);
|
|
755
|
+
}
|
|
756
|
+
function assistantMessageWithToolCalls(content, toolCalls) {
|
|
757
|
+
return {
|
|
758
|
+
type: "message",
|
|
759
|
+
role: "assistant",
|
|
760
|
+
content: [{ type: "text", text: content }],
|
|
761
|
+
toolCalls
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
var ToolCallAccumulator = class {
|
|
765
|
+
constructor() {
|
|
766
|
+
this.calls = /* @__PURE__ */ new Map();
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Processes a streaming tool call delta.
|
|
770
|
+
* Returns true if this started a new tool call.
|
|
771
|
+
*/
|
|
772
|
+
processDelta(delta) {
|
|
773
|
+
const existing = this.calls.get(delta.index);
|
|
774
|
+
if (!existing) {
|
|
775
|
+
this.calls.set(delta.index, {
|
|
776
|
+
id: delta.id ?? "",
|
|
777
|
+
type: delta.type ?? ToolTypes.Function,
|
|
778
|
+
function: {
|
|
779
|
+
name: delta.function?.name ?? "",
|
|
780
|
+
arguments: delta.function?.arguments ?? ""
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
return true;
|
|
784
|
+
}
|
|
785
|
+
if (delta.function) {
|
|
786
|
+
if (delta.function.name) {
|
|
787
|
+
existing.function = existing.function ?? { name: "", arguments: "" };
|
|
788
|
+
existing.function.name = delta.function.name;
|
|
789
|
+
}
|
|
790
|
+
if (delta.function.arguments) {
|
|
791
|
+
existing.function = existing.function ?? { name: "", arguments: "" };
|
|
792
|
+
existing.function.arguments += delta.function.arguments;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Returns all accumulated tool calls in index order.
|
|
799
|
+
*/
|
|
800
|
+
getToolCalls() {
|
|
801
|
+
if (this.calls.size === 0) {
|
|
802
|
+
return [];
|
|
803
|
+
}
|
|
804
|
+
const maxIdx = Math.max(...this.calls.keys());
|
|
805
|
+
const result = [];
|
|
806
|
+
for (let i = 0; i <= maxIdx; i++) {
|
|
807
|
+
const call = this.calls.get(i);
|
|
808
|
+
if (call) {
|
|
809
|
+
result.push(call);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return result;
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Returns a specific tool call by index, or undefined if not found.
|
|
816
|
+
*/
|
|
817
|
+
getToolCall(index) {
|
|
818
|
+
return this.calls.get(index);
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Clears all accumulated tool calls.
|
|
822
|
+
*/
|
|
823
|
+
reset() {
|
|
824
|
+
this.calls.clear();
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
var ToolArgsError = class extends Error {
|
|
828
|
+
constructor(message, toolCallId, toolName, rawArguments) {
|
|
829
|
+
super(message);
|
|
830
|
+
this.name = "ToolArgsError";
|
|
831
|
+
this.toolCallId = toolCallId;
|
|
832
|
+
this.toolName = toolName;
|
|
833
|
+
this.rawArguments = rawArguments;
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
function parseToolArgsWithSchema(call, schema) {
|
|
837
|
+
const toolName = call.function?.name ?? "unknown";
|
|
838
|
+
const rawArgs = call.function?.arguments ?? "";
|
|
839
|
+
let parsed;
|
|
840
|
+
try {
|
|
841
|
+
parsed = rawArgs ? JSON.parse(rawArgs) : {};
|
|
842
|
+
} catch (err) {
|
|
843
|
+
const message = err instanceof Error ? err.message : "Invalid JSON in arguments";
|
|
844
|
+
throw new ToolArgsError(
|
|
845
|
+
`Failed to parse arguments for tool '${toolName}': ${message}`,
|
|
846
|
+
call.id,
|
|
847
|
+
toolName,
|
|
848
|
+
rawArgs
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
return schema.parse(parsed);
|
|
853
|
+
} catch (err) {
|
|
854
|
+
let message;
|
|
855
|
+
if (err instanceof Error) {
|
|
856
|
+
const zodErr = err;
|
|
857
|
+
if (zodErr.errors && Array.isArray(zodErr.errors)) {
|
|
858
|
+
const issues = zodErr.errors.map((e) => {
|
|
859
|
+
const path = e.path.length > 0 ? `${e.path.join(".")}: ` : "";
|
|
860
|
+
return `${path}${e.message}`;
|
|
861
|
+
}).join("; ");
|
|
862
|
+
message = issues;
|
|
863
|
+
} else {
|
|
864
|
+
message = err.message;
|
|
865
|
+
}
|
|
866
|
+
} else {
|
|
867
|
+
message = String(err);
|
|
868
|
+
}
|
|
869
|
+
throw new ToolArgsError(
|
|
870
|
+
`Invalid arguments for tool '${toolName}': ${message}`,
|
|
871
|
+
call.id,
|
|
872
|
+
toolName,
|
|
873
|
+
rawArgs
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
function parseTypedToolCall(call, tool) {
|
|
878
|
+
if (!call.function) {
|
|
879
|
+
throw new ToolArgsError(
|
|
880
|
+
"Tool call missing function",
|
|
881
|
+
call.id,
|
|
882
|
+
tool.function.name,
|
|
883
|
+
""
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
if (call.function.name !== tool.function.name) {
|
|
887
|
+
throw new ToolArgsError(
|
|
888
|
+
`Expected tool '${tool.function.name}', got '${call.function.name}'`,
|
|
889
|
+
call.id,
|
|
890
|
+
tool.function.name,
|
|
891
|
+
call.function.arguments ?? ""
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
const parsed = parseToolArgsWithSchema(call, tool._schema);
|
|
895
|
+
return {
|
|
896
|
+
...call,
|
|
897
|
+
function: {
|
|
898
|
+
...call.function,
|
|
899
|
+
arguments: parsed
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
function getTypedToolCall(response, tool) {
|
|
904
|
+
for (const item of response.output || []) {
|
|
905
|
+
for (const call of item?.toolCalls || []) {
|
|
906
|
+
if (call.function?.name === tool.function.name) {
|
|
907
|
+
return parseTypedToolCall(call, tool);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return void 0;
|
|
912
|
+
}
|
|
913
|
+
function getTypedToolCalls(response, tool) {
|
|
914
|
+
const result = [];
|
|
915
|
+
for (const item of response.output || []) {
|
|
916
|
+
for (const call of item?.toolCalls || []) {
|
|
917
|
+
if (call.function?.name === tool.function.name) {
|
|
918
|
+
result.push(parseTypedToolCall(call, tool));
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return result;
|
|
923
|
+
}
|
|
924
|
+
function getToolName(call) {
|
|
925
|
+
return call.function?.name ?? "";
|
|
926
|
+
}
|
|
927
|
+
function getToolArgsRaw(call) {
|
|
928
|
+
return call.function?.arguments ?? "";
|
|
929
|
+
}
|
|
930
|
+
function getToolArgs(call) {
|
|
931
|
+
const raw = call.function?.arguments ?? "";
|
|
932
|
+
if (!raw) return { ok: true, args: {} };
|
|
933
|
+
try {
|
|
934
|
+
return { ok: true, args: JSON.parse(raw) };
|
|
935
|
+
} catch (err) {
|
|
936
|
+
return {
|
|
937
|
+
ok: false,
|
|
938
|
+
error: err instanceof Error ? err.message : "Invalid JSON",
|
|
939
|
+
raw
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
function getAllToolCalls(response) {
|
|
944
|
+
const calls = [];
|
|
945
|
+
for (const item of response.output || []) {
|
|
946
|
+
if (item.toolCalls) {
|
|
947
|
+
calls.push(...item.toolCalls);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return calls;
|
|
951
|
+
}
|
|
952
|
+
function getAssistantText(response) {
|
|
953
|
+
const texts = [];
|
|
954
|
+
for (const item of response.output || []) {
|
|
955
|
+
if (item.role === "assistant" && item.content) {
|
|
956
|
+
for (const part of item.content) {
|
|
957
|
+
if (part.type === "text" && part.text) {
|
|
958
|
+
texts.push(part.text);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return texts.join("");
|
|
964
|
+
}
|
|
965
|
+
var ToolRegistry = class {
|
|
966
|
+
constructor() {
|
|
967
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Registers a handler function for a tool name.
|
|
971
|
+
* @param name - The tool name (must match the function name in the tool definition)
|
|
972
|
+
* @param handler - Function to execute when this tool is called
|
|
973
|
+
* @returns this for chaining
|
|
974
|
+
*/
|
|
975
|
+
register(name, handler) {
|
|
976
|
+
this.handlers.set(name, handler);
|
|
977
|
+
return this;
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Unregisters a tool handler.
|
|
981
|
+
* @param name - The tool name to unregister
|
|
982
|
+
* @returns true if the handler was removed, false if it didn't exist
|
|
983
|
+
*/
|
|
984
|
+
unregister(name) {
|
|
985
|
+
return this.handlers.delete(name);
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Checks if a handler is registered for the given tool name.
|
|
989
|
+
*/
|
|
990
|
+
has(name) {
|
|
991
|
+
return this.handlers.has(name);
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Returns the list of registered tool names.
|
|
995
|
+
*/
|
|
996
|
+
getRegisteredTools() {
|
|
997
|
+
return Array.from(this.handlers.keys());
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Executes a single tool call.
|
|
1001
|
+
* @param call - The tool call to execute
|
|
1002
|
+
* @returns The execution result
|
|
1003
|
+
*/
|
|
1004
|
+
async execute(call) {
|
|
1005
|
+
const toolName = call.function?.name ?? "";
|
|
1006
|
+
const handler = this.handlers.get(toolName);
|
|
1007
|
+
if (!handler) {
|
|
1008
|
+
return {
|
|
1009
|
+
toolCallId: call.id,
|
|
1010
|
+
toolName,
|
|
1011
|
+
result: null,
|
|
1012
|
+
error: `Unknown tool: '${toolName}'. Available tools: ${this.getRegisteredTools().join(", ") || "none"}`
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
let args;
|
|
1016
|
+
try {
|
|
1017
|
+
args = call.function?.arguments ? JSON.parse(call.function.arguments) : {};
|
|
1018
|
+
} catch (err) {
|
|
1019
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1020
|
+
return {
|
|
1021
|
+
toolCallId: call.id,
|
|
1022
|
+
toolName,
|
|
1023
|
+
result: null,
|
|
1024
|
+
error: `Invalid JSON in arguments: ${errorMessage}`,
|
|
1025
|
+
isRetryable: true
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
try {
|
|
1029
|
+
const result = await handler(args, call);
|
|
1030
|
+
return {
|
|
1031
|
+
toolCallId: call.id,
|
|
1032
|
+
toolName,
|
|
1033
|
+
result
|
|
1034
|
+
};
|
|
1035
|
+
} catch (err) {
|
|
1036
|
+
const isRetryable = err instanceof ToolArgsError || err instanceof ToolArgumentError;
|
|
1037
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1038
|
+
return {
|
|
1039
|
+
toolCallId: call.id,
|
|
1040
|
+
toolName,
|
|
1041
|
+
result: null,
|
|
1042
|
+
error: errorMessage,
|
|
1043
|
+
isRetryable
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Executes multiple tool calls in parallel.
|
|
1049
|
+
* @param calls - Array of tool calls to execute
|
|
1050
|
+
* @returns Array of execution results in the same order as input
|
|
1051
|
+
*/
|
|
1052
|
+
async executeAll(calls) {
|
|
1053
|
+
return Promise.all(calls.map((call) => this.execute(call)));
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Converts execution results to tool result messages.
|
|
1057
|
+
* Useful for appending to the conversation history.
|
|
1058
|
+
* @param results - Array of execution results
|
|
1059
|
+
* @returns Array of tool result input items (role "tool")
|
|
1060
|
+
*/
|
|
1061
|
+
resultsToMessages(results) {
|
|
1062
|
+
return results.map((r) => {
|
|
1063
|
+
const content = r.error ? `Error: ${r.error}` : typeof r.result === "string" ? r.result : JSON.stringify(r.result);
|
|
1064
|
+
return toolResultMessage(r.toolCallId, content);
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
function formatToolErrorForModel(result) {
|
|
1069
|
+
const lines = [
|
|
1070
|
+
`Tool call error for '${result.toolName}': ${result.error}`
|
|
1071
|
+
];
|
|
1072
|
+
if (result.isRetryable) {
|
|
1073
|
+
lines.push("");
|
|
1074
|
+
lines.push("Please correct the arguments and try again.");
|
|
1075
|
+
}
|
|
1076
|
+
return lines.join("\n");
|
|
1077
|
+
}
|
|
1078
|
+
function hasRetryableErrors(results) {
|
|
1079
|
+
return results.some((r) => r.error && r.isRetryable);
|
|
1080
|
+
}
|
|
1081
|
+
function getRetryableErrors(results) {
|
|
1082
|
+
return results.filter((r) => r.error && r.isRetryable);
|
|
1083
|
+
}
|
|
1084
|
+
function createRetryMessages(results) {
|
|
1085
|
+
return results.filter((r) => r.error && r.isRetryable).map((r) => toolResultMessage(r.toolCallId, formatToolErrorForModel(r)));
|
|
1086
|
+
}
|
|
1087
|
+
async function executeWithRetry(registry, toolCalls, options = {}) {
|
|
1088
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
1089
|
+
let currentCalls = toolCalls;
|
|
1090
|
+
let attempt = 0;
|
|
1091
|
+
const successfulResults = /* @__PURE__ */ new Map();
|
|
1092
|
+
while (attempt <= maxRetries) {
|
|
1093
|
+
const results = await registry.executeAll(currentCalls);
|
|
1094
|
+
for (const result of results) {
|
|
1095
|
+
if (!result.error || !result.isRetryable) {
|
|
1096
|
+
successfulResults.set(result.toolCallId, result);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
const retryableResults = getRetryableErrors(results);
|
|
1100
|
+
if (retryableResults.length === 0 || !options.onRetry) {
|
|
1101
|
+
for (const result of results) {
|
|
1102
|
+
if (result.error && result.isRetryable) {
|
|
1103
|
+
successfulResults.set(result.toolCallId, result);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return Array.from(successfulResults.values());
|
|
1107
|
+
}
|
|
1108
|
+
attempt++;
|
|
1109
|
+
if (attempt > maxRetries) {
|
|
1110
|
+
for (const result of retryableResults) {
|
|
1111
|
+
successfulResults.set(result.toolCallId, result);
|
|
1112
|
+
}
|
|
1113
|
+
return Array.from(successfulResults.values());
|
|
1114
|
+
}
|
|
1115
|
+
const errorMessages = createRetryMessages(retryableResults);
|
|
1116
|
+
const newCalls = await options.onRetry(errorMessages, attempt);
|
|
1117
|
+
if (newCalls.length === 0) {
|
|
1118
|
+
for (const result of retryableResults) {
|
|
1119
|
+
successfulResults.set(result.toolCallId, result);
|
|
1120
|
+
}
|
|
1121
|
+
return Array.from(successfulResults.values());
|
|
1122
|
+
}
|
|
1123
|
+
currentCalls = newCalls;
|
|
1124
|
+
}
|
|
1125
|
+
return Array.from(successfulResults.values());
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
export {
|
|
1129
|
+
ErrorCodes,
|
|
1130
|
+
ModelRelayError,
|
|
1131
|
+
ConfigError,
|
|
1132
|
+
TransportError,
|
|
1133
|
+
StreamProtocolError,
|
|
1134
|
+
StreamTimeoutError,
|
|
1135
|
+
APIError,
|
|
1136
|
+
WorkflowValidationError,
|
|
1137
|
+
ToolArgumentError,
|
|
1138
|
+
PathEscapeError,
|
|
1139
|
+
AgentMaxTurnsError,
|
|
1140
|
+
parseErrorResponse,
|
|
1141
|
+
SDK_VERSION,
|
|
1142
|
+
DEFAULT_BASE_URL,
|
|
1143
|
+
DEFAULT_CLIENT_HEADER,
|
|
1144
|
+
DEFAULT_CONNECT_TIMEOUT_MS,
|
|
1145
|
+
DEFAULT_REQUEST_TIMEOUT_MS,
|
|
1146
|
+
StopReasons,
|
|
1147
|
+
asProviderId,
|
|
1148
|
+
asModelId,
|
|
1149
|
+
asTierCode,
|
|
1150
|
+
SubscriptionStatuses,
|
|
1151
|
+
BillingProviders,
|
|
1152
|
+
createUsage,
|
|
1153
|
+
MessageRoles,
|
|
1154
|
+
ContentPartTypes,
|
|
1155
|
+
InputItemTypes,
|
|
1156
|
+
OutputItemTypes,
|
|
1157
|
+
ToolTypes,
|
|
1158
|
+
ToolChoiceTypes,
|
|
1159
|
+
OutputFormatTypes,
|
|
1160
|
+
mergeMetrics,
|
|
1161
|
+
mergeTrace,
|
|
1162
|
+
normalizeStopReason,
|
|
1163
|
+
stopReasonToString,
|
|
1164
|
+
normalizeModelId,
|
|
1165
|
+
modelToString,
|
|
1166
|
+
createUserMessage,
|
|
1167
|
+
createAssistantMessage,
|
|
1168
|
+
createSystemMessage,
|
|
1169
|
+
createToolCall,
|
|
1170
|
+
createFunctionCall,
|
|
1171
|
+
zodToJsonSchema,
|
|
1172
|
+
createFunctionToolFromSchema,
|
|
1173
|
+
createTypedTool,
|
|
1174
|
+
createFunctionTool,
|
|
1175
|
+
toolChoiceAuto,
|
|
1176
|
+
toolChoiceRequired,
|
|
1177
|
+
toolChoiceNone,
|
|
1178
|
+
hasToolCalls,
|
|
1179
|
+
firstToolCall,
|
|
1180
|
+
toolResultMessage,
|
|
1181
|
+
respondToToolCall,
|
|
1182
|
+
assistantMessageWithToolCalls,
|
|
1183
|
+
ToolCallAccumulator,
|
|
1184
|
+
ToolArgsError,
|
|
1185
|
+
parseTypedToolCall,
|
|
1186
|
+
getTypedToolCall,
|
|
1187
|
+
getTypedToolCalls,
|
|
1188
|
+
getToolName,
|
|
1189
|
+
getToolArgsRaw,
|
|
1190
|
+
getToolArgs,
|
|
1191
|
+
getAllToolCalls,
|
|
1192
|
+
getAssistantText,
|
|
1193
|
+
ToolRegistry,
|
|
1194
|
+
formatToolErrorForModel,
|
|
1195
|
+
hasRetryableErrors,
|
|
1196
|
+
getRetryableErrors,
|
|
1197
|
+
createRetryMessages,
|
|
1198
|
+
executeWithRetry
|
|
1199
|
+
};
|