@x12i/ai-gateway 9.2.0 → 9.3.4
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 +105 -13
- package/dist/activity-manager.d.ts +1 -0
- package/dist/activity-manager.js +123 -26
- 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 +34 -6
- 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 +137 -45
- package/dist-cjs/activity-manager.d.ts +1 -0
- 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 +91 -67
- 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
package/dist/gateway-utils.d.ts
CHANGED
|
@@ -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.
|
package/dist/gateway-utils.js
CHANGED
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
* Handles utility functions
|
|
4
4
|
*/
|
|
5
5
|
import * as crypto from 'crypto';
|
|
6
|
+
import { ModelResolutionError } from '@x12i/ai-tools';
|
|
6
7
|
import { getPreParsedInstructions } from './gateway-instructions.js';
|
|
7
8
|
import { getModelMaxTokensFromFlexMd } from './flex-md-loader.js';
|
|
9
|
+
import { applyModelResolution } from './ai-tools-client.js';
|
|
10
|
+
import { getGatewayOperationalMode, isProdGatewayMode, resolveGatewayDefaultModel, warnDefaultModelSubstitution } from './gateway-mode.js';
|
|
8
11
|
/**
|
|
9
12
|
* Generates MD5 hash of a string
|
|
10
13
|
*/
|
|
@@ -29,11 +32,34 @@ export async function ensureTaskTypeId(request, logger) {
|
|
|
29
32
|
});
|
|
30
33
|
return taskTypeId;
|
|
31
34
|
}
|
|
35
|
+
function applyGatewayDefaultToMerged(merged, defaults, config) {
|
|
36
|
+
merged.model = defaults.model;
|
|
37
|
+
if (defaults.provider) {
|
|
38
|
+
merged.provider = defaults.provider;
|
|
39
|
+
}
|
|
40
|
+
else if (!merged.provider) {
|
|
41
|
+
merged.provider = config.defaultEngine;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function substituteGatewayDefaultModel(merged, request, config, logger, mergeOptions, reason, original) {
|
|
45
|
+
const operationalMode = getGatewayOperationalMode(config);
|
|
46
|
+
const defaults = resolveGatewayDefaultModel(mergeOptions?.defaultModelConfig, config.defaultEngine);
|
|
47
|
+
warnDefaultModelSubstitution(logger, request.identity, {
|
|
48
|
+
reason,
|
|
49
|
+
mode: operationalMode,
|
|
50
|
+
defaultSource: defaults.source,
|
|
51
|
+
defaultProvider: defaults.provider ?? merged.provider,
|
|
52
|
+
defaultModel: defaults.model,
|
|
53
|
+
originalProvider: original?.provider ?? merged.provider,
|
|
54
|
+
originalModel: original?.model
|
|
55
|
+
});
|
|
56
|
+
applyGatewayDefaultToMerged(merged, defaults, config);
|
|
57
|
+
}
|
|
32
58
|
/**
|
|
33
59
|
* Merges config with defaults
|
|
34
60
|
* Supports using internal system action defaults (internalSkill or skillAudit) when useInternalDefaults is set
|
|
35
61
|
*/
|
|
36
|
-
export async function mergeConfig(request, config, logger) {
|
|
62
|
+
export async function mergeConfig(request, config, logger, mergeOptions) {
|
|
37
63
|
const useInternalDefaults = request.useInternalDefaults;
|
|
38
64
|
const internalDefaults = useInternalDefaults
|
|
39
65
|
? (useInternalDefaults === 'skill'
|
|
@@ -52,8 +78,8 @@ export async function mergeConfig(request, config, logger) {
|
|
|
52
78
|
useInternalDefaults,
|
|
53
79
|
hasInternalDefaults: !!internalDefaults
|
|
54
80
|
});
|
|
55
|
-
|
|
56
|
-
const
|
|
81
|
+
const operationalMode = getGatewayOperationalMode(config);
|
|
82
|
+
const resolveModels = config.aiTools?.resolveModels !== false;
|
|
57
83
|
// Priority: modelConfig > request.config > internalSystemActions[useInternalDefaults] > gateway defaults
|
|
58
84
|
// First, merge modelConfig into a config-like object if present
|
|
59
85
|
const modelConfigAsConfig = request.modelConfig ? {
|
|
@@ -87,18 +113,67 @@ export async function mergeConfig(request, config, logger) {
|
|
|
87
113
|
...request.config,
|
|
88
114
|
// ModelConfig overrides (highest priority) - merge only defined values
|
|
89
115
|
...(modelConfigAsConfig ? Object.fromEntries(Object.entries(modelConfigAsConfig).filter(([_, value]) => value !== undefined)) : {}),
|
|
90
|
-
//
|
|
91
|
-
model: modelConfigAsConfig?.model || request.config?.model || internalDefaults?.model
|
|
116
|
+
// Model resolved below (catalog, default chain, or explicit pass-through)
|
|
117
|
+
model: modelConfigAsConfig?.model || request.config?.model || internalDefaults?.model,
|
|
92
118
|
// Ensure provider is set: modelConfig > request.config > internalDefaults > gateway default
|
|
93
|
-
// Provider is required for router to know which provider to use
|
|
94
119
|
provider: modelConfigAsConfig?.provider || request.config?.provider || internalDefaults?.engine || config.defaultEngine
|
|
95
120
|
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
121
|
+
const explicitModel = merged.model;
|
|
122
|
+
const originalProvider = merged.provider;
|
|
123
|
+
const originalModel = explicitModel;
|
|
124
|
+
if (!explicitModel) {
|
|
125
|
+
await substituteGatewayDefaultModel(merged, request, config, logger, mergeOptions, 'no_model_provided');
|
|
126
|
+
}
|
|
127
|
+
else if (resolveModels && mergeOptions?.catalog) {
|
|
128
|
+
try {
|
|
129
|
+
const resolution = await mergeOptions.catalog.resolveModel({
|
|
130
|
+
provider: merged.provider,
|
|
131
|
+
model: explicitModel
|
|
132
|
+
});
|
|
133
|
+
if (resolution.found) {
|
|
134
|
+
applyModelResolution(merged, resolution, config.defaultEngine);
|
|
135
|
+
request._modelResolution = {
|
|
136
|
+
modelId: resolution.modelId,
|
|
137
|
+
routedViaOpenRouter: resolution.routedViaOpenRouter,
|
|
138
|
+
confidence: resolution.confidence,
|
|
139
|
+
resolvedVia: resolution.resolvedVia,
|
|
140
|
+
originalProvider,
|
|
141
|
+
originalModel
|
|
142
|
+
};
|
|
143
|
+
logger.verbose('Catalog resolved model name', {
|
|
144
|
+
jobId: request.identity.jobId,
|
|
145
|
+
originalModel,
|
|
146
|
+
resolvedModelId: resolution.modelId,
|
|
147
|
+
provider: merged.provider,
|
|
148
|
+
model: merged.model,
|
|
149
|
+
confidence: resolution.confidence,
|
|
150
|
+
resolvedVia: resolution.resolvedVia
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else if (isProdGatewayMode(operationalMode)) {
|
|
154
|
+
await substituteGatewayDefaultModel(merged, request, config, logger, mergeOptions, 'model_resolution_failed', { provider: originalProvider, model: originalModel });
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
throw new ModelResolutionError({ provider: merged.provider, model: explicitModel }, resolution);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
if (error instanceof ModelResolutionError) {
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
if (isProdGatewayMode(operationalMode)) {
|
|
165
|
+
await substituteGatewayDefaultModel(merged, request, config, logger, mergeOptions, 'ai_tools_unavailable', { provider: originalProvider, model: originalModel });
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else if (resolveModels && !mergeOptions?.catalog && isProdGatewayMode(operationalMode)) {
|
|
173
|
+
await substituteGatewayDefaultModel(merged, request, config, logger, mergeOptions, 'ai_tools_unavailable', { provider: originalProvider, model: originalModel });
|
|
174
|
+
}
|
|
175
|
+
if (!merged.model) {
|
|
176
|
+
await substituteGatewayDefaultModel(merged, request, config, logger, mergeOptions, 'no_model_provided');
|
|
102
177
|
}
|
|
103
178
|
// Auto-get maxTokens from flex-md if not explicitly set in ANY config source
|
|
104
179
|
// Check all possible sources: request.config, internalDefaults, gateway config
|
|
@@ -359,6 +434,56 @@ export function resolveCostCompletionForActivity(routerResponse, tokens) {
|
|
|
359
434
|
}
|
|
360
435
|
return resolveActivityCostCompletion(tokens, costUsd);
|
|
361
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Router cost passthrough, then optional @x12i/ai-tools catalog pricing when still unpriced.
|
|
439
|
+
*/
|
|
440
|
+
export async function resolveCostCompletionWithAiTools(routerResponse, tokens, options) {
|
|
441
|
+
const routerStatus = pickRouterCostStatus(routerResponse);
|
|
442
|
+
const base = resolveCostCompletionForActivity(routerResponse, tokens);
|
|
443
|
+
if (base.costStatus === 'priced') {
|
|
444
|
+
return base;
|
|
445
|
+
}
|
|
446
|
+
if (routerStatus === 'unpriced') {
|
|
447
|
+
return base;
|
|
448
|
+
}
|
|
449
|
+
if (options?.calculateCost === false || !options?.calculator) {
|
|
450
|
+
return base;
|
|
451
|
+
}
|
|
452
|
+
if (!hasNonZeroTokenUsage(tokens)) {
|
|
453
|
+
return base;
|
|
454
|
+
}
|
|
455
|
+
const routing = pickInvokeRoutingMetadataSlice(routerResponse, options.mergedConfig);
|
|
456
|
+
const cfg = options.mergedConfig != null && typeof options.mergedConfig === 'object'
|
|
457
|
+
? options.mergedConfig
|
|
458
|
+
: {};
|
|
459
|
+
const provider = routing.provider ?? cfg.provider;
|
|
460
|
+
const modelUsed = routing.modelUsed ?? cfg.model;
|
|
461
|
+
if (!provider || !modelUsed) {
|
|
462
|
+
return base;
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
const result = await options.calculator.calculate({
|
|
466
|
+
tokens: {
|
|
467
|
+
prompt: tokens.prompt,
|
|
468
|
+
completion: tokens.completion,
|
|
469
|
+
total: tokens.total
|
|
470
|
+
},
|
|
471
|
+
provider,
|
|
472
|
+
modelUsed
|
|
473
|
+
});
|
|
474
|
+
if (typeof result.cost === 'number' && Number.isFinite(result.cost)) {
|
|
475
|
+
return {
|
|
476
|
+
cost: result.cost,
|
|
477
|
+
costStatus: 'priced',
|
|
478
|
+
...(result.breakdown ? { costBreakdown: result.breakdown } : {})
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
// Keep router/gateway unpriced fallback
|
|
484
|
+
}
|
|
485
|
+
return base;
|
|
486
|
+
}
|
|
362
487
|
/**
|
|
363
488
|
* Stable routing facts for gateway response metadata (router metadata + merged config fallbacks).
|
|
364
489
|
* Matches trace-mode resolution; intended for every successful invoke(), not only diagnostics.trace.
|
package/dist/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
|
}
|
package/dist/gateway.js
CHANGED
|
@@ -9,7 +9,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
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,
|
|
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';
|
|
13
14
|
import { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
14
15
|
import { setGatewayLastJobId, setGatewayRuntimeClients } from './runtime-objects.js';
|
|
15
16
|
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
@@ -45,7 +46,9 @@ export class AIGateway {
|
|
|
45
46
|
logger;
|
|
46
47
|
activityManager;
|
|
47
48
|
messageBuilderConfig;
|
|
49
|
+
defaultModelConfig = {};
|
|
48
50
|
_autoRegisterDone = false;
|
|
51
|
+
_aiToolsClient = null;
|
|
49
52
|
constructor(config = {}, activityManager) {
|
|
50
53
|
this.config = config;
|
|
51
54
|
this.activityManager = activityManager;
|
|
@@ -54,6 +57,7 @@ export class AIGateway {
|
|
|
54
57
|
this.router = components.router;
|
|
55
58
|
this.activityManager = components.activityManager;
|
|
56
59
|
this.messageBuilderConfig = components.messageBuilderConfig;
|
|
60
|
+
this.defaultModelConfig = components.defaultModelConfig ?? {};
|
|
57
61
|
setGatewayRuntimeClients({
|
|
58
62
|
activix: this.activityManager?.getTracker(),
|
|
59
63
|
logger: this.logger
|
|
@@ -77,7 +81,11 @@ export class AIGateway {
|
|
|
77
81
|
// Simple message construction
|
|
78
82
|
const messages = this.buildSimpleMessages(request);
|
|
79
83
|
// Merge config (modelConfig > request.config > gateway defaults)
|
|
80
|
-
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
|
+
});
|
|
81
89
|
// Activix start snapshot must match what the router receives (modelConfig-only callers omit request.config.model).
|
|
82
90
|
request._mergedRouterConfig = mergedConfig;
|
|
83
91
|
// Lazy auto-register providers from env (OPENAI_API_KEY, etc.) so consumers don't have to call init
|
|
@@ -111,7 +119,11 @@ export class AIGateway {
|
|
|
111
119
|
});
|
|
112
120
|
const metaChat = response?.metadata || {};
|
|
113
121
|
const tokensChat = extractTokenUsageFromRouterResponse(response);
|
|
114
|
-
const costCompletionChat =
|
|
122
|
+
const costCompletionChat = await resolveCostCompletionWithAiTools(response, tokensChat, {
|
|
123
|
+
mergedConfig,
|
|
124
|
+
calculator: aiTools?.calculator ?? null,
|
|
125
|
+
calculateCost: this.config.aiTools?.calculateCost
|
|
126
|
+
});
|
|
115
127
|
// Create enhanced response
|
|
116
128
|
const enhancedResponse = {
|
|
117
129
|
content: response.content || '',
|
|
@@ -130,7 +142,10 @@ export class AIGateway {
|
|
|
130
142
|
: { cost: costCompletionChat.cost })
|
|
131
143
|
}
|
|
132
144
|
: {}),
|
|
133
|
-
...(costCompletionChat.costStatus ? { costStatus: costCompletionChat.costStatus } : {})
|
|
145
|
+
...(costCompletionChat.costStatus ? { costStatus: costCompletionChat.costStatus } : {}),
|
|
146
|
+
...(costCompletionChat.costBreakdown
|
|
147
|
+
? { costBreakdown: costCompletionChat.costBreakdown }
|
|
148
|
+
: {})
|
|
134
149
|
}
|
|
135
150
|
};
|
|
136
151
|
// Track activity success if activity was started
|
|
@@ -250,7 +265,11 @@ export class AIGateway {
|
|
|
250
265
|
// Attach parsedSnapshot to request for activity tracking
|
|
251
266
|
request._parsedRequest = parsedSnapshot;
|
|
252
267
|
// Merge config (modelConfig > request.config > gateway defaults)
|
|
253
|
-
const
|
|
268
|
+
const aiTools = await this.getAiTools();
|
|
269
|
+
const mergedConfig = await mergeConfig(request, this.config, this.logger, {
|
|
270
|
+
defaultModelConfig: this.defaultModelConfig,
|
|
271
|
+
catalog: aiTools?.catalog ?? null
|
|
272
|
+
});
|
|
254
273
|
request._mergedRouterConfig = mergedConfig;
|
|
255
274
|
const diagnosticsMode = request.diagnostics?.mode;
|
|
256
275
|
const traceEnabled = diagnosticsMode === 'trace';
|
|
@@ -539,7 +558,11 @@ export class AIGateway {
|
|
|
539
558
|
tokens = second;
|
|
540
559
|
}
|
|
541
560
|
}
|
|
542
|
-
const costCompletion =
|
|
561
|
+
const costCompletion = await resolveCostCompletionWithAiTools(routerResponse, tokens, {
|
|
562
|
+
mergedConfig,
|
|
563
|
+
calculator: aiTools?.calculator ?? null,
|
|
564
|
+
calculateCost: this.config.aiTools?.calculateCost
|
|
565
|
+
});
|
|
543
566
|
const routerMetaForCost = routerResponse?.metadata || {};
|
|
544
567
|
const routingMetadataSlice = pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig);
|
|
545
568
|
const effectiveModelConfig = pickEffectiveModelConfigForMetadata(mergedConfig);
|
|
@@ -567,6 +590,7 @@ export class AIGateway {
|
|
|
567
590
|
}
|
|
568
591
|
: {}),
|
|
569
592
|
...(costCompletion.costStatus ? { costStatus: costCompletion.costStatus } : {}),
|
|
593
|
+
...(costCompletion.costBreakdown ? { costBreakdown: costCompletion.costBreakdown } : {}),
|
|
570
594
|
...(traceEnabled
|
|
571
595
|
? {
|
|
572
596
|
requestIds: traceRequestIds,
|
|
@@ -707,6 +731,10 @@ export class AIGateway {
|
|
|
707
731
|
logger: this.logger
|
|
708
732
|
});
|
|
709
733
|
}
|
|
734
|
+
getAiTools() {
|
|
735
|
+
this._aiToolsClient ??= getAiToolsClient(this.config, this.logger);
|
|
736
|
+
return this._aiToolsClient;
|
|
737
|
+
}
|
|
710
738
|
}
|
|
711
739
|
function resolveRuntimeJobId(request) {
|
|
712
740
|
return request.identity.jobId || request.identity.sessionId || request.aiRequestId;
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,9 @@ export { AIGateway } from './gateway.js';
|
|
|
17
17
|
export { InstructionNotFoundError, InstructionBackendError } 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, GatewayTraceRequestIds, GatewayTraceMergedConfig, EnhancedLLMResponse, InstructionMetadata, ValidationRule, TemplateRenderOptions, SmartInputConfig, SmartInputRenderOptions } from './types.js';
|
|
20
|
-
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, hasNonZeroTokenUsage } from './gateway-utils.js';
|
|
20
|
+
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, hasNonZeroTokenUsage } from './gateway-utils.js';
|
|
21
|
+
export { getGatewayOperationalMode, isProdGatewayMode, resolveGatewayDefaultModel, parseModelProviderSpec, CODE_DEFAULT_MODEL } from './gateway-mode.js';
|
|
22
|
+
export type { GatewayOperationalMode, GatewayDefaultModelSource, DefaultModelSubstitutionReason, ResolvedGatewayDefault } from './gateway-mode.js';
|
|
21
23
|
export type { ActivityCostStatus, ResolvedActivityCost } from './gateway-utils.js';
|
|
22
24
|
export { contractSpecToFieldKeys, enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
|
|
23
25
|
export type { OutputContractSpec } from './output-contract-normalizer.js';
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,8 @@ export * from '@x12i/ai-providers-router';
|
|
|
17
17
|
export { AIGateway } from './gateway.js';
|
|
18
18
|
export { InstructionNotFoundError, InstructionBackendError } from './instruction-errors.js';
|
|
19
19
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
20
|
-
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, hasNonZeroTokenUsage } from './gateway-utils.js';
|
|
20
|
+
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, hasNonZeroTokenUsage } from './gateway-utils.js';
|
|
21
|
+
export { getGatewayOperationalMode, isProdGatewayMode, resolveGatewayDefaultModel, parseModelProviderSpec, CODE_DEFAULT_MODEL } from './gateway-mode.js';
|
|
21
22
|
export { contractSpecToFieldKeys, enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
|
|
22
23
|
export { mergeGatewayAndRequestTemplateRenderOptions, mergeTemplateRenderOptions } from './template-render-merge.js';
|
|
23
24
|
export { GATEWAY_DUAL_MEMORY_ROOTS, buildMemoryResolutionRootFromWorkingMemory, coalesceMergedInputBucket, extractCallerInputsBag, mapSmartInputPathsInputsToInput, parseLooseJsonObject, prepareWorkingMemoryForTemplateRender, resolveGatewayMemoryPathValue } from './memory-path-resolution.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -340,6 +340,27 @@ export interface GatewayConfig extends Omit<RouterConfig, 'defaultEngine' | 'log
|
|
|
340
340
|
openRouter?: {
|
|
341
341
|
enabled?: boolean;
|
|
342
342
|
};
|
|
343
|
+
/**
|
|
344
|
+
* Operational mode override (`process.env.mode` / `MODE` when omitted).
|
|
345
|
+
* - `prod`: unresolved models fall back to {@link AI_GATEWAY_DEFAULT_MODEL} / packaged default (with Logxer warn).
|
|
346
|
+
* - `dev` / `debug`: unresolved models throw {@link ModelResolutionError} from `@x12i/ai-tools`.
|
|
347
|
+
*/
|
|
348
|
+
mode?: 'dev' | 'debug' | 'prod';
|
|
349
|
+
/**
|
|
350
|
+
* @x12i/ai-tools integration: catalog model resolution (request) and cost calculation (response).
|
|
351
|
+
*/
|
|
352
|
+
aiTools?: {
|
|
353
|
+
/** @default true */
|
|
354
|
+
enabled?: boolean;
|
|
355
|
+
/** Inject Catalox; otherwise `createCataloxFromEnv()` from `@x12i/catalox/firebase`. */
|
|
356
|
+
catalox?: import('@x12i/catalox').Catalox;
|
|
357
|
+
cacheTtlMs?: number;
|
|
358
|
+
/** @default true */
|
|
359
|
+
resolveModels?: boolean;
|
|
360
|
+
/** @default true */
|
|
361
|
+
calculateCost?: boolean;
|
|
362
|
+
costIncludeBreakdown?: boolean;
|
|
363
|
+
};
|
|
343
364
|
/**
|
|
344
365
|
* InstructionsBlocks overrides
|
|
345
366
|
* Key: block name, Value: block content
|