@x12i/ai-gateway 9.0.8 → 9.0.9
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/dist/gateway-utils.d.ts +6 -0
- package/dist/gateway-utils.js +47 -0
- package/dist/gateway.js +54 -20
- package/dist-cjs/gateway-utils.cjs +48 -0
- package/dist-cjs/gateway-utils.d.ts +6 -0
- package/dist-cjs/gateway.cjs +53 -19
- package/package.json +1 -1
package/dist/gateway-utils.d.ts
CHANGED
|
@@ -36,3 +36,9 @@ export declare function extractTokenUsageFromRouterResponse(routerResponse: unkn
|
|
|
36
36
|
completion: number;
|
|
37
37
|
total: number;
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Best-effort USD cost from router/sync AIResponse shape: metadata.costUsd (preferred),
|
|
41
|
+
* metadata.attempts[].costUsd, response root, then common raw payload locations.
|
|
42
|
+
* Does not compute cost from tokens — adapters must populate normalized fields or raw usage.cost-style keys.
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractCostUsdFromRouterResponse(routerResponse: unknown): number | undefined;
|
package/dist/gateway-utils.js
CHANGED
|
@@ -231,3 +231,50 @@ export function extractTokenUsageFromRouterResponse(routerResponse) {
|
|
|
231
231
|
}
|
|
232
232
|
return { prompt: 0, completion: 0, total: 0 };
|
|
233
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Best-effort USD cost from router/sync AIResponse shape: metadata.costUsd (preferred),
|
|
236
|
+
* metadata.attempts[].costUsd, response root, then common raw payload locations.
|
|
237
|
+
* Does not compute cost from tokens — adapters must populate normalized fields or raw usage.cost-style keys.
|
|
238
|
+
*/
|
|
239
|
+
export function extractCostUsdFromRouterResponse(routerResponse) {
|
|
240
|
+
if (routerResponse == null || typeof routerResponse !== 'object')
|
|
241
|
+
return undefined;
|
|
242
|
+
const r = routerResponse;
|
|
243
|
+
const meta = r.metadata != null && typeof r.metadata === 'object'
|
|
244
|
+
? r.metadata
|
|
245
|
+
: undefined;
|
|
246
|
+
const pick = (...vals) => firstFiniteNumber(...vals);
|
|
247
|
+
const fromMeta = pick(meta?.costUsd, meta?.cost);
|
|
248
|
+
if (fromMeta !== undefined)
|
|
249
|
+
return fromMeta;
|
|
250
|
+
const fromRoot = pick(r.costUsd, r.cost);
|
|
251
|
+
if (fromRoot !== undefined)
|
|
252
|
+
return fromRoot;
|
|
253
|
+
const attempts = meta?.attempts;
|
|
254
|
+
if (Array.isArray(attempts)) {
|
|
255
|
+
for (let i = attempts.length - 1; i >= 0; i--) {
|
|
256
|
+
const a = attempts[i];
|
|
257
|
+
if (a != null && typeof a === 'object') {
|
|
258
|
+
const o = a;
|
|
259
|
+
const c = pick(o.costUsd, o.cost);
|
|
260
|
+
if (c !== undefined)
|
|
261
|
+
return c;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const raw = r.rawResponse ?? r.raw;
|
|
266
|
+
if (raw != null && typeof raw === 'object') {
|
|
267
|
+
const rawObj = raw;
|
|
268
|
+
const usage = rawObj.usage;
|
|
269
|
+
if (usage != null && typeof usage === 'object') {
|
|
270
|
+
const u = usage;
|
|
271
|
+
const fromUsage = pick(u.cost, u.costUsd, u.total_cost, u.totalCost);
|
|
272
|
+
if (fromUsage !== undefined)
|
|
273
|
+
return fromUsage;
|
|
274
|
+
}
|
|
275
|
+
const fromRawTop = pick(rawObj.cost, rawObj.costUsd);
|
|
276
|
+
if (fromRawTop !== undefined)
|
|
277
|
+
return fromRawTop;
|
|
278
|
+
}
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
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 { extractTokenUsageFromRouterResponse, mergeConfig } from './gateway-utils.js';
|
|
11
|
+
import { extractCostUsdFromRouterResponse, 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';
|
|
@@ -16,6 +16,25 @@ import { invokeWithRetry } from './gateway-retry.js';
|
|
|
16
16
|
/** Error message thrown by the router when no provider is registered or specified */
|
|
17
17
|
const NO_PROVIDER_ERROR = 'No provider specified and no providers registered';
|
|
18
18
|
const NO_PROVIDER_HINT = ' Set OPEN_ROUTER_KEY (or OPENROUTER_API_KEY) in the environment to use OpenRouter, or register a provider with the router (e.g. via autoRegisterProviders or gateway config).';
|
|
19
|
+
/** Warn when a successful call reports no tokens and/or explicit zero cost (often missing adapter metadata). */
|
|
20
|
+
function warnIfSuccessfulInvokeReportsZeroUsageOrCost(logger, identity, meta, invokeKind) {
|
|
21
|
+
const { tokens, costUsd, cost } = meta;
|
|
22
|
+
const zeroTokens = tokens.prompt === 0 && tokens.completion === 0 && tokens.total === 0;
|
|
23
|
+
const zeroCostUsd = typeof costUsd === 'number' && costUsd === 0;
|
|
24
|
+
const zeroCost = typeof cost === 'number' && cost === 0;
|
|
25
|
+
if (!zeroTokens && !zeroCostUsd && !zeroCost)
|
|
26
|
+
return;
|
|
27
|
+
logger.warn('Successful provider response reported zero token usage and/or zero cost; verify router adapter usage and billing metadata', withActivityIdentity(identity, {
|
|
28
|
+
invokeKind,
|
|
29
|
+
zeroTokens,
|
|
30
|
+
zeroCostUsd,
|
|
31
|
+
zeroCostField: zeroCost,
|
|
32
|
+
tokens,
|
|
33
|
+
costUsd,
|
|
34
|
+
cost,
|
|
35
|
+
debugKind: gatewayLogDebug.anomaly
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
19
38
|
/**
|
|
20
39
|
* Simplified AI Gateway - Clean proxy implementation
|
|
21
40
|
*/
|
|
@@ -87,6 +106,8 @@ export class AIGateway {
|
|
|
87
106
|
},
|
|
88
107
|
mode: 'sync'
|
|
89
108
|
});
|
|
109
|
+
const costUsdChat = extractCostUsdFromRouterResponse(response);
|
|
110
|
+
const metaChat = response?.metadata || {};
|
|
90
111
|
// Create enhanced response
|
|
91
112
|
const enhancedResponse = {
|
|
92
113
|
content: response.content || '',
|
|
@@ -96,13 +117,20 @@ export class AIGateway {
|
|
|
96
117
|
latencyMs: Date.now() - startTime,
|
|
97
118
|
tokens: extractTokenUsageFromRouterResponse(response),
|
|
98
119
|
taskTypeId,
|
|
99
|
-
agentType: 'chat'
|
|
120
|
+
agentType: 'chat',
|
|
121
|
+
...(typeof costUsdChat === 'number'
|
|
122
|
+
? {
|
|
123
|
+
costUsd: costUsdChat,
|
|
124
|
+
...(typeof metaChat.cost === 'number' ? { cost: metaChat.cost } : { cost: costUsdChat })
|
|
125
|
+
}
|
|
126
|
+
: {})
|
|
100
127
|
}
|
|
101
128
|
};
|
|
102
129
|
// Track activity success if activity was started
|
|
103
130
|
if (activity) {
|
|
104
131
|
try {
|
|
105
132
|
await this.activityManager.logSuccess(activity, {
|
|
133
|
+
...(typeof costUsdChat === 'number' ? { cost: costUsdChat } : {}),
|
|
106
134
|
response: enhancedResponse,
|
|
107
135
|
endTime: Date.now(),
|
|
108
136
|
duration: Date.now() - startTime
|
|
@@ -116,6 +144,11 @@ export class AIGateway {
|
|
|
116
144
|
});
|
|
117
145
|
}
|
|
118
146
|
}
|
|
147
|
+
warnIfSuccessfulInvokeReportsZeroUsageOrCost(this.logger, request.identity, {
|
|
148
|
+
tokens: enhancedResponse.metadata.tokens,
|
|
149
|
+
costUsd: enhancedResponse.metadata.costUsd,
|
|
150
|
+
cost: enhancedResponse.metadata.cost
|
|
151
|
+
}, 'invokeChat');
|
|
119
152
|
return enhancedResponse;
|
|
120
153
|
}
|
|
121
154
|
catch (error) {
|
|
@@ -369,17 +402,9 @@ export class AIGateway {
|
|
|
369
402
|
a.routing.requestIds = requestIds;
|
|
370
403
|
a.modelUsed =
|
|
371
404
|
meta?.modelUsed || meta?.model || respAny.model || candidate.model;
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
? meta.cost
|
|
376
|
-
: typeof respAny?.costUsd === 'number'
|
|
377
|
-
? respAny.costUsd
|
|
378
|
-
: typeof respAny?.cost === 'number'
|
|
379
|
-
? respAny.cost
|
|
380
|
-
: undefined;
|
|
381
|
-
if (typeof costUsd === 'number')
|
|
382
|
-
a.costUsd = costUsd;
|
|
405
|
+
const attemptCostUsd = extractCostUsdFromRouterResponse(respAny);
|
|
406
|
+
if (typeof attemptCostUsd === 'number')
|
|
407
|
+
a.costUsd = attemptCostUsd;
|
|
383
408
|
if (includeRawProviderPayload) {
|
|
384
409
|
// Size-capped preview only.
|
|
385
410
|
const raw = respAny.rawResponse ?? respAny.raw ?? respAny;
|
|
@@ -489,6 +514,8 @@ export class AIGateway {
|
|
|
489
514
|
contentType = 'structured';
|
|
490
515
|
parsingMethod = 'flex-md';
|
|
491
516
|
const tokens = extractTokenUsageFromRouterResponse(routerResponse);
|
|
517
|
+
const resolvedCostUsd = extractCostUsdFromRouterResponse(routerResponse);
|
|
518
|
+
const routerMetaForCost = routerResponse?.metadata || {};
|
|
492
519
|
const enhancedResponse = {
|
|
493
520
|
content: content,
|
|
494
521
|
parsedContent: parsedContent,
|
|
@@ -501,6 +528,14 @@ export class AIGateway {
|
|
|
501
528
|
agentType: 'ai',
|
|
502
529
|
contentType,
|
|
503
530
|
parsingMethod,
|
|
531
|
+
...(typeof resolvedCostUsd === 'number'
|
|
532
|
+
? {
|
|
533
|
+
costUsd: resolvedCostUsd,
|
|
534
|
+
...(typeof routerMetaForCost.cost === 'number'
|
|
535
|
+
? { cost: routerMetaForCost.cost }
|
|
536
|
+
: { cost: resolvedCostUsd })
|
|
537
|
+
}
|
|
538
|
+
: {}),
|
|
504
539
|
...(traceEnabled
|
|
505
540
|
? (() => {
|
|
506
541
|
const meta = routerResponse?.metadata || {};
|
|
@@ -512,18 +547,11 @@ export class AIGateway {
|
|
|
512
547
|
: typeof mergedConfig?.maxTokens === 'number'
|
|
513
548
|
? mergedConfig.maxTokens
|
|
514
549
|
: undefined;
|
|
515
|
-
const costUsd = typeof meta.costUsd === 'number'
|
|
516
|
-
? meta.costUsd
|
|
517
|
-
: typeof meta.cost === 'number'
|
|
518
|
-
? meta.cost
|
|
519
|
-
: undefined;
|
|
520
550
|
return {
|
|
521
551
|
provider,
|
|
522
552
|
region,
|
|
523
553
|
modelUsed,
|
|
524
554
|
maxTokensRequested,
|
|
525
|
-
cost: typeof meta.cost === 'number' ? meta.cost : undefined,
|
|
526
|
-
costUsd,
|
|
527
555
|
requestIds: traceRequestIds,
|
|
528
556
|
retryCount: traceRetryCount,
|
|
529
557
|
fallbackCount: traceFallbackCount,
|
|
@@ -549,6 +577,7 @@ export class AIGateway {
|
|
|
549
577
|
usage: tokens
|
|
550
578
|
};
|
|
551
579
|
await this.activityManager.logSuccess(activity, {
|
|
580
|
+
...(typeof resolvedCostUsd === 'number' ? { cost: resolvedCostUsd } : {}),
|
|
552
581
|
response: activityResponse,
|
|
553
582
|
endTime: Date.now(),
|
|
554
583
|
duration: Date.now() - startTime
|
|
@@ -562,6 +591,11 @@ export class AIGateway {
|
|
|
562
591
|
});
|
|
563
592
|
}
|
|
564
593
|
}
|
|
594
|
+
warnIfSuccessfulInvokeReportsZeroUsageOrCost(this.logger, request.identity, {
|
|
595
|
+
tokens: enhancedResponse.metadata.tokens,
|
|
596
|
+
costUsd: enhancedResponse.metadata.costUsd,
|
|
597
|
+
cost: enhancedResponse.metadata.cost
|
|
598
|
+
}, 'invoke');
|
|
565
599
|
this.logger.debug('gateway: enhancedResponse', withActivityIdentity(request.identity, {
|
|
566
600
|
latencyMs: enhancedResponse.metadata?.latencyMs,
|
|
567
601
|
contentType: enhancedResponse.metadata?.contentType,
|
|
@@ -42,6 +42,7 @@ exports.ensureTaskTypeId = ensureTaskTypeId;
|
|
|
42
42
|
exports.mergeConfig = mergeConfig;
|
|
43
43
|
exports.normalizeRouterUsageTokens = normalizeRouterUsageTokens;
|
|
44
44
|
exports.extractTokenUsageFromRouterResponse = extractTokenUsageFromRouterResponse;
|
|
45
|
+
exports.extractCostUsdFromRouterResponse = extractCostUsdFromRouterResponse;
|
|
45
46
|
const crypto = __importStar(require("crypto"));
|
|
46
47
|
const gateway_instructions_js_1 = require("./gateway-instructions.cjs");
|
|
47
48
|
const flex_md_loader_js_1 = require("./flex-md-loader.cjs");
|
|
@@ -271,3 +272,50 @@ function extractTokenUsageFromRouterResponse(routerResponse) {
|
|
|
271
272
|
}
|
|
272
273
|
return { prompt: 0, completion: 0, total: 0 };
|
|
273
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Best-effort USD cost from router/sync AIResponse shape: metadata.costUsd (preferred),
|
|
277
|
+
* metadata.attempts[].costUsd, response root, then common raw payload locations.
|
|
278
|
+
* Does not compute cost from tokens — adapters must populate normalized fields or raw usage.cost-style keys.
|
|
279
|
+
*/
|
|
280
|
+
function extractCostUsdFromRouterResponse(routerResponse) {
|
|
281
|
+
if (routerResponse == null || typeof routerResponse !== 'object')
|
|
282
|
+
return undefined;
|
|
283
|
+
const r = routerResponse;
|
|
284
|
+
const meta = r.metadata != null && typeof r.metadata === 'object'
|
|
285
|
+
? r.metadata
|
|
286
|
+
: undefined;
|
|
287
|
+
const pick = (...vals) => firstFiniteNumber(...vals);
|
|
288
|
+
const fromMeta = pick(meta?.costUsd, meta?.cost);
|
|
289
|
+
if (fromMeta !== undefined)
|
|
290
|
+
return fromMeta;
|
|
291
|
+
const fromRoot = pick(r.costUsd, r.cost);
|
|
292
|
+
if (fromRoot !== undefined)
|
|
293
|
+
return fromRoot;
|
|
294
|
+
const attempts = meta?.attempts;
|
|
295
|
+
if (Array.isArray(attempts)) {
|
|
296
|
+
for (let i = attempts.length - 1; i >= 0; i--) {
|
|
297
|
+
const a = attempts[i];
|
|
298
|
+
if (a != null && typeof a === 'object') {
|
|
299
|
+
const o = a;
|
|
300
|
+
const c = pick(o.costUsd, o.cost);
|
|
301
|
+
if (c !== undefined)
|
|
302
|
+
return c;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const raw = r.rawResponse ?? r.raw;
|
|
307
|
+
if (raw != null && typeof raw === 'object') {
|
|
308
|
+
const rawObj = raw;
|
|
309
|
+
const usage = rawObj.usage;
|
|
310
|
+
if (usage != null && typeof usage === 'object') {
|
|
311
|
+
const u = usage;
|
|
312
|
+
const fromUsage = pick(u.cost, u.costUsd, u.total_cost, u.totalCost);
|
|
313
|
+
if (fromUsage !== undefined)
|
|
314
|
+
return fromUsage;
|
|
315
|
+
}
|
|
316
|
+
const fromRawTop = pick(rawObj.cost, rawObj.costUsd);
|
|
317
|
+
if (fromRawTop !== undefined)
|
|
318
|
+
return fromRawTop;
|
|
319
|
+
}
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
@@ -36,3 +36,9 @@ export declare function extractTokenUsageFromRouterResponse(routerResponse: unkn
|
|
|
36
36
|
completion: number;
|
|
37
37
|
total: number;
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Best-effort USD cost from router/sync AIResponse shape: metadata.costUsd (preferred),
|
|
41
|
+
* metadata.attempts[].costUsd, response root, then common raw payload locations.
|
|
42
|
+
* Does not compute cost from tokens — adapters must populate normalized fields or raw usage.cost-style keys.
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractCostUsdFromRouterResponse(routerResponse: unknown): number | undefined;
|
package/dist-cjs/gateway.cjs
CHANGED
|
@@ -19,6 +19,25 @@ const gateway_retry_js_1 = require("./gateway-retry.cjs");
|
|
|
19
19
|
/** Error message thrown by the router when no provider is registered or specified */
|
|
20
20
|
const NO_PROVIDER_ERROR = 'No provider specified and no providers registered';
|
|
21
21
|
const NO_PROVIDER_HINT = ' Set OPEN_ROUTER_KEY (or OPENROUTER_API_KEY) in the environment to use OpenRouter, or register a provider with the router (e.g. via autoRegisterProviders or gateway config).';
|
|
22
|
+
/** Warn when a successful call reports no tokens and/or explicit zero cost (often missing adapter metadata). */
|
|
23
|
+
function warnIfSuccessfulInvokeReportsZeroUsageOrCost(logger, identity, meta, invokeKind) {
|
|
24
|
+
const { tokens, costUsd, cost } = meta;
|
|
25
|
+
const zeroTokens = tokens.prompt === 0 && tokens.completion === 0 && tokens.total === 0;
|
|
26
|
+
const zeroCostUsd = typeof costUsd === 'number' && costUsd === 0;
|
|
27
|
+
const zeroCost = typeof cost === 'number' && cost === 0;
|
|
28
|
+
if (!zeroTokens && !zeroCostUsd && !zeroCost)
|
|
29
|
+
return;
|
|
30
|
+
logger.warn('Successful provider response reported zero token usage and/or zero cost; verify router adapter usage and billing metadata', (0, gateway_log_meta_js_1.withActivityIdentity)(identity, {
|
|
31
|
+
invokeKind,
|
|
32
|
+
zeroTokens,
|
|
33
|
+
zeroCostUsd,
|
|
34
|
+
zeroCostField: zeroCost,
|
|
35
|
+
tokens,
|
|
36
|
+
costUsd,
|
|
37
|
+
cost,
|
|
38
|
+
debugKind: gateway_log_meta_js_1.gatewayLogDebug.anomaly
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
22
41
|
/**
|
|
23
42
|
* Simplified AI Gateway - Clean proxy implementation
|
|
24
43
|
*/
|
|
@@ -90,6 +109,8 @@ class AIGateway {
|
|
|
90
109
|
},
|
|
91
110
|
mode: 'sync'
|
|
92
111
|
});
|
|
112
|
+
const costUsdChat = (0, gateway_utils_js_1.extractCostUsdFromRouterResponse)(response);
|
|
113
|
+
const metaChat = response?.metadata || {};
|
|
93
114
|
// Create enhanced response
|
|
94
115
|
const enhancedResponse = {
|
|
95
116
|
content: response.content || '',
|
|
@@ -99,13 +120,20 @@ class AIGateway {
|
|
|
99
120
|
latencyMs: Date.now() - startTime,
|
|
100
121
|
tokens: (0, gateway_utils_js_1.extractTokenUsageFromRouterResponse)(response),
|
|
101
122
|
taskTypeId,
|
|
102
|
-
agentType: 'chat'
|
|
123
|
+
agentType: 'chat',
|
|
124
|
+
...(typeof costUsdChat === 'number'
|
|
125
|
+
? {
|
|
126
|
+
costUsd: costUsdChat,
|
|
127
|
+
...(typeof metaChat.cost === 'number' ? { cost: metaChat.cost } : { cost: costUsdChat })
|
|
128
|
+
}
|
|
129
|
+
: {})
|
|
103
130
|
}
|
|
104
131
|
};
|
|
105
132
|
// Track activity success if activity was started
|
|
106
133
|
if (activity) {
|
|
107
134
|
try {
|
|
108
135
|
await this.activityManager.logSuccess(activity, {
|
|
136
|
+
...(typeof costUsdChat === 'number' ? { cost: costUsdChat } : {}),
|
|
109
137
|
response: enhancedResponse,
|
|
110
138
|
endTime: Date.now(),
|
|
111
139
|
duration: Date.now() - startTime
|
|
@@ -119,6 +147,11 @@ class AIGateway {
|
|
|
119
147
|
});
|
|
120
148
|
}
|
|
121
149
|
}
|
|
150
|
+
warnIfSuccessfulInvokeReportsZeroUsageOrCost(this.logger, request.identity, {
|
|
151
|
+
tokens: enhancedResponse.metadata.tokens,
|
|
152
|
+
costUsd: enhancedResponse.metadata.costUsd,
|
|
153
|
+
cost: enhancedResponse.metadata.cost
|
|
154
|
+
}, 'invokeChat');
|
|
122
155
|
return enhancedResponse;
|
|
123
156
|
}
|
|
124
157
|
catch (error) {
|
|
@@ -372,17 +405,9 @@ class AIGateway {
|
|
|
372
405
|
a.routing.requestIds = requestIds;
|
|
373
406
|
a.modelUsed =
|
|
374
407
|
meta?.modelUsed || meta?.model || respAny.model || candidate.model;
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
? meta.cost
|
|
379
|
-
: typeof respAny?.costUsd === 'number'
|
|
380
|
-
? respAny.costUsd
|
|
381
|
-
: typeof respAny?.cost === 'number'
|
|
382
|
-
? respAny.cost
|
|
383
|
-
: undefined;
|
|
384
|
-
if (typeof costUsd === 'number')
|
|
385
|
-
a.costUsd = costUsd;
|
|
408
|
+
const attemptCostUsd = (0, gateway_utils_js_1.extractCostUsdFromRouterResponse)(respAny);
|
|
409
|
+
if (typeof attemptCostUsd === 'number')
|
|
410
|
+
a.costUsd = attemptCostUsd;
|
|
386
411
|
if (includeRawProviderPayload) {
|
|
387
412
|
// Size-capped preview only.
|
|
388
413
|
const raw = respAny.rawResponse ?? respAny.raw ?? respAny;
|
|
@@ -492,6 +517,8 @@ class AIGateway {
|
|
|
492
517
|
contentType = 'structured';
|
|
493
518
|
parsingMethod = 'flex-md';
|
|
494
519
|
const tokens = (0, gateway_utils_js_1.extractTokenUsageFromRouterResponse)(routerResponse);
|
|
520
|
+
const resolvedCostUsd = (0, gateway_utils_js_1.extractCostUsdFromRouterResponse)(routerResponse);
|
|
521
|
+
const routerMetaForCost = routerResponse?.metadata || {};
|
|
495
522
|
const enhancedResponse = {
|
|
496
523
|
content: content,
|
|
497
524
|
parsedContent: parsedContent,
|
|
@@ -504,6 +531,14 @@ class AIGateway {
|
|
|
504
531
|
agentType: 'ai',
|
|
505
532
|
contentType,
|
|
506
533
|
parsingMethod,
|
|
534
|
+
...(typeof resolvedCostUsd === 'number'
|
|
535
|
+
? {
|
|
536
|
+
costUsd: resolvedCostUsd,
|
|
537
|
+
...(typeof routerMetaForCost.cost === 'number'
|
|
538
|
+
? { cost: routerMetaForCost.cost }
|
|
539
|
+
: { cost: resolvedCostUsd })
|
|
540
|
+
}
|
|
541
|
+
: {}),
|
|
507
542
|
...(traceEnabled
|
|
508
543
|
? (() => {
|
|
509
544
|
const meta = routerResponse?.metadata || {};
|
|
@@ -515,18 +550,11 @@ class AIGateway {
|
|
|
515
550
|
: typeof mergedConfig?.maxTokens === 'number'
|
|
516
551
|
? mergedConfig.maxTokens
|
|
517
552
|
: undefined;
|
|
518
|
-
const costUsd = typeof meta.costUsd === 'number'
|
|
519
|
-
? meta.costUsd
|
|
520
|
-
: typeof meta.cost === 'number'
|
|
521
|
-
? meta.cost
|
|
522
|
-
: undefined;
|
|
523
553
|
return {
|
|
524
554
|
provider,
|
|
525
555
|
region,
|
|
526
556
|
modelUsed,
|
|
527
557
|
maxTokensRequested,
|
|
528
|
-
cost: typeof meta.cost === 'number' ? meta.cost : undefined,
|
|
529
|
-
costUsd,
|
|
530
558
|
requestIds: traceRequestIds,
|
|
531
559
|
retryCount: traceRetryCount,
|
|
532
560
|
fallbackCount: traceFallbackCount,
|
|
@@ -552,6 +580,7 @@ class AIGateway {
|
|
|
552
580
|
usage: tokens
|
|
553
581
|
};
|
|
554
582
|
await this.activityManager.logSuccess(activity, {
|
|
583
|
+
...(typeof resolvedCostUsd === 'number' ? { cost: resolvedCostUsd } : {}),
|
|
555
584
|
response: activityResponse,
|
|
556
585
|
endTime: Date.now(),
|
|
557
586
|
duration: Date.now() - startTime
|
|
@@ -565,6 +594,11 @@ class AIGateway {
|
|
|
565
594
|
});
|
|
566
595
|
}
|
|
567
596
|
}
|
|
597
|
+
warnIfSuccessfulInvokeReportsZeroUsageOrCost(this.logger, request.identity, {
|
|
598
|
+
tokens: enhancedResponse.metadata.tokens,
|
|
599
|
+
costUsd: enhancedResponse.metadata.costUsd,
|
|
600
|
+
cost: enhancedResponse.metadata.cost
|
|
601
|
+
}, 'invoke');
|
|
568
602
|
this.logger.debug('gateway: enhancedResponse', (0, gateway_log_meta_js_1.withActivityIdentity)(request.identity, {
|
|
569
603
|
latencyMs: enhancedResponse.metadata?.latencyMs,
|
|
570
604
|
contentType: enhancedResponse.metadata?.contentType,
|