@x12i/ai-gateway 10.0.7 → 10.2.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 +30 -13
- package/dist/activity-manager.d.ts +2 -2
- package/dist/activity-manager.js +2 -2
- package/dist/ai-tools-client.d.ts +4 -2
- package/dist/ai-tools-client.js +8 -5
- package/dist/gateway-config.d.ts +1 -1
- package/dist/gateway-config.js +5 -5
- package/dist/gateway-utils.d.ts +1 -1
- package/dist/gateway-utils.js +32 -8
- package/dist/gateway.d.ts +1 -1
- package/dist/gateway.js +4 -4
- package/dist/index.d.ts +4 -3
- package/dist/index.js +4 -3
- package/dist/instruction-errors.d.ts +5 -0
- package/dist/instruction-errors.js +10 -0
- package/dist/invoke-model-ingress.d.ts +17 -0
- package/dist/invoke-model-ingress.js +56 -0
- package/dist/openrouter-routing.d.ts +11 -3
- package/dist/openrouter-routing.js +18 -5
- package/dist-cjs/activity-manager.cjs +2 -2
- package/dist-cjs/activity-manager.d.ts +2 -2
- package/dist-cjs/ai-tools-client.cjs +8 -5
- package/dist-cjs/ai-tools-client.d.ts +4 -2
- package/dist-cjs/gateway-config.cjs +5 -5
- package/dist-cjs/gateway-config.d.ts +1 -1
- package/dist-cjs/gateway-utils.cjs +32 -8
- package/dist-cjs/gateway-utils.d.ts +1 -1
- package/dist-cjs/gateway.cjs +4 -4
- package/dist-cjs/gateway.d.ts +1 -1
- package/dist-cjs/index.cjs +4 -3
- package/dist-cjs/index.d.ts +4 -3
- package/dist-cjs/instruction-errors.cjs +10 -0
- package/dist-cjs/instruction-errors.d.ts +5 -0
- package/dist-cjs/invoke-model-ingress.cjs +56 -0
- package/dist-cjs/invoke-model-ingress.d.ts +17 -0
- package/dist-cjs/openrouter-routing.cjs +18 -5
- package/dist-cjs/openrouter-routing.d.ts +11 -3
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @x12i/ai-gateway
|
|
2
2
|
|
|
3
|
-
Unified gateway for LLM provider routing, structured logging, optional Activix activity persistence, and cost/model resolution via **@x12i/ai-tools**
|
|
3
|
+
Unified gateway for LLM provider routing, structured logging, optional Activix activity persistence, and cost/model resolution via **@x12i/ai-tools** v3. Built on **@x12i/ai-providers-router**, **@x12i/logxer**, **@x12i/rendrix** (templates), and **@x12i/flex-md** (output-format hints and max-token lookup).
|
|
4
4
|
|
|
5
5
|
## What this package does
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ Unified gateway for LLM provider routing, structured logging, optional Activix a
|
|
|
13
13
|
| **Activix** | Optional Mongo-backed activity rows; billing written from gateway-computed slice on **`completeRecord`** (`outer.cost` + root fields). No Activix **`autoCost`** re-pricing. |
|
|
14
14
|
| **Trace mode** | `diagnostics.mode === 'trace'` adds `metadata.attempts[]`, `metadata.usage`, and per-attempt **`costUsd`** / **`costStatus`**. |
|
|
15
15
|
|
|
16
|
-
Pinned dependency versions are in `package.json` (currently **Activix ^8.
|
|
16
|
+
Pinned dependency versions are in `package.json` (currently **Activix ^8.6**, **ai-tools ^3.1**, **ai-profiles ^3.2**, **ai-providers-router ^4.9**).
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
@@ -87,7 +87,7 @@ console.log(response.content, response.metadata?.costUsd, response.metadata?.tok
|
|
|
87
87
|
|
|
88
88
|
### Providers without manual `register()`
|
|
89
89
|
|
|
90
|
-
- **OpenRouter:** Set **`OPENROUTER_API_KEY`** in `.env`. The gateway always passes this key to the router when set. **By default, OpenRouter is preferred** for routing (including when you also have direct keys such as `OPENAI_API_KEY`). **`@x12i/ai-tools`** resolves concrete model ids + provider via `resolveInvokeModel()` (catalog normalization, OpenRouter vs direct transport, router proxy flags). Pass catalog model ids such as `openai/gpt-4o-mini` or `
|
|
90
|
+
- **OpenRouter:** Set **`OPENROUTER_API_KEY`** in `.env`. The gateway always passes this key to the router when set. **By default, OpenRouter is preferred** for routing (including when you also have direct keys such as `OPENAI_API_KEY`). **`@x12i/ai-tools`** resolves concrete model ids + provider via `resolveInvokeModel()` (catalog normalization, OpenRouter vs direct transport, router proxy flags). Pass catalog model ids such as `openai/gpt-4o-mini` or `{ provider: 'openrouter', model: 'deepseek/deepseek-v4-pro' }`. Composite display slugs like `openrouter/deepseek/deepseek-v4-pro` are split at ingress (see **Invoke model ingress** below). Profile/choice aliases (`cheap/default`, …) must be resolved upstream (ai-tasks / `resolveAIProfile`) — the gateway rejects them with **`GATEWAY_ALIAS_MODEL_REJECTED`**.
|
|
91
91
|
- **`USE_OPENROUTER=false`:** Do **not** prefer OpenRouter when a direct provider API key exists — use the direct provider instead. OpenRouter is **still** used as **fallback** when the request targets a provider without a direct key (e.g. `anthropic` without `ANTHROPIC_API_KEY`). It does not disable OpenRouter while `OPENROUTER_API_KEY` is set.
|
|
92
92
|
- **Direct providers:** Set `OPENAI_API_KEY`, `GROK_API_KEY`, etc. Registered lazily on first invoke.
|
|
93
93
|
|
|
@@ -146,7 +146,7 @@ import {
|
|
|
146
146
|
} from '@x12i/ai-gateway';
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
**Required on every invoke:** `config.model` (or `modelConfig.model`) and `maxTokens` (`request.config`, `modelConfig`, `GatewayConfig`, or `internalSystemActions`). Missing model → `ModelRequiredError` (`code: 'MODEL_REQUIRED'`). Missing `maxTokens` → `MaxTokensRequiredError` (`code: 'MAX_TOKENS_REQUIRED'`). There is **no** packaged default model, **no** flex-md / Optimixer auto-fill, and **no** `GATEWAY_DEFAULT_MAX_TOKENS`. Use [@x12i/optimixer](https://www.npmjs.com/package/@x12i/optimixer) in the **client** that wraps this gateway if you want adaptive completion budgets.
|
|
149
|
+
**Required on every invoke:** `config.model` (or `modelConfig.model`) and `maxTokens` (`request.config`, `modelConfig`, `GatewayConfig`, or `internalSystemActions`). Missing model → `ModelRequiredError` (`code: 'MODEL_REQUIRED'`). Missing `maxTokens` → `MaxTokensRequiredError` (`code: 'MAX_TOKENS_REQUIRED'`). Profile/choice alias at invoke → `GatewayAliasModelRejectedError` (`code: 'GATEWAY_ALIAS_MODEL_REJECTED'`). There is **no** packaged default model, **no** flex-md / Optimixer auto-fill, and **no** `GATEWAY_DEFAULT_MAX_TOKENS`. Use [@x12i/optimixer](https://www.npmjs.com/package/@x12i/optimixer) in the **client** that wraps this gateway if you want adaptive completion budgets.
|
|
150
150
|
|
|
151
151
|
**Rate limiting:** removed from the gateway. See [upstream rate-limit spec](./docs/upstream-reports/AI_PROVIDER_ROUTER_RATE_LIMITING.md) — implement in `@x12i/ai-providers-router`.
|
|
152
152
|
|
|
@@ -184,7 +184,8 @@ Instructions must be **complete caller text** — the gateway no longer injects
|
|
|
184
184
|
|
|
185
185
|
| Variable | Role |
|
|
186
186
|
|----------|------|
|
|
187
|
-
| `MONGO_URI`, `MONGO_LOGS_DB` / `MONGO_DB` | Activix when no custom tracker |
|
|
187
|
+
| `MONGO_URI`, `MONGO_LOGS_DB` / `MONGO_DB` / `MONGO_AI_LOGS_DB` | Activix when no custom tracker |
|
|
188
|
+
| `ACTIVIX_DB_NAME` | Activix database override (falls back to `MONGO_AI_LOGS_DB` → `MONGO_LOGS_DB` → `MONGO_DB`) |
|
|
188
189
|
| `mode` / `MODE` | Operational mode (`dev`, `debug`, `prod`) — expose to downstream clients |
|
|
189
190
|
| `AI_GATEWAY_LOGS_LEVEL` | Log threshold for gateway diagnostics (`AI_GATEWAY` prefix): `error` … `verbose` |
|
|
190
191
|
| `AI_GATEWAY_VERBOSE` | Full payload lines (still requires `AI_GATEWAY_LOGS_LEVEL=verbose`) |
|
|
@@ -209,7 +210,21 @@ Exports: `GATEWAY_LOGXER_PACKAGE`, `GATEWAY_LOG_ENV_PREFIX`, `createGatewayLogge
|
|
|
209
210
|
|
|
210
211
|
---
|
|
211
212
|
|
|
212
|
-
##
|
|
213
|
+
## Invoke model ingress (v10.2+)
|
|
214
|
+
|
|
215
|
+
Before catalog lookup, **`mergeConfig()`** normalizes the invoke wire shape via **`normalizeInvokeModel`** from **`@x12i/ai-profiles`** (defense in depth — idempotent when upstream already fixed):
|
|
216
|
+
|
|
217
|
+
| Input | Result |
|
|
218
|
+
|-------|--------|
|
|
219
|
+
| `model: 'openrouter/deepseek/deepseek-v4-pro'`, `provider: 'unspecified'` | `{ provider: 'openrouter', model: 'deepseek/deepseek-v4-pro' }` → catalog lookup |
|
|
220
|
+
| `{ provider: 'openrouter', model: 'deepseek/deepseek-v4-pro' }` | Pass-through unchanged |
|
|
221
|
+
| `model: 'cheap/default'` (profile/choice alias) | **`GATEWAY_ALIAS_MODEL_REJECTED`** — resolve upstream via ai-tasks / `resolveAIProfile` |
|
|
222
|
+
|
|
223
|
+
Exported helpers: `normalizeInvokeModelAtIngress`, `GatewayAliasModelRejectedError`.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## @x12i/ai-tools v3 (models + cost)
|
|
213
228
|
|
|
214
229
|
Engine-owned catalog bootstrap and post-call billing. Consumers read **`metadata.costUsd`** / **`costStatus`** only — no direct `@x12i/ai-tools` dependency for cost.
|
|
215
230
|
|
|
@@ -233,7 +248,7 @@ Implemented in **`resolveCostCompletionWithAiTools`** only ( **`CostCalculator.c
|
|
|
233
248
|
| **`enabled`** | `true` | Bootstrap **`AiModelsCatalogClient`** + **`CostCalculator`** |
|
|
234
249
|
| **`calculateCost`** | `true` | Run post-call catalog pricing when router did not price |
|
|
235
250
|
| **`resolveModels`** | `true` | **`mergeConfig()`** → **`resolveInvokeModel()`** |
|
|
236
|
-
| **`modelsOnly`** | `true` | Reject profile shortcuts (`cheapest`,
|
|
251
|
+
| **`modelsOnly`** | `true` | Reject profile shortcuts at catalog resolution (`cheapest`, …). Aliases are **always** rejected at ingress regardless of this flag. |
|
|
237
252
|
| **`bundledOnly`** | `false` | Offline bundled catalogs only |
|
|
238
253
|
| **`costIncludeBreakdown`** | `false` | Include prompt/completion breakdown on priced results |
|
|
239
254
|
| **`catalogLane`** | `"text"` (ai-tools default) | Catalog lane for resolution + cost lookup (`text`, `image`, …) |
|
|
@@ -241,13 +256,13 @@ Implemented in **`resolveCostCompletionWithAiTools`** only ( **`CostCalculator.c
|
|
|
241
256
|
|
|
242
257
|
- **No Catalox / Firestore** — catalogs come from ai-tools open-assets JSON (optional **`bundledOnly`**).
|
|
243
258
|
|
|
244
|
-
Gateway exports the model orchestrator from `@x12i/ai-tools` ≥ **
|
|
259
|
+
Gateway exports the model orchestrator from `@x12i/ai-tools` ≥ **3.0.0** (`resolveInvokeModel`, `useOpenRouter`, …). Profile/choice keys (`cheap/default`, …) must be resolved **before** invoke — the gateway does not run alias resolution. Shortcuts like `cheapest` / bare `cheap` are rejected at ingress or catalog resolution.
|
|
245
260
|
|
|
246
261
|
Gateway billing helpers (exported for tests/integrators): `resolveCostCompletionWithAiTools`, `buildGatewayPricingRecord`, `catalogPricingSucceeded`, `buildTraceUsageSummary`, `enrichTraceAttemptsWithBilling`.
|
|
247
262
|
|
|
248
263
|
---
|
|
249
264
|
|
|
250
|
-
## Activity tracking (@x12i/activix 8.
|
|
265
|
+
## Activity tracking (@x12i/activix 8.6)
|
|
251
266
|
|
|
252
267
|
When tracking is enabled and no custom tracker is supplied, the gateway constructs Activix with fixed collection names (see `src/config/activity-tracking-config.ts`):
|
|
253
268
|
|
|
@@ -261,12 +276,14 @@ When tracking is enabled and no custom tracker is supplied, the gateway construc
|
|
|
261
276
|
|
|
262
277
|
**Successful completion** (no duplicate billing on `outer.metadata`):
|
|
263
278
|
|
|
264
|
-
- Root: `cost`, `costUsd`, `costStatus`, **`metadata`** (routing + billing mirror for
|
|
279
|
+
- Root: `cost`, `costUsd`, `costStatus`, **`metadata`** (routing + billing mirror; **`metadata.modelUsed`** for studio)
|
|
265
280
|
- `outer.metadata`: routing only (`modelUsed`, `provider`, …)
|
|
266
281
|
- `outer.cost`: Activix cost shape (`usd`, `tokens`, `provider`, `model`, `details`)
|
|
267
282
|
- `response.metadata`: same billing slice as returned to callers
|
|
268
283
|
|
|
269
|
-
Gateway resolves billing **before** `completeRecord` and sets **`outer.cost`** from that slice.
|
|
284
|
+
Activix **8.6** runs **`materializeRecordRoutingAndBilling`** on persist (root **`config`** mirror from routing metadata). Gateway resolves billing **before** `completeRecord` and sets **`outer.cost`** from that slice.
|
|
285
|
+
|
|
286
|
+
**`autoCost`:** Activix 8.6 default is **`false`**. The gateway keeps **`autoCost: false`** on the default activity manager so billing is not recomputed via ai-tools at persist time (no second pricing path). Custom **`activityTracker`** instances may opt in to Activix **`autoCost`** (uses **@x12i/ai-tools** v3 **`calculateFromRecord`** when enabled).
|
|
270
287
|
|
|
271
288
|
Mongo env: `MONGO_URI` + `MONGO_LOGS_DB` or `MONGO_DB`.
|
|
272
289
|
|
|
@@ -314,7 +331,7 @@ Adds **`metadata.attempts`**, **`metadata.usage`**, **`metadata.requestIds`**, a
|
|
|
314
331
|
|
|
315
332
|
Set via constructor `mode` or env `mode` / `MODE`. **Downstream hosts should document and expose `mode`** so graph/skill callers know resolution behavior.
|
|
316
333
|
|
|
317
|
-
Every mode requires an explicit **`model`** on the request (concrete catalog id
|
|
334
|
+
Every mode requires an explicit **`model`** on the request (concrete catalog id or normalized OpenRouter slug). Unknown models throw `ModelResolutionError`. Profile/choice aliases throw `GatewayAliasModelRejectedError` at ingress — resolve them upstream (ai-tasks).
|
|
318
335
|
|
|
319
336
|
---
|
|
320
337
|
|
|
@@ -329,7 +346,7 @@ Every mode requires an explicit **`model`** on the request (concrete catalog id
|
|
|
329
346
|
| `npm run test:flex-md-esm-regression` | ESM build regression for flex-md |
|
|
330
347
|
| `npm run test:prepublish` | `build` + `npm test` |
|
|
331
348
|
|
|
332
|
-
Live tests use `LIVE_TEST_PROVIDER` / `LIVE_TEST_MODEL` (default `openrouter` + `openai/gpt-4o-mini`). Set `
|
|
349
|
+
Live tests use `LIVE_TEST_PROVIDER` / `LIVE_TEST_MODEL` (default `openrouter` + `openai/gpt-4o-mini`). Set `LIVE_SKIP_INVOKE=1` to skip the LLM call. Profile alias invokes (`LIVE_TEST_PROFILES=1`) are **no longer supported** — resolve profiles upstream (ai-tasks) before calling the gateway.
|
|
333
350
|
|
|
334
351
|
---
|
|
335
352
|
|
|
@@ -32,8 +32,8 @@ export interface ActivityManagerConfig {
|
|
|
32
32
|
customTracker?: Activix;
|
|
33
33
|
logger: Logxer;
|
|
34
34
|
/**
|
|
35
|
-
* Activix
|
|
36
|
-
*
|
|
35
|
+
* Activix 8.6+ {@link ActivixAutoCostOptions}: optional persist-time cost via @x12i/ai-tools v3.
|
|
36
|
+
* Default gateway manager leaves this off — billing is set before completeRecord.
|
|
37
37
|
*/
|
|
38
38
|
autoCost?: boolean | ActivixAutoCostOptions;
|
|
39
39
|
}
|
package/dist/activity-manager.js
CHANGED
|
@@ -165,7 +165,7 @@ function pickActivixUsageTokens(response) {
|
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
167
|
/**
|
|
168
|
-
* Activix
|
|
168
|
+
* Activix 8.x `outer.cost` from gateway billing + routing (Run Analysis G8).
|
|
169
169
|
* Uses Activix {@link normalizeToActivixCostShape} so the shape matches package validators.
|
|
170
170
|
*/
|
|
171
171
|
function buildActivixOuterCost(routingMeta, billing, response) {
|
|
@@ -193,7 +193,7 @@ function buildActivixOuterCost(routingMeta, billing, response) {
|
|
|
193
193
|
};
|
|
194
194
|
return normalizeToActivixCostShape(candidate, 'gateway.outer.cost') ?? undefined;
|
|
195
195
|
}
|
|
196
|
-
/** Run-level record metadata (Activix
|
|
196
|
+
/** Run-level record metadata (Activix 8.x top-level `metadata`, sibling to `outer`). */
|
|
197
197
|
function buildActivixRecordMetadata(response, billing) {
|
|
198
198
|
const out = {
|
|
199
199
|
...pickActivixCompletionRoutingMetadata(response)
|
|
@@ -6,8 +6,10 @@ import { type AiToolsInvokeClient, type ModelResolutionSuccess, type OpenRouterR
|
|
|
6
6
|
import type { Logxer } from '@x12i/logxer';
|
|
7
7
|
import type { GatewayConfig } from './types.js';
|
|
8
8
|
export type AiToolsClientBundle = AiToolsInvokeClient;
|
|
9
|
-
export { resolveInvokeModel,
|
|
10
|
-
|
|
9
|
+
export { resolveInvokeModel, applyOpenRouterInvokeRouting, buildInvokeModelResolverOptions, enrichModelResolutionError, mapResolutionToRouterConfig, ModelProfileUnroutableError, ModelProfileInputRejectedError, MODEL_PROFILE_UNROUTABLE, getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, createAiToolsInvokeClient, } from '@x12i/ai-tools';
|
|
10
|
+
/** @deprecated Use `applyOpenRouterInvokeRouting` (ai-tools v3). */
|
|
11
|
+
export { applyOpenRouterInvokeRouting as applyOpenRouterInvokePolicy } from '@x12i/ai-tools';
|
|
12
|
+
export { resolveOpenRouterApiKey, resolveUseOpenRouter, resolvePreferOpenRouter, readUseOpenRouterFromEnv, readPreferOpenRouterFromEnv, } from './openrouter-routing.js';
|
|
11
13
|
export type { InvokeModelResolutionDiagnostics, InvokeModelResolutionInput, InvokeModelResolutionOptions, InvokeModelResolutionResult, InvokeRouterConfigSlice, AiToolsInvokeClient, } from '@x12i/ai-tools';
|
|
12
14
|
/** @deprecated Use buildInvokeModelResolverOptions */
|
|
13
15
|
export declare function buildModelResolverOptions(config: GatewayConfig, routingEnv?: OpenRouterRoutingConfig): import("@x12i/ai-tools").ModelResolverOptions;
|
package/dist/ai-tools-client.js
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, CostCalculator, } from '@x12i/ai-tools';
|
|
6
6
|
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
7
|
-
import {
|
|
8
|
-
export { resolveInvokeModel,
|
|
9
|
-
|
|
7
|
+
import { resolveUseOpenRouter } from './openrouter-routing.js';
|
|
8
|
+
export { resolveInvokeModel, applyOpenRouterInvokeRouting, buildInvokeModelResolverOptions, enrichModelResolutionError, mapResolutionToRouterConfig, ModelProfileUnroutableError, ModelProfileInputRejectedError, MODEL_PROFILE_UNROUTABLE, getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, createAiToolsInvokeClient, } from '@x12i/ai-tools';
|
|
9
|
+
/** @deprecated Use `applyOpenRouterInvokeRouting` (ai-tools v3). */
|
|
10
|
+
export { applyOpenRouterInvokeRouting as applyOpenRouterInvokePolicy } from '@x12i/ai-tools';
|
|
11
|
+
export { resolveOpenRouterApiKey, resolveUseOpenRouter, resolvePreferOpenRouter, readUseOpenRouterFromEnv, readPreferOpenRouterFromEnv, } from './openrouter-routing.js';
|
|
10
12
|
let bootstrapFailedLogged = false;
|
|
11
13
|
function invokeClientOptions(config) {
|
|
12
14
|
return {
|
|
@@ -26,7 +28,8 @@ function withCatalogLaneCalculator(client, config) {
|
|
|
26
28
|
...(config.aiTools?.costIncludeBreakdown ? { includeBreakdown: true } : {}),
|
|
27
29
|
resolverOptions: buildInvokeModelResolverOptions({
|
|
28
30
|
routingEnv: client.routingEnv,
|
|
29
|
-
catalogLane: lane
|
|
31
|
+
catalogLane: lane,
|
|
32
|
+
useOpenRouter: resolveUseOpenRouter(config),
|
|
30
33
|
})
|
|
31
34
|
})
|
|
32
35
|
};
|
|
@@ -35,7 +38,7 @@ function withCatalogLaneCalculator(client, config) {
|
|
|
35
38
|
export function buildModelResolverOptions(config, routingEnv) {
|
|
36
39
|
return buildInvokeModelResolverOptions({
|
|
37
40
|
routingEnv,
|
|
38
|
-
|
|
41
|
+
useOpenRouter: resolveUseOpenRouter(config),
|
|
39
42
|
});
|
|
40
43
|
}
|
|
41
44
|
/**
|
package/dist/gateway-config.d.ts
CHANGED
|
@@ -37,6 +37,6 @@ export declare function initializeGatewayComponents(config: GatewayConfig): {
|
|
|
37
37
|
activityManager: ActivityManager;
|
|
38
38
|
usageTracker: UsageTracker;
|
|
39
39
|
messageBuilderConfig: MessageBuilderConfig;
|
|
40
|
-
|
|
40
|
+
useOpenRouter: boolean;
|
|
41
41
|
openRouterApiKey?: string;
|
|
42
42
|
};
|
package/dist/gateway-config.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
|
-
import { resolveOpenRouterApiKey,
|
|
8
|
+
import { resolveOpenRouterApiKey, resolveUseOpenRouter, } from './openrouter-routing.js';
|
|
9
9
|
import { LLMProviderRouter } from '@x12i/ai-providers-router';
|
|
10
10
|
import { createGatewayLogger } from './logger-factory.js';
|
|
11
11
|
import { ActivityManager } from './activity-manager.js';
|
|
@@ -154,14 +154,14 @@ export function initializeGatewayComponents(config) {
|
|
|
154
154
|
if (config.logging !== undefined)
|
|
155
155
|
routerConfig.logging = config.logging;
|
|
156
156
|
const openRouterKey = resolveOpenRouterApiKey(config);
|
|
157
|
-
const
|
|
157
|
+
const useOpenRouter = resolveUseOpenRouter(config);
|
|
158
158
|
if (openRouterKey) {
|
|
159
159
|
routerConfig.openrouter = { apiKey: openRouterKey };
|
|
160
160
|
routerConfig.openRouter = {
|
|
161
161
|
enabled: true,
|
|
162
|
-
prefer:
|
|
162
|
+
prefer: useOpenRouter,
|
|
163
163
|
};
|
|
164
|
-
if (
|
|
164
|
+
if (useOpenRouter) {
|
|
165
165
|
routerConfig.defaultMode = 'openrouter';
|
|
166
166
|
}
|
|
167
167
|
}
|
|
@@ -190,7 +190,7 @@ export function initializeGatewayComponents(config) {
|
|
|
190
190
|
activityManager,
|
|
191
191
|
usageTracker,
|
|
192
192
|
messageBuilderConfig,
|
|
193
|
-
|
|
193
|
+
useOpenRouter,
|
|
194
194
|
openRouterApiKey: openRouterKey,
|
|
195
195
|
};
|
|
196
196
|
}
|
package/dist/gateway-utils.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type MergeConfigOptions = {
|
|
|
17
17
|
catalog?: AiModelsCatalogClient | null;
|
|
18
18
|
routingEnv?: OpenRouterRoutingConfig;
|
|
19
19
|
openRouterApiKey?: string;
|
|
20
|
-
|
|
20
|
+
useOpenRouter?: boolean;
|
|
21
21
|
};
|
|
22
22
|
export { MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, } from '@x12i/ai-tools';
|
|
23
23
|
/**
|
package/dist/gateway-utils.js
CHANGED
|
@@ -7,7 +7,8 @@ import { FallbackExhaustedError } from '@x12i/ai-providers-router';
|
|
|
7
7
|
import { ModelResolutionError, ModelProfileInputRejectedError, ModelProfileUnroutableError, resolveInvokeModel, } from '@x12i/ai-tools';
|
|
8
8
|
import { extractHttpStatusCode } from './gateway-retry.js';
|
|
9
9
|
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
10
|
-
import { MaxTokensRequiredError, ModelRequiredError } from './instruction-errors.js';
|
|
10
|
+
import { MaxTokensRequiredError, ModelRequiredError, } from './instruction-errors.js';
|
|
11
|
+
import { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
|
|
11
12
|
import { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P } from './gateway-defaults.js';
|
|
12
13
|
function getPreParsedInstructions(instructions) {
|
|
13
14
|
return instructions ?? '';
|
|
@@ -93,18 +94,34 @@ export async function mergeConfig(request, config, logger, mergeOptions) {
|
|
|
93
94
|
provider: modelConfigAsConfig?.provider || request.config?.provider || internalDefaults?.engine || config.defaultEngine
|
|
94
95
|
};
|
|
95
96
|
const explicitModel = merged.model;
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
let originalProvider = merged.provider;
|
|
98
|
+
let originalModel = explicitModel;
|
|
98
99
|
if (!explicitModel) {
|
|
99
100
|
throw new ModelRequiredError();
|
|
100
101
|
}
|
|
102
|
+
const ingress = normalizeInvokeModelAtIngress(merged.provider, explicitModel, {
|
|
103
|
+
useOpenRouter: mergeOptions?.useOpenRouter,
|
|
104
|
+
});
|
|
105
|
+
if (ingress.changed) {
|
|
106
|
+
logger.verbose('Invoke model normalized at ingress', {
|
|
107
|
+
jobId: request.identity.jobId,
|
|
108
|
+
originalProvider,
|
|
109
|
+
originalModel,
|
|
110
|
+
provider: ingress.provider,
|
|
111
|
+
model: ingress.model,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
merged.provider = ingress.provider;
|
|
115
|
+
merged.model = ingress.model;
|
|
116
|
+
originalProvider = merged.provider;
|
|
117
|
+
originalModel = merged.model;
|
|
101
118
|
if (resolveModels && mergeOptions?.catalog) {
|
|
102
119
|
try {
|
|
103
|
-
const resolved = await resolveInvokeModel({ provider: merged.provider, model:
|
|
120
|
+
const resolved = await resolveInvokeModel({ provider: merged.provider, model: merged.model }, {
|
|
104
121
|
catalog: mergeOptions.catalog,
|
|
105
122
|
routingEnv: mergeOptions.routingEnv,
|
|
106
123
|
openRouterApiKey: mergeOptions.openRouterApiKey,
|
|
107
|
-
|
|
124
|
+
useOpenRouter: mergeOptions.useOpenRouter,
|
|
108
125
|
defaultProvider: config.defaultEngine,
|
|
109
126
|
resolveModels: true,
|
|
110
127
|
modelsOnly: config.aiTools?.modelsOnly !== false,
|
|
@@ -142,11 +159,11 @@ export async function mergeConfig(request, config, logger, mergeOptions) {
|
|
|
142
159
|
}
|
|
143
160
|
}
|
|
144
161
|
else if (mergeOptions?.openRouterApiKey) {
|
|
145
|
-
const resolved = await resolveInvokeModel({ provider: merged.provider, model:
|
|
162
|
+
const resolved = await resolveInvokeModel({ provider: merged.provider, model: merged.model }, {
|
|
146
163
|
resolveModels: false,
|
|
147
164
|
routingEnv: mergeOptions.routingEnv,
|
|
148
165
|
openRouterApiKey: mergeOptions.openRouterApiKey,
|
|
149
|
-
|
|
166
|
+
useOpenRouter: mergeOptions.useOpenRouter,
|
|
150
167
|
defaultProvider: config.defaultEngine,
|
|
151
168
|
});
|
|
152
169
|
merged.provider = resolved.router.provider;
|
|
@@ -512,7 +529,14 @@ export async function resolveCostCompletionWithAiTools(routerResponse, tokens, o
|
|
|
512
529
|
options?.calculator &&
|
|
513
530
|
hasNonZeroTokenUsage(tokens)) {
|
|
514
531
|
try {
|
|
515
|
-
const record =
|
|
532
|
+
const record = routerResponse != null && typeof routerResponse === 'object'
|
|
533
|
+
? {
|
|
534
|
+
...routerResponse,
|
|
535
|
+
...(options.mergedConfig != null ? { config: options.mergedConfig } : {})
|
|
536
|
+
}
|
|
537
|
+
: options.mergedConfig != null
|
|
538
|
+
? { config: options.mergedConfig, metadata: { tokens } }
|
|
539
|
+
: { metadata: { tokens } };
|
|
516
540
|
const result = await options.calculator.calculateFromRecord(record);
|
|
517
541
|
billing = mapAiCostResultToResolvedActivityCost(billing, result);
|
|
518
542
|
}
|
package/dist/gateway.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare class AIGateway {
|
|
|
18
18
|
private messageBuilderConfig?;
|
|
19
19
|
private _autoRegisterDone;
|
|
20
20
|
private _aiToolsClient;
|
|
21
|
-
private readonly
|
|
21
|
+
private readonly useOpenRouter;
|
|
22
22
|
private readonly openRouterApiKey?;
|
|
23
23
|
constructor(config?: GatewayConfig, activityManager?: ActivityManager);
|
|
24
24
|
/**
|
package/dist/gateway.js
CHANGED
|
@@ -59,7 +59,7 @@ export class AIGateway {
|
|
|
59
59
|
messageBuilderConfig;
|
|
60
60
|
_autoRegisterDone = false;
|
|
61
61
|
_aiToolsClient = null;
|
|
62
|
-
|
|
62
|
+
useOpenRouter;
|
|
63
63
|
openRouterApiKey;
|
|
64
64
|
constructor(config = {}, activityManager) {
|
|
65
65
|
this.config = config;
|
|
@@ -69,7 +69,7 @@ export class AIGateway {
|
|
|
69
69
|
this.router = components.router;
|
|
70
70
|
this.activityManager = components.activityManager;
|
|
71
71
|
this.messageBuilderConfig = components.messageBuilderConfig;
|
|
72
|
-
this.
|
|
72
|
+
this.useOpenRouter = components.useOpenRouter;
|
|
73
73
|
this.openRouterApiKey = components.openRouterApiKey;
|
|
74
74
|
setGatewayRuntimeClients({
|
|
75
75
|
activix: this.activityManager?.getTracker(),
|
|
@@ -100,7 +100,7 @@ export class AIGateway {
|
|
|
100
100
|
catalog: aiTools?.catalog ?? null,
|
|
101
101
|
routingEnv: aiTools?.routingEnv,
|
|
102
102
|
openRouterApiKey: this.openRouterApiKey,
|
|
103
|
-
|
|
103
|
+
useOpenRouter: this.useOpenRouter,
|
|
104
104
|
});
|
|
105
105
|
// Activix start snapshot must match what the router receives (modelConfig-only callers omit request.config.model).
|
|
106
106
|
request._mergedRouterConfig = mergedConfig;
|
|
@@ -288,7 +288,7 @@ export class AIGateway {
|
|
|
288
288
|
catalog: aiTools?.catalog ?? null,
|
|
289
289
|
routingEnv: aiTools?.routingEnv,
|
|
290
290
|
openRouterApiKey: this.openRouterApiKey,
|
|
291
|
-
|
|
291
|
+
useOpenRouter: this.useOpenRouter,
|
|
292
292
|
});
|
|
293
293
|
request._mergedRouterConfig = mergedConfig;
|
|
294
294
|
logResolvedModelRouting(this.logger, request, mergedConfig);
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,8 @@ export type { RequestInterceptor, ResponseInterceptor } from '@x12i/ai-providers
|
|
|
14
14
|
export type { UsageTracker } from '@x12i/ai-providers-router';
|
|
15
15
|
export * from '@x12i/ai-providers-router';
|
|
16
16
|
export { AIGateway } from './gateway.js';
|
|
17
|
-
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError } from './instruction-errors.js';
|
|
17
|
+
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError, GatewayAliasModelRejectedError, } from './instruction-errors.js';
|
|
18
|
+
export { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
|
|
18
19
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
19
20
|
export type { GatewayConfig, ProviderModelRef, ModelConfig, RetryConfig, ChatRequest, AIInvokeRequest, AIRequest, GatewayActionType, GatewayInvokeRejectionMetadata, GatewayFallbackAttempt, GatewayTraceRequestIds, GatewayTraceAttempt, GatewayTraceUsageSummary, GatewayTraceMergedConfig, EnhancedLLMResponse, InstructionMetadata, ValidationRule, TemplateRenderOptions, SmartInputConfig, SmartInputRenderOptions } from './types.js';
|
|
20
21
|
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
@@ -40,8 +41,8 @@ export type { GatewayLoggerConfig } from './logger-factory.js';
|
|
|
40
41
|
export { GATEWAY_LOG_ENV_PREFIX, GATEWAY_LOGXER_PACKAGE, GATEWAY_STACK_LOG_PREFIXES, initializeGatewayPackageLogLevels, resetGatewayPackageLogLevelsInit } from './gateway-log-levels.js';
|
|
41
42
|
export { GatewayLogCode, gatewayErrorCode, gatewayInfoCode, gatewayWarnCode, gatewayAnomalyMeta, resolveLogDiagnosticsCatalogPath, exceptionEvidence, fieldEvidence } from './gateway-log-diagnostics.js';
|
|
42
43
|
export type { GatewayLogCode as GatewayDiagnosticCode } from './gateway-log-diagnostics.js';
|
|
43
|
-
/** Re-export @x12i/ai-tools invoke orchestrator (≥
|
|
44
|
-
export { resolveInvokeModel, applyModelResolution, applyOpenRouterInvokePolicy, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, buildModelResolverOptions, enrichModelResolutionError, getAiToolsClient, resetAiToolsClientForTests, resolveOpenRouterApiKey, resolvePreferOpenRouter, readPreferOpenRouterFromEnv, getAiToolsInvokeClient, createAiToolsInvokeClient, } from './ai-tools-client.js';
|
|
44
|
+
/** Re-export @x12i/ai-tools invoke orchestrator (≥ 3.0.0) for engine callers. */
|
|
45
|
+
export { resolveInvokeModel, applyModelResolution, applyOpenRouterInvokeRouting, applyOpenRouterInvokePolicy, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, buildModelResolverOptions, enrichModelResolutionError, getAiToolsClient, resetAiToolsClientForTests, resolveOpenRouterApiKey, resolveUseOpenRouter, resolvePreferOpenRouter, readUseOpenRouterFromEnv, readPreferOpenRouterFromEnv, getAiToolsInvokeClient, createAiToolsInvokeClient, } from './ai-tools-client.js';
|
|
45
46
|
export type { AiToolsClientBundle, AiToolsInvokeClient, InvokeModelResolutionDiagnostics, InvokeModelResolutionInput, InvokeModelResolutionOptions, InvokeModelResolutionResult, InvokeRouterConfigSlice, } from './ai-tools-client.js';
|
|
46
47
|
export { createLogxer, DebugLogAbstract, runWithLogContext, getStationRuntimeIdentity, mergeRuntimeIdentity, conditionEvidence, sourceEvidence, logReferenceEvidence, readAgentLoggingInstructions, resolveAgentLoggingInstructionsPath, applyPackageLogLevelsFromEnv, configurePackageLogLevels, mergePackageLogLevelsConfig, setPackageLogLevel, resolveStackLogLevelForPrefix, resolvePackageLogsLevel, parseLogxerPackageLevelsEnv, LOGXER_PACKAGE_LEVELS_ENV, LOGXER_PACKAGE_LOGS_DEFAULT_ENV } from '@x12i/logxer';
|
|
47
48
|
export { ROUTER_LOG_ENV_PREFIX } from '@x12i/ai-providers-router';
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,8 @@ export { ProviderNotFoundError, FallbackExhaustedError } from '@x12i/ai-provider
|
|
|
15
15
|
export * from '@x12i/ai-providers-router';
|
|
16
16
|
// Export enhanced gateway
|
|
17
17
|
export { AIGateway } from './gateway.js';
|
|
18
|
-
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError } from './instruction-errors.js';
|
|
18
|
+
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError, GatewayAliasModelRejectedError, } from './instruction-errors.js';
|
|
19
|
+
export { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
|
|
19
20
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
20
21
|
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
21
22
|
export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
|
|
@@ -33,8 +34,8 @@ export { activityIdentityToLogContext, activityIdentityToLogMeta, withActivityId
|
|
|
33
34
|
export { createGatewayLogger, resolveGatewayVerboseEnabled } from './logger-factory.js';
|
|
34
35
|
export { GATEWAY_LOG_ENV_PREFIX, GATEWAY_LOGXER_PACKAGE, GATEWAY_STACK_LOG_PREFIXES, initializeGatewayPackageLogLevels, resetGatewayPackageLogLevelsInit } from './gateway-log-levels.js';
|
|
35
36
|
export { GatewayLogCode, gatewayErrorCode, gatewayInfoCode, gatewayWarnCode, gatewayAnomalyMeta, resolveLogDiagnosticsCatalogPath, exceptionEvidence, fieldEvidence } from './gateway-log-diagnostics.js';
|
|
36
|
-
/** Re-export @x12i/ai-tools invoke orchestrator (≥
|
|
37
|
-
export { resolveInvokeModel, applyModelResolution, applyOpenRouterInvokePolicy, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, buildModelResolverOptions, enrichModelResolutionError, getAiToolsClient, resetAiToolsClientForTests, resolveOpenRouterApiKey, resolvePreferOpenRouter, readPreferOpenRouterFromEnv, getAiToolsInvokeClient, createAiToolsInvokeClient, } from './ai-tools-client.js';
|
|
37
|
+
/** Re-export @x12i/ai-tools invoke orchestrator (≥ 3.0.0) for engine callers. */
|
|
38
|
+
export { resolveInvokeModel, applyModelResolution, applyOpenRouterInvokeRouting, applyOpenRouterInvokePolicy, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, buildModelResolverOptions, enrichModelResolutionError, getAiToolsClient, resetAiToolsClientForTests, resolveOpenRouterApiKey, resolveUseOpenRouter, resolvePreferOpenRouter, readUseOpenRouterFromEnv, readPreferOpenRouterFromEnv, getAiToolsInvokeClient, createAiToolsInvokeClient, } from './ai-tools-client.js';
|
|
38
39
|
// Re-export logging (@x12i/logxer)
|
|
39
40
|
export { createLogxer, DebugLogAbstract, runWithLogContext, getStationRuntimeIdentity, mergeRuntimeIdentity, conditionEvidence, sourceEvidence, logReferenceEvidence, readAgentLoggingInstructions, resolveAgentLoggingInstructionsPath, applyPackageLogLevelsFromEnv, configurePackageLogLevels, mergePackageLogLevelsConfig, setPackageLogLevel, resolveStackLogLevelForPrefix, resolvePackageLogsLevel, parseLogxerPackageLevelsEnv, LOGXER_PACKAGE_LEVELS_ENV, LOGXER_PACKAGE_LOGS_DEFAULT_ENV } from '@x12i/logxer';
|
|
40
41
|
export { ROUTER_LOG_ENV_PREFIX } from '@x12i/ai-providers-router';
|
|
@@ -9,6 +9,11 @@ export declare class MaxTokensRequiredError extends Error {
|
|
|
9
9
|
readonly code = "MAX_TOKENS_REQUIRED";
|
|
10
10
|
constructor(message?: string);
|
|
11
11
|
}
|
|
12
|
+
export declare class GatewayAliasModelRejectedError extends Error {
|
|
13
|
+
readonly aliasModel: string;
|
|
14
|
+
readonly code = "GATEWAY_ALIAS_MODEL_REJECTED";
|
|
15
|
+
constructor(aliasModel: string, message?: string);
|
|
16
|
+
}
|
|
12
17
|
export declare class InstructionNotFoundError extends Error {
|
|
13
18
|
key: string;
|
|
14
19
|
backend: string;
|
|
@@ -15,6 +15,16 @@ export class MaxTokensRequiredError extends Error {
|
|
|
15
15
|
this.name = 'MaxTokensRequiredError';
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
export class GatewayAliasModelRejectedError extends Error {
|
|
19
|
+
aliasModel;
|
|
20
|
+
code = 'GATEWAY_ALIAS_MODEL_REJECTED';
|
|
21
|
+
constructor(aliasModel, message) {
|
|
22
|
+
super(message ??
|
|
23
|
+
`Profile/choice alias "${aliasModel}" cannot be used at invoke ingress. Resolve the alias upstream (ai-tasks / resolveAIProfile) before calling the gateway.`);
|
|
24
|
+
this.aliasModel = aliasModel;
|
|
25
|
+
this.name = 'GatewayAliasModelRejectedError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
18
28
|
export class InstructionNotFoundError extends Error {
|
|
19
29
|
key;
|
|
20
30
|
backend;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke model ingress normalization (@x12i/ai-profiles `normalizeInvokeModel`).
|
|
3
|
+
* Defense in depth before catalog lookup / `resolveInvokeModel`.
|
|
4
|
+
*/
|
|
5
|
+
export type InvokeModelIngressOptions = {
|
|
6
|
+
useOpenRouter?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type InvokeModelIngressResult = {
|
|
9
|
+
provider: string;
|
|
10
|
+
model: string;
|
|
11
|
+
changed: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Normalizes invoke wire shape at gateway ingress. Idempotent when upstream already fixed.
|
|
15
|
+
* Rejects profile/choice aliases — callers must resolve via ai-tasks / `resolveAIProfile`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function normalizeInvokeModelAtIngress(provider: string | undefined, model: string, options?: InvokeModelIngressOptions): InvokeModelIngressResult;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke model ingress normalization (@x12i/ai-profiles `normalizeInvokeModel`).
|
|
3
|
+
* Defense in depth before catalog lookup / `resolveInvokeModel`.
|
|
4
|
+
*/
|
|
5
|
+
import { AIProfilesError, normalizeInvokeModel } from '@x12i/ai-profiles';
|
|
6
|
+
import { GatewayAliasModelRejectedError } from './instruction-errors.js';
|
|
7
|
+
function looksLikeOpenRouterCompositeSlug(model, provider) {
|
|
8
|
+
const trimmed = model.trim();
|
|
9
|
+
if (trimmed.startsWith('openrouter/'))
|
|
10
|
+
return true;
|
|
11
|
+
const providerHint = provider?.trim().toLowerCase();
|
|
12
|
+
if ((!providerHint || providerHint === 'unspecified') && trimmed.includes('/')) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
function augmentShapeInvalidMessage(err, model, provider) {
|
|
18
|
+
if (err.code !== 'INVOKE_MODEL_SHAPE_INVALID')
|
|
19
|
+
return err;
|
|
20
|
+
if (!looksLikeOpenRouterCompositeSlug(model, provider))
|
|
21
|
+
return err;
|
|
22
|
+
return new AIProfilesError(err.code, `${err.message} Model looks like openrouter/vendor/model composite — split into provider + model.`, err.details);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes invoke wire shape at gateway ingress. Idempotent when upstream already fixed.
|
|
26
|
+
* Rejects profile/choice aliases — callers must resolve via ai-tasks / `resolveAIProfile`.
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeInvokeModelAtIngress(provider, model, options = {}) {
|
|
29
|
+
const inputProvider = provider?.trim();
|
|
30
|
+
const inputModel = model.trim();
|
|
31
|
+
try {
|
|
32
|
+
const normalized = normalizeInvokeModel({
|
|
33
|
+
model: inputModel,
|
|
34
|
+
provider: inputProvider,
|
|
35
|
+
routing: 'auto',
|
|
36
|
+
useOpenRouter: options.useOpenRouter,
|
|
37
|
+
}, { useOpenRouter: options.useOpenRouter });
|
|
38
|
+
const changed = normalized.provider !== (inputProvider?.toLowerCase() ?? inputProvider) ||
|
|
39
|
+
normalized.model !== inputModel;
|
|
40
|
+
return {
|
|
41
|
+
provider: normalized.provider,
|
|
42
|
+
model: normalized.model,
|
|
43
|
+
changed,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (err instanceof AIProfilesError) {
|
|
48
|
+
if (err.code === 'INVOKE_MODEL_ALIAS_AT_GATEWAY') {
|
|
49
|
+
const alias = typeof err.details?.model === 'string' ? err.details.model : inputModel;
|
|
50
|
+
throw new GatewayAliasModelRejectedError(alias);
|
|
51
|
+
}
|
|
52
|
+
throw augmentShapeInvalidMessage(err, inputModel, inputProvider);
|
|
53
|
+
}
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Gateway-level OpenRouter key +
|
|
2
|
+
* Gateway-level OpenRouter key + USE_OPENROUTER flags mapped to @x12i/ai-tools v3 invoke helpers.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { readUseOpenRouterFromEnv } from '@x12i/ai-profiles';
|
|
5
5
|
import type { GatewayConfig } from './types.js';
|
|
6
|
-
export {
|
|
6
|
+
export { readUseOpenRouterFromEnv };
|
|
7
7
|
export declare function resolveOpenRouterApiKey(config?: GatewayConfig): string | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Effective OpenRouter preference for invoke-time routing (ai-tools `useOpenRouter`).
|
|
10
|
+
* Constructor `openRouter.prefer` / `openRouter.enabled` override env `USE_OPENROUTER`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveUseOpenRouter(config?: GatewayConfig): boolean;
|
|
13
|
+
/** @deprecated Use {@link resolveUseOpenRouter} (ai-tools v3 renamed prefer → useOpenRouter). */
|
|
8
14
|
export declare function resolvePreferOpenRouter(config?: GatewayConfig): boolean;
|
|
15
|
+
/** @deprecated Use {@link readUseOpenRouterFromEnv} from `@x12i/ai-profiles`. */
|
|
16
|
+
export declare function readPreferOpenRouterFromEnv(): boolean;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Gateway-level OpenRouter key +
|
|
2
|
+
* Gateway-level OpenRouter key + USE_OPENROUTER flags mapped to @x12i/ai-tools v3 invoke helpers.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import { resolveOpenRouterApiKey as resolveOpenRouterApiKeyFromTools, resolveUseOpenRouterPreference, } from '@x12i/ai-tools';
|
|
5
|
+
import { readUseOpenRouterFromEnv } from '@x12i/ai-profiles';
|
|
6
|
+
export { readUseOpenRouterFromEnv };
|
|
6
7
|
export function resolveOpenRouterApiKey(config = {}) {
|
|
7
8
|
return resolveOpenRouterApiKeyFromTools(config.openrouter?.apiKey);
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Effective OpenRouter preference for invoke-time routing (ai-tools `useOpenRouter`).
|
|
12
|
+
* Constructor `openRouter.prefer` / `openRouter.enabled` override env `USE_OPENROUTER`.
|
|
13
|
+
*/
|
|
14
|
+
export function resolveUseOpenRouter(config = {}) {
|
|
10
15
|
if (config.openRouter?.prefer === true)
|
|
11
16
|
return true;
|
|
12
17
|
if (config.openRouter?.prefer === false)
|
|
@@ -15,5 +20,13 @@ export function resolvePreferOpenRouter(config = {}) {
|
|
|
15
20
|
return true;
|
|
16
21
|
if (config.openRouter?.enabled === false)
|
|
17
22
|
return false;
|
|
18
|
-
return
|
|
23
|
+
return resolveUseOpenRouterPreference();
|
|
24
|
+
}
|
|
25
|
+
/** @deprecated Use {@link resolveUseOpenRouter} (ai-tools v3 renamed prefer → useOpenRouter). */
|
|
26
|
+
export function resolvePreferOpenRouter(config = {}) {
|
|
27
|
+
return resolveUseOpenRouter(config);
|
|
28
|
+
}
|
|
29
|
+
/** @deprecated Use {@link readUseOpenRouterFromEnv} from `@x12i/ai-profiles`. */
|
|
30
|
+
export function readPreferOpenRouterFromEnv() {
|
|
31
|
+
return readUseOpenRouterFromEnv() ?? true;
|
|
19
32
|
}
|
|
@@ -165,7 +165,7 @@ function pickActivixUsageTokens(response) {
|
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
167
|
/**
|
|
168
|
-
* Activix
|
|
168
|
+
* Activix 8.x `outer.cost` from gateway billing + routing (Run Analysis G8).
|
|
169
169
|
* Uses Activix {@link normalizeToActivixCostShape} so the shape matches package validators.
|
|
170
170
|
*/
|
|
171
171
|
function buildActivixOuterCost(routingMeta, billing, response) {
|
|
@@ -193,7 +193,7 @@ function buildActivixOuterCost(routingMeta, billing, response) {
|
|
|
193
193
|
};
|
|
194
194
|
return normalizeToActivixCostShape(candidate, 'gateway.outer.cost') ?? undefined;
|
|
195
195
|
}
|
|
196
|
-
/** Run-level record metadata (Activix
|
|
196
|
+
/** Run-level record metadata (Activix 8.x top-level `metadata`, sibling to `outer`). */
|
|
197
197
|
function buildActivixRecordMetadata(response, billing) {
|
|
198
198
|
const out = {
|
|
199
199
|
...pickActivixCompletionRoutingMetadata(response)
|
|
@@ -32,8 +32,8 @@ export interface ActivityManagerConfig {
|
|
|
32
32
|
customTracker?: Activix;
|
|
33
33
|
logger: Logxer;
|
|
34
34
|
/**
|
|
35
|
-
* Activix
|
|
36
|
-
*
|
|
35
|
+
* Activix 8.6+ {@link ActivixAutoCostOptions}: optional persist-time cost via @x12i/ai-tools v3.
|
|
36
|
+
* Default gateway manager leaves this off — billing is set before completeRecord.
|
|
37
37
|
*/
|
|
38
38
|
autoCost?: boolean | ActivixAutoCostOptions;
|
|
39
39
|
}
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, CostCalculator, } from '@x12i/ai-tools';
|
|
6
6
|
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
7
|
-
import {
|
|
8
|
-
export { resolveInvokeModel,
|
|
9
|
-
|
|
7
|
+
import { resolveUseOpenRouter } from './openrouter-routing.js';
|
|
8
|
+
export { resolveInvokeModel, applyOpenRouterInvokeRouting, buildInvokeModelResolverOptions, enrichModelResolutionError, mapResolutionToRouterConfig, ModelProfileUnroutableError, ModelProfileInputRejectedError, MODEL_PROFILE_UNROUTABLE, getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, createAiToolsInvokeClient, } from '@x12i/ai-tools';
|
|
9
|
+
/** @deprecated Use `applyOpenRouterInvokeRouting` (ai-tools v3). */
|
|
10
|
+
export { applyOpenRouterInvokeRouting as applyOpenRouterInvokePolicy } from '@x12i/ai-tools';
|
|
11
|
+
export { resolveOpenRouterApiKey, resolveUseOpenRouter, resolvePreferOpenRouter, readUseOpenRouterFromEnv, readPreferOpenRouterFromEnv, } from './openrouter-routing.js';
|
|
10
12
|
let bootstrapFailedLogged = false;
|
|
11
13
|
function invokeClientOptions(config) {
|
|
12
14
|
return {
|
|
@@ -26,7 +28,8 @@ function withCatalogLaneCalculator(client, config) {
|
|
|
26
28
|
...(config.aiTools?.costIncludeBreakdown ? { includeBreakdown: true } : {}),
|
|
27
29
|
resolverOptions: buildInvokeModelResolverOptions({
|
|
28
30
|
routingEnv: client.routingEnv,
|
|
29
|
-
catalogLane: lane
|
|
31
|
+
catalogLane: lane,
|
|
32
|
+
useOpenRouter: resolveUseOpenRouter(config),
|
|
30
33
|
})
|
|
31
34
|
})
|
|
32
35
|
};
|
|
@@ -35,7 +38,7 @@ function withCatalogLaneCalculator(client, config) {
|
|
|
35
38
|
export function buildModelResolverOptions(config, routingEnv) {
|
|
36
39
|
return buildInvokeModelResolverOptions({
|
|
37
40
|
routingEnv,
|
|
38
|
-
|
|
41
|
+
useOpenRouter: resolveUseOpenRouter(config),
|
|
39
42
|
});
|
|
40
43
|
}
|
|
41
44
|
/**
|
|
@@ -6,8 +6,10 @@ import { type AiToolsInvokeClient, type ModelResolutionSuccess, type OpenRouterR
|
|
|
6
6
|
import type { Logxer } from '@x12i/logxer';
|
|
7
7
|
import type { GatewayConfig } from './types.js';
|
|
8
8
|
export type AiToolsClientBundle = AiToolsInvokeClient;
|
|
9
|
-
export { resolveInvokeModel,
|
|
10
|
-
|
|
9
|
+
export { resolveInvokeModel, applyOpenRouterInvokeRouting, buildInvokeModelResolverOptions, enrichModelResolutionError, mapResolutionToRouterConfig, ModelProfileUnroutableError, ModelProfileInputRejectedError, MODEL_PROFILE_UNROUTABLE, getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, createAiToolsInvokeClient, } from '@x12i/ai-tools';
|
|
10
|
+
/** @deprecated Use `applyOpenRouterInvokeRouting` (ai-tools v3). */
|
|
11
|
+
export { applyOpenRouterInvokeRouting as applyOpenRouterInvokePolicy } from '@x12i/ai-tools';
|
|
12
|
+
export { resolveOpenRouterApiKey, resolveUseOpenRouter, resolvePreferOpenRouter, readUseOpenRouterFromEnv, readPreferOpenRouterFromEnv, } from './openrouter-routing.js';
|
|
11
13
|
export type { InvokeModelResolutionDiagnostics, InvokeModelResolutionInput, InvokeModelResolutionOptions, InvokeModelResolutionResult, InvokeRouterConfigSlice, AiToolsInvokeClient, } from '@x12i/ai-tools';
|
|
12
14
|
/** @deprecated Use buildInvokeModelResolverOptions */
|
|
13
15
|
export declare function buildModelResolverOptions(config: GatewayConfig, routingEnv?: OpenRouterRoutingConfig): import("@x12i/ai-tools").ModelResolverOptions;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
|
-
import { resolveOpenRouterApiKey,
|
|
8
|
+
import { resolveOpenRouterApiKey, resolveUseOpenRouter, } from './openrouter-routing.js';
|
|
9
9
|
import { LLMProviderRouter } from '@x12i/ai-providers-router';
|
|
10
10
|
import { createGatewayLogger } from './logger-factory.js';
|
|
11
11
|
import { ActivityManager } from './activity-manager.js';
|
|
@@ -154,14 +154,14 @@ export function initializeGatewayComponents(config) {
|
|
|
154
154
|
if (config.logging !== undefined)
|
|
155
155
|
routerConfig.logging = config.logging;
|
|
156
156
|
const openRouterKey = resolveOpenRouterApiKey(config);
|
|
157
|
-
const
|
|
157
|
+
const useOpenRouter = resolveUseOpenRouter(config);
|
|
158
158
|
if (openRouterKey) {
|
|
159
159
|
routerConfig.openrouter = { apiKey: openRouterKey };
|
|
160
160
|
routerConfig.openRouter = {
|
|
161
161
|
enabled: true,
|
|
162
|
-
prefer:
|
|
162
|
+
prefer: useOpenRouter,
|
|
163
163
|
};
|
|
164
|
-
if (
|
|
164
|
+
if (useOpenRouter) {
|
|
165
165
|
routerConfig.defaultMode = 'openrouter';
|
|
166
166
|
}
|
|
167
167
|
}
|
|
@@ -190,7 +190,7 @@ export function initializeGatewayComponents(config) {
|
|
|
190
190
|
activityManager,
|
|
191
191
|
usageTracker,
|
|
192
192
|
messageBuilderConfig,
|
|
193
|
-
|
|
193
|
+
useOpenRouter,
|
|
194
194
|
openRouterApiKey: openRouterKey,
|
|
195
195
|
};
|
|
196
196
|
}
|
|
@@ -37,6 +37,6 @@ export declare function initializeGatewayComponents(config: GatewayConfig): {
|
|
|
37
37
|
activityManager: ActivityManager;
|
|
38
38
|
usageTracker: UsageTracker;
|
|
39
39
|
messageBuilderConfig: MessageBuilderConfig;
|
|
40
|
-
|
|
40
|
+
useOpenRouter: boolean;
|
|
41
41
|
openRouterApiKey?: string;
|
|
42
42
|
};
|
|
@@ -7,7 +7,8 @@ import { FallbackExhaustedError } from '@x12i/ai-providers-router';
|
|
|
7
7
|
import { ModelResolutionError, ModelProfileInputRejectedError, ModelProfileUnroutableError, resolveInvokeModel, } from '@x12i/ai-tools';
|
|
8
8
|
import { extractHttpStatusCode } from './gateway-retry.js';
|
|
9
9
|
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
10
|
-
import { MaxTokensRequiredError, ModelRequiredError } from './instruction-errors.js';
|
|
10
|
+
import { MaxTokensRequiredError, ModelRequiredError, } from './instruction-errors.js';
|
|
11
|
+
import { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
|
|
11
12
|
import { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P } from './gateway-defaults.js';
|
|
12
13
|
function getPreParsedInstructions(instructions) {
|
|
13
14
|
return instructions ?? '';
|
|
@@ -93,18 +94,34 @@ export async function mergeConfig(request, config, logger, mergeOptions) {
|
|
|
93
94
|
provider: modelConfigAsConfig?.provider || request.config?.provider || internalDefaults?.engine || config.defaultEngine
|
|
94
95
|
};
|
|
95
96
|
const explicitModel = merged.model;
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
let originalProvider = merged.provider;
|
|
98
|
+
let originalModel = explicitModel;
|
|
98
99
|
if (!explicitModel) {
|
|
99
100
|
throw new ModelRequiredError();
|
|
100
101
|
}
|
|
102
|
+
const ingress = normalizeInvokeModelAtIngress(merged.provider, explicitModel, {
|
|
103
|
+
useOpenRouter: mergeOptions?.useOpenRouter,
|
|
104
|
+
});
|
|
105
|
+
if (ingress.changed) {
|
|
106
|
+
logger.verbose('Invoke model normalized at ingress', {
|
|
107
|
+
jobId: request.identity.jobId,
|
|
108
|
+
originalProvider,
|
|
109
|
+
originalModel,
|
|
110
|
+
provider: ingress.provider,
|
|
111
|
+
model: ingress.model,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
merged.provider = ingress.provider;
|
|
115
|
+
merged.model = ingress.model;
|
|
116
|
+
originalProvider = merged.provider;
|
|
117
|
+
originalModel = merged.model;
|
|
101
118
|
if (resolveModels && mergeOptions?.catalog) {
|
|
102
119
|
try {
|
|
103
|
-
const resolved = await resolveInvokeModel({ provider: merged.provider, model:
|
|
120
|
+
const resolved = await resolveInvokeModel({ provider: merged.provider, model: merged.model }, {
|
|
104
121
|
catalog: mergeOptions.catalog,
|
|
105
122
|
routingEnv: mergeOptions.routingEnv,
|
|
106
123
|
openRouterApiKey: mergeOptions.openRouterApiKey,
|
|
107
|
-
|
|
124
|
+
useOpenRouter: mergeOptions.useOpenRouter,
|
|
108
125
|
defaultProvider: config.defaultEngine,
|
|
109
126
|
resolveModels: true,
|
|
110
127
|
modelsOnly: config.aiTools?.modelsOnly !== false,
|
|
@@ -142,11 +159,11 @@ export async function mergeConfig(request, config, logger, mergeOptions) {
|
|
|
142
159
|
}
|
|
143
160
|
}
|
|
144
161
|
else if (mergeOptions?.openRouterApiKey) {
|
|
145
|
-
const resolved = await resolveInvokeModel({ provider: merged.provider, model:
|
|
162
|
+
const resolved = await resolveInvokeModel({ provider: merged.provider, model: merged.model }, {
|
|
146
163
|
resolveModels: false,
|
|
147
164
|
routingEnv: mergeOptions.routingEnv,
|
|
148
165
|
openRouterApiKey: mergeOptions.openRouterApiKey,
|
|
149
|
-
|
|
166
|
+
useOpenRouter: mergeOptions.useOpenRouter,
|
|
150
167
|
defaultProvider: config.defaultEngine,
|
|
151
168
|
});
|
|
152
169
|
merged.provider = resolved.router.provider;
|
|
@@ -512,7 +529,14 @@ export async function resolveCostCompletionWithAiTools(routerResponse, tokens, o
|
|
|
512
529
|
options?.calculator &&
|
|
513
530
|
hasNonZeroTokenUsage(tokens)) {
|
|
514
531
|
try {
|
|
515
|
-
const record =
|
|
532
|
+
const record = routerResponse != null && typeof routerResponse === 'object'
|
|
533
|
+
? {
|
|
534
|
+
...routerResponse,
|
|
535
|
+
...(options.mergedConfig != null ? { config: options.mergedConfig } : {})
|
|
536
|
+
}
|
|
537
|
+
: options.mergedConfig != null
|
|
538
|
+
? { config: options.mergedConfig, metadata: { tokens } }
|
|
539
|
+
: { metadata: { tokens } };
|
|
516
540
|
const result = await options.calculator.calculateFromRecord(record);
|
|
517
541
|
billing = mapAiCostResultToResolvedActivityCost(billing, result);
|
|
518
542
|
}
|
|
@@ -17,7 +17,7 @@ export type MergeConfigOptions = {
|
|
|
17
17
|
catalog?: AiModelsCatalogClient | null;
|
|
18
18
|
routingEnv?: OpenRouterRoutingConfig;
|
|
19
19
|
openRouterApiKey?: string;
|
|
20
|
-
|
|
20
|
+
useOpenRouter?: boolean;
|
|
21
21
|
};
|
|
22
22
|
export { MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, } from '@x12i/ai-tools';
|
|
23
23
|
/**
|
package/dist-cjs/gateway.cjs
CHANGED
|
@@ -59,7 +59,7 @@ export class AIGateway {
|
|
|
59
59
|
messageBuilderConfig;
|
|
60
60
|
_autoRegisterDone = false;
|
|
61
61
|
_aiToolsClient = null;
|
|
62
|
-
|
|
62
|
+
useOpenRouter;
|
|
63
63
|
openRouterApiKey;
|
|
64
64
|
constructor(config = {}, activityManager) {
|
|
65
65
|
this.config = config;
|
|
@@ -69,7 +69,7 @@ export class AIGateway {
|
|
|
69
69
|
this.router = components.router;
|
|
70
70
|
this.activityManager = components.activityManager;
|
|
71
71
|
this.messageBuilderConfig = components.messageBuilderConfig;
|
|
72
|
-
this.
|
|
72
|
+
this.useOpenRouter = components.useOpenRouter;
|
|
73
73
|
this.openRouterApiKey = components.openRouterApiKey;
|
|
74
74
|
setGatewayRuntimeClients({
|
|
75
75
|
activix: this.activityManager?.getTracker(),
|
|
@@ -100,7 +100,7 @@ export class AIGateway {
|
|
|
100
100
|
catalog: aiTools?.catalog ?? null,
|
|
101
101
|
routingEnv: aiTools?.routingEnv,
|
|
102
102
|
openRouterApiKey: this.openRouterApiKey,
|
|
103
|
-
|
|
103
|
+
useOpenRouter: this.useOpenRouter,
|
|
104
104
|
});
|
|
105
105
|
// Activix start snapshot must match what the router receives (modelConfig-only callers omit request.config.model).
|
|
106
106
|
request._mergedRouterConfig = mergedConfig;
|
|
@@ -288,7 +288,7 @@ export class AIGateway {
|
|
|
288
288
|
catalog: aiTools?.catalog ?? null,
|
|
289
289
|
routingEnv: aiTools?.routingEnv,
|
|
290
290
|
openRouterApiKey: this.openRouterApiKey,
|
|
291
|
-
|
|
291
|
+
useOpenRouter: this.useOpenRouter,
|
|
292
292
|
});
|
|
293
293
|
request._mergedRouterConfig = mergedConfig;
|
|
294
294
|
logResolvedModelRouting(this.logger, request, mergedConfig);
|
package/dist-cjs/gateway.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare class AIGateway {
|
|
|
18
18
|
private messageBuilderConfig?;
|
|
19
19
|
private _autoRegisterDone;
|
|
20
20
|
private _aiToolsClient;
|
|
21
|
-
private readonly
|
|
21
|
+
private readonly useOpenRouter;
|
|
22
22
|
private readonly openRouterApiKey?;
|
|
23
23
|
constructor(config?: GatewayConfig, activityManager?: ActivityManager);
|
|
24
24
|
/**
|
package/dist-cjs/index.cjs
CHANGED
|
@@ -15,7 +15,8 @@ export { ProviderNotFoundError, FallbackExhaustedError } from '@x12i/ai-provider
|
|
|
15
15
|
export * from '@x12i/ai-providers-router';
|
|
16
16
|
// Export enhanced gateway
|
|
17
17
|
export { AIGateway } from './gateway.js';
|
|
18
|
-
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError } from './instruction-errors.js';
|
|
18
|
+
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError, GatewayAliasModelRejectedError, } from './instruction-errors.js';
|
|
19
|
+
export { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
|
|
19
20
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
20
21
|
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
21
22
|
export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
|
|
@@ -33,8 +34,8 @@ export { activityIdentityToLogContext, activityIdentityToLogMeta, withActivityId
|
|
|
33
34
|
export { createGatewayLogger, resolveGatewayVerboseEnabled } from './logger-factory.js';
|
|
34
35
|
export { GATEWAY_LOG_ENV_PREFIX, GATEWAY_LOGXER_PACKAGE, GATEWAY_STACK_LOG_PREFIXES, initializeGatewayPackageLogLevels, resetGatewayPackageLogLevelsInit } from './gateway-log-levels.js';
|
|
35
36
|
export { GatewayLogCode, gatewayErrorCode, gatewayInfoCode, gatewayWarnCode, gatewayAnomalyMeta, resolveLogDiagnosticsCatalogPath, exceptionEvidence, fieldEvidence } from './gateway-log-diagnostics.js';
|
|
36
|
-
/** Re-export @x12i/ai-tools invoke orchestrator (≥
|
|
37
|
-
export { resolveInvokeModel, applyModelResolution, applyOpenRouterInvokePolicy, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, buildModelResolverOptions, enrichModelResolutionError, getAiToolsClient, resetAiToolsClientForTests, resolveOpenRouterApiKey, resolvePreferOpenRouter, readPreferOpenRouterFromEnv, getAiToolsInvokeClient, createAiToolsInvokeClient, } from './ai-tools-client.js';
|
|
37
|
+
/** Re-export @x12i/ai-tools invoke orchestrator (≥ 3.0.0) for engine callers. */
|
|
38
|
+
export { resolveInvokeModel, applyModelResolution, applyOpenRouterInvokeRouting, applyOpenRouterInvokePolicy, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, buildModelResolverOptions, enrichModelResolutionError, getAiToolsClient, resetAiToolsClientForTests, resolveOpenRouterApiKey, resolveUseOpenRouter, resolvePreferOpenRouter, readUseOpenRouterFromEnv, readPreferOpenRouterFromEnv, getAiToolsInvokeClient, createAiToolsInvokeClient, } from './ai-tools-client.js';
|
|
38
39
|
// Re-export logging (@x12i/logxer)
|
|
39
40
|
export { createLogxer, DebugLogAbstract, runWithLogContext, getStationRuntimeIdentity, mergeRuntimeIdentity, conditionEvidence, sourceEvidence, logReferenceEvidence, readAgentLoggingInstructions, resolveAgentLoggingInstructionsPath, applyPackageLogLevelsFromEnv, configurePackageLogLevels, mergePackageLogLevelsConfig, setPackageLogLevel, resolveStackLogLevelForPrefix, resolvePackageLogsLevel, parseLogxerPackageLevelsEnv, LOGXER_PACKAGE_LEVELS_ENV, LOGXER_PACKAGE_LOGS_DEFAULT_ENV } from '@x12i/logxer';
|
|
40
41
|
export { ROUTER_LOG_ENV_PREFIX } from '@x12i/ai-providers-router';
|
package/dist-cjs/index.d.ts
CHANGED
|
@@ -14,7 +14,8 @@ export type { RequestInterceptor, ResponseInterceptor } from '@x12i/ai-providers
|
|
|
14
14
|
export type { UsageTracker } from '@x12i/ai-providers-router';
|
|
15
15
|
export * from '@x12i/ai-providers-router';
|
|
16
16
|
export { AIGateway } from './gateway.js';
|
|
17
|
-
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError } from './instruction-errors.js';
|
|
17
|
+
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError, GatewayAliasModelRejectedError, } from './instruction-errors.js';
|
|
18
|
+
export { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
|
|
18
19
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
19
20
|
export type { GatewayConfig, ProviderModelRef, ModelConfig, RetryConfig, ChatRequest, AIInvokeRequest, AIRequest, GatewayActionType, GatewayInvokeRejectionMetadata, GatewayFallbackAttempt, GatewayTraceRequestIds, GatewayTraceAttempt, GatewayTraceUsageSummary, GatewayTraceMergedConfig, EnhancedLLMResponse, InstructionMetadata, ValidationRule, TemplateRenderOptions, SmartInputConfig, SmartInputRenderOptions } from './types.js';
|
|
20
21
|
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
@@ -40,8 +41,8 @@ export type { GatewayLoggerConfig } from './logger-factory.js';
|
|
|
40
41
|
export { GATEWAY_LOG_ENV_PREFIX, GATEWAY_LOGXER_PACKAGE, GATEWAY_STACK_LOG_PREFIXES, initializeGatewayPackageLogLevels, resetGatewayPackageLogLevelsInit } from './gateway-log-levels.js';
|
|
41
42
|
export { GatewayLogCode, gatewayErrorCode, gatewayInfoCode, gatewayWarnCode, gatewayAnomalyMeta, resolveLogDiagnosticsCatalogPath, exceptionEvidence, fieldEvidence } from './gateway-log-diagnostics.js';
|
|
42
43
|
export type { GatewayLogCode as GatewayDiagnosticCode } from './gateway-log-diagnostics.js';
|
|
43
|
-
/** Re-export @x12i/ai-tools invoke orchestrator (≥
|
|
44
|
-
export { resolveInvokeModel, applyModelResolution, applyOpenRouterInvokePolicy, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, buildModelResolverOptions, enrichModelResolutionError, getAiToolsClient, resetAiToolsClientForTests, resolveOpenRouterApiKey, resolvePreferOpenRouter, readPreferOpenRouterFromEnv, getAiToolsInvokeClient, createAiToolsInvokeClient, } from './ai-tools-client.js';
|
|
44
|
+
/** Re-export @x12i/ai-tools invoke orchestrator (≥ 3.0.0) for engine callers. */
|
|
45
|
+
export { resolveInvokeModel, applyModelResolution, applyOpenRouterInvokeRouting, applyOpenRouterInvokePolicy, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, buildModelResolverOptions, enrichModelResolutionError, getAiToolsClient, resetAiToolsClientForTests, resolveOpenRouterApiKey, resolveUseOpenRouter, resolvePreferOpenRouter, readUseOpenRouterFromEnv, readPreferOpenRouterFromEnv, getAiToolsInvokeClient, createAiToolsInvokeClient, } from './ai-tools-client.js';
|
|
45
46
|
export type { AiToolsClientBundle, AiToolsInvokeClient, InvokeModelResolutionDiagnostics, InvokeModelResolutionInput, InvokeModelResolutionOptions, InvokeModelResolutionResult, InvokeRouterConfigSlice, } from './ai-tools-client.js';
|
|
46
47
|
export { createLogxer, DebugLogAbstract, runWithLogContext, getStationRuntimeIdentity, mergeRuntimeIdentity, conditionEvidence, sourceEvidence, logReferenceEvidence, readAgentLoggingInstructions, resolveAgentLoggingInstructionsPath, applyPackageLogLevelsFromEnv, configurePackageLogLevels, mergePackageLogLevelsConfig, setPackageLogLevel, resolveStackLogLevelForPrefix, resolvePackageLogsLevel, parseLogxerPackageLevelsEnv, LOGXER_PACKAGE_LEVELS_ENV, LOGXER_PACKAGE_LOGS_DEFAULT_ENV } from '@x12i/logxer';
|
|
47
48
|
export { ROUTER_LOG_ENV_PREFIX } from '@x12i/ai-providers-router';
|
|
@@ -15,6 +15,16 @@ export class MaxTokensRequiredError extends Error {
|
|
|
15
15
|
this.name = 'MaxTokensRequiredError';
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
export class GatewayAliasModelRejectedError extends Error {
|
|
19
|
+
aliasModel;
|
|
20
|
+
code = 'GATEWAY_ALIAS_MODEL_REJECTED';
|
|
21
|
+
constructor(aliasModel, message) {
|
|
22
|
+
super(message ??
|
|
23
|
+
`Profile/choice alias "${aliasModel}" cannot be used at invoke ingress. Resolve the alias upstream (ai-tasks / resolveAIProfile) before calling the gateway.`);
|
|
24
|
+
this.aliasModel = aliasModel;
|
|
25
|
+
this.name = 'GatewayAliasModelRejectedError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
18
28
|
export class InstructionNotFoundError extends Error {
|
|
19
29
|
key;
|
|
20
30
|
backend;
|
|
@@ -9,6 +9,11 @@ export declare class MaxTokensRequiredError extends Error {
|
|
|
9
9
|
readonly code = "MAX_TOKENS_REQUIRED";
|
|
10
10
|
constructor(message?: string);
|
|
11
11
|
}
|
|
12
|
+
export declare class GatewayAliasModelRejectedError extends Error {
|
|
13
|
+
readonly aliasModel: string;
|
|
14
|
+
readonly code = "GATEWAY_ALIAS_MODEL_REJECTED";
|
|
15
|
+
constructor(aliasModel: string, message?: string);
|
|
16
|
+
}
|
|
12
17
|
export declare class InstructionNotFoundError extends Error {
|
|
13
18
|
key: string;
|
|
14
19
|
backend: string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke model ingress normalization (@x12i/ai-profiles `normalizeInvokeModel`).
|
|
3
|
+
* Defense in depth before catalog lookup / `resolveInvokeModel`.
|
|
4
|
+
*/
|
|
5
|
+
import { AIProfilesError, normalizeInvokeModel } from '@x12i/ai-profiles';
|
|
6
|
+
import { GatewayAliasModelRejectedError } from './instruction-errors.js';
|
|
7
|
+
function looksLikeOpenRouterCompositeSlug(model, provider) {
|
|
8
|
+
const trimmed = model.trim();
|
|
9
|
+
if (trimmed.startsWith('openrouter/'))
|
|
10
|
+
return true;
|
|
11
|
+
const providerHint = provider?.trim().toLowerCase();
|
|
12
|
+
if ((!providerHint || providerHint === 'unspecified') && trimmed.includes('/')) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
function augmentShapeInvalidMessage(err, model, provider) {
|
|
18
|
+
if (err.code !== 'INVOKE_MODEL_SHAPE_INVALID')
|
|
19
|
+
return err;
|
|
20
|
+
if (!looksLikeOpenRouterCompositeSlug(model, provider))
|
|
21
|
+
return err;
|
|
22
|
+
return new AIProfilesError(err.code, `${err.message} Model looks like openrouter/vendor/model composite — split into provider + model.`, err.details);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes invoke wire shape at gateway ingress. Idempotent when upstream already fixed.
|
|
26
|
+
* Rejects profile/choice aliases — callers must resolve via ai-tasks / `resolveAIProfile`.
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeInvokeModelAtIngress(provider, model, options = {}) {
|
|
29
|
+
const inputProvider = provider?.trim();
|
|
30
|
+
const inputModel = model.trim();
|
|
31
|
+
try {
|
|
32
|
+
const normalized = normalizeInvokeModel({
|
|
33
|
+
model: inputModel,
|
|
34
|
+
provider: inputProvider,
|
|
35
|
+
routing: 'auto',
|
|
36
|
+
useOpenRouter: options.useOpenRouter,
|
|
37
|
+
}, { useOpenRouter: options.useOpenRouter });
|
|
38
|
+
const changed = normalized.provider !== (inputProvider?.toLowerCase() ?? inputProvider) ||
|
|
39
|
+
normalized.model !== inputModel;
|
|
40
|
+
return {
|
|
41
|
+
provider: normalized.provider,
|
|
42
|
+
model: normalized.model,
|
|
43
|
+
changed,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (err instanceof AIProfilesError) {
|
|
48
|
+
if (err.code === 'INVOKE_MODEL_ALIAS_AT_GATEWAY') {
|
|
49
|
+
const alias = typeof err.details?.model === 'string' ? err.details.model : inputModel;
|
|
50
|
+
throw new GatewayAliasModelRejectedError(alias);
|
|
51
|
+
}
|
|
52
|
+
throw augmentShapeInvalidMessage(err, inputModel, inputProvider);
|
|
53
|
+
}
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke model ingress normalization (@x12i/ai-profiles `normalizeInvokeModel`).
|
|
3
|
+
* Defense in depth before catalog lookup / `resolveInvokeModel`.
|
|
4
|
+
*/
|
|
5
|
+
export type InvokeModelIngressOptions = {
|
|
6
|
+
useOpenRouter?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type InvokeModelIngressResult = {
|
|
9
|
+
provider: string;
|
|
10
|
+
model: string;
|
|
11
|
+
changed: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Normalizes invoke wire shape at gateway ingress. Idempotent when upstream already fixed.
|
|
15
|
+
* Rejects profile/choice aliases — callers must resolve via ai-tasks / `resolveAIProfile`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function normalizeInvokeModelAtIngress(provider: string | undefined, model: string, options?: InvokeModelIngressOptions): InvokeModelIngressResult;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Gateway-level OpenRouter key +
|
|
2
|
+
* Gateway-level OpenRouter key + USE_OPENROUTER flags mapped to @x12i/ai-tools v3 invoke helpers.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import { resolveOpenRouterApiKey as resolveOpenRouterApiKeyFromTools, resolveUseOpenRouterPreference, } from '@x12i/ai-tools';
|
|
5
|
+
import { readUseOpenRouterFromEnv } from '@x12i/ai-profiles';
|
|
6
|
+
export { readUseOpenRouterFromEnv };
|
|
6
7
|
export function resolveOpenRouterApiKey(config = {}) {
|
|
7
8
|
return resolveOpenRouterApiKeyFromTools(config.openrouter?.apiKey);
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Effective OpenRouter preference for invoke-time routing (ai-tools `useOpenRouter`).
|
|
12
|
+
* Constructor `openRouter.prefer` / `openRouter.enabled` override env `USE_OPENROUTER`.
|
|
13
|
+
*/
|
|
14
|
+
export function resolveUseOpenRouter(config = {}) {
|
|
10
15
|
if (config.openRouter?.prefer === true)
|
|
11
16
|
return true;
|
|
12
17
|
if (config.openRouter?.prefer === false)
|
|
@@ -15,5 +20,13 @@ export function resolvePreferOpenRouter(config = {}) {
|
|
|
15
20
|
return true;
|
|
16
21
|
if (config.openRouter?.enabled === false)
|
|
17
22
|
return false;
|
|
18
|
-
return
|
|
23
|
+
return resolveUseOpenRouterPreference();
|
|
24
|
+
}
|
|
25
|
+
/** @deprecated Use {@link resolveUseOpenRouter} (ai-tools v3 renamed prefer → useOpenRouter). */
|
|
26
|
+
export function resolvePreferOpenRouter(config = {}) {
|
|
27
|
+
return resolveUseOpenRouter(config);
|
|
28
|
+
}
|
|
29
|
+
/** @deprecated Use {@link readUseOpenRouterFromEnv} from `@x12i/ai-profiles`. */
|
|
30
|
+
export function readPreferOpenRouterFromEnv() {
|
|
31
|
+
return readUseOpenRouterFromEnv() ?? true;
|
|
19
32
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Gateway-level OpenRouter key +
|
|
2
|
+
* Gateway-level OpenRouter key + USE_OPENROUTER flags mapped to @x12i/ai-tools v3 invoke helpers.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { readUseOpenRouterFromEnv } from '@x12i/ai-profiles';
|
|
5
5
|
import type { GatewayConfig } from './types.js';
|
|
6
|
-
export {
|
|
6
|
+
export { readUseOpenRouterFromEnv };
|
|
7
7
|
export declare function resolveOpenRouterApiKey(config?: GatewayConfig): string | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Effective OpenRouter preference for invoke-time routing (ai-tools `useOpenRouter`).
|
|
10
|
+
* Constructor `openRouter.prefer` / `openRouter.enabled` override env `USE_OPENROUTER`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveUseOpenRouter(config?: GatewayConfig): boolean;
|
|
13
|
+
/** @deprecated Use {@link resolveUseOpenRouter} (ai-tools v3 renamed prefer → useOpenRouter). */
|
|
8
14
|
export declare function resolvePreferOpenRouter(config?: GatewayConfig): boolean;
|
|
15
|
+
/** @deprecated Use {@link readUseOpenRouterFromEnv} from `@x12i/ai-profiles`. */
|
|
16
|
+
export declare function readPreferOpenRouterFromEnv(): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x12i/ai-gateway",
|
|
3
|
-
"version": "10.0
|
|
3
|
+
"version": "10.2.0",
|
|
4
4
|
"description": "AI Gateway - Unified interface for LLM provider routing and management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -41,9 +41,10 @@
|
|
|
41
41
|
"author": "x12i",
|
|
42
42
|
"license": "mit",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@x12i/activix": "^8.
|
|
44
|
+
"@x12i/activix": "^8.6.1",
|
|
45
|
+
"@x12i/ai-profiles": "^3.2.0",
|
|
45
46
|
"@x12i/ai-providers-router": "^4.9.2",
|
|
46
|
-
"@x12i/ai-tools": "^
|
|
47
|
+
"@x12i/ai-tools": "^3.1.0",
|
|
47
48
|
"@x12i/flex-md": "^4.8.0",
|
|
48
49
|
"@x12i/logxer": "^4.6.0",
|
|
49
50
|
"@x12i/rendrix": "^4.3.0"
|