@x12i/ai-gateway 9.2.0 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/ai-tools-client.d.ts +20 -0
- package/dist/ai-tools-client.js +91 -0
- package/dist/gateway-config.d.ts +2 -0
- package/dist/gateway-config.js +2 -1
- package/dist/gateway-mode.d.ts +40 -0
- package/dist/gateway-mode.js +75 -0
- package/dist/gateway-utils.d.ts +28 -1
- package/dist/gateway-utils.js +137 -12
- package/dist/gateway.d.ts +3 -0
- package/dist/gateway.js +29 -5
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/types.d.ts +21 -0
- package/dist-cjs/activity-manager.cjs +14 -19
- package/dist-cjs/ai-tools-client.cjs +91 -0
- package/dist-cjs/ai-tools-client.d.ts +20 -0
- package/dist-cjs/config/activity-tracking-config.cjs +1 -4
- package/dist-cjs/content-normalizer/content-normalizer.cjs +3 -8
- package/dist-cjs/content-normalizer/index.cjs +1 -7
- package/dist-cjs/content-normalizer/types.cjs +1 -2
- package/dist-cjs/flex-md-loader.cjs +20 -67
- package/dist-cjs/gateway-config.cjs +25 -63
- package/dist-cjs/gateway-config.d.ts +2 -0
- package/dist-cjs/gateway-conversion.cjs +10 -48
- package/dist-cjs/gateway-instructions.cjs +5 -10
- package/dist-cjs/gateway-log-meta.cjs +9 -14
- package/dist-cjs/gateway-memory.cjs +2 -6
- package/dist-cjs/gateway-messages.cjs +3 -6
- package/dist-cjs/gateway-meta.cjs +1 -4
- package/dist-cjs/gateway-mode.cjs +75 -0
- package/dist-cjs/gateway-mode.d.ts +40 -0
- package/dist-cjs/gateway-provider-auto-register.cjs +2 -38
- package/dist-cjs/gateway-provider.cjs +10 -22
- package/dist-cjs/gateway-rate-limiter-constants.cjs +2 -5
- package/dist-cjs/gateway-rate-limiter.cjs +5 -9
- package/dist-cjs/gateway-retry.cjs +6 -14
- package/dist-cjs/gateway-utils.cjs +160 -89
- package/dist-cjs/gateway-utils.d.ts +28 -1
- package/dist-cjs/gateway-validation.cjs +2 -6
- package/dist-cjs/gateway.cjs +86 -66
- package/dist-cjs/gateway.d.ts +3 -0
- package/dist-cjs/index.cjs +22 -98
- package/dist-cjs/index.d.ts +3 -1
- package/dist-cjs/instruction-errors.cjs +2 -7
- package/dist-cjs/instruction-optimizer.cjs +4 -10
- package/dist-cjs/instructions-parser.cjs +5 -10
- package/dist-cjs/logger-factory.cjs +3 -6
- package/dist-cjs/memory-path-resolution.cjs +8 -18
- package/dist-cjs/message-builder.cjs +11 -47
- package/dist-cjs/object-types-library-integration.cjs +3 -8
- package/dist-cjs/object-types-library.cjs +5 -10
- package/dist-cjs/output-auditor.cjs +1 -4
- package/dist-cjs/output-contract-normalizer.cjs +9 -14
- package/dist-cjs/request-report-generator.cjs +1 -4
- package/dist-cjs/response-analyzer/format-type-detector.cjs +1 -5
- package/dist-cjs/response-analyzer/index.cjs +3 -9
- package/dist-cjs/response-analyzer/object-type-detector.cjs +1 -5
- package/dist-cjs/response-analyzer/response-analyzer.cjs +6 -10
- package/dist-cjs/response-analyzer/types.cjs +1 -2
- package/dist-cjs/response-fallback-fixer.cjs +1 -4
- package/dist-cjs/runtime-objects.cjs +7 -13
- package/dist-cjs/template-parser.cjs +5 -42
- package/dist-cjs/template-render-merge.cjs +2 -6
- package/dist-cjs/troubleshooting-helper.cjs +13 -28
- package/dist-cjs/types.cjs +1 -2
- package/dist-cjs/types.d.ts +21 -0
- package/dist-cjs/usage-tracker.cjs +3 -7
- package/package.json +11 -5
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { AIInvokeRequest, ChatRequest, GatewayConfig, GatewayInvokeRejectionMetadata, GatewayTraceMergedConfig, GatewayTraceRequestIds, ModelConfig } from './types.js';
|
|
6
6
|
import type { Logxer } from '@x12i/logxer';
|
|
7
|
+
import { type AiModelsCatalogClient, type CostCalculator } from '@x12i/ai-tools';
|
|
7
8
|
/**
|
|
8
9
|
* Generates MD5 hash of a string
|
|
9
10
|
*/
|
|
@@ -12,13 +13,17 @@ export declare function generateMD5Hash(text: string): string;
|
|
|
12
13
|
* Auto-generates taskTypeId from MD5 hash of pre-parsed instructions if not provided
|
|
13
14
|
*/
|
|
14
15
|
export declare function ensureTaskTypeId(request: ChatRequest, logger: Logxer): Promise<string>;
|
|
16
|
+
export type MergeConfigOptions = {
|
|
17
|
+
defaultModelConfig?: Record<string, unknown>;
|
|
18
|
+
catalog?: AiModelsCatalogClient | null;
|
|
19
|
+
};
|
|
15
20
|
/**
|
|
16
21
|
* Merges config with defaults
|
|
17
22
|
* Supports using internal system action defaults (internalSkill or skillAudit) when useInternalDefaults is set
|
|
18
23
|
*/
|
|
19
24
|
export declare function mergeConfig(request: ChatRequest & {
|
|
20
25
|
useInternalDefaults?: 'skill' | 'audit';
|
|
21
|
-
}, config: GatewayConfig, logger: Logxer): Promise<ChatRequest['config']>;
|
|
26
|
+
}, config: GatewayConfig, logger: Logxer, mergeOptions?: MergeConfigOptions): Promise<ChatRequest['config']>;
|
|
22
27
|
/**
|
|
23
28
|
* Maps provider/router usage objects to gateway token counts (`metadata.tokens`, Activix, trace attempts).
|
|
24
29
|
* Handles promptTokens/inputTokens, OpenAI-style snake_case, Responses-style input/output tokens, and missing total (sum prompt+completion).
|
|
@@ -48,6 +53,15 @@ export type ActivityCostStatus = 'priced' | 'unpriced';
|
|
|
48
53
|
export type ResolvedActivityCost = {
|
|
49
54
|
cost?: number;
|
|
50
55
|
costStatus?: ActivityCostStatus;
|
|
56
|
+
costBreakdown?: {
|
|
57
|
+
promptCostUsd: number;
|
|
58
|
+
completionCostUsd: number;
|
|
59
|
+
cachingCostUsd?: number;
|
|
60
|
+
reasoningCostUsd?: number;
|
|
61
|
+
audioCostUsd?: number;
|
|
62
|
+
imageCostUsd?: number;
|
|
63
|
+
requestFlatCostUsd?: number;
|
|
64
|
+
};
|
|
51
65
|
};
|
|
52
66
|
export declare function hasNonZeroTokenUsage(tokens: {
|
|
53
67
|
prompt: number;
|
|
@@ -72,6 +86,19 @@ export declare function resolveCostCompletionForActivity(routerResponse: unknown
|
|
|
72
86
|
completion: number;
|
|
73
87
|
total: number;
|
|
74
88
|
}): ResolvedActivityCost;
|
|
89
|
+
export type ResolveCostCompletionOptions = {
|
|
90
|
+
mergedConfig?: unknown;
|
|
91
|
+
calculator?: CostCalculator | null;
|
|
92
|
+
calculateCost?: boolean;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Router cost passthrough, then optional @x12i/ai-tools catalog pricing when still unpriced.
|
|
96
|
+
*/
|
|
97
|
+
export declare function resolveCostCompletionWithAiTools(routerResponse: unknown, tokens: {
|
|
98
|
+
prompt: number;
|
|
99
|
+
completion: number;
|
|
100
|
+
total: number;
|
|
101
|
+
}, options?: ResolveCostCompletionOptions): Promise<ResolvedActivityCost>;
|
|
75
102
|
/**
|
|
76
103
|
* Stable routing facts for gateway response metadata (router metadata + merged config fallbacks).
|
|
77
104
|
* Matches trace-mode resolution; intended for every successful invoke(), not only diagnostics.trace.
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Gateway Validation Module
|
|
4
3
|
* Basic validation for clean proxy implementation
|
|
5
4
|
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.validateChatRequest = validateChatRequest;
|
|
8
|
-
exports.validateAIRequest = validateAIRequest;
|
|
9
5
|
function validateMandatoryRuntimeIdentity(request) {
|
|
10
6
|
const id = request.identity;
|
|
11
7
|
if (id === undefined || id === null || typeof id !== 'object') {
|
|
@@ -16,7 +12,7 @@ function validateMandatoryRuntimeIdentity(request) {
|
|
|
16
12
|
/**
|
|
17
13
|
* Validates ChatRequest has required fields
|
|
18
14
|
*/
|
|
19
|
-
function validateChatRequest(request) {
|
|
15
|
+
export function validateChatRequest(request) {
|
|
20
16
|
if (!request.aiRequestId) {
|
|
21
17
|
throw new Error('aiRequestId is required');
|
|
22
18
|
}
|
|
@@ -40,7 +36,7 @@ const GATEWAY_ACTION_TYPES = ['skill', 'preSkill', 'postSkill'];
|
|
|
40
36
|
/**
|
|
41
37
|
* Validates AIInvokeRequest has required fields
|
|
42
38
|
*/
|
|
43
|
-
function validateAIRequest(request) {
|
|
39
|
+
export function validateAIRequest(request) {
|
|
44
40
|
if (!request.aiRequestId) {
|
|
45
41
|
throw new Error('aiRequestId is required for AI requests');
|
|
46
42
|
}
|
package/dist-cjs/gateway.cjs
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* @x12i/ai-gateway
|
|
4
3
|
*
|
|
5
4
|
* Simplified AI Gateway - Clean proxy implementation
|
|
6
5
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const gateway_retry_js_1 = require("./gateway-retry.cjs");
|
|
6
|
+
import { validateChatRequest, validateAIRequest } from './gateway-validation.js';
|
|
7
|
+
import { ensureGatewayRequestIdentity } from './activity-manager.js';
|
|
8
|
+
import { initializeGatewayComponents } from './gateway-config.js';
|
|
9
|
+
import { buildMessages } from './message-builder.js';
|
|
10
|
+
import { extractJsonFromFlexMd } from './flex-md-loader.js';
|
|
11
|
+
import { enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
|
|
12
|
+
import { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, capActivityFullResponsePayload, DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, extractCostUsdFromRouterResponse, extractTokenUsageFromRouterResponse, mergeConfig, pickEffectiveModelConfigForMetadata, pickInvokeRoutingMetadataSlice, pickTraceMergedRouterConfig, resolveCostCompletionWithAiTools, tryExtractRouterLikePayloadFromErrorChain } from './gateway-utils.js';
|
|
13
|
+
import { getAiToolsClient } from './ai-tools-client.js';
|
|
14
|
+
import { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
15
|
+
import { setGatewayLastJobId, setGatewayRuntimeClients } from './runtime-objects.js';
|
|
16
|
+
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
17
|
+
import { invokeWithRetry } from './gateway-retry.js';
|
|
20
18
|
/** Error message thrown by the router when no provider is registered or specified */
|
|
21
19
|
const NO_PROVIDER_ERROR = 'No provider specified and no providers registered';
|
|
22
20
|
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).';
|
|
@@ -28,7 +26,7 @@ function warnIfSuccessfulInvokeReportsZeroUsageOrCost(logger, identity, meta, in
|
|
|
28
26
|
const zeroCost = typeof cost === 'number' && cost === 0;
|
|
29
27
|
if (!zeroTokens && !zeroCostUsd && !zeroCost)
|
|
30
28
|
return;
|
|
31
|
-
logger.warn('Successful provider response reported zero token usage and/or zero cost; verify router adapter usage and billing metadata',
|
|
29
|
+
logger.warn('Successful provider response reported zero token usage and/or zero cost; verify router adapter usage and billing metadata', withActivityIdentity(identity, {
|
|
32
30
|
invokeKind,
|
|
33
31
|
zeroTokens,
|
|
34
32
|
zeroCostUsd,
|
|
@@ -36,28 +34,31 @@ function warnIfSuccessfulInvokeReportsZeroUsageOrCost(logger, identity, meta, in
|
|
|
36
34
|
tokens,
|
|
37
35
|
costUsd,
|
|
38
36
|
cost,
|
|
39
|
-
debugKind:
|
|
37
|
+
debugKind: gatewayLogDebug.anomaly
|
|
40
38
|
}));
|
|
41
39
|
}
|
|
42
40
|
/**
|
|
43
41
|
* Simplified AI Gateway - Clean proxy implementation
|
|
44
42
|
*/
|
|
45
|
-
class AIGateway {
|
|
43
|
+
export class AIGateway {
|
|
46
44
|
router;
|
|
47
45
|
config;
|
|
48
46
|
logger;
|
|
49
47
|
activityManager;
|
|
50
48
|
messageBuilderConfig;
|
|
49
|
+
defaultModelConfig = {};
|
|
51
50
|
_autoRegisterDone = false;
|
|
51
|
+
_aiToolsClient = null;
|
|
52
52
|
constructor(config = {}, activityManager) {
|
|
53
53
|
this.config = config;
|
|
54
54
|
this.activityManager = activityManager;
|
|
55
|
-
const components =
|
|
55
|
+
const components = initializeGatewayComponents(config);
|
|
56
56
|
this.logger = components.logger;
|
|
57
57
|
this.router = components.router;
|
|
58
58
|
this.activityManager = components.activityManager;
|
|
59
59
|
this.messageBuilderConfig = components.messageBuilderConfig;
|
|
60
|
-
|
|
60
|
+
this.defaultModelConfig = components.defaultModelConfig ?? {};
|
|
61
|
+
setGatewayRuntimeClients({
|
|
61
62
|
activix: this.activityManager?.getTracker(),
|
|
62
63
|
logger: this.logger
|
|
63
64
|
});
|
|
@@ -72,20 +73,24 @@ class AIGateway {
|
|
|
72
73
|
async invokeChat(request) {
|
|
73
74
|
const startTime = Date.now();
|
|
74
75
|
// Basic validation
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
validateChatRequest(request);
|
|
77
|
+
ensureGatewayRequestIdentity(request, undefined, this.logger);
|
|
78
|
+
setGatewayLastJobId(resolveRuntimeJobId(request));
|
|
78
79
|
// Generate simple task type ID
|
|
79
80
|
const taskTypeId = request.taskTypeId || `task-${Date.now()}`;
|
|
80
81
|
// Simple message construction
|
|
81
82
|
const messages = this.buildSimpleMessages(request);
|
|
82
83
|
// Merge config (modelConfig > request.config > gateway defaults)
|
|
83
|
-
const
|
|
84
|
+
const aiTools = await this.getAiTools();
|
|
85
|
+
const mergedConfig = await mergeConfig(request, this.config, this.logger, {
|
|
86
|
+
defaultModelConfig: this.defaultModelConfig,
|
|
87
|
+
catalog: aiTools?.catalog ?? null
|
|
88
|
+
});
|
|
84
89
|
// Activix start snapshot must match what the router receives (modelConfig-only callers omit request.config.model).
|
|
85
90
|
request._mergedRouterConfig = mergedConfig;
|
|
86
91
|
// Lazy auto-register providers from env (OPENAI_API_KEY, etc.) so consumers don't have to call init
|
|
87
92
|
if (!this._autoRegisterDone) {
|
|
88
|
-
await
|
|
93
|
+
await autoRegisterProviders(this.router, this.logger);
|
|
89
94
|
this._autoRegisterDone = true;
|
|
90
95
|
}
|
|
91
96
|
// Start activity tracking if available
|
|
@@ -113,8 +118,12 @@ class AIGateway {
|
|
|
113
118
|
mode: 'sync'
|
|
114
119
|
});
|
|
115
120
|
const metaChat = response?.metadata || {};
|
|
116
|
-
const tokensChat =
|
|
117
|
-
const costCompletionChat =
|
|
121
|
+
const tokensChat = extractTokenUsageFromRouterResponse(response);
|
|
122
|
+
const costCompletionChat = await resolveCostCompletionWithAiTools(response, tokensChat, {
|
|
123
|
+
mergedConfig,
|
|
124
|
+
calculator: aiTools?.calculator ?? null,
|
|
125
|
+
calculateCost: this.config.aiTools?.calculateCost
|
|
126
|
+
});
|
|
118
127
|
// Create enhanced response
|
|
119
128
|
const enhancedResponse = {
|
|
120
129
|
content: response.content || '',
|
|
@@ -175,9 +184,9 @@ class AIGateway {
|
|
|
175
184
|
async invoke(request) {
|
|
176
185
|
const startTime = Date.now();
|
|
177
186
|
// Basic validation
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
187
|
+
validateAIRequest(request);
|
|
188
|
+
ensureGatewayRequestIdentity(request, undefined, this.logger);
|
|
189
|
+
setGatewayLastJobId(resolveRuntimeJobId(request));
|
|
181
190
|
// Generate simple task type ID
|
|
182
191
|
const taskTypeId = request.taskTypeId || `task-${Date.now()}`;
|
|
183
192
|
// Resolve instructions and build messages using proper components
|
|
@@ -188,7 +197,7 @@ class AIGateway {
|
|
|
188
197
|
// Use proper instruction resolution and message building
|
|
189
198
|
let builtMessages;
|
|
190
199
|
try {
|
|
191
|
-
builtMessages = await
|
|
200
|
+
builtMessages = await buildMessages(request, this.messageBuilderConfig, {
|
|
192
201
|
parsedSnapshot
|
|
193
202
|
});
|
|
194
203
|
messages = builtMessages.messages;
|
|
@@ -233,13 +242,13 @@ class AIGateway {
|
|
|
233
242
|
failureType: 'validation-failure'
|
|
234
243
|
}, startTime);
|
|
235
244
|
}
|
|
236
|
-
const rejectMeta =
|
|
245
|
+
const rejectMeta = buildInvokeRejectionMetadata({
|
|
237
246
|
request,
|
|
238
247
|
taskTypeId,
|
|
239
248
|
startTime,
|
|
240
249
|
gatewayAiRequestId: request.aiRequestId
|
|
241
250
|
});
|
|
242
|
-
|
|
251
|
+
attachGatewayInvokeRejectionMetadata(err, rejectMeta);
|
|
243
252
|
// Re-throw the error so it propagates to the caller
|
|
244
253
|
throw err;
|
|
245
254
|
}
|
|
@@ -253,14 +262,18 @@ class AIGateway {
|
|
|
253
262
|
// Attach parsedSnapshot to request for activity tracking
|
|
254
263
|
request._parsedRequest = parsedSnapshot;
|
|
255
264
|
// Merge config (modelConfig > request.config > gateway defaults)
|
|
256
|
-
const
|
|
265
|
+
const aiTools = await this.getAiTools();
|
|
266
|
+
const mergedConfig = await mergeConfig(request, this.config, this.logger, {
|
|
267
|
+
defaultModelConfig: this.defaultModelConfig,
|
|
268
|
+
catalog: aiTools?.catalog ?? null
|
|
269
|
+
});
|
|
257
270
|
request._mergedRouterConfig = mergedConfig;
|
|
258
271
|
const diagnosticsMode = request.diagnostics?.mode;
|
|
259
272
|
const traceEnabled = diagnosticsMode === 'trace';
|
|
260
273
|
const includeRawProviderPayload = request.diagnostics?.includeRawProviderPayload === true;
|
|
261
274
|
// Lazy auto-register providers from env (OPENAI_API_KEY, etc.) so consumers don't have to call init
|
|
262
275
|
if (!this._autoRegisterDone) {
|
|
263
|
-
await
|
|
276
|
+
await autoRegisterProviders(this.router, this.logger);
|
|
264
277
|
this._autoRegisterDone = true;
|
|
265
278
|
}
|
|
266
279
|
// Start activity tracking if available
|
|
@@ -351,7 +364,7 @@ class AIGateway {
|
|
|
351
364
|
// Track per-retry attempt objects through retry hooks.
|
|
352
365
|
const attemptIndexByRetry = new Map();
|
|
353
366
|
try {
|
|
354
|
-
const result = await
|
|
367
|
+
const result = await invokeWithRetry({
|
|
355
368
|
...baseRequest,
|
|
356
369
|
request: {
|
|
357
370
|
...baseRequest.request,
|
|
@@ -391,7 +404,7 @@ class AIGateway {
|
|
|
391
404
|
const respAny = tryResp;
|
|
392
405
|
if (ok && respAny) {
|
|
393
406
|
const meta = respAny.metadata || {};
|
|
394
|
-
const tokenCounts =
|
|
407
|
+
const tokenCounts = extractTokenUsageFromRouterResponse(respAny);
|
|
395
408
|
a.usage = {
|
|
396
409
|
tokens: tokenCounts,
|
|
397
410
|
maxTokensRequested: typeof meta?.maxTokensRequested === 'number'
|
|
@@ -420,7 +433,7 @@ class AIGateway {
|
|
|
420
433
|
a.routing.requestIds = requestIds;
|
|
421
434
|
a.modelUsed =
|
|
422
435
|
meta?.modelUsed || meta?.model || respAny.model || candidate.model;
|
|
423
|
-
const attemptCostUsd =
|
|
436
|
+
const attemptCostUsd = extractCostUsdFromRouterResponse(respAny);
|
|
424
437
|
if (typeof attemptCostUsd === 'number')
|
|
425
438
|
a.costUsd = attemptCostUsd;
|
|
426
439
|
if (includeRawProviderPayload) {
|
|
@@ -485,35 +498,35 @@ class AIGateway {
|
|
|
485
498
|
let parsingMethod = undefined;
|
|
486
499
|
// Actually use flex-md parsing - extract structured data from markdown
|
|
487
500
|
try {
|
|
488
|
-
this.logger.debug('Attempting flex-md extraction',
|
|
501
|
+
this.logger.debug('Attempting flex-md extraction', withActivityIdentity(request.identity, {
|
|
489
502
|
contentLength: content.length,
|
|
490
503
|
hasInstructions: !!resolvedRequest.instructions,
|
|
491
|
-
debugKind:
|
|
504
|
+
debugKind: gatewayLogDebug.intent
|
|
492
505
|
}));
|
|
493
506
|
// Let flex-md extract structured data from the response content
|
|
494
|
-
const extractionResult = await
|
|
495
|
-
this.logger.debug('Flex-md extraction result',
|
|
507
|
+
const extractionResult = await extractJsonFromFlexMd(content, this.logger);
|
|
508
|
+
this.logger.debug('Flex-md extraction result', withActivityIdentity(request.identity, {
|
|
496
509
|
hasResult: !!extractionResult,
|
|
497
510
|
hasJson: !!(extractionResult && extractionResult.json),
|
|
498
511
|
method: extractionResult?.method,
|
|
499
512
|
jsonType: extractionResult?.json ? typeof extractionResult.json : 'none',
|
|
500
|
-
debugKind:
|
|
513
|
+
debugKind: gatewayLogDebug.state
|
|
501
514
|
}));
|
|
502
515
|
if (extractionResult && extractionResult.json) {
|
|
503
516
|
// Successfully extracted structured data
|
|
504
517
|
parsedContent = extractionResult.json;
|
|
505
|
-
this.logger.info('Flex-md extraction successful - parsed into structured object',
|
|
518
|
+
this.logger.info('Flex-md extraction successful - parsed into structured object', withActivityIdentity(request.identity, {
|
|
506
519
|
method: extractionResult.method,
|
|
507
520
|
extractedKeys: Object.keys(extractionResult.json),
|
|
508
|
-
debugKind:
|
|
521
|
+
debugKind: gatewayLogDebug.event
|
|
509
522
|
}));
|
|
510
523
|
}
|
|
511
524
|
else {
|
|
512
525
|
// Extraction failed, fall back to raw text wrapper
|
|
513
|
-
this.logger.warn('Flex-md extraction failed - no structured data extracted',
|
|
526
|
+
this.logger.warn('Flex-md extraction failed - no structured data extracted', withActivityIdentity(request.identity, {
|
|
514
527
|
hasResult: !!extractionResult,
|
|
515
528
|
method: extractionResult?.method || 'none',
|
|
516
|
-
debugKind:
|
|
529
|
+
debugKind: gatewayLogDebug.anomaly
|
|
517
530
|
}));
|
|
518
531
|
parsedContent = { rawText: content };
|
|
519
532
|
}
|
|
@@ -521,32 +534,36 @@ class AIGateway {
|
|
|
521
534
|
catch (extractionError) {
|
|
522
535
|
// Extraction failed, fall back to raw text wrapper
|
|
523
536
|
const errorMessage = extractionError instanceof Error ? extractionError.message : String(extractionError);
|
|
524
|
-
this.logger.warn('Flex-md extraction failed - flex-md library compatibility issue',
|
|
537
|
+
this.logger.warn('Flex-md extraction failed - flex-md library compatibility issue', withActivityIdentity(request.identity, {
|
|
525
538
|
error: errorMessage,
|
|
526
539
|
issue: 'flex-md uses require() in ES module context - needs fixing in flex-md-loader.ts',
|
|
527
540
|
fallback: 'using rawText wrapper',
|
|
528
|
-
debugKind:
|
|
541
|
+
debugKind: gatewayLogDebug.anomaly
|
|
529
542
|
}));
|
|
530
543
|
parsedContent = { rawText: content };
|
|
531
544
|
}
|
|
532
545
|
contentType = 'structured';
|
|
533
546
|
parsingMethod = 'flex-md';
|
|
534
|
-
const outputContractKeys =
|
|
535
|
-
parsedContent = await
|
|
536
|
-
let tokens =
|
|
547
|
+
const outputContractKeys = resolveOutputContractFieldKeys(request);
|
|
548
|
+
parsedContent = await enrichParsedContentForOutputContract(parsedContent, content, outputContractKeys, this.logger);
|
|
549
|
+
let tokens = extractTokenUsageFromRouterResponse(routerResponse);
|
|
537
550
|
if (!(tokens.prompt || tokens.completion || tokens.total)) {
|
|
538
551
|
const alt = routerResponse?.rawResponse ?? routerResponse?.raw;
|
|
539
552
|
if (alt != null && typeof alt === 'object' && alt !== routerResponse) {
|
|
540
|
-
const second =
|
|
553
|
+
const second = extractTokenUsageFromRouterResponse(alt);
|
|
541
554
|
if (second.prompt || second.completion || second.total)
|
|
542
555
|
tokens = second;
|
|
543
556
|
}
|
|
544
557
|
}
|
|
545
|
-
const costCompletion =
|
|
558
|
+
const costCompletion = await resolveCostCompletionWithAiTools(routerResponse, tokens, {
|
|
559
|
+
mergedConfig,
|
|
560
|
+
calculator: aiTools?.calculator ?? null,
|
|
561
|
+
calculateCost: this.config.aiTools?.calculateCost
|
|
562
|
+
});
|
|
546
563
|
const routerMetaForCost = routerResponse?.metadata || {};
|
|
547
|
-
const routingMetadataSlice =
|
|
548
|
-
const effectiveModelConfig =
|
|
549
|
-
const traceMergedRouterSnapshot = traceEnabled ?
|
|
564
|
+
const routingMetadataSlice = pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig);
|
|
565
|
+
const effectiveModelConfig = pickEffectiveModelConfigForMetadata(mergedConfig);
|
|
566
|
+
const traceMergedRouterSnapshot = traceEnabled ? pickTraceMergedRouterConfig(mergedConfig) : undefined;
|
|
550
567
|
const enhancedResponse = {
|
|
551
568
|
content: content,
|
|
552
569
|
parsedContent: parsedContent,
|
|
@@ -590,10 +607,10 @@ class AIGateway {
|
|
|
590
607
|
const includeFullProviderBlob = diag?.includeFullProviderResponseInActivity !== false;
|
|
591
608
|
const maxFullChars = typeof diag?.activityFullResponseMaxChars === 'number' && diag.activityFullResponseMaxChars > 0
|
|
592
609
|
? diag.activityFullResponseMaxChars
|
|
593
|
-
:
|
|
610
|
+
: DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS;
|
|
594
611
|
const rawFull = routerResponse.rawResponse || routerResponse;
|
|
595
612
|
const fullResponseForActivity = includeFullProviderBlob
|
|
596
|
-
?
|
|
613
|
+
? capActivityFullResponsePayload(rawFull, maxFullChars)
|
|
597
614
|
: undefined;
|
|
598
615
|
// Create activity response with proper structure for ActivityTracker
|
|
599
616
|
const activityResponse = {
|
|
@@ -627,17 +644,17 @@ class AIGateway {
|
|
|
627
644
|
costUsd: enhancedResponse.metadata.costUsd,
|
|
628
645
|
cost: enhancedResponse.metadata.cost
|
|
629
646
|
}, 'invoke');
|
|
630
|
-
this.logger.debug('gateway: enhancedResponse',
|
|
647
|
+
this.logger.debug('gateway: enhancedResponse', withActivityIdentity(request.identity, {
|
|
631
648
|
latencyMs: enhancedResponse.metadata?.latencyMs,
|
|
632
649
|
contentType: enhancedResponse.metadata?.contentType,
|
|
633
|
-
debugKind:
|
|
650
|
+
debugKind: gatewayLogDebug.state
|
|
634
651
|
}));
|
|
635
652
|
return enhancedResponse;
|
|
636
653
|
}
|
|
637
654
|
catch (error) {
|
|
638
655
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
639
|
-
const partial =
|
|
640
|
-
const rejectMeta =
|
|
656
|
+
const partial = tryExtractRouterLikePayloadFromErrorChain(err);
|
|
657
|
+
const rejectMeta = buildInvokeRejectionMetadata({
|
|
641
658
|
request,
|
|
642
659
|
taskTypeId,
|
|
643
660
|
startTime,
|
|
@@ -645,11 +662,11 @@ class AIGateway {
|
|
|
645
662
|
partialRouterPayload: partial,
|
|
646
663
|
gatewayAiRequestId: request.aiRequestId
|
|
647
664
|
});
|
|
648
|
-
|
|
665
|
+
attachGatewayInvokeRejectionMetadata(err, rejectMeta);
|
|
649
666
|
if (err.message.includes(NO_PROVIDER_ERROR)) {
|
|
650
667
|
const wrapped = new Error(err.message + NO_PROVIDER_HINT);
|
|
651
668
|
wrapped.cause = err;
|
|
652
|
-
|
|
669
|
+
attachGatewayInvokeRejectionMetadata(wrapped, rejectMeta);
|
|
653
670
|
throw wrapped;
|
|
654
671
|
}
|
|
655
672
|
throw err;
|
|
@@ -705,13 +722,16 @@ class AIGateway {
|
|
|
705
722
|
}
|
|
706
723
|
setActivityManager(activityManager) {
|
|
707
724
|
this.activityManager = activityManager;
|
|
708
|
-
|
|
725
|
+
setGatewayRuntimeClients({
|
|
709
726
|
activix: this.activityManager.getTracker(),
|
|
710
727
|
logger: this.logger
|
|
711
728
|
});
|
|
712
729
|
}
|
|
730
|
+
getAiTools() {
|
|
731
|
+
this._aiToolsClient ??= getAiToolsClient(this.config, this.logger);
|
|
732
|
+
return this._aiToolsClient;
|
|
733
|
+
}
|
|
713
734
|
}
|
|
714
|
-
exports.AIGateway = AIGateway;
|
|
715
735
|
function resolveRuntimeJobId(request) {
|
|
716
736
|
return request.identity.jobId || request.identity.sessionId || request.aiRequestId;
|
|
717
737
|
}
|
package/dist-cjs/gateway.d.ts
CHANGED
|
@@ -16,7 +16,9 @@ export declare class AIGateway {
|
|
|
16
16
|
private logger;
|
|
17
17
|
private activityManager?;
|
|
18
18
|
private messageBuilderConfig?;
|
|
19
|
+
private defaultModelConfig;
|
|
19
20
|
private _autoRegisterDone;
|
|
21
|
+
private _aiToolsClient;
|
|
20
22
|
constructor(config?: GatewayConfig, activityManager?: ActivityManager);
|
|
21
23
|
/**
|
|
22
24
|
* Invoke chat request (without structured output requirements)
|
|
@@ -36,4 +38,5 @@ export declare class AIGateway {
|
|
|
36
38
|
getLogger(): Logxer;
|
|
37
39
|
getActivityManager(): ActivityManager | undefined;
|
|
38
40
|
setActivityManager(activityManager: ActivityManager): void;
|
|
41
|
+
private getAiTools;
|
|
39
42
|
}
|