elasticdash-sdk 0.2.8 → 0.2.9

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/index.cjs CHANGED
@@ -1751,18 +1751,18 @@ function interceptFetch() {
1751
1751
  const ctx = getCaptureContext();
1752
1752
  const httpCtx = getHttpRunContext();
1753
1753
  const obsCtx = getObservabilityContext();
1754
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1755
+ if (isAIProviderUrl(url)) {
1756
+ return originalFetch(input, init);
1757
+ }
1754
1758
  const mockHeader = encodeMockConfigHeader();
1755
1759
  if (!ctx && !httpCtx && !obsCtx) {
1756
1760
  return originalFetch(input, mockHeader ? attachMockConfigHeader(init, mockHeader) : init);
1757
1761
  }
1758
1762
  if (mockHeader) init = attachMockConfigHeader(init, mockHeader);
1759
- const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1760
1763
  const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
1761
1764
  const rawHeaders = init?.headers ?? (input instanceof Request ? input.headers : void 0);
1762
1765
  const rawBody = init?.body ?? (input instanceof Request ? input.body : void 0);
1763
- if (isAIProviderUrl(url)) {
1764
- return originalFetch(input, init);
1765
- }
1766
1766
  if (!ctx && !httpCtx && obsCtx) {
1767
1767
  const id2 = obsCtx.nextId();
1768
1768
  const { response: response2, event: event2 } = await executeLiveAndRecord(originalFetch, input, init, id2, url, method, rawHeaders, rawBody);
@@ -1853,7 +1853,8 @@ var init_http = __esm({
1853
1853
  /https?:\/\/api\.anthropic\.com\/v1\/messages/,
1854
1854
  /https?:\/\/generativelanguage\.googleapis\.com\/.*\/models\/[^/:]+:(generateContent|streamGenerateContent)/,
1855
1855
  /https?:\/\/api\.x\.ai\/v1\/(chat\/)?completions/,
1856
- /https?:\/\/api\.moonshot\.ai\/v1\/(chat\/)?completions/
1856
+ /https?:\/\/api\.moonshot\.ai\/v1\/(chat\/)?completions/,
1857
+ /https?:\/\/bedrock-runtime\.[^./]+\.amazonaws\.com\/model\/[^/]+\/(invoke|invoke-with-response-stream|converse|converse-stream)/
1857
1858
  ];
1858
1859
  }
1859
1860
  });
@@ -1861,8 +1862,14 @@ var init_http = __esm({
1861
1862
  // src/interceptors/ai-interceptor.ts
1862
1863
  var ai_interceptor_exports = {};
1863
1864
  __export(ai_interceptor_exports, {
1865
+ bedrockApiKind: () => bedrockApiKind,
1866
+ bedrockVendor: () => bedrockVendor,
1867
+ bufferBedrockEventStream: () => bufferBedrockEventStream,
1864
1868
  consumeCapturedLLMRequest: () => consumeCapturedLLMRequest,
1869
+ encodeBedrockFrame: () => encodeBedrockFrame,
1870
+ extractBedrockModelId: () => extractBedrockModelId,
1865
1871
  installAIInterceptor: () => installAIInterceptor,
1872
+ parseBedrockEventStream: () => parseBedrockEventStream,
1866
1873
  uninstallAIInterceptor: () => uninstallAIInterceptor
1867
1874
  });
1868
1875
  function isAIWrapperActive() {
@@ -1894,7 +1901,7 @@ function extractPromptSnippet(body) {
1894
1901
  }
1895
1902
  return void 0;
1896
1903
  }
1897
- function extractUsage(provider, body) {
1904
+ function extractUsage(provider, body, url) {
1898
1905
  if (provider === "openai" || provider === "grok" || provider === "kimi") {
1899
1906
  const u = body.usage;
1900
1907
  if (!u) return void 0;
@@ -1910,9 +1917,24 @@ function extractUsage(provider, body) {
1910
1917
  if (!u) return void 0;
1911
1918
  return { inputTokens: u.promptTokenCount, outputTokens: u.candidatesTokenCount, totalTokens: u.totalTokenCount };
1912
1919
  }
1920
+ if (provider === "bedrock") {
1921
+ const kind = url ? bedrockApiKind(url) : void 0;
1922
+ const vendor = url ? bedrockVendor(extractBedrockModelId(url)) : "unknown";
1923
+ if (kind === "converse" || kind === "converse-stream") {
1924
+ const u = body.usage;
1925
+ if (!u) return void 0;
1926
+ return { inputTokens: u.inputTokens, outputTokens: u.outputTokens, totalTokens: u.totalTokens ?? (u.inputTokens ?? 0) + (u.outputTokens ?? 0) };
1927
+ }
1928
+ if (vendor === "anthropic") {
1929
+ const u = body.usage;
1930
+ if (!u) return void 0;
1931
+ return { inputTokens: u.input_tokens, outputTokens: u.output_tokens, totalTokens: (u.input_tokens ?? 0) + (u.output_tokens ?? 0) };
1932
+ }
1933
+ return void 0;
1934
+ }
1913
1935
  return void 0;
1914
1936
  }
1915
- function extractAssistantMessage(provider, body) {
1937
+ function extractAssistantMessage(provider, body, url) {
1916
1938
  if (provider === "openai" || provider === "grok" || provider === "kimi") {
1917
1939
  const choices = body.choices;
1918
1940
  if (Array.isArray(choices) && choices.length > 0) {
@@ -1933,6 +1955,17 @@ function extractAssistantMessage(provider, body) {
1933
1955
  if (content && typeof content === "object") return content;
1934
1956
  }
1935
1957
  }
1958
+ if (provider === "bedrock") {
1959
+ const kind = url ? bedrockApiKind(url) : void 0;
1960
+ const vendor = url ? bedrockVendor(extractBedrockModelId(url)) : "unknown";
1961
+ if (kind === "converse" || kind === "converse-stream") {
1962
+ const output = body.output;
1963
+ const msg = output?.message;
1964
+ if (msg && typeof msg === "object") return msg;
1965
+ } else if (vendor === "anthropic" && Array.isArray(body.content)) {
1966
+ return { role: "assistant", content: body.content };
1967
+ }
1968
+ }
1936
1969
  return null;
1937
1970
  }
1938
1971
  function detectProvider2(url) {
@@ -1941,14 +1974,94 @@ function detectProvider2(url) {
1941
1974
  }
1942
1975
  return null;
1943
1976
  }
1977
+ function bedrockApiKind(url) {
1978
+ if (/\/converse-stream(\?|$)/.test(url)) return "converse-stream";
1979
+ if (/\/converse(\?|$)/.test(url)) return "converse";
1980
+ if (/\/invoke-with-response-stream(\?|$)/.test(url)) return "invoke-stream";
1981
+ if (/\/invoke(\?|$)/.test(url)) return "invoke";
1982
+ return void 0;
1983
+ }
1984
+ function extractBedrockModelId(url) {
1985
+ const m = /\/model\/([^/]+)\//.exec(url);
1986
+ if (!m) return "unknown";
1987
+ try {
1988
+ return decodeURIComponent(m[1]);
1989
+ } catch {
1990
+ return m[1];
1991
+ }
1992
+ }
1993
+ function bedrockVendor(modelId) {
1994
+ const stripped = modelId.replace(/^(us|eu|apac|au|ap)\./, "");
1995
+ const vendor = stripped.split(".")[0];
1996
+ switch (vendor) {
1997
+ case "anthropic":
1998
+ return "anthropic";
1999
+ case "amazon":
2000
+ return "amazon";
2001
+ case "cohere":
2002
+ return "cohere";
2003
+ case "meta":
2004
+ return "meta";
2005
+ case "mistral":
2006
+ return "mistral";
2007
+ case "ai21":
2008
+ return "ai21";
2009
+ case "stability":
2010
+ return "stability";
2011
+ default:
2012
+ return "unknown";
2013
+ }
2014
+ }
1944
2015
  function extractModel2(provider, body, url) {
1945
2016
  if (provider === "gemini") {
1946
2017
  const match = /\/models\/([^/:]+):/.exec(url);
1947
2018
  return match ? match[1] : "unknown";
1948
2019
  }
2020
+ if (provider === "bedrock") {
2021
+ return extractBedrockModelId(url);
2022
+ }
1949
2023
  return typeof body.model === "string" ? body.model : "unknown";
1950
2024
  }
1951
- function extractPrompt2(provider, body) {
2025
+ function extractPrompt2(provider, body, url) {
2026
+ if (provider === "bedrock") {
2027
+ const kind = url ? bedrockApiKind(url) : void 0;
2028
+ const vendor = url ? bedrockVendor(extractBedrockModelId(url)) : "unknown";
2029
+ if (kind === "converse" || kind === "converse-stream") {
2030
+ let systemPrefix = "";
2031
+ if (Array.isArray(body.system)) {
2032
+ systemPrefix = body.system.map((b) => b && typeof b === "object" ? String(b.text ?? "") : String(b)).filter(Boolean).map((t) => `system: ${t}`).join("\n");
2033
+ if (systemPrefix) systemPrefix += "\n";
2034
+ }
2035
+ const messages = body.messages;
2036
+ if (Array.isArray(messages)) {
2037
+ const msgText = messages.map((m) => {
2038
+ if (!m || typeof m !== "object") return String(m);
2039
+ const msg = m;
2040
+ let content = msg.content;
2041
+ if (Array.isArray(content)) {
2042
+ content = content.map((b) => b && typeof b === "object" ? String(b.text ?? "") : String(b)).filter(Boolean).join("");
2043
+ }
2044
+ return `${msg.role}: ${content}`;
2045
+ }).join("\n");
2046
+ return systemPrefix + msgText;
2047
+ }
2048
+ return systemPrefix;
2049
+ }
2050
+ if (vendor === "anthropic") {
2051
+ provider = "anthropic";
2052
+ } else {
2053
+ if (typeof body.prompt === "string") return body.prompt;
2054
+ if (typeof body.inputText === "string") return body.inputText;
2055
+ if (Array.isArray(body.messages)) {
2056
+ return body.messages.map((m) => {
2057
+ if (!m || typeof m !== "object") return String(m);
2058
+ const msg = m;
2059
+ return `${msg.role ?? "user"}: ${String(msg.content ?? "")}`;
2060
+ }).join("\n");
2061
+ }
2062
+ return "";
2063
+ }
2064
+ }
1952
2065
  if (provider === "openai" || provider === "anthropic" || provider === "grok" || provider === "kimi") {
1953
2066
  let systemPrefix = "";
1954
2067
  if (provider === "anthropic") {
@@ -2010,10 +2123,47 @@ function extractPrompt2(provider, body) {
2010
2123
  }
2011
2124
  return "";
2012
2125
  }
2013
- function extractCompletion2(provider, responseBody) {
2126
+ function extractCompletion2(provider, responseBody, url) {
2014
2127
  if (responseBody.streamed === true && typeof responseBody.completion === "string") {
2015
2128
  return responseBody.completion;
2016
2129
  }
2130
+ if (provider === "bedrock") {
2131
+ const kind = url ? bedrockApiKind(url) : void 0;
2132
+ const vendor = url ? bedrockVendor(extractBedrockModelId(url)) : "unknown";
2133
+ if (kind === "converse" || kind === "converse-stream") {
2134
+ const output = responseBody.output;
2135
+ const msg = output?.message;
2136
+ if (msg && Array.isArray(msg.content)) {
2137
+ return msg.content.map((b) => b && typeof b === "object" ? String(b.text ?? "") : "").filter(Boolean).join("");
2138
+ }
2139
+ return "";
2140
+ }
2141
+ if (vendor === "anthropic") {
2142
+ const content = responseBody.content;
2143
+ if (Array.isArray(content)) {
2144
+ return content.map((block) => {
2145
+ if (block && typeof block === "object") {
2146
+ const b = block;
2147
+ if (b.type === "text" && typeof b.text === "string") return b.text;
2148
+ }
2149
+ return "";
2150
+ }).filter(Boolean).join("");
2151
+ }
2152
+ return "";
2153
+ }
2154
+ if (typeof responseBody.completion === "string") return responseBody.completion;
2155
+ if (typeof responseBody.outputText === "string") return responseBody.outputText;
2156
+ if (typeof responseBody.generation === "string") return responseBody.generation;
2157
+ if (Array.isArray(responseBody.results)) {
2158
+ return responseBody.results.map((r) => r && typeof r === "object" ? String(r.outputText ?? r.text ?? "") : "").filter(Boolean).join("");
2159
+ }
2160
+ if (Array.isArray(responseBody.choices)) {
2161
+ const first = responseBody.choices[0];
2162
+ const msg = first?.message;
2163
+ if (msg && typeof msg.content === "string") return msg.content;
2164
+ }
2165
+ return "";
2166
+ }
2017
2167
  if (provider === "openai" || provider === "grok" || provider === "kimi") {
2018
2168
  const choices = responseBody.choices;
2019
2169
  if (Array.isArray(choices) && choices.length > 0) {
@@ -2064,6 +2214,182 @@ function extractCompletion2(provider, responseBody) {
2064
2214
  }
2065
2215
  return "";
2066
2216
  }
2217
+ function crc32(bytes, init = 4294967295) {
2218
+ let crc = init;
2219
+ for (let i = 0; i < bytes.length; i++) {
2220
+ crc = crc ^ bytes[i];
2221
+ for (let j = 0; j < 8; j++) {
2222
+ crc = crc >>> 1 ^ 3988292384 & -(crc & 1);
2223
+ }
2224
+ }
2225
+ return (crc ^ 4294967295) >>> 0;
2226
+ }
2227
+ function concatChunks(chunks) {
2228
+ let len = 0;
2229
+ for (const c of chunks) len += c.length;
2230
+ const out = new Uint8Array(len);
2231
+ let offset = 0;
2232
+ for (const c of chunks) {
2233
+ out.set(c, offset);
2234
+ offset += c.length;
2235
+ }
2236
+ return out;
2237
+ }
2238
+ function parseBedrockEventStream(buf) {
2239
+ const frames = [];
2240
+ const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
2241
+ const decoder = new TextDecoder();
2242
+ let offset = 0;
2243
+ while (offset + 12 <= buf.length) {
2244
+ const totalLen = view.getUint32(offset, false);
2245
+ const headersLen = view.getUint32(offset + 4, false);
2246
+ if (totalLen < 16 || totalLen > buf.length - offset) break;
2247
+ const headers2 = {};
2248
+ let h = offset + 12;
2249
+ const hEnd = h + headersLen;
2250
+ while (h < hEnd) {
2251
+ const nameLen = buf[h];
2252
+ h += 1;
2253
+ if (h + nameLen > hEnd) break;
2254
+ const name = decoder.decode(buf.subarray(h, h + nameLen));
2255
+ h += nameLen;
2256
+ const type = buf[h];
2257
+ h += 1;
2258
+ if (type === 7) {
2259
+ const valueLen = view.getUint16(h, false);
2260
+ h += 2;
2261
+ headers2[name] = decoder.decode(buf.subarray(h, h + valueLen));
2262
+ h += valueLen;
2263
+ } else {
2264
+ const lenByType = { 0: 0, 1: 0, 2: 1, 3: 2, 4: 4, 5: 8, 8: 8, 9: 16 };
2265
+ const skipLen = lenByType[type];
2266
+ if (skipLen === void 0) break;
2267
+ h += skipLen;
2268
+ }
2269
+ }
2270
+ const payloadStart = offset + 12 + headersLen;
2271
+ const payloadEnd = offset + totalLen - 4;
2272
+ if (payloadEnd >= payloadStart) {
2273
+ frames.push({ headers: headers2, payload: buf.subarray(payloadStart, payloadEnd) });
2274
+ }
2275
+ offset += totalLen;
2276
+ }
2277
+ return frames;
2278
+ }
2279
+ function encodeBedrockFrame(eventType, payloadObj) {
2280
+ const encoder = new TextEncoder();
2281
+ const payload = encoder.encode(JSON.stringify(payloadObj));
2282
+ const writeHeader = (name, value) => {
2283
+ const nameBytes = encoder.encode(name);
2284
+ const valueBytes = encoder.encode(value);
2285
+ const out2 = new Uint8Array(1 + nameBytes.length + 1 + 2 + valueBytes.length);
2286
+ const dv2 = new DataView(out2.buffer);
2287
+ let p = 0;
2288
+ out2[p++] = nameBytes.length;
2289
+ out2.set(nameBytes, p);
2290
+ p += nameBytes.length;
2291
+ out2[p++] = 7;
2292
+ dv2.setUint16(p, valueBytes.length, false);
2293
+ p += 2;
2294
+ out2.set(valueBytes, p);
2295
+ return out2;
2296
+ };
2297
+ const headers2 = concatChunks([
2298
+ writeHeader(":event-type", eventType),
2299
+ writeHeader(":content-type", "application/json"),
2300
+ writeHeader(":message-type", "event")
2301
+ ]);
2302
+ const totalLen = 12 + headers2.length + payload.length + 4;
2303
+ const out = new Uint8Array(totalLen);
2304
+ const dv = new DataView(out.buffer);
2305
+ dv.setUint32(0, totalLen, false);
2306
+ dv.setUint32(4, headers2.length, false);
2307
+ const preludeCrc = crc32(out.subarray(0, 8));
2308
+ dv.setUint32(8, preludeCrc, false);
2309
+ out.set(headers2, 12);
2310
+ out.set(payload, 12 + headers2.length);
2311
+ const msgCrc = crc32(out.subarray(0, totalLen - 4));
2312
+ dv.setUint32(totalLen - 4, msgCrc, false);
2313
+ return out;
2314
+ }
2315
+ async function bufferBedrockEventStream(url, stream) {
2316
+ const reader = stream.getReader();
2317
+ const chunks = [];
2318
+ try {
2319
+ for (; ; ) {
2320
+ const { done, value } = await reader.read();
2321
+ if (done) break;
2322
+ chunks.push(value);
2323
+ }
2324
+ } finally {
2325
+ reader.releaseLock();
2326
+ }
2327
+ const buf = concatChunks(chunks);
2328
+ const frames = parseBedrockEventStream(buf);
2329
+ const kind = bedrockApiKind(url);
2330
+ const vendor = bedrockVendor(extractBedrockModelId(url));
2331
+ const isInvoke = kind === "invoke-stream";
2332
+ const decoder = new TextDecoder();
2333
+ let completion = "";
2334
+ let usage;
2335
+ for (const frame of frames) {
2336
+ let obj;
2337
+ try {
2338
+ obj = JSON.parse(decoder.decode(frame.payload));
2339
+ } catch {
2340
+ continue;
2341
+ }
2342
+ const eventType = frame.headers[":event-type"];
2343
+ if (isInvoke && typeof obj.bytes === "string") {
2344
+ try {
2345
+ const innerJson = typeof atob === "function" ? atob(obj.bytes) : Buffer.from(obj.bytes, "base64").toString("utf8");
2346
+ const decoded = JSON.parse(innerJson);
2347
+ if (vendor === "anthropic") {
2348
+ if (decoded.type === "content_block_delta") {
2349
+ const d = decoded.delta;
2350
+ if (d?.type === "text_delta" && typeof d.text === "string") completion += d.text;
2351
+ }
2352
+ if (decoded.type === "message_delta") {
2353
+ const u = decoded.usage;
2354
+ if (u) usage = extractUsage("anthropic", { usage: u }) ?? usage;
2355
+ }
2356
+ if (decoded.type === "message_start") {
2357
+ const msg = decoded.message;
2358
+ if (msg?.usage) usage = extractUsage("anthropic", { usage: msg.usage }) ?? usage;
2359
+ }
2360
+ } else {
2361
+ if (typeof decoded.outputText === "string") completion += decoded.outputText;
2362
+ if (typeof decoded.generation === "string") completion += decoded.generation;
2363
+ }
2364
+ } catch {
2365
+ }
2366
+ continue;
2367
+ }
2368
+ if (eventType === "contentBlockDelta" || obj.contentBlockDelta) {
2369
+ const body = obj.contentBlockDelta ?? obj;
2370
+ const delta = body.delta;
2371
+ if (typeof delta?.text === "string") completion += delta.text;
2372
+ }
2373
+ if (eventType === "metadata" || obj.metadata) {
2374
+ const body = obj.metadata ?? obj;
2375
+ const u = body.usage;
2376
+ if (u) {
2377
+ usage = { inputTokens: u.inputTokens, outputTokens: u.outputTokens, totalTokens: u.totalTokens ?? (u.inputTokens ?? 0) + (u.outputTokens ?? 0) };
2378
+ }
2379
+ }
2380
+ }
2381
+ return { completion, usage };
2382
+ }
2383
+ function streamContentType(provider) {
2384
+ if (provider === "gemini") return "application/json";
2385
+ if (provider === "bedrock") return "application/vnd.amazon.eventstream";
2386
+ return "text/event-stream";
2387
+ }
2388
+ async function bufferAIStream(provider, url, stream) {
2389
+ if (provider === "bedrock") return bufferBedrockEventStream(url, stream);
2390
+ const completion = await bufferSSEStream(provider, stream);
2391
+ return { completion };
2392
+ }
2067
2393
  async function bufferSSEStream(provider, stream) {
2068
2394
  const decoder = new TextDecoder();
2069
2395
  const reader = stream.getReader();
@@ -2157,7 +2483,7 @@ function extractStreamUsage(provider, rawSSE) {
2157
2483
  }
2158
2484
  return void 0;
2159
2485
  }
2160
- function synthesizeCompletionJSON(provider, completion) {
2486
+ function synthesizeCompletionJSON(provider, completion, url) {
2161
2487
  if (provider === "gemini") {
2162
2488
  return {
2163
2489
  candidates: [{ content: { parts: [{ text: completion }], role: "model" }, finishReason: "STOP" }]
@@ -2173,14 +2499,67 @@ function synthesizeCompletionJSON(provider, completion) {
2173
2499
  stop_sequence: null
2174
2500
  };
2175
2501
  }
2502
+ if (provider === "bedrock") {
2503
+ const kind = url ? bedrockApiKind(url) : void 0;
2504
+ const vendor = url ? bedrockVendor(extractBedrockModelId(url)) : "unknown";
2505
+ if (kind === "converse" || kind === "converse-stream") {
2506
+ return {
2507
+ output: { message: { role: "assistant", content: [{ text: completion }] } },
2508
+ stopReason: "end_turn",
2509
+ usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
2510
+ };
2511
+ }
2512
+ if (vendor === "anthropic") {
2513
+ return {
2514
+ id: "replay",
2515
+ type: "message",
2516
+ role: "assistant",
2517
+ content: [{ type: "text", text: completion }],
2518
+ stop_reason: "end_turn",
2519
+ stop_sequence: null
2520
+ };
2521
+ }
2522
+ return { outputText: completion };
2523
+ }
2176
2524
  return {
2177
2525
  id: "replay",
2178
2526
  object: "chat.completion",
2179
2527
  choices: [{ index: 0, message: { role: "assistant", content: completion }, finish_reason: "stop" }]
2180
2528
  };
2181
2529
  }
2182
- function synthesizeSSEStream(provider, completion) {
2530
+ function synthesizeSSEStream(provider, completion, url) {
2183
2531
  const encoder = new TextEncoder();
2532
+ if (provider === "bedrock") {
2533
+ const kind = url ? bedrockApiKind(url) : void 0;
2534
+ const vendor = url ? bedrockVendor(extractBedrockModelId(url)) : "unknown";
2535
+ return new ReadableStream({
2536
+ start(ctrl) {
2537
+ if (kind === "converse-stream") {
2538
+ ctrl.enqueue(encodeBedrockFrame("messageStart", { role: "assistant" }));
2539
+ ctrl.enqueue(encodeBedrockFrame("contentBlockDelta", { contentBlockIndex: 0, delta: { text: completion } }));
2540
+ ctrl.enqueue(encodeBedrockFrame("contentBlockStop", { contentBlockIndex: 0 }));
2541
+ ctrl.enqueue(encodeBedrockFrame("messageStop", { stopReason: "end_turn" }));
2542
+ ctrl.enqueue(encodeBedrockFrame("metadata", { usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }, metrics: { latencyMs: 0 } }));
2543
+ } else if (vendor === "anthropic") {
2544
+ const wrap = (eventType, inner) => {
2545
+ const innerJson = JSON.stringify(inner);
2546
+ const b64 = typeof btoa === "function" ? btoa(innerJson) : Buffer.from(innerJson, "utf8").toString("base64");
2547
+ return encodeBedrockFrame(eventType, { bytes: b64 });
2548
+ };
2549
+ ctrl.enqueue(wrap("chunk", { type: "message_start", message: { id: "replay", type: "message", role: "assistant", content: [], stop_reason: null, stop_sequence: null } }));
2550
+ ctrl.enqueue(wrap("chunk", { type: "content_block_start", index: 0, content_block: { type: "text", text: "" } }));
2551
+ ctrl.enqueue(wrap("chunk", { type: "content_block_delta", index: 0, delta: { type: "text_delta", text: completion } }));
2552
+ ctrl.enqueue(wrap("chunk", { type: "content_block_stop", index: 0 }));
2553
+ ctrl.enqueue(wrap("chunk", { type: "message_delta", delta: { stop_reason: "end_turn", stop_sequence: null } }));
2554
+ ctrl.enqueue(wrap("chunk", { type: "message_stop" }));
2555
+ } else {
2556
+ const b64 = typeof btoa === "function" ? btoa(JSON.stringify({ outputText: completion })) : Buffer.from(JSON.stringify({ outputText: completion }), "utf8").toString("base64");
2557
+ ctrl.enqueue(encodeBedrockFrame("chunk", { bytes: b64 }));
2558
+ }
2559
+ ctrl.close();
2560
+ }
2561
+ });
2562
+ }
2184
2563
  return new ReadableStream({
2185
2564
  start(ctrl) {
2186
2565
  if (provider === "gemini") {
@@ -2261,25 +2640,33 @@ function installAIInterceptor() {
2261
2640
  body: capturedReq,
2262
2641
  promptSnippet: capturedSnippet
2263
2642
  };
2264
- const isStreaming2 = capturedReq.stream === true;
2643
+ const isStreaming2 = provider === "bedrock" ? (bedrockApiKind(url) ?? "").endsWith("-stream") : capturedReq.stream === true;
2265
2644
  try {
2266
2645
  const cloned = response2.clone();
2267
2646
  if (!isStreaming2) {
2268
2647
  const responseBody = await cloned.json();
2269
- captured.usage = extractUsage(provider, responseBody);
2648
+ captured.usage = extractUsage(provider, responseBody, url);
2270
2649
  } else if (cloned.body) {
2271
- try {
2272
- const decoder = new TextDecoder();
2273
- const reader = cloned.body.getReader();
2274
- let rawSSE = "";
2275
- for (; ; ) {
2276
- const { done, value } = await reader.read();
2277
- if (done) break;
2278
- rawSSE += decoder.decode(value, { stream: true });
2650
+ if (provider === "bedrock") {
2651
+ try {
2652
+ const { usage } = await bufferBedrockEventStream(url, cloned.body);
2653
+ captured.usage = usage;
2654
+ } catch {
2655
+ }
2656
+ } else {
2657
+ try {
2658
+ const decoder = new TextDecoder();
2659
+ const reader = cloned.body.getReader();
2660
+ let rawSSE = "";
2661
+ for (; ; ) {
2662
+ const { done, value } = await reader.read();
2663
+ if (done) break;
2664
+ rawSSE += decoder.decode(value, { stream: true });
2665
+ }
2666
+ reader.releaseLock();
2667
+ captured.usage = extractStreamUsage(provider, rawSSE);
2668
+ } catch {
2279
2669
  }
2280
- reader.releaseLock();
2281
- captured.usage = extractStreamUsage(provider, rawSSE);
2282
- } catch {
2283
2670
  }
2284
2671
  }
2285
2672
  } catch {
@@ -2304,12 +2691,19 @@ function installAIInterceptor() {
2304
2691
  if (rawBody && typeof rawBody === "string") {
2305
2692
  const body = JSON.parse(rawBody);
2306
2693
  model = extractModel2(provider, body, url);
2307
- prompt = extractPrompt2(provider, body);
2308
- isStreaming = body.stream === true;
2694
+ prompt = extractPrompt2(provider, body, url);
2695
+ isStreaming = provider === "bedrock" ? (bedrockApiKind(url) ?? "").endsWith("-stream") : body.stream === true;
2309
2696
  if (Array.isArray(body.messages)) messages = body.messages;
2310
2697
  else if (Array.isArray(body.contents)) messages = body.contents;
2698
+ } else if (provider === "bedrock") {
2699
+ isStreaming = (bedrockApiKind(url) ?? "").endsWith("-stream");
2700
+ model = extractModel2(provider, {}, url);
2311
2701
  }
2312
2702
  } catch {
2703
+ if (provider === "bedrock") {
2704
+ isStreaming = (bedrockApiKind(url) ?? "").endsWith("-stream");
2705
+ model = extractModel2(provider, {}, url);
2706
+ }
2313
2707
  }
2314
2708
  const ctx = getCaptureContext();
2315
2709
  if (!traceAtCall && !ctx && obsCtx && !isAIWrapperActive()) {
@@ -2319,9 +2713,9 @@ function installAIInterceptor() {
2319
2713
  const response2 = await originalFetch2(input, init);
2320
2714
  if (isStreaming && response2.body) {
2321
2715
  const [streamForCaller, streamForRecorder] = response2.body.tee();
2322
- bufferSSEStream(provider, streamForRecorder).then((completion) => {
2716
+ bufferAIStream(provider, url, streamForRecorder).then(({ completion, usage }) => {
2323
2717
  const durationMs = rawDateNow() - start;
2324
- pushTelemetryEvent({ id, type: "ai", name: model, input: eventInput, output: { streamed: true, completion }, timestamp: start, durationMs });
2718
+ pushTelemetryEvent({ id, type: "ai", name: model, input: eventInput, output: { streamed: true, completion }, timestamp: start, durationMs, ...usage ? { usage } : {} });
2325
2719
  }).catch(() => {
2326
2720
  const durationMs = rawDateNow() - start;
2327
2721
  pushTelemetryEvent({ id, type: "ai", name: model, input: eventInput, output: null, streamed: true, streamRaw: "", timestamp: start, durationMs });
@@ -2331,8 +2725,8 @@ function installAIInterceptor() {
2331
2725
  try {
2332
2726
  const cloned = response2.clone();
2333
2727
  const responseBody = await cloned.json();
2334
- const completion = extractCompletion2(provider, responseBody);
2335
- const usage = extractUsage(provider, responseBody);
2728
+ const completion = extractCompletion2(provider, responseBody, url);
2729
+ const usage = extractUsage(provider, responseBody, url);
2336
2730
  const durationMs = rawDateNow() - start;
2337
2731
  const event = {
2338
2732
  id,
@@ -2358,14 +2752,14 @@ function installAIInterceptor() {
2358
2752
  if (frozen && frozen.type === "ai") {
2359
2753
  pushTelemetryEvent(frozen);
2360
2754
  const frozenOutput = frozen.output;
2361
- const completion = frozenOutput ? extractCompletion2(provider, frozenOutput) : "(replayed)";
2755
+ const completion = frozenOutput ? extractCompletion2(provider, frozenOutput, url) : "(replayed)";
2362
2756
  if (isStreaming) {
2363
- return new Response(synthesizeSSEStream(provider, completion), {
2757
+ return new Response(synthesizeSSEStream(provider, completion, url), {
2364
2758
  status: 200,
2365
- headers: { "Content-Type": provider === "gemini" ? "application/json" : "text/event-stream" }
2759
+ headers: { "Content-Type": streamContentType(provider) }
2366
2760
  });
2367
2761
  }
2368
- const body = frozenOutput?.streamed === true ? synthesizeCompletionJSON(provider, completion) : frozenOutput ?? synthesizeCompletionJSON(provider, completion);
2762
+ const body = frozenOutput?.streamed === true ? synthesizeCompletionJSON(provider, completion, url) : frozenOutput ?? synthesizeCompletionJSON(provider, completion, url);
2369
2763
  return new Response(JSON.stringify(body), {
2370
2764
  status: 200,
2371
2765
  headers: { "Content-Type": "application/json" }
@@ -2375,9 +2769,9 @@ function installAIInterceptor() {
2375
2769
  const response2 = await originalFetch2(input, init);
2376
2770
  if (isStreaming && response2.body) {
2377
2771
  const [streamForCaller, streamForRecorder] = response2.body.tee();
2378
- bufferSSEStream(provider, streamForRecorder).then((completion) => {
2772
+ bufferAIStream(provider, url, streamForRecorder).then(({ completion, usage }) => {
2379
2773
  const durationMs = rawDateNow() - start;
2380
- pushTelemetryEvent({ id, type: "ai", name: model, input: eventInput, output: { streamed: true, completion }, timestamp: start, durationMs });
2774
+ pushTelemetryEvent({ id, type: "ai", name: model, input: eventInput, output: { streamed: true, completion }, timestamp: start, durationMs, ...usage ? { usage } : {} });
2381
2775
  }).catch(() => {
2382
2776
  const durationMs = rawDateNow() - start;
2383
2777
  pushTelemetryEvent({ id, type: "ai", name: model, input: eventInput, output: null, streamed: true, streamRaw: "", timestamp: start, durationMs });
@@ -2387,8 +2781,8 @@ function installAIInterceptor() {
2387
2781
  try {
2388
2782
  const cloned = response2.clone();
2389
2783
  const responseBody = await cloned.json();
2390
- const completion = extractCompletion2(provider, responseBody);
2391
- const usage = extractUsage(provider, responseBody);
2784
+ const completion = extractCompletion2(provider, responseBody, url);
2785
+ const usage = extractUsage(provider, responseBody, url);
2392
2786
  const durationMs = rawDateNow() - start;
2393
2787
  pushTelemetryEvent({ id, type: "ai", name: model, input: eventInput, output: { completion }, timestamp: start, durationMs, ...usage ? { usage } : {} });
2394
2788
  } catch {
@@ -2410,16 +2804,16 @@ function installAIInterceptor() {
2410
2804
  if (isReplayMatch && historicalEvent) {
2411
2805
  recorder.record(historicalEvent);
2412
2806
  const historicalOutput = historicalEvent.output;
2413
- const completion = historicalOutput ? extractCompletion2(provider, historicalOutput) : "(replayed)";
2807
+ const completion = historicalOutput ? extractCompletion2(provider, historicalOutput, url) : "(replayed)";
2414
2808
  traceAtCall.recordLLMStep({ model, provider, prompt, completion, workflowEventId: id });
2415
2809
  if (isStreaming) {
2416
- return new Response(synthesizeSSEStream(provider, completion), {
2810
+ return new Response(synthesizeSSEStream(provider, completion, url), {
2417
2811
  status: 200,
2418
- headers: { "Content-Type": provider === "gemini" ? "application/json" : "text/event-stream" }
2812
+ headers: { "Content-Type": streamContentType(provider) }
2419
2813
  });
2420
2814
  }
2421
2815
  if (historicalOutput?.streamed === true) {
2422
- return new Response(JSON.stringify(synthesizeCompletionJSON(provider, completion)), {
2816
+ return new Response(JSON.stringify(synthesizeCompletionJSON(provider, completion, url)), {
2423
2817
  status: 200,
2424
2818
  headers: { "Content-Type": "application/json" }
2425
2819
  });
@@ -2436,10 +2830,10 @@ function installAIInterceptor() {
2436
2830
  if (response2.body) {
2437
2831
  const [streamForCaller, streamForRecorder] = response2.body.tee();
2438
2832
  recorder.trackAsync(
2439
- bufferSSEStream(provider, streamForRecorder).then((completion) => {
2833
+ bufferAIStream(provider, url, streamForRecorder).then(({ completion, usage }) => {
2440
2834
  const durationMs2 = rawDateNow() - start;
2441
2835
  traceAtCall.recordLLMStep({ model, provider, prompt, completion, workflowEventId: id, durationMs: durationMs2 });
2442
- recorder.record({ id, type: "ai", name: model, input: { url, provider, model, prompt, messages }, output: { streamed: true, completion }, timestamp: start, durationMs: durationMs2 });
2836
+ recorder.record({ id, type: "ai", name: model, input: { url, provider, model, prompt, messages }, output: { streamed: true, completion }, timestamp: start, durationMs: durationMs2, ...usage ? { usage } : {} });
2443
2837
  }).catch(() => {
2444
2838
  const durationMs2 = rawDateNow() - start;
2445
2839
  traceAtCall.recordLLMStep({ model, provider, prompt, completion: "(streamed-error)", workflowEventId: id, durationMs: durationMs2 });
@@ -2459,9 +2853,9 @@ function installAIInterceptor() {
2459
2853
  try {
2460
2854
  const cloned = response2.clone();
2461
2855
  const responseBody = await cloned.json();
2462
- const completion = extractCompletion2(provider, responseBody);
2463
- const usage = extractUsage(provider, responseBody);
2464
- const assistantMessage = extractAssistantMessage(provider, responseBody);
2856
+ const completion = extractCompletion2(provider, responseBody, url);
2857
+ const usage = extractUsage(provider, responseBody, url);
2858
+ const assistantMessage = extractAssistantMessage(provider, responseBody, url);
2465
2859
  traceAtCall.recordLLMStep({ model, provider, prompt, completion, workflowEventId: id, durationMs });
2466
2860
  recorder.record({ id, type: "ai", name: model, input: { url, provider, model, prompt, messages }, output: assistantMessage ?? responseBody, timestamp: start, durationMs, usage });
2467
2861
  } catch {
@@ -2475,7 +2869,7 @@ function installAIInterceptor() {
2475
2869
  const response = await originalFetch2(input, init);
2476
2870
  if (isStreaming && response.body) {
2477
2871
  const [streamForCaller, streamForRecorder] = response.body.tee();
2478
- bufferSSEStream(provider, streamForRecorder).then((completion) => {
2872
+ bufferAIStream(provider, url, streamForRecorder).then(({ completion }) => {
2479
2873
  traceAtCall.recordLLMStep({ model, provider, prompt, completion });
2480
2874
  }).catch(() => {
2481
2875
  traceAtCall.recordLLMStep({ model, provider, prompt, completion: "(streamed-error)" });
@@ -2485,7 +2879,7 @@ function installAIInterceptor() {
2485
2879
  try {
2486
2880
  const cloned = response.clone();
2487
2881
  const responseBody = await cloned.json();
2488
- const completion = extractCompletion2(provider, responseBody);
2882
+ const completion = extractCompletion2(provider, responseBody, url);
2489
2883
  traceAtCall.recordLLMStep({ model, provider, prompt, completion });
2490
2884
  } catch {
2491
2885
  traceAtCall.recordLLMStep({ model, provider, prompt, completion: "" });
@@ -2515,7 +2909,8 @@ var init_ai_interceptor = __esm({
2515
2909
  anthropic: /https?:\/\/api\.anthropic\.com\/v1\/messages/,
2516
2910
  gemini: /https?:\/\/generativelanguage\.googleapis\.com\/.*\/models\/[^\/:]+:(generateContent|streamGenerateContent)/,
2517
2911
  grok: /https?:\/\/api\.x\.ai\/v1\/(chat\/)?completions/,
2518
- kimi: /https?:\/\/api\.moonshot\.ai\/v1\/(chat\/)?completions/
2912
+ kimi: /https?:\/\/api\.moonshot\.ai\/v1\/(chat\/)?completions/,
2913
+ bedrock: /https?:\/\/bedrock-runtime\.[^./]+\.amazonaws\.com\/model\/[^/]+\/(invoke|invoke-with-response-stream|converse|converse-stream)/
2519
2914
  };
2520
2915
  originalFetch2 = null;
2521
2916
  }