@x12i/ai-dispatcher 2.4.0 → 3.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/README.md +24 -5
- package/dist/index.cjs +432 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -2
- package/dist/index.d.ts +67 -2
- package/dist/index.js +426 -122
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -41,6 +51,314 @@ var import_openrouter_runtime4 = require("@x12i/openrouter-runtime");
|
|
|
41
51
|
// src/create-dispatcher.ts
|
|
42
52
|
var import_provider_metadata = require("@x12i/provider-metadata");
|
|
43
53
|
|
|
54
|
+
// src/ai-input.ts
|
|
55
|
+
var import_node_async_hooks = require("async_hooks");
|
|
56
|
+
var import_core = require("@x12i/ai-input/core");
|
|
57
|
+
|
|
58
|
+
// src/types/providers.ts
|
|
59
|
+
var IMPLEMENTED_AI_PROVIDERS = ["openrouter", "bedrock", "openai", "cloudflare"];
|
|
60
|
+
function isImplementedAiProvider(provider) {
|
|
61
|
+
return IMPLEMENTED_AI_PROVIDERS.includes(provider);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/errors.ts
|
|
65
|
+
var AiDispatcherError = class extends Error {
|
|
66
|
+
constructor(code, message, details) {
|
|
67
|
+
super(message);
|
|
68
|
+
this.code = code;
|
|
69
|
+
this.details = details;
|
|
70
|
+
this.name = "AiDispatcherError";
|
|
71
|
+
}
|
|
72
|
+
code;
|
|
73
|
+
details;
|
|
74
|
+
};
|
|
75
|
+
function providerNotImplemented(provider) {
|
|
76
|
+
const implemented = [...IMPLEMENTED_AI_PROVIDERS];
|
|
77
|
+
return new AiDispatcherError(
|
|
78
|
+
"PROVIDER_NOT_IMPLEMENTED",
|
|
79
|
+
`AI provider "${provider}" is not implemented yet. Supported by @x12i/ai-dispatcher: ${implemented.map((id) => `"${id}"`).join(", ")}.`,
|
|
80
|
+
{
|
|
81
|
+
provider,
|
|
82
|
+
implementedProviders: implemented,
|
|
83
|
+
operatorHint: `Pass provider: "${implemented[0]}" (or omit provider to use the default).`
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/ai-input.ts
|
|
89
|
+
var GPT_ENCODINGS = /* @__PURE__ */ new Set(["o200k_base", "cl100k_base", "p50k_base", "p50k_edit", "r50k_base"]);
|
|
90
|
+
var inputStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
91
|
+
function mergeAiInput(factory, request) {
|
|
92
|
+
if (request === false) return false;
|
|
93
|
+
if (request === void 0) {
|
|
94
|
+
if (factory === false) return false;
|
|
95
|
+
return factory ?? {};
|
|
96
|
+
}
|
|
97
|
+
const base = typeof factory === "object" ? factory : {};
|
|
98
|
+
const { approvals: requestApprovals, ...requestRest } = request;
|
|
99
|
+
return {
|
|
100
|
+
...base,
|
|
101
|
+
...requestRest,
|
|
102
|
+
...requestApprovals !== void 0 ? { approvals: requestApprovals } : base.approvals !== void 0 ? { approvals: base.approvals } : {}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function visibleInputResolution(track) {
|
|
106
|
+
if (!track.enabled) return { enabled: false, changed: false };
|
|
107
|
+
const baseline = isBaselineCodec(track.codec) && track.representation === "value";
|
|
108
|
+
if (!track.changed && baseline && !track.surfaced) return void 0;
|
|
109
|
+
return {
|
|
110
|
+
enabled: true,
|
|
111
|
+
...track.model ? { model: track.model } : {},
|
|
112
|
+
mode: track.mode,
|
|
113
|
+
codec: track.codec,
|
|
114
|
+
representation: track.representation,
|
|
115
|
+
approvalSource: track.approvalSource,
|
|
116
|
+
changed: track.changed
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function wrapToolRegistry(tools) {
|
|
120
|
+
const wrapped = {};
|
|
121
|
+
for (const [name, executor] of Object.entries(tools)) {
|
|
122
|
+
if (!executor) continue;
|
|
123
|
+
wrapped[name] = wrapProviderExecutor(executor);
|
|
124
|
+
}
|
|
125
|
+
return wrapped;
|
|
126
|
+
}
|
|
127
|
+
function wrapProviderExecutor(executor) {
|
|
128
|
+
return async (args, context) => {
|
|
129
|
+
const result = await executor(args, context);
|
|
130
|
+
const track = inputStorage.getStore();
|
|
131
|
+
if (!track?.enabled || !track.client) return result;
|
|
132
|
+
return applyEncodedResult(track, result);
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async function applyAiInput(params) {
|
|
136
|
+
const requestSetting = params.request.aiInput;
|
|
137
|
+
delete params.request.aiInput;
|
|
138
|
+
const settings = mergeAiInput(params.factory, requestSetting);
|
|
139
|
+
if (settings === false) return disabledTrack();
|
|
140
|
+
const track = await openTrack(settings, lookupModel(settings.model, params.effectiveModel));
|
|
141
|
+
await encodeRequestText(params.request, track);
|
|
142
|
+
wrapRequestTools(params.request, track);
|
|
143
|
+
return track;
|
|
144
|
+
}
|
|
145
|
+
function disabledTrack() {
|
|
146
|
+
return {
|
|
147
|
+
enabled: false,
|
|
148
|
+
mode: "compact",
|
|
149
|
+
codec: "text",
|
|
150
|
+
representation: "value",
|
|
151
|
+
approvalSource: "baseline",
|
|
152
|
+
changed: false,
|
|
153
|
+
surfaced: false
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async function openTrack(settings, model) {
|
|
157
|
+
const tokenizer = settings.tokenizer ?? "none";
|
|
158
|
+
const mode = settings.mode ?? "compact";
|
|
159
|
+
let tokenCounter;
|
|
160
|
+
try {
|
|
161
|
+
tokenCounter = await loadTokenizer(tokenizer);
|
|
162
|
+
const framing = framingFor(settings);
|
|
163
|
+
const resolved = (0, import_core.resolveSettings)({
|
|
164
|
+
files: [],
|
|
165
|
+
env: {},
|
|
166
|
+
factory: {
|
|
167
|
+
mode,
|
|
168
|
+
codec: settings.codec ?? "auto",
|
|
169
|
+
readableFormat: settings.readableFormat ?? "auto",
|
|
170
|
+
sharing: settings.sharing ?? "auto",
|
|
171
|
+
tokenizer: tokenCounter?.id ?? "none",
|
|
172
|
+
framing,
|
|
173
|
+
...model ? { model } : {}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
const approvals = settings.approvals;
|
|
177
|
+
const client = (0, import_core.createCoreClient)({
|
|
178
|
+
config: resolved.config,
|
|
179
|
+
explanation: resolved.explanation,
|
|
180
|
+
...approvals && approvals.length > 0 ? { policy: { approvals: [...approvals] } } : {},
|
|
181
|
+
...tokenCounter ? { tokenCounter } : {},
|
|
182
|
+
warn: () => void 0
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
enabled: true,
|
|
186
|
+
client,
|
|
187
|
+
...model ? { model } : {},
|
|
188
|
+
mode,
|
|
189
|
+
codec: "text",
|
|
190
|
+
representation: "value",
|
|
191
|
+
approvalSource: "baseline",
|
|
192
|
+
changed: false,
|
|
193
|
+
surfaced: false
|
|
194
|
+
};
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw mapAiInputError(error);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function lookupModel(override, effectiveModel) {
|
|
200
|
+
const explicit = override?.trim();
|
|
201
|
+
const chosen = explicit ? explicit : effectiveModel.trim();
|
|
202
|
+
return chosen.length > 0 ? chosen : void 0;
|
|
203
|
+
}
|
|
204
|
+
function framingFor(settings) {
|
|
205
|
+
if (settings.framing) return settings.framing;
|
|
206
|
+
const tokenizer = settings.tokenizer ?? "none";
|
|
207
|
+
if (tokenizer !== "none" && (settings.approvals?.length ?? 0) > 0) return "self-describing";
|
|
208
|
+
return "raw";
|
|
209
|
+
}
|
|
210
|
+
async function loadTokenizer(tokenizer) {
|
|
211
|
+
if (tokenizer === "none") return void 0;
|
|
212
|
+
if (!tokenizer.startsWith("gpt:")) {
|
|
213
|
+
throw new AiDispatcherError("AI_INPUT_FAILED", `Unsupported tokenizer "${tokenizer}".`, {
|
|
214
|
+
code: "INVALID_CONFIG"
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const encoding = tokenizer.slice("gpt:".length);
|
|
218
|
+
if (!GPT_ENCODINGS.has(encoding)) {
|
|
219
|
+
throw new AiDispatcherError("AI_INPUT_FAILED", `Unsupported tokenizer "${tokenizer}".`, {
|
|
220
|
+
code: "INVALID_CONFIG"
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const gpt = await import("@x12i/ai-input/tokenizers/gpt");
|
|
224
|
+
return gpt.createGptTokenCounter(encoding);
|
|
225
|
+
}
|
|
226
|
+
async function encodeRequestText(request, track) {
|
|
227
|
+
if (typeof request.prompt === "string") request.prompt = await encodeString(track, request.prompt);
|
|
228
|
+
if (typeof request.system === "string") request.system = await encodeString(track, request.system);
|
|
229
|
+
if (typeof request.instructions === "string") request.instructions = await encodeString(track, request.instructions);
|
|
230
|
+
if (typeof request.input === "string") {
|
|
231
|
+
request.input = await encodeString(track, request.input);
|
|
232
|
+
} else if (Array.isArray(request.input)) {
|
|
233
|
+
for (let index = 0; index < request.input.length; index += 1) {
|
|
234
|
+
const item = request.input[index];
|
|
235
|
+
if (typeof item === "string") {
|
|
236
|
+
request.input[index] = await encodeString(track, item);
|
|
237
|
+
} else if (item) {
|
|
238
|
+
await encodeContentRecord(item, track);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (request.messages) {
|
|
243
|
+
for (const message of request.messages) {
|
|
244
|
+
await encodeMessage(message, track);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function encodeMessage(message, track) {
|
|
249
|
+
if (typeof message.content === "string") {
|
|
250
|
+
message.content = await encodeString(track, message.content);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
for (const part of message.content) {
|
|
254
|
+
if (part.type === "text") part.text = await encodeString(track, part.text);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async function encodeContentRecord(record2, track) {
|
|
258
|
+
if (typeof record2.content === "string") {
|
|
259
|
+
record2.content = await encodeString(track, record2.content);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (!Array.isArray(record2.content)) return;
|
|
263
|
+
for (const part of record2.content) {
|
|
264
|
+
if (!isRecord(part) || typeof part.text !== "string") continue;
|
|
265
|
+
if (part.type === "text" || part.type === "input_text") {
|
|
266
|
+
part.text = await encodeString(track, part.text);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function wrapRequestTools(request, track) {
|
|
271
|
+
if (!request.functionTools) return;
|
|
272
|
+
request.functionTools = request.functionTools.map((tool) => {
|
|
273
|
+
if (!tool.executor) return tool;
|
|
274
|
+
const executor = tool.executor;
|
|
275
|
+
return {
|
|
276
|
+
...tool,
|
|
277
|
+
executor: async (args, context) => applyEncodedResult(track, await executor(args, context))
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
async function encodeString(track, value) {
|
|
282
|
+
const encoded = await applyEncodedResult(track, value);
|
|
283
|
+
return typeof encoded === "string" ? encoded : value;
|
|
284
|
+
}
|
|
285
|
+
async function applyEncodedResult(track, value) {
|
|
286
|
+
if (!track.enabled || !track.client) return value;
|
|
287
|
+
let prepared;
|
|
288
|
+
try {
|
|
289
|
+
prepared = await track.client.prepare(value);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
const failure = readPackageFailure(error);
|
|
292
|
+
if (failure?.code === "UNSUPPORTED_VALUE") return value;
|
|
293
|
+
throw mapAiInputError(error);
|
|
294
|
+
}
|
|
295
|
+
const wire = unframedBaseline(prepared);
|
|
296
|
+
note(track, prepared, value, wire);
|
|
297
|
+
if (wire === canonicalWire(value)) return value;
|
|
298
|
+
return wire;
|
|
299
|
+
}
|
|
300
|
+
function unframedBaseline(prepared) {
|
|
301
|
+
const baseline = isBaselineCodec(prepared.codec) && prepared.representation === "value";
|
|
302
|
+
if (!baseline || prepared.framing === "raw") return prepared.text;
|
|
303
|
+
const newline = prepared.text.indexOf("\n");
|
|
304
|
+
if (!prepared.text.startsWith("@ai-input/") || newline < 0) return prepared.text;
|
|
305
|
+
return prepared.text.slice(newline + 1);
|
|
306
|
+
}
|
|
307
|
+
function isBaselineCodec(codec) {
|
|
308
|
+
return codec === "json" || codec === "text" || codec === "outline";
|
|
309
|
+
}
|
|
310
|
+
function note(track, prepared, source, wire) {
|
|
311
|
+
const changed = wire !== canonicalWire(source);
|
|
312
|
+
if (changed) track.changed = true;
|
|
313
|
+
const alternate = prepared.codec === "toon" || prepared.representation !== "value";
|
|
314
|
+
if (alternate || !track.surfaced) {
|
|
315
|
+
track.codec = prepared.codec;
|
|
316
|
+
track.representation = prepared.representation;
|
|
317
|
+
track.approvalSource = prepared.receipt.approvalSource;
|
|
318
|
+
track.mode = prepared.mode;
|
|
319
|
+
}
|
|
320
|
+
if (alternate) track.surfaced = true;
|
|
321
|
+
}
|
|
322
|
+
function canonicalWire(value) {
|
|
323
|
+
if (typeof value === "string") return value;
|
|
324
|
+
return JSON.stringify(value);
|
|
325
|
+
}
|
|
326
|
+
function mapAiInputError(error) {
|
|
327
|
+
if (error instanceof AiDispatcherError) return error;
|
|
328
|
+
const failure = readPackageFailure(error);
|
|
329
|
+
if (failure) {
|
|
330
|
+
return new AiDispatcherError("AI_INPUT_FAILED", failure.message, {
|
|
331
|
+
code: failure.code,
|
|
332
|
+
...failure.path !== void 0 ? { path: failure.path } : {},
|
|
333
|
+
...failure.details !== void 0 ? { details: failure.details } : {}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const message = error instanceof Error ? error.message : "Input preparation failed.";
|
|
337
|
+
return new AiDispatcherError("AI_INPUT_FAILED", message);
|
|
338
|
+
}
|
|
339
|
+
function readPackageFailure(error) {
|
|
340
|
+
if ((0, import_core.isAiInputError)(error)) {
|
|
341
|
+
return {
|
|
342
|
+
code: error.code,
|
|
343
|
+
message: error.message,
|
|
344
|
+
...error.path !== void 0 ? { path: error.path } : {},
|
|
345
|
+
...error.details !== void 0 ? { details: error.details } : {}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (!(error instanceof Error) || error.name !== "AiInputError") return void 0;
|
|
349
|
+
const coded = error;
|
|
350
|
+
if (typeof coded.code !== "string") return void 0;
|
|
351
|
+
return {
|
|
352
|
+
code: coded.code,
|
|
353
|
+
message: error.message,
|
|
354
|
+
...typeof coded.path === "string" ? { path: coded.path } : {},
|
|
355
|
+
...coded.details !== void 0 ? { details: coded.details } : {}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function isRecord(value) {
|
|
359
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
360
|
+
}
|
|
361
|
+
|
|
44
362
|
// src/cache-usage.ts
|
|
45
363
|
function readCacheTokens(source) {
|
|
46
364
|
const cached = [];
|
|
@@ -89,7 +407,7 @@ function record(value) {
|
|
|
89
407
|
|
|
90
408
|
// src/json-pointer.ts
|
|
91
409
|
var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
92
|
-
function
|
|
410
|
+
function isRecord2(value) {
|
|
93
411
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
94
412
|
}
|
|
95
413
|
function unescapeToken(token) {
|
|
@@ -116,7 +434,7 @@ function walk(current, tokens) {
|
|
|
116
434
|
if (index >= current.length) return [];
|
|
117
435
|
return walk(current[index], rest);
|
|
118
436
|
}
|
|
119
|
-
if (!
|
|
437
|
+
if (!isRecord2(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
|
|
120
438
|
return walk(current[head], rest);
|
|
121
439
|
}
|
|
122
440
|
function valuesAtPointer(root, pointer) {
|
|
@@ -153,7 +471,7 @@ function collectParents(current, tokens) {
|
|
|
153
471
|
if (index >= current.length) return [];
|
|
154
472
|
return collectParents(current[index], rest);
|
|
155
473
|
}
|
|
156
|
-
if (!
|
|
474
|
+
if (!isRecord2(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
|
|
157
475
|
return collectParents(current[head], rest);
|
|
158
476
|
}
|
|
159
477
|
function deletePointer(root, pointer) {
|
|
@@ -169,27 +487,27 @@ function deletePointer(root, pointer) {
|
|
|
169
487
|
if (index < parent.length) parent.splice(index, 1);
|
|
170
488
|
continue;
|
|
171
489
|
}
|
|
172
|
-
if (
|
|
490
|
+
if (isRecord2(parent) && Object.prototype.hasOwnProperty.call(parent, last)) {
|
|
173
491
|
delete parent[last];
|
|
174
492
|
}
|
|
175
493
|
}
|
|
176
494
|
}
|
|
177
495
|
|
|
178
496
|
// src/reasoning-extract.ts
|
|
179
|
-
function
|
|
497
|
+
function isRecord3(value) {
|
|
180
498
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
181
499
|
}
|
|
182
500
|
function textFromValue(value) {
|
|
183
501
|
if (typeof value === "string") return value;
|
|
184
502
|
if (Array.isArray(value)) return value.map(textFromValue).join("");
|
|
185
|
-
if (!
|
|
503
|
+
if (!isRecord3(value)) return "";
|
|
186
504
|
if (typeof value.text === "string") return value.text;
|
|
187
505
|
if (typeof value.summary === "string") return value.summary;
|
|
188
506
|
return "";
|
|
189
507
|
}
|
|
190
508
|
function extractStreamReasoningText(event, response) {
|
|
191
509
|
if (response.streamReasoningKeys.length > 0) {
|
|
192
|
-
if (!
|
|
510
|
+
if (!isRecord3(event)) return void 0;
|
|
193
511
|
for (const key of response.streamReasoningKeys) {
|
|
194
512
|
const text = textFromValue(event[key]);
|
|
195
513
|
if (text) return text;
|
|
@@ -287,6 +605,8 @@ function applyResolution(response, prepared) {
|
|
|
287
605
|
if (prepared.cacheWarnings?.length) {
|
|
288
606
|
result.warnings = [...result.warnings, ...prepared.cacheWarnings];
|
|
289
607
|
}
|
|
608
|
+
const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
|
|
609
|
+
if (inputResolution) result.inputResolution = inputResolution;
|
|
290
610
|
applyCacheUsage(result);
|
|
291
611
|
const directive = prepared.directive;
|
|
292
612
|
if (!directive) return result;
|
|
@@ -330,7 +650,7 @@ function applyUsage(response, raw, directive) {
|
|
|
330
650
|
}
|
|
331
651
|
|
|
332
652
|
// src/decorate-stream.ts
|
|
333
|
-
function
|
|
653
|
+
function isRecord4(value) {
|
|
334
654
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
335
655
|
}
|
|
336
656
|
async function* decorateStream(prepared, source) {
|
|
@@ -388,11 +708,13 @@ async function* decorateStream(prepared, source) {
|
|
|
388
708
|
};
|
|
389
709
|
}
|
|
390
710
|
}
|
|
391
|
-
const data =
|
|
711
|
+
const data = isRecord4(event.data) ? { ...event.data } : {};
|
|
392
712
|
if (splitter) data.text = splitter.answer;
|
|
393
713
|
if (reasoningText) data.reasoningText = reasoningText;
|
|
394
714
|
if (prepared.resolution) data.reasoningResolution = prepared.resolution;
|
|
395
715
|
if (prepared.cacheResolution) data.cacheResolution = prepared.cacheResolution;
|
|
716
|
+
const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
|
|
717
|
+
if (inputResolution) data.inputResolution = inputResolution;
|
|
396
718
|
yield { ...event, data };
|
|
397
719
|
continue;
|
|
398
720
|
}
|
|
@@ -400,18 +722,20 @@ async function* decorateStream(prepared, source) {
|
|
|
400
722
|
}
|
|
401
723
|
}
|
|
402
724
|
function withResolution(event, prepared) {
|
|
403
|
-
|
|
404
|
-
|
|
725
|
+
const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
|
|
726
|
+
if (!prepared.resolution && !prepared.cacheResolution && !inputResolution) return event;
|
|
727
|
+
const data = isRecord4(event.data) ? { ...event.data } : {};
|
|
405
728
|
if (prepared.resolution) data.reasoningResolution = prepared.resolution;
|
|
406
729
|
if (prepared.cacheResolution) data.cacheResolution = prepared.cacheResolution;
|
|
730
|
+
if (inputResolution) data.inputResolution = inputResolution;
|
|
407
731
|
return { ...event, data };
|
|
408
732
|
}
|
|
409
733
|
function textOf(event) {
|
|
410
|
-
if (!
|
|
734
|
+
if (!isRecord4(event.data) || typeof event.data.text !== "string") return "";
|
|
411
735
|
return event.data.text;
|
|
412
736
|
}
|
|
413
737
|
function adjustUsageEvent(event, prepared) {
|
|
414
|
-
if (!
|
|
738
|
+
if (!isRecord4(event.data) || !isRecord4(event.data.usage)) return event;
|
|
415
739
|
const paths = prepared.directive?.usage?.reasoningTokenPaths;
|
|
416
740
|
const usage = { ...event.data.usage };
|
|
417
741
|
let changed = false;
|
|
@@ -440,36 +764,6 @@ function adjustUsageEvent(event, prepared) {
|
|
|
440
764
|
return { ...event, data: { ...event.data, usage } };
|
|
441
765
|
}
|
|
442
766
|
|
|
443
|
-
// src/types/providers.ts
|
|
444
|
-
var IMPLEMENTED_AI_PROVIDERS = ["openrouter", "bedrock", "openai", "cloudflare"];
|
|
445
|
-
function isImplementedAiProvider(provider) {
|
|
446
|
-
return IMPLEMENTED_AI_PROVIDERS.includes(provider);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// src/errors.ts
|
|
450
|
-
var AiDispatcherError = class extends Error {
|
|
451
|
-
constructor(code, message, details) {
|
|
452
|
-
super(message);
|
|
453
|
-
this.code = code;
|
|
454
|
-
this.details = details;
|
|
455
|
-
this.name = "AiDispatcherError";
|
|
456
|
-
}
|
|
457
|
-
code;
|
|
458
|
-
details;
|
|
459
|
-
};
|
|
460
|
-
function providerNotImplemented(provider) {
|
|
461
|
-
const implemented = [...IMPLEMENTED_AI_PROVIDERS];
|
|
462
|
-
return new AiDispatcherError(
|
|
463
|
-
"PROVIDER_NOT_IMPLEMENTED",
|
|
464
|
-
`AI provider "${provider}" is not implemented yet. Supported by @x12i/ai-dispatcher: ${implemented.map((id) => `"${id}"`).join(", ")}.`,
|
|
465
|
-
{
|
|
466
|
-
provider,
|
|
467
|
-
implementedProviders: implemented,
|
|
468
|
-
operatorHint: `Pass provider: "${implemented[0]}" (or omit provider to use the default).`
|
|
469
|
-
}
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
767
|
// src/mcp.ts
|
|
474
768
|
var TOOL_TOKEN = /^[A-Za-z0-9_-]+$/;
|
|
475
769
|
var MAX_EXPOSED_NAME_LENGTH = 64;
|
|
@@ -491,7 +785,7 @@ function indexMcpServers(servers) {
|
|
|
491
785
|
for (const server of servers) {
|
|
492
786
|
assertServer(server, seenServers);
|
|
493
787
|
for (const tool of server.tools) {
|
|
494
|
-
if (!
|
|
788
|
+
if (!isRecord5(tool)) {
|
|
495
789
|
throw new AiDispatcherError(
|
|
496
790
|
"MCP_TOOL_NAME_INVALID",
|
|
497
791
|
`MCP server "${server.id}" has a tool that is not an object.`
|
|
@@ -578,18 +872,17 @@ function applyMcpCatalog(params) {
|
|
|
578
872
|
function createMcpExecutor(server, rawName) {
|
|
579
873
|
return async (args, context) => {
|
|
580
874
|
const result = await server.callTool(rawName, toArgs(args), context);
|
|
581
|
-
return
|
|
875
|
+
return mcpModelValue(result);
|
|
582
876
|
};
|
|
583
877
|
}
|
|
584
|
-
function
|
|
585
|
-
if (typeof result === "string") return result;
|
|
586
|
-
if (!isRecord4(result)) return stringify(result);
|
|
878
|
+
function mcpModelValue(result) {
|
|
879
|
+
if (typeof result === "string" || !isRecord5(result)) return result;
|
|
587
880
|
const text = textContent(result.content);
|
|
588
881
|
if (result.isError === true) {
|
|
589
882
|
throw new Error(text ?? stringify(result));
|
|
590
883
|
}
|
|
591
884
|
if (text !== void 0) return text;
|
|
592
|
-
return
|
|
885
|
+
return result;
|
|
593
886
|
}
|
|
594
887
|
function resolveAllowlist(filter, tools) {
|
|
595
888
|
if (filter === void 0) return void 0;
|
|
@@ -608,7 +901,7 @@ function resolveAllowlist(filter, tools) {
|
|
|
608
901
|
return new Set(filter);
|
|
609
902
|
}
|
|
610
903
|
function assertServer(server, seen) {
|
|
611
|
-
if (!
|
|
904
|
+
if (!isRecord5(server) || typeof server.id !== "string" || server.id.length === 0 || !Array.isArray(server.tools) || typeof server.callTool !== "function") {
|
|
612
905
|
throw new AiDispatcherError(
|
|
613
906
|
"MCP_SERVER_INVALID",
|
|
614
907
|
"An MCP server needs an id, a tools array, and a callTool function."
|
|
@@ -653,7 +946,7 @@ function toolDescription(tool) {
|
|
|
653
946
|
}
|
|
654
947
|
function toolParameters(tool) {
|
|
655
948
|
if (tool.inputSchema === void 0) return { ...DEFAULT_PARAMETERS, properties: {} };
|
|
656
|
-
if (!
|
|
949
|
+
if (!isRecord5(tool.inputSchema)) {
|
|
657
950
|
throw new AiDispatcherError(
|
|
658
951
|
"MCP_TOOL_NAME_INVALID",
|
|
659
952
|
`MCP tool "${tool.name}" inputSchema must be an object.`
|
|
@@ -665,14 +958,14 @@ function textContent(content) {
|
|
|
665
958
|
if (!Array.isArray(content)) return void 0;
|
|
666
959
|
const parts = [];
|
|
667
960
|
for (const part of content) {
|
|
668
|
-
if (!
|
|
961
|
+
if (!isRecord5(part) || part.type !== "text" || typeof part.text !== "string") continue;
|
|
669
962
|
parts.push(part.text);
|
|
670
963
|
}
|
|
671
964
|
if (!parts.length) return void 0;
|
|
672
965
|
return parts.join("\n");
|
|
673
966
|
}
|
|
674
967
|
function toArgs(args) {
|
|
675
|
-
if (
|
|
968
|
+
if (isRecord5(args)) return args;
|
|
676
969
|
return {};
|
|
677
970
|
}
|
|
678
971
|
function listFailed(serverId, error) {
|
|
@@ -684,7 +977,7 @@ function stringify(result) {
|
|
|
684
977
|
const serialized = JSON.stringify(result);
|
|
685
978
|
return serialized === void 0 ? "null" : serialized;
|
|
686
979
|
}
|
|
687
|
-
function
|
|
980
|
+
function isRecord5(value) {
|
|
688
981
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
689
982
|
}
|
|
690
983
|
|
|
@@ -1240,7 +1533,7 @@ function compileCloudflareRequest(request, options, call, streaming) {
|
|
|
1240
1533
|
if (call?.finalizeBody) body = call.finalizeBody(body);
|
|
1241
1534
|
if (endpoint !== "run") body = { ...body, stream: streaming };
|
|
1242
1535
|
const gateway = mergeGateway(options.gateway, call?.cloudflare?.gateway);
|
|
1243
|
-
const fromBody =
|
|
1536
|
+
const fromBody = isRecord6(body.metadata) ? body.metadata : void 0;
|
|
1244
1537
|
const packed = (0, import_openrouter_runtime.packProviderRecord)(
|
|
1245
1538
|
request.metadata,
|
|
1246
1539
|
request.agentId,
|
|
@@ -1494,7 +1787,7 @@ function messagesFromInput(request) {
|
|
|
1494
1787
|
messages.push({ role: "user", content: item });
|
|
1495
1788
|
continue;
|
|
1496
1789
|
}
|
|
1497
|
-
if (!
|
|
1790
|
+
if (!isRecord6(item)) continue;
|
|
1498
1791
|
const role = item.role === "assistant" || item.role === "tool" || item.role === "system" ? item.role : "user";
|
|
1499
1792
|
if (role === "system") continue;
|
|
1500
1793
|
const content = inputContent(item.content);
|
|
@@ -1511,12 +1804,12 @@ function inputContent(content) {
|
|
|
1511
1804
|
if (!Array.isArray(content)) return void 0;
|
|
1512
1805
|
const parts = [];
|
|
1513
1806
|
for (const part of content) {
|
|
1514
|
-
if (!
|
|
1807
|
+
if (!isRecord6(part)) continue;
|
|
1515
1808
|
if ((part.type === "text" || part.type === "input_text") && typeof part.text === "string") {
|
|
1516
1809
|
parts.push({ type: "text", text: part.text });
|
|
1517
1810
|
} else if (part.type === "image_url" && typeof part.imageUrl === "string") {
|
|
1518
1811
|
parts.push({ type: "image_url", imageUrl: part.imageUrl });
|
|
1519
|
-
} else if (part.type === "image_url" &&
|
|
1812
|
+
} else if (part.type === "image_url" && isRecord6(part.image_url) && typeof part.image_url.url === "string") {
|
|
1520
1813
|
parts.push({ type: "image_url", imageUrl: part.image_url.url });
|
|
1521
1814
|
} else if (part.type === "input_image" && typeof part.image_url === "string") {
|
|
1522
1815
|
parts.push({ type: "image_url", imageUrl: part.image_url });
|
|
@@ -1735,7 +2028,7 @@ function continueMessages(body, response, results) {
|
|
|
1735
2028
|
return { ...body, messages };
|
|
1736
2029
|
}
|
|
1737
2030
|
function continueRun(body, response, results) {
|
|
1738
|
-
const input =
|
|
2031
|
+
const input = isRecord6(body.input) ? body.input : {};
|
|
1739
2032
|
const continued = continueChat({ messages: input.messages }, response, results);
|
|
1740
2033
|
return { ...body, input: { ...input, messages: continued.messages } };
|
|
1741
2034
|
}
|
|
@@ -1824,7 +2117,7 @@ async function* streamCloudflare(request, options, call) {
|
|
|
1824
2117
|
throw failure;
|
|
1825
2118
|
}
|
|
1826
2119
|
for await (const event of parseSse(response)) {
|
|
1827
|
-
if (!
|
|
2120
|
+
if (!isRecord6(event)) continue;
|
|
1828
2121
|
const catalogReasoning = call?.extractStreamReasoning?.(event);
|
|
1829
2122
|
const reasoningText = catalogReasoning || reasoningDelta(event);
|
|
1830
2123
|
if (reasoningText) yield { type: "stream.reasoning.delta", requestId, data: { text: reasoningText } };
|
|
@@ -1887,7 +2180,7 @@ async function sendOnce(compiled, options, timeoutMs) {
|
|
|
1887
2180
|
if (!response.ok) throw await readFailure(response);
|
|
1888
2181
|
const text = await response.text();
|
|
1889
2182
|
const parsed = text ? JSON.parse(text) : {};
|
|
1890
|
-
if (
|
|
2183
|
+
if (isRecord6(parsed) && parsed.success === false) {
|
|
1891
2184
|
throw new AiDispatcherError(
|
|
1892
2185
|
"PROVIDER_REQUEST_FAILED",
|
|
1893
2186
|
messageFrom(parsed) ?? "Cloudflare request failed.",
|
|
@@ -1938,7 +2231,7 @@ function isConfigError(code) {
|
|
|
1938
2231
|
}
|
|
1939
2232
|
function asTerminalError(error) {
|
|
1940
2233
|
if (error instanceof AiDispatcherError && error.code === "PROVIDER_RETRYABLE") {
|
|
1941
|
-
const status =
|
|
2234
|
+
const status = isRecord6(error.details) ? error.details.status : void 0;
|
|
1942
2235
|
if (status === 429) return new AiDispatcherError("PROVIDER_RATE_LIMITED", error.message, error.details);
|
|
1943
2236
|
return new AiDispatcherError("PROVIDER_GATEWAY_UNREACHABLE", error.message, error.details);
|
|
1944
2237
|
}
|
|
@@ -1948,7 +2241,7 @@ function asTerminalError(error) {
|
|
|
1948
2241
|
function normalizeCloudflareResponse(params) {
|
|
1949
2242
|
const payload = unwrap(params.response);
|
|
1950
2243
|
const calls = params.functionTools ?? extractCalls(params.compiled.endpoint, params.response);
|
|
1951
|
-
const usage = normalizeUsage(payload.usage) ?? normalizeUsage(
|
|
2244
|
+
const usage = normalizeUsage(payload.usage) ?? normalizeUsage(isRecord6(params.response) ? params.response.usage : void 0);
|
|
1952
2245
|
const status = params.status ?? (calls.length ? "requires_action" : "completed");
|
|
1953
2246
|
return {
|
|
1954
2247
|
id: typeof payload.id === "string" ? payload.id : params.requestId,
|
|
@@ -1981,16 +2274,16 @@ function answerText(response) {
|
|
|
1981
2274
|
if (chat) return chat;
|
|
1982
2275
|
const anthropic = anthropicAnswer(response);
|
|
1983
2276
|
if (anthropic) return anthropic;
|
|
1984
|
-
if (
|
|
2277
|
+
if (isRecord6(response.result)) return answerText(response.result);
|
|
1985
2278
|
return "";
|
|
1986
2279
|
}
|
|
1987
2280
|
function responsesAnswer(response) {
|
|
1988
2281
|
if (!Array.isArray(response.output)) return "";
|
|
1989
2282
|
const parts = [];
|
|
1990
2283
|
for (const item of response.output) {
|
|
1991
|
-
if (!
|
|
2284
|
+
if (!isRecord6(item) || item.type !== "message" || !Array.isArray(item.content)) continue;
|
|
1992
2285
|
for (const part of item.content) {
|
|
1993
|
-
if (
|
|
2286
|
+
if (isRecord6(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
|
|
1994
2287
|
}
|
|
1995
2288
|
}
|
|
1996
2289
|
return parts.join("");
|
|
@@ -2001,16 +2294,16 @@ function chatAnswer(response) {
|
|
|
2001
2294
|
const content = message.content;
|
|
2002
2295
|
if (typeof content === "string") return content;
|
|
2003
2296
|
if (!Array.isArray(content)) return "";
|
|
2004
|
-
return content.map((part) =>
|
|
2297
|
+
return content.map((part) => isRecord6(part) && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
|
|
2005
2298
|
}
|
|
2006
2299
|
function anthropicAnswer(response) {
|
|
2007
2300
|
if (!Array.isArray(response.content)) return "";
|
|
2008
|
-
return response.content.map((part) =>
|
|
2301
|
+
return response.content.map((part) => isRecord6(part) && part.type === "text" && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
|
|
2009
2302
|
}
|
|
2010
2303
|
function chatAssistantMessage(response) {
|
|
2011
|
-
if (!Array.isArray(response.choices) || !
|
|
2304
|
+
if (!Array.isArray(response.choices) || !isRecord6(response.choices[0])) return void 0;
|
|
2012
2305
|
const message = response.choices[0].message;
|
|
2013
|
-
return
|
|
2306
|
+
return isRecord6(message) ? message : void 0;
|
|
2014
2307
|
}
|
|
2015
2308
|
function extractCalls(endpoint, response) {
|
|
2016
2309
|
const record2 = unwrap(response);
|
|
@@ -2022,7 +2315,7 @@ function responsesCalls(response) {
|
|
|
2022
2315
|
if (!Array.isArray(response.output)) return [];
|
|
2023
2316
|
const calls = [];
|
|
2024
2317
|
for (const item of response.output) {
|
|
2025
|
-
if (!
|
|
2318
|
+
if (!isRecord6(item) || item.type !== "function_call") continue;
|
|
2026
2319
|
calls.push(callUsage(
|
|
2027
2320
|
typeof item.name === "string" ? item.name : "function",
|
|
2028
2321
|
typeof item.call_id === "string" ? item.call_id : "function",
|
|
@@ -2036,7 +2329,7 @@ function chatCalls(response) {
|
|
|
2036
2329
|
if (!message || !Array.isArray(message.tool_calls)) return [];
|
|
2037
2330
|
const calls = [];
|
|
2038
2331
|
for (const item of message.tool_calls) {
|
|
2039
|
-
if (!
|
|
2332
|
+
if (!isRecord6(item) || !isRecord6(item.function)) continue;
|
|
2040
2333
|
const name = typeof item.function.name === "string" ? item.function.name : "function";
|
|
2041
2334
|
calls.push(callUsage(
|
|
2042
2335
|
name,
|
|
@@ -2050,7 +2343,7 @@ function anthropicCalls(response) {
|
|
|
2050
2343
|
if (!Array.isArray(response.content)) return [];
|
|
2051
2344
|
const calls = [];
|
|
2052
2345
|
for (const item of response.content) {
|
|
2053
|
-
if (!
|
|
2346
|
+
if (!isRecord6(item) || item.type !== "tool_use") continue;
|
|
2054
2347
|
const name = typeof item.name === "string" ? item.name : "function";
|
|
2055
2348
|
calls.push({
|
|
2056
2349
|
name,
|
|
@@ -2089,7 +2382,7 @@ function emptyToolUsage(calls) {
|
|
|
2089
2382
|
};
|
|
2090
2383
|
}
|
|
2091
2384
|
function normalizeUsage(usage) {
|
|
2092
|
-
if (!
|
|
2385
|
+
if (!isRecord6(usage)) return void 0;
|
|
2093
2386
|
const inputTokens = numberValue(usage.input_tokens) ?? numberValue(usage.prompt_tokens);
|
|
2094
2387
|
const outputTokens = numberValue(usage.output_tokens) ?? numberValue(usage.completion_tokens);
|
|
2095
2388
|
const totalTokens = numberValue(usage.total_tokens) ?? (inputTokens !== void 0 || outputTokens !== void 0 ? (inputTokens ?? 0) + (outputTokens ?? 0) : void 0);
|
|
@@ -2140,7 +2433,7 @@ function errorEvent(requestId, error) {
|
|
|
2140
2433
|
}
|
|
2141
2434
|
function textDelta(event) {
|
|
2142
2435
|
if (event.type === "response.output_text.delta" && typeof event.delta === "string" && event.delta) return event.delta;
|
|
2143
|
-
if (event.type === "content_block_delta" &&
|
|
2436
|
+
if (event.type === "content_block_delta" && isRecord6(event.delta) && event.delta.type === "text_delta" && typeof event.delta.text === "string" && event.delta.text) {
|
|
2144
2437
|
return event.delta.text;
|
|
2145
2438
|
}
|
|
2146
2439
|
const choice = firstChoiceDelta(event);
|
|
@@ -2149,7 +2442,7 @@ function textDelta(event) {
|
|
|
2149
2442
|
}
|
|
2150
2443
|
function reasoningDelta(event) {
|
|
2151
2444
|
if (event.type === "response.reasoning_summary_text.delta" && typeof event.delta === "string" && event.delta) return event.delta;
|
|
2152
|
-
if (event.type === "content_block_delta" &&
|
|
2445
|
+
if (event.type === "content_block_delta" && isRecord6(event.delta) && event.delta.type === "thinking_delta" && typeof event.delta.thinking === "string" && event.delta.thinking) {
|
|
2153
2446
|
return event.delta.thinking;
|
|
2154
2447
|
}
|
|
2155
2448
|
const choice = firstChoiceDelta(event);
|
|
@@ -2166,13 +2459,13 @@ function toolCallDelta(event) {
|
|
|
2166
2459
|
argumentsDelta: event.delta
|
|
2167
2460
|
};
|
|
2168
2461
|
}
|
|
2169
|
-
if (event.type === "content_block_delta" &&
|
|
2462
|
+
if (event.type === "content_block_delta" && isRecord6(event.delta) && event.delta.type === "input_json_delta" && typeof event.delta.partial_json === "string") {
|
|
2170
2463
|
return { index: typeof event.index === "number" ? event.index : 0, argumentsDelta: event.delta.partial_json };
|
|
2171
2464
|
}
|
|
2172
2465
|
const choice = firstChoiceDelta(event);
|
|
2173
|
-
if (!choice || !Array.isArray(choice.tool_calls) || !
|
|
2466
|
+
if (!choice || !Array.isArray(choice.tool_calls) || !isRecord6(choice.tool_calls[0])) return void 0;
|
|
2174
2467
|
const tool = choice.tool_calls[0];
|
|
2175
|
-
const fn =
|
|
2468
|
+
const fn = isRecord6(tool.function) ? tool.function : void 0;
|
|
2176
2469
|
const args = fn && typeof fn.arguments === "string" ? fn.arguments : void 0;
|
|
2177
2470
|
if (!args && !(fn && typeof fn.name === "string")) return void 0;
|
|
2178
2471
|
return {
|
|
@@ -2182,18 +2475,18 @@ function toolCallDelta(event) {
|
|
|
2182
2475
|
};
|
|
2183
2476
|
}
|
|
2184
2477
|
function streamUsage(event) {
|
|
2185
|
-
if (event.type === "response.completed" &&
|
|
2186
|
-
if (
|
|
2478
|
+
if (event.type === "response.completed" && isRecord6(event.response)) return normalizeUsage(event.response.usage);
|
|
2479
|
+
if (isRecord6(event.usage)) return normalizeUsage(event.usage);
|
|
2187
2480
|
return void 0;
|
|
2188
2481
|
}
|
|
2189
2482
|
function firstChoiceDelta(event) {
|
|
2190
|
-
if (!Array.isArray(event.choices) || !
|
|
2483
|
+
if (!Array.isArray(event.choices) || !isRecord6(event.choices[0])) return void 0;
|
|
2191
2484
|
const delta = event.choices[0].delta;
|
|
2192
|
-
return
|
|
2485
|
+
return isRecord6(delta) ? delta : void 0;
|
|
2193
2486
|
}
|
|
2194
2487
|
function unwrap(response) {
|
|
2195
|
-
if (!
|
|
2196
|
-
if (response.success === true &&
|
|
2488
|
+
if (!isRecord6(response)) return {};
|
|
2489
|
+
if (response.success === true && isRecord6(response.result)) return response.result;
|
|
2197
2490
|
return response;
|
|
2198
2491
|
}
|
|
2199
2492
|
function modelOf(compiled) {
|
|
@@ -2201,12 +2494,12 @@ function modelOf(compiled) {
|
|
|
2201
2494
|
return "";
|
|
2202
2495
|
}
|
|
2203
2496
|
function messageFrom(body) {
|
|
2204
|
-
if (!
|
|
2497
|
+
if (!isRecord6(body)) return void 0;
|
|
2205
2498
|
if (typeof body.message === "string") return body.message;
|
|
2206
|
-
if (
|
|
2499
|
+
if (isRecord6(body.error) && typeof body.error.message === "string") return body.error.message;
|
|
2207
2500
|
if (Array.isArray(body.errors)) {
|
|
2208
2501
|
for (const error of body.errors) {
|
|
2209
|
-
if (
|
|
2502
|
+
if (isRecord6(error) && typeof error.message === "string") return error.message;
|
|
2210
2503
|
}
|
|
2211
2504
|
}
|
|
2212
2505
|
return void 0;
|
|
@@ -2347,7 +2640,7 @@ function readEnv(name) {
|
|
|
2347
2640
|
if (typeof process === "undefined") return void 0;
|
|
2348
2641
|
return process.env[name];
|
|
2349
2642
|
}
|
|
2350
|
-
function
|
|
2643
|
+
function isRecord6(value) {
|
|
2351
2644
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2352
2645
|
}
|
|
2353
2646
|
|
|
@@ -2555,7 +2848,7 @@ function settleNested(policy, kind, parentEffort) {
|
|
|
2555
2848
|
if (kind === "fusion") next.judgeModel = directive.model;
|
|
2556
2849
|
else next.model = directive.model;
|
|
2557
2850
|
}
|
|
2558
|
-
if (
|
|
2851
|
+
if (isRecord7(directive.body.reasoning)) next.reasoning = directive.body.reasoning;
|
|
2559
2852
|
return next;
|
|
2560
2853
|
}
|
|
2561
2854
|
function applyPromptPrefix(request, prefix) {
|
|
@@ -2605,7 +2898,7 @@ function prefixInputItems(items, prefix) {
|
|
|
2605
2898
|
}
|
|
2606
2899
|
if (!Array.isArray(item.content)) continue;
|
|
2607
2900
|
for (const part of item.content) {
|
|
2608
|
-
if (!
|
|
2901
|
+
if (!isRecord7(part) || typeof part.text !== "string") continue;
|
|
2609
2902
|
if (part.type === "text" || part.type === "input_text") {
|
|
2610
2903
|
part.text = prefix + part.text;
|
|
2611
2904
|
return true;
|
|
@@ -2666,7 +2959,7 @@ var ROLE_STAMP_FIELDS = ["role", "roleLayer", "roleDigest", "profileChoice"];
|
|
|
2666
2959
|
function applyRoleStamp(request) {
|
|
2667
2960
|
const stamp = request.role;
|
|
2668
2961
|
delete request.role;
|
|
2669
|
-
if (!
|
|
2962
|
+
if (!isRecord7(stamp)) return;
|
|
2670
2963
|
const identity = {};
|
|
2671
2964
|
for (const field of ROLE_STAMP_FIELDS) {
|
|
2672
2965
|
const value = stamp[field];
|
|
@@ -2681,8 +2974,8 @@ function applyRoleStamp(request) {
|
|
|
2681
2974
|
}
|
|
2682
2975
|
function absorbOverrideMetadata(request) {
|
|
2683
2976
|
const overrides = request.rawOpenRouterOverrides;
|
|
2684
|
-
if (!
|
|
2685
|
-
if (
|
|
2977
|
+
if (!isRecord7(overrides) || !Object.prototype.hasOwnProperty.call(overrides, "metadata")) return;
|
|
2978
|
+
if (isRecord7(overrides.metadata)) {
|
|
2686
2979
|
const current = { ...request.metadata ?? {} };
|
|
2687
2980
|
const extra = {};
|
|
2688
2981
|
for (const [key, value] of Object.entries(overrides.metadata)) {
|
|
@@ -2721,7 +3014,7 @@ function hasOwnKeys(value) {
|
|
|
2721
3014
|
function hasStreamMetadata(directive) {
|
|
2722
3015
|
return directive.response.streamReasoningKeys.length > 0 || directive.response.streamReasoningPaths.length > 0;
|
|
2723
3016
|
}
|
|
2724
|
-
function
|
|
3017
|
+
function isRecord7(value) {
|
|
2725
3018
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2726
3019
|
}
|
|
2727
3020
|
function readEnv2(name) {
|
|
@@ -3040,7 +3333,7 @@ async function runOpenAi(request, options, call, requestId) {
|
|
|
3040
3333
|
let response;
|
|
3041
3334
|
for (let iteration = 0; iteration <= maxIterations; iteration += 1) {
|
|
3042
3335
|
response = await sendWithRetries2(active, options, timeoutFor2(request, options));
|
|
3043
|
-
const calls = functionCalls(
|
|
3336
|
+
const calls = functionCalls(isRecord8(response) ? response : {});
|
|
3044
3337
|
if (!calls.length) break;
|
|
3045
3338
|
if (!calls.every((item) => resolveExecutor2(item.name, request, options))) {
|
|
3046
3339
|
return normalizeOpenAiResponse({
|
|
@@ -3060,7 +3353,7 @@ async function runOpenAi(request, options, call, requestId) {
|
|
|
3060
3353
|
results.push(await executeOpenAiTool(item, request, options));
|
|
3061
3354
|
}
|
|
3062
3355
|
functionUsage.push(...results);
|
|
3063
|
-
const record2 =
|
|
3356
|
+
const record2 = isRecord8(response) ? response : {};
|
|
3064
3357
|
active = {
|
|
3065
3358
|
...active,
|
|
3066
3359
|
body: clean2({
|
|
@@ -3175,7 +3468,7 @@ async function* streamOpenAi(request, options, call) {
|
|
|
3175
3468
|
throw failure;
|
|
3176
3469
|
}
|
|
3177
3470
|
for await (const event of parseSse2(response)) {
|
|
3178
|
-
if (!
|
|
3471
|
+
if (!isRecord8(event)) continue;
|
|
3179
3472
|
if (event.type === "response.output_text.delta" && typeof event.delta === "string" && event.delta) {
|
|
3180
3473
|
text += event.delta;
|
|
3181
3474
|
yield { type: "stream.text.delta", requestId, data: { text: event.delta } };
|
|
@@ -3194,7 +3487,7 @@ async function* streamOpenAi(request, options, call) {
|
|
|
3194
3487
|
argumentsDelta: event.delta
|
|
3195
3488
|
}
|
|
3196
3489
|
};
|
|
3197
|
-
} else if (event.type === "response.completed" &&
|
|
3490
|
+
} else if (event.type === "response.completed" && isRecord8(event.response)) {
|
|
3198
3491
|
const usage = normalizeUsage2(event.response.usage);
|
|
3199
3492
|
if (usage) yield { type: "stream.usage", requestId, data: { usage } };
|
|
3200
3493
|
} else if (event.type === "error") {
|
|
@@ -3288,7 +3581,7 @@ function isConfigError2(code) {
|
|
|
3288
3581
|
}
|
|
3289
3582
|
function asTerminalError2(error) {
|
|
3290
3583
|
if (error instanceof AiDispatcherError && error.code === "PROVIDER_RETRYABLE") {
|
|
3291
|
-
const status =
|
|
3584
|
+
const status = isRecord8(error.details) ? error.details.status : void 0;
|
|
3292
3585
|
if (status === 429) return new AiDispatcherError("PROVIDER_RATE_LIMITED", error.message, error.details);
|
|
3293
3586
|
return new AiDispatcherError("PROVIDER_GATEWAY_UNREACHABLE", error.message, error.details);
|
|
3294
3587
|
}
|
|
@@ -3296,7 +3589,7 @@ function asTerminalError2(error) {
|
|
|
3296
3589
|
return new AiDispatcherError("PROVIDER_REQUEST_FAILED", error instanceof Error ? error.message : String(error));
|
|
3297
3590
|
}
|
|
3298
3591
|
function normalizeOpenAiResponse(params) {
|
|
3299
|
-
const record2 =
|
|
3592
|
+
const record2 = isRecord8(params.response) ? params.response : {};
|
|
3300
3593
|
const text = answerText2(record2);
|
|
3301
3594
|
const calls = params.functionTools ?? functionCalls(record2);
|
|
3302
3595
|
const usage = normalizeUsage2(record2.usage);
|
|
@@ -3323,9 +3616,9 @@ function answerText2(response) {
|
|
|
3323
3616
|
if (Array.isArray(output)) {
|
|
3324
3617
|
const parts = [];
|
|
3325
3618
|
for (const item of output) {
|
|
3326
|
-
if (!
|
|
3619
|
+
if (!isRecord8(item) || item.type === "reasoning" || item.type !== "message" || !Array.isArray(item.content)) continue;
|
|
3327
3620
|
for (const part of item.content) {
|
|
3328
|
-
if (
|
|
3621
|
+
if (isRecord8(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
|
|
3329
3622
|
}
|
|
3330
3623
|
}
|
|
3331
3624
|
if (parts.length > 0) return parts.join("");
|
|
@@ -3336,7 +3629,7 @@ function functionCalls(response) {
|
|
|
3336
3629
|
if (!Array.isArray(response.output)) return [];
|
|
3337
3630
|
const calls = [];
|
|
3338
3631
|
for (const item of response.output) {
|
|
3339
|
-
if (!
|
|
3632
|
+
if (!isRecord8(item) || item.type !== "function_call") continue;
|
|
3340
3633
|
const name = typeof item.name === "string" ? item.name : "function";
|
|
3341
3634
|
const callId = typeof item.call_id === "string" ? item.call_id : name;
|
|
3342
3635
|
let args = item.arguments;
|
|
@@ -3368,7 +3661,7 @@ function toolUsage(calls) {
|
|
|
3368
3661
|
};
|
|
3369
3662
|
}
|
|
3370
3663
|
function normalizeUsage2(usage) {
|
|
3371
|
-
if (!
|
|
3664
|
+
if (!isRecord8(usage)) return void 0;
|
|
3372
3665
|
const inputTokens = numberValue2(usage.input_tokens);
|
|
3373
3666
|
const outputTokens = numberValue2(usage.output_tokens);
|
|
3374
3667
|
const totalTokens = numberValue2(usage.total_tokens) ?? (inputTokens !== void 0 || outputTokens !== void 0 ? (inputTokens ?? 0) + (outputTokens ?? 0) : void 0);
|
|
@@ -3420,16 +3713,16 @@ function modelOf2(compiled) {
|
|
|
3420
3713
|
return typeof compiled.body.model === "string" ? compiled.body.model : "";
|
|
3421
3714
|
}
|
|
3422
3715
|
function messageFrom2(body) {
|
|
3423
|
-
if (!
|
|
3716
|
+
if (!isRecord8(body)) return void 0;
|
|
3424
3717
|
if (typeof body.message === "string") return body.message;
|
|
3425
|
-
if (
|
|
3718
|
+
if (isRecord8(body.error) && typeof body.error.message === "string") return body.error.message;
|
|
3426
3719
|
return void 0;
|
|
3427
3720
|
}
|
|
3428
3721
|
function numberValue2(value) {
|
|
3429
3722
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3430
3723
|
}
|
|
3431
3724
|
function attachProviderMetadata(body, request, packer) {
|
|
3432
|
-
const fromBody =
|
|
3725
|
+
const fromBody = isRecord8(body.metadata) ? body.metadata : void 0;
|
|
3433
3726
|
const packed = (0, import_openrouter_runtime2.packProviderRecord)(request.metadata, request.agentId, fromBody, packer);
|
|
3434
3727
|
if (packed.wire) return { body: { ...body, metadata: packed.wire }, ...packed.warning ? { warning: packed.warning } : {} };
|
|
3435
3728
|
if (body.metadata === void 0) return { body, ...packed.warning ? { warning: packed.warning } : {} };
|
|
@@ -3503,7 +3796,7 @@ function readEnv3(name) {
|
|
|
3503
3796
|
if (typeof process === "undefined") return void 0;
|
|
3504
3797
|
return process.env[name];
|
|
3505
3798
|
}
|
|
3506
|
-
function
|
|
3799
|
+
function isRecord8(value) {
|
|
3507
3800
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3508
3801
|
}
|
|
3509
3802
|
|
|
@@ -3571,6 +3864,7 @@ function createAiDispatcher(options = {}) {
|
|
|
3571
3864
|
if (provider === "openrouter") {
|
|
3572
3865
|
const openrouterOptions = {
|
|
3573
3866
|
...options.openrouter,
|
|
3867
|
+
...options.openrouter?.tools ? { tools: wrapToolRegistry(options.openrouter.tools) } : {},
|
|
3574
3868
|
metadataPacker: packer,
|
|
3575
3869
|
...options.logger && !options.openrouter?.logger ? { logger: options.logger } : {}
|
|
3576
3870
|
};
|
|
@@ -3579,6 +3873,7 @@ function createAiDispatcher(options = {}) {
|
|
|
3579
3873
|
if (provider === "bedrock") {
|
|
3580
3874
|
const bedrockOptions = {
|
|
3581
3875
|
...options.bedrock,
|
|
3876
|
+
...options.bedrock?.tools ? { tools: wrapToolRegistry(options.bedrock.tools) } : {},
|
|
3582
3877
|
metadataPacker: packer,
|
|
3583
3878
|
...options.logger && !options.bedrock?.logger ? { logger: options.logger } : {}
|
|
3584
3879
|
};
|
|
@@ -3587,6 +3882,7 @@ function createAiDispatcher(options = {}) {
|
|
|
3587
3882
|
if (provider === "openai") {
|
|
3588
3883
|
const openaiOptions = {
|
|
3589
3884
|
...options.openai,
|
|
3885
|
+
...options.openai?.tools ? { tools: wrapToolRegistry(options.openai.tools) } : {},
|
|
3590
3886
|
...options.logger && !options.openai?.logger ? { logger: options.logger } : {}
|
|
3591
3887
|
};
|
|
3592
3888
|
return createOpenAiProviderAdapter(openaiOptions, packer);
|
|
@@ -3594,6 +3890,7 @@ function createAiDispatcher(options = {}) {
|
|
|
3594
3890
|
if (provider === "cloudflare") {
|
|
3595
3891
|
const cloudflareOptions = {
|
|
3596
3892
|
...options.cloudflare,
|
|
3893
|
+
...options.cloudflare?.tools ? { tools: wrapToolRegistry(options.cloudflare.tools) } : {},
|
|
3597
3894
|
...options.logger && !options.cloudflare?.logger ? { logger: options.logger } : {}
|
|
3598
3895
|
};
|
|
3599
3896
|
return createCloudflareProviderAdapter(cloudflareOptions, packer);
|
|
@@ -3615,21 +3912,27 @@ function createAiDispatcher(options = {}) {
|
|
|
3615
3912
|
return {
|
|
3616
3913
|
provider: defaultProvider,
|
|
3617
3914
|
async run(request) {
|
|
3618
|
-
const prepared = prepare(request, true);
|
|
3915
|
+
const prepared = await prepare(request, true);
|
|
3619
3916
|
log(options, "ai-dispatcher.request.started", prepared, "run", false);
|
|
3620
|
-
const response = await
|
|
3917
|
+
const response = await inputStorage.run(
|
|
3918
|
+
prepared.inputTrack,
|
|
3919
|
+
() => getAdapter(prepared.provider).run(prepared.request, callOf(prepared.call))
|
|
3920
|
+
);
|
|
3621
3921
|
return applyResolution(response, prepared);
|
|
3622
3922
|
},
|
|
3623
3923
|
executeStreamingChat(request) {
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
prepared
|
|
3628
|
-
|
|
3629
|
-
|
|
3924
|
+
return (async function* () {
|
|
3925
|
+
const prepared = await prepare(request, false);
|
|
3926
|
+
log(options, "ai-dispatcher.request.started", prepared, "executeStreamingChat", true);
|
|
3927
|
+
if (prepared.inputTrack) inputStorage.enterWith(prepared.inputTrack);
|
|
3928
|
+
yield* decorateStream(
|
|
3929
|
+
prepared,
|
|
3930
|
+
getAdapter(prepared.provider).executeStreamingChat(prepared.request, callOf(prepared.call))
|
|
3931
|
+
);
|
|
3932
|
+
})();
|
|
3630
3933
|
},
|
|
3631
|
-
compile(request) {
|
|
3632
|
-
const prepared = prepare(request, true);
|
|
3934
|
+
async compile(request) {
|
|
3935
|
+
const prepared = await prepare(request, true);
|
|
3633
3936
|
log(options, "ai-dispatcher.request.compiled", prepared, "compile", false);
|
|
3634
3937
|
const compiled = getAdapter(prepared.provider).compile(prepared.request, callOf(prepared.call));
|
|
3635
3938
|
return attachCompileMeta(prepared, compiled);
|
|
@@ -3643,7 +3946,7 @@ function createAiDispatcher(options = {}) {
|
|
|
3643
3946
|
return pending;
|
|
3644
3947
|
}
|
|
3645
3948
|
};
|
|
3646
|
-
function prepare(request, includeMcp) {
|
|
3949
|
+
async function prepare(request, includeMcp) {
|
|
3647
3950
|
const provider = resolveProvider(request.provider, defaultProvider);
|
|
3648
3951
|
const prepared = prepareDispatchRequest({ request, provider, options });
|
|
3649
3952
|
const tools = providerTools(provider, options);
|
|
@@ -3653,6 +3956,11 @@ function createAiDispatcher(options = {}) {
|
|
|
3653
3956
|
...tools ? { providerTools: tools } : {},
|
|
3654
3957
|
includeMcp
|
|
3655
3958
|
});
|
|
3959
|
+
prepared.inputTrack = await applyAiInput({
|
|
3960
|
+
request: prepared.request,
|
|
3961
|
+
effectiveModel: prepared.effectiveModel,
|
|
3962
|
+
factory: options.aiInput
|
|
3963
|
+
});
|
|
3656
3964
|
return prepared;
|
|
3657
3965
|
}
|
|
3658
3966
|
}
|
|
@@ -3670,7 +3978,8 @@ function callOf(call) {
|
|
|
3670
3978
|
}
|
|
3671
3979
|
function attachCompileMeta(prepared, compiled) {
|
|
3672
3980
|
const record2 = compiled;
|
|
3673
|
-
const headers =
|
|
3981
|
+
const headers = isRecord9(record2.headers) ? redactHeaders(record2.headers) : void 0;
|
|
3982
|
+
const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
|
|
3674
3983
|
return {
|
|
3675
3984
|
...record2,
|
|
3676
3985
|
...headers ? { headers } : {},
|
|
@@ -3679,6 +3988,7 @@ function attachCompileMeta(prepared, compiled) {
|
|
|
3679
3988
|
effectiveModel: prepared.effectiveModel,
|
|
3680
3989
|
...prepared.resolution ? { reasoningResolution: prepared.resolution } : {},
|
|
3681
3990
|
...prepared.cacheResolution ? { cacheResolution: prepared.cacheResolution } : {},
|
|
3991
|
+
...inputResolution ? { inputResolution } : {},
|
|
3682
3992
|
...prepared.cacheWarnings?.length ? { warnings: [...Array.isArray(record2.warnings) ? record2.warnings : [], ...prepared.cacheWarnings] } : {}
|
|
3683
3993
|
};
|
|
3684
3994
|
}
|
|
@@ -3704,7 +4014,7 @@ function log(options, event, prepared, entrypoint, streaming) {
|
|
|
3704
4014
|
if (event.endsWith("compiled")) options.logger?.debug?.(event, payload);
|
|
3705
4015
|
else options.logger?.info?.(event, payload);
|
|
3706
4016
|
}
|
|
3707
|
-
function
|
|
4017
|
+
function isRecord9(value) {
|
|
3708
4018
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3709
4019
|
}
|
|
3710
4020
|
|