@x12i/ai-gateway 9.0.7 → 9.0.8

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.
@@ -19,3 +19,20 @@ export declare function ensureTaskTypeId(request: ChatRequest, logger: Logxer):
19
19
  export declare function mergeConfig(request: ChatRequest & {
20
20
  useInternalDefaults?: 'skill' | 'audit';
21
21
  }, config: GatewayConfig, logger: Logxer): Promise<ChatRequest['config']>;
22
+ /**
23
+ * Maps provider/router usage objects to gateway token counts (`metadata.tokens`, Activix, trace attempts).
24
+ * Handles promptTokens/inputTokens, OpenAI-style snake_case, and missing total (sum prompt+completion).
25
+ */
26
+ export declare function normalizeRouterUsageTokens(usage: unknown): {
27
+ prompt: number;
28
+ completion: number;
29
+ total: number;
30
+ } | undefined;
31
+ /**
32
+ * Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
33
+ */
34
+ export declare function extractTokenUsageFromRouterResponse(routerResponse: unknown): {
35
+ prompt: number;
36
+ completion: number;
37
+ total: number;
38
+ };
@@ -179,3 +179,55 @@ export async function mergeConfig(request, config, logger) {
179
179
  });
180
180
  return merged;
181
181
  }
182
+ function firstFiniteNumber(...vals) {
183
+ for (const v of vals) {
184
+ if (typeof v === 'number' && Number.isFinite(v))
185
+ return v;
186
+ }
187
+ return undefined;
188
+ }
189
+ /**
190
+ * Maps provider/router usage objects to gateway token counts (`metadata.tokens`, Activix, trace attempts).
191
+ * Handles promptTokens/inputTokens, OpenAI-style snake_case, and missing total (sum prompt+completion).
192
+ */
193
+ export function normalizeRouterUsageTokens(usage) {
194
+ if (usage == null || typeof usage !== 'object')
195
+ return undefined;
196
+ const u = usage;
197
+ const prompt = firstFiniteNumber(u.promptTokens, u.inputTokens, u.prompt, u.prompt_tokens) ?? 0;
198
+ const completion = firstFiniteNumber(u.completionTokens, u.outputTokens, u.completion, u.completion_tokens) ?? 0;
199
+ let total = firstFiniteNumber(u.totalTokens, u.total_tokens) ?? 0;
200
+ if (!total && (prompt || completion))
201
+ total = prompt + completion;
202
+ return { prompt, completion, total };
203
+ }
204
+ /**
205
+ * Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
206
+ */
207
+ export function extractTokenUsageFromRouterResponse(routerResponse) {
208
+ if (routerResponse == null || typeof routerResponse !== 'object') {
209
+ return { prompt: 0, completion: 0, total: 0 };
210
+ }
211
+ const r = routerResponse;
212
+ const meta = r.metadata != null && typeof r.metadata === 'object'
213
+ ? r.metadata
214
+ : undefined;
215
+ const buckets = [r.usage];
216
+ if (meta) {
217
+ buckets.push(meta.usage);
218
+ const nested = meta['ai-activities-response'];
219
+ if (nested != null && typeof nested === 'object') {
220
+ buckets.push(nested.usage);
221
+ }
222
+ }
223
+ const raw = r.rawResponse ?? r.raw;
224
+ if (raw != null && typeof raw === 'object') {
225
+ buckets.push(raw.usage);
226
+ }
227
+ for (const b of buckets) {
228
+ const n = normalizeRouterUsageTokens(b);
229
+ if (n && (n.prompt || n.completion || n.total))
230
+ return n;
231
+ }
232
+ return { prompt: 0, completion: 0, total: 0 };
233
+ }
package/dist/gateway.js CHANGED
@@ -8,7 +8,7 @@ import { ensureGatewayRequestIdentity } from './activity-manager.js';
8
8
  import { initializeGatewayComponents } from './gateway-config.js';
9
9
  import { buildMessages } from './message-builder.js';
10
10
  import { extractJsonFromFlexMd } from './flex-md-loader.js';
11
- import { mergeConfig } from './gateway-utils.js';
11
+ import { extractTokenUsageFromRouterResponse, mergeConfig } from './gateway-utils.js';
12
12
  import { autoRegisterProviders } from './gateway-provider-auto-register.js';
13
13
  import { setGatewayLastJobId, setGatewayRuntimeClients } from './runtime-objects.js';
14
14
  import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
@@ -94,7 +94,7 @@ export class AIGateway {
94
94
  aiRequestId: request.aiRequestId,
95
95
  identity: request.identity,
96
96
  latencyMs: Date.now() - startTime,
97
- tokens: response.usage || { prompt: 0, completion: 0, total: 0 },
97
+ tokens: extractTokenUsageFromRouterResponse(response),
98
98
  taskTypeId,
99
99
  agentType: 'chat'
100
100
  }
@@ -340,12 +340,9 @@ export class AIGateway {
340
340
  const respAny = tryResp;
341
341
  if (ok && respAny) {
342
342
  const meta = respAny.metadata || {};
343
- const usage = respAny.usage || meta?.['ai-activities-response']?.usage;
344
- const prompt = usage?.promptTokens ?? usage?.inputTokens ?? 0;
345
- const completion = usage?.completionTokens ?? usage?.outputTokens ?? 0;
346
- const total = usage?.totalTokens ?? 0;
343
+ const tokenCounts = extractTokenUsageFromRouterResponse(respAny);
347
344
  a.usage = {
348
- tokens: { prompt, completion, total },
345
+ tokens: tokenCounts,
349
346
  maxTokensRequested: typeof meta?.maxTokensRequested === 'number'
350
347
  ? meta.maxTokensRequested
351
348
  : typeof mergedConfig?.maxTokens === 'number'
@@ -491,23 +488,7 @@ export class AIGateway {
491
488
  }
492
489
  contentType = 'structured';
493
490
  parsingMethod = 'flex-md';
494
- // Extract token usage properly
495
- let tokens = { prompt: 0, completion: 0, total: 0 };
496
- if (routerResponse.usage) {
497
- tokens = {
498
- prompt: routerResponse.usage.promptTokens || routerResponse.usage.inputTokens || 0,
499
- completion: routerResponse.usage.completionTokens || routerResponse.usage.outputTokens || 0,
500
- total: routerResponse.usage.totalTokens || 0
501
- };
502
- }
503
- else if (routerResponse.metadata?.['ai-activities-response']?.usage) {
504
- const usage = routerResponse.metadata['ai-activities-response'].usage;
505
- tokens = {
506
- prompt: usage.promptTokens || usage.inputTokens || 0,
507
- completion: usage.completionTokens || usage.outputTokens || 0,
508
- total: usage.totalTokens || 0
509
- };
510
- }
491
+ const tokens = extractTokenUsageFromRouterResponse(routerResponse);
511
492
  const enhancedResponse = {
512
493
  content: content,
513
494
  parsedContent: parsedContent,
@@ -8,6 +8,7 @@
8
8
  *
9
9
  * Note: x-models dependency removed - usage tracking functions are permanently disabled
10
10
  */
11
+ import { extractTokenUsageFromRouterResponse } from './gateway-utils.js';
11
12
  /**
12
13
  * Manages usage tracking for LLM requests
13
14
  */
@@ -26,11 +27,7 @@ export class UsageTracker {
26
27
  * @returns Token usage breakdown
27
28
  */
28
29
  extractTokens(response) {
29
- return {
30
- prompt: response.usage?.promptTokens || 0,
31
- completion: response.usage?.completionTokens || 0,
32
- total: response.usage?.totalTokens || 0
33
- };
30
+ return extractTokenUsageFromRouterResponse(response);
34
31
  }
35
32
  /**
36
33
  * Records usage for a request
@@ -40,6 +40,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.generateMD5Hash = generateMD5Hash;
41
41
  exports.ensureTaskTypeId = ensureTaskTypeId;
42
42
  exports.mergeConfig = mergeConfig;
43
+ exports.normalizeRouterUsageTokens = normalizeRouterUsageTokens;
44
+ exports.extractTokenUsageFromRouterResponse = extractTokenUsageFromRouterResponse;
43
45
  const crypto = __importStar(require("crypto"));
44
46
  const gateway_instructions_js_1 = require("./gateway-instructions.cjs");
45
47
  const flex_md_loader_js_1 = require("./flex-md-loader.cjs");
@@ -217,3 +219,55 @@ async function mergeConfig(request, config, logger) {
217
219
  });
218
220
  return merged;
219
221
  }
222
+ function firstFiniteNumber(...vals) {
223
+ for (const v of vals) {
224
+ if (typeof v === 'number' && Number.isFinite(v))
225
+ return v;
226
+ }
227
+ return undefined;
228
+ }
229
+ /**
230
+ * Maps provider/router usage objects to gateway token counts (`metadata.tokens`, Activix, trace attempts).
231
+ * Handles promptTokens/inputTokens, OpenAI-style snake_case, and missing total (sum prompt+completion).
232
+ */
233
+ function normalizeRouterUsageTokens(usage) {
234
+ if (usage == null || typeof usage !== 'object')
235
+ return undefined;
236
+ const u = usage;
237
+ const prompt = firstFiniteNumber(u.promptTokens, u.inputTokens, u.prompt, u.prompt_tokens) ?? 0;
238
+ const completion = firstFiniteNumber(u.completionTokens, u.outputTokens, u.completion, u.completion_tokens) ?? 0;
239
+ let total = firstFiniteNumber(u.totalTokens, u.total_tokens) ?? 0;
240
+ if (!total && (prompt || completion))
241
+ total = prompt + completion;
242
+ return { prompt, completion, total };
243
+ }
244
+ /**
245
+ * Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
246
+ */
247
+ function extractTokenUsageFromRouterResponse(routerResponse) {
248
+ if (routerResponse == null || typeof routerResponse !== 'object') {
249
+ return { prompt: 0, completion: 0, total: 0 };
250
+ }
251
+ const r = routerResponse;
252
+ const meta = r.metadata != null && typeof r.metadata === 'object'
253
+ ? r.metadata
254
+ : undefined;
255
+ const buckets = [r.usage];
256
+ if (meta) {
257
+ buckets.push(meta.usage);
258
+ const nested = meta['ai-activities-response'];
259
+ if (nested != null && typeof nested === 'object') {
260
+ buckets.push(nested.usage);
261
+ }
262
+ }
263
+ const raw = r.rawResponse ?? r.raw;
264
+ if (raw != null && typeof raw === 'object') {
265
+ buckets.push(raw.usage);
266
+ }
267
+ for (const b of buckets) {
268
+ const n = normalizeRouterUsageTokens(b);
269
+ if (n && (n.prompt || n.completion || n.total))
270
+ return n;
271
+ }
272
+ return { prompt: 0, completion: 0, total: 0 };
273
+ }
@@ -19,3 +19,20 @@ export declare function ensureTaskTypeId(request: ChatRequest, logger: Logxer):
19
19
  export declare function mergeConfig(request: ChatRequest & {
20
20
  useInternalDefaults?: 'skill' | 'audit';
21
21
  }, config: GatewayConfig, logger: Logxer): Promise<ChatRequest['config']>;
22
+ /**
23
+ * Maps provider/router usage objects to gateway token counts (`metadata.tokens`, Activix, trace attempts).
24
+ * Handles promptTokens/inputTokens, OpenAI-style snake_case, and missing total (sum prompt+completion).
25
+ */
26
+ export declare function normalizeRouterUsageTokens(usage: unknown): {
27
+ prompt: number;
28
+ completion: number;
29
+ total: number;
30
+ } | undefined;
31
+ /**
32
+ * Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
33
+ */
34
+ export declare function extractTokenUsageFromRouterResponse(routerResponse: unknown): {
35
+ prompt: number;
36
+ completion: number;
37
+ total: number;
38
+ };
@@ -97,7 +97,7 @@ class AIGateway {
97
97
  aiRequestId: request.aiRequestId,
98
98
  identity: request.identity,
99
99
  latencyMs: Date.now() - startTime,
100
- tokens: response.usage || { prompt: 0, completion: 0, total: 0 },
100
+ tokens: (0, gateway_utils_js_1.extractTokenUsageFromRouterResponse)(response),
101
101
  taskTypeId,
102
102
  agentType: 'chat'
103
103
  }
@@ -343,12 +343,9 @@ class AIGateway {
343
343
  const respAny = tryResp;
344
344
  if (ok && respAny) {
345
345
  const meta = respAny.metadata || {};
346
- const usage = respAny.usage || meta?.['ai-activities-response']?.usage;
347
- const prompt = usage?.promptTokens ?? usage?.inputTokens ?? 0;
348
- const completion = usage?.completionTokens ?? usage?.outputTokens ?? 0;
349
- const total = usage?.totalTokens ?? 0;
346
+ const tokenCounts = (0, gateway_utils_js_1.extractTokenUsageFromRouterResponse)(respAny);
350
347
  a.usage = {
351
- tokens: { prompt, completion, total },
348
+ tokens: tokenCounts,
352
349
  maxTokensRequested: typeof meta?.maxTokensRequested === 'number'
353
350
  ? meta.maxTokensRequested
354
351
  : typeof mergedConfig?.maxTokens === 'number'
@@ -494,23 +491,7 @@ class AIGateway {
494
491
  }
495
492
  contentType = 'structured';
496
493
  parsingMethod = 'flex-md';
497
- // Extract token usage properly
498
- let tokens = { prompt: 0, completion: 0, total: 0 };
499
- if (routerResponse.usage) {
500
- tokens = {
501
- prompt: routerResponse.usage.promptTokens || routerResponse.usage.inputTokens || 0,
502
- completion: routerResponse.usage.completionTokens || routerResponse.usage.outputTokens || 0,
503
- total: routerResponse.usage.totalTokens || 0
504
- };
505
- }
506
- else if (routerResponse.metadata?.['ai-activities-response']?.usage) {
507
- const usage = routerResponse.metadata['ai-activities-response'].usage;
508
- tokens = {
509
- prompt: usage.promptTokens || usage.inputTokens || 0,
510
- completion: usage.completionTokens || usage.outputTokens || 0,
511
- total: usage.totalTokens || 0
512
- };
513
- }
494
+ const tokens = (0, gateway_utils_js_1.extractTokenUsageFromRouterResponse)(routerResponse);
514
495
  const enhancedResponse = {
515
496
  content: content,
516
497
  parsedContent: parsedContent,
@@ -11,6 +11,7 @@
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.UsageTracker = void 0;
14
+ const gateway_utils_js_1 = require("./gateway-utils.cjs");
14
15
  /**
15
16
  * Manages usage tracking for LLM requests
16
17
  */
@@ -29,11 +30,7 @@ class UsageTracker {
29
30
  * @returns Token usage breakdown
30
31
  */
31
32
  extractTokens(response) {
32
- return {
33
- prompt: response.usage?.promptTokens || 0,
34
- completion: response.usage?.completionTokens || 0,
35
- total: response.usage?.totalTokens || 0
36
- };
33
+ return (0, gateway_utils_js_1.extractTokenUsageFromRouterResponse)(response);
37
34
  }
38
35
  /**
39
36
  * Records usage for a request
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x12i/ai-gateway",
3
- "version": "9.0.7",
3
+ "version": "9.0.8",
4
4
  "description": "AI Gateway - Unified interface for LLM provider routing and management",
5
5
  "type": "module",
6
6
  "exports": {