@x12i/ai-dispatcher 2.3.0 → 2.3.2

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 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` 2.3.0. It runs on Node 20 or newer and publishes ESM and CommonJS from the same entry.
7
+ The package is `@x12i/ai-dispatcher` 2.3.1. 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
 
@@ -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. 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.
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`. Pass the Cloudflare `cf-aig-metadata` header as that same object or as the raw JSON string. 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
181
 
182
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.
183
183
 
@@ -446,6 +446,17 @@ await dispatcher.run({
446
446
 
447
447
  Cloudflare supports the same `run`, `compile`, and `executeStreamingChat` entrypoints. `executeStreamingChat` rejects `/ai/run`. `reasoningEffort` uses the OpenAI or OpenRouter catalog, as described above. `cache` sets AI Gateway headers and, for GPT on Responses and Claude on Messages, the native breakpoint. Local function tools and MCP tools follow the OpenAI loop. OpenRouter server tools are rejected. `rawOpenRouterOverrides` is rejected except for a `metadata` object, which is folded into `metadata`. `responseFormat` is `text` on Responses, `response_format` on chat, and rejected on messages and run.
448
448
 
449
+ ## Credentials
450
+
451
+ A provider is available when the dispatcher can resolve the credential it sends. `compile` and `run` throw when a required value is missing.
452
+
453
+ | Provider | Option | Environment |
454
+ | --- | --- | --- |
455
+ | OpenRouter | `openrouter.apiKey` | `OPENROUTER_API_KEY`, then `OPEN_ROUTER_KEY` |
456
+ | Bedrock | `bedrock.region` and optional `bedrock.credentials` | `AWS_REGION` or `AWS_DEFAULT_REGION`. When `credentials` is omitted, the AWS SDK default chain is used. An explicit credential is `accessKeyId`, `secretAccessKey`, and optional `sessionToken`. |
457
+ | OpenAI | `openai.apiKey` | `OPENAI_API_KEY` |
458
+ | Cloudflare | `cloudflare.apiToken` and `cloudflare.accountId` | `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` |
459
+
449
460
  ## Errors
450
461
 
451
462
  Catalog failures throw `AiDispatcherError` with `code`, `message`, and the catalog `details`. Dispatcher codes include:
@@ -466,7 +477,7 @@ Catalog failures throw `AiDispatcherError` with `code`, `message`, and the catal
466
477
  | `PROVIDER_AUTH_FAILED` | OpenAI or Cloudflare HTTP `401` or `403` |
467
478
  | `PROVIDER_MODEL_NOT_FOUND` | OpenAI or Cloudflare HTTP `404` |
468
479
  | `PROVIDER_REQUEST_FAILED` | Other OpenAI or Cloudflare HTTP failures |
469
- | `PROVIDER_RATE_LIMITED` | Cloudflare HTTP `429` after retries |
480
+ | `PROVIDER_RATE_LIMITED` | OpenAI or Cloudflare HTTP `429` after retries |
470
481
  | `PROVIDER_RETRYABLE` / `PROVIDER_GATEWAY_UNREACHABLE` | OpenAI or Cloudflare transport failures after retries, including timeout |
471
482
  | `FUNCTION_TOOL_LIMIT` | Function-tool calls exceed `maxFunctionToolCalls` |
472
483
  | `UNSUPPORTED_CONTENT` | Bedrock received an image URL or content part it cannot send |
package/dist/index.cjs CHANGED
@@ -3280,8 +3280,7 @@ async function readFailure2(response) {
3280
3280
  function classifyStatus2(status) {
3281
3281
  if (status === 401 || status === 403) return "PROVIDER_AUTH_FAILED";
3282
3282
  if (status === 404) return "PROVIDER_MODEL_NOT_FOUND";
3283
- if (status === 429) return "PROVIDER_RATE_LIMITED";
3284
- if (status === 408 || status >= 500) return "PROVIDER_RETRYABLE";
3283
+ if (status === 429 || status === 408 || status >= 500) return "PROVIDER_RETRYABLE";
3285
3284
  return "PROVIDER_REQUEST_FAILED";
3286
3285
  }
3287
3286
  function isConfigError2(code) {
@@ -3289,6 +3288,8 @@ function isConfigError2(code) {
3289
3288
  }
3290
3289
  function asTerminalError2(error) {
3291
3290
  if (error instanceof AiDispatcherError && error.code === "PROVIDER_RETRYABLE") {
3291
+ const status = isRecord7(error.details) ? error.details.status : void 0;
3292
+ if (status === 429) return new AiDispatcherError("PROVIDER_RATE_LIMITED", error.message, error.details);
3292
3293
  return new AiDispatcherError("PROVIDER_GATEWAY_UNREACHABLE", error.message, error.details);
3293
3294
  }
3294
3295
  if (error instanceof AiDispatcherError) return error;