@x12i/ai-gateway 9.1.6 → 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.
Files changed (78) hide show
  1. package/README.md +45 -0
  2. package/dist/activity-manager.d.ts +1 -0
  3. package/dist/activity-manager.js +7 -0
  4. package/dist/ai-tools-client.d.ts +20 -0
  5. package/dist/ai-tools-client.js +91 -0
  6. package/dist/flex-md-loader.d.ts +5 -0
  7. package/dist/flex-md-loader.js +16 -0
  8. package/dist/gateway-config.d.ts +2 -0
  9. package/dist/gateway-config.js +2 -1
  10. package/dist/gateway-mode.d.ts +40 -0
  11. package/dist/gateway-mode.js +75 -0
  12. package/dist/gateway-utils.d.ts +57 -1
  13. package/dist/gateway-utils.js +181 -12
  14. package/dist/gateway.d.ts +3 -0
  15. package/dist/gateway.js +47 -15
  16. package/dist/index.d.ts +6 -1
  17. package/dist/index.js +3 -1
  18. package/dist/output-contract-normalizer.d.ts +21 -0
  19. package/dist/output-contract-normalizer.js +121 -0
  20. package/dist/types.d.ts +35 -0
  21. package/dist-cjs/activity-manager.cjs +21 -19
  22. package/dist-cjs/activity-manager.d.ts +1 -0
  23. package/dist-cjs/ai-tools-client.cjs +91 -0
  24. package/dist-cjs/ai-tools-client.d.ts +20 -0
  25. package/dist-cjs/config/activity-tracking-config.cjs +1 -4
  26. package/dist-cjs/content-normalizer/content-normalizer.cjs +3 -8
  27. package/dist-cjs/content-normalizer/index.cjs +1 -7
  28. package/dist-cjs/content-normalizer/types.cjs +1 -2
  29. package/dist-cjs/flex-md-loader.cjs +35 -65
  30. package/dist-cjs/flex-md-loader.d.ts +5 -0
  31. package/dist-cjs/gateway-config.cjs +25 -63
  32. package/dist-cjs/gateway-config.d.ts +2 -0
  33. package/dist-cjs/gateway-conversion.cjs +10 -48
  34. package/dist-cjs/gateway-instructions.cjs +5 -10
  35. package/dist-cjs/gateway-log-meta.cjs +9 -14
  36. package/dist-cjs/gateway-memory.cjs +2 -6
  37. package/dist-cjs/gateway-messages.cjs +3 -6
  38. package/dist-cjs/gateway-meta.cjs +1 -4
  39. package/dist-cjs/gateway-mode.cjs +75 -0
  40. package/dist-cjs/gateway-mode.d.ts +40 -0
  41. package/dist-cjs/gateway-provider-auto-register.cjs +2 -38
  42. package/dist-cjs/gateway-provider.cjs +10 -22
  43. package/dist-cjs/gateway-rate-limiter-constants.cjs +2 -5
  44. package/dist-cjs/gateway-rate-limiter.cjs +5 -9
  45. package/dist-cjs/gateway-retry.cjs +6 -14
  46. package/dist-cjs/gateway-utils.cjs +201 -83
  47. package/dist-cjs/gateway-utils.d.ts +57 -1
  48. package/dist-cjs/gateway-validation.cjs +2 -6
  49. package/dist-cjs/gateway.cjs +100 -72
  50. package/dist-cjs/gateway.d.ts +3 -0
  51. package/dist-cjs/index.cjs +22 -91
  52. package/dist-cjs/index.d.ts +6 -1
  53. package/dist-cjs/instruction-errors.cjs +2 -7
  54. package/dist-cjs/instruction-optimizer.cjs +4 -10
  55. package/dist-cjs/instructions-parser.cjs +5 -10
  56. package/dist-cjs/logger-factory.cjs +3 -6
  57. package/dist-cjs/memory-path-resolution.cjs +8 -18
  58. package/dist-cjs/message-builder.cjs +11 -47
  59. package/dist-cjs/object-types-library-integration.cjs +3 -8
  60. package/dist-cjs/object-types-library.cjs +5 -10
  61. package/dist-cjs/output-auditor.cjs +1 -4
  62. package/dist-cjs/output-contract-normalizer.cjs +121 -0
  63. package/dist-cjs/output-contract-normalizer.d.ts +21 -0
  64. package/dist-cjs/request-report-generator.cjs +1 -4
  65. package/dist-cjs/response-analyzer/format-type-detector.cjs +1 -5
  66. package/dist-cjs/response-analyzer/index.cjs +3 -9
  67. package/dist-cjs/response-analyzer/object-type-detector.cjs +1 -5
  68. package/dist-cjs/response-analyzer/response-analyzer.cjs +6 -10
  69. package/dist-cjs/response-analyzer/types.cjs +1 -2
  70. package/dist-cjs/response-fallback-fixer.cjs +1 -4
  71. package/dist-cjs/runtime-objects.cjs +7 -13
  72. package/dist-cjs/template-parser.cjs +5 -42
  73. package/dist-cjs/template-render-merge.cjs +2 -6
  74. package/dist-cjs/troubleshooting-helper.cjs +13 -28
  75. package/dist-cjs/types.cjs +1 -2
  76. package/dist-cjs/types.d.ts +35 -0
  77. package/dist-cjs/usage-tracker.cjs +3 -7
  78. package/package.json +11 -5
package/README.md CHANGED
@@ -368,6 +368,51 @@ The gateway only exposes official queryable clients. It exposes `activixClient`
368
368
 
369
369
  See [Runtime Objects Observability Methodology](./docs/RUNTIME_OBJECTS_OBSERVABILITY.md) for the reusable package-level contract.
370
370
 
371
+ ### Model catalog resolution and defaults (`@x12i/ai-tools`)
372
+
373
+ Before each invoke, the gateway can normalize caller `config.model` / `modelConfig` via the **ai-models** Catalox catalog (`@x12i/ai-tools`). After invoke, when the router leaves cost **unpriced**, the gateway may compute USD from the same catalog.
374
+
375
+ **Environment variables:**
376
+
377
+ | Variable | Purpose |
378
+ |----------|---------|
379
+ | `AI_GATEWAY_DEFAULT_MODEL` | Default model when none is provided, or when resolution fails in **`mode=prod`**. Supports `provider/model` (e.g. `openrouter/openai/gpt-5-nano`) or a bare model id. |
380
+ | `mode` / `MODE` | `prod` — unresolved models fall back to the default chain (with **Logxer `warn`**). `dev` / `debug` / omitted — unresolved models throw **`ModelResolutionError`**. |
381
+
382
+ **Default model priority** (prod fallback only): `AI_GATEWAY_DEFAULT_MODEL` → `src/defaults/model-config.json` `defaultModel` → code constant `gpt-5-nano`.
383
+
384
+ **Logxer warnings** on default substitution include structured fields: `reason` (`no_model_provided`, `model_resolution_failed`, `ai_tools_unavailable`), `defaultSource` (`env`, `model-config.json`, `code`), `originalModel`, `defaultModel`, and `mode`.
385
+
386
+ Catalox/Firebase credentials are required for catalog bootstrap (same as `@x12i/ai-tools` — see that package’s README). Disable with `aiTools: { enabled: false }` on `GatewayConfig`, or inject `aiTools.catalox` for tests.
387
+
388
+ **GatewayConfig (optional overrides):**
389
+
390
+ ```typescript
391
+ const gateway = new AIGateway({
392
+ mode: 'prod', // or 'dev' | 'debug' — overrides process.env.mode
393
+ aiTools: {
394
+ enabled: true,
395
+ resolveModels: true,
396
+ calculateCost: true,
397
+ costIncludeBreakdown: false,
398
+ cacheTtlMs: 60_000,
399
+ // catalox: injectedCataloxInstance,
400
+ },
401
+ });
402
+ ```
403
+
404
+ **Tests before release:**
405
+
406
+ ```bash
407
+ npm run build
408
+ npm test # integration (tsx)
409
+ npm run test:ai-tools # unit: mode, defaults, cost helper
410
+ npm run test:live # LIVE: catalog + invoke (needs .env + Firebase + LLM key)
411
+ npm run test:real:comprehensive # optional: compiled real router matrix + npm test
412
+ ```
413
+
414
+ See [`.env.example`](./.env.example) for `AI_GATEWAY_DEFAULT_MODEL`, `mode`, provider keys, and Firebase/Catalox variables.
415
+
371
416
  **Recommended (auto-configured from environment variables):**
372
417
 
373
418
  ```typescript
@@ -120,6 +120,7 @@ export declare class ActivityManager {
120
120
  */
121
121
  logSuccess(activity: ActivityMetadata | undefined, details: {
122
122
  cost?: number;
123
+ costStatus?: 'priced' | 'unpriced';
123
124
  response: any;
124
125
  endTime: number;
125
126
  duration: number;
@@ -155,6 +155,12 @@ function pickActivixCompletionRoutingMetadata(response) {
155
155
  if (m.effectiveModelConfig != null && typeof m.effectiveModelConfig === 'object') {
156
156
  out.effectiveModelConfig = m.effectiveModelConfig;
157
157
  }
158
+ if (typeof m.cost === 'number' && Number.isFinite(m.cost))
159
+ out.cost = m.cost;
160
+ if (typeof m.costUsd === 'number' && Number.isFinite(m.costUsd))
161
+ out.costUsd = m.costUsd;
162
+ if (m.costStatus === 'priced' || m.costStatus === 'unpriced')
163
+ out.costStatus = m.costStatus;
158
164
  return out;
159
165
  }
160
166
  function mergeGatewayActivityIdentity(request, aiRequestId, extras) {
@@ -844,6 +850,7 @@ export class ActivityManager {
844
850
  }
845
851
  await this.activix.completeRecord(activity.activityId, {
846
852
  cost: details.cost,
853
+ ...(details.costStatus ? { costStatus: details.costStatus } : {}),
847
854
  response: details.response,
848
855
  outer: {
849
856
  output: details.response,
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Lazy @x12i/ai-tools catalog + cost calculator bootstrap.
3
+ */
4
+ import { AiModelsCatalogClient, CostCalculator, type ModelResolutionSuccess } from '@x12i/ai-tools';
5
+ import type { Logxer } from '@x12i/logxer';
6
+ import type { ChatRequest, GatewayConfig } from './types.js';
7
+ export type AiToolsClientBundle = {
8
+ catalog: AiModelsCatalogClient;
9
+ calculator: CostCalculator;
10
+ };
11
+ /**
12
+ * Returns catalog + calculator, or null when disabled or bootstrap fails.
13
+ */
14
+ export declare function getAiToolsClient(config: GatewayConfig, logger: Logxer): Promise<AiToolsClientBundle | null>;
15
+ /** Reset singleton (tests). */
16
+ export declare function resetAiToolsClientForTests(): void;
17
+ /**
18
+ * Map catalog resolution to router config provider/model fields.
19
+ */
20
+ export declare function applyModelResolution(merged: NonNullable<ChatRequest['config']>, resolution: ModelResolutionSuccess, gatewayDefaultEngine?: string): void;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Lazy @x12i/ai-tools catalog + cost calculator bootstrap.
3
+ */
4
+ import { AiModelsCatalogClient, CostCalculator, ensureAiModelsCatalog } from '@x12i/ai-tools';
5
+ import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
6
+ let sharedClientPromise = null;
7
+ let sharedConfigKey;
8
+ let bootstrapFailedLogged = false;
9
+ function configKey(config) {
10
+ const injected = config.aiTools?.catalox ? 'injected' : 'env';
11
+ return `${injected}:${config.aiTools?.cacheTtlMs ?? ''}:${config.aiTools?.costIncludeBreakdown ?? ''}`;
12
+ }
13
+ /**
14
+ * Returns catalog + calculator, or null when disabled or bootstrap fails.
15
+ */
16
+ export async function getAiToolsClient(config, logger) {
17
+ if (config.aiTools?.enabled === false) {
18
+ return null;
19
+ }
20
+ const key = configKey(config);
21
+ if (sharedClientPromise && sharedConfigKey !== key) {
22
+ sharedClientPromise = null;
23
+ }
24
+ sharedConfigKey = key;
25
+ if (!sharedClientPromise) {
26
+ sharedClientPromise = bootstrapAiTools(config, logger);
27
+ }
28
+ return sharedClientPromise;
29
+ }
30
+ /** Reset singleton (tests). */
31
+ export function resetAiToolsClientForTests() {
32
+ sharedClientPromise = null;
33
+ sharedConfigKey = undefined;
34
+ bootstrapFailedLogged = false;
35
+ }
36
+ async function bootstrapAiTools(config, logger) {
37
+ try {
38
+ let catalox = config.aiTools?.catalox;
39
+ if (!catalox) {
40
+ const { createCataloxFromEnv } = await import('@x12i/catalox/firebase');
41
+ const bootstrapped = createCataloxFromEnv();
42
+ catalox = bootstrapped.catalox;
43
+ }
44
+ await ensureAiModelsCatalog(catalox);
45
+ const catalog = new AiModelsCatalogClient({
46
+ catalox,
47
+ cacheTtlMs: config.aiTools?.cacheTtlMs
48
+ });
49
+ const calculator = new CostCalculator(catalog, {
50
+ includeBreakdown: config.aiTools?.costIncludeBreakdown === true
51
+ });
52
+ logger.debug('ai-tools catalog client ready', {
53
+ debugKind: gatewayLogDebug.state
54
+ });
55
+ return { catalog, calculator };
56
+ }
57
+ catch (error) {
58
+ if (!bootstrapFailedLogged) {
59
+ bootstrapFailedLogged = true;
60
+ logger.warn('ai-tools catalog bootstrap failed; model resolution and catalog cost calculation disabled', withActivityIdentity(undefined, {
61
+ error: error instanceof Error ? error.message : String(error),
62
+ debugKind: gatewayLogDebug.anomaly
63
+ }));
64
+ }
65
+ return null;
66
+ }
67
+ }
68
+ /**
69
+ * Map catalog resolution to router config provider/model fields.
70
+ */
71
+ export function applyModelResolution(merged, resolution, gatewayDefaultEngine) {
72
+ if (resolution.routedViaOpenRouter) {
73
+ merged.provider = 'openrouter';
74
+ merged.model = resolution.modelId;
75
+ return;
76
+ }
77
+ const slash = resolution.modelId.indexOf('/');
78
+ if (slash > 0) {
79
+ merged.provider = resolution.record?.providerId ?? resolution.modelId.slice(0, slash);
80
+ merged.model = resolution.modelId.slice(slash + 1);
81
+ }
82
+ else {
83
+ merged.model = resolution.modelId;
84
+ if (resolution.record?.providerId) {
85
+ merged.provider = resolution.record.providerId;
86
+ }
87
+ }
88
+ if (!merged.provider && gatewayDefaultEngine) {
89
+ merged.provider = gatewayDefaultEngine;
90
+ }
91
+ }
@@ -43,6 +43,11 @@ export declare function extractJsonFromFlexMd(content: string, logger?: Logxer):
43
43
  json: any;
44
44
  method: string;
45
45
  } | null>;
46
+ /**
47
+ * Section-based markdown → camelCase field map (e.g. `### Short Answer` → `shortAnswer`).
48
+ * Used when output contracts need structured `parsed` fields but flex-md returned only `rawText`.
49
+ */
50
+ export declare function parseMarkdownSectionsFromContent(content: string, logger?: Logxer): Record<string, unknown>;
46
51
  /**
47
52
  * Check if flex-md module is available
48
53
  */
@@ -503,6 +503,22 @@ function fallbackMarkdownParser(content, logger) {
503
503
  method: 'fallback-raw-text'
504
504
  };
505
505
  }
506
+ /**
507
+ * Section-based markdown → camelCase field map (e.g. `### Short Answer` → `shortAnswer`).
508
+ * Used when output contracts need structured `parsed` fields but flex-md returned only `rawText`.
509
+ */
510
+ export function parseMarkdownSectionsFromContent(content, logger) {
511
+ const parsed = fallbackMarkdownParser(content, logger);
512
+ const json = parsed.json;
513
+ if (json != null && typeof json === 'object' && !Array.isArray(json)) {
514
+ const keys = Object.keys(json);
515
+ if (keys.length === 1 && keys[0] === 'rawText') {
516
+ return {};
517
+ }
518
+ return json;
519
+ }
520
+ return {};
521
+ }
506
522
  /**
507
523
  * Fallback JSON extraction when flex-md is not available
508
524
  */
@@ -19,6 +19,7 @@ export interface GatewayConfigContext {
19
19
  usageTracker: UsageTracker;
20
20
  messageBuilderConfig: MessageBuilderConfig;
21
21
  }
22
+ export type InitializedGatewayComponents = ReturnType<typeof initializeGatewayComponents>;
22
23
  /**
23
24
  * Loads configuration from JSON files (model config and instructionsBlocks).
24
25
  * Pass a {@link Logxer} instance so load diagnostics go through logxer (not console).
@@ -46,4 +47,5 @@ export declare function initializeGatewayComponents(config: GatewayConfig): {
46
47
  activityManager: ActivityManager;
47
48
  usageTracker: UsageTracker;
48
49
  messageBuilderConfig: MessageBuilderConfig;
50
+ defaultModelConfig: Record<string, unknown>;
49
51
  };
@@ -283,6 +283,7 @@ export function initializeGatewayComponents(config) {
283
283
  router,
284
284
  activityManager,
285
285
  usageTracker,
286
- messageBuilderConfig
286
+ messageBuilderConfig,
287
+ defaultModelConfig
287
288
  };
288
289
  }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Gateway operational mode (prod vs dev/debug) and default model resolution.
3
+ */
4
+ import type { Logxer } from '@x12i/logxer';
5
+ import type { ActivityIdentity, GatewayConfig } from './types.js';
6
+ export type GatewayOperationalMode = 'prod' | 'debug' | 'dev';
7
+ export type GatewayDefaultModelSource = 'env' | 'model-config.json' | 'code';
8
+ export type DefaultModelSubstitutionReason = 'no_model_provided' | 'model_resolution_failed' | 'ai_tools_unavailable';
9
+ export declare const CODE_DEFAULT_MODEL = "gpt-5-nano";
10
+ export type ResolvedGatewayDefault = {
11
+ model: string;
12
+ provider?: string;
13
+ source: GatewayDefaultModelSource;
14
+ };
15
+ /**
16
+ * Operational mode: `GatewayConfig.mode` overrides `process.env.mode` / `MODE`.
17
+ * Only `prod` allows silent default-model substitution; all other values are strict.
18
+ */
19
+ export declare function getGatewayOperationalMode(config?: Pick<GatewayConfig, 'mode'>): GatewayOperationalMode;
20
+ export declare function isProdGatewayMode(mode: GatewayOperationalMode): boolean;
21
+ /**
22
+ * Parse `provider/model` or bare model id (OpenRouter ids may contain multiple slashes).
23
+ */
24
+ export declare function parseModelProviderSpec(spec: string): {
25
+ provider?: string;
26
+ model: string;
27
+ };
28
+ /**
29
+ * Default model priority: AI_GATEWAY_DEFAULT_MODEL → model-config.json → code constant.
30
+ */
31
+ export declare function resolveGatewayDefaultModel(defaultModelConfig?: Record<string, unknown>, gatewayDefaultEngine?: string): ResolvedGatewayDefault;
32
+ export declare function warnDefaultModelSubstitution(logger: Logxer, identity: Partial<ActivityIdentity> | undefined, details: {
33
+ reason: DefaultModelSubstitutionReason;
34
+ mode: GatewayOperationalMode;
35
+ defaultSource: GatewayDefaultModelSource;
36
+ defaultProvider?: string;
37
+ defaultModel: string;
38
+ originalProvider?: string;
39
+ originalModel?: string;
40
+ }): void;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Gateway operational mode (prod vs dev/debug) and default model resolution.
3
+ */
4
+ import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
5
+ export const CODE_DEFAULT_MODEL = 'gpt-5-nano';
6
+ /**
7
+ * Operational mode: `GatewayConfig.mode` overrides `process.env.mode` / `MODE`.
8
+ * Only `prod` allows silent default-model substitution; all other values are strict.
9
+ */
10
+ export function getGatewayOperationalMode(config) {
11
+ if (config?.mode) {
12
+ return config.mode;
13
+ }
14
+ const raw = (process.env.mode ?? process.env.MODE ?? '').toLowerCase();
15
+ if (raw === 'prod')
16
+ return 'prod';
17
+ if (raw === 'dev')
18
+ return 'dev';
19
+ return 'debug';
20
+ }
21
+ export function isProdGatewayMode(mode) {
22
+ return mode === 'prod';
23
+ }
24
+ /**
25
+ * Parse `provider/model` or bare model id (OpenRouter ids may contain multiple slashes).
26
+ */
27
+ export function parseModelProviderSpec(spec) {
28
+ const trimmed = spec.trim();
29
+ if (!trimmed) {
30
+ return { model: CODE_DEFAULT_MODEL };
31
+ }
32
+ const slash = trimmed.indexOf('/');
33
+ if (slash === -1) {
34
+ return { model: trimmed };
35
+ }
36
+ const first = trimmed.slice(0, slash);
37
+ const rest = trimmed.slice(slash + 1);
38
+ if (rest.includes('/') && (first === 'openrouter' || first === 'open-router')) {
39
+ return { provider: 'openrouter', model: trimmed };
40
+ }
41
+ return { provider: first, model: rest };
42
+ }
43
+ /**
44
+ * Default model priority: AI_GATEWAY_DEFAULT_MODEL → model-config.json → code constant.
45
+ */
46
+ export function resolveGatewayDefaultModel(defaultModelConfig, gatewayDefaultEngine) {
47
+ const envSpec = process.env.AI_GATEWAY_DEFAULT_MODEL?.trim();
48
+ if (envSpec) {
49
+ const parsed = parseModelProviderSpec(envSpec);
50
+ return { model: parsed.model, provider: parsed.provider, source: 'env' };
51
+ }
52
+ const jsonModel = typeof defaultModelConfig?.defaultModel === 'string' ? defaultModelConfig.defaultModel : undefined;
53
+ if (jsonModel) {
54
+ const parsed = parseModelProviderSpec(jsonModel);
55
+ const jsonEngine = typeof defaultModelConfig?.defaultEngine === 'string'
56
+ ? defaultModelConfig.defaultEngine
57
+ : gatewayDefaultEngine;
58
+ return {
59
+ model: parsed.model,
60
+ provider: parsed.provider ?? jsonEngine,
61
+ source: 'model-config.json'
62
+ };
63
+ }
64
+ return {
65
+ model: CODE_DEFAULT_MODEL,
66
+ provider: gatewayDefaultEngine,
67
+ source: 'code'
68
+ };
69
+ }
70
+ export function warnDefaultModelSubstitution(logger, identity, details) {
71
+ logger.warn('Gateway substituted default model for request', withActivityIdentity(identity, {
72
+ ...details,
73
+ debugKind: gatewayLogDebug.anomaly
74
+ }));
75
+ }
@@ -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).
@@ -43,6 +48,57 @@ export declare function extractTokenUsageFromRouterResponse(routerResponse: unkn
43
48
  * Does not compute cost from tokens — adapters must populate normalized fields or raw usage.cost-style keys.
44
49
  */
45
50
  export declare function extractCostUsdFromRouterResponse(routerResponse: unknown): number | undefined;
51
+ /** Activity billing state when token usage is recorded (Run Analysis G8). */
52
+ export type ActivityCostStatus = 'priced' | 'unpriced';
53
+ export type ResolvedActivityCost = {
54
+ cost?: number;
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
+ };
65
+ };
66
+ export declare function hasNonZeroTokenUsage(tokens: {
67
+ prompt: number;
68
+ completion: number;
69
+ total: number;
70
+ }): boolean;
71
+ /**
72
+ * Gateway fallback when the router does not set `metadata.costStatus`.
73
+ * Prefer {@link resolveCostCompletionForActivity} at invoke boundaries.
74
+ */
75
+ export declare function resolveActivityCostCompletion(tokens: {
76
+ prompt: number;
77
+ completion: number;
78
+ total: number;
79
+ }, costUsd: number | undefined): ResolvedActivityCost;
80
+ /**
81
+ * Activity cost slice for Activix: router `metadata.costStatus` / cost wins when present;
82
+ * otherwise gateway applies the G8 fallback (usage + no price → `unpriced`).
83
+ */
84
+ export declare function resolveCostCompletionForActivity(routerResponse: unknown, tokens: {
85
+ prompt: number;
86
+ completion: number;
87
+ total: number;
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>;
46
102
  /**
47
103
  * Stable routing facts for gateway response metadata (router metadata + merged config fallbacks).
48
104
  * Matches trace-mode resolution; intended for every successful invoke(), not only diagnostics.trace.