opencode-crs-bedrock 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +13 -4
- package/index.ts +9 -40
- package/package.json +6 -4
- package/dist/index.js +0 -547
package/bun.lock
CHANGED
|
@@ -5,15 +5,24 @@
|
|
|
5
5
|
"": {
|
|
6
6
|
"name": "opencode-crs-bedrock",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@opencode-ai/plugin": "
|
|
8
|
+
"@opencode-ai/plugin": "^0.4.45",
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/bun": "latest",
|
|
9
12
|
},
|
|
10
13
|
},
|
|
11
14
|
},
|
|
12
15
|
"packages": {
|
|
13
|
-
"@opencode-ai/plugin": ["@opencode-ai/plugin@
|
|
16
|
+
"@opencode-ai/plugin": ["@opencode-ai/plugin@0.4.45", "", { "dependencies": { "@opencode-ai/sdk": "0.4.19" } }, "sha512-TuD+FNmA6vN+/B82qayCvOyTXRuAtfvU0U95UKSZoNrYgIBhpD/sW/oS65iUv5QwQqzO8BxI4DYWjmLIqCz8uw=="],
|
|
17
|
+
|
|
18
|
+
"@opencode-ai/sdk": ["@opencode-ai/sdk@0.4.19", "", {}, "sha512-7V+wDR1+m+TQZAraAh/bOSObiA/uysG1YIXZVe6gl1sQAXDtkG2FYCzs0gTZ/ORdkUKEnr3vyQIk895Mu0CC/w=="],
|
|
19
|
+
|
|
20
|
+
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
|
|
21
|
+
|
|
22
|
+
"@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="],
|
|
14
23
|
|
|
15
|
-
"
|
|
24
|
+
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
|
|
16
25
|
|
|
17
|
-
"
|
|
26
|
+
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
|
18
27
|
}
|
|
19
28
|
}
|
package/index.ts
CHANGED
|
@@ -967,25 +967,18 @@ async function transformJsonResponse(response: Response): Promise<Response> {
|
|
|
967
967
|
}
|
|
968
968
|
}
|
|
969
969
|
|
|
970
|
-
type GetAuthFn = () => Promise<{ key: string } | null>;
|
|
971
|
-
|
|
972
970
|
/**
|
|
973
971
|
* Creates a custom fetch function that handles CRS-specific transformations
|
|
974
972
|
*/
|
|
975
973
|
function createCRSFetch(
|
|
976
|
-
|
|
974
|
+
authKey: string
|
|
977
975
|
): typeof fetch {
|
|
978
976
|
return async function crsFetch(
|
|
979
977
|
input: RequestInfo | URL,
|
|
980
978
|
init?: RequestInit
|
|
981
979
|
): Promise<Response> {
|
|
982
|
-
const auth = await getAuth();
|
|
983
|
-
if (!auth?.key) {
|
|
984
|
-
return fetch(input, init);
|
|
985
|
-
}
|
|
986
|
-
|
|
987
980
|
const url = normalizeApiUrl(input);
|
|
988
|
-
const headers = buildRequestHeaders(input, init,
|
|
981
|
+
const headers = buildRequestHeaders(input, init, authKey);
|
|
989
982
|
const tools = extractToolsFromBody(init?.body);
|
|
990
983
|
|
|
991
984
|
const response = await fetch(url, { ...init, headers });
|
|
@@ -1016,45 +1009,21 @@ export const CRSAuthPlugin: Plugin = async () => {
|
|
|
1016
1009
|
auth: {
|
|
1017
1010
|
provider: PROVIDER_ID,
|
|
1018
1011
|
|
|
1012
|
+
methods: [],
|
|
1013
|
+
|
|
1019
1014
|
async loader(
|
|
1020
|
-
|
|
1015
|
+
_getAuth: () => Promise<Auth>,
|
|
1021
1016
|
provider: Provider
|
|
1022
1017
|
) {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
baseURL = baseURL.replace(/\/?$/, "/v1/");
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
let apiKey = provider?.options?.apiKey as string | undefined;
|
|
1029
|
-
if (!apiKey) {
|
|
1030
|
-
const auth = await getAuth();
|
|
1031
|
-
apiKey = auth?.type === "api" ? auth.key : undefined;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
if (!apiKey) {
|
|
1035
|
-
return {};
|
|
1036
|
-
}
|
|
1018
|
+
const baseURL = provider?.options?.baseURL as string | undefined;
|
|
1019
|
+
const apiKey = provider?.options?.apiKey as string | undefined;
|
|
1037
1020
|
|
|
1038
1021
|
return {
|
|
1039
|
-
apiKey
|
|
1022
|
+
apiKey,
|
|
1040
1023
|
baseURL,
|
|
1041
|
-
fetch: createCRSFetch(
|
|
1024
|
+
fetch: createCRSFetch(apiKey!),
|
|
1042
1025
|
};
|
|
1043
1026
|
},
|
|
1044
|
-
|
|
1045
|
-
methods: [
|
|
1046
|
-
{
|
|
1047
|
-
label: "Enter CRS API Key",
|
|
1048
|
-
type: "api" as const,
|
|
1049
|
-
prompts: [
|
|
1050
|
-
{
|
|
1051
|
-
type: "text" as const,
|
|
1052
|
-
message: "Enter your CRS API Key (cr_...)",
|
|
1053
|
-
key: "apiKey",
|
|
1054
|
-
},
|
|
1055
|
-
],
|
|
1056
|
-
},
|
|
1057
|
-
],
|
|
1058
1027
|
},
|
|
1059
1028
|
};
|
|
1060
1029
|
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-crs-bedrock",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "OpenCode plugin for FeedMob CRS proxy to AWS Bedrock Anthropic models",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"
|
|
6
|
+
"module": "index.ts",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"opencode",
|
|
9
9
|
"plugin",
|
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
"claude",
|
|
13
13
|
"anthropic"
|
|
14
14
|
],
|
|
15
|
-
"license": "MIT",
|
|
16
15
|
"dependencies": {
|
|
17
|
-
"@opencode-ai/plugin": "
|
|
16
|
+
"@opencode-ai/plugin": "^0.4.45"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/bun": "latest"
|
|
18
20
|
}
|
|
19
21
|
}
|
package/dist/index.js
DELETED
|
@@ -1,547 +0,0 @@
|
|
|
1
|
-
// index.ts
|
|
2
|
-
var PROVIDER_ID = "crs";
|
|
3
|
-
var VALID_SSE_EVENT_TYPES = new Set([
|
|
4
|
-
"message_start",
|
|
5
|
-
"content_block_start",
|
|
6
|
-
"content_block_delta",
|
|
7
|
-
"content_block_stop",
|
|
8
|
-
"message_delta",
|
|
9
|
-
"message_stop",
|
|
10
|
-
"ping",
|
|
11
|
-
"error"
|
|
12
|
-
]);
|
|
13
|
-
var DEBUG_SSE = process.env.CRS_DEBUG_SSE === "true";
|
|
14
|
-
function debugLog(...args) {
|
|
15
|
-
if (DEBUG_SSE) {
|
|
16
|
-
console.error("[CRS-SSE]", ...args);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function generateToolUseId() {
|
|
20
|
-
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
21
|
-
let id = "toolu_01";
|
|
22
|
-
for (let i = 0;i < 22; i++) {
|
|
23
|
-
id += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
24
|
-
}
|
|
25
|
-
return id;
|
|
26
|
-
}
|
|
27
|
-
function emitSSEEvent(controller, eventType, data) {
|
|
28
|
-
controller.enqueue(`event: ${eventType}
|
|
29
|
-
`);
|
|
30
|
-
controller.enqueue(`data: ${JSON.stringify(data)}
|
|
31
|
-
`);
|
|
32
|
-
controller.enqueue(`
|
|
33
|
-
`);
|
|
34
|
-
}
|
|
35
|
-
function emitEventHeader(controller, eventType) {
|
|
36
|
-
controller.enqueue(`event: ${eventType}
|
|
37
|
-
`);
|
|
38
|
-
}
|
|
39
|
-
function buildToolSchemas(tools) {
|
|
40
|
-
const schemas = new Map;
|
|
41
|
-
for (const tool of tools) {
|
|
42
|
-
const params = tool.input_schema?.properties || {};
|
|
43
|
-
const required = new Set(tool.input_schema?.required || []);
|
|
44
|
-
schemas.set(tool.name, { params, required });
|
|
45
|
-
}
|
|
46
|
-
return schemas;
|
|
47
|
-
}
|
|
48
|
-
function inferToolName(jsonStr, toolSchemas) {
|
|
49
|
-
try {
|
|
50
|
-
const input = JSON.parse(jsonStr);
|
|
51
|
-
const inputKeys = new Set(Object.keys(input));
|
|
52
|
-
let bestMatch = null;
|
|
53
|
-
let bestScore = -1;
|
|
54
|
-
for (const [toolName, schema] of toolSchemas) {
|
|
55
|
-
const schemaKeys = new Set(Object.keys(schema.params));
|
|
56
|
-
let matchCount = 0;
|
|
57
|
-
let mismatchCount = 0;
|
|
58
|
-
for (const key of inputKeys) {
|
|
59
|
-
if (schemaKeys.has(key)) {
|
|
60
|
-
matchCount++;
|
|
61
|
-
} else {
|
|
62
|
-
mismatchCount++;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
let hasAllRequired = true;
|
|
66
|
-
for (const req of schema.required) {
|
|
67
|
-
if (!inputKeys.has(req)) {
|
|
68
|
-
hasAllRequired = false;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const score = matchCount - mismatchCount + (hasAllRequired ? 10 : 0);
|
|
73
|
-
if (score > bestScore) {
|
|
74
|
-
bestScore = score;
|
|
75
|
-
bestMatch = toolName;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return bestMatch;
|
|
79
|
-
} catch {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
var BEDROCK_MODEL_MAPPINGS = {
|
|
84
|
-
"us.anthropic.claude-sonnet-4-20250514-v1:0": "claude-sonnet-4-20250514",
|
|
85
|
-
"us.anthropic.claude-opus-4-20250514-v1:0": "claude-opus-4-20250514",
|
|
86
|
-
"us.anthropic.claude-3-5-sonnet-20241022-v2:0": "claude-3-5-sonnet-20241022",
|
|
87
|
-
"us.anthropic.claude-3-5-haiku-20241022-v1:0": "claude-3-5-haiku-20241022",
|
|
88
|
-
"us.anthropic.claude-3-opus-20240229-v1:0": "claude-3-opus-20240229"
|
|
89
|
-
};
|
|
90
|
-
function mapBedrockModelId(bedrockModelId) {
|
|
91
|
-
if (BEDROCK_MODEL_MAPPINGS[bedrockModelId]) {
|
|
92
|
-
return BEDROCK_MODEL_MAPPINGS[bedrockModelId];
|
|
93
|
-
}
|
|
94
|
-
const match = bedrockModelId.match(/us\.anthropic\.(.+?)-v\d+:\d+$/);
|
|
95
|
-
if (match) {
|
|
96
|
-
return match[1];
|
|
97
|
-
}
|
|
98
|
-
return bedrockModelId;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
class SSETransformState {
|
|
102
|
-
toolSchemas;
|
|
103
|
-
buffer;
|
|
104
|
-
currentEventType;
|
|
105
|
-
startedBlocks;
|
|
106
|
-
pendingToolBlocks;
|
|
107
|
-
constructor(toolSchemas) {
|
|
108
|
-
this.toolSchemas = toolSchemas;
|
|
109
|
-
this.buffer = "";
|
|
110
|
-
this.currentEventType = null;
|
|
111
|
-
this.startedBlocks = new Map;
|
|
112
|
-
this.pendingToolBlocks = new Map;
|
|
113
|
-
}
|
|
114
|
-
trackBlockStart(index, contentBlock) {
|
|
115
|
-
const blockType = contentBlock?.type || "text";
|
|
116
|
-
if (blockType === "tool_use") {
|
|
117
|
-
const toolBlock = contentBlock;
|
|
118
|
-
this.startedBlocks.set(index, {
|
|
119
|
-
type: "tool_use",
|
|
120
|
-
id: toolBlock.id || generateToolUseId(),
|
|
121
|
-
name: toolBlock.name || "unknown",
|
|
122
|
-
inputBuffer: "",
|
|
123
|
-
emitted: true
|
|
124
|
-
});
|
|
125
|
-
} else {
|
|
126
|
-
this.startedBlocks.set(index, { type: "text", emitted: true });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
bufferToolDelta(index, eventType, data, partialJson) {
|
|
130
|
-
if (!this.pendingToolBlocks.has(index)) {
|
|
131
|
-
this.pendingToolBlocks.set(index, {
|
|
132
|
-
toolId: generateToolUseId(),
|
|
133
|
-
deltas: [],
|
|
134
|
-
inputBuffer: ""
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
const pending = this.pendingToolBlocks.get(index);
|
|
138
|
-
pending.inputBuffer += partialJson;
|
|
139
|
-
pending.deltas.push({ event: eventType, data: { ...data } });
|
|
140
|
-
debugLog("Buffering tool delta, accumulated:", pending.inputBuffer);
|
|
141
|
-
}
|
|
142
|
-
hasPendingToolBlock(index) {
|
|
143
|
-
return this.pendingToolBlocks.has(index);
|
|
144
|
-
}
|
|
145
|
-
flushPendingToolBlocks(controller) {
|
|
146
|
-
for (const [blockIndex, pending] of this.pendingToolBlocks) {
|
|
147
|
-
const inferredName = inferToolName(pending.inputBuffer, this.toolSchemas) || "unknown";
|
|
148
|
-
debugLog("Flushing pending tool block", blockIndex, "with inferred name:", inferredName);
|
|
149
|
-
emitSSEEvent(controller, "content_block_start", {
|
|
150
|
-
type: "content_block_start",
|
|
151
|
-
index: blockIndex,
|
|
152
|
-
content_block: {
|
|
153
|
-
type: "tool_use",
|
|
154
|
-
id: pending.toolId,
|
|
155
|
-
name: inferredName,
|
|
156
|
-
input: {}
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
for (const buffered of pending.deltas) {
|
|
160
|
-
emitSSEEvent(controller, buffered.event, {
|
|
161
|
-
type: buffered.event,
|
|
162
|
-
...buffered.data
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
this.startedBlocks.set(blockIndex, {
|
|
166
|
-
type: "tool_use",
|
|
167
|
-
id: pending.toolId,
|
|
168
|
-
name: inferredName,
|
|
169
|
-
inputBuffer: pending.inputBuffer,
|
|
170
|
-
emitted: true
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
this.pendingToolBlocks.clear();
|
|
174
|
-
}
|
|
175
|
-
closeAllBlocks(controller) {
|
|
176
|
-
for (const [blockIndex] of this.startedBlocks) {
|
|
177
|
-
emitSSEEvent(controller, "content_block_stop", {
|
|
178
|
-
type: "content_block_stop",
|
|
179
|
-
index: blockIndex
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
this.startedBlocks.clear();
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
function createSSETransformStream(tools = []) {
|
|
186
|
-
const state = new SSETransformState(buildToolSchemas(tools));
|
|
187
|
-
return new TransformStream({
|
|
188
|
-
transform(chunk, controller) {
|
|
189
|
-
state.buffer += chunk;
|
|
190
|
-
const lines = state.buffer.split(`
|
|
191
|
-
`);
|
|
192
|
-
state.buffer = lines.pop() || "";
|
|
193
|
-
for (const line of lines) {
|
|
194
|
-
const result = processSSELine(line, state, controller);
|
|
195
|
-
if (result === "skip")
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
flush(controller) {
|
|
200
|
-
if (state.buffer.trim()) {
|
|
201
|
-
controller.enqueue(state.buffer);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
function processSSELine(line, state, controller) {
|
|
207
|
-
const trimmedLine = line.trim();
|
|
208
|
-
if (trimmedLine === "") {
|
|
209
|
-
controller.enqueue(`
|
|
210
|
-
`);
|
|
211
|
-
state.currentEventType = null;
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
if (trimmedLine.startsWith(":")) {
|
|
215
|
-
controller.enqueue(line + `
|
|
216
|
-
`);
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
if (trimmedLine.startsWith("event:")) {
|
|
220
|
-
const eventType = trimmedLine.slice(6).trim();
|
|
221
|
-
if (!VALID_SSE_EVENT_TYPES.has(eventType)) {
|
|
222
|
-
state.currentEventType = null;
|
|
223
|
-
return "skip";
|
|
224
|
-
}
|
|
225
|
-
state.currentEventType = eventType;
|
|
226
|
-
controller.enqueue(line + `
|
|
227
|
-
`);
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
if (trimmedLine.startsWith("data:")) {
|
|
231
|
-
return processDataLine(trimmedLine, line, state, controller);
|
|
232
|
-
}
|
|
233
|
-
controller.enqueue(line + `
|
|
234
|
-
`);
|
|
235
|
-
}
|
|
236
|
-
function processDataLine(trimmedLine, originalLine, state, controller) {
|
|
237
|
-
const dataContent = trimmedLine.slice(5).trim();
|
|
238
|
-
if (dataContent === "[DONE]") {
|
|
239
|
-
controller.enqueue(originalLine + `
|
|
240
|
-
`);
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
if (state.currentEventType === null) {
|
|
244
|
-
return "skip";
|
|
245
|
-
}
|
|
246
|
-
try {
|
|
247
|
-
const data = JSON.parse(dataContent);
|
|
248
|
-
debugLog("Event:", state.currentEventType, "Data:", JSON.stringify(data).slice(0, 500));
|
|
249
|
-
const shouldSkip = handleEventData(data, state, controller);
|
|
250
|
-
if (shouldSkip)
|
|
251
|
-
return "skip";
|
|
252
|
-
const normalized = normalizeEventData(data, state.currentEventType);
|
|
253
|
-
controller.enqueue(`data: ${JSON.stringify(normalized)}
|
|
254
|
-
`);
|
|
255
|
-
} catch {
|
|
256
|
-
controller.enqueue(originalLine + `
|
|
257
|
-
`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
function handleEventData(data, state, controller) {
|
|
261
|
-
const eventType = state.currentEventType;
|
|
262
|
-
if (eventType === "content_block_start" && typeof data.index === "number") {
|
|
263
|
-
const blockData = data;
|
|
264
|
-
state.trackBlockStart(blockData.index, blockData.content_block);
|
|
265
|
-
return false;
|
|
266
|
-
}
|
|
267
|
-
if (eventType === "content_block_delta" && typeof data.index === "number") {
|
|
268
|
-
return handleContentBlockDelta(data, state, controller);
|
|
269
|
-
}
|
|
270
|
-
if (eventType === "content_block_stop" && typeof data.index === "number") {
|
|
271
|
-
state.startedBlocks.delete(data.index);
|
|
272
|
-
return false;
|
|
273
|
-
}
|
|
274
|
-
if (eventType === "message_delta") {
|
|
275
|
-
state.flushPendingToolBlocks(controller);
|
|
276
|
-
if (state.startedBlocks.size > 0) {
|
|
277
|
-
state.closeAllBlocks(controller);
|
|
278
|
-
}
|
|
279
|
-
emitEventHeader(controller, eventType);
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
284
|
-
function handleContentBlockDelta(data, state, controller) {
|
|
285
|
-
const index = data.index;
|
|
286
|
-
const deltaType = data.delta?.type;
|
|
287
|
-
if (!state.startedBlocks.has(index)) {
|
|
288
|
-
if (deltaType === "input_json_delta") {
|
|
289
|
-
const jsonDelta = data.delta;
|
|
290
|
-
state.bufferToolDelta(index, state.currentEventType, data, jsonDelta.partial_json || "");
|
|
291
|
-
return true;
|
|
292
|
-
} else {
|
|
293
|
-
injectTextBlockStart(index, state, controller);
|
|
294
|
-
}
|
|
295
|
-
} else if (deltaType === "input_json_delta" && state.hasPendingToolBlock(index)) {
|
|
296
|
-
const jsonDelta = data.delta;
|
|
297
|
-
state.bufferToolDelta(index, state.currentEventType, data, jsonDelta.partial_json || "");
|
|
298
|
-
return true;
|
|
299
|
-
} else if (deltaType === "input_json_delta") {
|
|
300
|
-
const blockInfo = state.startedBlocks.get(index);
|
|
301
|
-
if (blockInfo?.type === "tool_use") {
|
|
302
|
-
const jsonDelta = data.delta;
|
|
303
|
-
blockInfo.inputBuffer = (blockInfo.inputBuffer || "") + (jsonDelta.partial_json || "");
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
fixDeltaTypeMismatch(data, state.startedBlocks.get(index));
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
function injectTextBlockStart(index, state, controller) {
|
|
310
|
-
state.startedBlocks.set(index, { type: "text", emitted: true });
|
|
311
|
-
emitSSEEvent(controller, "content_block_start", {
|
|
312
|
-
type: "content_block_start",
|
|
313
|
-
index,
|
|
314
|
-
content_block: { type: "text", text: "" }
|
|
315
|
-
});
|
|
316
|
-
emitEventHeader(controller, state.currentEventType);
|
|
317
|
-
}
|
|
318
|
-
function fixDeltaTypeMismatch(data, blockInfo) {
|
|
319
|
-
if (!blockInfo || !data.delta)
|
|
320
|
-
return;
|
|
321
|
-
const blockType = blockInfo.type || "text";
|
|
322
|
-
if (blockType === "tool_use" && data.delta.type === "text_delta") {
|
|
323
|
-
const textDelta = data.delta;
|
|
324
|
-
data.delta = {
|
|
325
|
-
type: "input_json_delta",
|
|
326
|
-
partial_json: textDelta.text || ""
|
|
327
|
-
};
|
|
328
|
-
} else if (blockType === "text" && data.delta.type === "input_json_delta") {
|
|
329
|
-
const jsonDelta = data.delta;
|
|
330
|
-
data.delta = {
|
|
331
|
-
type: "text_delta",
|
|
332
|
-
text: jsonDelta.partial_json || ""
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function normalizeEventData(data, eventType) {
|
|
337
|
-
const normalized = { ...data };
|
|
338
|
-
if (!normalized.type) {
|
|
339
|
-
normalized.type = eventType;
|
|
340
|
-
}
|
|
341
|
-
if (eventType === "message_start") {
|
|
342
|
-
return normalizeMessageStart(data);
|
|
343
|
-
}
|
|
344
|
-
if (eventType === "content_block_start") {
|
|
345
|
-
return normalizeContentBlockStart(normalized);
|
|
346
|
-
}
|
|
347
|
-
return normalized;
|
|
348
|
-
}
|
|
349
|
-
function normalizeMessageStart(data) {
|
|
350
|
-
if (data.type === "message" || !data.message) {
|
|
351
|
-
const messageData = { ...data };
|
|
352
|
-
delete messageData.type;
|
|
353
|
-
if (typeof messageData.model === "string" && messageData.model.includes("us.anthropic.")) {
|
|
354
|
-
messageData.model = mapBedrockModelId(messageData.model);
|
|
355
|
-
}
|
|
356
|
-
return { type: "message_start", message: messageData };
|
|
357
|
-
}
|
|
358
|
-
if (data.message?.model?.includes("us.anthropic.")) {
|
|
359
|
-
const normalized = {
|
|
360
|
-
...data,
|
|
361
|
-
message: {
|
|
362
|
-
...data.message,
|
|
363
|
-
model: mapBedrockModelId(data.message.model)
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
return normalized;
|
|
367
|
-
}
|
|
368
|
-
return data;
|
|
369
|
-
}
|
|
370
|
-
function normalizeContentBlockStart(normalized) {
|
|
371
|
-
if (!normalized.content_block) {
|
|
372
|
-
return {
|
|
373
|
-
...normalized,
|
|
374
|
-
content_block: { type: "text", text: "" }
|
|
375
|
-
};
|
|
376
|
-
} else if (normalized.content_block.type === "tool_use") {
|
|
377
|
-
const toolBlock = normalized.content_block;
|
|
378
|
-
return {
|
|
379
|
-
...normalized,
|
|
380
|
-
content_block: {
|
|
381
|
-
...toolBlock,
|
|
382
|
-
id: toolBlock.id || generateToolUseId(),
|
|
383
|
-
name: toolBlock.name || "unknown"
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
return normalized;
|
|
388
|
-
}
|
|
389
|
-
function extractToolsFromBody(body) {
|
|
390
|
-
if (!body)
|
|
391
|
-
return [];
|
|
392
|
-
try {
|
|
393
|
-
const bodyStr = typeof body === "string" ? body : new TextDecoder().decode(body);
|
|
394
|
-
const bodyJson = JSON.parse(bodyStr);
|
|
395
|
-
return Array.isArray(bodyJson.tools) ? bodyJson.tools : [];
|
|
396
|
-
} catch {
|
|
397
|
-
return [];
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
function buildRequestHeaders(input, init, apiKey) {
|
|
401
|
-
const headers = new Headers;
|
|
402
|
-
if (input instanceof Request) {
|
|
403
|
-
input.headers.forEach((value, key) => headers.set(key, value));
|
|
404
|
-
}
|
|
405
|
-
if (init?.headers) {
|
|
406
|
-
const initHeaders = init.headers;
|
|
407
|
-
if (initHeaders instanceof Headers) {
|
|
408
|
-
initHeaders.forEach((value, key) => headers.set(key, value));
|
|
409
|
-
} else if (Array.isArray(initHeaders)) {
|
|
410
|
-
for (const [key, value] of initHeaders) {
|
|
411
|
-
if (value !== undefined)
|
|
412
|
-
headers.set(key, String(value));
|
|
413
|
-
}
|
|
414
|
-
} else {
|
|
415
|
-
for (const [key, value] of Object.entries(initHeaders)) {
|
|
416
|
-
if (value !== undefined)
|
|
417
|
-
headers.set(key, String(value));
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
headers.set("x-api-key", apiKey);
|
|
422
|
-
if (!headers.has("anthropic-version")) {
|
|
423
|
-
headers.set("anthropic-version", "2023-06-01");
|
|
424
|
-
}
|
|
425
|
-
return headers;
|
|
426
|
-
}
|
|
427
|
-
function normalizeApiUrl(input) {
|
|
428
|
-
const url = input instanceof Request ? input.url : String(input);
|
|
429
|
-
const urlObj = new URL(url);
|
|
430
|
-
if (!urlObj.pathname.includes("/v1/")) {
|
|
431
|
-
if (urlObj.pathname.includes("/api/")) {
|
|
432
|
-
urlObj.pathname = urlObj.pathname.replace("/api/", "/api/v1/");
|
|
433
|
-
} else {
|
|
434
|
-
urlObj.pathname = urlObj.pathname.replace(/^\/?/, "/v1/");
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
return urlObj.toString();
|
|
438
|
-
}
|
|
439
|
-
function transformStreamingResponse(response, tools) {
|
|
440
|
-
const reader = response.body.getReader();
|
|
441
|
-
const decoder = new TextDecoder;
|
|
442
|
-
const encoder = new TextEncoder;
|
|
443
|
-
const transformStream = createSSETransformStream(tools);
|
|
444
|
-
const writer = transformStream.writable.getWriter();
|
|
445
|
-
(async () => {
|
|
446
|
-
try {
|
|
447
|
-
while (true) {
|
|
448
|
-
const { done, value } = await reader.read();
|
|
449
|
-
if (done) {
|
|
450
|
-
await writer.close();
|
|
451
|
-
break;
|
|
452
|
-
}
|
|
453
|
-
await writer.write(decoder.decode(value, { stream: true }));
|
|
454
|
-
}
|
|
455
|
-
} catch (error) {
|
|
456
|
-
await writer.abort(error);
|
|
457
|
-
}
|
|
458
|
-
})();
|
|
459
|
-
const transformedBody = transformStream.readable.pipeThrough(new TransformStream({
|
|
460
|
-
transform(chunk, controller) {
|
|
461
|
-
controller.enqueue(encoder.encode(chunk));
|
|
462
|
-
}
|
|
463
|
-
}));
|
|
464
|
-
return new Response(transformedBody, {
|
|
465
|
-
status: response.status,
|
|
466
|
-
statusText: response.statusText,
|
|
467
|
-
headers: response.headers
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
async function transformJsonResponse(response) {
|
|
471
|
-
try {
|
|
472
|
-
const text = await response.text();
|
|
473
|
-
const data = JSON.parse(text);
|
|
474
|
-
if (data.model?.includes("us.anthropic.")) {
|
|
475
|
-
data.model = mapBedrockModelId(data.model);
|
|
476
|
-
}
|
|
477
|
-
return new Response(JSON.stringify(data), {
|
|
478
|
-
status: response.status,
|
|
479
|
-
statusText: response.statusText,
|
|
480
|
-
headers: response.headers
|
|
481
|
-
});
|
|
482
|
-
} catch {
|
|
483
|
-
return response;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
function createCRSFetch(getAuth) {
|
|
487
|
-
return async function crsFetch(input, init) {
|
|
488
|
-
const auth = await getAuth();
|
|
489
|
-
if (!auth?.key) {
|
|
490
|
-
return fetch(input, init);
|
|
491
|
-
}
|
|
492
|
-
const url = normalizeApiUrl(input);
|
|
493
|
-
const headers = buildRequestHeaders(input, init, auth.key);
|
|
494
|
-
const tools = extractToolsFromBody(init?.body);
|
|
495
|
-
const response = await fetch(url, { ...init, headers });
|
|
496
|
-
const contentType = response.headers.get("content-type") || "";
|
|
497
|
-
if (contentType.includes("text/event-stream") && response.body) {
|
|
498
|
-
return transformStreamingResponse(response, tools);
|
|
499
|
-
}
|
|
500
|
-
if (contentType.includes("application/json")) {
|
|
501
|
-
return transformJsonResponse(response);
|
|
502
|
-
}
|
|
503
|
-
return response;
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
var CRSAuthPlugin = async () => {
|
|
507
|
-
return {
|
|
508
|
-
auth: {
|
|
509
|
-
provider: PROVIDER_ID,
|
|
510
|
-
async loader(getAuth, provider) {
|
|
511
|
-
let baseURL = provider?.options?.baseURL;
|
|
512
|
-
if (baseURL && !baseURL.includes("/v1")) {
|
|
513
|
-
baseURL = baseURL.replace(/\/?$/, "/v1/");
|
|
514
|
-
}
|
|
515
|
-
let apiKey = provider?.options?.apiKey;
|
|
516
|
-
if (!apiKey) {
|
|
517
|
-
const auth = await getAuth();
|
|
518
|
-
apiKey = auth?.type === "api" ? auth.key : undefined;
|
|
519
|
-
}
|
|
520
|
-
if (!apiKey) {
|
|
521
|
-
return {};
|
|
522
|
-
}
|
|
523
|
-
return {
|
|
524
|
-
apiKey: "",
|
|
525
|
-
baseURL,
|
|
526
|
-
fetch: createCRSFetch(() => Promise.resolve({ key: apiKey }))
|
|
527
|
-
};
|
|
528
|
-
},
|
|
529
|
-
methods: [
|
|
530
|
-
{
|
|
531
|
-
label: "Enter CRS API Key",
|
|
532
|
-
type: "api",
|
|
533
|
-
prompts: [
|
|
534
|
-
{
|
|
535
|
-
type: "text",
|
|
536
|
-
message: "Enter your CRS API Key (cr_...)",
|
|
537
|
-
key: "apiKey"
|
|
538
|
-
}
|
|
539
|
-
]
|
|
540
|
-
}
|
|
541
|
-
]
|
|
542
|
-
}
|
|
543
|
-
};
|
|
544
|
-
};
|
|
545
|
-
export {
|
|
546
|
-
CRSAuthPlugin
|
|
547
|
-
};
|