@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,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bedrock Converse codec. Pure request/response translation between OpenAI
|
|
3
|
+
* Chat Completions JSON and Amazon Bedrock Converse, including streaming SSE.
|
|
4
|
+
* Model discovery and transport live in `bedrock-source.ts`.
|
|
5
|
+
*/
|
|
6
|
+
import { randomId } from "@velum-labs/routekit-runtime/timing";
|
|
7
|
+
import { anthropicReasoningDetailsOf, anthropicReasoningExtension, reasoningIndex, reasoningSelectionOf, withoutRouteKitExtensions } from "@velum-labs/routekit-provider-core";
|
|
8
|
+
function record(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
10
|
+
? value
|
|
11
|
+
: undefined;
|
|
12
|
+
}
|
|
13
|
+
export function isOpusFiveModel(modelId) {
|
|
14
|
+
return /(?:^|\.)anthropic\.claude-opus-5$/.test(modelId);
|
|
15
|
+
}
|
|
16
|
+
function textParts(content) {
|
|
17
|
+
if (typeof content === "string")
|
|
18
|
+
return content;
|
|
19
|
+
if (!Array.isArray(content))
|
|
20
|
+
return "";
|
|
21
|
+
return content
|
|
22
|
+
.flatMap((part) => {
|
|
23
|
+
const item = record(part);
|
|
24
|
+
return typeof item?.text === "string" ? [item.text] : [];
|
|
25
|
+
})
|
|
26
|
+
.join("");
|
|
27
|
+
}
|
|
28
|
+
function imageBlock(part) {
|
|
29
|
+
const imageUrl = typeof part.image_url === "string" ? part.image_url : record(part.image_url)?.url;
|
|
30
|
+
if (typeof imageUrl !== "string")
|
|
31
|
+
return undefined;
|
|
32
|
+
const match = /^data:image\/(png|jpeg|gif|webp);base64,(.+)$/s.exec(imageUrl);
|
|
33
|
+
if (match === null)
|
|
34
|
+
return undefined;
|
|
35
|
+
return {
|
|
36
|
+
image: {
|
|
37
|
+
format: match[1],
|
|
38
|
+
source: { bytes: Uint8Array.from(Buffer.from(match[2], "base64")) }
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function bedrockReasoningBlocks(details) {
|
|
43
|
+
return anthropicReasoningDetailsOf(details, "message")
|
|
44
|
+
.sort((left, right) => reasoningIndex(left) - reasoningIndex(right))
|
|
45
|
+
.map((detail) => {
|
|
46
|
+
const metadata = anthropicReasoningExtension(detail);
|
|
47
|
+
return metadata?.redacted === true
|
|
48
|
+
? {
|
|
49
|
+
reasoningContent: {
|
|
50
|
+
redactedContent: Uint8Array.from(Buffer.from(detail.encryptedContent ?? "", "base64"))
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
: {
|
|
54
|
+
reasoningContent: {
|
|
55
|
+
reasoningText: {
|
|
56
|
+
text: detail.text ?? "",
|
|
57
|
+
...(metadata?.signature !== undefined ? { signature: metadata.signature } : {})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function messageContent(message) {
|
|
64
|
+
const blocks = message.role === "assistant" ? bedrockReasoningBlocks(message.reasoning_details) : [];
|
|
65
|
+
if (typeof message.content === "string") {
|
|
66
|
+
if (message.content.length > 0)
|
|
67
|
+
blocks.push({ text: message.content });
|
|
68
|
+
}
|
|
69
|
+
else if (Array.isArray(message.content)) {
|
|
70
|
+
for (const value of message.content) {
|
|
71
|
+
const part = record(value);
|
|
72
|
+
if (part === undefined)
|
|
73
|
+
continue;
|
|
74
|
+
if (part.type === "image_url") {
|
|
75
|
+
const image = imageBlock(part);
|
|
76
|
+
if (image !== undefined)
|
|
77
|
+
blocks.push(image);
|
|
78
|
+
}
|
|
79
|
+
else if (typeof part.text === "string" && part.text.length > 0) {
|
|
80
|
+
blocks.push({ text: part.text });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const call of message.tool_calls ?? []) {
|
|
85
|
+
let input = {};
|
|
86
|
+
try {
|
|
87
|
+
input = JSON.parse(call.function?.arguments ?? "{}");
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
input = { raw: call.function?.arguments ?? "" };
|
|
91
|
+
}
|
|
92
|
+
blocks.push({
|
|
93
|
+
toolUse: {
|
|
94
|
+
toolUseId: call.id ?? randomId(12, "toolu_"),
|
|
95
|
+
name: call.function?.name ?? "tool",
|
|
96
|
+
input: input
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return blocks;
|
|
101
|
+
}
|
|
102
|
+
function bedrockMessages(body) {
|
|
103
|
+
const systemText = (body.messages ?? [])
|
|
104
|
+
.filter((message) => message.role === "system")
|
|
105
|
+
.map((message) => textParts(message.content))
|
|
106
|
+
.filter((text) => text.length > 0);
|
|
107
|
+
const messages = [];
|
|
108
|
+
const sourceMessages = body.messages ?? [];
|
|
109
|
+
for (let index = 0; index < sourceMessages.length; index += 1) {
|
|
110
|
+
const message = sourceMessages[index];
|
|
111
|
+
if (message.role === "system")
|
|
112
|
+
continue;
|
|
113
|
+
if (message.role === "tool") {
|
|
114
|
+
const content = [];
|
|
115
|
+
let cursor = index;
|
|
116
|
+
while (cursor < sourceMessages.length && sourceMessages[cursor]?.role === "tool") {
|
|
117
|
+
const toolMessage = sourceMessages[cursor];
|
|
118
|
+
content.push({
|
|
119
|
+
toolResult: {
|
|
120
|
+
toolUseId: toolMessage.tool_call_id ?? "",
|
|
121
|
+
content: [{ text: textParts(toolMessage.content) }]
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
cursor += 1;
|
|
125
|
+
}
|
|
126
|
+
messages.push({ role: "user", content });
|
|
127
|
+
index = cursor - 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (message.role !== "user" && message.role !== "assistant")
|
|
131
|
+
continue;
|
|
132
|
+
const content = messageContent(message);
|
|
133
|
+
if (content.length > 0)
|
|
134
|
+
messages.push({ role: message.role, content });
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
...(systemText.length > 0 ? { system: systemText.map((text) => ({ text })) } : {}),
|
|
138
|
+
messages
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function bedrockTools(body) {
|
|
142
|
+
const tools = (body.tools ?? []).flatMap((tool) => {
|
|
143
|
+
const fn = tool.function;
|
|
144
|
+
if (tool.type !== "function" || typeof fn?.name !== "string")
|
|
145
|
+
return [];
|
|
146
|
+
return [
|
|
147
|
+
{
|
|
148
|
+
toolSpec: {
|
|
149
|
+
name: fn.name,
|
|
150
|
+
...(typeof fn.description === "string" ? { description: fn.description } : {}),
|
|
151
|
+
inputSchema: { json: (record(fn.parameters) ?? {}) }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
];
|
|
155
|
+
});
|
|
156
|
+
return tools.length > 0 ? tools : undefined;
|
|
157
|
+
}
|
|
158
|
+
function toolChoice(value) {
|
|
159
|
+
if (value === "required")
|
|
160
|
+
return { any: {} };
|
|
161
|
+
const name = record(record(value)?.function)?.name;
|
|
162
|
+
if (typeof name === "string")
|
|
163
|
+
return { tool: { name } };
|
|
164
|
+
return { auto: {} };
|
|
165
|
+
}
|
|
166
|
+
export function toBedrockConverseInput(body) {
|
|
167
|
+
const canonical = withoutRouteKitExtensions(body);
|
|
168
|
+
if (typeof canonical.model !== "string" || canonical.model.length === 0)
|
|
169
|
+
throw new Error("Bedrock chat requires a model");
|
|
170
|
+
const translated = bedrockMessages(canonical);
|
|
171
|
+
const tools = canonical.tool_choice === "none" ? undefined : bedrockTools(canonical);
|
|
172
|
+
const maxTokens = typeof canonical.max_completion_tokens === "number"
|
|
173
|
+
? canonical.max_completion_tokens
|
|
174
|
+
: canonical.max_tokens;
|
|
175
|
+
const stops = typeof canonical.stop === "string"
|
|
176
|
+
? [canonical.stop]
|
|
177
|
+
: Array.isArray(canonical.stop)
|
|
178
|
+
? canonical.stop.filter((value) => typeof value === "string")
|
|
179
|
+
: undefined;
|
|
180
|
+
const selection = reasoningSelectionOf(body);
|
|
181
|
+
const thinking = selection.mode === "budget"
|
|
182
|
+
? { thinking: { type: "enabled", budget_tokens: selection.budgetTokens } }
|
|
183
|
+
: isOpusFiveModel(canonical.model)
|
|
184
|
+
? selection.mode === "effort"
|
|
185
|
+
? { thinking: { type: "adaptive" }, output_config: { effort: selection.effort } }
|
|
186
|
+
: selection.mode === "adaptive"
|
|
187
|
+
? { thinking: { type: "adaptive" } }
|
|
188
|
+
: selection.mode === "disabled"
|
|
189
|
+
? { thinking: { type: "disabled" } }
|
|
190
|
+
: undefined
|
|
191
|
+
: undefined;
|
|
192
|
+
const additionalModelRequestFields = thinking !== undefined || typeof canonical.top_k === "number"
|
|
193
|
+
? {
|
|
194
|
+
...thinking,
|
|
195
|
+
...(typeof canonical.top_k === "number" ? { top_k: canonical.top_k } : {})
|
|
196
|
+
}
|
|
197
|
+
: undefined;
|
|
198
|
+
return {
|
|
199
|
+
modelId: canonical.model,
|
|
200
|
+
...translated,
|
|
201
|
+
...(tools !== undefined
|
|
202
|
+
? { toolConfig: { tools, toolChoice: toolChoice(canonical.tool_choice) } }
|
|
203
|
+
: {}),
|
|
204
|
+
...(typeof maxTokens === "number" ||
|
|
205
|
+
typeof canonical.temperature === "number" ||
|
|
206
|
+
typeof canonical.top_p === "number" ||
|
|
207
|
+
stops !== undefined
|
|
208
|
+
? {
|
|
209
|
+
inferenceConfig: {
|
|
210
|
+
...(typeof maxTokens === "number" ? { maxTokens } : {}),
|
|
211
|
+
...(typeof canonical.temperature === "number"
|
|
212
|
+
? { temperature: canonical.temperature }
|
|
213
|
+
: {}),
|
|
214
|
+
...(typeof canonical.top_p === "number" ? { topP: canonical.top_p } : {}),
|
|
215
|
+
...(stops !== undefined ? { stopSequences: stops } : {})
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
: {}),
|
|
219
|
+
...(additionalModelRequestFields !== undefined ? { additionalModelRequestFields } : {})
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function finishReason(reason) {
|
|
223
|
+
switch (reason) {
|
|
224
|
+
case "max_tokens":
|
|
225
|
+
return "length";
|
|
226
|
+
case "tool_use":
|
|
227
|
+
return "tool_calls";
|
|
228
|
+
case "content_filtered":
|
|
229
|
+
case "guardrail_intervened":
|
|
230
|
+
return "content_filter";
|
|
231
|
+
default:
|
|
232
|
+
return "stop";
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function usage(value) {
|
|
236
|
+
if (value === undefined)
|
|
237
|
+
return undefined;
|
|
238
|
+
const prompt = value.inputTokens ?? 0;
|
|
239
|
+
const completion = value.outputTokens ?? 0;
|
|
240
|
+
return {
|
|
241
|
+
prompt_tokens: prompt,
|
|
242
|
+
completion_tokens: completion,
|
|
243
|
+
total_tokens: value.totalTokens ?? prompt + completion
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function outputMessage(output) {
|
|
247
|
+
const content = output.output?.message?.content ?? [];
|
|
248
|
+
let text = "";
|
|
249
|
+
let reasoning = "";
|
|
250
|
+
const reasoningDetails = [];
|
|
251
|
+
const toolCalls = [];
|
|
252
|
+
for (const [index, block] of content.entries()) {
|
|
253
|
+
if ("text" in block && typeof block.text === "string")
|
|
254
|
+
text += block.text;
|
|
255
|
+
if ("toolUse" in block && block.toolUse !== undefined)
|
|
256
|
+
toolCalls.push({
|
|
257
|
+
id: block.toolUse.toolUseId,
|
|
258
|
+
type: "function",
|
|
259
|
+
function: { name: block.toolUse.name, arguments: JSON.stringify(block.toolUse.input ?? {}) }
|
|
260
|
+
});
|
|
261
|
+
if ("reasoningContent" in block && block.reasoningContent !== undefined) {
|
|
262
|
+
if ("reasoningText" in block.reasoningContent &&
|
|
263
|
+
block.reasoningContent.reasoningText !== undefined) {
|
|
264
|
+
const reasoningText = block.reasoningContent.reasoningText;
|
|
265
|
+
reasoning += reasoningText.text ?? "";
|
|
266
|
+
reasoningDetails.push({
|
|
267
|
+
text: reasoningText.text ?? "",
|
|
268
|
+
extensions: [
|
|
269
|
+
{
|
|
270
|
+
namespace: "anthropic.reasoning",
|
|
271
|
+
value: { index, signature: reasoningText.signature ?? "" }
|
|
272
|
+
}
|
|
273
|
+
]
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
else if ("redactedContent" in block.reasoningContent &&
|
|
277
|
+
block.reasoningContent.redactedContent !== undefined) {
|
|
278
|
+
reasoningDetails.push({
|
|
279
|
+
encryptedContent: Buffer.from(block.reasoningContent.redactedContent).toString("base64"),
|
|
280
|
+
extensions: [
|
|
281
|
+
{
|
|
282
|
+
namespace: "anthropic.reasoning",
|
|
283
|
+
value: { index, redacted: true }
|
|
284
|
+
}
|
|
285
|
+
]
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
role: "assistant",
|
|
292
|
+
content: text,
|
|
293
|
+
...(reasoning.length > 0 ? { reasoning } : {}),
|
|
294
|
+
...(reasoningDetails.length > 0 ? { reasoning_details: reasoningDetails } : {}),
|
|
295
|
+
...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {})
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
export function fromBedrockConverseOutput(output, model) {
|
|
299
|
+
return {
|
|
300
|
+
id: output.$metadata.requestId ?? randomId(18, "chatcmpl_"),
|
|
301
|
+
object: "chat.completion",
|
|
302
|
+
created: Math.floor(Date.now() / 1000),
|
|
303
|
+
model,
|
|
304
|
+
choices: [
|
|
305
|
+
{ index: 0, message: outputMessage(output), finish_reason: finishReason(output.stopReason) }
|
|
306
|
+
],
|
|
307
|
+
...(usage(output.usage) !== undefined ? { usage: usage(output.usage) } : {})
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function streamError(event) {
|
|
311
|
+
for (const key of [
|
|
312
|
+
"internalServerException",
|
|
313
|
+
"modelStreamErrorException",
|
|
314
|
+
"validationException",
|
|
315
|
+
"throttlingException",
|
|
316
|
+
"serviceUnavailableException"
|
|
317
|
+
]) {
|
|
318
|
+
const value = event[key];
|
|
319
|
+
if (value !== undefined)
|
|
320
|
+
return new Error(value.message ?? `Bedrock stream ${key}`);
|
|
321
|
+
}
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
function sse(data) {
|
|
325
|
+
return new TextEncoder().encode(`data: ${typeof data === "string" ? data : JSON.stringify(data)}\n\n`);
|
|
326
|
+
}
|
|
327
|
+
export function streamResponse(stream, model, signal) {
|
|
328
|
+
const id = randomId(18, "chatcmpl_");
|
|
329
|
+
const created = Math.floor(Date.now() / 1000);
|
|
330
|
+
const chunk = (delta, finishReasonValue = null, extra = {}) => ({
|
|
331
|
+
id,
|
|
332
|
+
object: "chat.completion.chunk",
|
|
333
|
+
created,
|
|
334
|
+
model,
|
|
335
|
+
choices: [{ index: 0, delta, finish_reason: finishReasonValue }],
|
|
336
|
+
...extra
|
|
337
|
+
});
|
|
338
|
+
const body = new ReadableStream({
|
|
339
|
+
async start(controller) {
|
|
340
|
+
const indexes = new Map();
|
|
341
|
+
const reasoningIndexes = new Set();
|
|
342
|
+
let nextTool = 0;
|
|
343
|
+
try {
|
|
344
|
+
controller.enqueue(sse(chunk({ role: "assistant" })));
|
|
345
|
+
for await (const event of stream) {
|
|
346
|
+
if (signal?.aborted === true)
|
|
347
|
+
throw signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
348
|
+
const error = streamError(event);
|
|
349
|
+
if (error !== undefined)
|
|
350
|
+
throw error;
|
|
351
|
+
if (event.contentBlockStart?.start?.toolUse !== undefined) {
|
|
352
|
+
const sourceIndex = event.contentBlockStart.contentBlockIndex;
|
|
353
|
+
const index = nextTool++;
|
|
354
|
+
if (sourceIndex !== undefined)
|
|
355
|
+
indexes.set(sourceIndex, index);
|
|
356
|
+
const tool = event.contentBlockStart.start.toolUse;
|
|
357
|
+
controller.enqueue(sse(chunk({
|
|
358
|
+
tool_calls: [
|
|
359
|
+
{
|
|
360
|
+
index,
|
|
361
|
+
id: tool.toolUseId,
|
|
362
|
+
type: "function",
|
|
363
|
+
function: { name: tool.name, arguments: "" }
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
})));
|
|
367
|
+
}
|
|
368
|
+
const delta = event.contentBlockDelta?.delta;
|
|
369
|
+
if (delta !== undefined && "text" in delta && typeof delta.text === "string")
|
|
370
|
+
controller.enqueue(sse(chunk({ content: delta.text })));
|
|
371
|
+
if (delta !== undefined && "toolUse" in delta && delta.toolUse !== undefined) {
|
|
372
|
+
const sourceIndex = event.contentBlockDelta?.contentBlockIndex;
|
|
373
|
+
const index = sourceIndex === undefined ? 0 : (indexes.get(sourceIndex) ?? 0);
|
|
374
|
+
controller.enqueue(sse(chunk({
|
|
375
|
+
tool_calls: [{ index, function: { arguments: delta.toolUse.input ?? "" } }]
|
|
376
|
+
})));
|
|
377
|
+
}
|
|
378
|
+
if (delta !== undefined &&
|
|
379
|
+
"reasoningContent" in delta &&
|
|
380
|
+
delta.reasoningContent !== undefined) {
|
|
381
|
+
const reasoningIndex = event.contentBlockDelta?.contentBlockIndex ?? 0;
|
|
382
|
+
const isThinkingDelta = ("text" in delta.reasoningContent &&
|
|
383
|
+
typeof delta.reasoningContent.text === "string") ||
|
|
384
|
+
("signature" in delta.reasoningContent &&
|
|
385
|
+
typeof delta.reasoningContent.signature === "string");
|
|
386
|
+
if (isThinkingDelta && !reasoningIndexes.has(reasoningIndex)) {
|
|
387
|
+
reasoningIndexes.add(reasoningIndex);
|
|
388
|
+
controller.enqueue(sse(chunk({
|
|
389
|
+
reasoning_details: [
|
|
390
|
+
{
|
|
391
|
+
type: "thinking",
|
|
392
|
+
index: reasoningIndex,
|
|
393
|
+
phase: "start"
|
|
394
|
+
}
|
|
395
|
+
]
|
|
396
|
+
})));
|
|
397
|
+
}
|
|
398
|
+
if ("text" in delta.reasoningContent &&
|
|
399
|
+
typeof delta.reasoningContent.text === "string") {
|
|
400
|
+
controller.enqueue(sse(chunk({
|
|
401
|
+
reasoning: delta.reasoningContent.text,
|
|
402
|
+
reasoning_details: [
|
|
403
|
+
{
|
|
404
|
+
type: "thinking",
|
|
405
|
+
index: reasoningIndex,
|
|
406
|
+
phase: "delta",
|
|
407
|
+
thinking: delta.reasoningContent.text
|
|
408
|
+
}
|
|
409
|
+
]
|
|
410
|
+
})));
|
|
411
|
+
}
|
|
412
|
+
else if ("signature" in delta.reasoningContent &&
|
|
413
|
+
typeof delta.reasoningContent.signature === "string") {
|
|
414
|
+
controller.enqueue(sse(chunk({
|
|
415
|
+
reasoning_details: [
|
|
416
|
+
{
|
|
417
|
+
type: "thinking",
|
|
418
|
+
index: reasoningIndex,
|
|
419
|
+
phase: "signature",
|
|
420
|
+
signature: delta.reasoningContent.signature
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
})));
|
|
424
|
+
}
|
|
425
|
+
else if ("redactedContent" in delta.reasoningContent &&
|
|
426
|
+
delta.reasoningContent.redactedContent !== undefined) {
|
|
427
|
+
controller.enqueue(sse(chunk({
|
|
428
|
+
reasoning_details: [
|
|
429
|
+
{
|
|
430
|
+
type: "redacted_thinking",
|
|
431
|
+
index: reasoningIndex,
|
|
432
|
+
phase: "block",
|
|
433
|
+
data: Buffer.from(delta.reasoningContent.redactedContent).toString("base64")
|
|
434
|
+
}
|
|
435
|
+
]
|
|
436
|
+
})));
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
const stoppedIndex = event.contentBlockStop?.contentBlockIndex;
|
|
440
|
+
if (stoppedIndex !== undefined && reasoningIndexes.has(stoppedIndex)) {
|
|
441
|
+
controller.enqueue(sse(chunk({
|
|
442
|
+
reasoning_details: [
|
|
443
|
+
{
|
|
444
|
+
type: "thinking",
|
|
445
|
+
index: stoppedIndex,
|
|
446
|
+
phase: "stop"
|
|
447
|
+
}
|
|
448
|
+
]
|
|
449
|
+
})));
|
|
450
|
+
}
|
|
451
|
+
if (event.messageStop !== undefined)
|
|
452
|
+
controller.enqueue(sse(chunk({}, finishReason(event.messageStop.stopReason))));
|
|
453
|
+
if (event.metadata?.usage !== undefined)
|
|
454
|
+
controller.enqueue(sse(chunk({}, null, { usage: usage(event.metadata.usage) })));
|
|
455
|
+
}
|
|
456
|
+
controller.enqueue(sse("[DONE]"));
|
|
457
|
+
controller.close();
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
if (signal?.aborted !== true) {
|
|
461
|
+
controller.enqueue(sse({
|
|
462
|
+
error: {
|
|
463
|
+
type: "provider_error",
|
|
464
|
+
message: error instanceof Error ? error.message : String(error)
|
|
465
|
+
}
|
|
466
|
+
}));
|
|
467
|
+
controller.enqueue(sse("[DONE]"));
|
|
468
|
+
}
|
|
469
|
+
controller.close();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
return new Response(body, { headers: { "content-type": "text/event-stream; charset=utf-8" } });
|
|
474
|
+
}
|
|
475
|
+
export function errorResponse(error) {
|
|
476
|
+
const metadata = record(record(error)?.$metadata);
|
|
477
|
+
const status = typeof metadata?.httpStatusCode === "number" ? metadata.httpStatusCode : 502;
|
|
478
|
+
return Response.json({
|
|
479
|
+
error: {
|
|
480
|
+
type: "provider_error",
|
|
481
|
+
message: error instanceof Error ? error.message : String(error)
|
|
482
|
+
}
|
|
483
|
+
}, { status });
|
|
484
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accept only the HTTPS S3 object URL forms documented for CloudFormation
|
|
3
|
+
* Quick Create. Website, accelerate, service-lookalike, bucket-root, signed,
|
|
4
|
+
* and credential-bearing URLs are intentionally rejected.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isBedrockRoleTemplateUrl(value: string): boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const awsRegionPattern = "[a-z]{2}(?:-[a-z]+)+-\\d";
|
|
2
|
+
const bucketPattern = "[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])";
|
|
3
|
+
const bucketNamePattern = new RegExp(`^${bucketPattern}$`, "u");
|
|
4
|
+
const virtualHostedS3Pattern = new RegExp(`^${bucketPattern}\\.s3\\.${awsRegionPattern}\\.amazonaws\\.com$`, "u");
|
|
5
|
+
const pathStyleS3Pattern = new RegExp(`^s3[.-]${awsRegionPattern}\\.amazonaws\\.com$`, "u");
|
|
6
|
+
function hasTemplateObjectPath(url, pathStyle) {
|
|
7
|
+
const pathSegments = url.pathname.split("/").filter((segment) => segment !== "");
|
|
8
|
+
if (!pathStyle)
|
|
9
|
+
return pathSegments.length >= 1;
|
|
10
|
+
return (pathSegments.length >= 2 &&
|
|
11
|
+
bucketNamePattern.test(pathSegments[0] ?? ""));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Accept only the HTTPS S3 object URL forms documented for CloudFormation
|
|
15
|
+
* Quick Create. Website, accelerate, service-lookalike, bucket-root, signed,
|
|
16
|
+
* and credential-bearing URLs are intentionally rejected.
|
|
17
|
+
*/
|
|
18
|
+
export function isBedrockRoleTemplateUrl(value) {
|
|
19
|
+
try {
|
|
20
|
+
const authority = /^https:\/\/([^/?#]+)(?:[/?#]|$)/iu.exec(value)?.[1];
|
|
21
|
+
if (authority === undefined || authority.includes(":") || authority.includes("@")) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const url = new URL(value);
|
|
25
|
+
if (url.protocol !== "https:" ||
|
|
26
|
+
url.port !== "" ||
|
|
27
|
+
url.username !== "" ||
|
|
28
|
+
url.password !== "" ||
|
|
29
|
+
url.search !== "" ||
|
|
30
|
+
url.hash !== "") {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const hostname = url.hostname.toLowerCase();
|
|
34
|
+
const pathStyle = pathStyleS3Pattern.test(hostname);
|
|
35
|
+
return ((pathStyle || virtualHostedS3Pattern.test(hostname)) &&
|
|
36
|
+
hasTemplateObjectPath(url, pathStyle));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fromNodeProviderChain, fromTemporaryCredentials } from "@aws-sdk/credential-providers";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { type ProviderCredentialAdapter, type ProviderEnrollmentAdapter } from "@velum-labs/routekit-provider-core";
|
|
4
|
+
export declare const bedrockRoleEnrollment: ProviderEnrollmentAdapter;
|
|
5
|
+
type CredentialDependencies = Readonly<{
|
|
6
|
+
nodeProviderChain: typeof fromNodeProviderChain;
|
|
7
|
+
temporaryCredentials: typeof fromTemporaryCredentials;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function makeBedrockRoleCredentials(dependencies?: CredentialDependencies): ProviderCredentialAdapter;
|
|
10
|
+
export declare const bedrockRoleCredentials: Readonly<{
|
|
11
|
+
resolve: (binding: import("@velum-labs/routekit-provider-core").ProviderCredentialBinding, context: Readonly<{
|
|
12
|
+
sourceId: string;
|
|
13
|
+
credentials?: import("@velum-labs/routekit-provider-core").ProviderCredentialAccess;
|
|
14
|
+
resources?: Readonly<Record<string, unknown>>;
|
|
15
|
+
}>) => Effect.Effect<import("@velum-labs/routekit-provider-core").ProviderCredentialRuntime, Error, import("@velum-labs/routekit-runtime/effect").RouteKitPlatform>;
|
|
16
|
+
}>;
|
|
17
|
+
export {};
|