@x12i/ai-gateway 9.6.7 → 9.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,176 @@
1
+ {
2
+ "GATEWAY_ZERO_USAGE_OR_COST": {
3
+ "defaultLevel": "warn",
4
+ "title": "Successful invoke reported zero token usage and/or zero cost",
5
+ "impact": "Billing and usage metadata may be missing from the provider adapter.",
6
+ "possibleCauses": [
7
+ "Router adapter did not populate usage fields.",
8
+ "Provider response omitted token counts.",
9
+ "Cost calculation was skipped or returned zero."
10
+ ],
11
+ "remediation": [
12
+ "Verify router adapter usage extraction.",
13
+ "Confirm ai-tools cost resolution is enabled when pricing is expected."
14
+ ],
15
+ "retryable": false,
16
+ "userActionRequired": false,
17
+ "confidence": "medium"
18
+ },
19
+ "GATEWAY_FALLBACK_CHAIN_EXHAUSTED": {
20
+ "defaultLevel": "error",
21
+ "title": "Provider fallback chain exhausted without a successful response",
22
+ "impact": "The AI request failed after all configured provider/model candidates were tried.",
23
+ "possibleCauses": [
24
+ "All providers in the chain returned errors.",
25
+ "Rate limits or auth failures across candidates.",
26
+ "Model or profile misconfiguration."
27
+ ],
28
+ "remediation": [
29
+ "Inspect fallbackAttempts in the log evidence.",
30
+ "Verify API keys and provider registration.",
31
+ "Review trace-mode metadata.attempts when enabled."
32
+ ],
33
+ "retryable": true,
34
+ "userActionRequired": true,
35
+ "confidence": "high"
36
+ },
37
+ "GATEWAY_FLEX_MD_EXTRACTION_FAILED": {
38
+ "defaultLevel": "warn",
39
+ "title": "Flex-md extraction did not produce structured output",
40
+ "impact": "Parsed content falls back to a rawText wrapper; downstream contract validation may fail.",
41
+ "possibleCauses": [
42
+ "Model response was plain text without expected markdown structure.",
43
+ "flex-md headers or fenced blocks were missing.",
44
+ "Response was truncated before structured content completed."
45
+ ],
46
+ "remediation": [
47
+ "Inspect raw response sample.",
48
+ "Tighten instructions for markdown/JSON output format.",
49
+ "Check max token allocation."
50
+ ],
51
+ "retryable": true,
52
+ "userActionRequired": false,
53
+ "confidence": "medium"
54
+ },
55
+ "GATEWAY_FLEX_MD_EXTRACTION_ERROR": {
56
+ "defaultLevel": "warn",
57
+ "title": "Flex-md extraction threw during parsing",
58
+ "impact": "Structured parsing failed; gateway uses rawText fallback.",
59
+ "possibleCauses": [
60
+ "flex-md library compatibility or loader issue.",
61
+ "Malformed markdown in the model response.",
62
+ "Unexpected response shape from the provider."
63
+ ],
64
+ "remediation": [
65
+ "Review the exception evidence.",
66
+ "Confirm flex-md version alignment with the gateway.",
67
+ "Inspect raw provider content."
68
+ ],
69
+ "retryable": true,
70
+ "userActionRequired": false,
71
+ "confidence": "medium"
72
+ },
73
+ "GATEWAY_DEFAULT_MODEL_SUBSTITUTED": {
74
+ "defaultLevel": "warn",
75
+ "title": "Gateway substituted the configured default model",
76
+ "impact": "The request may run on a different provider/model than the caller specified.",
77
+ "possibleCauses": [
78
+ "Request omitted model while gateway defaults apply.",
79
+ "Operational mode requires a packaged default engine.",
80
+ "Profile resolution fell back to gateway defaults."
81
+ ],
82
+ "remediation": [
83
+ "Pass an explicit model on the request when substitution is undesired.",
84
+ "Review default model configuration and AI_TOOLS routing."
85
+ ],
86
+ "retryable": false,
87
+ "userActionRequired": false,
88
+ "confidence": "high"
89
+ },
90
+ "GATEWAY_RETRY_MAX_EXCEEDED": {
91
+ "defaultLevel": "warn",
92
+ "title": "Provider invoke retries exhausted",
93
+ "impact": "The router call failed after the configured retry budget.",
94
+ "possibleCauses": [
95
+ "Transient network or server errors persisted.",
96
+ "Throttling (429) without sufficient backoff.",
97
+ "Non-recoverable provider outage."
98
+ ],
99
+ "remediation": [
100
+ "Inspect error and status code evidence.",
101
+ "Increase retry limits or backoff if appropriate.",
102
+ "Check provider status and rate limits."
103
+ ],
104
+ "retryable": true,
105
+ "userActionRequired": false,
106
+ "confidence": "high"
107
+ },
108
+ "GATEWAY_RETRY_ATTEMPT": {
109
+ "defaultLevel": "warn",
110
+ "title": "Retrying provider invoke after error",
111
+ "impact": "Request latency increases while the gateway retries.",
112
+ "possibleCauses": [
113
+ "Transient HTTP or network failure.",
114
+ "Rate limiting with backoff.",
115
+ "Timeout from the provider."
116
+ ],
117
+ "remediation": [
118
+ "Monitor retry counts per job.",
119
+ "Tune retry policy if retries are frequent."
120
+ ],
121
+ "retryable": true,
122
+ "userActionRequired": false,
123
+ "confidence": "medium"
124
+ },
125
+ "GATEWAY_OPTIMIXER_ACTIVIX_UNAVAILABLE": {
126
+ "defaultLevel": "warn",
127
+ "title": "Optimixer enabled but Activix is unavailable",
128
+ "impact": "Adaptive max_tokens prediction is disabled for this gateway instance.",
129
+ "possibleCauses": [
130
+ "Activity tracking is disabled or Activix failed to initialize.",
131
+ "Mongo connection or collection configuration is missing."
132
+ ],
133
+ "remediation": [
134
+ "Enable activity tracking with a working Activix connection.",
135
+ "Verify activixCollection configuration."
136
+ ],
137
+ "retryable": false,
138
+ "userActionRequired": true,
139
+ "confidence": "high"
140
+ },
141
+ "GATEWAY_OPTIMIXER_INIT_FAILED": {
142
+ "defaultLevel": "warn",
143
+ "title": "Optimixer initialization failed",
144
+ "impact": "Adaptive max_tokens prediction is disabled.",
145
+ "possibleCauses": [
146
+ "Activix schema or collection mismatch.",
147
+ "Optimixer configuration error.",
148
+ "Dependency or network failure during create()."
149
+ ],
150
+ "remediation": [
151
+ "Check Activix connectivity and collection names.",
152
+ "Review optimixer gateway config."
153
+ ],
154
+ "retryable": false,
155
+ "userActionRequired": true,
156
+ "confidence": "high"
157
+ },
158
+ "GATEWAY_OPTIMIXER_PREDICT_FAILED": {
159
+ "defaultLevel": "warn",
160
+ "title": "Optimixer predictAiMaxTokens failed",
161
+ "impact": "Caller should use fallback max_tokens for the invoke.",
162
+ "possibleCauses": [
163
+ "Insufficient historical samples for the template.",
164
+ "Token estimation or profile resolution failed.",
165
+ "Optimixer internal error."
166
+ ],
167
+ "remediation": [
168
+ "Set explicit max_tokens on the request.",
169
+ "Verify templateId and model profile fields.",
170
+ "Check prediction history in Activix."
171
+ ],
172
+ "retryable": true,
173
+ "userActionRequired": false,
174
+ "confidence": "medium"
175
+ }
176
+ }
@@ -162,7 +162,10 @@ export function initializeGatewayComponents(config) {
162
162
  const logger = createGatewayLogger({
163
163
  enableLogging: config.enableLogging ?? true,
164
164
  packageName: config.packageName,
165
- customLogger: config.logger
165
+ customLogger: config.logger,
166
+ logging: config.logging,
167
+ packageLogLevels: config.packageLogLevels,
168
+ logLevel: config.logLevel
166
169
  });
167
170
  const { defaultModelConfig, defaultInstructionsBlocks, defaultTemplateRendering } = loadConfig(logger);
168
171
  logger.verbose('Gateway initializing', {
@@ -194,6 +197,12 @@ export function initializeGatewayComponents(config) {
194
197
  routerConfig.autoDiscover = config.autoDiscover;
195
198
  if (config.usageTracker !== undefined)
196
199
  routerConfig.usageTracker = config.usageTracker;
200
+ if (config.verbose !== undefined)
201
+ routerConfig.verbose = config.verbose;
202
+ if (config.logLevel !== undefined)
203
+ routerConfig.logLevel = config.logLevel;
204
+ if (config.logging !== undefined)
205
+ routerConfig.logging = config.logging;
197
206
  // OpenRouter: always pass apiKey when set (fallback for providers without direct keys).
198
207
  // USE_OPENROUTER=false only disables *preferring* OpenRouter when direct provider keys exist.
199
208
  const openRouterKey = resolveOpenRouterApiKey(config);
@@ -0,0 +1,23 @@
1
+ import { exceptionEvidence, fieldEvidence, type LogMeta, type Logxer } from '@x12i/logxer';
2
+ import type { ActivityIdentity } from './types.js';
3
+ export { exceptionEvidence, fieldEvidence };
4
+ /** Stable diagnostic codes backed by `src/defaults/log-diagnostics.json`. */
5
+ export declare const GatewayLogCode: {
6
+ readonly ZERO_USAGE_OR_COST: "GATEWAY_ZERO_USAGE_OR_COST";
7
+ readonly FALLBACK_CHAIN_EXHAUSTED: "GATEWAY_FALLBACK_CHAIN_EXHAUSTED";
8
+ readonly FLEX_MD_EXTRACTION_FAILED: "GATEWAY_FLEX_MD_EXTRACTION_FAILED";
9
+ readonly FLEX_MD_EXTRACTION_ERROR: "GATEWAY_FLEX_MD_EXTRACTION_ERROR";
10
+ readonly DEFAULT_MODEL_SUBSTITUTED: "GATEWAY_DEFAULT_MODEL_SUBSTITUTED";
11
+ readonly RETRY_MAX_EXCEEDED: "GATEWAY_RETRY_MAX_EXCEEDED";
12
+ readonly RETRY_ATTEMPT: "GATEWAY_RETRY_ATTEMPT";
13
+ readonly OPTIMIXER_ACTIVIX_UNAVAILABLE: "GATEWAY_OPTIMIXER_ACTIVIX_UNAVAILABLE";
14
+ readonly OPTIMIXER_INIT_FAILED: "GATEWAY_OPTIMIXER_INIT_FAILED";
15
+ readonly OPTIMIXER_PREDICT_FAILED: "GATEWAY_OPTIMIXER_PREDICT_FAILED";
16
+ };
17
+ export type GatewayLogCode = (typeof GatewayLogCode)[keyof typeof GatewayLogCode];
18
+ /** Resolve packaged `defaults/log-diagnostics.json` for createLogxer diagnostics.catalogPath. */
19
+ export declare function resolveLogDiagnosticsCatalogPath(): string | undefined;
20
+ export declare function gatewayWarnCode(logger: Logxer, code: GatewayLogCode | string, identity: Partial<ActivityIdentity> | undefined, data?: LogMeta): void;
21
+ export declare function gatewayErrorCode(logger: Logxer, code: GatewayLogCode | string, identity: Partial<ActivityIdentity> | undefined, data?: LogMeta): void;
22
+ export declare function gatewayInfoCode(logger: Logxer, code: GatewayLogCode | string, identity: Partial<ActivityIdentity> | undefined, data?: LogMeta): void;
23
+ export declare function gatewayAnomalyMeta(identity: Partial<ActivityIdentity> | undefined, data?: LogMeta): LogMeta;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Logxer 4.4+ diagnostic catalog codes and helpers for structured anomaly logging.
3
+ */
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import { exceptionEvidence, fieldEvidence } from '@x12i/logxer';
8
+ import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
9
+ export { exceptionEvidence, fieldEvidence };
10
+ /** Stable diagnostic codes backed by `src/defaults/log-diagnostics.json`. */
11
+ export const GatewayLogCode = {
12
+ ZERO_USAGE_OR_COST: 'GATEWAY_ZERO_USAGE_OR_COST',
13
+ FALLBACK_CHAIN_EXHAUSTED: 'GATEWAY_FALLBACK_CHAIN_EXHAUSTED',
14
+ FLEX_MD_EXTRACTION_FAILED: 'GATEWAY_FLEX_MD_EXTRACTION_FAILED',
15
+ FLEX_MD_EXTRACTION_ERROR: 'GATEWAY_FLEX_MD_EXTRACTION_ERROR',
16
+ DEFAULT_MODEL_SUBSTITUTED: 'GATEWAY_DEFAULT_MODEL_SUBSTITUTED',
17
+ RETRY_MAX_EXCEEDED: 'GATEWAY_RETRY_MAX_EXCEEDED',
18
+ RETRY_ATTEMPT: 'GATEWAY_RETRY_ATTEMPT',
19
+ OPTIMIXER_ACTIVIX_UNAVAILABLE: 'GATEWAY_OPTIMIXER_ACTIVIX_UNAVAILABLE',
20
+ OPTIMIXER_INIT_FAILED: 'GATEWAY_OPTIMIXER_INIT_FAILED',
21
+ OPTIMIXER_PREDICT_FAILED: 'GATEWAY_OPTIMIXER_PREDICT_FAILED'
22
+ };
23
+ function getModuleDir() {
24
+ if (typeof __dirname !== 'undefined') {
25
+ return __dirname;
26
+ }
27
+ try {
28
+ const getMetaUrl = new Function('return import.meta.url');
29
+ const metaUrl = getMetaUrl();
30
+ if (metaUrl && metaUrl.startsWith('file:')) {
31
+ return path.dirname(fileURLToPath(metaUrl));
32
+ }
33
+ }
34
+ catch {
35
+ // fall through
36
+ }
37
+ return process.cwd();
38
+ }
39
+ /** Resolve packaged `defaults/log-diagnostics.json` for createLogxer diagnostics.catalogPath. */
40
+ export function resolveLogDiagnosticsCatalogPath() {
41
+ const moduleDir = getModuleDir();
42
+ const cwd = process.cwd();
43
+ const candidates = [
44
+ moduleDir,
45
+ path.resolve(moduleDir, '..'),
46
+ path.resolve(moduleDir, '../dist'),
47
+ path.resolve(moduleDir, '../dist-cjs'),
48
+ path.resolve(moduleDir, '../src'),
49
+ path.join(cwd, 'dist'),
50
+ path.join(cwd, 'dist-cjs'),
51
+ path.join(cwd, 'src')
52
+ ];
53
+ for (const dir of candidates) {
54
+ const catalogPath = path.join(dir, 'defaults', 'log-diagnostics.json');
55
+ if (fs.existsSync(catalogPath)) {
56
+ return catalogPath;
57
+ }
58
+ }
59
+ return undefined;
60
+ }
61
+ export function gatewayWarnCode(logger, code, identity, data) {
62
+ logger.warnCode(code, withActivityIdentity(identity, data));
63
+ }
64
+ export function gatewayErrorCode(logger, code, identity, data) {
65
+ logger.errorCode(code, withActivityIdentity(identity, data));
66
+ }
67
+ export function gatewayInfoCode(logger, code, identity, data) {
68
+ logger.infoCode(code, withActivityIdentity(identity, data));
69
+ }
70
+ export function gatewayAnomalyMeta(identity, data) {
71
+ return withActivityIdentity(identity, data, gatewayLogDebug.anomaly);
72
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Logxer ≥ 4.5 — process-wide package log levels for the gateway dependency stack.
3
+ * @see node_modules/@x12i/logxer/docs/package-log-levels-stack.md
4
+ */
5
+ import { type PackageLogLevelsConfig } from '@x12i/logxer';
6
+ /** Stable ERC 2.0 env prefix — controls `AI_GATEWAY_LOGS_LEVEL`, etc. */
7
+ export declare const GATEWAY_LOG_ENV_PREFIX = "AI_GATEWAY";
8
+ /** Known `envPrefix` values in this package's dependency stack (for hosts / `.env`). */
9
+ export declare const GATEWAY_STACK_LOG_PREFIXES: {
10
+ readonly gateway: "AI_GATEWAY";
11
+ readonly router: "AI_PROVIDER_ROUTER";
12
+ readonly flexMd: "FLEX_MD";
13
+ readonly optimixer: "OPTIMIXER";
14
+ };
15
+ /**
16
+ * Load bulk env (`LOGXER_PACKAGE_LEVELS`, `LOGXER_PACKAGE_LOGS_DEFAULT`) and merge optional host config.
17
+ * Idempotent — safe to call from `createGatewayLogger` / `initializeGatewayComponents`.
18
+ */
19
+ export declare function initializeGatewayPackageLogLevels(options?: {
20
+ packageLogLevels?: PackageLogLevelsConfig;
21
+ }): void;
22
+ /** Reset init flag (tests). */
23
+ export declare function resetGatewayPackageLogLevelsInit(): void;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Logxer ≥ 4.5 — process-wide package log levels for the gateway dependency stack.
3
+ * @see node_modules/@x12i/logxer/docs/package-log-levels-stack.md
4
+ */
5
+ import { applyPackageLogLevelsFromEnv, mergePackageLogLevelsConfig } from '@x12i/logxer';
6
+ import { ROUTER_LOG_ENV_PREFIX } from '@x12i/ai-providers-router';
7
+ /** Stable ERC 2.0 env prefix — controls `AI_GATEWAY_LOGS_LEVEL`, etc. */
8
+ export const GATEWAY_LOG_ENV_PREFIX = 'AI_GATEWAY';
9
+ /** Known `envPrefix` values in this package's dependency stack (for hosts / `.env`). */
10
+ export const GATEWAY_STACK_LOG_PREFIXES = {
11
+ gateway: GATEWAY_LOG_ENV_PREFIX,
12
+ router: ROUTER_LOG_ENV_PREFIX,
13
+ flexMd: 'FLEX_MD',
14
+ optimixer: 'OPTIMIXER'
15
+ };
16
+ let packageLogLevelsInitialized = false;
17
+ /**
18
+ * Load bulk env (`LOGXER_PACKAGE_LEVELS`, `LOGXER_PACKAGE_LOGS_DEFAULT`) and merge optional host config.
19
+ * Idempotent — safe to call from `createGatewayLogger` / `initializeGatewayComponents`.
20
+ */
21
+ export function initializeGatewayPackageLogLevels(options) {
22
+ if (!packageLogLevelsInitialized) {
23
+ applyPackageLogLevelsFromEnv();
24
+ packageLogLevelsInitialized = true;
25
+ }
26
+ if (options?.packageLogLevels) {
27
+ mergePackageLogLevelsConfig(options.packageLogLevels);
28
+ }
29
+ }
30
+ /** Reset init flag (tests). */
31
+ export function resetGatewayPackageLogLevelsInit() {
32
+ packageLogLevelsInitialized = false;
33
+ }
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Gateway operational mode (prod vs dev/debug) and default model resolution.
3
3
  */
4
- import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
4
+ import { gatewayLogDebug } from './gateway-log-meta.js';
5
+ import { fieldEvidence, GatewayLogCode, gatewayWarnCode } from './gateway-log-diagnostics.js';
5
6
  /** Profile name resolved via ai-tools + {@link @x12i/ai-profiles} when catalog is enabled. */
6
7
  export const CODE_DEFAULT_MODEL = 'cheap';
7
8
  /**
@@ -69,8 +70,16 @@ export function resolveGatewayDefaultModel(defaultModelConfig, gatewayDefaultEng
69
70
  };
70
71
  }
71
72
  export function warnDefaultModelSubstitution(logger, identity, details) {
72
- logger.warn('Gateway substituted default model for request', withActivityIdentity(identity, {
73
+ gatewayWarnCode(logger, GatewayLogCode.DEFAULT_MODEL_SUBSTITUTED, identity, {
73
74
  ...details,
74
- debugKind: gatewayLogDebug.anomaly
75
- }));
75
+ debugKind: gatewayLogDebug.anomaly,
76
+ evidence: [
77
+ fieldEvidence('defaultModel', details.defaultModel),
78
+ fieldEvidence('defaultSource', details.defaultSource),
79
+ fieldEvidence('reason', details.reason),
80
+ fieldEvidence('mode', details.mode),
81
+ ...(details.originalModel ? [fieldEvidence('originalModel', details.originalModel)] : []),
82
+ ...(details.originalProvider ? [fieldEvidence('originalProvider', details.originalProvider)] : [])
83
+ ]
84
+ });
76
85
  }
@@ -5,6 +5,7 @@
5
5
  * NOTE: Retry delays use SIMPLE SLEEP (not smart rate limiting).
6
6
  * Between-calls rate limiting is handled separately in gateway-rate-limiter.ts (smart).
7
7
  */
8
+ import { exceptionEvidence, fieldEvidence, GatewayLogCode, gatewayWarnCode } from './gateway-log-diagnostics.js';
8
9
  /**
9
10
  * Determines if an error is a network error (fetch failed, DNS, connectivity)
10
11
  */
@@ -146,12 +147,18 @@ export async function invokeWithRetry(routerRequest, retryConfig, jobId, router,
146
147
  }
147
148
  // If this was the last attempt, throw the error
148
149
  if (attempt >= maxRetries) {
149
- logger.warn('Max retries exceeded', {
150
+ gatewayWarnCode(logger, GatewayLogCode.RETRY_MAX_EXCEEDED, undefined, {
150
151
  jobId,
151
152
  error: lastError.message,
152
153
  errorName: lastError.name,
153
154
  totalAttempts: attempt + 1,
154
- maxRetries
155
+ maxRetries,
156
+ evidence: [
157
+ fieldEvidence('jobId', jobId),
158
+ fieldEvidence('totalAttempts', attempt + 1),
159
+ fieldEvidence('maxRetries', maxRetries),
160
+ exceptionEvidence(lastError)
161
+ ]
155
162
  });
156
163
  throw lastError;
157
164
  }
@@ -187,7 +194,7 @@ export async function invokeWithRetry(routerRequest, retryConfig, jobId, router,
187
194
  errorType,
188
195
  delayMs: Math.round(delay)
189
196
  });
190
- logger.warn('Retrying request after error', {
197
+ gatewayWarnCode(logger, GatewayLogCode.RETRY_ATTEMPT, undefined, {
191
198
  jobId,
192
199
  error: lastError.message,
193
200
  errorName: lastError.name,
@@ -195,7 +202,15 @@ export async function invokeWithRetry(routerRequest, retryConfig, jobId, router,
195
202
  attempt: attempt + 1,
196
203
  maxRetries: maxRetries + 1,
197
204
  delayMs: Math.round(delay),
198
- statusCode
205
+ statusCode,
206
+ evidence: [
207
+ fieldEvidence('jobId', jobId),
208
+ fieldEvidence('attempt', attempt + 1),
209
+ fieldEvidence('errorType', errorType),
210
+ fieldEvidence('delayMs', Math.round(delay)),
211
+ ...(statusCode !== undefined ? [fieldEvidence('statusCode', statusCode)] : []),
212
+ exceptionEvidence(lastError)
213
+ ]
199
214
  });
200
215
  await sleep(delay);
201
216
  }
package/dist/gateway.js CHANGED
@@ -16,6 +16,7 @@ import { autoRegisterProviders } from './gateway-provider-auto-register.js';
16
16
  import { applyOpenRouterInvokePolicy } from './ai-tools-client.js';
17
17
  import { setGatewayLastJobId, setGatewayRuntimeClients } from './runtime-objects.js';
18
18
  import { gatewayLogDebug, withActivityIdentity, withGatewayLogContext } from './gateway-log-meta.js';
19
+ import { exceptionEvidence, fieldEvidence, GatewayLogCode, gatewayErrorCode, gatewayWarnCode } from './gateway-log-diagnostics.js';
19
20
  import { invokeWithRetry } from './gateway-retry.js';
20
21
  /** Error message thrown by the router when no provider is registered or specified */
21
22
  const NO_PROVIDER_ERROR = 'No provider specified and no providers registered';
@@ -28,16 +29,24 @@ function warnIfSuccessfulInvokeReportsZeroUsageOrCost(logger, identity, meta, in
28
29
  const zeroCost = typeof cost === 'number' && cost === 0;
29
30
  if (!zeroTokens && !zeroCostUsd && !zeroCost)
30
31
  return;
31
- logger.warn('Successful provider response reported zero token usage and/or zero cost; verify router adapter usage and billing metadata', withActivityIdentity(identity, {
32
+ gatewayWarnCode(logger, GatewayLogCode.ZERO_USAGE_OR_COST, identity, {
32
33
  invokeKind,
33
34
  zeroTokens,
34
35
  zeroCostUsd,
35
36
  zeroCostField: zeroCost,
36
- tokens,
37
- costUsd,
38
- cost,
39
- debugKind: gatewayLogDebug.anomaly
40
- }));
37
+ debugKind: gatewayLogDebug.anomaly,
38
+ evidence: [
39
+ fieldEvidence('tokens.prompt', tokens.prompt),
40
+ fieldEvidence('tokens.completion', tokens.completion),
41
+ fieldEvidence('tokens.total', tokens.total),
42
+ fieldEvidence('costUsd', costUsd),
43
+ fieldEvidence('cost', cost)
44
+ ],
45
+ diagnostics: {
46
+ expected: 'Successful invoke should include non-zero token usage or cost when billing metadata is available.',
47
+ actual: `zeroTokens=${zeroTokens}, zeroCostUsd=${zeroCostUsd}, zeroCost=${zeroCost}`
48
+ }
49
+ });
41
50
  }
42
51
  /**
43
52
  * Simplified AI Gateway - Clean proxy implementation
@@ -494,12 +503,20 @@ export class AIGateway {
494
503
  if (!response) {
495
504
  const fallbackAttempts = buildGatewayFallbackAttemptsFromTrace(traceAttempts, deduped, lastError);
496
505
  const providersTried = [...new Set(deduped.map((c) => c.provider))];
497
- this.logger.error('Trace fallback chain exhausted', withActivityIdentity(request.identity, {
506
+ gatewayErrorCode(this.logger, GatewayLogCode.FALLBACK_CHAIN_EXHAUSTED, request.identity, {
498
507
  providersTried,
499
- candidates: deduped,
500
- fallbackAttempts,
501
- debugKind: gatewayLogDebug.anomaly
502
- }));
508
+ debugKind: gatewayLogDebug.anomaly,
509
+ evidence: [
510
+ fieldEvidence('providersTried', providersTried),
511
+ fieldEvidence('candidateCount', deduped.length),
512
+ fieldEvidence('fallbackAttempts', fallbackAttempts),
513
+ ...(lastError ? [fieldEvidence('lastError.message', lastError.message)] : [])
514
+ ],
515
+ diagnostics: {
516
+ expected: 'At least one provider/model candidate in the fallback chain should succeed.',
517
+ actual: 'All candidates failed or returned no response.'
518
+ }
519
+ });
503
520
  const exhausted = new FallbackExhaustedError(mapGatewayFallbackAttemptsToRouter(fallbackAttempts));
504
521
  exhausted.message = formatFallbackExhaustionMessage(fallbackAttempts, deduped);
505
522
  if (lastError) {
@@ -570,23 +587,32 @@ export class AIGateway {
570
587
  }
571
588
  else {
572
589
  // Extraction failed, fall back to raw text wrapper
573
- this.logger.warn('Flex-md extraction failed - no structured data extracted', withActivityIdentity(request.identity, {
590
+ gatewayWarnCode(this.logger, GatewayLogCode.FLEX_MD_EXTRACTION_FAILED, request.identity, {
574
591
  hasResult: !!extractionResult,
575
592
  method: extractionResult?.method || 'none',
576
- debugKind: gatewayLogDebug.anomaly
577
- }));
593
+ debugKind: gatewayLogDebug.anomaly,
594
+ evidence: [
595
+ fieldEvidence('content.length', content.length),
596
+ fieldEvidence('extraction.method', extractionResult?.method || 'none')
597
+ ]
598
+ });
578
599
  parsedContent = { rawText: content };
579
600
  }
580
601
  }
581
602
  catch (extractionError) {
582
603
  // Extraction failed, fall back to raw text wrapper
583
604
  const errorMessage = extractionError instanceof Error ? extractionError.message : String(extractionError);
584
- this.logger.warn('Flex-md extraction failed - flex-md library compatibility issue', withActivityIdentity(request.identity, {
605
+ gatewayWarnCode(this.logger, GatewayLogCode.FLEX_MD_EXTRACTION_ERROR, request.identity, {
585
606
  error: errorMessage,
586
- issue: 'flex-md uses require() in ES module context - needs fixing in flex-md-loader.ts',
587
607
  fallback: 'using rawText wrapper',
588
- debugKind: gatewayLogDebug.anomaly
589
- }));
608
+ debugKind: gatewayLogDebug.anomaly,
609
+ evidence: [
610
+ fieldEvidence('content.length', content.length),
611
+ ...(extractionError instanceof Error
612
+ ? [exceptionEvidence(extractionError)]
613
+ : [fieldEvidence('error.message', errorMessage)])
614
+ ]
615
+ });
590
616
  parsedContent = { rawText: content };
591
617
  }
592
618
  contentType = 'structured';
package/dist/index.d.ts CHANGED
@@ -35,9 +35,13 @@ export { ActivityManager, ensureGatewayRequestIdentity } from './activity-manage
35
35
  export { OptimixerManager } from './optimixer-manager.js';
36
36
  export type { ActivityIdentity } from './types.js';
37
37
  export { activityIdentityToLogContext, activityIdentityToLogMeta, withActivityIdentity, withGatewayLogContext, gatewayLogDebug } from './gateway-log-meta.js';
38
- export { createGatewayLogger, GATEWAY_LOG_ENV_PREFIX } from './logger-factory.js';
39
- export { createLogxer, DebugLogAbstract, runWithLogContext, getStationRuntimeIdentity, mergeRuntimeIdentity } from '@x12i/logxer';
40
- export type { Logxer, LogMeta, RuntimeIdentity, LogRuntimeContext, GetJobLogsInput, GetJobLogsResult, QueryableLogLine } from '@x12i/logxer';
38
+ export { createGatewayLogger } from './logger-factory.js';
39
+ export { GATEWAY_LOG_ENV_PREFIX, GATEWAY_STACK_LOG_PREFIXES, initializeGatewayPackageLogLevels, resetGatewayPackageLogLevelsInit } from './gateway-log-levels.js';
40
+ export { GatewayLogCode, gatewayErrorCode, gatewayInfoCode, gatewayWarnCode, gatewayAnomalyMeta, resolveLogDiagnosticsCatalogPath, exceptionEvidence, fieldEvidence } from './gateway-log-diagnostics.js';
41
+ export type { GatewayLogCode as GatewayDiagnosticCode } from './gateway-log-diagnostics.js';
42
+ export { createLogxer, DebugLogAbstract, runWithLogContext, getStationRuntimeIdentity, mergeRuntimeIdentity, conditionEvidence, sourceEvidence, logReferenceEvidence, readAgentLoggingInstructions, resolveAgentLoggingInstructionsPath, applyPackageLogLevelsFromEnv, configurePackageLogLevels, mergePackageLogLevelsConfig, setPackageLogLevel, resolveStackLogLevelForPrefix, resolvePackageLogsLevel, parseLogxerPackageLevelsEnv, LOGXER_PACKAGE_LEVELS_ENV, LOGXER_PACKAGE_LOGS_DEFAULT_ENV } from '@x12i/logxer';
43
+ export { ROUTER_LOG_ENV_PREFIX } from '@x12i/ai-providers-router';
44
+ export type { Logxer, LogMeta, RuntimeIdentity, LogRuntimeContext, GetJobLogsInput, GetJobLogsResult, QueryableLogLine, LogDiagnostics, DiagnosticEvidence, ScopeCriteria, ScopeLogsResult, StackLoggingOptions, PackageLogLevelsConfig, PackageLogLevelSetting } from '@x12i/logxer';
41
45
  export { runtimeObjects } from './runtime-objects.js';
42
46
  export type { ActivixQueryableClient, LogxerQueryableClient, PackageRuntimeObjects, RuntimeObjects } from './runtime-objects.js';
43
47
  export { GatewayRateLimiter } from './gateway-rate-limiter.js';
package/dist/index.js CHANGED
@@ -30,9 +30,12 @@ export { normalizeToActivixCostShape } from '@x12i/activix';
30
30
  export { ActivityManager, ensureGatewayRequestIdentity } from './activity-manager.js';
31
31
  export { OptimixerManager } from './optimixer-manager.js';
32
32
  export { activityIdentityToLogContext, activityIdentityToLogMeta, withActivityIdentity, withGatewayLogContext, gatewayLogDebug } from './gateway-log-meta.js';
33
- export { createGatewayLogger, GATEWAY_LOG_ENV_PREFIX } from './logger-factory.js';
33
+ export { createGatewayLogger } from './logger-factory.js';
34
+ export { GATEWAY_LOG_ENV_PREFIX, GATEWAY_STACK_LOG_PREFIXES, initializeGatewayPackageLogLevels, resetGatewayPackageLogLevelsInit } from './gateway-log-levels.js';
35
+ export { GatewayLogCode, gatewayErrorCode, gatewayInfoCode, gatewayWarnCode, gatewayAnomalyMeta, resolveLogDiagnosticsCatalogPath, exceptionEvidence, fieldEvidence } from './gateway-log-diagnostics.js';
34
36
  // Re-export logging (@x12i/logxer)
35
- export { createLogxer, DebugLogAbstract, runWithLogContext, getStationRuntimeIdentity, mergeRuntimeIdentity } from '@x12i/logxer';
37
+ export { createLogxer, DebugLogAbstract, runWithLogContext, getStationRuntimeIdentity, mergeRuntimeIdentity, conditionEvidence, sourceEvidence, logReferenceEvidence, readAgentLoggingInstructions, resolveAgentLoggingInstructionsPath, applyPackageLogLevelsFromEnv, configurePackageLogLevels, mergePackageLogLevelsConfig, setPackageLogLevel, resolveStackLogLevelForPrefix, resolvePackageLogsLevel, parseLogxerPackageLevelsEnv, LOGXER_PACKAGE_LEVELS_ENV, LOGXER_PACKAGE_LOGS_DEFAULT_ENV } from '@x12i/logxer';
38
+ export { ROUTER_LOG_ENV_PREFIX } from '@x12i/ai-providers-router';
36
39
  // Runtime observability surface (leaf package: no downstream runtime objects)
37
40
  export { runtimeObjects } from './runtime-objects.js';
38
41
  // Export rate limiter
@@ -3,9 +3,11 @@
3
3
  *
4
4
  * Creates and configures logxer instances for the gateway
5
5
  */
6
- import { type Logxer } from '@x12i/logxer';
7
- /** Stable ERC 2.0 env prefix — controls `AI_GATEWAY_LOGS_LEVEL`, etc. */
8
- export declare const GATEWAY_LOG_ENV_PREFIX = "AI_GATEWAY";
6
+ import { type LogLevel, type Logxer, type PackageLogLevelsConfig, type StackLoggingOptions } from '@x12i/logxer';
7
+ import { GATEWAY_LOG_ENV_PREFIX } from './gateway-log-levels.js';
8
+ export { GATEWAY_LOG_ENV_PREFIX };
9
+ /** True for the internal no-op logger when `enableLogging` is false. */
10
+ export declare function isNoOpGatewayLogger(logger: Logxer): boolean;
9
11
  /**
10
12
  * Creates a logger instance based on configuration
11
13
  *
@@ -16,4 +18,10 @@ export declare function createGatewayLogger(config: {
16
18
  enableLogging: boolean;
17
19
  packageName?: string;
18
20
  customLogger?: Logxer;
21
+ /** Parent/host pass-through (logxer ≥ 4.5). Forwarded as `createLogxer(..., { stack })`. */
22
+ logging?: StackLoggingOptions;
23
+ /** Merged into the process registry after `applyPackageLogLevelsFromEnv()`. */
24
+ packageLogLevels?: PackageLogLevelsConfig;
25
+ /** Explicit level for this gateway logger instance (wins over stack/registry/env). */
26
+ logLevel?: LogLevel;
19
27
  }): Logxer;