@x12i/ai-gateway 10.0.4 → 10.0.6
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 +50 -15
- package/dist/ai-tools-client.js +18 -2
- package/dist/gateway-config.js +2 -11
- package/dist/gateway-utils.d.ts +22 -6
- package/dist/gateway-utils.js +129 -69
- package/dist/gateway.js +2 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +7 -0
- package/dist-cjs/ai-tools-client.cjs +18 -2
- package/dist-cjs/gateway-config.cjs +2 -11
- package/dist-cjs/gateway-utils.cjs +129 -69
- package/dist-cjs/gateway-utils.d.ts +22 -6
- package/dist-cjs/gateway.cjs +2 -5
- package/dist-cjs/index.cjs +1 -1
- package/dist-cjs/index.d.ts +1 -1
- package/dist-cjs/types.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@ Unified gateway for LLM provider routing, structured logging, optional Activix a
|
|
|
9
9
|
| **Routing** | Registers providers (or lazy-registers from env), invokes the router with merged model config, retries, and optional fallback chain. |
|
|
10
10
|
| **`invoke()`** | Builds messages from instructions + prompt templates + `workingMemory`; requires runtime **identity** and **actionType** / **actionRef**. |
|
|
11
11
|
| **`invokeChat()`** | Raw chat-style requests; no instruction builder or action classification. |
|
|
12
|
-
| **Cost** |
|
|
13
|
-
| **Activix** | Optional Mongo-backed activity rows
|
|
14
|
-
| **Trace mode** | `diagnostics.mode === 'trace'` adds `metadata.attempts[]`, `metadata.usage`, and per-attempt
|
|
12
|
+
| **Cost** | Steps A→D on every successful **`invoke()`** / **`invokeChat()`**: router cost first, then **`@x12i/ai-tools`** catalog via **`calculateFromRecord`** when still unpriced. Single path — **`resolveCostCompletionWithAiTools`**. |
|
|
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
|
+
| **Trace mode** | `diagnostics.mode === 'trace'` adds `metadata.attempts[]`, `metadata.usage`, and per-attempt **`costUsd`** / **`costStatus`**. |
|
|
15
15
|
|
|
16
16
|
Pinned dependency versions are in `package.json` (currently **Activix ^8.5**, **ai-tools ^2.5**, **ai-providers-router ^4.9**).
|
|
17
17
|
|
|
@@ -171,6 +171,7 @@ Hosts wrapping the gateway should expose on **their** public API:
|
|
|
171
171
|
| `temperature`, `topP`, `frequencyPenalty`, `presencePenalty`, `maxTokens` | Optional | Document defaults from `GATEWAY_DEFAULT_*` |
|
|
172
172
|
| `retry` | Optional | Same shape as `RetryConfig`; defaults from `GATEWAY_DEFAULT_RETRY` |
|
|
173
173
|
| `mode` | Optional | `'dev'` \| `'debug'` \| `'prod'` — pass through to `GatewayConfig.mode` |
|
|
174
|
+
| Billing | Read-only on response | **`response.metadata.costUsd`**, **`costStatus`**, **`tokens`** — gateway-owned; do not re-price |
|
|
174
175
|
| `templateRenderOptions` / `smartInput` | Optional | Rendrix overrides |
|
|
175
176
|
|
|
176
177
|
Instructions must be **complete caller text** — the gateway no longer injects packaged instruction blocks.
|
|
@@ -210,19 +211,43 @@ Exports: `GATEWAY_LOGXER_PACKAGE`, `GATEWAY_LOG_ENV_PREFIX`, `createGatewayLogge
|
|
|
210
211
|
|
|
211
212
|
## @x12i/ai-tools v2 (models + cost)
|
|
212
213
|
|
|
213
|
-
-
|
|
214
|
-
- **`aiTools.enabled`** — bootstrap catalog client + calculator.
|
|
215
|
-
- **`aiTools.resolveModels`** — `mergeConfig()` calls `resolveInvokeModel()` (catalog + OpenRouter/direct routing).
|
|
216
|
-
- **`aiTools.modelsOnly`** — **`true` by default** — reject profile shortcuts (`cheapest`, `cheap/default`, …); pass concrete model ids only.
|
|
217
|
-
- **`aiTools.calculateCost`** — prices usage before Activix `completeRecord` when the router did not mark the call priced.
|
|
214
|
+
Engine-owned catalog bootstrap and post-call billing. Consumers read **`metadata.costUsd`** / **`costStatus`** only — no direct `@x12i/ai-tools` dependency for cost.
|
|
218
215
|
|
|
219
|
-
|
|
216
|
+
### Resolution order (after every successful LLM call)
|
|
220
217
|
|
|
221
|
-
|
|
218
|
+
| Step | Condition | Result |
|
|
219
|
+
|------|-----------|--------|
|
|
220
|
+
| A | Router/provider returned finite **`costUsd`** (or equivalent) | **`costStatus: "priced"`**, set cost |
|
|
221
|
+
| B | Tokens + catalog pricing succeeds (`isAuthoritative`, not `unknownModel`, finite cost ≥ 0) | **`priced`** (+ optional breakdown) |
|
|
222
|
+
| C | Tokens but no price | **`unpriced`** |
|
|
223
|
+
| D | No usage | omit **`costUsd`** and **`costStatus`** |
|
|
224
|
+
|
|
225
|
+
Step A always wins; explicit router **`costStatus: "unpriced"`** is never overridden by catalog.
|
|
226
|
+
|
|
227
|
+
Implemented in **`resolveCostCompletionWithAiTools`** only ( **`CostCalculator.calculateFromRecord`** via **`buildGatewayPricingRecord`** for Step B). Upstream target: **`resolveInvokeBilling`** in ai-tools — [AI_TOOLS_INVOKE_BILLING_ORCHESTRATOR_SPEC.md](./docs/upstream-reports/AI_TOOLS_INVOKE_BILLING_ORCHESTRATOR_SPEC.md).
|
|
228
|
+
|
|
229
|
+
### `aiTools` config (aligned with funcx / generic engine contract)
|
|
230
|
+
|
|
231
|
+
| Flag | Default | Purpose |
|
|
232
|
+
|------|---------|---------|
|
|
233
|
+
| **`enabled`** | `true` | Bootstrap **`AiModelsCatalogClient`** + **`CostCalculator`** |
|
|
234
|
+
| **`calculateCost`** | `true` | Run post-call catalog pricing when router did not price |
|
|
235
|
+
| **`resolveModels`** | `true` | **`mergeConfig()`** → **`resolveInvokeModel()`** |
|
|
236
|
+
| **`modelsOnly`** | `true` | Reject profile shortcuts (`cheapest`, `cheap/default`, …) |
|
|
237
|
+
| **`bundledOnly`** | `false` | Offline bundled catalogs only |
|
|
238
|
+
| **`costIncludeBreakdown`** | `false` | Include prompt/completion breakdown on priced results |
|
|
239
|
+
| **`catalogLane`** | `"text"` (ai-tools default) | Catalog lane for resolution + cost lookup (`text`, `image`, …) |
|
|
240
|
+
| **`cacheTtlMs`** | ai-tools default (24h) | In-memory catalog cache TTL |
|
|
241
|
+
|
|
242
|
+
- **No Catalox / Firestore** — catalogs come from ai-tools open-assets JSON (optional **`bundledOnly`**).
|
|
243
|
+
|
|
244
|
+
Gateway exports the model orchestrator from `@x12i/ai-tools` ≥ **2.5.0** (`resolveInvokeModel`, …) — see [AI_TOOLS_INVOKE_MODEL_RESOLUTION_ORCHESTRATOR_SPEC.md](./docs/upstream-reports/AI_TOOLS_INVOKE_MODEL_RESOLUTION_ORCHESTRATOR_SPEC.md).
|
|
245
|
+
|
|
246
|
+
Gateway billing helpers (exported for tests/integrators): `resolveCostCompletionWithAiTools`, `buildGatewayPricingRecord`, `catalogPricingSucceeded`, `buildTraceUsageSummary`, `enrichTraceAttemptsWithBilling`.
|
|
222
247
|
|
|
223
248
|
---
|
|
224
249
|
|
|
225
|
-
## Activity tracking (@x12i/activix
|
|
250
|
+
## Activity tracking (@x12i/activix 8.x)
|
|
226
251
|
|
|
227
252
|
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`):
|
|
228
253
|
|
|
@@ -241,7 +266,7 @@ When tracking is enabled and no custom tracker is supplied, the gateway construc
|
|
|
241
266
|
- `outer.cost`: Activix cost shape (`usd`, `tokens`, `provider`, `model`, `details`)
|
|
242
267
|
- `response.metadata`: same billing slice as returned to callers
|
|
243
268
|
|
|
244
|
-
|
|
269
|
+
Gateway resolves billing **before** `completeRecord` and sets **`outer.cost`** from that slice. Activix **`autoCost`** is **not** used on the default activity manager (no second pricing path).
|
|
245
270
|
|
|
246
271
|
Mongo env: `MONGO_URI` + `MONGO_LOGS_DB` or `MONGO_DB`.
|
|
247
272
|
|
|
@@ -249,10 +274,20 @@ Mongo env: `MONGO_URI` + `MONGO_LOGS_DB` or `MONGO_DB`.
|
|
|
249
274
|
|
|
250
275
|
## Response metadata and cost
|
|
251
276
|
|
|
252
|
-
On every successful **`invoke()`**:
|
|
277
|
+
On every successful **`invoke()`** and **`invokeChat()`**:
|
|
278
|
+
|
|
279
|
+
- **`metadata.provider`**, **`modelUsed`**, **`maxTokensRequested`**, **`effectiveModelConfig`** (invoke only)
|
|
280
|
+
- **`metadata.tokens`**, **`costStatus`**, **`costUsd`**, optional **`costBreakdown`**; **`cost`** mirrors **`costUsd`** when priced
|
|
281
|
+
|
|
282
|
+
### Client rules (ai-skills, graph-engine, etc.)
|
|
283
|
+
|
|
284
|
+
| `metadata.costStatus` | Meaning | Client action |
|
|
285
|
+
|------------------------|---------|---------------|
|
|
286
|
+
| **`priced`** | Gateway resolved a billable USD amount | Use **`metadata.costUsd`** (or **`cost`**) |
|
|
287
|
+
| **`unpriced`** | Tokens recorded; no authoritative price | Do **not** call ai-tools or re-price |
|
|
288
|
+
| *(absent)* | No token usage | No billing signal |
|
|
253
289
|
|
|
254
|
-
-
|
|
255
|
-
- **`metadata.tokens`**, **`costStatus`**, **`costUsd`** when usage exists and pricing applies
|
|
290
|
+
Do **not** add a direct **`@x12i/ai-tools`** dependency for post-call cost. For Activix rows you write yourself, use **`normalizeToActivixCostShape`** (re-exported from `@x12i/activix`) from **`costUsd`** + **`metadata.tokens`**.
|
|
256
291
|
|
|
257
292
|
Full contract: [AI Gateway invoke execution metadata](./docs/AI_GATEWAY_INVOKE_EXECUTION_METADATA.md).
|
|
258
293
|
|
package/dist/ai-tools-client.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @x12i/ai-tools invoke client bootstrap for the gateway.
|
|
3
3
|
* Model resolution orchestration lives in ai-tools ≥ 2.5.0 (`resolveInvokeModel`).
|
|
4
4
|
*/
|
|
5
|
-
import { getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, } from '@x12i/ai-tools';
|
|
5
|
+
import { getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, CostCalculator, } from '@x12i/ai-tools';
|
|
6
6
|
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
7
7
|
import { resolvePreferOpenRouter } from './openrouter-routing.js';
|
|
8
8
|
export { resolveInvokeModel, applyOpenRouterInvokePolicy, buildInvokeModelResolverOptions, enrichModelResolutionError, mapResolutionToRouterConfig, ModelProfileUnroutableError, ModelProfileInputRejectedError, MODEL_PROFILE_UNROUTABLE, getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, createAiToolsInvokeClient, } from '@x12i/ai-tools';
|
|
@@ -13,7 +13,22 @@ function invokeClientOptions(config) {
|
|
|
13
13
|
cacheTtlMs: config.aiTools?.cacheTtlMs,
|
|
14
14
|
...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {}),
|
|
15
15
|
...(config.aiTools?.costIncludeBreakdown ? { costIncludeBreakdown: true } : {}),
|
|
16
|
-
cacheKey: `${config.aiTools?.cacheTtlMs ?? ''}:${config.aiTools?.costIncludeBreakdown ?? ''}:${config.aiTools?.bundledOnly ?? ''}`,
|
|
16
|
+
cacheKey: `${config.aiTools?.cacheTtlMs ?? ''}:${config.aiTools?.costIncludeBreakdown ?? ''}:${config.aiTools?.bundledOnly ?? ''}:${config.aiTools?.catalogLane ?? ''}`,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function withCatalogLaneCalculator(client, config) {
|
|
20
|
+
const lane = config.aiTools?.catalogLane;
|
|
21
|
+
if (!lane)
|
|
22
|
+
return client;
|
|
23
|
+
return {
|
|
24
|
+
...client,
|
|
25
|
+
calculator: new CostCalculator(client.catalog, {
|
|
26
|
+
...(config.aiTools?.costIncludeBreakdown ? { includeBreakdown: true } : {}),
|
|
27
|
+
resolverOptions: buildInvokeModelResolverOptions({
|
|
28
|
+
routingEnv: client.routingEnv,
|
|
29
|
+
catalogLane: lane
|
|
30
|
+
})
|
|
31
|
+
})
|
|
17
32
|
};
|
|
18
33
|
}
|
|
19
34
|
/** @deprecated Use buildInvokeModelResolverOptions */
|
|
@@ -53,6 +68,7 @@ export async function getAiToolsClient(config, logger) {
|
|
|
53
68
|
logger.debug('ai-tools catalog client ready', {
|
|
54
69
|
debugKind: gatewayLogDebug.state,
|
|
55
70
|
});
|
|
71
|
+
return withCatalogLaneCalculator(client, config);
|
|
56
72
|
}
|
|
57
73
|
return client;
|
|
58
74
|
}
|
package/dist/gateway-config.js
CHANGED
|
@@ -176,17 +176,8 @@ export function initializeGatewayComponents(config) {
|
|
|
176
176
|
enableActivityTracking: config.enableActivityTracking ?? true,
|
|
177
177
|
customTracker: config.activityTracker,
|
|
178
178
|
logger,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
: {
|
|
182
|
-
autoCost: config.aiTools?.enabled === false || config.aiTools?.calculateCost === false
|
|
183
|
-
? false
|
|
184
|
-
: {
|
|
185
|
-
enabled: true,
|
|
186
|
-
overwriteOuterCost: false,
|
|
187
|
-
...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {})
|
|
188
|
-
}
|
|
189
|
-
})
|
|
179
|
+
// Billing is resolved in gateway before logSuccess; Activix gets outer.cost from that slice only.
|
|
180
|
+
...(config.activityTracker ? {} : { autoCost: false })
|
|
190
181
|
});
|
|
191
182
|
const templateRendering = mergeTemplateRenderOptions(defaultTemplateRendering, config.templateRendering);
|
|
192
183
|
const messageBuilderConfig = {
|
package/dist/gateway-utils.d.ts
CHANGED
|
@@ -72,8 +72,8 @@ export declare function hasNonZeroTokenUsage(tokens: {
|
|
|
72
72
|
total: number;
|
|
73
73
|
}): boolean;
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
76
|
-
* Prefer {@link
|
|
75
|
+
* Step A/C/D cost slice when the router omits explicit `metadata.costStatus`.
|
|
76
|
+
* Prefer {@link resolveCostCompletionWithAiTools} at invoke boundaries.
|
|
77
77
|
*/
|
|
78
78
|
export declare function resolveActivityCostCompletion(tokens: {
|
|
79
79
|
prompt: number;
|
|
@@ -81,8 +81,7 @@ export declare function resolveActivityCostCompletion(tokens: {
|
|
|
81
81
|
total: number;
|
|
82
82
|
}, costUsd: number | undefined): ResolvedActivityCost;
|
|
83
83
|
/**
|
|
84
|
-
*
|
|
85
|
-
* otherwise gateway applies the G8 fallback (usage + no price → `unpriced`).
|
|
84
|
+
* Step A router passthrough + Step C when the router omits `metadata.costStatus`.
|
|
86
85
|
*/
|
|
87
86
|
export declare function resolveCostCompletionForActivity(routerResponse: unknown, tokens: {
|
|
88
87
|
prompt: number;
|
|
@@ -94,7 +93,23 @@ export type ResolveCostCompletionOptions = {
|
|
|
94
93
|
calculator?: CostCalculator | null;
|
|
95
94
|
calculateCost?: boolean;
|
|
96
95
|
};
|
|
97
|
-
/**
|
|
96
|
+
/** Optional cache/reasoning token fields for catalog pricing records. */
|
|
97
|
+
export type InvokeUsageExtras = {
|
|
98
|
+
cached?: number;
|
|
99
|
+
cacheWrite?: number;
|
|
100
|
+
reasoning?: number;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Best-effort cache/reasoning token counts from router usage buckets
|
|
104
|
+
* (for {@link buildGatewayPricingRecord} / ai-tools {@link CostCalculator.calculateFromRecord}).
|
|
105
|
+
*/
|
|
106
|
+
export declare function extractUsageExtrasFromRouterResponse(routerResponse: unknown): InvokeUsageExtras;
|
|
107
|
+
/**
|
|
108
|
+
* Whether ai-tools catalog pricing is authoritative enough for Step B (`priced`).
|
|
109
|
+
* Matches the generic engine contract: authoritative catalog hit with finite cost ≥ 0.
|
|
110
|
+
*/
|
|
111
|
+
export declare function catalogPricingSucceeded(result: AiCostResult): boolean;
|
|
112
|
+
/** Record shape for {@link CostCalculator.calculateFromRecord} (shared engine contract). */
|
|
98
113
|
export declare function buildGatewayPricingRecord(routerResponse: unknown, tokens: {
|
|
99
114
|
prompt: number;
|
|
100
115
|
completion: number;
|
|
@@ -102,7 +117,8 @@ export declare function buildGatewayPricingRecord(routerResponse: unknown, token
|
|
|
102
117
|
}, mergedConfig?: unknown): Record<string, unknown>;
|
|
103
118
|
export declare function mapAiCostResultToResolvedActivityCost(base: ResolvedActivityCost, result: AiCostResult): ResolvedActivityCost;
|
|
104
119
|
/**
|
|
105
|
-
*
|
|
120
|
+
* Post-invoke billing (Steps A→D): router cost, then catalog via ai-tools when still unpriced.
|
|
121
|
+
* Single entry point for `invoke()` / `invokeChat()` and trace enrichment.
|
|
106
122
|
*/
|
|
107
123
|
export declare function resolveCostCompletionWithAiTools(routerResponse: unknown, tokens: {
|
|
108
124
|
prompt: number;
|
package/dist/gateway-utils.js
CHANGED
|
@@ -108,6 +108,8 @@ export async function mergeConfig(request, config, logger, mergeOptions) {
|
|
|
108
108
|
defaultProvider: config.defaultEngine,
|
|
109
109
|
resolveModels: true,
|
|
110
110
|
modelsOnly: config.aiTools?.modelsOnly !== false,
|
|
111
|
+
...(config.aiTools?.catalogLane ? { catalogLane: config.aiTools.catalogLane } : {}),
|
|
112
|
+
...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {}),
|
|
111
113
|
});
|
|
112
114
|
merged.provider = resolved.router.provider;
|
|
113
115
|
merged.model = resolved.router.model;
|
|
@@ -338,8 +340,8 @@ function pickRouterCostStatus(routerResponse) {
|
|
|
338
340
|
return status === 'priced' || status === 'unpriced' ? status : undefined;
|
|
339
341
|
}
|
|
340
342
|
/**
|
|
341
|
-
*
|
|
342
|
-
* Prefer {@link
|
|
343
|
+
* Step A/C/D cost slice when the router omits explicit `metadata.costStatus`.
|
|
344
|
+
* Prefer {@link resolveCostCompletionWithAiTools} at invoke boundaries.
|
|
343
345
|
*/
|
|
344
346
|
export function resolveActivityCostCompletion(tokens, costUsd) {
|
|
345
347
|
if (typeof costUsd === 'number' && Number.isFinite(costUsd)) {
|
|
@@ -351,8 +353,7 @@ export function resolveActivityCostCompletion(tokens, costUsd) {
|
|
|
351
353
|
return {};
|
|
352
354
|
}
|
|
353
355
|
/**
|
|
354
|
-
*
|
|
355
|
-
* otherwise gateway applies the G8 fallback (usage + no price → `unpriced`).
|
|
356
|
+
* Step A router passthrough + Step C when the router omits `metadata.costStatus`.
|
|
356
357
|
*/
|
|
357
358
|
export function resolveCostCompletionForActivity(routerResponse, tokens) {
|
|
358
359
|
const routerStatus = pickRouterCostStatus(routerResponse);
|
|
@@ -368,42 +369,119 @@ export function resolveCostCompletionForActivity(routerResponse, tokens) {
|
|
|
368
369
|
}
|
|
369
370
|
return resolveActivityCostCompletion(tokens, costUsd);
|
|
370
371
|
}
|
|
371
|
-
/**
|
|
372
|
+
/**
|
|
373
|
+
* Best-effort cache/reasoning token counts from router usage buckets
|
|
374
|
+
* (for {@link buildGatewayPricingRecord} / ai-tools {@link CostCalculator.calculateFromRecord}).
|
|
375
|
+
*/
|
|
376
|
+
export function extractUsageExtrasFromRouterResponse(routerResponse) {
|
|
377
|
+
if (routerResponse == null || typeof routerResponse !== 'object')
|
|
378
|
+
return {};
|
|
379
|
+
const r = routerResponse;
|
|
380
|
+
const roots = [r.usage];
|
|
381
|
+
const meta = r.metadata != null && typeof r.metadata === 'object'
|
|
382
|
+
? r.metadata
|
|
383
|
+
: undefined;
|
|
384
|
+
if (meta) {
|
|
385
|
+
roots.push(meta.usage, meta.tokens);
|
|
386
|
+
}
|
|
387
|
+
const raw = r.rawResponse ?? r.raw;
|
|
388
|
+
if (raw != null && typeof raw === 'object') {
|
|
389
|
+
roots.push(raw.usage);
|
|
390
|
+
}
|
|
391
|
+
const extras = {};
|
|
392
|
+
for (const bucket of roots) {
|
|
393
|
+
if (bucket == null || typeof bucket !== 'object')
|
|
394
|
+
continue;
|
|
395
|
+
const u = bucket;
|
|
396
|
+
const cached = firstFiniteNumber(u.cached, u.cached_tokens, u.cachedTokens, u.cache_read_tokens, u.cacheReadTokens);
|
|
397
|
+
const cacheWrite = firstFiniteNumber(u.cacheWrite, u.cache_write_tokens, u.cacheWriteTokens);
|
|
398
|
+
const reasoning = firstFiniteNumber(u.reasoning, u.reasoning_tokens, u.reasoningTokens);
|
|
399
|
+
if (cached !== undefined && extras.cached === undefined)
|
|
400
|
+
extras.cached = cached;
|
|
401
|
+
if (cacheWrite !== undefined && extras.cacheWrite === undefined)
|
|
402
|
+
extras.cacheWrite = cacheWrite;
|
|
403
|
+
if (reasoning !== undefined && extras.reasoning === undefined)
|
|
404
|
+
extras.reasoning = reasoning;
|
|
405
|
+
}
|
|
406
|
+
return extras;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Whether ai-tools catalog pricing is authoritative enough for Step B (`priced`).
|
|
410
|
+
* Matches the generic engine contract: authoritative catalog hit with finite cost ≥ 0.
|
|
411
|
+
*/
|
|
412
|
+
export function catalogPricingSucceeded(result) {
|
|
413
|
+
if (result.unknownModel)
|
|
414
|
+
return false;
|
|
415
|
+
if (!result.isAuthoritative)
|
|
416
|
+
return false;
|
|
417
|
+
if (result.source === 'estimate-fallback' || result.source === 'local')
|
|
418
|
+
return false;
|
|
419
|
+
if (typeof result.cost !== 'number' || !Number.isFinite(result.cost) || result.cost < 0) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
/** Record shape for {@link CostCalculator.calculateFromRecord} (shared engine contract). */
|
|
372
425
|
export function buildGatewayPricingRecord(routerResponse, tokens, mergedConfig) {
|
|
373
|
-
const base = routerResponse != null && typeof routerResponse === 'object'
|
|
374
|
-
? { ...routerResponse }
|
|
375
|
-
: {};
|
|
376
|
-
const meta = base.metadata != null && typeof base.metadata === 'object'
|
|
377
|
-
? { ...base.metadata }
|
|
378
|
-
: {};
|
|
379
426
|
const routing = pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig);
|
|
427
|
+
const cfg = mergedConfig != null && typeof mergedConfig === 'object'
|
|
428
|
+
? mergedConfig
|
|
429
|
+
: {};
|
|
430
|
+
const requestModel = typeof cfg.model === 'string'
|
|
431
|
+
? cfg.model
|
|
432
|
+
: typeof routing.modelUsed === 'string'
|
|
433
|
+
? routing.modelUsed
|
|
434
|
+
: undefined;
|
|
435
|
+
const modelUsed = routing.modelUsed ?? requestModel;
|
|
436
|
+
const provider = routing.provider ??
|
|
437
|
+
(typeof cfg.provider === 'string' ? cfg.provider : undefined) ??
|
|
438
|
+
'openrouter';
|
|
439
|
+
const usageExtras = extractUsageExtrasFromRouterResponse(routerResponse);
|
|
440
|
+
const tokenSlice = {
|
|
441
|
+
prompt: tokens.prompt,
|
|
442
|
+
completion: tokens.completion,
|
|
443
|
+
total: tokens.total,
|
|
444
|
+
...usageExtras
|
|
445
|
+
};
|
|
380
446
|
return {
|
|
381
|
-
|
|
447
|
+
model: modelUsed ?? requestModel ?? '',
|
|
448
|
+
...(requestModel && modelUsed && requestModel !== modelUsed
|
|
449
|
+
? { modelAlias: requestModel }
|
|
450
|
+
: {}),
|
|
451
|
+
...(modelUsed ? { modelUsed, usedModel: modelUsed } : {}),
|
|
452
|
+
provider,
|
|
453
|
+
...(provider || routing.region
|
|
454
|
+
? {
|
|
455
|
+
routing: {
|
|
456
|
+
provider,
|
|
457
|
+
...(routing.region ? { region: routing.region } : {})
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
: {}),
|
|
382
461
|
usage: {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
462
|
+
prompt_tokens: tokens.prompt,
|
|
463
|
+
completion_tokens: tokens.completion,
|
|
464
|
+
total_tokens: tokens.total,
|
|
465
|
+
...(usageExtras.cached !== undefined ? { cachedTokensPrompt: usageExtras.cached } : {}),
|
|
466
|
+
...(usageExtras.cached !== undefined ? { cachedTokensTotal: usageExtras.cached } : {})
|
|
386
467
|
},
|
|
387
|
-
tokens,
|
|
468
|
+
promptTokens: tokens.prompt,
|
|
469
|
+
completionTokens: tokens.completion,
|
|
470
|
+
totalTokens: tokens.total,
|
|
471
|
+
tokens: tokenSlice,
|
|
388
472
|
metadata: {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
...(routing.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
473
|
+
provider,
|
|
474
|
+
...(modelUsed ? { modelUsed, model: modelUsed } : {}),
|
|
475
|
+
...(routing.maxTokensRequested !== undefined
|
|
476
|
+
? { maxTokensRequested: routing.maxTokensRequested }
|
|
477
|
+
: {}),
|
|
478
|
+
tokens: tokenSlice
|
|
395
479
|
},
|
|
396
480
|
...(mergedConfig != null ? { config: mergedConfig } : {})
|
|
397
481
|
};
|
|
398
482
|
}
|
|
399
483
|
export function mapAiCostResultToResolvedActivityCost(base, result) {
|
|
400
|
-
if (result
|
|
401
|
-
return base.costStatus ? base : { ...base, costStatus: 'unpriced' };
|
|
402
|
-
}
|
|
403
|
-
if (typeof result.cost !== 'number' || !Number.isFinite(result.cost)) {
|
|
404
|
-
return base;
|
|
405
|
-
}
|
|
406
|
-
if (!result.isAuthoritative && result.source === 'estimate-fallback') {
|
|
484
|
+
if (!catalogPricingSucceeded(result)) {
|
|
407
485
|
return base.costStatus ? base : { ...base, costStatus: 'unpriced' };
|
|
408
486
|
}
|
|
409
487
|
return {
|
|
@@ -413,54 +491,36 @@ export function mapAiCostResultToResolvedActivityCost(base, result) {
|
|
|
413
491
|
};
|
|
414
492
|
}
|
|
415
493
|
/**
|
|
416
|
-
*
|
|
494
|
+
* Step C/D: token usage without billing signal → `unpriced`; no usage → omit status.
|
|
495
|
+
*/
|
|
496
|
+
function finalizeInvokeBillingCost(billing, tokens) {
|
|
497
|
+
if (!billing.costStatus && hasNonZeroTokenUsage(tokens)) {
|
|
498
|
+
return { ...billing, costStatus: 'unpriced' };
|
|
499
|
+
}
|
|
500
|
+
return billing;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Post-invoke billing (Steps A→D): router cost, then catalog via ai-tools when still unpriced.
|
|
504
|
+
* Single entry point for `invoke()` / `invokeChat()` and trace enrichment.
|
|
417
505
|
*/
|
|
418
506
|
export async function resolveCostCompletionWithAiTools(routerResponse, tokens, options) {
|
|
419
507
|
const routerStatus = pickRouterCostStatus(routerResponse);
|
|
420
|
-
|
|
421
|
-
if (
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
if (options?.calculateCost === false || !options?.calculator) {
|
|
428
|
-
return base;
|
|
429
|
-
}
|
|
430
|
-
if (!hasNonZeroTokenUsage(tokens)) {
|
|
431
|
-
return base;
|
|
432
|
-
}
|
|
433
|
-
try {
|
|
434
|
-
const record = buildGatewayPricingRecord(routerResponse, tokens, options.mergedConfig);
|
|
435
|
-
const result = await options.calculator.calculateFromRecord(record);
|
|
436
|
-
return mapAiCostResultToResolvedActivityCost(base, result);
|
|
437
|
-
}
|
|
438
|
-
catch {
|
|
439
|
-
const routing = pickInvokeRoutingMetadataSlice(routerResponse, options.mergedConfig);
|
|
440
|
-
const cfg = options.mergedConfig != null && typeof options.mergedConfig === 'object'
|
|
441
|
-
? options.mergedConfig
|
|
442
|
-
: {};
|
|
443
|
-
const provider = routing.provider ?? cfg.provider;
|
|
444
|
-
const modelUsed = routing.modelUsed ?? cfg.model;
|
|
445
|
-
if (!provider || !modelUsed) {
|
|
446
|
-
return base;
|
|
447
|
-
}
|
|
508
|
+
let billing = resolveCostCompletionForActivity(routerResponse, tokens);
|
|
509
|
+
if (billing.costStatus !== 'priced' &&
|
|
510
|
+
routerStatus !== 'unpriced' &&
|
|
511
|
+
options?.calculateCost !== false &&
|
|
512
|
+
options?.calculator &&
|
|
513
|
+
hasNonZeroTokenUsage(tokens)) {
|
|
448
514
|
try {
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
completion: tokens.completion,
|
|
453
|
-
total: tokens.total
|
|
454
|
-
},
|
|
455
|
-
provider,
|
|
456
|
-
usedModel: modelUsed
|
|
457
|
-
});
|
|
458
|
-
return mapAiCostResultToResolvedActivityCost(base, result);
|
|
515
|
+
const record = buildGatewayPricingRecord(routerResponse, tokens, options.mergedConfig);
|
|
516
|
+
const result = await options.calculator.calculateFromRecord(record);
|
|
517
|
+
billing = mapAiCostResultToResolvedActivityCost(billing, result);
|
|
459
518
|
}
|
|
460
519
|
catch {
|
|
461
|
-
|
|
520
|
+
// Step B unavailable — Step C applies below.
|
|
462
521
|
}
|
|
463
522
|
}
|
|
523
|
+
return finalizeInvokeBillingCost(billing, tokens);
|
|
464
524
|
}
|
|
465
525
|
function applyBillingToTraceAttempt(attempt, billing) {
|
|
466
526
|
if (billing.costStatus === 'priced' || billing.costStatus === 'unpriced') {
|
package/dist/gateway.js
CHANGED
|
@@ -11,7 +11,7 @@ import { resolveRetryConfig } from './gateway-defaults.js';
|
|
|
11
11
|
import { buildMessages } from './message-builder.js';
|
|
12
12
|
import { extractJsonFromFlexMd } from './flex-md-loader.js';
|
|
13
13
|
import { enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
|
|
14
|
-
import { attachGatewayInvokeRejectionMetadata, buildGatewayFallbackAttemptsFromTrace, buildInvokeRejectionMetadata, capActivityFullResponsePayload, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter,
|
|
14
|
+
import { attachGatewayInvokeRejectionMetadata, buildGatewayFallbackAttemptsFromTrace, buildInvokeRejectionMetadata, capActivityFullResponsePayload, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter, DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, extractCostUsdFromRouterResponse, extractTokenUsageFromRouterResponse, mergeConfig, pickEffectiveModelConfigForMetadata, pickInvokeRoutingMetadataSlice, pickTraceMergedRouterConfig, resolveCostCompletionWithAiTools, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, tryExtractRouterLikePayloadFromErrorChain } from './gateway-utils.js';
|
|
15
15
|
import { getAiToolsClient } from './ai-tools-client.js';
|
|
16
16
|
import { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
17
17
|
import { setGatewayLastJobId, setGatewayRuntimeClients } from './runtime-objects.js';
|
|
@@ -135,7 +135,7 @@ export class AIGateway {
|
|
|
135
135
|
});
|
|
136
136
|
const metaChat = response?.metadata || {};
|
|
137
137
|
const tokensChat = extractTokenUsageFromRouterResponse(response);
|
|
138
|
-
|
|
138
|
+
let costCompletionChat = await resolveCostCompletionWithAiTools(response, tokensChat, {
|
|
139
139
|
mergedConfig,
|
|
140
140
|
calculator: aiTools?.calculator ?? null,
|
|
141
141
|
calculateCost: this.config.aiTools?.calculateCost
|
|
@@ -614,9 +614,6 @@ export class AIGateway {
|
|
|
614
614
|
calculator: aiTools?.calculator ?? null,
|
|
615
615
|
calculateCost: this.config.aiTools?.calculateCost
|
|
616
616
|
});
|
|
617
|
-
if (!costCompletion.costStatus && hasNonZeroTokenUsage(tokens)) {
|
|
618
|
-
costCompletion = { ...costCompletion, costStatus: 'unpriced' };
|
|
619
|
-
}
|
|
620
617
|
const routerMetaForCost = routerResponse?.metadata || {};
|
|
621
618
|
const routingMetadataSlice = pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig);
|
|
622
619
|
const effectiveModelConfig = pickEffectiveModelConfigForMetadata(mergedConfig);
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { AIGateway } from './gateway.js';
|
|
|
17
17
|
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError } from './instruction-errors.js';
|
|
18
18
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
19
19
|
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
|
-
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
20
|
+
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
21
|
export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
|
|
22
22
|
export type { GatewayOperationalMode } from './gateway-mode.js';
|
|
23
23
|
export { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_RETRY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P, resolveRetryConfig } from './gateway-defaults.js';
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ export * from '@x12i/ai-providers-router';
|
|
|
17
17
|
export { AIGateway } from './gateway.js';
|
|
18
18
|
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError } from './instruction-errors.js';
|
|
19
19
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
20
|
-
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
20
|
+
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
21
|
export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
|
|
22
22
|
export { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_RETRY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P, resolveRetryConfig } from './gateway-defaults.js';
|
|
23
23
|
export { contractSpecToFieldKeys, enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ type AIModel = string;
|
|
|
9
9
|
export type UsageTier = string;
|
|
10
10
|
import type { Activix } from '@x12i/activix';
|
|
11
11
|
import type { SmartInputConfig, SmartInputRenderOptions, TemplateRenderOptions } from '@x12i/rendrix';
|
|
12
|
+
import type { ProfileCatalogLane } from '@x12i/ai-profiles';
|
|
12
13
|
import type { Logxer, PackageLogLevelsConfig } from '@x12i/logxer';
|
|
13
14
|
/**
|
|
14
15
|
* Diagnostics options for opt-in authoritative tracing.
|
|
@@ -415,6 +416,11 @@ export interface GatewayConfig extends Omit<RouterConfig, 'defaultEngine' | 'log
|
|
|
415
416
|
cacheTtlMs?: number;
|
|
416
417
|
/** Use bundled catalog JSON only (offline / tests). */
|
|
417
418
|
bundledOnly?: boolean;
|
|
419
|
+
/**
|
|
420
|
+
* Catalog lane for model resolution and cost lookup (`text`, `image`, …).
|
|
421
|
+
* @default `"text"` in ai-tools when omitted.
|
|
422
|
+
*/
|
|
423
|
+
catalogLane?: ProfileCatalogLane;
|
|
418
424
|
/** @default true */
|
|
419
425
|
resolveModels?: boolean;
|
|
420
426
|
/**
|
|
@@ -424,6 +430,7 @@ export interface GatewayConfig extends Omit<RouterConfig, 'defaultEngine' | 'log
|
|
|
424
430
|
modelsOnly?: boolean;
|
|
425
431
|
/** @default true */
|
|
426
432
|
calculateCost?: boolean;
|
|
433
|
+
/** @default false — when true, priced results may include prompt/completion breakdown. */
|
|
427
434
|
costIncludeBreakdown?: boolean;
|
|
428
435
|
};
|
|
429
436
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @x12i/ai-tools invoke client bootstrap for the gateway.
|
|
3
3
|
* Model resolution orchestration lives in ai-tools ≥ 2.5.0 (`resolveInvokeModel`).
|
|
4
4
|
*/
|
|
5
|
-
import { getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, } from '@x12i/ai-tools';
|
|
5
|
+
import { getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, CostCalculator, } from '@x12i/ai-tools';
|
|
6
6
|
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
7
7
|
import { resolvePreferOpenRouter } from './openrouter-routing.js';
|
|
8
8
|
export { resolveInvokeModel, applyOpenRouterInvokePolicy, buildInvokeModelResolverOptions, enrichModelResolutionError, mapResolutionToRouterConfig, ModelProfileUnroutableError, ModelProfileInputRejectedError, MODEL_PROFILE_UNROUTABLE, getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, createAiToolsInvokeClient, } from '@x12i/ai-tools';
|
|
@@ -13,7 +13,22 @@ function invokeClientOptions(config) {
|
|
|
13
13
|
cacheTtlMs: config.aiTools?.cacheTtlMs,
|
|
14
14
|
...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {}),
|
|
15
15
|
...(config.aiTools?.costIncludeBreakdown ? { costIncludeBreakdown: true } : {}),
|
|
16
|
-
cacheKey: `${config.aiTools?.cacheTtlMs ?? ''}:${config.aiTools?.costIncludeBreakdown ?? ''}:${config.aiTools?.bundledOnly ?? ''}`,
|
|
16
|
+
cacheKey: `${config.aiTools?.cacheTtlMs ?? ''}:${config.aiTools?.costIncludeBreakdown ?? ''}:${config.aiTools?.bundledOnly ?? ''}:${config.aiTools?.catalogLane ?? ''}`,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function withCatalogLaneCalculator(client, config) {
|
|
20
|
+
const lane = config.aiTools?.catalogLane;
|
|
21
|
+
if (!lane)
|
|
22
|
+
return client;
|
|
23
|
+
return {
|
|
24
|
+
...client,
|
|
25
|
+
calculator: new CostCalculator(client.catalog, {
|
|
26
|
+
...(config.aiTools?.costIncludeBreakdown ? { includeBreakdown: true } : {}),
|
|
27
|
+
resolverOptions: buildInvokeModelResolverOptions({
|
|
28
|
+
routingEnv: client.routingEnv,
|
|
29
|
+
catalogLane: lane
|
|
30
|
+
})
|
|
31
|
+
})
|
|
17
32
|
};
|
|
18
33
|
}
|
|
19
34
|
/** @deprecated Use buildInvokeModelResolverOptions */
|
|
@@ -53,6 +68,7 @@ export async function getAiToolsClient(config, logger) {
|
|
|
53
68
|
logger.debug('ai-tools catalog client ready', {
|
|
54
69
|
debugKind: gatewayLogDebug.state,
|
|
55
70
|
});
|
|
71
|
+
return withCatalogLaneCalculator(client, config);
|
|
56
72
|
}
|
|
57
73
|
return client;
|
|
58
74
|
}
|
|
@@ -176,17 +176,8 @@ export function initializeGatewayComponents(config) {
|
|
|
176
176
|
enableActivityTracking: config.enableActivityTracking ?? true,
|
|
177
177
|
customTracker: config.activityTracker,
|
|
178
178
|
logger,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
: {
|
|
182
|
-
autoCost: config.aiTools?.enabled === false || config.aiTools?.calculateCost === false
|
|
183
|
-
? false
|
|
184
|
-
: {
|
|
185
|
-
enabled: true,
|
|
186
|
-
overwriteOuterCost: false,
|
|
187
|
-
...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {})
|
|
188
|
-
}
|
|
189
|
-
})
|
|
179
|
+
// Billing is resolved in gateway before logSuccess; Activix gets outer.cost from that slice only.
|
|
180
|
+
...(config.activityTracker ? {} : { autoCost: false })
|
|
190
181
|
});
|
|
191
182
|
const templateRendering = mergeTemplateRenderOptions(defaultTemplateRendering, config.templateRendering);
|
|
192
183
|
const messageBuilderConfig = {
|
|
@@ -108,6 +108,8 @@ export async function mergeConfig(request, config, logger, mergeOptions) {
|
|
|
108
108
|
defaultProvider: config.defaultEngine,
|
|
109
109
|
resolveModels: true,
|
|
110
110
|
modelsOnly: config.aiTools?.modelsOnly !== false,
|
|
111
|
+
...(config.aiTools?.catalogLane ? { catalogLane: config.aiTools.catalogLane } : {}),
|
|
112
|
+
...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {}),
|
|
111
113
|
});
|
|
112
114
|
merged.provider = resolved.router.provider;
|
|
113
115
|
merged.model = resolved.router.model;
|
|
@@ -338,8 +340,8 @@ function pickRouterCostStatus(routerResponse) {
|
|
|
338
340
|
return status === 'priced' || status === 'unpriced' ? status : undefined;
|
|
339
341
|
}
|
|
340
342
|
/**
|
|
341
|
-
*
|
|
342
|
-
* Prefer {@link
|
|
343
|
+
* Step A/C/D cost slice when the router omits explicit `metadata.costStatus`.
|
|
344
|
+
* Prefer {@link resolveCostCompletionWithAiTools} at invoke boundaries.
|
|
343
345
|
*/
|
|
344
346
|
export function resolveActivityCostCompletion(tokens, costUsd) {
|
|
345
347
|
if (typeof costUsd === 'number' && Number.isFinite(costUsd)) {
|
|
@@ -351,8 +353,7 @@ export function resolveActivityCostCompletion(tokens, costUsd) {
|
|
|
351
353
|
return {};
|
|
352
354
|
}
|
|
353
355
|
/**
|
|
354
|
-
*
|
|
355
|
-
* otherwise gateway applies the G8 fallback (usage + no price → `unpriced`).
|
|
356
|
+
* Step A router passthrough + Step C when the router omits `metadata.costStatus`.
|
|
356
357
|
*/
|
|
357
358
|
export function resolveCostCompletionForActivity(routerResponse, tokens) {
|
|
358
359
|
const routerStatus = pickRouterCostStatus(routerResponse);
|
|
@@ -368,42 +369,119 @@ export function resolveCostCompletionForActivity(routerResponse, tokens) {
|
|
|
368
369
|
}
|
|
369
370
|
return resolveActivityCostCompletion(tokens, costUsd);
|
|
370
371
|
}
|
|
371
|
-
/**
|
|
372
|
+
/**
|
|
373
|
+
* Best-effort cache/reasoning token counts from router usage buckets
|
|
374
|
+
* (for {@link buildGatewayPricingRecord} / ai-tools {@link CostCalculator.calculateFromRecord}).
|
|
375
|
+
*/
|
|
376
|
+
export function extractUsageExtrasFromRouterResponse(routerResponse) {
|
|
377
|
+
if (routerResponse == null || typeof routerResponse !== 'object')
|
|
378
|
+
return {};
|
|
379
|
+
const r = routerResponse;
|
|
380
|
+
const roots = [r.usage];
|
|
381
|
+
const meta = r.metadata != null && typeof r.metadata === 'object'
|
|
382
|
+
? r.metadata
|
|
383
|
+
: undefined;
|
|
384
|
+
if (meta) {
|
|
385
|
+
roots.push(meta.usage, meta.tokens);
|
|
386
|
+
}
|
|
387
|
+
const raw = r.rawResponse ?? r.raw;
|
|
388
|
+
if (raw != null && typeof raw === 'object') {
|
|
389
|
+
roots.push(raw.usage);
|
|
390
|
+
}
|
|
391
|
+
const extras = {};
|
|
392
|
+
for (const bucket of roots) {
|
|
393
|
+
if (bucket == null || typeof bucket !== 'object')
|
|
394
|
+
continue;
|
|
395
|
+
const u = bucket;
|
|
396
|
+
const cached = firstFiniteNumber(u.cached, u.cached_tokens, u.cachedTokens, u.cache_read_tokens, u.cacheReadTokens);
|
|
397
|
+
const cacheWrite = firstFiniteNumber(u.cacheWrite, u.cache_write_tokens, u.cacheWriteTokens);
|
|
398
|
+
const reasoning = firstFiniteNumber(u.reasoning, u.reasoning_tokens, u.reasoningTokens);
|
|
399
|
+
if (cached !== undefined && extras.cached === undefined)
|
|
400
|
+
extras.cached = cached;
|
|
401
|
+
if (cacheWrite !== undefined && extras.cacheWrite === undefined)
|
|
402
|
+
extras.cacheWrite = cacheWrite;
|
|
403
|
+
if (reasoning !== undefined && extras.reasoning === undefined)
|
|
404
|
+
extras.reasoning = reasoning;
|
|
405
|
+
}
|
|
406
|
+
return extras;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Whether ai-tools catalog pricing is authoritative enough for Step B (`priced`).
|
|
410
|
+
* Matches the generic engine contract: authoritative catalog hit with finite cost ≥ 0.
|
|
411
|
+
*/
|
|
412
|
+
export function catalogPricingSucceeded(result) {
|
|
413
|
+
if (result.unknownModel)
|
|
414
|
+
return false;
|
|
415
|
+
if (!result.isAuthoritative)
|
|
416
|
+
return false;
|
|
417
|
+
if (result.source === 'estimate-fallback' || result.source === 'local')
|
|
418
|
+
return false;
|
|
419
|
+
if (typeof result.cost !== 'number' || !Number.isFinite(result.cost) || result.cost < 0) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
/** Record shape for {@link CostCalculator.calculateFromRecord} (shared engine contract). */
|
|
372
425
|
export function buildGatewayPricingRecord(routerResponse, tokens, mergedConfig) {
|
|
373
|
-
const base = routerResponse != null && typeof routerResponse === 'object'
|
|
374
|
-
? { ...routerResponse }
|
|
375
|
-
: {};
|
|
376
|
-
const meta = base.metadata != null && typeof base.metadata === 'object'
|
|
377
|
-
? { ...base.metadata }
|
|
378
|
-
: {};
|
|
379
426
|
const routing = pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig);
|
|
427
|
+
const cfg = mergedConfig != null && typeof mergedConfig === 'object'
|
|
428
|
+
? mergedConfig
|
|
429
|
+
: {};
|
|
430
|
+
const requestModel = typeof cfg.model === 'string'
|
|
431
|
+
? cfg.model
|
|
432
|
+
: typeof routing.modelUsed === 'string'
|
|
433
|
+
? routing.modelUsed
|
|
434
|
+
: undefined;
|
|
435
|
+
const modelUsed = routing.modelUsed ?? requestModel;
|
|
436
|
+
const provider = routing.provider ??
|
|
437
|
+
(typeof cfg.provider === 'string' ? cfg.provider : undefined) ??
|
|
438
|
+
'openrouter';
|
|
439
|
+
const usageExtras = extractUsageExtrasFromRouterResponse(routerResponse);
|
|
440
|
+
const tokenSlice = {
|
|
441
|
+
prompt: tokens.prompt,
|
|
442
|
+
completion: tokens.completion,
|
|
443
|
+
total: tokens.total,
|
|
444
|
+
...usageExtras
|
|
445
|
+
};
|
|
380
446
|
return {
|
|
381
|
-
|
|
447
|
+
model: modelUsed ?? requestModel ?? '',
|
|
448
|
+
...(requestModel && modelUsed && requestModel !== modelUsed
|
|
449
|
+
? { modelAlias: requestModel }
|
|
450
|
+
: {}),
|
|
451
|
+
...(modelUsed ? { modelUsed, usedModel: modelUsed } : {}),
|
|
452
|
+
provider,
|
|
453
|
+
...(provider || routing.region
|
|
454
|
+
? {
|
|
455
|
+
routing: {
|
|
456
|
+
provider,
|
|
457
|
+
...(routing.region ? { region: routing.region } : {})
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
: {}),
|
|
382
461
|
usage: {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
462
|
+
prompt_tokens: tokens.prompt,
|
|
463
|
+
completion_tokens: tokens.completion,
|
|
464
|
+
total_tokens: tokens.total,
|
|
465
|
+
...(usageExtras.cached !== undefined ? { cachedTokensPrompt: usageExtras.cached } : {}),
|
|
466
|
+
...(usageExtras.cached !== undefined ? { cachedTokensTotal: usageExtras.cached } : {})
|
|
386
467
|
},
|
|
387
|
-
tokens,
|
|
468
|
+
promptTokens: tokens.prompt,
|
|
469
|
+
completionTokens: tokens.completion,
|
|
470
|
+
totalTokens: tokens.total,
|
|
471
|
+
tokens: tokenSlice,
|
|
388
472
|
metadata: {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
...(routing.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
473
|
+
provider,
|
|
474
|
+
...(modelUsed ? { modelUsed, model: modelUsed } : {}),
|
|
475
|
+
...(routing.maxTokensRequested !== undefined
|
|
476
|
+
? { maxTokensRequested: routing.maxTokensRequested }
|
|
477
|
+
: {}),
|
|
478
|
+
tokens: tokenSlice
|
|
395
479
|
},
|
|
396
480
|
...(mergedConfig != null ? { config: mergedConfig } : {})
|
|
397
481
|
};
|
|
398
482
|
}
|
|
399
483
|
export function mapAiCostResultToResolvedActivityCost(base, result) {
|
|
400
|
-
if (result
|
|
401
|
-
return base.costStatus ? base : { ...base, costStatus: 'unpriced' };
|
|
402
|
-
}
|
|
403
|
-
if (typeof result.cost !== 'number' || !Number.isFinite(result.cost)) {
|
|
404
|
-
return base;
|
|
405
|
-
}
|
|
406
|
-
if (!result.isAuthoritative && result.source === 'estimate-fallback') {
|
|
484
|
+
if (!catalogPricingSucceeded(result)) {
|
|
407
485
|
return base.costStatus ? base : { ...base, costStatus: 'unpriced' };
|
|
408
486
|
}
|
|
409
487
|
return {
|
|
@@ -413,54 +491,36 @@ export function mapAiCostResultToResolvedActivityCost(base, result) {
|
|
|
413
491
|
};
|
|
414
492
|
}
|
|
415
493
|
/**
|
|
416
|
-
*
|
|
494
|
+
* Step C/D: token usage without billing signal → `unpriced`; no usage → omit status.
|
|
495
|
+
*/
|
|
496
|
+
function finalizeInvokeBillingCost(billing, tokens) {
|
|
497
|
+
if (!billing.costStatus && hasNonZeroTokenUsage(tokens)) {
|
|
498
|
+
return { ...billing, costStatus: 'unpriced' };
|
|
499
|
+
}
|
|
500
|
+
return billing;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Post-invoke billing (Steps A→D): router cost, then catalog via ai-tools when still unpriced.
|
|
504
|
+
* Single entry point for `invoke()` / `invokeChat()` and trace enrichment.
|
|
417
505
|
*/
|
|
418
506
|
export async function resolveCostCompletionWithAiTools(routerResponse, tokens, options) {
|
|
419
507
|
const routerStatus = pickRouterCostStatus(routerResponse);
|
|
420
|
-
|
|
421
|
-
if (
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
if (options?.calculateCost === false || !options?.calculator) {
|
|
428
|
-
return base;
|
|
429
|
-
}
|
|
430
|
-
if (!hasNonZeroTokenUsage(tokens)) {
|
|
431
|
-
return base;
|
|
432
|
-
}
|
|
433
|
-
try {
|
|
434
|
-
const record = buildGatewayPricingRecord(routerResponse, tokens, options.mergedConfig);
|
|
435
|
-
const result = await options.calculator.calculateFromRecord(record);
|
|
436
|
-
return mapAiCostResultToResolvedActivityCost(base, result);
|
|
437
|
-
}
|
|
438
|
-
catch {
|
|
439
|
-
const routing = pickInvokeRoutingMetadataSlice(routerResponse, options.mergedConfig);
|
|
440
|
-
const cfg = options.mergedConfig != null && typeof options.mergedConfig === 'object'
|
|
441
|
-
? options.mergedConfig
|
|
442
|
-
: {};
|
|
443
|
-
const provider = routing.provider ?? cfg.provider;
|
|
444
|
-
const modelUsed = routing.modelUsed ?? cfg.model;
|
|
445
|
-
if (!provider || !modelUsed) {
|
|
446
|
-
return base;
|
|
447
|
-
}
|
|
508
|
+
let billing = resolveCostCompletionForActivity(routerResponse, tokens);
|
|
509
|
+
if (billing.costStatus !== 'priced' &&
|
|
510
|
+
routerStatus !== 'unpriced' &&
|
|
511
|
+
options?.calculateCost !== false &&
|
|
512
|
+
options?.calculator &&
|
|
513
|
+
hasNonZeroTokenUsage(tokens)) {
|
|
448
514
|
try {
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
completion: tokens.completion,
|
|
453
|
-
total: tokens.total
|
|
454
|
-
},
|
|
455
|
-
provider,
|
|
456
|
-
usedModel: modelUsed
|
|
457
|
-
});
|
|
458
|
-
return mapAiCostResultToResolvedActivityCost(base, result);
|
|
515
|
+
const record = buildGatewayPricingRecord(routerResponse, tokens, options.mergedConfig);
|
|
516
|
+
const result = await options.calculator.calculateFromRecord(record);
|
|
517
|
+
billing = mapAiCostResultToResolvedActivityCost(billing, result);
|
|
459
518
|
}
|
|
460
519
|
catch {
|
|
461
|
-
|
|
520
|
+
// Step B unavailable — Step C applies below.
|
|
462
521
|
}
|
|
463
522
|
}
|
|
523
|
+
return finalizeInvokeBillingCost(billing, tokens);
|
|
464
524
|
}
|
|
465
525
|
function applyBillingToTraceAttempt(attempt, billing) {
|
|
466
526
|
if (billing.costStatus === 'priced' || billing.costStatus === 'unpriced') {
|
|
@@ -72,8 +72,8 @@ export declare function hasNonZeroTokenUsage(tokens: {
|
|
|
72
72
|
total: number;
|
|
73
73
|
}): boolean;
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
76
|
-
* Prefer {@link
|
|
75
|
+
* Step A/C/D cost slice when the router omits explicit `metadata.costStatus`.
|
|
76
|
+
* Prefer {@link resolveCostCompletionWithAiTools} at invoke boundaries.
|
|
77
77
|
*/
|
|
78
78
|
export declare function resolveActivityCostCompletion(tokens: {
|
|
79
79
|
prompt: number;
|
|
@@ -81,8 +81,7 @@ export declare function resolveActivityCostCompletion(tokens: {
|
|
|
81
81
|
total: number;
|
|
82
82
|
}, costUsd: number | undefined): ResolvedActivityCost;
|
|
83
83
|
/**
|
|
84
|
-
*
|
|
85
|
-
* otherwise gateway applies the G8 fallback (usage + no price → `unpriced`).
|
|
84
|
+
* Step A router passthrough + Step C when the router omits `metadata.costStatus`.
|
|
86
85
|
*/
|
|
87
86
|
export declare function resolveCostCompletionForActivity(routerResponse: unknown, tokens: {
|
|
88
87
|
prompt: number;
|
|
@@ -94,7 +93,23 @@ export type ResolveCostCompletionOptions = {
|
|
|
94
93
|
calculator?: CostCalculator | null;
|
|
95
94
|
calculateCost?: boolean;
|
|
96
95
|
};
|
|
97
|
-
/**
|
|
96
|
+
/** Optional cache/reasoning token fields for catalog pricing records. */
|
|
97
|
+
export type InvokeUsageExtras = {
|
|
98
|
+
cached?: number;
|
|
99
|
+
cacheWrite?: number;
|
|
100
|
+
reasoning?: number;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Best-effort cache/reasoning token counts from router usage buckets
|
|
104
|
+
* (for {@link buildGatewayPricingRecord} / ai-tools {@link CostCalculator.calculateFromRecord}).
|
|
105
|
+
*/
|
|
106
|
+
export declare function extractUsageExtrasFromRouterResponse(routerResponse: unknown): InvokeUsageExtras;
|
|
107
|
+
/**
|
|
108
|
+
* Whether ai-tools catalog pricing is authoritative enough for Step B (`priced`).
|
|
109
|
+
* Matches the generic engine contract: authoritative catalog hit with finite cost ≥ 0.
|
|
110
|
+
*/
|
|
111
|
+
export declare function catalogPricingSucceeded(result: AiCostResult): boolean;
|
|
112
|
+
/** Record shape for {@link CostCalculator.calculateFromRecord} (shared engine contract). */
|
|
98
113
|
export declare function buildGatewayPricingRecord(routerResponse: unknown, tokens: {
|
|
99
114
|
prompt: number;
|
|
100
115
|
completion: number;
|
|
@@ -102,7 +117,8 @@ export declare function buildGatewayPricingRecord(routerResponse: unknown, token
|
|
|
102
117
|
}, mergedConfig?: unknown): Record<string, unknown>;
|
|
103
118
|
export declare function mapAiCostResultToResolvedActivityCost(base: ResolvedActivityCost, result: AiCostResult): ResolvedActivityCost;
|
|
104
119
|
/**
|
|
105
|
-
*
|
|
120
|
+
* Post-invoke billing (Steps A→D): router cost, then catalog via ai-tools when still unpriced.
|
|
121
|
+
* Single entry point for `invoke()` / `invokeChat()` and trace enrichment.
|
|
106
122
|
*/
|
|
107
123
|
export declare function resolveCostCompletionWithAiTools(routerResponse: unknown, tokens: {
|
|
108
124
|
prompt: number;
|
package/dist-cjs/gateway.cjs
CHANGED
|
@@ -11,7 +11,7 @@ import { resolveRetryConfig } from './gateway-defaults.js';
|
|
|
11
11
|
import { buildMessages } from './message-builder.js';
|
|
12
12
|
import { extractJsonFromFlexMd } from './flex-md-loader.js';
|
|
13
13
|
import { enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
|
|
14
|
-
import { attachGatewayInvokeRejectionMetadata, buildGatewayFallbackAttemptsFromTrace, buildInvokeRejectionMetadata, capActivityFullResponsePayload, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter,
|
|
14
|
+
import { attachGatewayInvokeRejectionMetadata, buildGatewayFallbackAttemptsFromTrace, buildInvokeRejectionMetadata, capActivityFullResponsePayload, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter, DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, extractCostUsdFromRouterResponse, extractTokenUsageFromRouterResponse, mergeConfig, pickEffectiveModelConfigForMetadata, pickInvokeRoutingMetadataSlice, pickTraceMergedRouterConfig, resolveCostCompletionWithAiTools, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, tryExtractRouterLikePayloadFromErrorChain } from './gateway-utils.js';
|
|
15
15
|
import { getAiToolsClient } from './ai-tools-client.js';
|
|
16
16
|
import { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
17
17
|
import { setGatewayLastJobId, setGatewayRuntimeClients } from './runtime-objects.js';
|
|
@@ -135,7 +135,7 @@ export class AIGateway {
|
|
|
135
135
|
});
|
|
136
136
|
const metaChat = response?.metadata || {};
|
|
137
137
|
const tokensChat = extractTokenUsageFromRouterResponse(response);
|
|
138
|
-
|
|
138
|
+
let costCompletionChat = await resolveCostCompletionWithAiTools(response, tokensChat, {
|
|
139
139
|
mergedConfig,
|
|
140
140
|
calculator: aiTools?.calculator ?? null,
|
|
141
141
|
calculateCost: this.config.aiTools?.calculateCost
|
|
@@ -614,9 +614,6 @@ export class AIGateway {
|
|
|
614
614
|
calculator: aiTools?.calculator ?? null,
|
|
615
615
|
calculateCost: this.config.aiTools?.calculateCost
|
|
616
616
|
});
|
|
617
|
-
if (!costCompletion.costStatus && hasNonZeroTokenUsage(tokens)) {
|
|
618
|
-
costCompletion = { ...costCompletion, costStatus: 'unpriced' };
|
|
619
|
-
}
|
|
620
617
|
const routerMetaForCost = routerResponse?.metadata || {};
|
|
621
618
|
const routingMetadataSlice = pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig);
|
|
622
619
|
const effectiveModelConfig = pickEffectiveModelConfigForMetadata(mergedConfig);
|
package/dist-cjs/index.cjs
CHANGED
|
@@ -17,7 +17,7 @@ export * from '@x12i/ai-providers-router';
|
|
|
17
17
|
export { AIGateway } from './gateway.js';
|
|
18
18
|
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError } from './instruction-errors.js';
|
|
19
19
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
20
|
-
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
20
|
+
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
21
|
export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
|
|
22
22
|
export { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_RETRY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P, resolveRetryConfig } from './gateway-defaults.js';
|
|
23
23
|
export { contractSpecToFieldKeys, enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
|
package/dist-cjs/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { AIGateway } from './gateway.js';
|
|
|
17
17
|
export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError, MaxTokensRequiredError } from './instruction-errors.js';
|
|
18
18
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
19
19
|
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
|
-
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
20
|
+
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
21
|
export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
|
|
22
22
|
export type { GatewayOperationalMode } from './gateway-mode.js';
|
|
23
23
|
export { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_RETRY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P, resolveRetryConfig } from './gateway-defaults.js';
|
package/dist-cjs/types.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ type AIModel = string;
|
|
|
9
9
|
export type UsageTier = string;
|
|
10
10
|
import type { Activix } from '@x12i/activix';
|
|
11
11
|
import type { SmartInputConfig, SmartInputRenderOptions, TemplateRenderOptions } from '@x12i/rendrix';
|
|
12
|
+
import type { ProfileCatalogLane } from '@x12i/ai-profiles';
|
|
12
13
|
import type { Logxer, PackageLogLevelsConfig } from '@x12i/logxer';
|
|
13
14
|
/**
|
|
14
15
|
* Diagnostics options for opt-in authoritative tracing.
|
|
@@ -415,6 +416,11 @@ export interface GatewayConfig extends Omit<RouterConfig, 'defaultEngine' | 'log
|
|
|
415
416
|
cacheTtlMs?: number;
|
|
416
417
|
/** Use bundled catalog JSON only (offline / tests). */
|
|
417
418
|
bundledOnly?: boolean;
|
|
419
|
+
/**
|
|
420
|
+
* Catalog lane for model resolution and cost lookup (`text`, `image`, …).
|
|
421
|
+
* @default `"text"` in ai-tools when omitted.
|
|
422
|
+
*/
|
|
423
|
+
catalogLane?: ProfileCatalogLane;
|
|
418
424
|
/** @default true */
|
|
419
425
|
resolveModels?: boolean;
|
|
420
426
|
/**
|
|
@@ -424,6 +430,7 @@ export interface GatewayConfig extends Omit<RouterConfig, 'defaultEngine' | 'log
|
|
|
424
430
|
modelsOnly?: boolean;
|
|
425
431
|
/** @default true */
|
|
426
432
|
calculateCost?: boolean;
|
|
433
|
+
/** @default false — when true, priced results may include prompt/completion breakdown. */
|
|
427
434
|
costIncludeBreakdown?: boolean;
|
|
428
435
|
};
|
|
429
436
|
/**
|