@x12i/ai-gateway 9.6.7 → 9.6.8

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.
@@ -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';
@@ -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
@@ -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';
@@ -4,8 +4,14 @@
4
4
  * Creates and configures logxer instances for the gateway
5
5
  */
6
6
  import { createLogxer, getStationRuntimeIdentity, mergeRuntimeIdentity } from '@x12i/logxer';
7
- /** Stable ERC 2.0 env prefix — controls `AI_GATEWAY_LOGS_LEVEL`, etc. */
8
- export const GATEWAY_LOG_ENV_PREFIX = 'AI_GATEWAY';
7
+ import { resolveLogDiagnosticsCatalogPath } from './gateway-log-diagnostics.js';
8
+ import { GATEWAY_LOG_ENV_PREFIX, initializeGatewayPackageLogLevels } from './gateway-log-levels.js';
9
+ export { GATEWAY_LOG_ENV_PREFIX };
10
+ const NOOP_GATEWAY_LOGGER = Symbol('noop-gateway-logger');
11
+ /** True for the internal no-op logger when `enableLogging` is false. */
12
+ export function isNoOpGatewayLogger(logger) {
13
+ return logger[NOOP_GATEWAY_LOGGER] === true;
14
+ }
9
15
  function resolveGatewayRuntimeIdentity(packageName) {
10
16
  return mergeRuntimeIdentity(getStationRuntimeIdentity(), {
11
17
  service: process.env.AI_GATEWAY_LOG_SERVICE ?? packageName ?? GATEWAY_LOG_ENV_PREFIX,
@@ -25,26 +31,46 @@ export function createGatewayLogger(config) {
25
31
  if (!config.enableLogging) {
26
32
  return createNoOpLogger();
27
33
  }
34
+ initializeGatewayPackageLogLevels({ packageLogLevels: config.packageLogLevels });
35
+ const catalogPath = resolveLogDiagnosticsCatalogPath();
28
36
  return createLogxer({
29
37
  packageName: config.packageName || GATEWAY_LOG_ENV_PREFIX,
30
38
  envPrefix: GATEWAY_LOG_ENV_PREFIX,
31
39
  debugNamespace: 'ai-gateway'
32
40
  }, {
33
- // Level/format/sinks: ERC 2.0 env discovery (`AI_GATEWAY_LOGS_LEVEL`, host-level format, …).
34
- runtimeIdentity: resolveGatewayRuntimeIdentity(config.packageName)
41
+ runtimeIdentity: resolveGatewayRuntimeIdentity(config.packageName),
42
+ stack: config.logging,
43
+ ...(config.logLevel !== undefined ? { logLevel: config.logLevel } : {}),
44
+ ...(catalogPath
45
+ ? {
46
+ diagnostics: {
47
+ catalogPath,
48
+ consoleMode: 'summary'
49
+ }
50
+ }
51
+ : {})
35
52
  });
36
53
  }
37
54
  function createNoOpLogger() {
38
55
  const noop = () => { };
39
- return {
56
+ const noopAsync = async () => ({ lines: [], total: 0 });
57
+ const logger = {
40
58
  verbose: noop,
41
59
  debug: noop,
42
60
  info: noop,
43
61
  warn: noop,
44
62
  error: noop,
45
63
  success: noop,
64
+ infoCode: noop,
65
+ warnCode: noop,
66
+ errorCode: noop,
67
+ diagnostic: noop,
68
+ getJobLogs: noopAsync,
69
+ scopeLogs: noopAsync,
46
70
  isLevelEnabled: () => false,
47
71
  getConfig: () => ({}),
48
- shadow: {}
72
+ shadow: {},
73
+ [NOOP_GATEWAY_LOGGER]: true
49
74
  };
75
+ return logger;
50
76
  }
@@ -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;
@@ -1,4 +1,5 @@
1
1
  import { Optimixer } from '@x12i/optimixer';
2
+ import { exceptionEvidence, fieldEvidence, GatewayLogCode, gatewayWarnCode } from './gateway-log-diagnostics.js';
2
3
  import { resolveActivityTrackingConfig } from './config/activity-tracking-config.js';
3
4
  import { estimateMessagesTokenSizes } from './token-estimate.js';
4
5
  /** Optimixer bucket key: prefer taskTypeId (template), then identity actionType, else gateway default. */
@@ -47,8 +48,9 @@ export class OptimixerManager {
47
48
  async initialize() {
48
49
  const activix = await this.getActivix();
49
50
  if (!activix) {
50
- this.logger.warn('Optimixer enabled but Activix is unavailable; adaptive max_tokens disabled', {
51
- activixCollection: this.activixCollection
51
+ gatewayWarnCode(this.logger, GatewayLogCode.OPTIMIXER_ACTIVIX_UNAVAILABLE, undefined, {
52
+ activixCollection: this.activixCollection,
53
+ evidence: [fieldEvidence('activixCollection', this.activixCollection)]
52
54
  });
53
55
  return;
54
56
  }
@@ -65,8 +67,12 @@ export class OptimixerManager {
65
67
  });
66
68
  }
67
69
  catch (error) {
68
- this.logger.warn('Optimixer initialization failed; adaptive max_tokens disabled', {
69
- error: error instanceof Error ? error.message : String(error)
70
+ gatewayWarnCode(this.logger, GatewayLogCode.OPTIMIXER_INIT_FAILED, undefined, {
71
+ error: error instanceof Error ? error.message : String(error),
72
+ evidence: [
73
+ fieldEvidence('activixCollection', this.activixCollection),
74
+ ...(error instanceof Error ? [exceptionEvidence(error)] : [])
75
+ ]
70
76
  });
71
77
  this.optimixer = undefined;
72
78
  }
@@ -93,9 +99,13 @@ export class OptimixerManager {
93
99
  });
94
100
  }
95
101
  catch (error) {
96
- this.logger.warn('Optimixer predictAiMaxTokens failed; caller should use fallback max_tokens', {
102
+ gatewayWarnCode(this.logger, GatewayLogCode.OPTIMIXER_PREDICT_FAILED, request.identity, {
97
103
  error: error instanceof Error ? error.message : String(error),
98
- aiRequestId: request.aiRequestId
104
+ aiRequestId: request.aiRequestId,
105
+ evidence: [
106
+ fieldEvidence('aiRequestId', request.aiRequestId),
107
+ ...(error instanceof Error ? [exceptionEvidence(error)] : [])
108
+ ]
99
109
  });
100
110
  return undefined;
101
111
  }
@@ -1,3 +1,4 @@
1
+ import { isNoOpGatewayLogger } from './logger-factory.js';
1
2
  export let runtimeObjects = createRuntimeObjectsForMode();
2
3
  export function setGatewayLastJobId(jobId) {
3
4
  const objects = ensureRuntimeObjectsForCurrentMode();
@@ -41,6 +42,14 @@ function asActivixQueryableClient(activix) {
41
42
  return typeof candidate.getJobActivities === 'function' ? candidate : undefined;
42
43
  }
43
44
  function asLogxerQueryableClient(logger) {
45
+ if (isNoOpGatewayLogger(logger)) {
46
+ return undefined;
47
+ }
44
48
  const candidate = logger;
45
- return typeof candidate.getJobLogs === 'function' ? candidate : undefined;
49
+ if (typeof candidate.getJobLogs !== 'function') {
50
+ return undefined;
51
+ }
52
+ return typeof candidate.scopeLogs === 'function'
53
+ ? { getJobLogs: candidate.getJobLogs.bind(candidate), scopeLogs: candidate.scopeLogs.bind(candidate) }
54
+ : { getJobLogs: candidate.getJobLogs.bind(candidate) };
46
55
  }
@@ -2,7 +2,7 @@ import type { Logxer } from '@x12i/logxer';
2
2
  import type { Activix, ActivixQueryableClient } from '@x12i/activix';
3
3
  export type { ActivixQueryableClient } from '@x12i/activix';
4
4
  export type { GetJobLogsInput, GetJobLogsResult } from '@x12i/logxer';
5
- export type LogxerQueryableClient = Pick<Logxer, 'getJobLogs'>;
5
+ export type LogxerQueryableClient = Pick<Logxer, 'getJobLogs'> & Partial<Pick<Logxer, 'scopeLogs'>>;
6
6
  export type PackageRuntimeObjects = {
7
7
  name: string;
8
8
  activixClient?: ActivixQueryableClient;
@@ -9,7 +9,7 @@ type AIModel = string;
9
9
  export type UsageTier = string;
10
10
  import type { Activix } from '@x12i/activix';
11
11
  import type { SmartInputConfig, SmartInputRenderOptions, TemplateRenderOptions } from '@x12i/rendrix';
12
- import type { Logxer } from '@x12i/logxer';
12
+ import type { Logxer, PackageLogLevelsConfig } from '@x12i/logxer';
13
13
  /**
14
14
  * Diagnostics options for opt-in authoritative tracing.
15
15
  * Default behavior must remain minimal when diagnostics are not enabled.
@@ -349,6 +349,13 @@ export interface GatewayConfig extends Omit<RouterConfig, 'defaultEngine' | 'log
349
349
  * @default true
350
350
  */
351
351
  enableLogging?: boolean;
352
+ /**
353
+ * Process-wide package log level registry (logxer ≥ 4.5).
354
+ * Merged after `applyPackageLogLevelsFromEnv()` on first gateway init.
355
+ * `logging` (from {@link RouterConfig}) is forwarded to the gateway logger and router.
356
+ * @example `{ default: 'warn', levels: { AI_GATEWAY: 'info', AI_PROVIDER_ROUTER: 'debug' } }`
357
+ */
358
+ packageLogLevels?: PackageLogLevelsConfig;
352
359
  /**
353
360
  * Custom logger instance (optional)
354
361
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x12i/ai-gateway",
3
- "version": "9.6.7",
3
+ "version": "9.6.8",
4
4
  "description": "AI Gateway - Unified interface for LLM provider routing and management",
5
5
  "type": "module",
6
6
  "exports": {
@@ -42,11 +42,11 @@
42
42
  "license": "mit",
43
43
  "dependencies": {
44
44
  "@x12i/activix": "^8.3.1",
45
- "@x12i/ai-providers-router": "^4.8.6",
45
+ "@x12i/ai-providers-router": "^4.8.8",
46
46
  "@x12i/ai-tools": "^2.1.2",
47
47
  "@x12i/flex-md": "^4.8.0",
48
- "@x12i/logxer": "^4.4.0",
49
- "@x12i/optimixer": "^2.4.1",
48
+ "@x12i/logxer": "^4.5.0",
49
+ "@x12i/optimixer": "^2.4.2",
50
50
  "@x12i/rendrix": "^4.3.0"
51
51
  },
52
52
  "devDependencies": {