@x12i/ai-dispatcher 2.1.0 → 2.3.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 +12 -10
- package/dist/index.cjs +39 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -6
- package/dist/index.d.ts +20 -6
- package/dist/index.js +41 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @x12i/ai-dispatcher
|
|
2
2
|
|
|
3
|
-
One request shape for OpenRouter, AWS Bedrock, the OpenAI Responses API, and Cloudflare AI. You send the same request shape. The package picks the provider, and `@x12i/ai-profiles
|
|
3
|
+
One request shape for OpenRouter, AWS Bedrock, the OpenAI Responses API, and Cloudflare AI. You send the same request shape. The package picks the provider, and `@x12i/ai-profiles@^5.1.0` turns `reasoningEffort` into that provider's wire fields. Callers do not map effort levels onto provider fields or parse provider reasoning channels.
|
|
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` 2.
|
|
7
|
+
The package is `@x12i/ai-dispatcher` 2.3.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
|
|
17
|
+
The package depends on `@x12i/ai-profiles@^5.1.0`, `@x12i/provider-metadata@^1.2.0`, `@x12i/openrouter-runtime@^2.2.0`, and `@x12i/bedrock-runtime@^2.2.0`. `registerHostIntents` is re-exported from this package so a host registers intents on the same `@x12i/ai-profiles` copy the dispatcher resolves with.
|
|
18
18
|
|
|
19
19
|
## One request
|
|
20
20
|
|
|
@@ -56,7 +56,7 @@ Use model ids that belong to the selected catalog target. An OpenRouter id is no
|
|
|
56
56
|
| `openai` | `gpt-5.4` | `reasoning.effort: "low"` on the Responses body |
|
|
57
57
|
| `cloudflare` | `openai/gpt-5.4` | `reasoning.effort: "low"` on the Cloudflare Responses body |
|
|
58
58
|
|
|
59
|
-
Representative catalog results for `@x12i/ai-profiles@5.
|
|
59
|
+
Representative catalog results for `@x12i/ai-profiles@5.1.0`:
|
|
60
60
|
|
|
61
61
|
- OpenRouter `~google/gemini-flash-latest` + `max` is `degraded` and sends `reasoning.effort: "high"`.
|
|
62
62
|
- OpenRouter `aion-labs/aion-2.0` is `ignored` with an empty reasoning body. The call still runs.
|
|
@@ -153,7 +153,7 @@ Text can arrive as `prompt`, `messages`, `input`, `system`, or `instructions`. A
|
|
|
153
153
|
| `cloudflare` | Per-call overrides: `endpoint`, `gatewayId`, `run`, and `cf-aig-*` controls. Stripped before compilation. |
|
|
154
154
|
| `mcp` | `false` or `[]` omits MCP tools registered at initialization. A list of exposed names attaches only those tools. Omit the field to attach every registered MCP tool. |
|
|
155
155
|
|
|
156
|
-
Set `metadata` for the pairs you want back from a log. `agentId`, `orgId`, `stepId`, and `skillId` are fields on the same record. A dedicated field wins over the same key inside `metadata`. Omit an id you do not have. The dispatcher does not invent one.
|
|
156
|
+
Set `metadata` for the pairs you want back from a log. `agentId`, `orgId`, `stepId`, and `skillId` are fields on the same record. A `role` object expands to `role`, `roleLayer`, `roleDigest`, and `profileChoice` on that record. A dedicated field wins over the same key inside `metadata`. Omit an id you do not have. The dispatcher does not invent one.
|
|
157
157
|
|
|
158
158
|
```ts
|
|
159
159
|
import { decodeProviderMetadata } from "@x12i/ai-dispatcher";
|
|
@@ -177,7 +177,9 @@ 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. 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.
|
|
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`, `role`, `roleLayer`, `roleDigest`, `profileChoice`, `recordId`, and `objectType`. A trimmed call adds a `METADATA_RECORD_TRIMMED` warning.
|
|
181
|
+
|
|
182
|
+
Repeating vendor fields normalize to one key. Prefer `metadata.api_key_name` over `metadata.openrouter.api_key_name`. `normalizeMetadata` and `decodeProviderMetadata` promote known aliases and keep the vendor key. Grow `METADATA_NORMALIZATION_MAP` as more shared fields appear.
|
|
181
183
|
|
|
182
184
|
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
185
|
|
|
@@ -241,7 +243,7 @@ Text blocks in an MCP result are joined and returned to the model. `isError: tru
|
|
|
241
243
|
|
|
242
244
|
## Reasoning effort
|
|
243
245
|
|
|
244
|
-
`reasoningEffort` is `minimal | low | medium | high | max`.
|
|
246
|
+
`reasoningEffort` is `minimal | low | medium | high | max`. `extra-high` is rejected with `INVALID_EFFORT`. It is not stored as `max`.
|
|
245
247
|
|
|
246
248
|
| Request | Catalog |
|
|
247
249
|
| --- | --- |
|
|
@@ -294,7 +296,7 @@ The rest of the response is the runtime shape: `id`, `status` (`completed`, `fai
|
|
|
294
296
|
|
|
295
297
|
Streaming emits `stream.reasoning.delta` for thought text. That text is not copied into `stream.text.delta`, tool-argument deltas, or structured-output text. If a future catalog asks for think-tag stripping, tags are split out of answer deltas before those deltas are yielded.
|
|
296
298
|
|
|
297
|
-
The dispatcher does not store conversation history. When a directive lists history fields to drop, callers pass those paths to `stripReasoningHistoryFields(message, paths)`. The helper clones the message, deletes only those pointers, and is safe to call twice. Catalog 5.
|
|
299
|
+
The dispatcher does not store conversation history. When a directive lists history fields to drop, callers pass those paths to `stripReasoningHistoryFields(message, paths)`. The helper clones the message, deletes only those pointers, and is safe to call twice. Catalog 5.1.0 paths are empty, so a real directive deletes nothing.
|
|
298
300
|
|
|
299
301
|
## Streaming
|
|
300
302
|
|
|
@@ -482,11 +484,11 @@ Pass `logger` on the dispatcher. `run` and `executeStreamingChat` emit `ai-dispa
|
|
|
482
484
|
|
|
483
485
|
## Limitations
|
|
484
486
|
|
|
485
|
-
- Catalog 5.
|
|
487
|
+
- Catalog 5.1.0 does not currently emit a prompt prefix, a replacement model, think-tag stripping, history deletions, or a reasoning token budget. Those paths exist for the directive type and are covered by synthetic tests.
|
|
486
488
|
- Prompt caching is a dispatcher profile, not an `@x12i/ai-profiles` field. It writes one breakpoint after `system` or `instructions`. It does not place extra breakpoints on tools or on a prefix that changes in the middle of the call.
|
|
487
489
|
- Provenance notes about output-token headroom are not enforced, because the resolver does not return them as fields.
|
|
488
490
|
- OpenRouter server tools, `apiMode: "chat"`, and `rawOpenRouterOverrides` stay on OpenRouter. Bedrock, direct OpenAI, and Cloudflare reject the server-tool fields and `rawOpenRouterOverrides` keys other than `metadata`. Cloudflare accepts `apiMode: "chat"` as the chat-completions endpoint.
|
|
489
491
|
- Direct OpenAI and Cloudflare streaming do not run the function-tool loop. MCP tools are not attached to `executeStreamingChat` on any provider. Cloudflare `/ai/run` does not stream.
|
|
490
492
|
- OpenRouter advisor, subagent, and fusion cannot call MCP sessions registered on the dispatcher.
|
|
491
493
|
- Bedrock `run()` returns a failed response for `UNSUPPORTED_CONTENT`. `compile()` throws that error.
|
|
492
|
-
- Live provider calls are not part of the package test suite. Tests mock HTTP and the Bedrock client and use the real 5.
|
|
494
|
+
- Live provider calls are not part of the package test suite. Tests mock HTTP and the Bedrock client and use the real 5.1.0 catalog for contract cases.
|
package/dist/index.cjs
CHANGED
|
@@ -23,14 +23,19 @@ __export(index_exports, {
|
|
|
23
23
|
AiDispatcherError: () => AiDispatcherError,
|
|
24
24
|
DEFAULT_METADATA_PRIORITY: () => DEFAULT_METADATA_PRIORITY,
|
|
25
25
|
IMPLEMENTED_AI_PROVIDERS: () => IMPLEMENTED_AI_PROVIDERS,
|
|
26
|
+
METADATA_NORMALIZATION_MAP: () => import_openrouter_runtime4.METADATA_NORMALIZATION_MAP,
|
|
27
|
+
canonicalMetadataKey: () => import_openrouter_runtime4.canonicalMetadataKey,
|
|
26
28
|
createAiDispatcher: () => createAiDispatcher,
|
|
27
29
|
decodeProviderMetadata: () => import_openrouter_runtime4.decodeProviderMetadata,
|
|
28
30
|
isImplementedAiProvider: () => isImplementedAiProvider,
|
|
31
|
+
normalizeMetadata: () => import_openrouter_runtime4.normalizeMetadata,
|
|
29
32
|
providerNotImplemented: () => providerNotImplemented,
|
|
33
|
+
registerHostIntents: () => import_ai_profiles2.registerHostIntents,
|
|
30
34
|
resolveProvider: () => resolveProvider,
|
|
31
35
|
stripReasoningHistoryFields: () => stripReasoningHistoryFields
|
|
32
36
|
});
|
|
33
37
|
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
var import_ai_profiles2 = require("@x12i/ai-profiles");
|
|
34
39
|
var import_openrouter_runtime4 = require("@x12i/openrouter-runtime");
|
|
35
40
|
|
|
36
41
|
// src/create-dispatcher.ts
|
|
@@ -2349,6 +2354,7 @@ function isRecord5(value) {
|
|
|
2349
2354
|
// src/prepare-request.ts
|
|
2350
2355
|
function prepareDispatchRequest(params) {
|
|
2351
2356
|
const cloned = cloneRequest(params.request);
|
|
2357
|
+
applyRoleStamp(cloned);
|
|
2352
2358
|
applyDispatchIdentity(cloned);
|
|
2353
2359
|
absorbOverrideMetadata(cloned);
|
|
2354
2360
|
const provider = params.provider;
|
|
@@ -2656,6 +2662,23 @@ function defaultModelFor(provider, options) {
|
|
|
2656
2662
|
return options.openai?.defaultModel;
|
|
2657
2663
|
}
|
|
2658
2664
|
var DISPATCH_IDENTITY_FIELDS = ["orgId", "stepId", "skillId"];
|
|
2665
|
+
var ROLE_STAMP_FIELDS = ["role", "roleLayer", "roleDigest", "profileChoice"];
|
|
2666
|
+
function applyRoleStamp(request) {
|
|
2667
|
+
const stamp = request.role;
|
|
2668
|
+
delete request.role;
|
|
2669
|
+
if (!isRecord6(stamp)) return;
|
|
2670
|
+
const identity = {};
|
|
2671
|
+
for (const field of ROLE_STAMP_FIELDS) {
|
|
2672
|
+
const value = stamp[field];
|
|
2673
|
+
if (typeof value !== "string") continue;
|
|
2674
|
+
const trimmed = value.trim();
|
|
2675
|
+
if (trimmed) identity[field] = trimmed;
|
|
2676
|
+
}
|
|
2677
|
+
if (!Object.keys(identity).length) return;
|
|
2678
|
+
const rest = { ...request.metadata ?? {} };
|
|
2679
|
+
for (const key of Object.keys(identity)) delete rest[key];
|
|
2680
|
+
request.metadata = { ...identity, ...rest };
|
|
2681
|
+
}
|
|
2659
2682
|
function absorbOverrideMetadata(request) {
|
|
2660
2683
|
const overrides = request.rawOpenRouterOverrides;
|
|
2661
2684
|
if (!isRecord6(overrides) || !Object.prototype.hasOwnProperty.call(overrides, "metadata")) return;
|
|
@@ -3525,7 +3548,18 @@ function resolveProvider(requestProvider, defaultProvider) {
|
|
|
3525
3548
|
|
|
3526
3549
|
// src/create-dispatcher.ts
|
|
3527
3550
|
var SECRET_HEADERS = /* @__PURE__ */ new Set(["authorization", "x-api-key", "api-key"]);
|
|
3528
|
-
var DEFAULT_METADATA_PRIORITY = [
|
|
3551
|
+
var DEFAULT_METADATA_PRIORITY = [
|
|
3552
|
+
"agentId",
|
|
3553
|
+
"orgId",
|
|
3554
|
+
"stepId",
|
|
3555
|
+
"skillId",
|
|
3556
|
+
"role",
|
|
3557
|
+
"roleLayer",
|
|
3558
|
+
"roleDigest",
|
|
3559
|
+
"profileChoice",
|
|
3560
|
+
"recordId",
|
|
3561
|
+
"objectType"
|
|
3562
|
+
];
|
|
3529
3563
|
function createAiDispatcher(options = {}) {
|
|
3530
3564
|
const packer = (0, import_provider_metadata.createMetadataPacker)({ priority: options.metadataPriority ?? DEFAULT_METADATA_PRIORITY });
|
|
3531
3565
|
const defaultProvider = resolveProvider(options.provider, "openrouter");
|
|
@@ -3684,10 +3718,14 @@ function stripReasoningHistoryFields(message, paths) {
|
|
|
3684
3718
|
AiDispatcherError,
|
|
3685
3719
|
DEFAULT_METADATA_PRIORITY,
|
|
3686
3720
|
IMPLEMENTED_AI_PROVIDERS,
|
|
3721
|
+
METADATA_NORMALIZATION_MAP,
|
|
3722
|
+
canonicalMetadataKey,
|
|
3687
3723
|
createAiDispatcher,
|
|
3688
3724
|
decodeProviderMetadata,
|
|
3689
3725
|
isImplementedAiProvider,
|
|
3726
|
+
normalizeMetadata,
|
|
3690
3727
|
providerNotImplemented,
|
|
3728
|
+
registerHostIntents,
|
|
3691
3729
|
resolveProvider,
|
|
3692
3730
|
stripReasoningHistoryFields
|
|
3693
3731
|
});
|