@x12i/ai-gateway 11.0.0 → 11.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -463,14 +463,14 @@ At invoke, gateway merges server-tool intent in this order (later wins):
463
463
  2. `request.config.serverTools` / `request.config.openrouter`
464
464
  3. `request.modelConfig.serverTools` / `request.modelConfig.openrouter`
465
465
 
466
- Gateway then applies **post-routing policy** internally (`applyPostRoutingServerToolsPolicy`): required tools force `provider=openrouter`; `allowed` tools on non-OpenRouter providers are stripped with a warning. **Do not duplicate post-routing policy in consumers** — merge pre-invoke intent only.
466
+ Gateway then applies **post-routing policy** internally (`applyPostRoutingServerToolsPolicy`): any active server tool (`allowed` or `required`) forces `provider=openrouter` so tools are sent in the OpenRouter request. **Do not duplicate post-routing policy in consumers** — merge pre-invoke intent only.
467
467
 
468
468
  ### Stable error codes
469
469
 
470
470
  | Code | When |
471
471
  |------|------|
472
- | `SERVER_TOOL_REQUIRES_OPENROUTER_PROVIDER` | Not thrown — gateway **forces** `provider=openrouter` when required tools are configured and a key is present |
473
- | `OPENROUTER_SERVER_TOOL_REQUIRES_KEY` | Required tools but no OpenRouter key (`codeAliases` includes `OPENROUTER_SERVER_TOOL_REQUIRES_OPENROUTER_KEY`) |
472
+ | `SERVER_TOOL_REQUIRES_OPENROUTER_PROVIDER` | Not thrown — gateway **forces** `provider=openrouter` when active server tools are configured and a key is present |
473
+ | `OPENROUTER_SERVER_TOOL_REQUIRES_KEY` | Active server tools but no OpenRouter key (`codeAliases` includes `OPENROUTER_SERVER_TOOL_REQUIRES_OPENROUTER_KEY`) |
474
474
  | `WEB_SEARCH_REQUIRED_BUT_NOT_USED`, etc. | From `@x12i/openrouter-runtime` policy → `GatewayPolicyViolationError.code` and `error.metadata.openrouterRuntime.policyViolations` |
475
475
  | `CITATIONS_REQUIRED_BUT_MISSING` | `@x12i/openrouter-runtime` ≥ **1.0.3** policy when `webSearch.requireCitations: true` (or global default) and search used with no citations |
476
476
  | `APPLY_PATCH_REQUIRES_RESPONSES_API` | `applyPatch` with `openrouter.apiMode: 'chat'` |
@@ -209,7 +209,7 @@ export async function mergeConfig(request, config, logger, mergeOptions) {
209
209
  });
210
210
  if (serverToolsPolicy.forceOpenRouter) {
211
211
  merged.provider = 'openrouter';
212
- logger.verbose('Required OpenRouter server tool forced provider=openrouter', {
212
+ logger.verbose('OpenRouter server tool forced provider=openrouter', {
213
213
  jobId: request.identity.jobId,
214
214
  });
215
215
  }
@@ -5,5 +5,6 @@ export type OpenRouterRuntimeProviderOptions = {
5
5
  apiKey: string;
6
6
  logger?: Logxer;
7
7
  runtime?: OpenRouterRuntime;
8
+ debugOpenRouterBody?: boolean;
8
9
  };
9
10
  export declare function createOpenRouterRuntimeProvider(options: OpenRouterRuntimeProviderOptions): ProviderModule;
@@ -1,7 +1,75 @@
1
1
  import { createOpenRouterRuntime, RuntimeConfigError, OpenRouterHttpError, } from '@x12i/openrouter-runtime';
2
2
  import { OPENROUTER_RUNTIME_OPERATION } from './map-gateway-request.js';
3
3
  import { throwMappedOpenRouterHttpError, throwMappedRuntimeConfigError, throwMappedRuntimeResponseErrors, } from './map-runtime-errors.js';
4
+ import { redactRawOpenRouterPayload } from './map-trace.js';
5
+ function hasOwn(obj, key) {
6
+ return Object.prototype.hasOwnProperty.call(obj, key);
7
+ }
8
+ function summarizeStreamValue(value) {
9
+ if (value == null || ['boolean', 'number', 'string'].includes(typeof value))
10
+ return value;
11
+ if (Array.isArray(value))
12
+ return `[array:${value.length}]`;
13
+ return `[${typeof value}]`;
14
+ }
15
+ function collectStreamFields(value, path = 'body', depth = 0, out = []) {
16
+ if (out.length >= 20 || depth > 8 || value == null || typeof value !== 'object')
17
+ return out;
18
+ if (Array.isArray(value)) {
19
+ for (let i = 0; i < value.length && out.length < 20; i += 1) {
20
+ collectStreamFields(value[i], `${path}[${i}]`, depth + 1, out);
21
+ }
22
+ return out;
23
+ }
24
+ for (const [key, child] of Object.entries(value)) {
25
+ const childPath = `${path}.${key}`;
26
+ if (key === 'stream') {
27
+ out.push({ path: childPath, value: summarizeStreamValue(child) });
28
+ if (out.length >= 20)
29
+ return out;
30
+ }
31
+ collectStreamFields(child, childPath, depth + 1, out);
32
+ }
33
+ return out;
34
+ }
35
+ function summarizeServerTools(serverTools) {
36
+ if (!serverTools)
37
+ return undefined;
38
+ const out = {};
39
+ for (const [key, value] of Object.entries(serverTools)) {
40
+ if (Array.isArray(value)) {
41
+ out[key] = value.map((item) => item.mode).join(',');
42
+ continue;
43
+ }
44
+ if (value && typeof value === 'object' && 'mode' in value && typeof value.mode === 'string') {
45
+ out[key] = value.mode;
46
+ }
47
+ }
48
+ return Object.keys(out).length ? out : undefined;
49
+ }
50
+ function summarizeCompiledTools(compiled) {
51
+ const tools = Array.isArray(compiled.body.tools) ? compiled.body.tools : [];
52
+ const toolTypes = tools
53
+ .map((tool) => (tool && typeof tool === 'object' && 'type' in tool ? String(tool.type) : undefined))
54
+ .filter((type) => typeof type === 'string');
55
+ return {
56
+ apiMode: compiled.apiMode,
57
+ toolCount: tools.length,
58
+ toolTypes,
59
+ bodyKeys: Object.keys(compiled.body),
60
+ hasTopLevelStream: hasOwn(compiled.body, 'stream'),
61
+ topLevelStream: compiled.body.stream,
62
+ streamFields: collectStreamFields(compiled.body),
63
+ };
64
+ }
65
+ function shouldDebugOpenRouterBody(options) {
66
+ if (options.debugOpenRouterBody === true)
67
+ return true;
68
+ const env = process.env.AI_GATEWAY_DEBUG_OPENROUTER_BODY?.toLowerCase();
69
+ return env === '1' || env === 'true' || env === 'yes';
70
+ }
4
71
  export function createOpenRouterRuntimeProvider(options) {
72
+ const debugOpenRouterBody = shouldDebugOpenRouterBody(options);
5
73
  const runtime = options.runtime ??
6
74
  createOpenRouterRuntime({
7
75
  apiKey: options.apiKey,
@@ -9,6 +77,34 @@ export function createOpenRouterRuntimeProvider(options) {
9
77
  retry: { enabled: false },
10
78
  onPolicyViolation: 'throw',
11
79
  },
80
+ logger: options.logger
81
+ ? {
82
+ debug: (event, data) => options.logger?.debug(event, data),
83
+ info: (event, data) => options.logger?.info(event, data),
84
+ warn: (event, data) => options.logger?.warn(event, data),
85
+ error: (event, data) => options.logger?.error(event, data),
86
+ }
87
+ : undefined,
88
+ hooks: options.logger
89
+ ? {
90
+ beforeOpenRouterRequest: (compiled) => {
91
+ const summary = summarizeCompiledTools(compiled);
92
+ options.logger?.debug('OpenRouter runtime compiled request before fetch', {
93
+ ...summary,
94
+ url: compiled.url,
95
+ });
96
+ if (debugOpenRouterBody) {
97
+ const redactedBody = redactRawOpenRouterPayload(compiled.body);
98
+ options.logger?.debug('OpenRouter runtime compiled request body before fetch', {
99
+ apiMode: compiled.apiMode,
100
+ url: compiled.url,
101
+ body: redactedBody,
102
+ bodyJson: JSON.stringify(redactedBody, null, 2),
103
+ });
104
+ }
105
+ },
106
+ }
107
+ : undefined,
12
108
  });
13
109
  return {
14
110
  name: 'openrouter',
@@ -22,6 +118,21 @@ export function createOpenRouterRuntimeProvider(options) {
22
118
  throw new Error(`Unsupported OpenRouter runtime operation: ${spec.operation}`);
23
119
  }
24
120
  const runtimeRequest = spec.args;
121
+ const rawOverrideKeys = runtimeRequest.rawOpenRouterOverrides
122
+ ? Object.keys(runtimeRequest.rawOpenRouterOverrides)
123
+ : [];
124
+ const rawOverrides = runtimeRequest.rawOpenRouterOverrides ?? {};
125
+ const rawOverrideHasStream = hasOwn(rawOverrides, 'stream');
126
+ options.logger?.debug('OpenRouter runtime request server tools', {
127
+ requestId: spec.requestId,
128
+ model: runtimeRequest.model,
129
+ apiMode: runtimeRequest.apiMode,
130
+ serverTools: summarizeServerTools(runtimeRequest.serverTools),
131
+ rawOverrideKeys,
132
+ rawOverrideHasStream,
133
+ rawOverrideStream: rawOverrides.stream,
134
+ rawOverridesToolsPresent: Object.prototype.hasOwnProperty.call(rawOverrides, 'tools'),
135
+ });
25
136
  try {
26
137
  const response = await runtime.run(runtimeRequest);
27
138
  if (response.status !== 'completed') {
@@ -22,14 +22,17 @@ function buildGenerationOverrides(config) {
22
22
  overrides.stop = config.stop;
23
23
  return overrides;
24
24
  }
25
+ function buildRawOpenRouterOverrides(generationOverrides, trustedRaw) {
26
+ const merged = { ...generationOverrides, ...trustedRaw };
27
+ return Object.keys(merged).length ? merged : undefined;
28
+ }
25
29
  export function mapGatewayRequestToRuntimeRequest(request, exec) {
26
30
  const config = request.config ?? {};
27
31
  const openrouter = config.openrouter;
28
32
  const generationOverrides = buildGenerationOverrides(config);
29
33
  const trustedRaw = openrouter?.rawOverrides ?? {};
30
- const rawOpenRouterOverrides = Object.keys(generationOverrides).length || Object.keys(trustedRaw).length
31
- ? { ...generationOverrides, ...trustedRaw }
32
- : undefined;
34
+ const serverTools = mapGatewayServerTools(config.serverTools);
35
+ const rawOpenRouterOverrides = buildRawOpenRouterOverrides(generationOverrides, trustedRaw);
33
36
  const aiRequestId = (typeof request.aiRequestId === 'string' && request.aiRequestId) ||
34
37
  (typeof request.identity?.aiRequestId === 'string' ? request.identity.aiRequestId : undefined);
35
38
  return {
@@ -42,7 +45,7 @@ export function mapGatewayRequestToRuntimeRequest(request, exec) {
42
45
  reasoning: config.reasoning != null && typeof config.reasoning === 'object'
43
46
  ? config.reasoning
44
47
  : undefined,
45
- serverTools: mapGatewayServerTools(config.serverTools),
48
+ serverTools,
46
49
  execution: exec?.timeoutMs ? { timeoutMs: exec.timeoutMs } : undefined,
47
50
  rawOpenRouterOverrides,
48
51
  metadata: {
@@ -1,14 +1,19 @@
1
1
  import { createOpenRouterRuntimeAdapter } from './create-openrouter-runtime-adapter.js';
2
2
  import { createOpenRouterRuntimeProvider } from './create-openrouter-runtime-provider.js';
3
3
  export function registerOpenRouterRuntime(router, options) {
4
- const { apiKey, logger } = options;
5
- const provider = createOpenRouterRuntimeProvider({ apiKey, logger });
4
+ const { apiKey, logger, runtimeConfig } = options;
5
+ const provider = createOpenRouterRuntimeProvider({
6
+ apiKey,
7
+ logger,
8
+ debugOpenRouterBody: runtimeConfig?.debugOpenRouterBody,
9
+ });
6
10
  router.registerProvider(provider);
7
11
  const adapter = createOpenRouterRuntimeAdapter();
8
12
  router.getAdapterRegistry().register(adapter);
9
13
  logger?.info('Registered OpenRouter runtime provider and adapter', {
10
14
  provider: 'openrouter',
11
15
  runtimeEnabled: true,
16
+ debugOpenRouterBody: runtimeConfig?.debugOpenRouterBody,
12
17
  });
13
18
  }
14
19
  export { shouldUseOpenRouterRuntime, resolveOpenRouterRuntimeDefaults } from './should-use-openrouter-runtime.js';
@@ -73,22 +73,12 @@ export function applyPostRoutingServerToolsPolicy(input) {
73
73
  return { serverTools, warnings, forceOpenRouter: false };
74
74
  }
75
75
  validateApplyPatchConfig(serverTools, openrouter);
76
- if (hasRequiredServerTool(serverTools)) {
77
- if (!openRouterApiKey) {
78
- throw new ProviderConfigError('OPENROUTER_SERVER_TOOL_REQUIRES_KEY', 'Required OpenRouter server tools need OPENROUTER_API_KEY or GatewayConfig.openrouter.apiKey', { codeAliases: ['OPENROUTER_SERVER_TOOL_REQUIRES_OPENROUTER_KEY'] });
79
- }
80
- if (provider !== 'openrouter') {
81
- provider = 'openrouter';
82
- return { serverTools, warnings, forceOpenRouter: true };
83
- }
84
- return { serverTools, warnings, forceOpenRouter: false };
76
+ if (!openRouterApiKey) {
77
+ throw new ProviderConfigError('OPENROUTER_SERVER_TOOL_REQUIRES_KEY', 'OpenRouter server tools need OPENROUTER_API_KEY or GatewayConfig.openrouter.apiKey', { codeAliases: ['OPENROUTER_SERVER_TOOL_REQUIRES_OPENROUTER_KEY'] });
85
78
  }
86
79
  if (provider !== 'openrouter') {
87
- warnings.push({
88
- code: 'SERVER_TOOLS_IGNORED_FOR_NON_OPENROUTER_PROVIDER',
89
- message: 'OpenRouter server tools were requested but the final provider is not openrouter; tools were stripped',
90
- });
91
- return { serverTools: undefined, warnings, forceOpenRouter: false };
80
+ provider = 'openrouter';
81
+ return { serverTools, warnings, forceOpenRouter: true };
92
82
  }
93
83
  return { serverTools, warnings, forceOpenRouter: false };
94
84
  }
package/dist/types.d.ts CHANGED
@@ -176,6 +176,12 @@ export interface GatewayOpenRouterRuntimeConfig {
176
176
  enabled?: boolean;
177
177
  apiMode?: 'chat' | 'responses' | 'auto';
178
178
  includeRawInTrace?: boolean;
179
+ /**
180
+ * Emit the exact compiled OpenRouter request body at debug level before fetch.
181
+ * Intended for short-lived diagnostics; request prompts/content are included.
182
+ * Can also be enabled with AI_GATEWAY_DEBUG_OPENROUTER_BODY=1.
183
+ */
184
+ debugOpenRouterBody?: boolean;
179
185
  }
180
186
  export interface GatewayServerToolUsage {
181
187
  requested: boolean;
@@ -209,7 +209,7 @@ export async function mergeConfig(request, config, logger, mergeOptions) {
209
209
  });
210
210
  if (serverToolsPolicy.forceOpenRouter) {
211
211
  merged.provider = 'openrouter';
212
- logger.verbose('Required OpenRouter server tool forced provider=openrouter', {
212
+ logger.verbose('OpenRouter server tool forced provider=openrouter', {
213
213
  jobId: request.identity.jobId,
214
214
  });
215
215
  }
@@ -1,7 +1,75 @@
1
1
  import { createOpenRouterRuntime, RuntimeConfigError, OpenRouterHttpError, } from '@x12i/openrouter-runtime';
2
2
  import { OPENROUTER_RUNTIME_OPERATION } from './map-gateway-request.js';
3
3
  import { throwMappedOpenRouterHttpError, throwMappedRuntimeConfigError, throwMappedRuntimeResponseErrors, } from './map-runtime-errors.js';
4
+ import { redactRawOpenRouterPayload } from './map-trace.js';
5
+ function hasOwn(obj, key) {
6
+ return Object.prototype.hasOwnProperty.call(obj, key);
7
+ }
8
+ function summarizeStreamValue(value) {
9
+ if (value == null || ['boolean', 'number', 'string'].includes(typeof value))
10
+ return value;
11
+ if (Array.isArray(value))
12
+ return `[array:${value.length}]`;
13
+ return `[${typeof value}]`;
14
+ }
15
+ function collectStreamFields(value, path = 'body', depth = 0, out = []) {
16
+ if (out.length >= 20 || depth > 8 || value == null || typeof value !== 'object')
17
+ return out;
18
+ if (Array.isArray(value)) {
19
+ for (let i = 0; i < value.length && out.length < 20; i += 1) {
20
+ collectStreamFields(value[i], `${path}[${i}]`, depth + 1, out);
21
+ }
22
+ return out;
23
+ }
24
+ for (const [key, child] of Object.entries(value)) {
25
+ const childPath = `${path}.${key}`;
26
+ if (key === 'stream') {
27
+ out.push({ path: childPath, value: summarizeStreamValue(child) });
28
+ if (out.length >= 20)
29
+ return out;
30
+ }
31
+ collectStreamFields(child, childPath, depth + 1, out);
32
+ }
33
+ return out;
34
+ }
35
+ function summarizeServerTools(serverTools) {
36
+ if (!serverTools)
37
+ return undefined;
38
+ const out = {};
39
+ for (const [key, value] of Object.entries(serverTools)) {
40
+ if (Array.isArray(value)) {
41
+ out[key] = value.map((item) => item.mode).join(',');
42
+ continue;
43
+ }
44
+ if (value && typeof value === 'object' && 'mode' in value && typeof value.mode === 'string') {
45
+ out[key] = value.mode;
46
+ }
47
+ }
48
+ return Object.keys(out).length ? out : undefined;
49
+ }
50
+ function summarizeCompiledTools(compiled) {
51
+ const tools = Array.isArray(compiled.body.tools) ? compiled.body.tools : [];
52
+ const toolTypes = tools
53
+ .map((tool) => (tool && typeof tool === 'object' && 'type' in tool ? String(tool.type) : undefined))
54
+ .filter((type) => typeof type === 'string');
55
+ return {
56
+ apiMode: compiled.apiMode,
57
+ toolCount: tools.length,
58
+ toolTypes,
59
+ bodyKeys: Object.keys(compiled.body),
60
+ hasTopLevelStream: hasOwn(compiled.body, 'stream'),
61
+ topLevelStream: compiled.body.stream,
62
+ streamFields: collectStreamFields(compiled.body),
63
+ };
64
+ }
65
+ function shouldDebugOpenRouterBody(options) {
66
+ if (options.debugOpenRouterBody === true)
67
+ return true;
68
+ const env = process.env.AI_GATEWAY_DEBUG_OPENROUTER_BODY?.toLowerCase();
69
+ return env === '1' || env === 'true' || env === 'yes';
70
+ }
4
71
  export function createOpenRouterRuntimeProvider(options) {
72
+ const debugOpenRouterBody = shouldDebugOpenRouterBody(options);
5
73
  const runtime = options.runtime ??
6
74
  createOpenRouterRuntime({
7
75
  apiKey: options.apiKey,
@@ -9,6 +77,34 @@ export function createOpenRouterRuntimeProvider(options) {
9
77
  retry: { enabled: false },
10
78
  onPolicyViolation: 'throw',
11
79
  },
80
+ logger: options.logger
81
+ ? {
82
+ debug: (event, data) => options.logger?.debug(event, data),
83
+ info: (event, data) => options.logger?.info(event, data),
84
+ warn: (event, data) => options.logger?.warn(event, data),
85
+ error: (event, data) => options.logger?.error(event, data),
86
+ }
87
+ : undefined,
88
+ hooks: options.logger
89
+ ? {
90
+ beforeOpenRouterRequest: (compiled) => {
91
+ const summary = summarizeCompiledTools(compiled);
92
+ options.logger?.debug('OpenRouter runtime compiled request before fetch', {
93
+ ...summary,
94
+ url: compiled.url,
95
+ });
96
+ if (debugOpenRouterBody) {
97
+ const redactedBody = redactRawOpenRouterPayload(compiled.body);
98
+ options.logger?.debug('OpenRouter runtime compiled request body before fetch', {
99
+ apiMode: compiled.apiMode,
100
+ url: compiled.url,
101
+ body: redactedBody,
102
+ bodyJson: JSON.stringify(redactedBody, null, 2),
103
+ });
104
+ }
105
+ },
106
+ }
107
+ : undefined,
12
108
  });
13
109
  return {
14
110
  name: 'openrouter',
@@ -22,6 +118,21 @@ export function createOpenRouterRuntimeProvider(options) {
22
118
  throw new Error(`Unsupported OpenRouter runtime operation: ${spec.operation}`);
23
119
  }
24
120
  const runtimeRequest = spec.args;
121
+ const rawOverrideKeys = runtimeRequest.rawOpenRouterOverrides
122
+ ? Object.keys(runtimeRequest.rawOpenRouterOverrides)
123
+ : [];
124
+ const rawOverrides = runtimeRequest.rawOpenRouterOverrides ?? {};
125
+ const rawOverrideHasStream = hasOwn(rawOverrides, 'stream');
126
+ options.logger?.debug('OpenRouter runtime request server tools', {
127
+ requestId: spec.requestId,
128
+ model: runtimeRequest.model,
129
+ apiMode: runtimeRequest.apiMode,
130
+ serverTools: summarizeServerTools(runtimeRequest.serverTools),
131
+ rawOverrideKeys,
132
+ rawOverrideHasStream,
133
+ rawOverrideStream: rawOverrides.stream,
134
+ rawOverridesToolsPresent: Object.prototype.hasOwnProperty.call(rawOverrides, 'tools'),
135
+ });
25
136
  try {
26
137
  const response = await runtime.run(runtimeRequest);
27
138
  if (response.status !== 'completed') {
@@ -5,5 +5,6 @@ export type OpenRouterRuntimeProviderOptions = {
5
5
  apiKey: string;
6
6
  logger?: Logxer;
7
7
  runtime?: OpenRouterRuntime;
8
+ debugOpenRouterBody?: boolean;
8
9
  };
9
10
  export declare function createOpenRouterRuntimeProvider(options: OpenRouterRuntimeProviderOptions): ProviderModule;
@@ -22,14 +22,17 @@ function buildGenerationOverrides(config) {
22
22
  overrides.stop = config.stop;
23
23
  return overrides;
24
24
  }
25
+ function buildRawOpenRouterOverrides(generationOverrides, trustedRaw) {
26
+ const merged = { ...generationOverrides, ...trustedRaw };
27
+ return Object.keys(merged).length ? merged : undefined;
28
+ }
25
29
  export function mapGatewayRequestToRuntimeRequest(request, exec) {
26
30
  const config = request.config ?? {};
27
31
  const openrouter = config.openrouter;
28
32
  const generationOverrides = buildGenerationOverrides(config);
29
33
  const trustedRaw = openrouter?.rawOverrides ?? {};
30
- const rawOpenRouterOverrides = Object.keys(generationOverrides).length || Object.keys(trustedRaw).length
31
- ? { ...generationOverrides, ...trustedRaw }
32
- : undefined;
34
+ const serverTools = mapGatewayServerTools(config.serverTools);
35
+ const rawOpenRouterOverrides = buildRawOpenRouterOverrides(generationOverrides, trustedRaw);
33
36
  const aiRequestId = (typeof request.aiRequestId === 'string' && request.aiRequestId) ||
34
37
  (typeof request.identity?.aiRequestId === 'string' ? request.identity.aiRequestId : undefined);
35
38
  return {
@@ -42,7 +45,7 @@ export function mapGatewayRequestToRuntimeRequest(request, exec) {
42
45
  reasoning: config.reasoning != null && typeof config.reasoning === 'object'
43
46
  ? config.reasoning
44
47
  : undefined,
45
- serverTools: mapGatewayServerTools(config.serverTools),
48
+ serverTools,
46
49
  execution: exec?.timeoutMs ? { timeoutMs: exec.timeoutMs } : undefined,
47
50
  rawOpenRouterOverrides,
48
51
  metadata: {
@@ -1,14 +1,19 @@
1
1
  import { createOpenRouterRuntimeAdapter } from './create-openrouter-runtime-adapter.js';
2
2
  import { createOpenRouterRuntimeProvider } from './create-openrouter-runtime-provider.js';
3
3
  export function registerOpenRouterRuntime(router, options) {
4
- const { apiKey, logger } = options;
5
- const provider = createOpenRouterRuntimeProvider({ apiKey, logger });
4
+ const { apiKey, logger, runtimeConfig } = options;
5
+ const provider = createOpenRouterRuntimeProvider({
6
+ apiKey,
7
+ logger,
8
+ debugOpenRouterBody: runtimeConfig?.debugOpenRouterBody,
9
+ });
6
10
  router.registerProvider(provider);
7
11
  const adapter = createOpenRouterRuntimeAdapter();
8
12
  router.getAdapterRegistry().register(adapter);
9
13
  logger?.info('Registered OpenRouter runtime provider and adapter', {
10
14
  provider: 'openrouter',
11
15
  runtimeEnabled: true,
16
+ debugOpenRouterBody: runtimeConfig?.debugOpenRouterBody,
12
17
  });
13
18
  }
14
19
  export { shouldUseOpenRouterRuntime, resolveOpenRouterRuntimeDefaults } from './should-use-openrouter-runtime.js';
@@ -73,22 +73,12 @@ export function applyPostRoutingServerToolsPolicy(input) {
73
73
  return { serverTools, warnings, forceOpenRouter: false };
74
74
  }
75
75
  validateApplyPatchConfig(serverTools, openrouter);
76
- if (hasRequiredServerTool(serverTools)) {
77
- if (!openRouterApiKey) {
78
- throw new ProviderConfigError('OPENROUTER_SERVER_TOOL_REQUIRES_KEY', 'Required OpenRouter server tools need OPENROUTER_API_KEY or GatewayConfig.openrouter.apiKey', { codeAliases: ['OPENROUTER_SERVER_TOOL_REQUIRES_OPENROUTER_KEY'] });
79
- }
80
- if (provider !== 'openrouter') {
81
- provider = 'openrouter';
82
- return { serverTools, warnings, forceOpenRouter: true };
83
- }
84
- return { serverTools, warnings, forceOpenRouter: false };
76
+ if (!openRouterApiKey) {
77
+ throw new ProviderConfigError('OPENROUTER_SERVER_TOOL_REQUIRES_KEY', 'OpenRouter server tools need OPENROUTER_API_KEY or GatewayConfig.openrouter.apiKey', { codeAliases: ['OPENROUTER_SERVER_TOOL_REQUIRES_OPENROUTER_KEY'] });
85
78
  }
86
79
  if (provider !== 'openrouter') {
87
- warnings.push({
88
- code: 'SERVER_TOOLS_IGNORED_FOR_NON_OPENROUTER_PROVIDER',
89
- message: 'OpenRouter server tools were requested but the final provider is not openrouter; tools were stripped',
90
- });
91
- return { serverTools: undefined, warnings, forceOpenRouter: false };
80
+ provider = 'openrouter';
81
+ return { serverTools, warnings, forceOpenRouter: true };
92
82
  }
93
83
  return { serverTools, warnings, forceOpenRouter: false };
94
84
  }
@@ -176,6 +176,12 @@ export interface GatewayOpenRouterRuntimeConfig {
176
176
  enabled?: boolean;
177
177
  apiMode?: 'chat' | 'responses' | 'auto';
178
178
  includeRawInTrace?: boolean;
179
+ /**
180
+ * Emit the exact compiled OpenRouter request body at debug level before fetch.
181
+ * Intended for short-lived diagnostics; request prompts/content are included.
182
+ * Can also be enabled with AI_GATEWAY_DEBUG_OPENROUTER_BODY=1.
183
+ */
184
+ debugOpenRouterBody?: boolean;
179
185
  }
180
186
  export interface GatewayServerToolUsage {
181
187
  requested: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x12i/ai-gateway",
3
- "version": "11.0.0",
3
+ "version": "11.0.2",
4
4
  "description": "AI Gateway - Unified interface for LLM provider routing and management",
5
5
  "type": "module",
6
6
  "exports": {
@@ -44,11 +44,11 @@
44
44
  "dependencies": {
45
45
  "@x12i/activix": "^9.0.2",
46
46
  "@x12i/ai-profiles": "^3.4.1",
47
- "@x12i/ai-providers-router": "^4.10.0",
47
+ "@x12i/ai-providers-router": "^4.11.1",
48
48
  "@x12i/ai-tools": "^3.3.5",
49
49
  "@x12i/flex-md": "^4.8.0",
50
50
  "@x12i/logxer": "^5.1.0",
51
- "@x12i/openrouter-runtime": "^1.0.4",
51
+ "@x12i/openrouter-runtime": "^1.0.5",
52
52
  "@x12i/rendrix": "^4.3.0"
53
53
  },
54
54
  "devDependencies": {