@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
|
@@ -1,78 +1,27 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Gateway Utilities Module
|
|
4
3
|
* Handles utility functions
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Object.defineProperty(o, k2, desc);
|
|
13
|
-
}) : (function(o, m, k, k2) {
|
|
14
|
-
if (k2 === undefined) k2 = k;
|
|
15
|
-
o[k2] = m[k];
|
|
16
|
-
}));
|
|
17
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
-
}) : function(o, v) {
|
|
20
|
-
o["default"] = v;
|
|
21
|
-
});
|
|
22
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
-
var ownKeys = function(o) {
|
|
24
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
-
var ar = [];
|
|
26
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
-
return ar;
|
|
28
|
-
};
|
|
29
|
-
return ownKeys(o);
|
|
30
|
-
};
|
|
31
|
-
return function (mod) {
|
|
32
|
-
if (mod && mod.__esModule) return mod;
|
|
33
|
-
var result = {};
|
|
34
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
-
__setModuleDefault(result, mod);
|
|
36
|
-
return result;
|
|
37
|
-
};
|
|
38
|
-
})();
|
|
39
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS = void 0;
|
|
41
|
-
exports.generateMD5Hash = generateMD5Hash;
|
|
42
|
-
exports.ensureTaskTypeId = ensureTaskTypeId;
|
|
43
|
-
exports.mergeConfig = mergeConfig;
|
|
44
|
-
exports.normalizeRouterUsageTokens = normalizeRouterUsageTokens;
|
|
45
|
-
exports.extractTokenUsageFromRouterResponse = extractTokenUsageFromRouterResponse;
|
|
46
|
-
exports.extractCostUsdFromRouterResponse = extractCostUsdFromRouterResponse;
|
|
47
|
-
exports.hasNonZeroTokenUsage = hasNonZeroTokenUsage;
|
|
48
|
-
exports.resolveActivityCostCompletion = resolveActivityCostCompletion;
|
|
49
|
-
exports.resolveCostCompletionForActivity = resolveCostCompletionForActivity;
|
|
50
|
-
exports.pickInvokeRoutingMetadataSlice = pickInvokeRoutingMetadataSlice;
|
|
51
|
-
exports.pickEffectiveModelConfigForMetadata = pickEffectiveModelConfigForMetadata;
|
|
52
|
-
exports.pickTraceMergedRouterConfig = pickTraceMergedRouterConfig;
|
|
53
|
-
exports.pickEffectiveModelConfigFromInvokeRequest = pickEffectiveModelConfigFromInvokeRequest;
|
|
54
|
-
exports.tryExtractRouterLikePayloadFromErrorChain = tryExtractRouterLikePayloadFromErrorChain;
|
|
55
|
-
exports.pickRequestIdsFromRouterLike = pickRequestIdsFromRouterLike;
|
|
56
|
-
exports.buildInvokeRejectionMetadata = buildInvokeRejectionMetadata;
|
|
57
|
-
exports.attachGatewayInvokeRejectionMetadata = attachGatewayInvokeRejectionMetadata;
|
|
58
|
-
exports.capActivityFullResponsePayload = capActivityFullResponsePayload;
|
|
59
|
-
const crypto = __importStar(require("crypto"));
|
|
60
|
-
const gateway_instructions_js_1 = require("./gateway-instructions.cjs");
|
|
61
|
-
const flex_md_loader_js_1 = require("./flex-md-loader.cjs");
|
|
5
|
+
import * as crypto from 'crypto';
|
|
6
|
+
import { ModelResolutionError } from '@x12i/ai-tools';
|
|
7
|
+
import { getPreParsedInstructions } from './gateway-instructions.js';
|
|
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';
|
|
62
11
|
/**
|
|
63
12
|
* Generates MD5 hash of a string
|
|
64
13
|
*/
|
|
65
|
-
function generateMD5Hash(text) {
|
|
14
|
+
export function generateMD5Hash(text) {
|
|
66
15
|
return crypto.createHash('md5').update(text).digest('hex');
|
|
67
16
|
}
|
|
68
17
|
/**
|
|
69
18
|
* Auto-generates taskTypeId from MD5 hash of pre-parsed instructions if not provided
|
|
70
19
|
*/
|
|
71
|
-
async function ensureTaskTypeId(request, logger) {
|
|
20
|
+
export async function ensureTaskTypeId(request, logger) {
|
|
72
21
|
if (request.taskTypeId) {
|
|
73
22
|
return request.taskTypeId;
|
|
74
23
|
}
|
|
75
|
-
const preParsedInstructions =
|
|
24
|
+
const preParsedInstructions = getPreParsedInstructions(request.instructions);
|
|
76
25
|
// Generate MD5 hash
|
|
77
26
|
const taskTypeId = generateMD5Hash(preParsedInstructions);
|
|
78
27
|
logger.debug('Auto-generated taskTypeId from instructions MD5 hash', {
|
|
@@ -83,11 +32,34 @@ async function ensureTaskTypeId(request, logger) {
|
|
|
83
32
|
});
|
|
84
33
|
return taskTypeId;
|
|
85
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
|
+
}
|
|
86
58
|
/**
|
|
87
59
|
* Merges config with defaults
|
|
88
60
|
* Supports using internal system action defaults (internalSkill or skillAudit) when useInternalDefaults is set
|
|
89
61
|
*/
|
|
90
|
-
async function mergeConfig(request, config, logger) {
|
|
62
|
+
export async function mergeConfig(request, config, logger, mergeOptions) {
|
|
91
63
|
const useInternalDefaults = request.useInternalDefaults;
|
|
92
64
|
const internalDefaults = useInternalDefaults
|
|
93
65
|
? (useInternalDefaults === 'skill'
|
|
@@ -106,8 +78,8 @@ async function mergeConfig(request, config, logger) {
|
|
|
106
78
|
useInternalDefaults,
|
|
107
79
|
hasInternalDefaults: !!internalDefaults
|
|
108
80
|
});
|
|
109
|
-
|
|
110
|
-
const
|
|
81
|
+
const operationalMode = getGatewayOperationalMode(config);
|
|
82
|
+
const resolveModels = config.aiTools?.resolveModels !== false;
|
|
111
83
|
// Priority: modelConfig > request.config > internalSystemActions[useInternalDefaults] > gateway defaults
|
|
112
84
|
// First, merge modelConfig into a config-like object if present
|
|
113
85
|
const modelConfigAsConfig = request.modelConfig ? {
|
|
@@ -141,18 +113,67 @@ async function mergeConfig(request, config, logger) {
|
|
|
141
113
|
...request.config,
|
|
142
114
|
// ModelConfig overrides (highest priority) - merge only defined values
|
|
143
115
|
...(modelConfigAsConfig ? Object.fromEntries(Object.entries(modelConfigAsConfig).filter(([_, value]) => value !== undefined)) : {}),
|
|
144
|
-
//
|
|
145
|
-
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,
|
|
146
118
|
// Ensure provider is set: modelConfig > request.config > internalDefaults > gateway default
|
|
147
|
-
// Provider is required for router to know which provider to use
|
|
148
119
|
provider: modelConfigAsConfig?.provider || request.config?.provider || internalDefaults?.engine || config.defaultEngine
|
|
149
120
|
};
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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');
|
|
156
177
|
}
|
|
157
178
|
// Auto-get maxTokens from flex-md if not explicitly set in ANY config source
|
|
158
179
|
// Check all possible sources: request.config, internalDefaults, gateway config
|
|
@@ -162,7 +183,7 @@ async function mergeConfig(request, config, logger) {
|
|
|
162
183
|
if (!maxTokensExplicitlySet && merged.model && merged.provider) {
|
|
163
184
|
// Try to get maxTokens from flex-md
|
|
164
185
|
try {
|
|
165
|
-
const flexMdMaxTokens = await
|
|
186
|
+
const flexMdMaxTokens = await getModelMaxTokensFromFlexMd(merged.provider, merged.model);
|
|
166
187
|
if (flexMdMaxTokens && flexMdMaxTokens > 0) {
|
|
167
188
|
merged.maxTokens = flexMdMaxTokens;
|
|
168
189
|
logger.debug('Using maxTokens from flex-md', {
|
|
@@ -252,7 +273,7 @@ function isNonZeroTokenCount(n) {
|
|
|
252
273
|
* Maps provider/router usage objects to gateway token counts (`metadata.tokens`, Activix, trace attempts).
|
|
253
274
|
* Handles promptTokens/inputTokens, OpenAI-style snake_case, Responses-style input/output tokens, and missing total (sum prompt+completion).
|
|
254
275
|
*/
|
|
255
|
-
function normalizeRouterUsageTokens(usage) {
|
|
276
|
+
export function normalizeRouterUsageTokens(usage) {
|
|
256
277
|
if (usage == null || typeof usage !== 'object')
|
|
257
278
|
return undefined;
|
|
258
279
|
const u = usage;
|
|
@@ -300,7 +321,7 @@ function firstNonZeroUsageFromBuckets(buckets) {
|
|
|
300
321
|
* Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
|
|
301
322
|
* Prefers the raw/provider body (`rawResponse` / `raw`) when it carries non-zero usage before re-reading the outer envelope.
|
|
302
323
|
*/
|
|
303
|
-
function extractTokenUsageFromRouterResponse(routerResponse) {
|
|
324
|
+
export function extractTokenUsageFromRouterResponse(routerResponse) {
|
|
304
325
|
const zeros = { prompt: 0, completion: 0, total: 0 };
|
|
305
326
|
if (routerResponse == null || typeof routerResponse !== 'object') {
|
|
306
327
|
return zeros;
|
|
@@ -327,7 +348,7 @@ function extractTokenUsageFromRouterResponse(routerResponse) {
|
|
|
327
348
|
* metadata.attempts[].costUsd, response root, then common raw payload locations.
|
|
328
349
|
* Does not compute cost from tokens — adapters must populate normalized fields or raw usage.cost-style keys.
|
|
329
350
|
*/
|
|
330
|
-
function extractCostUsdFromRouterResponse(routerResponse) {
|
|
351
|
+
export function extractCostUsdFromRouterResponse(routerResponse) {
|
|
331
352
|
if (routerResponse == null || typeof routerResponse !== 'object')
|
|
332
353
|
return undefined;
|
|
333
354
|
const r = routerResponse;
|
|
@@ -369,7 +390,7 @@ function extractCostUsdFromRouterResponse(routerResponse) {
|
|
|
369
390
|
}
|
|
370
391
|
return undefined;
|
|
371
392
|
}
|
|
372
|
-
function hasNonZeroTokenUsage(tokens) {
|
|
393
|
+
export function hasNonZeroTokenUsage(tokens) {
|
|
373
394
|
return !!(tokens.prompt || tokens.completion || tokens.total);
|
|
374
395
|
}
|
|
375
396
|
function pickRouterCostStatus(routerResponse) {
|
|
@@ -386,7 +407,7 @@ function pickRouterCostStatus(routerResponse) {
|
|
|
386
407
|
* Gateway fallback when the router does not set `metadata.costStatus`.
|
|
387
408
|
* Prefer {@link resolveCostCompletionForActivity} at invoke boundaries.
|
|
388
409
|
*/
|
|
389
|
-
function resolveActivityCostCompletion(tokens, costUsd) {
|
|
410
|
+
export function resolveActivityCostCompletion(tokens, costUsd) {
|
|
390
411
|
if (typeof costUsd === 'number' && Number.isFinite(costUsd)) {
|
|
391
412
|
return { cost: costUsd, costStatus: 'priced' };
|
|
392
413
|
}
|
|
@@ -399,7 +420,7 @@ function resolveActivityCostCompletion(tokens, costUsd) {
|
|
|
399
420
|
* Activity cost slice for Activix: router `metadata.costStatus` / cost wins when present;
|
|
400
421
|
* otherwise gateway applies the G8 fallback (usage + no price → `unpriced`).
|
|
401
422
|
*/
|
|
402
|
-
function resolveCostCompletionForActivity(routerResponse, tokens) {
|
|
423
|
+
export function resolveCostCompletionForActivity(routerResponse, tokens) {
|
|
403
424
|
const routerStatus = pickRouterCostStatus(routerResponse);
|
|
404
425
|
const costUsd = extractCostUsdFromRouterResponse(routerResponse);
|
|
405
426
|
if (routerStatus === 'priced') {
|
|
@@ -413,11 +434,61 @@ function resolveCostCompletionForActivity(routerResponse, tokens) {
|
|
|
413
434
|
}
|
|
414
435
|
return resolveActivityCostCompletion(tokens, costUsd);
|
|
415
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
|
+
}
|
|
416
487
|
/**
|
|
417
488
|
* Stable routing facts for gateway response metadata (router metadata + merged config fallbacks).
|
|
418
489
|
* Matches trace-mode resolution; intended for every successful invoke(), not only diagnostics.trace.
|
|
419
490
|
*/
|
|
420
|
-
function pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig) {
|
|
491
|
+
export function pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig) {
|
|
421
492
|
const rr = routerResponse != null && typeof routerResponse === 'object' ? routerResponse : {};
|
|
422
493
|
const meta = rr.metadata != null && typeof rr.metadata === 'object' ? rr.metadata : {};
|
|
423
494
|
const cfg = mergedConfig != null && typeof mergedConfig === 'object' ? mergedConfig : {};
|
|
@@ -443,7 +514,7 @@ function pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig) {
|
|
|
443
514
|
/**
|
|
444
515
|
* Allowlisted generation profile from merged config for client introspection (no secrets, no arbitrary extras).
|
|
445
516
|
*/
|
|
446
|
-
function pickEffectiveModelConfigForMetadata(mergedConfig) {
|
|
517
|
+
export function pickEffectiveModelConfigForMetadata(mergedConfig) {
|
|
447
518
|
if (mergedConfig == null || typeof mergedConfig !== 'object')
|
|
448
519
|
return undefined;
|
|
449
520
|
const c = mergedConfig;
|
|
@@ -466,7 +537,7 @@ const TRACE_MERGED_ROUTER_NUMERIC_KEYS = [
|
|
|
466
537
|
/**
|
|
467
538
|
* Allowlisted snapshot of merged router config for diagnostics trace responses (no arbitrary extras).
|
|
468
539
|
*/
|
|
469
|
-
function pickTraceMergedRouterConfig(mergedConfig) {
|
|
540
|
+
export function pickTraceMergedRouterConfig(mergedConfig) {
|
|
470
541
|
if (mergedConfig == null || typeof mergedConfig !== 'object')
|
|
471
542
|
return undefined;
|
|
472
543
|
const c = mergedConfig;
|
|
@@ -495,7 +566,7 @@ const EFFECTIVE_MODEL_CONFIG_KEYS = ['model', 'modelId', 'provider', 'temperatur
|
|
|
495
566
|
* Allowlisted generation fields from request only (before mergeConfig / flex-md).
|
|
496
567
|
* Priority matches mergeConfig: modelConfig overrides request.config per key.
|
|
497
568
|
*/
|
|
498
|
-
function pickEffectiveModelConfigFromInvokeRequest(request) {
|
|
569
|
+
export function pickEffectiveModelConfigFromInvokeRequest(request) {
|
|
499
570
|
const cfg = (request.config ?? {});
|
|
500
571
|
const mc = (request.modelConfig ?? {});
|
|
501
572
|
const out = {};
|
|
@@ -523,7 +594,7 @@ function isRouterLikeEnvelope(value) {
|
|
|
523
594
|
* Walk `error`, optional `error.cause`, and common adapter fields (`response`, `routerResponse`, …)
|
|
524
595
|
* to find a router-shaped object for token / correlation extraction.
|
|
525
596
|
*/
|
|
526
|
-
function tryExtractRouterLikePayloadFromErrorChain(error, maxDepth = 8) {
|
|
597
|
+
export function tryExtractRouterLikePayloadFromErrorChain(error, maxDepth = 8) {
|
|
527
598
|
const seen = new Set();
|
|
528
599
|
let cur = error;
|
|
529
600
|
for (let i = 0; i < maxDepth && cur != null; i++) {
|
|
@@ -544,7 +615,7 @@ function tryExtractRouterLikePayloadFromErrorChain(error, maxDepth = 8) {
|
|
|
544
615
|
}
|
|
545
616
|
return undefined;
|
|
546
617
|
}
|
|
547
|
-
function pickRequestIdsFromRouterLike(gatewayAiRequestId, routerLike) {
|
|
618
|
+
export function pickRequestIdsFromRouterLike(gatewayAiRequestId, routerLike) {
|
|
548
619
|
if (typeof gatewayAiRequestId !== 'string' || gatewayAiRequestId.length === 0) {
|
|
549
620
|
return undefined;
|
|
550
621
|
}
|
|
@@ -570,7 +641,7 @@ function pickRequestIdsFromRouterLike(gatewayAiRequestId, routerLike) {
|
|
|
570
641
|
}
|
|
571
642
|
return out;
|
|
572
643
|
}
|
|
573
|
-
function buildInvokeRejectionMetadata(args) {
|
|
644
|
+
export function buildInvokeRejectionMetadata(args) {
|
|
574
645
|
const gid = args.gatewayAiRequestId ?? args.request.aiRequestId;
|
|
575
646
|
const partial = args.partialRouterPayload;
|
|
576
647
|
const mc = args.mergedConfig;
|
|
@@ -595,16 +666,16 @@ function buildInvokeRejectionMetadata(args) {
|
|
|
595
666
|
...(mc === undefined ? { mergeConfigUnavailable: true } : {})
|
|
596
667
|
};
|
|
597
668
|
}
|
|
598
|
-
function attachGatewayInvokeRejectionMetadata(err, metadata) {
|
|
669
|
+
export function attachGatewayInvokeRejectionMetadata(err, metadata) {
|
|
599
670
|
err.metadata = metadata;
|
|
600
671
|
}
|
|
601
672
|
/** Default JSON string length cap for Activix `content.fullResponse` when diagnostics allow storing it. */
|
|
602
|
-
|
|
673
|
+
export const DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS = 512_000;
|
|
603
674
|
/**
|
|
604
675
|
* Size-cap a provider/router payload before storing on an activity record.
|
|
605
676
|
* Non-serializable values become a small marker object instead of throwing.
|
|
606
677
|
*/
|
|
607
|
-
function capActivityFullResponsePayload(payload, maxChars =
|
|
678
|
+
export function capActivityFullResponsePayload(payload, maxChars = DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS) {
|
|
608
679
|
if (payload == null)
|
|
609
680
|
return payload;
|
|
610
681
|
let serialized;
|
|
@@ -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
|
}
|