@velum-labs/routekit-daemon 1.3.2 → 1.5.0
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/dist/account-enroll-service.d.ts +1 -3
- package/dist/account-enroll-service.js +262 -262
- package/dist/account-mutation-service.d.ts +1 -3
- package/dist/account-mutation-service.js +302 -283
- package/dist/account-query-service.d.ts +1 -3
- package/dist/account-query-service.js +88 -86
- package/dist/call-attribution-store.js +544 -1
- package/dist/control-dispatch.js +9 -15
- package/dist/daemon-bootstrap-preflight.js +1 -1
- package/dist/daemon-control-handlers.d.ts +1 -1
- package/dist/daemon-control-handlers.js +24 -27
- package/dist/daemon-generations.d.ts +14 -28
- package/dist/daemon-generations.js +114 -53
- package/dist/daemon-lifecycle-service.d.ts +1 -3
- package/dist/daemon-lifecycle-service.js +88 -94
- package/dist/daemon-maintenance.d.ts +27 -49
- package/dist/daemon-maintenance.js +5 -1
- package/dist/daemon-options.d.ts +3 -2
- package/dist/daemon-policy-context.d.ts +1 -0
- package/dist/daemon-policy-context.js +1 -0
- package/dist/daemon-runtime-state.d.ts +1 -1
- package/dist/daemon-runtime-state.js +2 -3
- package/dist/daemon-state-context.d.ts +1 -1
- package/dist/daemon-state-context.js +1 -38
- package/dist/doctor-service.d.ts +1 -3
- package/dist/doctor-service.js +87 -91
- package/dist/effect/daemon-live.d.ts +7 -6
- package/dist/effect/daemon-live.js +57 -135
- package/dist/eval-routing-service.d.ts +1 -3
- package/dist/eval-routing-service.js +45 -69
- package/dist/eval-session-execution-store.d.ts +24 -0
- package/dist/eval-session-execution-store.js +253 -0
- package/dist/host-protocol.d.ts +1 -0
- package/dist/host.js +3 -0
- package/dist/internal-providers/provider-anthropic/index.d.ts +23 -0
- package/dist/internal-providers/provider-anthropic/index.js +42 -0
- package/dist/internal-providers/provider-anthropic/test/provider.test.js +33 -0
- package/dist/internal-providers/provider-azure/index.d.ts +25 -0
- package/dist/internal-providers/provider-azure/index.js +101 -0
- package/dist/internal-providers/provider-azure/test/provider.test.js +83 -0
- package/dist/internal-providers/provider-bedrock/codec.d.ts +11 -0
- package/dist/internal-providers/provider-bedrock/codec.js +484 -0
- package/dist/internal-providers/provider-bedrock/connection-settings.d.ts +6 -0
- package/dist/internal-providers/provider-bedrock/connection-settings.js +41 -0
- package/dist/internal-providers/provider-bedrock/connection.d.ts +17 -0
- package/dist/internal-providers/provider-bedrock/connection.js +143 -0
- package/dist/internal-providers/provider-bedrock/index.d.ts +27 -0
- package/dist/internal-providers/provider-bedrock/index.js +47 -0
- package/dist/internal-providers/provider-bedrock/source.d.ts +39 -0
- package/dist/internal-providers/provider-bedrock/source.js +454 -0
- package/dist/internal-providers/provider-bedrock/test/bedrock-source.test.d.ts +1 -0
- package/dist/internal-providers/provider-bedrock/test/bedrock-source.test.js +786 -0
- package/dist/internal-providers/provider-bedrock/test/connection.test.d.ts +1 -0
- package/dist/internal-providers/provider-bedrock/test/connection.test.js +249 -0
- package/dist/internal-providers/provider-bedrock/test/workload-credentials.test.d.ts +1 -0
- package/dist/internal-providers/provider-bedrock/test/workload-credentials.test.js +29 -0
- package/dist/internal-providers/provider-bedrock/workload-credentials.d.ts +5 -0
- package/dist/internal-providers/provider-bedrock/workload-credentials.js +14 -0
- package/dist/internal-providers/provider-claude-code/index.d.ts +6 -0
- package/dist/internal-providers/provider-claude-code/index.js +4 -0
- package/dist/internal-providers/provider-claude-code/module-api.d.ts +25 -0
- package/dist/internal-providers/provider-claude-code/module-api.js +31 -0
- package/dist/internal-providers/provider-claude-code/relay.d.ts +20 -0
- package/dist/internal-providers/provider-claude-code/relay.js +115 -0
- package/dist/internal-providers/provider-claude-code/subscription.d.ts +2 -0
- package/dist/internal-providers/provider-claude-code/subscription.js +194 -0
- package/dist/internal-providers/provider-claude-code/test/http-submission.test.d.ts +1 -0
- package/dist/internal-providers/provider-claude-code/test/http-submission.test.js +140 -0
- package/dist/internal-providers/provider-claude-code/test/relay.test.d.ts +1 -0
- package/dist/internal-providers/provider-claude-code/test/relay.test.js +379 -0
- package/dist/internal-providers/provider-claude-code/test/subscription.test.d.ts +1 -0
- package/dist/internal-providers/provider-claude-code/test/subscription.test.js +256 -0
- package/dist/internal-providers/provider-cliproxy/index.d.ts +9 -0
- package/dist/internal-providers/provider-cliproxy/index.js +15 -0
- package/dist/internal-providers/provider-codex/index.d.ts +6 -0
- package/dist/internal-providers/provider-codex/index.js +7 -0
- package/dist/internal-providers/provider-codex/module-api.d.ts +25 -0
- package/dist/internal-providers/provider-codex/module-api.js +31 -0
- package/dist/internal-providers/provider-codex/relay.d.ts +123 -0
- package/dist/internal-providers/provider-codex/relay.js +261 -0
- package/dist/internal-providers/provider-codex/subscription.d.ts +2 -0
- package/dist/internal-providers/provider-codex/subscription.js +555 -0
- package/dist/internal-providers/provider-codex/test/http-submission.test.d.ts +1 -0
- package/dist/internal-providers/provider-codex/test/http-submission.test.js +142 -0
- package/dist/internal-providers/provider-codex/test/relay.test.d.ts +1 -0
- package/dist/internal-providers/provider-codex/test/relay.test.js +600 -0
- package/dist/internal-providers/provider-codex/test/subscription.test.d.ts +1 -0
- package/dist/internal-providers/provider-codex/test/subscription.test.js +70 -0
- package/dist/internal-providers/provider-codex/test/translated-content.test.d.ts +1 -0
- package/dist/internal-providers/provider-codex/test/translated-content.test.js +91 -0
- package/dist/internal-providers/provider-custom/index.d.ts +9 -0
- package/dist/internal-providers/provider-custom/index.js +15 -0
- package/dist/internal-providers/provider-google/index.d.ts +23 -0
- package/dist/internal-providers/provider-google/index.js +43 -0
- package/dist/internal-providers/provider-google/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-google/test/provider.test.js +34 -0
- package/dist/internal-providers/provider-google-vertex/index.d.ts +28 -0
- package/dist/internal-providers/provider-google-vertex/index.js +137 -0
- package/dist/internal-providers/provider-google-vertex/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-google-vertex/test/provider.test.js +85 -0
- package/dist/internal-providers/provider-openai/documented-reasoning.d.ts +9 -0
- package/dist/internal-providers/provider-openai/documented-reasoning.js +26 -0
- package/dist/internal-providers/provider-openai/index.d.ts +23 -0
- package/dist/internal-providers/provider-openai/index.js +74 -0
- package/dist/internal-providers/provider-openai/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-openai/test/provider.test.js +268 -0
- package/dist/internal-providers/provider-openai-compatible/index.d.ts +9 -0
- package/dist/internal-providers/provider-openai-compatible/index.js +15 -0
- package/dist/internal-providers/provider-openai-compatible/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-openai-compatible/test/provider.test.js +40 -0
- package/dist/internal-providers/provider-openrouter/connection.d.ts +5 -0
- package/dist/internal-providers/provider-openrouter/connection.js +143 -0
- package/dist/internal-providers/provider-openrouter/index.d.ts +28 -0
- package/dist/internal-providers/provider-openrouter/index.js +42 -0
- package/dist/internal-providers/provider-openrouter/pricing.d.ts +4 -0
- package/dist/internal-providers/provider-openrouter/pricing.js +108 -0
- package/dist/internal-providers/provider-openrouter/test/connection.test.d.ts +1 -0
- package/dist/internal-providers/provider-openrouter/test/connection.test.js +71 -0
- package/dist/internal-providers/provider-openrouter/test/pricing.test.d.ts +1 -0
- package/dist/internal-providers/provider-openrouter/test/pricing.test.js +45 -0
- package/dist/internal-providers/provider-openrouter/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-openrouter/test/provider.test.js +141 -0
- package/dist/internal-providers/provider-openrouter/test/validation.test.d.ts +1 -0
- package/dist/internal-providers/provider-openrouter/test/validation.test.js +225 -0
- package/dist/launcher-service.d.ts +3 -7
- package/dist/launcher-service.js +144 -133
- package/dist/provider-modules.d.ts +6 -0
- package/dist/provider-modules.js +55 -0
- package/dist/provider-query-service.d.ts +1 -3
- package/dist/provider-query-service.js +140 -143
- package/dist/router-generation-service.d.ts +1 -3
- package/dist/router-generation-service.js +83 -78
- package/dist/services/eval-session/service.d.ts +20 -8
- package/dist/services/eval-session/service.js +410 -40
- package/dist/services/gateway-generation/service.d.ts +7 -3
- package/dist/services/gateway-generation/service.js +126 -63
- package/dist/services/generations/service.d.ts +3 -3
- package/dist/services/generations/service.js +5 -1
- package/dist/telemetry-application-service.d.ts +1 -3
- package/dist/telemetry-application-service.js +41 -45
- package/dist/test/call-attribution-store.test.js +186 -0
- package/dist/test/daemon-accounts.test.js +2 -2
- package/dist/test/daemon-data-plane.test.js +24 -45
- package/dist/test/daemon-fixtures.js +1 -1
- package/dist/test/daemon-generation-transaction.test.js +66 -2
- package/dist/test/daemon-runtime-state.test.js +28 -1
- package/dist/test/effect-daemon.test.js +38 -0
- package/dist/test/eval-routing-application-service.test.js +6 -6
- package/dist/test/eval-session-execution.test.d.ts +1 -0
- package/dist/test/eval-session-execution.test.js +356 -0
- package/dist/test/eval-session-service.test.js +70 -1
- package/dist/test/host-worker-session.test.js +1 -0
- package/dist/test/launcher-session.test.d.ts +1 -0
- package/dist/test/launcher-session.test.js +87 -0
- package/dist/test/leaderboard.test.js +25 -1
- package/dist/test/provider-modules.test.d.ts +1 -0
- package/dist/test/provider-modules.test.js +65 -0
- package/dist/test/routekit-upstream-sources.test.d.ts +1 -0
- package/dist/test/routekit-upstream-sources.test.js +40 -0
- package/dist/test/runtime-composition-types.test.js +11 -0
- package/dist/token-application-service.d.ts +1 -3
- package/dist/token-application-service.js +38 -44
- package/dist/worker.js +5 -3
- package/package.json +35 -11
- package/dist/application-services.d.ts +0 -10
- package/dist/application-services.js +0 -22
- package/dist/daemon-lifecycle.d.ts +0 -54
- package/dist/daemon-lifecycle.js +0 -177
- package/dist/test/application-services.test.js +0 -135
- package/dist/test/daemon-lifecycle.test.js +0 -182
- /package/dist/{test/application-services.test.d.ts → internal-providers/provider-anthropic/test/provider.test.d.ts} +0 -0
- /package/dist/{test/daemon-lifecycle.test.d.ts → internal-providers/provider-azure/test/provider.test.d.ts} +0 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amazon Bedrock provider source. Anthropic models use Bedrock Converse;
|
|
3
|
+
* allowlisted OpenAI frontier models use the regional bedrock-mantle
|
|
4
|
+
* OpenAI-compatible API.
|
|
5
|
+
*/
|
|
6
|
+
import { BedrockClient, ListFoundationModelsCommand, ListInferenceProfilesCommand } from "@aws-sdk/client-bedrock";
|
|
7
|
+
import { BedrockRuntimeClient, ConverseCommand, ConverseStreamCommand } from "@aws-sdk/client-bedrock-runtime";
|
|
8
|
+
import { RouteKitFailure } from "@velum-labs/routekit-runtime/effect";
|
|
9
|
+
import { Effect } from "effect";
|
|
10
|
+
import { errorResponse, fromBedrockConverseOutput, isOpusFiveModel, streamResponse, toBedrockConverseInput } from "./codec.js";
|
|
11
|
+
import { OpenAiBackend } from "@velum-labs/routekit-provider-core";
|
|
12
|
+
import { gatewayTry, gatewayTryPromise } from "@velum-labs/routekit-provider-core";
|
|
13
|
+
function sdkClientConfig(env) {
|
|
14
|
+
const region = env.AWS_REGION ?? env.AWS_DEFAULT_REGION;
|
|
15
|
+
const accessKeyId = env.AWS_ACCESS_KEY_ID;
|
|
16
|
+
const secretAccessKey = env.AWS_SECRET_ACCESS_KEY;
|
|
17
|
+
if ((accessKeyId === undefined) !== (secretAccessKey === undefined)) {
|
|
18
|
+
throw new Error("AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be configured together");
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
...(region === undefined ? {} : { region }),
|
|
22
|
+
...(accessKeyId === undefined || secretAccessKey === undefined
|
|
23
|
+
? {}
|
|
24
|
+
: {
|
|
25
|
+
credentials: {
|
|
26
|
+
accessKeyId,
|
|
27
|
+
secretAccessKey,
|
|
28
|
+
...(env.AWS_SESSION_TOKEN === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: { sessionToken: env.AWS_SESSION_TOKEN })
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export { fromBedrockConverseOutput, toBedrockConverseInput };
|
|
36
|
+
export const BEDROCK_OPENAI_ALLOWLIST = [
|
|
37
|
+
"openai.gpt-5.6-sol",
|
|
38
|
+
"openai.gpt-5.6-terra",
|
|
39
|
+
"openai.gpt-5.6-luna"
|
|
40
|
+
];
|
|
41
|
+
const BEDROCK_OPENAI_MODEL = /^(?:(?:us|eu|global)\.)?openai\.gpt-/;
|
|
42
|
+
export function isBedrockOpenAiModel(modelId) {
|
|
43
|
+
return BEDROCK_OPENAI_MODEL.test(modelId);
|
|
44
|
+
}
|
|
45
|
+
function record(value) {
|
|
46
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
47
|
+
? value
|
|
48
|
+
: undefined;
|
|
49
|
+
}
|
|
50
|
+
function recognizedEncryptedPrefix(value) {
|
|
51
|
+
return typeof value === "string" && (value.startsWith("rsn_") || value.startsWith("smry_"));
|
|
52
|
+
}
|
|
53
|
+
function sanitizeBedrockMantleContent(content) {
|
|
54
|
+
if (!Array.isArray(content))
|
|
55
|
+
return { content, changed: false };
|
|
56
|
+
const parts = content.filter((part) => record(part)?.type !== "encrypted_content");
|
|
57
|
+
return { content: parts, changed: parts.length !== content.length };
|
|
58
|
+
}
|
|
59
|
+
function sanitizeBedrockMantleInputItem(item) {
|
|
60
|
+
const entry = record(item);
|
|
61
|
+
if (entry === undefined)
|
|
62
|
+
return item;
|
|
63
|
+
const type = typeof entry.type === "string" ? entry.type : "";
|
|
64
|
+
if (type === "compaction" &&
|
|
65
|
+
"encrypted_content" in entry &&
|
|
66
|
+
!recognizedEncryptedPrefix(entry.encrypted_content)) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
if (type === "agent_message") {
|
|
70
|
+
const { content } = sanitizeBedrockMantleContent(entry.content);
|
|
71
|
+
return { type: "message", role: "user", content };
|
|
72
|
+
}
|
|
73
|
+
let next = entry;
|
|
74
|
+
let changed = false;
|
|
75
|
+
if (Array.isArray(entry.content)) {
|
|
76
|
+
const sanitized = sanitizeBedrockMantleContent(entry.content);
|
|
77
|
+
if (sanitized.changed) {
|
|
78
|
+
next = { ...next, content: sanitized.content };
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (type === "reasoning" &&
|
|
83
|
+
"encrypted_content" in next &&
|
|
84
|
+
!recognizedEncryptedPrefix(next.encrypted_content)) {
|
|
85
|
+
const { encrypted_content: _ignored, ...rest } = next;
|
|
86
|
+
next = rest;
|
|
87
|
+
changed = true;
|
|
88
|
+
}
|
|
89
|
+
return changed ? next : item;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Remove OpenAI/Codex protocol fields that bedrock-mantle rejects while
|
|
93
|
+
* leaving native OpenAI egress unchanged.
|
|
94
|
+
*/
|
|
95
|
+
export function sanitizeBedrockMantleRequestBody(body) {
|
|
96
|
+
const payload = record(body);
|
|
97
|
+
if (payload === undefined)
|
|
98
|
+
return body;
|
|
99
|
+
let next = payload;
|
|
100
|
+
let changed = false;
|
|
101
|
+
if (Array.isArray(payload.tools)) {
|
|
102
|
+
const tools = payload.tools.map((tool) => {
|
|
103
|
+
const entry = record(tool);
|
|
104
|
+
if (entry === undefined)
|
|
105
|
+
return tool;
|
|
106
|
+
const type = typeof entry.type === "string" ? entry.type : "";
|
|
107
|
+
if (!type.startsWith("web_search") || !("search_content_types" in entry))
|
|
108
|
+
return tool;
|
|
109
|
+
changed = true;
|
|
110
|
+
const { search_content_types: _ignored, ...rest } = entry;
|
|
111
|
+
return rest;
|
|
112
|
+
});
|
|
113
|
+
if (changed)
|
|
114
|
+
next = { ...next, tools };
|
|
115
|
+
}
|
|
116
|
+
if (Array.isArray(payload.input)) {
|
|
117
|
+
const input = [];
|
|
118
|
+
let inputChanged = false;
|
|
119
|
+
for (const item of payload.input) {
|
|
120
|
+
const sanitized = sanitizeBedrockMantleInputItem(item);
|
|
121
|
+
if (sanitized === undefined) {
|
|
122
|
+
inputChanged = true;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (sanitized !== item)
|
|
126
|
+
inputChanged = true;
|
|
127
|
+
input.push(sanitized);
|
|
128
|
+
}
|
|
129
|
+
if (inputChanged) {
|
|
130
|
+
changed = true;
|
|
131
|
+
next = { ...next, input };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return changed ? next : body;
|
|
135
|
+
}
|
|
136
|
+
function mantleApiKey(env) {
|
|
137
|
+
const key = env.AWS_BEARER_TOKEN_BEDROCK ?? env.BEDROCK_API_KEY;
|
|
138
|
+
return typeof key === "string" && key.length > 0 ? key : undefined;
|
|
139
|
+
}
|
|
140
|
+
function mantleRegion(env) {
|
|
141
|
+
const region = env.AWS_REGION ?? env.AWS_DEFAULT_REGION;
|
|
142
|
+
return typeof region === "string" && region.length > 0 ? region : undefined;
|
|
143
|
+
}
|
|
144
|
+
function mantleBaseUrl(region) {
|
|
145
|
+
return `https://bedrock-mantle.${region}.api.aws/openai/v1`;
|
|
146
|
+
}
|
|
147
|
+
function bedrockOpenAiNativeId(modelId) {
|
|
148
|
+
return modelId.replace(/^(?:us|eu|global)\./, "");
|
|
149
|
+
}
|
|
150
|
+
function bedrockOpenAiReasoning(modelId) {
|
|
151
|
+
const native = bedrockOpenAiNativeId(modelId).replace(/^openai\./, "");
|
|
152
|
+
if (/^gpt-5\.6(?:-(?:sol|terra|luna))?(?:-\d{4}-\d{2}-\d{2})?$/.test(native)) {
|
|
153
|
+
return {
|
|
154
|
+
status: "supported",
|
|
155
|
+
efforts: ["none", "low", "medium", "high", "xhigh", "max"].map((id) => ({ id })),
|
|
156
|
+
defaultEffort: "medium",
|
|
157
|
+
wireShape: "openai-responses",
|
|
158
|
+
provenance: "builtin"
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (/^gpt-5\.(?:4|5)(?:-\d{4}-\d{2}-\d{2})?$/.test(native)) {
|
|
162
|
+
return {
|
|
163
|
+
status: "supported",
|
|
164
|
+
efforts: ["none", "low", "medium", "high", "xhigh"].map((id) => ({ id })),
|
|
165
|
+
wireShape: "openai-responses",
|
|
166
|
+
provenance: "builtin"
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
status: "supported",
|
|
171
|
+
wireShape: "openai-responses",
|
|
172
|
+
provenance: "builtin"
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function bedrockOpenAiDiscoveredModel(id) {
|
|
176
|
+
return {
|
|
177
|
+
id,
|
|
178
|
+
metadata: {
|
|
179
|
+
architecture: {
|
|
180
|
+
modality: "text+image->text",
|
|
181
|
+
inputModalities: ["text", "image"],
|
|
182
|
+
outputModalities: ["text"]
|
|
183
|
+
},
|
|
184
|
+
supportedParameters: ["tools", "tool_choice"],
|
|
185
|
+
provenance: "route"
|
|
186
|
+
},
|
|
187
|
+
reasoning: bedrockOpenAiReasoning(id),
|
|
188
|
+
capabilities: { streaming: "supported" }
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function anthropicFoundationModel(model) {
|
|
192
|
+
return (model.providerName?.toLowerCase() === "anthropic" &&
|
|
193
|
+
model.modelLifecycle?.status === "ACTIVE" &&
|
|
194
|
+
typeof model.modelId === "string" &&
|
|
195
|
+
model.modelId.length > 0);
|
|
196
|
+
}
|
|
197
|
+
function foundationIdFromArn(arn) {
|
|
198
|
+
if (arn === undefined)
|
|
199
|
+
return undefined;
|
|
200
|
+
const marker = ":foundation-model/";
|
|
201
|
+
const index = arn.indexOf(marker);
|
|
202
|
+
return index < 0 ? undefined : arn.slice(index + marker.length);
|
|
203
|
+
}
|
|
204
|
+
function activeAnthropicProfile(profile, anthropicModels) {
|
|
205
|
+
return (profile.status === "ACTIVE" &&
|
|
206
|
+
typeof profile.inferenceProfileId === "string" &&
|
|
207
|
+
profile.inferenceProfileId.length > 0 &&
|
|
208
|
+
(profile.models ?? []).some((model) => {
|
|
209
|
+
const id = foundationIdFromArn(model.modelArn);
|
|
210
|
+
return id !== undefined && anthropicModels.has(id);
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
function inferenceProfilePriority(profileId) {
|
|
214
|
+
if (profileId.startsWith("us."))
|
|
215
|
+
return 0;
|
|
216
|
+
if (profileId.startsWith("global."))
|
|
217
|
+
return 1;
|
|
218
|
+
return 2;
|
|
219
|
+
}
|
|
220
|
+
function preferredInferenceProfile(current, candidate) {
|
|
221
|
+
if (current === undefined)
|
|
222
|
+
return candidate;
|
|
223
|
+
return inferenceProfilePriority(candidate) < inferenceProfilePriority(current)
|
|
224
|
+
? candidate
|
|
225
|
+
: current;
|
|
226
|
+
}
|
|
227
|
+
function bedrockReasoningCapabilities(modelId) {
|
|
228
|
+
if (modelId === undefined || !isOpusFiveModel(modelId))
|
|
229
|
+
return undefined;
|
|
230
|
+
return {
|
|
231
|
+
status: "supported",
|
|
232
|
+
efforts: ["low", "medium", "high", "max"].map((id) => ({ id })),
|
|
233
|
+
adaptive: true,
|
|
234
|
+
wireShape: "bedrock-converse",
|
|
235
|
+
provenance: "builtin"
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function bedrockMetadata(model) {
|
|
239
|
+
const inputModalities = (model.inputModalities ?? []).map((value) => value.toLowerCase());
|
|
240
|
+
const outputModalities = (model.outputModalities ?? []).map((value) => value.toLowerCase());
|
|
241
|
+
const inputs = inputModalities.length > 0 ? inputModalities : ["text"];
|
|
242
|
+
const outputs = outputModalities.length > 0 ? outputModalities : ["text"];
|
|
243
|
+
return {
|
|
244
|
+
architecture: {
|
|
245
|
+
modality: `${inputs.join("+")}->${outputs.join("+")}`,
|
|
246
|
+
inputModalities: inputs,
|
|
247
|
+
outputModalities: outputs
|
|
248
|
+
},
|
|
249
|
+
supportedParameters: ["tools", "tool_choice"],
|
|
250
|
+
provenance: inputModalities.length > 0 || outputModalities.length > 0 ? "provider" : "route"
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function bedrockDiscoveredModel(id, model) {
|
|
254
|
+
const reasoning = bedrockReasoningCapabilities(model.modelId);
|
|
255
|
+
return {
|
|
256
|
+
id,
|
|
257
|
+
metadata: bedrockMetadata(model),
|
|
258
|
+
...(reasoning !== undefined ? { reasoning } : {}),
|
|
259
|
+
...(model.responseStreamingSupported !== undefined
|
|
260
|
+
? {
|
|
261
|
+
capabilities: {
|
|
262
|
+
streaming: model.responseStreamingSupported ? "supported" : "unsupported"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
: {})
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
export class BedrockProviderSource {
|
|
269
|
+
sourceId;
|
|
270
|
+
discovery;
|
|
271
|
+
requests;
|
|
272
|
+
responses;
|
|
273
|
+
capabilities;
|
|
274
|
+
resource;
|
|
275
|
+
#control;
|
|
276
|
+
#runtime;
|
|
277
|
+
#env;
|
|
278
|
+
#injectedMantle;
|
|
279
|
+
#mantle;
|
|
280
|
+
#inferenceProfilesByFoundation = new Map();
|
|
281
|
+
constructor(options = {}) {
|
|
282
|
+
this.sourceId = options.sourceId ?? "bedrock";
|
|
283
|
+
this.#env = options.env ?? process.env;
|
|
284
|
+
const clientConfig = {
|
|
285
|
+
...sdkClientConfig(this.#env),
|
|
286
|
+
...(options.credentials === undefined ? {} : { credentials: options.credentials })
|
|
287
|
+
};
|
|
288
|
+
this.#control = options.controlClient ?? new BedrockClient(clientConfig);
|
|
289
|
+
this.#runtime =
|
|
290
|
+
options.runtimeClient ?? new BedrockRuntimeClient(clientConfig);
|
|
291
|
+
this.#injectedMantle = options.mantleBackend;
|
|
292
|
+
this.discovery = { discoverModels: (signal) => this.#discoverModels(signal) };
|
|
293
|
+
this.requests = {
|
|
294
|
+
chat: (body, signal, requestOptions) => this.#chat(body, signal, requestOptions),
|
|
295
|
+
embeddings: () => Effect.succeed(Response.json({ error: { type: "not_implemented", message: "Bedrock embeddings are not supported" } }, { status: 501 }))
|
|
296
|
+
};
|
|
297
|
+
this.responses = {
|
|
298
|
+
kind: "responses",
|
|
299
|
+
supports: (model) => isBedrockOpenAiModel(model),
|
|
300
|
+
execute: (body, signal, requestOptions) => this.#responses(body, signal, requestOptions)
|
|
301
|
+
};
|
|
302
|
+
this.capabilities = {
|
|
303
|
+
forModel: () => ({}),
|
|
304
|
+
reasoningForModel: (model) => this.#reasoningCapabilities(model)
|
|
305
|
+
};
|
|
306
|
+
this.resource = {
|
|
307
|
+
kind: "owned",
|
|
308
|
+
close: Effect.sync(() => {
|
|
309
|
+
this.#control.destroy?.();
|
|
310
|
+
this.#runtime.destroy?.();
|
|
311
|
+
})
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
#mantleBackend() {
|
|
315
|
+
if (this.#injectedMantle !== undefined)
|
|
316
|
+
return this.#injectedMantle;
|
|
317
|
+
if (this.#mantle !== undefined)
|
|
318
|
+
return this.#mantle;
|
|
319
|
+
const apiKey = mantleApiKey(this.#env);
|
|
320
|
+
const region = mantleRegion(this.#env);
|
|
321
|
+
if (apiKey === undefined || region === undefined)
|
|
322
|
+
return undefined;
|
|
323
|
+
this.#mantle = new OpenAiBackend({
|
|
324
|
+
baseUrl: mantleBaseUrl(region),
|
|
325
|
+
apiKey
|
|
326
|
+
});
|
|
327
|
+
return this.#mantle;
|
|
328
|
+
}
|
|
329
|
+
#missingMantleResponse() {
|
|
330
|
+
return Response.json({
|
|
331
|
+
error: {
|
|
332
|
+
type: "invalid_request_error",
|
|
333
|
+
message: "Bedrock OpenAI models require AWS_BEARER_TOKEN_BEDROCK and AWS_REGION"
|
|
334
|
+
}
|
|
335
|
+
}, { status: 400 });
|
|
336
|
+
}
|
|
337
|
+
#discoverModels(signal) {
|
|
338
|
+
const self = this;
|
|
339
|
+
return Effect.gen(function* () {
|
|
340
|
+
self.#inferenceProfilesByFoundation.clear();
|
|
341
|
+
const abort = signal === undefined ? undefined : { abortSignal: signal };
|
|
342
|
+
const discovered = yield* Effect.gen(function* () {
|
|
343
|
+
const foundation = yield* gatewayTryPromise(() => self.#control.send(new ListFoundationModelsCommand({ byProvider: "Anthropic" }), abort));
|
|
344
|
+
const foundations = (foundation.modelSummaries ?? []).filter(anthropicFoundationModel);
|
|
345
|
+
const byId = new Map(foundations.map((model) => [model.modelId, model]));
|
|
346
|
+
const ids = new Set(byId.keys());
|
|
347
|
+
const anthropic = new Map(foundations.map((model) => [
|
|
348
|
+
model.modelId,
|
|
349
|
+
bedrockDiscoveredModel(model.modelId, model)
|
|
350
|
+
]));
|
|
351
|
+
let nextToken;
|
|
352
|
+
do {
|
|
353
|
+
const profiles = yield* gatewayTryPromise(() => self.#control.send(new ListInferenceProfilesCommand({
|
|
354
|
+
...(nextToken !== undefined ? { nextToken } : {})
|
|
355
|
+
}), abort));
|
|
356
|
+
for (const profile of profiles.inferenceProfileSummaries ?? []) {
|
|
357
|
+
if (!activeAnthropicProfile(profile, ids))
|
|
358
|
+
continue;
|
|
359
|
+
const backingId = (profile.models ?? [])
|
|
360
|
+
.map((model) => foundationIdFromArn(model.modelArn))
|
|
361
|
+
.find((id) => id !== undefined && ids.has(id));
|
|
362
|
+
if (backingId !== undefined) {
|
|
363
|
+
self.#inferenceProfilesByFoundation.set(backingId, preferredInferenceProfile(self.#inferenceProfilesByFoundation.get(backingId), profile.inferenceProfileId));
|
|
364
|
+
anthropic.set(profile.inferenceProfileId, bedrockDiscoveredModel(profile.inferenceProfileId, byId.get(backingId)));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
nextToken = profiles.nextToken;
|
|
368
|
+
} while (nextToken !== undefined && nextToken.length > 0);
|
|
369
|
+
return anthropic;
|
|
370
|
+
}).pipe(Effect.catch((error) => {
|
|
371
|
+
if (mantleApiKey(self.#env) === undefined)
|
|
372
|
+
return Effect.fail(error);
|
|
373
|
+
self.#inferenceProfilesByFoundation.clear();
|
|
374
|
+
return Effect.succeed(new Map());
|
|
375
|
+
}));
|
|
376
|
+
if (mantleApiKey(self.#env) !== undefined) {
|
|
377
|
+
for (const id of BEDROCK_OPENAI_ALLOWLIST) {
|
|
378
|
+
discovered.set(id, bedrockOpenAiDiscoveredModel(id));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (discovered.size === 0) {
|
|
382
|
+
return yield* new RouteKitFailure({
|
|
383
|
+
message: "model discovery returned no active Anthropic Bedrock models"
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return [...discovered.values()];
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
#responses(body, signal, options) {
|
|
390
|
+
const requestedModel = record(body)?.model;
|
|
391
|
+
const model = typeof requestedModel === "string" ? requestedModel : "";
|
|
392
|
+
if (!isBedrockOpenAiModel(model)) {
|
|
393
|
+
return Effect.succeed(Response.json({ error: { type: "not_supported", message: "native Responses egress is not supported" } }, { status: 501 }));
|
|
394
|
+
}
|
|
395
|
+
const backend = this.#mantleBackend();
|
|
396
|
+
if (backend === undefined)
|
|
397
|
+
return Effect.succeed(this.#missingMantleResponse());
|
|
398
|
+
return backend.responses(sanitizeBedrockMantleRequestBody(body), signal, options);
|
|
399
|
+
}
|
|
400
|
+
#chat(body, signal, options) {
|
|
401
|
+
const self = this;
|
|
402
|
+
return Effect.gen(function* () {
|
|
403
|
+
const requestedModel = record(body)?.model;
|
|
404
|
+
if (typeof requestedModel === "string" && isBedrockOpenAiModel(requestedModel)) {
|
|
405
|
+
const backend = self.#mantleBackend();
|
|
406
|
+
if (backend === undefined)
|
|
407
|
+
return self.#missingMantleResponse();
|
|
408
|
+
return yield* backend.chat(sanitizeBedrockMantleRequestBody(body), signal, options);
|
|
409
|
+
}
|
|
410
|
+
const input = yield* gatewayTry(() => toBedrockConverseInput(body)).pipe(Effect.catch((error) => Effect.succeed(Response.json({
|
|
411
|
+
error: {
|
|
412
|
+
type: "invalid_request_error",
|
|
413
|
+
message: error instanceof Error ? error.message : String(error)
|
|
414
|
+
}
|
|
415
|
+
}, { status: 400 }))));
|
|
416
|
+
if (input instanceof Response)
|
|
417
|
+
return input;
|
|
418
|
+
const stream = record(body)?.stream === true;
|
|
419
|
+
const modelId = input.modelId;
|
|
420
|
+
if (modelId === undefined)
|
|
421
|
+
return errorResponse("Bedrock chat requires a model");
|
|
422
|
+
const runtimeModelId = modelId === "anthropic.claude-opus-5"
|
|
423
|
+
? (self.#inferenceProfilesByFoundation.get(modelId) ?? modelId)
|
|
424
|
+
: modelId;
|
|
425
|
+
const runtimeInput = runtimeModelId === modelId ? input : { ...input, modelId: runtimeModelId };
|
|
426
|
+
const abort = signal === undefined ? undefined : { abortSignal: signal };
|
|
427
|
+
if (stream) {
|
|
428
|
+
const output = yield* gatewayTryPromise(() => self.#runtime.send(new ConverseStreamCommand(runtimeInput), abort)).pipe(Effect.catch((error) => Effect.succeed(errorResponse(error))));
|
|
429
|
+
if (output instanceof Response)
|
|
430
|
+
return output;
|
|
431
|
+
if (output.stream === undefined)
|
|
432
|
+
return errorResponse("Bedrock returned no response stream");
|
|
433
|
+
return streamResponse(output.stream, modelId, signal);
|
|
434
|
+
}
|
|
435
|
+
const output = yield* gatewayTryPromise(() => self.#runtime.send(new ConverseCommand(runtimeInput), abort)).pipe(Effect.catch((error) => Effect.succeed(errorResponse(error))));
|
|
436
|
+
if (output instanceof Response)
|
|
437
|
+
return output;
|
|
438
|
+
return Response.json(fromBedrockConverseOutput(output, modelId));
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
#reasoningCapabilities(model) {
|
|
442
|
+
if (model !== undefined && isBedrockOpenAiModel(model)) {
|
|
443
|
+
return bedrockOpenAiReasoning(model);
|
|
444
|
+
}
|
|
445
|
+
const known = bedrockReasoningCapabilities(model);
|
|
446
|
+
if (known !== undefined)
|
|
447
|
+
return known;
|
|
448
|
+
return {
|
|
449
|
+
status: "unknown",
|
|
450
|
+
wireShape: "bedrock-converse",
|
|
451
|
+
provenance: "provider"
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|