@x12i/ai-gateway 9.6.3 → 9.6.5

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 (40) hide show
  1. package/README.md +10 -3
  2. package/dist/ai-tools-client.d.ts +27 -3
  3. package/dist/ai-tools-client.js +54 -8
  4. package/dist/gateway-config.d.ts +2 -0
  5. package/dist/gateway-config.js +16 -10
  6. package/dist/gateway-log-meta.d.ts +5 -1
  7. package/dist/gateway-log-meta.js +19 -1
  8. package/dist/gateway-provider-auto-register.js +1 -1
  9. package/dist/gateway-utils.d.ts +2 -1
  10. package/dist/gateway-utils.js +9 -7
  11. package/dist/gateway.d.ts +2 -0
  12. package/dist/gateway.js +601 -578
  13. package/dist/index.d.ts +4 -3
  14. package/dist/index.js +3 -2
  15. package/dist/logger-factory.d.ts +2 -0
  16. package/dist/logger-factory.js +11 -14
  17. package/dist/openrouter-routing.d.ts +12 -0
  18. package/dist/openrouter-routing.js +27 -0
  19. package/dist/runtime-objects.d.ts +2 -19
  20. package/dist/types.d.ts +4 -1
  21. package/dist-cjs/ai-tools-client.cjs +54 -8
  22. package/dist-cjs/ai-tools-client.d.ts +27 -3
  23. package/dist-cjs/gateway-config.cjs +16 -10
  24. package/dist-cjs/gateway-config.d.ts +2 -0
  25. package/dist-cjs/gateway-log-meta.cjs +19 -1
  26. package/dist-cjs/gateway-log-meta.d.ts +5 -1
  27. package/dist-cjs/gateway-provider-auto-register.cjs +1 -1
  28. package/dist-cjs/gateway-utils.cjs +9 -7
  29. package/dist-cjs/gateway-utils.d.ts +2 -1
  30. package/dist-cjs/gateway.cjs +601 -578
  31. package/dist-cjs/gateway.d.ts +2 -0
  32. package/dist-cjs/index.cjs +3 -2
  33. package/dist-cjs/index.d.ts +4 -3
  34. package/dist-cjs/logger-factory.cjs +11 -14
  35. package/dist-cjs/logger-factory.d.ts +2 -0
  36. package/dist-cjs/openrouter-routing.cjs +27 -0
  37. package/dist-cjs/openrouter-routing.d.ts +12 -0
  38. package/dist-cjs/runtime-objects.d.ts +2 -19
  39. package/dist-cjs/types.d.ts +4 -1
  40. package/package.json +5 -5
package/dist/index.d.ts CHANGED
@@ -34,9 +34,10 @@ export { normalizeToActivixCostShape } from '@x12i/activix';
34
34
  export { ActivityManager, ensureGatewayRequestIdentity } from './activity-manager.js';
35
35
  export { OptimixerManager } from './optimixer-manager.js';
36
36
  export type { ActivityIdentity } from './types.js';
37
- export { activityIdentityToLogMeta, withActivityIdentity, gatewayLogDebug } from './gateway-log-meta.js';
38
- export { createLogxer, DebugLogAbstract } from '@x12i/logxer';
39
- export type { Logxer, LogMeta, RuntimeIdentity } from '@x12i/logxer';
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';
40
41
  export { runtimeObjects } from './runtime-objects.js';
41
42
  export type { ActivixQueryableClient, LogxerQueryableClient, PackageRuntimeObjects, RuntimeObjects } from './runtime-objects.js';
42
43
  export { GatewayRateLimiter } from './gateway-rate-limiter.js';
package/dist/index.js CHANGED
@@ -29,9 +29,10 @@ export { Activix } from '@x12i/activix';
29
29
  export { normalizeToActivixCostShape } from '@x12i/activix';
30
30
  export { ActivityManager, ensureGatewayRequestIdentity } from './activity-manager.js';
31
31
  export { OptimixerManager } from './optimixer-manager.js';
32
- export { activityIdentityToLogMeta, withActivityIdentity, gatewayLogDebug } from './gateway-log-meta.js';
32
+ export { activityIdentityToLogContext, activityIdentityToLogMeta, withActivityIdentity, withGatewayLogContext, gatewayLogDebug } from './gateway-log-meta.js';
33
+ export { createGatewayLogger, GATEWAY_LOG_ENV_PREFIX } from './logger-factory.js';
33
34
  // Re-export logging (@x12i/logxer)
34
- export { createLogxer, DebugLogAbstract } from '@x12i/logxer';
35
+ export { createLogxer, DebugLogAbstract, runWithLogContext, getStationRuntimeIdentity, mergeRuntimeIdentity } from '@x12i/logxer';
35
36
  // Runtime observability surface (leaf package: no downstream runtime objects)
36
37
  export { runtimeObjects } from './runtime-objects.js';
37
38
  // Export rate limiter
@@ -4,6 +4,8 @@
4
4
  * Creates and configures logxer instances for the gateway
5
5
  */
6
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";
7
9
  /**
8
10
  * Creates a logger instance based on configuration
9
11
  *
@@ -3,16 +3,14 @@
3
3
  *
4
4
  * Creates and configures logxer instances for the gateway
5
5
  */
6
- import { createLogxer } from '@x12i/logxer';
7
- function resolveDefaultRuntimeIdentity(packageName) {
8
- const service = process.env.AI_GATEWAY_LOG_SERVICE ?? packageName ?? 'AI_GATEWAY';
9
- if (!service)
10
- return undefined;
11
- return {
12
- service,
13
- env: process.env.NODE_ENV,
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';
9
+ function resolveGatewayRuntimeIdentity(packageName) {
10
+ return mergeRuntimeIdentity(getStationRuntimeIdentity(), {
11
+ service: process.env.AI_GATEWAY_LOG_SERVICE ?? packageName ?? GATEWAY_LOG_ENV_PREFIX,
14
12
  version: process.env.npm_package_version
15
- };
13
+ });
16
14
  }
17
15
  /**
18
16
  * Creates a logger instance based on configuration
@@ -28,13 +26,12 @@ export function createGatewayLogger(config) {
28
26
  return createNoOpLogger();
29
27
  }
30
28
  return createLogxer({
31
- packageName: config.packageName || 'AI_GATEWAY',
32
- envPrefix: config.packageName || 'AI_GATEWAY',
29
+ packageName: config.packageName || GATEWAY_LOG_ENV_PREFIX,
30
+ envPrefix: GATEWAY_LOG_ENV_PREFIX,
33
31
  debugNamespace: 'ai-gateway'
34
32
  }, {
35
- logLevel: 'info',
36
- logFormat: 'json',
37
- runtimeIdentity: resolveDefaultRuntimeIdentity(config.packageName)
33
+ // Level/format/sinks: ERC 2.0 env discovery (`AI_GATEWAY_LOGS_LEVEL`, host-level format, …).
34
+ runtimeIdentity: resolveGatewayRuntimeIdentity(config.packageName)
38
35
  });
39
36
  }
40
37
  function createNoOpLogger() {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * OpenRouter API key and gateway-level "prefer OpenRouter" flag.
3
+ * Provider/model routing (openrouter vs vendor) is resolved by @x12i/ai-tools in mergeConfig.
4
+ */
5
+ import type { GatewayConfig } from './types.js';
6
+ export declare function resolveOpenRouterApiKey(config?: GatewayConfig): string | undefined;
7
+ /**
8
+ * When true, pass `routeViaOpenRouter: true` into ai-tools resolveModel (prefer OpenRouter even with direct keys).
9
+ * Default: true when OPENROUTER_API_KEY is set. `USE_OPENROUTER=false` turns this off; ai-tools still falls back
10
+ * when a vendor API key is missing.
11
+ */
12
+ export declare function resolvePreferOpenRouter(config?: GatewayConfig): boolean;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * OpenRouter API key and gateway-level "prefer OpenRouter" flag.
3
+ * Provider/model routing (openrouter vs vendor) is resolved by @x12i/ai-tools in mergeConfig.
4
+ */
5
+ export function resolveOpenRouterApiKey(config = {}) {
6
+ const explicit = config.openrouter?.apiKey;
7
+ if (typeof explicit === 'string' && explicit.trim() && !explicit.startsWith('ENV.')) {
8
+ return explicit.trim();
9
+ }
10
+ const env = process.env.OPENROUTER_API_KEY?.trim();
11
+ return env || undefined;
12
+ }
13
+ /**
14
+ * When true, pass `routeViaOpenRouter: true` into ai-tools resolveModel (prefer OpenRouter even with direct keys).
15
+ * Default: true when OPENROUTER_API_KEY is set. `USE_OPENROUTER=false` turns this off; ai-tools still falls back
16
+ * when a vendor API key is missing.
17
+ */
18
+ export function resolvePreferOpenRouter(config = {}) {
19
+ if (config.openRouter?.enabled === true)
20
+ return true;
21
+ if (config.openRouter?.enabled === false)
22
+ return false;
23
+ const env = process.env.USE_OPENROUTER;
24
+ if (env === 'false' || env === '0')
25
+ return false;
26
+ return !!resolveOpenRouterApiKey(config);
27
+ }
@@ -1,25 +1,8 @@
1
1
  import type { Logxer } from '@x12i/logxer';
2
2
  import type { Activix, ActivixQueryableClient } from '@x12i/activix';
3
3
  export type { ActivixQueryableClient } from '@x12i/activix';
4
- export type LogxerQueryableClient = {
5
- getJobLogs(input: {
6
- jobId: string;
7
- graphId?: string;
8
- nodeId?: string;
9
- limit?: number;
10
- }): Promise<{
11
- jobId: string;
12
- lines: Array<{
13
- ts?: number | string;
14
- level?: 'debug' | 'info' | 'warn' | 'error';
15
- scope?: string;
16
- packageName?: string;
17
- nodeId?: string;
18
- message: string;
19
- data?: unknown;
20
- }>;
21
- }>;
22
- };
4
+ export type { GetJobLogsInput, GetJobLogsResult } from '@x12i/logxer';
5
+ export type LogxerQueryableClient = Pick<Logxer, 'getJobLogs'>;
23
6
  export type PackageRuntimeObjects = {
24
7
  name: string;
25
8
  activixClient?: ActivixQueryableClient;
package/dist/types.d.ts CHANGED
@@ -375,10 +375,13 @@ export interface GatewayConfig extends Omit<RouterConfig, 'defaultEngine' | 'log
375
375
  apiKey: string;
376
376
  };
377
377
  /**
378
- * Enable OpenRouter mode. When omitted, the gateway uses process.env.USE_OPENROUTER or enables when a key is set.
378
+ * OpenRouter preference (not a hard off-switch when {@link openrouter}.apiKey or OPENROUTER_API_KEY is set).
379
+ * - `enabled: true` or omitted with OPENROUTER_API_KEY: prefer OpenRouter even when direct provider keys exist.
380
+ * - `enabled: false` or USE_OPENROUTER=false: use direct providers when their API keys exist; OpenRouter still used as fallback when a requested provider has no key.
379
381
  */
380
382
  openRouter?: {
381
383
  enabled?: boolean;
384
+ prefer?: boolean;
382
385
  };
383
386
  /**
384
387
  * Operational mode override (`process.env.mode` / `MODE` when omitted).
@@ -1,14 +1,26 @@
1
1
  /**
2
2
  * Lazy @x12i/ai-tools catalog + cost calculator bootstrap.
3
3
  */
4
- import { AiModelsCatalogClient, CostCalculator } from '@x12i/ai-tools';
4
+ import { AiModelsCatalogClient, CostCalculator, isEffectiveOpenRouterTransport, loadOpenRouterRoutingEnv, resolveModelVendorFromResolution } from '@x12i/ai-tools';
5
5
  import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
6
+ import { resolvePreferOpenRouter } from './openrouter-routing.js';
6
7
  let sharedClientPromise = null;
7
8
  let sharedConfigKey;
8
9
  let bootstrapFailedLogged = false;
9
10
  function configKey(config) {
10
11
  return `${config.aiTools?.cacheTtlMs ?? ''}:${config.aiTools?.costIncludeBreakdown ?? ''}:${config.aiTools?.bundledOnly ?? ''}`;
11
12
  }
13
+ /**
14
+ * Per-invoke resolver options: ai-tools decides OpenRouter vs direct from env + optional gateway prefer override.
15
+ */
16
+ export function buildModelResolverOptions(config, routingEnv) {
17
+ const env = routingEnv ?? loadOpenRouterRoutingEnv();
18
+ const prefer = resolvePreferOpenRouter(config);
19
+ return {
20
+ routingEnv: env,
21
+ ...(prefer ? { routeViaOpenRouter: true } : {}),
22
+ };
23
+ }
12
24
  /**
13
25
  * Returns catalog + calculator, or null when disabled or bootstrap fails.
14
26
  */
@@ -34,33 +46,43 @@ export function resetAiToolsClientForTests() {
34
46
  }
35
47
  async function bootstrapAiTools(config, logger) {
36
48
  try {
49
+ const routingEnv = loadOpenRouterRoutingEnv();
37
50
  const catalog = new AiModelsCatalogClient({
38
51
  cacheTtlMs: config.aiTools?.cacheTtlMs,
39
- ...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {})
52
+ ...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {}),
53
+ resolverOptions: { routingEnv },
40
54
  });
41
55
  const calculator = new CostCalculator(catalog, {
42
- includeBreakdown: config.aiTools?.costIncludeBreakdown === true
56
+ includeBreakdown: config.aiTools?.costIncludeBreakdown === true,
43
57
  });
44
58
  logger.debug('ai-tools catalog client ready', {
45
- debugKind: gatewayLogDebug.state
59
+ debugKind: gatewayLogDebug.state,
46
60
  });
47
- return { catalog, calculator };
61
+ return { catalog, calculator, routingEnv };
48
62
  }
49
63
  catch (error) {
50
64
  if (!bootstrapFailedLogged) {
51
65
  bootstrapFailedLogged = true;
52
66
  logger.warn('ai-tools catalog bootstrap failed; model resolution and catalog cost calculation disabled', withActivityIdentity(undefined, {
53
67
  error: error instanceof Error ? error.message : String(error),
54
- debugKind: gatewayLogDebug.anomaly
68
+ debugKind: gatewayLogDebug.anomaly,
55
69
  }));
56
70
  }
57
71
  return null;
58
72
  }
59
73
  }
60
74
  /**
61
- * Map catalog resolution to router config provider/model fields.
75
+ * Map catalog resolution to router `{ provider, model }` (agnostic to openrouter vs vendor input).
62
76
  */
63
- export function applyModelResolution(merged, resolution, gatewayDefaultEngine) {
77
+ export function applyModelResolution(merged, resolution, gatewayDefaultEngine, inputModel) {
78
+ const ref = resolveModelVendorFromResolution(resolution, inputModel ?? merged.model ?? '', {
79
+ asOpenRouter: resolution.routedViaOpenRouter,
80
+ });
81
+ if (ref) {
82
+ merged.provider = ref.provider;
83
+ merged.model = ref.model;
84
+ return;
85
+ }
64
86
  if (resolution.routedViaOpenRouter) {
65
87
  merged.provider = 'openrouter';
66
88
  merged.model = resolution.modelId;
@@ -81,3 +103,27 @@ export function applyModelResolution(merged, resolution, gatewayDefaultEngine) {
81
103
  merged.provider = gatewayDefaultEngine;
82
104
  }
83
105
  }
106
+ /**
107
+ * Router invoke flags after mergeConfig + ai-tools resolution (OpenRouter vs direct transport).
108
+ */
109
+ export function applyOpenRouterInvokePolicy(merged, options) {
110
+ if (!options.openRouterApiKey?.trim())
111
+ return;
112
+ const routingEnv = options.routingEnv ?? loadOpenRouterRoutingEnv();
113
+ const viaOpenRouter = options.resolution?.routedViaOpenRouter === true ||
114
+ (options.resolution?.routedViaOpenRouter !== false &&
115
+ isEffectiveOpenRouterTransport(routingEnv, {
116
+ provider: merged.provider,
117
+ modelId: merged.model,
118
+ routeViaOpenRouter: options.preferOpenRouter ? true : undefined,
119
+ }));
120
+ if (viaOpenRouter) {
121
+ merged.allowOpenRouterProxy = true;
122
+ if (merged.provider && merged.provider !== 'openrouter') {
123
+ merged.providerProxy = 'openrouter';
124
+ }
125
+ }
126
+ else {
127
+ merged.allowOpenRouterProxy = false;
128
+ }
129
+ }
@@ -1,13 +1,18 @@
1
1
  /**
2
2
  * Lazy @x12i/ai-tools catalog + cost calculator bootstrap.
3
3
  */
4
- import { AiModelsCatalogClient, CostCalculator, type ModelResolutionSuccess } from '@x12i/ai-tools';
4
+ import { AiModelsCatalogClient, CostCalculator, type ModelResolutionSuccess, type ModelResolverOptions, type OpenRouterRoutingConfig } from '@x12i/ai-tools';
5
5
  import type { Logxer } from '@x12i/logxer';
6
6
  import type { ChatRequest, GatewayConfig } from './types.js';
7
7
  export type AiToolsClientBundle = {
8
8
  catalog: AiModelsCatalogClient;
9
9
  calculator: CostCalculator;
10
+ routingEnv: OpenRouterRoutingConfig;
10
11
  };
12
+ /**
13
+ * Per-invoke resolver options: ai-tools decides OpenRouter vs direct from env + optional gateway prefer override.
14
+ */
15
+ export declare function buildModelResolverOptions(config: GatewayConfig, routingEnv?: OpenRouterRoutingConfig): ModelResolverOptions;
11
16
  /**
12
17
  * Returns catalog + calculator, or null when disabled or bootstrap fails.
13
18
  */
@@ -15,6 +20,25 @@ export declare function getAiToolsClient(config: GatewayConfig, logger: Logxer):
15
20
  /** Reset singleton (tests). */
16
21
  export declare function resetAiToolsClientForTests(): void;
17
22
  /**
18
- * Map catalog resolution to router config provider/model fields.
23
+ * Map catalog resolution to router `{ provider, model }` (agnostic to openrouter vs vendor input).
24
+ */
25
+ export declare function applyModelResolution(merged: NonNullable<ChatRequest['config']>, resolution: ModelResolutionSuccess, gatewayDefaultEngine?: string, inputModel?: string): void;
26
+ type RouterConfigSlice = {
27
+ provider?: string;
28
+ model?: string;
29
+ allowOpenRouterProxy?: boolean;
30
+ providerProxy?: string;
31
+ };
32
+ type ModelResolutionMeta = {
33
+ routedViaOpenRouter?: boolean;
34
+ };
35
+ /**
36
+ * Router invoke flags after mergeConfig + ai-tools resolution (OpenRouter vs direct transport).
19
37
  */
20
- export declare function applyModelResolution(merged: NonNullable<ChatRequest['config']>, resolution: ModelResolutionSuccess, gatewayDefaultEngine?: string): void;
38
+ export declare function applyOpenRouterInvokePolicy(merged: RouterConfigSlice, options: {
39
+ openRouterApiKey?: string;
40
+ preferOpenRouter?: boolean;
41
+ routingEnv?: OpenRouterRoutingConfig;
42
+ resolution?: ModelResolutionMeta;
43
+ }): void;
44
+ export {};
@@ -5,6 +5,7 @@
5
5
  import * as fs from 'fs';
6
6
  import * as path from 'path';
7
7
  import { fileURLToPath } from 'url';
8
+ import { resolveOpenRouterApiKey, resolvePreferOpenRouter, } from './openrouter-routing.js';
8
9
  /** Resolve current module directory across ESM/CJS builds. */
9
10
  function getModuleDir() {
10
11
  if (typeof __dirname !== 'undefined') {
@@ -193,16 +194,19 @@ export function initializeGatewayComponents(config) {
193
194
  routerConfig.autoDiscover = config.autoDiscover;
194
195
  if (config.usageTracker !== undefined)
195
196
  routerConfig.usageTracker = config.usageTracker;
196
- // OpenRouter: enable when key is set and not explicitly disabled (so consumers get a working default without registering providers).
197
- // Prefer explicit config from consumer (e.g. ai-skills) to avoid env-loading timing; fall back to process.env.
198
- const explicitOpenRouterKey = config.openrouter?.apiKey;
199
- const isExplicitKey = typeof explicitOpenRouterKey === 'string' && !explicitOpenRouterKey.startsWith('ENV.');
200
- const openRouterKey = isExplicitKey ? explicitOpenRouterKey : (process.env.OPENROUTER_API_KEY ?? process.env.OPEN_ROUTER_KEY);
201
- const useOpenRouter = config.openRouter?.enabled !== undefined ? config.openRouter?.enabled : process.env.USE_OPENROUTER;
202
- if (openRouterKey && useOpenRouter !== false && useOpenRouter !== 'false') {
203
- routerConfig.openRouter = { enabled: true };
197
+ // OpenRouter: always pass apiKey when set (fallback for providers without direct keys).
198
+ // USE_OPENROUTER=false only disables *preferring* OpenRouter when direct provider keys exist.
199
+ const openRouterKey = resolveOpenRouterApiKey(config);
200
+ const preferOpenRouter = resolvePreferOpenRouter(config);
201
+ if (openRouterKey) {
204
202
  routerConfig.openrouter = { apiKey: openRouterKey };
205
- routerConfig.defaultMode = 'openrouter';
203
+ routerConfig.openRouter = {
204
+ enabled: true,
205
+ prefer: preferOpenRouter,
206
+ };
207
+ if (preferOpenRouter) {
208
+ routerConfig.defaultMode = 'openrouter';
209
+ }
206
210
  }
207
211
  const router = new LLMProviderRouter(routerConfig);
208
212
  // Set up BETWEEN-CALLS rate limiting as a request interceptor (applies to all provider calls)
@@ -302,6 +306,8 @@ export function initializeGatewayComponents(config) {
302
306
  optimixerManager,
303
307
  usageTracker,
304
308
  messageBuilderConfig,
305
- defaultModelConfig
309
+ defaultModelConfig,
310
+ preferOpenRouter,
311
+ openRouterApiKey: openRouterKey,
306
312
  };
307
313
  }
@@ -51,4 +51,6 @@ export declare function initializeGatewayComponents(config: GatewayConfig): {
51
51
  usageTracker: UsageTracker;
52
52
  messageBuilderConfig: MessageBuilderConfig;
53
53
  defaultModelConfig: Record<string, unknown>;
54
+ preferOpenRouter: boolean;
55
+ openRouterApiKey?: string;
54
56
  };
@@ -1,4 +1,4 @@
1
- import { DebugLogAbstract } from '@x12i/logxer';
1
+ import { DebugLogAbstract, runWithLogContext } from '@x12i/logxer';
2
2
  /** Subset of identity forwarded on every structured log line (plus full envelope under `identity`). */
3
3
  export function activityIdentityToLogMeta(identity) {
4
4
  if (!identity) {
@@ -27,6 +27,24 @@ export function withActivityIdentity(identity, data, debugKind) {
27
27
  ...(debugKind !== undefined ? { debugKind } : {})
28
28
  };
29
29
  }
30
+ /** Map gateway identity into Logxer ALS correlation context (`runWithLogContext`). */
31
+ export function activityIdentityToLogContext(identity) {
32
+ if (!identity) {
33
+ return {};
34
+ }
35
+ return {
36
+ jobId: identity.jobId,
37
+ taskId: identity.taskId,
38
+ graphId: identity.graphId,
39
+ nodeId: identity.nodeId,
40
+ sessionId: identity.sessionId,
41
+ correlationId: identity.aiRequestId
42
+ };
43
+ }
44
+ /** Run gateway work with Logxer correlation context for `getJobLogs` and structured envelopes. */
45
+ export function withGatewayLogContext(identity, fn) {
46
+ return runWithLogContext(activityIdentityToLogContext(identity), fn);
47
+ }
30
48
  export const gatewayLogDebug = {
31
49
  anomaly: DebugLogAbstract.ANOMALY,
32
50
  trace: DebugLogAbstract.TRACE,
@@ -1,11 +1,15 @@
1
1
  /**
2
2
  * Maps gateway {@link ActivityIdentity} into Logxer {@link LogMeta} fields for correlation and querying.
3
3
  */
4
- import type { LogMeta } from '@x12i/logxer';
4
+ import type { LogMeta, LogRuntimeContext } from '@x12i/logxer';
5
5
  import type { ActivityIdentity } from './types.js';
6
6
  /** Subset of identity forwarded on every structured log line (plus full envelope under `identity`). */
7
7
  export declare function activityIdentityToLogMeta(identity: Partial<ActivityIdentity> | undefined): LogMeta;
8
8
  export declare function withActivityIdentity(identity: Partial<ActivityIdentity> | undefined, data?: LogMeta, debugKind?: LogMeta['debugKind']): LogMeta;
9
+ /** Map gateway identity into Logxer ALS correlation context (`runWithLogContext`). */
10
+ export declare function activityIdentityToLogContext(identity: Partial<ActivityIdentity> | undefined): LogRuntimeContext;
11
+ /** Run gateway work with Logxer correlation context for `getJobLogs` and structured envelopes. */
12
+ export declare function withGatewayLogContext<T>(identity: Partial<ActivityIdentity> | undefined, fn: () => T | Promise<T>): Promise<T>;
9
13
  export declare const gatewayLogDebug: {
10
14
  readonly anomaly: "ANOMALY";
11
15
  readonly trace: "TRACE";
@@ -146,7 +146,7 @@ export async function autoRegisterProviders(router, logger) {
146
146
  optionalEnvVars: PROVIDER_CONFIGS
147
147
  .filter(p => p.optional)
148
148
  .map(p => p.envVar),
149
- openRouter: 'Set OPENROUTER_API_KEY (and do not set USE_OPENROUTER=false) to use OpenRouter without registering a provider. Legacy OPEN_ROUTER_KEY is still accepted.',
149
+ openRouter: 'Set OPENROUTER_API_KEY for OpenRouter (default route when set). USE_OPENROUTER=false prefers direct provider keys when present; OpenRouter is still used when a requested provider has no key.',
150
150
  note: 'You can still manually register providers using gateway.register(provider)'
151
151
  });
152
152
  }
@@ -9,7 +9,7 @@ import { extractHttpStatusCode } from './gateway-retry.js';
9
9
  import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
10
10
  import { getPreParsedInstructions } from './gateway-instructions.js';
11
11
  import { getModelMaxTokensFromFlexMd } from './flex-md-loader.js';
12
- import { applyModelResolution } from './ai-tools-client.js';
12
+ import { applyModelResolution, buildModelResolverOptions } from './ai-tools-client.js';
13
13
  import { getGatewayOperationalMode, isProdGatewayMode, resolveGatewayDefaultModel, warnDefaultModelSubstitution } from './gateway-mode.js';
14
14
  /**
15
15
  * Generates MD5 hash of a string
@@ -65,14 +65,15 @@ async function tryResolveSubstitutedDefaultModel(merged, request, config, logger
65
65
  return;
66
66
  }
67
67
  try {
68
+ const resolverOptions = buildModelResolverOptions(config, mergeOptions?.routingEnv);
68
69
  const resolution = await catalog.resolveModel({
69
70
  provider: merged.provider,
70
- model: merged.model
71
- });
71
+ model: merged.model,
72
+ }, resolverOptions);
72
73
  if (!resolution.found) {
73
74
  return;
74
75
  }
75
- applyModelResolution(merged, resolution, config.defaultEngine);
76
+ applyModelResolution(merged, resolution, config.defaultEngine, merged.model);
76
77
  request._modelResolution = {
77
78
  modelId: resolution.modelId,
78
79
  routedViaOpenRouter: resolution.routedViaOpenRouter,
@@ -182,12 +183,13 @@ export async function mergeConfig(request, config, logger, mergeOptions) {
182
183
  }
183
184
  else if (resolveModels && mergeOptions?.catalog) {
184
185
  try {
186
+ const resolverOptions = buildModelResolverOptions(config, mergeOptions?.routingEnv);
185
187
  const resolution = await mergeOptions.catalog.resolveModel({
186
188
  provider: merged.provider,
187
- model: explicitModel
188
- });
189
+ model: explicitModel,
190
+ }, resolverOptions);
189
191
  if (resolution.found) {
190
- applyModelResolution(merged, resolution, config.defaultEngine);
192
+ applyModelResolution(merged, resolution, config.defaultEngine, explicitModel);
191
193
  request._modelResolution = {
192
194
  modelId: resolution.modelId,
193
195
  routedViaOpenRouter: resolution.routedViaOpenRouter,
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type { AIInvokeRequest, ChatRequest, GatewayConfig, GatewayFallbackAttempt, GatewayInvokeRejectionMetadata, GatewayTraceAttempt, GatewayTraceMergedConfig, GatewayTraceRequestIds, GatewayTraceUsageSummary, ModelConfig } from './types.js';
6
6
  import type { Logxer } from '@x12i/logxer';
7
- import { type AiCostResult, type AiModelsCatalogClient, type CostCalculator } from '@x12i/ai-tools';
7
+ import { type AiCostResult, type AiModelsCatalogClient, type CostCalculator, type OpenRouterRoutingConfig } from '@x12i/ai-tools';
8
8
  /**
9
9
  * Generates MD5 hash of a string
10
10
  */
@@ -16,6 +16,7 @@ export declare function ensureTaskTypeId(request: ChatRequest, logger: Logxer):
16
16
  export type MergeConfigOptions = {
17
17
  defaultModelConfig?: Record<string, unknown>;
18
18
  catalog?: AiModelsCatalogClient | null;
19
+ routingEnv?: OpenRouterRoutingConfig;
19
20
  };
20
21
  /**
21
22
  * True when any caller-controlled config source set `maxTokens` (Optimixer should not override).