@x12i/ai-dispatcher 1.10.0 → 2.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 +3 -3
- package/dist/index.cjs +34 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +35 -25
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ One request shape for OpenRouter, AWS Bedrock, the OpenAI Responses API, and Clo
|
|
|
4
4
|
|
|
5
5
|
**Implemented providers:** `openrouter`, `bedrock`, `openai`, `cloudflare`. Any other provider throws `PROVIDER_NOT_IMPLEMENTED`.
|
|
6
6
|
|
|
7
|
-
The package is `@x12i/ai-dispatcher` 1.
|
|
7
|
+
The package is `@x12i/ai-dispatcher` 2.1.0. It runs on Node 20 or newer and publishes ESM and CommonJS from the same entry.
|
|
8
8
|
|
|
9
9
|
Connected MCP servers can be passed to `createAiDispatcher`. `run()` and `compile()` expose those tools to the model. `executeStreamingChat` does not. Details are in [MCP tools](#mcp-tools).
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ Connected MCP servers can be passed to `createAiDispatcher`. `run()` and `compil
|
|
|
14
14
|
npm install @x12i/ai-dispatcher
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
The package depends on `@x12i/ai-profiles@5.0.0`, `@x12i/openrouter-runtime@^1.
|
|
17
|
+
The package depends on `@x12i/ai-profiles@5.0.0`, `@x12i/provider-metadata@1.1.0`, `@x12i/openrouter-runtime@^2.1.0`, and `@x12i/bedrock-runtime@^2.1.0`.
|
|
18
18
|
|
|
19
19
|
## One request
|
|
20
20
|
|
|
@@ -177,7 +177,7 @@ const record = decodeProviderMetadata(logMetadata);
|
|
|
177
177
|
|
|
178
178
|
`metadata` is the one record. When `rawOpenRouterOverrides` only repeats `metadata`, the dispatcher folds it in. `metadata` wins when the same key is in both places. String, number, and boolean values are stored, including a JSON string. Objects and arrays stay on the normalized response and the tool context.
|
|
179
179
|
|
|
180
|
-
Every provider writes that record as `m.0`, `m.1`, and so on: one base64 string, 256 characters per piece, at most 16 pieces. The base64 is the UTF-8 JSON object of the string map, with keys sorted. `decodeProviderMetadata` reads those pieces from OpenRouter body `metadata`, OpenAI Responses `metadata`, Bedrock `requestMetadata`, or Cloudflare body `metadata` and `cf-aig-metadata`. The pieces are the same on every provider. A
|
|
180
|
+
Every provider writes that record as `m.0`, `m.1`, and so on: one base64 string, 256 characters per piece, at most 16 pieces. The base64 is the UTF-8 JSON object of the string map, with keys sorted. `decodeProviderMetadata` reads those pieces from OpenRouter body `metadata`, OpenAI Responses `metadata`, Bedrock `requestMetadata`, or Cloudflare body `metadata` and `cf-aig-metadata`. The pieces are the same on every provider. The dispatcher trims on every call. Pass `metadataPriority` (highest priority first) to choose what stays. The default keeps `agentId`, `orgId`, `stepId`, `skillId`, `recordId`, and `objectType`. A trimmed call adds a `METADATA_RECORD_TRIMMED` warning.
|
|
181
181
|
|
|
182
182
|
OpenRouter also puts `agentId` in `X-Title` and `X-OpenRouter-Title`, ahead of `appAttribution.appName` and `defaultHeaders`. That header is the activity-log App name. `appAttribution.siteUrl` is still `HTTP-Referer` when set. The App header is not a second metadata record.
|
|
183
183
|
|
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AiDispatcherError: () => AiDispatcherError,
|
|
24
|
+
DEFAULT_METADATA_PRIORITY: () => DEFAULT_METADATA_PRIORITY,
|
|
24
25
|
IMPLEMENTED_AI_PROVIDERS: () => IMPLEMENTED_AI_PROVIDERS,
|
|
25
26
|
createAiDispatcher: () => createAiDispatcher,
|
|
26
27
|
decodeProviderMetadata: () => import_openrouter_runtime4.decodeProviderMetadata,
|
|
@@ -32,6 +33,9 @@ __export(index_exports, {
|
|
|
32
33
|
module.exports = __toCommonJS(index_exports);
|
|
33
34
|
var import_openrouter_runtime4 = require("@x12i/openrouter-runtime");
|
|
34
35
|
|
|
36
|
+
// src/create-dispatcher.ts
|
|
37
|
+
var import_provider_metadata = require("@x12i/provider-metadata");
|
|
38
|
+
|
|
35
39
|
// src/cache-usage.ts
|
|
36
40
|
function readCacheTokens(source) {
|
|
37
41
|
const cached = [];
|
|
@@ -1178,8 +1182,8 @@ function restoreCloudflareModel(model, prefix) {
|
|
|
1178
1182
|
if (!prefix || model.includes("/")) return model;
|
|
1179
1183
|
return `${prefix}${model}`;
|
|
1180
1184
|
}
|
|
1181
|
-
function createCloudflareProviderAdapter(options = {}) {
|
|
1182
|
-
const resolved = resolveCloudflareOptions(options);
|
|
1185
|
+
function createCloudflareProviderAdapter(options = {}, packer) {
|
|
1186
|
+
const resolved = resolveCloudflareOptions(options, packer);
|
|
1183
1187
|
return {
|
|
1184
1188
|
compile(request, call) {
|
|
1185
1189
|
return compileCloudflareRequest(request, resolved, call, false);
|
|
@@ -1232,12 +1236,13 @@ function compileCloudflareRequest(request, options, call, streaming) {
|
|
|
1232
1236
|
if (endpoint !== "run") body = { ...body, stream: streaming };
|
|
1233
1237
|
const gateway = mergeGateway(options.gateway, call?.cloudflare?.gateway);
|
|
1234
1238
|
const fromBody = isRecord5(body.metadata) ? body.metadata : void 0;
|
|
1235
|
-
const
|
|
1239
|
+
const packed = (0, import_openrouter_runtime.packProviderRecord)(
|
|
1236
1240
|
request.metadata,
|
|
1237
1241
|
request.agentId,
|
|
1238
|
-
|
|
1239
|
-
|
|
1242
|
+
overlayMetadata(gateway?.metadata, fromBody),
|
|
1243
|
+
options.metadataPacker
|
|
1240
1244
|
);
|
|
1245
|
+
const record2 = packed.wire;
|
|
1241
1246
|
body = applyProviderRecord(body, record2);
|
|
1242
1247
|
if (endpoint === "run") body = wrapRun(body, run);
|
|
1243
1248
|
return {
|
|
@@ -1245,7 +1250,7 @@ function compileCloudflareRequest(request, options, call, streaming) {
|
|
|
1245
1250
|
url: endpointUrl(options, endpoint),
|
|
1246
1251
|
headers: buildHeaders(options, call?.cloudflare, model, record2),
|
|
1247
1252
|
body,
|
|
1248
|
-
warnings: []
|
|
1253
|
+
warnings: packed.warning ? [packed.warning] : []
|
|
1249
1254
|
};
|
|
1250
1255
|
}
|
|
1251
1256
|
function compileResponsesBody(request, model) {
|
|
@@ -2294,7 +2299,7 @@ function parseSsePart(part) {
|
|
|
2294
2299
|
return void 0;
|
|
2295
2300
|
}
|
|
2296
2301
|
}
|
|
2297
|
-
function resolveCloudflareOptions(options) {
|
|
2302
|
+
function resolveCloudflareOptions(options, packer) {
|
|
2298
2303
|
const {
|
|
2299
2304
|
apiToken,
|
|
2300
2305
|
accountId,
|
|
@@ -2323,7 +2328,8 @@ function resolveCloudflareOptions(options) {
|
|
|
2323
2328
|
maxAttempts: maxAttempts ?? 2,
|
|
2324
2329
|
fetch: fetch ?? globalThis.fetch,
|
|
2325
2330
|
...tools ? { tools } : {},
|
|
2326
|
-
...logger ? { logger } : {}
|
|
2331
|
+
...logger ? { logger } : {},
|
|
2332
|
+
...packer ? { metadataPacker: packer } : {}
|
|
2327
2333
|
};
|
|
2328
2334
|
}
|
|
2329
2335
|
function hasGateway(gateway) {
|
|
@@ -2838,8 +2844,8 @@ function isBedrockToolChoice(toolChoice) {
|
|
|
2838
2844
|
var import_openrouter_runtime2 = require("@x12i/openrouter-runtime");
|
|
2839
2845
|
var DEFAULT_BASE_URL2 = "https://api.openai.com/v1";
|
|
2840
2846
|
var DEFAULT_TIMEOUT_MS2 = 12e4;
|
|
2841
|
-
function createOpenAiProviderAdapter(options = {}) {
|
|
2842
|
-
const resolved = resolveOpenAiOptions(options);
|
|
2847
|
+
function createOpenAiProviderAdapter(options = {}, packer) {
|
|
2848
|
+
const resolved = resolveOpenAiOptions(options, packer);
|
|
2843
2849
|
return {
|
|
2844
2850
|
compile(request, call) {
|
|
2845
2851
|
return compileOpenAiRequest(request, resolved, call, false);
|
|
@@ -2892,12 +2898,13 @@ function compileOpenAiRequest(request, options, call, streaming) {
|
|
|
2892
2898
|
if (call?.finalizeBody) body = call.finalizeBody(body);
|
|
2893
2899
|
if (streaming) body = { ...body, stream: true };
|
|
2894
2900
|
else body = { ...body, stream: false };
|
|
2895
|
-
|
|
2901
|
+
const packed = attachProviderMetadata(body, request, options.metadataPacker);
|
|
2902
|
+
body = packed.body;
|
|
2896
2903
|
return {
|
|
2897
2904
|
url: `${options.baseUrl}/responses`,
|
|
2898
2905
|
headers: buildHeaders2(options),
|
|
2899
2906
|
body,
|
|
2900
|
-
warnings: []
|
|
2907
|
+
warnings: packed.warning ? [packed.warning] : []
|
|
2901
2908
|
};
|
|
2902
2909
|
}
|
|
2903
2910
|
function assertSupported2(request) {
|
|
@@ -3397,14 +3404,14 @@ function messageFrom2(body) {
|
|
|
3397
3404
|
function numberValue2(value) {
|
|
3398
3405
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3399
3406
|
}
|
|
3400
|
-
function attachProviderMetadata(body, request) {
|
|
3407
|
+
function attachProviderMetadata(body, request, packer) {
|
|
3401
3408
|
const fromBody = isRecord7(body.metadata) ? body.metadata : void 0;
|
|
3402
|
-
const
|
|
3403
|
-
if (
|
|
3404
|
-
if (body.metadata === void 0) return body;
|
|
3409
|
+
const packed = (0, import_openrouter_runtime2.packProviderRecord)(request.metadata, request.agentId, fromBody, packer);
|
|
3410
|
+
if (packed.wire) return { body: { ...body, metadata: packed.wire }, ...packed.warning ? { warning: packed.warning } : {} };
|
|
3411
|
+
if (body.metadata === void 0) return { body, ...packed.warning ? { warning: packed.warning } : {} };
|
|
3405
3412
|
const rest = { ...body };
|
|
3406
3413
|
delete rest.metadata;
|
|
3407
|
-
return rest;
|
|
3414
|
+
return { body: rest, ...packed.warning ? { warning: packed.warning } : {} };
|
|
3408
3415
|
}
|
|
3409
3416
|
function clean2(body) {
|
|
3410
3417
|
return Object.fromEntries(Object.entries(body).filter(([, value]) => value !== void 0));
|
|
@@ -3452,7 +3459,7 @@ function parseSsePart2(part) {
|
|
|
3452
3459
|
return void 0;
|
|
3453
3460
|
}
|
|
3454
3461
|
}
|
|
3455
|
-
function resolveOpenAiOptions(options) {
|
|
3462
|
+
function resolveOpenAiOptions(options, packer) {
|
|
3456
3463
|
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
|
|
3457
3464
|
return {
|
|
3458
3465
|
apiKey: options.apiKey ?? readEnv3("OPENAI_API_KEY") ?? "",
|
|
@@ -3464,7 +3471,8 @@ function resolveOpenAiOptions(options) {
|
|
|
3464
3471
|
maxAttempts: options.maxAttempts ?? 3,
|
|
3465
3472
|
fetch: options.fetch ?? globalThis.fetch,
|
|
3466
3473
|
...options.tools ? { tools: options.tools } : {},
|
|
3467
|
-
...options.logger ? { logger: options.logger } : {}
|
|
3474
|
+
...options.logger ? { logger: options.logger } : {},
|
|
3475
|
+
...packer ? { metadataPacker: packer } : {}
|
|
3468
3476
|
};
|
|
3469
3477
|
}
|
|
3470
3478
|
function readEnv3(name) {
|
|
@@ -3517,7 +3525,9 @@ function resolveProvider(requestProvider, defaultProvider) {
|
|
|
3517
3525
|
|
|
3518
3526
|
// src/create-dispatcher.ts
|
|
3519
3527
|
var SECRET_HEADERS = /* @__PURE__ */ new Set(["authorization", "x-api-key", "api-key"]);
|
|
3528
|
+
var DEFAULT_METADATA_PRIORITY = ["agentId", "orgId", "stepId", "skillId", "recordId", "objectType"];
|
|
3520
3529
|
function createAiDispatcher(options = {}) {
|
|
3530
|
+
const packer = (0, import_provider_metadata.createMetadataPacker)({ priority: options.metadataPriority ?? DEFAULT_METADATA_PRIORITY });
|
|
3521
3531
|
const defaultProvider = resolveProvider(options.provider, "openrouter");
|
|
3522
3532
|
const catalog = createMcpCatalog(options.mcp);
|
|
3523
3533
|
let refreshChain = Promise.resolve();
|
|
@@ -3526,6 +3536,7 @@ function createAiDispatcher(options = {}) {
|
|
|
3526
3536
|
if (provider === "openrouter") {
|
|
3527
3537
|
const openrouterOptions = {
|
|
3528
3538
|
...options.openrouter,
|
|
3539
|
+
metadataPacker: packer,
|
|
3529
3540
|
...options.logger && !options.openrouter?.logger ? { logger: options.logger } : {}
|
|
3530
3541
|
};
|
|
3531
3542
|
return createOpenRouterProviderAdapter(openrouterOptions);
|
|
@@ -3533,6 +3544,7 @@ function createAiDispatcher(options = {}) {
|
|
|
3533
3544
|
if (provider === "bedrock") {
|
|
3534
3545
|
const bedrockOptions = {
|
|
3535
3546
|
...options.bedrock,
|
|
3547
|
+
metadataPacker: packer,
|
|
3536
3548
|
...options.logger && !options.bedrock?.logger ? { logger: options.logger } : {}
|
|
3537
3549
|
};
|
|
3538
3550
|
return createBedrockProviderAdapter(bedrockOptions);
|
|
@@ -3542,14 +3554,14 @@ function createAiDispatcher(options = {}) {
|
|
|
3542
3554
|
...options.openai,
|
|
3543
3555
|
...options.logger && !options.openai?.logger ? { logger: options.logger } : {}
|
|
3544
3556
|
};
|
|
3545
|
-
return createOpenAiProviderAdapter(openaiOptions);
|
|
3557
|
+
return createOpenAiProviderAdapter(openaiOptions, packer);
|
|
3546
3558
|
}
|
|
3547
3559
|
if (provider === "cloudflare") {
|
|
3548
3560
|
const cloudflareOptions = {
|
|
3549
3561
|
...options.cloudflare,
|
|
3550
3562
|
...options.logger && !options.cloudflare?.logger ? { logger: options.logger } : {}
|
|
3551
3563
|
};
|
|
3552
|
-
return createCloudflareProviderAdapter(cloudflareOptions);
|
|
3564
|
+
return createCloudflareProviderAdapter(cloudflareOptions, packer);
|
|
3553
3565
|
}
|
|
3554
3566
|
throw new AiDispatcherError(
|
|
3555
3567
|
"PROVIDER_ADAPTER_MISSING",
|
|
@@ -3670,6 +3682,7 @@ function stripReasoningHistoryFields(message, paths) {
|
|
|
3670
3682
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3671
3683
|
0 && (module.exports = {
|
|
3672
3684
|
AiDispatcherError,
|
|
3685
|
+
DEFAULT_METADATA_PRIORITY,
|
|
3673
3686
|
IMPLEMENTED_AI_PROVIDERS,
|
|
3674
3687
|
createAiDispatcher,
|
|
3675
3688
|
decodeProviderMetadata,
|