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