@x12i/ai-gateway 10.4.0 → 10.4.3
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 +28 -1
- package/dist/activity-manager.js +24 -13
- package/dist/gateway-utils.d.ts +3 -1
- package/dist/gateway-utils.js +100 -5
- package/dist/gateway.js +21 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/instruction-errors.d.ts +11 -2
- package/dist/instruction-errors.js +8 -2
- package/dist/openrouter-runtime-adapter/create-openrouter-runtime-provider.js +1 -1
- package/dist/openrouter-runtime-adapter/index.d.ts +1 -1
- package/dist/openrouter-runtime-adapter/index.js +1 -1
- package/dist/openrouter-runtime-adapter/map-runtime-errors.js +18 -4
- package/dist/openrouter-runtime-adapter/map-runtime-response.d.ts +3 -0
- package/dist/openrouter-runtime-adapter/map-runtime-response.js +33 -0
- package/dist/openrouter-runtime-adapter/map-trace.js +5 -1
- package/dist/openrouter-runtime-adapter/validate-server-tools.js +1 -1
- package/dist/types.d.ts +19 -0
- package/dist-cjs/activity-manager.cjs +24 -13
- package/dist-cjs/gateway-utils.cjs +100 -5
- package/dist-cjs/gateway-utils.d.ts +3 -1
- package/dist-cjs/gateway.cjs +21 -1
- package/dist-cjs/index.cjs +1 -1
- package/dist-cjs/index.d.ts +2 -2
- package/dist-cjs/instruction-errors.cjs +8 -2
- package/dist-cjs/instruction-errors.d.ts +11 -2
- package/dist-cjs/openrouter-runtime-adapter/create-openrouter-runtime-provider.cjs +1 -1
- package/dist-cjs/openrouter-runtime-adapter/index.cjs +1 -1
- package/dist-cjs/openrouter-runtime-adapter/index.d.ts +1 -1
- package/dist-cjs/openrouter-runtime-adapter/map-runtime-errors.cjs +18 -4
- package/dist-cjs/openrouter-runtime-adapter/map-runtime-response.cjs +33 -0
- package/dist-cjs/openrouter-runtime-adapter/map-runtime-response.d.ts +3 -0
- package/dist-cjs/openrouter-runtime-adapter/map-trace.cjs +5 -1
- package/dist-cjs/openrouter-runtime-adapter/validate-server-tools.cjs +1 -1
- package/dist-cjs/types.d.ts +19 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -342,11 +342,12 @@ Every mode requires an explicit **`model`** on the request (concrete catalog id
|
|
|
342
342
|
| `npm test` | All unit/integration tests in `.tests/run-all.js` (tsx, no network) |
|
|
343
343
|
| `npm run test:ai-tools` | ai-tools + cost + trace helper unit tests |
|
|
344
344
|
| `npm run test:ai-tools:live` | Real invoke + dev strict model check (needs API key) |
|
|
345
|
+
| `npm run test:openrouter-live` | OpenRouter runtime + optional server tools (needs `OPENROUTER_API_KEY`) |
|
|
345
346
|
| `npm run test:flex-md-parsing` | flex-md parsing scenarios |
|
|
346
347
|
| `npm run test:flex-md-esm-regression` | ESM build regression for flex-md |
|
|
347
348
|
| `npm run test:prepublish` | `build` + `npm test` |
|
|
348
349
|
|
|
349
|
-
Live tests use `LIVE_TEST_PROVIDER` / `LIVE_TEST_MODEL` (default `openrouter` + `openai/gpt-4o-mini`). Set `LIVE_SKIP_INVOKE=1` to skip the LLM call. Profile alias invokes (`LIVE_TEST_PROFILES=1`) are **no longer supported** — resolve profiles upstream (ai-tasks) before calling the gateway.
|
|
350
|
+
Live tests use `LIVE_TEST_PROVIDER` / `LIVE_TEST_MODEL` (default `openrouter` + `openai/gpt-4o-mini`). Set `LIVE_SKIP_INVOKE=1` to skip the LLM call. `npm run test:openrouter-live` exercises plain OpenRouter calls plus datetime and web search server tools (requires `OPENROUTER_API_KEY`). Profile alias invokes (`LIVE_TEST_PROFILES=1`) are **no longer supported** — resolve profiles upstream (ai-tasks) before calling the gateway.
|
|
350
351
|
|
|
351
352
|
---
|
|
352
353
|
|
|
@@ -437,8 +438,34 @@ Tool usage and artifacts appear under:
|
|
|
437
438
|
- `response.metadata.patchProposals`
|
|
438
439
|
- `response.metadata.openrouterRuntime`
|
|
439
440
|
|
|
441
|
+
In **`diagnostics.mode: 'trace'`**, mirrors may also appear on **`response.metadata.usage.serverTools`** and **`response.metadata.usage.citations`**. Per-attempt slices are on **`metadata.attempts[n].openrouterRuntime`**.
|
|
442
|
+
|
|
443
|
+
On failure, OpenRouter tool metadata is preserved on **`error.metadata`** (see [AI_GATEWAY_INVOKE_EXECUTION_METADATA.md](./docs/AI_GATEWAY_INVOKE_EXECUTION_METADATA.md)).
|
|
444
|
+
|
|
440
445
|
Cost remains under `response.metadata.costUsd`, `response.metadata.costStatus`, and `response.metadata.tokens`.
|
|
441
446
|
|
|
447
|
+
### Config merge precedence
|
|
448
|
+
|
|
449
|
+
At invoke, gateway merges server-tool intent in this order (later wins):
|
|
450
|
+
|
|
451
|
+
1. `GatewayConfig.defaultServerTools` / `openrouterRuntime` defaults
|
|
452
|
+
2. `request.config.serverTools` / `request.config.openrouter`
|
|
453
|
+
3. `request.modelConfig.serverTools` / `request.modelConfig.openrouter`
|
|
454
|
+
|
|
455
|
+
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.
|
|
456
|
+
|
|
457
|
+
### Stable error codes
|
|
458
|
+
|
|
459
|
+
| Code | When |
|
|
460
|
+
|------|------|
|
|
461
|
+
| `SERVER_TOOL_REQUIRES_OPENROUTER_PROVIDER` | Not thrown — gateway **forces** `provider=openrouter` when required tools are configured and a key is present |
|
|
462
|
+
| `OPENROUTER_SERVER_TOOL_REQUIRES_KEY` | Required tools but no OpenRouter key (`codeAliases` includes `OPENROUTER_SERVER_TOOL_REQUIRES_OPENROUTER_KEY`) |
|
|
463
|
+
| `WEB_SEARCH_REQUIRED_BUT_NOT_USED`, etc. | From `@x12i/openrouter-runtime` policy → `GatewayPolicyViolationError.code` and `error.metadata.openrouterRuntime.policyViolations` |
|
|
464
|
+
| `CITATIONS_REQUIRED_BUT_MISSING` | `@x12i/openrouter-runtime` ≥ **1.0.3** policy when `webSearch.requireCitations: true` (or global default) and search used with no citations |
|
|
465
|
+
| `APPLY_PATCH_REQUIRES_RESPONSES_API` | `applyPatch` with `openrouter.apiMode: 'chat'` |
|
|
466
|
+
|
|
467
|
+
OpenRouter runtime is configured with **`onPolicyViolation: 'throw'`** so required-tool violations surface as **`policy_violation`** responses mapped to gateway errors.
|
|
468
|
+
|
|
442
469
|
---
|
|
443
470
|
|
|
444
471
|
## Documentation index
|
package/dist/activity-manager.js
CHANGED
|
@@ -244,20 +244,23 @@ function pickActivixCompletionRoutingMetadata(response) {
|
|
|
244
244
|
out.citationCount = citations.length;
|
|
245
245
|
}
|
|
246
246
|
const serverTools = m.serverTools;
|
|
247
|
+
if (serverTools != null && typeof serverTools === 'object') {
|
|
248
|
+
out.serverTools = serverTools;
|
|
249
|
+
}
|
|
247
250
|
const generatedImages = m.generatedImages;
|
|
248
251
|
const patchProposals = m.patchProposals;
|
|
252
|
+
if (Array.isArray(generatedImages) && generatedImages.length) {
|
|
253
|
+
out.generatedImageCount = generatedImages.length;
|
|
254
|
+
}
|
|
255
|
+
if (Array.isArray(patchProposals) && patchProposals.length) {
|
|
256
|
+
out.patchProposalCount = patchProposals.length;
|
|
257
|
+
}
|
|
249
258
|
const openrouterRuntime = m.openrouterRuntime;
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
(Array.isArray(generatedImages) && generatedImages.length) ||
|
|
253
|
-
(Array.isArray(patchProposals) && patchProposals.length)) {
|
|
259
|
+
if (openrouterRuntime != null && typeof openrouterRuntime === 'object') {
|
|
260
|
+
const ort = openrouterRuntime;
|
|
254
261
|
out.openrouterRuntime = {
|
|
255
|
-
...(
|
|
256
|
-
|
|
257
|
-
apiMode: openrouterRuntime.apiMode,
|
|
258
|
-
serverTools,
|
|
259
|
-
}
|
|
260
|
-
: { serverTools }),
|
|
262
|
+
...(typeof ort.apiMode === 'string' ? { apiMode: ort.apiMode } : {}),
|
|
263
|
+
...(Array.isArray(ort.warnings) ? { warnings: ort.warnings } : {}),
|
|
261
264
|
citationCount: Array.isArray(citations) ? citations.length : 0,
|
|
262
265
|
generatedImageCount: Array.isArray(generatedImages) ? generatedImages.length : 0,
|
|
263
266
|
patchProposalCount: Array.isArray(patchProposals) ? patchProposals.length : 0,
|
|
@@ -948,6 +951,14 @@ export class ActivityManager {
|
|
|
948
951
|
const outerMetadata = pickActivixCompletionRoutingMetadata(details.response);
|
|
949
952
|
const outerCost = buildActivixOuterCost(outerMetadata, billingSlice, details.response);
|
|
950
953
|
const recordMetadata = buildActivixRecordMetadata(details.response, billingSlice);
|
|
954
|
+
const outerOutput = details.response != null && typeof details.response === 'object'
|
|
955
|
+
? {
|
|
956
|
+
...details.response,
|
|
957
|
+
...(Array.isArray(outerMetadata.citations) && outerMetadata.citations.length
|
|
958
|
+
? { citations: outerMetadata.citations }
|
|
959
|
+
: {}),
|
|
960
|
+
}
|
|
961
|
+
: details.response;
|
|
951
962
|
await this.activix.completeRecord(activity.activityId, {
|
|
952
963
|
cost: details.cost,
|
|
953
964
|
...(typeof details.cost === 'number' && Number.isFinite(details.cost)
|
|
@@ -957,10 +968,10 @@ export class ActivityManager {
|
|
|
957
968
|
response: details.response,
|
|
958
969
|
...(Object.keys(recordMetadata).length > 0 ? { metadata: recordMetadata } : {}),
|
|
959
970
|
outer: {
|
|
960
|
-
output:
|
|
971
|
+
output: outerOutput,
|
|
961
972
|
metadata: outerMetadata,
|
|
962
|
-
...(outerCost ? { cost: outerCost } : {})
|
|
963
|
-
}
|
|
973
|
+
...(outerCost ? { cost: outerCost } : {}),
|
|
974
|
+
},
|
|
964
975
|
}, { collection });
|
|
965
976
|
this.logger.debug('Activix.completeRecord completed', {
|
|
966
977
|
aiRequestId: activity.aiRequestId,
|
package/dist/gateway-utils.d.ts
CHANGED
|
@@ -158,7 +158,7 @@ export declare function pickInvokeRoutingMetadataSlice(routerResponse: unknown,
|
|
|
158
158
|
/**
|
|
159
159
|
* Allowlisted generation profile from merged config for client introspection (no secrets, no arbitrary extras).
|
|
160
160
|
*/
|
|
161
|
-
export declare function pickEffectiveModelConfigForMetadata(mergedConfig: unknown): Partial<Pick<ModelConfig, 'model' | 'modelId' | 'provider' | 'temperature' | 'maxTokens' | 'topP'>> | undefined;
|
|
161
|
+
export declare function pickEffectiveModelConfigForMetadata(mergedConfig: unknown): Partial<Pick<ModelConfig, 'model' | 'modelId' | 'provider' | 'temperature' | 'maxTokens' | 'topP' | 'serverTools' | 'openrouter'>> | undefined;
|
|
162
162
|
/**
|
|
163
163
|
* Allowlisted snapshot of merged router config for diagnostics trace responses (no arbitrary extras).
|
|
164
164
|
*/
|
|
@@ -174,6 +174,8 @@ export declare function pickEffectiveModelConfigFromInvokeRequest(request: Pick<
|
|
|
174
174
|
* to find a router-shaped object for token / correlation extraction.
|
|
175
175
|
*/
|
|
176
176
|
export declare function tryExtractRouterLikePayloadFromErrorChain(error: unknown, maxDepth?: number): unknown;
|
|
177
|
+
/** Extract OpenRouter tool metadata from invoke error chains (policy errors, partial router bodies). */
|
|
178
|
+
export declare function tryExtractOpenRouterMetadataFromErrorChain(error: unknown, maxDepth?: number): ReturnType<typeof pickEnhancedOpenRouterMetadata>;
|
|
177
179
|
export declare function pickRequestIdsFromRouterLike(gatewayAiRequestId: string | undefined, routerLike: unknown): GatewayTraceRequestIds | undefined;
|
|
178
180
|
type ModelResolutionCandidate = {
|
|
179
181
|
provider: string;
|
package/dist/gateway-utils.js
CHANGED
|
@@ -7,7 +7,7 @@ import { FallbackExhaustedError } from '@x12i/ai-providers-router';
|
|
|
7
7
|
import { ModelResolutionError, ModelProfileInputRejectedError, ModelProfileUnroutableError, resolveInvokeModel, } from '@x12i/ai-tools';
|
|
8
8
|
import { extractHttpStatusCode } from './gateway-retry.js';
|
|
9
9
|
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
10
|
-
import { MaxTokensRequiredError, ModelRequiredError, } from './instruction-errors.js';
|
|
10
|
+
import { MaxTokensRequiredError, ModelRequiredError, GatewayPolicyViolationError, } from './instruction-errors.js';
|
|
11
11
|
import { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
|
|
12
12
|
import { applyOnlineVariantMigration, applyPostRoutingServerToolsPolicy, mergeOpenRouterConfig, mergeServerToolsConfig, normalizeApplyPatchDefaults, resolveOpenRouterRuntimeDefaults, pickOpenRouterRuntimeMetadataSlice, enrichTraceOpenRouterRuntimeMetadata, } from './openrouter-runtime-adapter/index.js';
|
|
13
13
|
import { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P } from './gateway-defaults.js';
|
|
@@ -626,7 +626,9 @@ function buildTraceAttemptPricingRecord(attempt, mergedConfig) {
|
|
|
626
626
|
* Trace-mode summary: final token usage + resolved billing (after catalog pricing when applicable).
|
|
627
627
|
*/
|
|
628
628
|
export function buildTraceUsageSummary(tokens, billing, maxTokensRequested, openrouterExtras) {
|
|
629
|
-
|
|
629
|
+
const hasOpenRouterMirror = openrouterExtras?.serverTools != null ||
|
|
630
|
+
(openrouterExtras?.citations?.length ?? 0) > 0;
|
|
631
|
+
if (!hasNonZeroTokenUsage(tokens) && !billing.costStatus && !hasOpenRouterMirror) {
|
|
630
632
|
return undefined;
|
|
631
633
|
}
|
|
632
634
|
const summary = { tokens };
|
|
@@ -643,7 +645,7 @@ export function buildTraceUsageSummary(tokens, billing, maxTokensRequested, open
|
|
|
643
645
|
if (billing.costBreakdown) {
|
|
644
646
|
summary.costBreakdown = billing.costBreakdown;
|
|
645
647
|
}
|
|
646
|
-
if (openrouterExtras?.serverTools)
|
|
648
|
+
if (openrouterExtras?.serverTools != null)
|
|
647
649
|
summary.serverTools = openrouterExtras.serverTools;
|
|
648
650
|
if (openrouterExtras?.citations?.length)
|
|
649
651
|
summary.citations = openrouterExtras.citations;
|
|
@@ -729,7 +731,16 @@ export function pickEffectiveModelConfigForMetadata(mergedConfig) {
|
|
|
729
731
|
if (mergedConfig == null || typeof mergedConfig !== 'object')
|
|
730
732
|
return undefined;
|
|
731
733
|
const c = mergedConfig;
|
|
732
|
-
const keys = [
|
|
734
|
+
const keys = [
|
|
735
|
+
'model',
|
|
736
|
+
'modelId',
|
|
737
|
+
'provider',
|
|
738
|
+
'temperature',
|
|
739
|
+
'maxTokens',
|
|
740
|
+
'topP',
|
|
741
|
+
'serverTools',
|
|
742
|
+
'openrouter',
|
|
743
|
+
];
|
|
733
744
|
const out = {};
|
|
734
745
|
for (const k of keys) {
|
|
735
746
|
const v = c[k];
|
|
@@ -822,10 +833,64 @@ export function tryExtractRouterLikePayloadFromErrorChain(error, maxDepth = 8) {
|
|
|
822
833
|
if (isRouterLikeEnvelope(n))
|
|
823
834
|
return n;
|
|
824
835
|
}
|
|
836
|
+
const rejMeta = o.metadata;
|
|
837
|
+
if (rejMeta != null && typeof rejMeta === 'object' && isRouterLikeEnvelope({ metadata: rejMeta })) {
|
|
838
|
+
return { metadata: rejMeta };
|
|
839
|
+
}
|
|
840
|
+
if (cur instanceof GatewayPolicyViolationError && cur.routerMetadata) {
|
|
841
|
+
return { metadata: cur.routerMetadata };
|
|
842
|
+
}
|
|
825
843
|
cur = o.cause;
|
|
826
844
|
}
|
|
827
845
|
return undefined;
|
|
828
846
|
}
|
|
847
|
+
/** Extract OpenRouter tool metadata from invoke error chains (policy errors, partial router bodies). */
|
|
848
|
+
export function tryExtractOpenRouterMetadataFromErrorChain(error, maxDepth = 8) {
|
|
849
|
+
const partial = tryExtractRouterLikePayloadFromErrorChain(error, maxDepth);
|
|
850
|
+
if (partial !== undefined) {
|
|
851
|
+
const fromPartial = pickEnhancedOpenRouterMetadata(partial, false);
|
|
852
|
+
if (fromPartial.serverTools ||
|
|
853
|
+
fromPartial.openrouterRuntime ||
|
|
854
|
+
(fromPartial.citations?.length ?? 0) > 0 ||
|
|
855
|
+
(fromPartial.generatedImages?.length ?? 0) > 0 ||
|
|
856
|
+
(fromPartial.patchProposals?.length ?? 0) > 0) {
|
|
857
|
+
return fromPartial;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
const seen = new Set();
|
|
861
|
+
let cur = error;
|
|
862
|
+
for (let i = 0; i < maxDepth && cur != null; i++) {
|
|
863
|
+
if (typeof cur !== 'object')
|
|
864
|
+
break;
|
|
865
|
+
if (seen.has(cur))
|
|
866
|
+
break;
|
|
867
|
+
seen.add(cur);
|
|
868
|
+
if (cur instanceof GatewayPolicyViolationError && cur.routerMetadata) {
|
|
869
|
+
return {
|
|
870
|
+
serverTools: cur.routerMetadata.serverTools,
|
|
871
|
+
citations: cur.routerMetadata.citations,
|
|
872
|
+
generatedImages: cur.routerMetadata.generatedImages,
|
|
873
|
+
patchProposals: cur.routerMetadata.patchProposals,
|
|
874
|
+
openrouterRuntime: cur.routerMetadata.openrouterRuntime,
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
const rejMeta = cur.metadata;
|
|
878
|
+
if (rejMeta &&
|
|
879
|
+
(rejMeta.serverTools ||
|
|
880
|
+
rejMeta.openrouterRuntime ||
|
|
881
|
+
(rejMeta.citations?.length ?? 0) > 0)) {
|
|
882
|
+
return {
|
|
883
|
+
serverTools: rejMeta.serverTools,
|
|
884
|
+
citations: rejMeta.citations,
|
|
885
|
+
generatedImages: rejMeta.generatedImages,
|
|
886
|
+
patchProposals: rejMeta.patchProposals,
|
|
887
|
+
openrouterRuntime: rejMeta.openrouterRuntime,
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
cur = cur.cause;
|
|
891
|
+
}
|
|
892
|
+
return {};
|
|
893
|
+
}
|
|
829
894
|
export function pickRequestIdsFromRouterLike(gatewayAiRequestId, routerLike) {
|
|
830
895
|
if (typeof gatewayAiRequestId !== 'string' || gatewayAiRequestId.length === 0) {
|
|
831
896
|
return undefined;
|
|
@@ -999,6 +1064,22 @@ export function buildInvokeRejectionMetadata(args) {
|
|
|
999
1064
|
const fallbackAttempts = args.error !== undefined
|
|
1000
1065
|
? tryExtractFallbackAttemptsFromErrorChain(args.error)
|
|
1001
1066
|
: undefined;
|
|
1067
|
+
const openrouterFromPartial = partial !== undefined ? pickEnhancedOpenRouterMetadata(partial, false) : {};
|
|
1068
|
+
const openrouterFromError = args.error !== undefined ? tryExtractOpenRouterMetadataFromErrorChain(args.error) : {};
|
|
1069
|
+
const openrouterMetadata = {
|
|
1070
|
+
...openrouterFromPartial,
|
|
1071
|
+
...(openrouterFromError.serverTools ? { serverTools: openrouterFromError.serverTools } : {}),
|
|
1072
|
+
...(openrouterFromError.citations?.length ? { citations: openrouterFromError.citations } : {}),
|
|
1073
|
+
...(openrouterFromError.generatedImages?.length
|
|
1074
|
+
? { generatedImages: openrouterFromError.generatedImages }
|
|
1075
|
+
: {}),
|
|
1076
|
+
...(openrouterFromError.patchProposals?.length
|
|
1077
|
+
? { patchProposals: openrouterFromError.patchProposals }
|
|
1078
|
+
: {}),
|
|
1079
|
+
...(openrouterFromError.openrouterRuntime
|
|
1080
|
+
? { openrouterRuntime: openrouterFromError.openrouterRuntime }
|
|
1081
|
+
: {}),
|
|
1082
|
+
};
|
|
1002
1083
|
return {
|
|
1003
1084
|
aiRequestId: args.request.aiRequestId,
|
|
1004
1085
|
identity: args.request.identity,
|
|
@@ -1009,7 +1090,21 @@ export function buildInvokeRejectionMetadata(args) {
|
|
|
1009
1090
|
...(tokens !== undefined ? { tokens } : {}),
|
|
1010
1091
|
...(requestIds !== undefined ? { requestIds } : {}),
|
|
1011
1092
|
...(fallbackAttempts !== undefined ? { fallbackAttempts } : {}),
|
|
1012
|
-
...(mc === undefined ? { mergeConfigUnavailable: true } : {})
|
|
1093
|
+
...(mc === undefined ? { mergeConfigUnavailable: true } : {}),
|
|
1094
|
+
...(openrouterMetadata.serverTools ? { serverTools: openrouterMetadata.serverTools } : {}),
|
|
1095
|
+
...(openrouterMetadata.citations?.length ? { citations: openrouterMetadata.citations } : {}),
|
|
1096
|
+
...(openrouterMetadata.generatedImages?.length
|
|
1097
|
+
? { generatedImages: openrouterMetadata.generatedImages }
|
|
1098
|
+
: {}),
|
|
1099
|
+
...(openrouterMetadata.patchProposals?.length
|
|
1100
|
+
? { patchProposals: openrouterMetadata.patchProposals }
|
|
1101
|
+
: {}),
|
|
1102
|
+
...(openrouterMetadata.openrouterRuntime
|
|
1103
|
+
? { openrouterRuntime: openrouterMetadata.openrouterRuntime }
|
|
1104
|
+
: {}),
|
|
1105
|
+
...(openrouterMetadata.openrouterRuntime?.policyViolations?.length
|
|
1106
|
+
? { policyViolations: openrouterMetadata.openrouterRuntime.policyViolations }
|
|
1107
|
+
: {}),
|
|
1013
1108
|
};
|
|
1014
1109
|
}
|
|
1015
1110
|
export function attachGatewayInvokeRejectionMetadata(err, metadata) {
|
package/dist/gateway.js
CHANGED
|
@@ -11,7 +11,7 @@ import { resolveRetryConfig } from './gateway-defaults.js';
|
|
|
11
11
|
import { buildMessages } from './message-builder.js';
|
|
12
12
|
import { extractJsonFromFlexMd } from './flex-md-loader.js';
|
|
13
13
|
import { enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
|
|
14
|
-
import { attachGatewayInvokeRejectionMetadata, buildGatewayFallbackAttemptsFromTrace, buildInvokeRejectionMetadata, capActivityFullResponsePayload, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter, DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, extractCostUsdFromRouterResponse, extractTokenUsageFromRouterResponse, mergeConfig, pickEffectiveModelConfigForMetadata, pickInvokeRoutingMetadataSlice, pickTraceMergedRouterConfig, pickEnhancedOpenRouterMetadata, resolveCostCompletionWithAiTools, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, tryExtractRouterLikePayloadFromErrorChain } from './gateway-utils.js';
|
|
14
|
+
import { attachGatewayInvokeRejectionMetadata, buildGatewayFallbackAttemptsFromTrace, buildInvokeRejectionMetadata, capActivityFullResponsePayload, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter, DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, extractCostUsdFromRouterResponse, extractTokenUsageFromRouterResponse, mergeConfig, pickEffectiveModelConfigForMetadata, pickInvokeRoutingMetadataSlice, pickTraceMergedRouterConfig, pickEnhancedOpenRouterMetadata, resolveCostCompletionWithAiTools, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, tryExtractRouterLikePayloadFromErrorChain, tryExtractOpenRouterMetadataFromErrorChain, } from './gateway-utils.js';
|
|
15
15
|
import { buildTraceAttemptOpenRouterRuntimeSlice } from './openrouter-runtime-adapter/index.js';
|
|
16
16
|
import { getAiToolsClient } from './ai-tools-client.js';
|
|
17
17
|
import { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
@@ -479,6 +479,10 @@ export class AIGateway {
|
|
|
479
479
|
}
|
|
480
480
|
else if (tryErr) {
|
|
481
481
|
a.error = { name: tryErr.name || 'Error', message: capErrorMessage(tryErr.message || String(tryErr)) };
|
|
482
|
+
const errOpenRouter = tryExtractOpenRouterMetadataFromErrorChain(tryErr);
|
|
483
|
+
const runtimeSlice = buildTraceAttemptOpenRouterRuntimeSlice(errOpenRouter);
|
|
484
|
+
if (runtimeSlice)
|
|
485
|
+
a.openrouterRuntime = runtimeSlice;
|
|
482
486
|
}
|
|
483
487
|
}
|
|
484
488
|
});
|
|
@@ -745,6 +749,22 @@ export class AIGateway {
|
|
|
745
749
|
error: err
|
|
746
750
|
});
|
|
747
751
|
attachGatewayInvokeRejectionMetadata(err, rejectMeta);
|
|
752
|
+
if (this.activityManager && activity) {
|
|
753
|
+
try {
|
|
754
|
+
await this.activityManager.logFailure(activity, err, {
|
|
755
|
+
endTime: Date.now(),
|
|
756
|
+
duration: Date.now() - startTime,
|
|
757
|
+
error: err.message,
|
|
758
|
+
response: { metadata: rejectMeta },
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
catch (activityError) {
|
|
762
|
+
this.logger.warn('Failed to track activity failure', {
|
|
763
|
+
aiRequestId: request.aiRequestId,
|
|
764
|
+
error: activityError instanceof Error ? activityError.message : String(activityError),
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
}
|
|
748
768
|
if (err.message.includes(NO_PROVIDER_ERROR)) {
|
|
749
769
|
const wrapped = new Error(err.message + NO_PROVIDER_HINT);
|
|
750
770
|
wrapped.cause = err;
|
package/dist/index.d.ts
CHANGED
|
@@ -18,8 +18,8 @@ export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError,
|
|
|
18
18
|
export { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
|
|
19
19
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
20
20
|
export { registerOpenRouterRuntime, shouldUseOpenRouterRuntime, mapGatewayServerTools, } from './openrouter-runtime-adapter/index.js';
|
|
21
|
-
export type { GatewayConfig, ProviderModelRef, ModelConfig, GatewayModelConfig, GatewayServerToolsConfig, GatewayOpenRouterConfig, GatewayOpenRouterRuntimeConfig, GatewayServerToolUsageMap, GatewayCitation, GatewayGeneratedImage, GatewayPatchProposal, GatewayOpenRouterRuntimeMetadata, RetryConfig, ChatRequest, AIInvokeRequest, AIRequest, GatewayActionType, GatewayInvokeRejectionMetadata, GatewayFallbackAttempt, GatewayTraceRequestIds, GatewayTraceAttempt, GatewayTraceUsageSummary, GatewayTraceMergedConfig, EnhancedLLMResponse, InstructionMetadata, ValidationRule, TemplateRenderOptions, SmartInputConfig, SmartInputRenderOptions } from './types.js';
|
|
22
|
-
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, pickEnhancedOpenRouterMetadata, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
21
|
+
export type { GatewayConfig, ProviderModelRef, ModelConfig, GatewayModelConfig, GatewayServerToolsConfig, GatewayServerToolMode, GatewayServerToolUsage, GatewayNestedServerTool, GatewayWebSearchToolConfig, GatewayWebFetchToolConfig, GatewayDatetimeToolConfig, GatewayImageGenerationToolConfig, GatewayApplyPatchToolConfig, GatewayFusionToolConfig, GatewayAdvisorToolConfig, GatewaySubagentToolConfig, GatewayOpenRouterErrorMetadata, GatewayOpenRouterConfig, GatewayOpenRouterRuntimeConfig, GatewayServerToolUsageMap, GatewayCitation, GatewayGeneratedImage, GatewayPatchProposal, GatewayOpenRouterRuntimeMetadata, RetryConfig, ChatRequest, AIInvokeRequest, AIRequest, GatewayActionType, GatewayInvokeRejectionMetadata, GatewayFallbackAttempt, GatewayTraceRequestIds, GatewayTraceAttempt, GatewayTraceUsageSummary, GatewayTraceMergedConfig, EnhancedLLMResponse, InstructionMetadata, ValidationRule, TemplateRenderOptions, SmartInputConfig, SmartInputRenderOptions } from './types.js';
|
|
22
|
+
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractOpenRouterMetadataFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, pickEnhancedOpenRouterMetadata, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
23
23
|
export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
|
|
24
24
|
export type { GatewayOperationalMode } from './gateway-mode.js';
|
|
25
25
|
export { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_RETRY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P, resolveRetryConfig } from './gateway-defaults.js';
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError,
|
|
|
19
19
|
export { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
|
|
20
20
|
export { autoRegisterProviders } from './gateway-provider-auto-register.js';
|
|
21
21
|
export { registerOpenRouterRuntime, shouldUseOpenRouterRuntime, mapGatewayServerTools, } from './openrouter-runtime-adapter/index.js';
|
|
22
|
-
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, pickEnhancedOpenRouterMetadata, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
22
|
+
export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractOpenRouterMetadataFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, pickEnhancedOpenRouterMetadata, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
|
|
23
23
|
export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
|
|
24
24
|
export { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_RETRY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P, resolveRetryConfig } from './gateway-defaults.js';
|
|
25
25
|
export { contractSpecToFieldKeys, enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Errors for instruction / prompt template resolution.
|
|
3
3
|
*/
|
|
4
|
+
import type { GatewayOpenRouterErrorMetadata } from './types.js';
|
|
4
5
|
export declare class ModelRequiredError extends Error {
|
|
5
6
|
readonly code = "MODEL_REQUIRED";
|
|
6
7
|
constructor(message?: string);
|
|
@@ -20,7 +21,11 @@ export declare class GatewayValidationError extends Error {
|
|
|
20
21
|
}
|
|
21
22
|
export declare class ProviderConfigError extends Error {
|
|
22
23
|
readonly code: string;
|
|
23
|
-
|
|
24
|
+
/** Stable alternate codes documented for upstream consumers (e.g. CR-GW-ST-001). */
|
|
25
|
+
readonly codeAliases?: readonly string[];
|
|
26
|
+
constructor(code: string, message: string, options?: {
|
|
27
|
+
codeAliases?: readonly string[];
|
|
28
|
+
});
|
|
24
29
|
}
|
|
25
30
|
export declare class ProviderInvokeError extends Error {
|
|
26
31
|
readonly code: string;
|
|
@@ -33,7 +38,11 @@ export declare class ProviderInvokeError extends Error {
|
|
|
33
38
|
}
|
|
34
39
|
export declare class GatewayPolicyViolationError extends Error {
|
|
35
40
|
readonly code: string;
|
|
36
|
-
|
|
41
|
+
/** Partial OpenRouter tool metadata preserved when policy fails after a runtime response. */
|
|
42
|
+
routerMetadata?: GatewayOpenRouterErrorMetadata;
|
|
43
|
+
constructor(code: string, message: string, options?: {
|
|
44
|
+
routerMetadata?: GatewayOpenRouterErrorMetadata;
|
|
45
|
+
});
|
|
37
46
|
}
|
|
38
47
|
export declare class InstructionNotFoundError extends Error {
|
|
39
48
|
key: string;
|
|
@@ -35,10 +35,13 @@ export class GatewayValidationError extends Error {
|
|
|
35
35
|
}
|
|
36
36
|
export class ProviderConfigError extends Error {
|
|
37
37
|
code;
|
|
38
|
-
|
|
38
|
+
/** Stable alternate codes documented for upstream consumers (e.g. CR-GW-ST-001). */
|
|
39
|
+
codeAliases;
|
|
40
|
+
constructor(code, message, options) {
|
|
39
41
|
super(message);
|
|
40
42
|
this.name = 'ProviderConfigError';
|
|
41
43
|
this.code = code;
|
|
44
|
+
this.codeAliases = options?.codeAliases;
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
export class ProviderInvokeError extends Error {
|
|
@@ -55,10 +58,13 @@ export class ProviderInvokeError extends Error {
|
|
|
55
58
|
}
|
|
56
59
|
export class GatewayPolicyViolationError extends Error {
|
|
57
60
|
code;
|
|
58
|
-
|
|
61
|
+
/** Partial OpenRouter tool metadata preserved when policy fails after a runtime response. */
|
|
62
|
+
routerMetadata;
|
|
63
|
+
constructor(code, message, options) {
|
|
59
64
|
super(message);
|
|
60
65
|
this.name = 'GatewayPolicyViolationError';
|
|
61
66
|
this.code = code;
|
|
67
|
+
this.routerMetadata = options?.routerMetadata;
|
|
62
68
|
}
|
|
63
69
|
}
|
|
64
70
|
export class InstructionNotFoundError extends Error {
|
|
@@ -3,6 +3,6 @@ export { createOpenRouterRuntimeProvider } from './create-openrouter-runtime-pro
|
|
|
3
3
|
export { createOpenRouterRuntimeAdapter } from './create-openrouter-runtime-adapter.js';
|
|
4
4
|
export { mapGatewayServerTools } from './map-server-tools.js';
|
|
5
5
|
export { mapGatewayRequestToRuntimeRequest } from './map-gateway-request.js';
|
|
6
|
-
export { pickOpenRouterRuntimeMetadataSlice, extractOpenRouterRuntimeRouterMetadata, parseRuntimeResponseToAIResponse, } from './map-runtime-response.js';
|
|
6
|
+
export { buildPartialMetadataFromRuntimeResponse, buildRouterLikeEnvelopeFromRuntimeResponse, pickOpenRouterRuntimeMetadataSlice, extractOpenRouterRuntimeRouterMetadata, parseRuntimeResponseToAIResponse, } from './map-runtime-response.js';
|
|
7
7
|
export { applyOnlineVariantMigration, applyPostRoutingServerToolsPolicy, mergeOpenRouterConfig, mergeServerToolsConfig, normalizeApplyPatchDefaults, validateApplyPatchConfig, hasAnyActiveServerTool, hasRequiredServerTool, } from './validate-server-tools.js';
|
|
8
8
|
export { buildTraceAttemptOpenRouterRuntimeSlice, enrichTraceOpenRouterRuntimeMetadata, redactRawOpenRouterPayload, } from './map-trace.js';
|
|
@@ -3,6 +3,6 @@ export { createOpenRouterRuntimeProvider } from './create-openrouter-runtime-pro
|
|
|
3
3
|
export { createOpenRouterRuntimeAdapter } from './create-openrouter-runtime-adapter.js';
|
|
4
4
|
export { mapGatewayServerTools } from './map-server-tools.js';
|
|
5
5
|
export { mapGatewayRequestToRuntimeRequest } from './map-gateway-request.js';
|
|
6
|
-
export { pickOpenRouterRuntimeMetadataSlice, extractOpenRouterRuntimeRouterMetadata, parseRuntimeResponseToAIResponse, } from './map-runtime-response.js';
|
|
6
|
+
export { buildPartialMetadataFromRuntimeResponse, buildRouterLikeEnvelopeFromRuntimeResponse, pickOpenRouterRuntimeMetadataSlice, extractOpenRouterRuntimeRouterMetadata, parseRuntimeResponseToAIResponse, } from './map-runtime-response.js';
|
|
7
7
|
export { applyOnlineVariantMigration, applyPostRoutingServerToolsPolicy, mergeOpenRouterConfig, mergeServerToolsConfig, normalizeApplyPatchDefaults, validateApplyPatchConfig, hasAnyActiveServerTool, hasRequiredServerTool, } from './validate-server-tools.js';
|
|
8
8
|
export { buildTraceAttemptOpenRouterRuntimeSlice, enrichTraceOpenRouterRuntimeMetadata, redactRawOpenRouterPayload, } from './map-trace.js';
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { OpenRouterHttpError, RuntimeConfigError, } from '@x12i/openrouter-runtime';
|
|
2
2
|
import { GatewayPolicyViolationError, GatewayValidationError, ProviderConfigError, ProviderInvokeError, } from '../instruction-errors.js';
|
|
3
|
+
import { buildPartialMetadataFromRuntimeResponse, buildRouterLikeEnvelopeFromRuntimeResponse, } from './map-runtime-response.js';
|
|
4
|
+
function attachRouterEnvelopeToError(err, response) {
|
|
5
|
+
const envelope = buildRouterLikeEnvelopeFromRuntimeResponse(response);
|
|
6
|
+
err.response = envelope;
|
|
7
|
+
}
|
|
3
8
|
export function throwMappedRuntimeConfigError(err) {
|
|
4
9
|
const code = err.code;
|
|
5
10
|
if (code === 'OPENROUTER_API_KEY_MISSING') {
|
|
@@ -25,17 +30,26 @@ export function throwMappedOpenRouterHttpError(err) {
|
|
|
25
30
|
export function throwMappedRuntimeResponseErrors(response) {
|
|
26
31
|
if (response.status === 'completed')
|
|
27
32
|
return;
|
|
33
|
+
const routerMetadata = buildPartialMetadataFromRuntimeResponse(response);
|
|
28
34
|
const primary = response.errors[0];
|
|
29
35
|
if (!primary) {
|
|
30
|
-
|
|
36
|
+
const err = new ProviderInvokeError('OPENROUTER_REQUEST_FAILED', 'OpenRouter runtime request failed');
|
|
37
|
+
attachRouterEnvelopeToError(err, response);
|
|
38
|
+
throw err;
|
|
31
39
|
}
|
|
32
40
|
if (primary.source === 'policy' || response.status === 'policy_violation') {
|
|
33
|
-
|
|
41
|
+
const err = new GatewayPolicyViolationError(primary.code, primary.message, { routerMetadata });
|
|
42
|
+
attachRouterEnvelopeToError(err, response);
|
|
43
|
+
throw err;
|
|
34
44
|
}
|
|
35
45
|
if (primary.retryable) {
|
|
36
|
-
|
|
46
|
+
const err = new ProviderInvokeError(primary.code, primary.message, { retryable: true });
|
|
47
|
+
attachRouterEnvelopeToError(err, response);
|
|
48
|
+
throw err;
|
|
37
49
|
}
|
|
38
|
-
|
|
50
|
+
const err = new ProviderInvokeError(primary.code, primary.message);
|
|
51
|
+
attachRouterEnvelopeToError(err, response);
|
|
52
|
+
throw err;
|
|
39
53
|
}
|
|
40
54
|
export function mapRuntimeErrorToGatewayError(err) {
|
|
41
55
|
if (err instanceof RuntimeConfigError) {
|
|
@@ -15,6 +15,9 @@ export type OpenRouterRuntimeRouterMetadata = {
|
|
|
15
15
|
costUsd?: number;
|
|
16
16
|
costStatus?: 'priced' | 'unpriced';
|
|
17
17
|
};
|
|
18
|
+
export declare function buildPartialMetadataFromRuntimeResponse(response: RuntimeResponse): OpenRouterRuntimeRouterMetadata;
|
|
19
|
+
/** Router-shaped envelope for error-chain metadata extraction. */
|
|
20
|
+
export declare function buildRouterLikeEnvelopeFromRuntimeResponse(response: RuntimeResponse): Record<string, unknown>;
|
|
18
21
|
export declare function extractOpenRouterRuntimeRouterMetadata(routerResponse: unknown): OpenRouterRuntimeRouterMetadata;
|
|
19
22
|
export declare function parseRuntimeResponseToAIResponse(input: {
|
|
20
23
|
requestId: string;
|
|
@@ -64,6 +64,39 @@ export function buildOpenRouterRuntimeMetadata(response) {
|
|
|
64
64
|
requestIds: response.id ? [response.id] : undefined,
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
+
export function buildPartialMetadataFromRuntimeResponse(response) {
|
|
68
|
+
return {
|
|
69
|
+
serverTools: mapRuntimeToolUsage(response.toolUsage),
|
|
70
|
+
citations: mapRuntimeCitations(response.citations),
|
|
71
|
+
generatedImages: mapRuntimeImages(response.images),
|
|
72
|
+
patchProposals: mapRuntimePatches(response.patches),
|
|
73
|
+
openrouterRuntime: buildOpenRouterRuntimeMetadata(response),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Router-shaped envelope for error-chain metadata extraction. */
|
|
77
|
+
export function buildRouterLikeEnvelopeFromRuntimeResponse(response) {
|
|
78
|
+
const partial = buildPartialMetadataFromRuntimeResponse(response);
|
|
79
|
+
const usage = response.usage;
|
|
80
|
+
const promptTokens = usage?.inputTokens ?? 0;
|
|
81
|
+
const completionTokens = usage?.outputTokens ?? 0;
|
|
82
|
+
return {
|
|
83
|
+
requestId: response.id,
|
|
84
|
+
metadata: {
|
|
85
|
+
provider: 'openrouter',
|
|
86
|
+
modelUsed: response.model,
|
|
87
|
+
serverTools: partial.serverTools,
|
|
88
|
+
citations: partial.citations,
|
|
89
|
+
generatedImages: partial.generatedImages,
|
|
90
|
+
patchProposals: partial.patchProposals,
|
|
91
|
+
openrouterRuntime: partial.openrouterRuntime,
|
|
92
|
+
},
|
|
93
|
+
usage: {
|
|
94
|
+
promptTokens,
|
|
95
|
+
completionTokens,
|
|
96
|
+
totalTokens: usage?.totalTokens ?? promptTokens + completionTokens,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
67
100
|
export function extractOpenRouterRuntimeRouterMetadata(routerResponse) {
|
|
68
101
|
if (routerResponse == null || typeof routerResponse !== 'object')
|
|
69
102
|
return {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export function buildTraceAttemptOpenRouterRuntimeSlice(metadata) {
|
|
2
|
-
|
|
2
|
+
const hasPolicy = (metadata.openrouterRuntime?.policyViolations?.length ?? 0) > 0 ||
|
|
3
|
+
(metadata.openrouterRuntime?.warnings?.length ?? 0) > 0;
|
|
4
|
+
if (!metadata.openrouterRuntime && !metadata.serverTools && !hasPolicy)
|
|
3
5
|
return undefined;
|
|
4
6
|
return {
|
|
5
7
|
apiMode: metadata.openrouterRuntime?.apiMode,
|
|
@@ -7,6 +9,8 @@ export function buildTraceAttemptOpenRouterRuntimeSlice(metadata) {
|
|
|
7
9
|
citationCount: metadata.citations?.length ?? 0,
|
|
8
10
|
generatedImageCount: metadata.generatedImages?.length ?? 0,
|
|
9
11
|
patchProposalCount: metadata.patchProposals?.length ?? 0,
|
|
12
|
+
warnings: metadata.openrouterRuntime?.warnings ?? [],
|
|
13
|
+
policyViolations: metadata.openrouterRuntime?.policyViolations ?? [],
|
|
10
14
|
};
|
|
11
15
|
}
|
|
12
16
|
export function enrichTraceOpenRouterRuntimeMetadata(base, metadata, traceEnabled) {
|
|
@@ -75,7 +75,7 @@ export function applyPostRoutingServerToolsPolicy(input) {
|
|
|
75
75
|
validateApplyPatchConfig(serverTools, openrouter);
|
|
76
76
|
if (hasRequiredServerTool(serverTools)) {
|
|
77
77
|
if (!openRouterApiKey) {
|
|
78
|
-
throw new ProviderConfigError('OPENROUTER_SERVER_TOOL_REQUIRES_KEY', 'Required OpenRouter server tools need OPENROUTER_API_KEY or GatewayConfig.openrouter.apiKey');
|
|
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
79
|
}
|
|
80
80
|
if (provider !== 'openrouter') {
|
|
81
81
|
provider = 'openrouter';
|
package/dist/types.d.ts
CHANGED
|
@@ -297,8 +297,18 @@ export type GatewayTraceAttempt = {
|
|
|
297
297
|
citationCount?: number;
|
|
298
298
|
generatedImageCount?: number;
|
|
299
299
|
patchProposalCount?: number;
|
|
300
|
+
warnings?: GatewayWarning[];
|
|
301
|
+
policyViolations?: GatewayPolicyViolation[];
|
|
300
302
|
};
|
|
301
303
|
};
|
|
304
|
+
/** OpenRouter tool metadata attached to policy/invoke errors and rejection payloads. */
|
|
305
|
+
export type GatewayOpenRouterErrorMetadata = {
|
|
306
|
+
serverTools?: GatewayServerToolUsageMap;
|
|
307
|
+
citations?: GatewayCitation[];
|
|
308
|
+
generatedImages?: GatewayGeneratedImage[];
|
|
309
|
+
patchProposals?: GatewayPatchProposal[];
|
|
310
|
+
openrouterRuntime?: GatewayOpenRouterRuntimeMetadata;
|
|
311
|
+
};
|
|
302
312
|
/**
|
|
303
313
|
* Allowlisted merged router/generation config returned in {@link EnhancedLLMResponse.metadata}
|
|
304
314
|
* when `diagnostics.mode === 'trace'`. Omits arbitrary extras and secrets.
|
|
@@ -354,6 +364,13 @@ export type GatewayInvokeRejectionMetadata = {
|
|
|
354
364
|
* Routing facts may only reflect request.config / modelConfig, not flex-md defaults.
|
|
355
365
|
*/
|
|
356
366
|
mergeConfigUnavailable?: true;
|
|
367
|
+
/** OpenRouter server-tool usage map when available on partial router response or policy error. */
|
|
368
|
+
serverTools?: GatewayServerToolUsageMap;
|
|
369
|
+
citations?: GatewayCitation[];
|
|
370
|
+
generatedImages?: GatewayGeneratedImage[];
|
|
371
|
+
patchProposals?: GatewayPatchProposal[];
|
|
372
|
+
openrouterRuntime?: GatewayOpenRouterRuntimeMetadata;
|
|
373
|
+
policyViolations?: GatewayPolicyViolation[];
|
|
357
374
|
};
|
|
358
375
|
/** Serializable slice of a router fallback attempt for rejection metadata. */
|
|
359
376
|
export type GatewayFallbackAttempt = {
|
|
@@ -523,10 +540,12 @@ export interface ModelConfig {
|
|
|
523
540
|
stop?: string[];
|
|
524
541
|
/**
|
|
525
542
|
* OpenRouter-only server tools. Ignored unless final provider is openrouter.
|
|
543
|
+
* Merge precedence at invoke: `modelConfig.serverTools` > `config.serverTools` > `GatewayConfig.defaultServerTools`.
|
|
526
544
|
*/
|
|
527
545
|
serverTools?: GatewayServerToolsConfig;
|
|
528
546
|
/**
|
|
529
547
|
* OpenRouter-specific execution controls.
|
|
548
|
+
* Merge precedence at invoke: `modelConfig.openrouter` > `config.openrouter` > `GatewayConfig.openrouterRuntime` defaults.
|
|
530
549
|
*/
|
|
531
550
|
openrouter?: GatewayOpenRouterConfig;
|
|
532
551
|
/**
|