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/README.md +15 -8
- package/dist/index.cjs +445 -50
- package/dist/interceptors/ai-interceptor.d.ts +28 -0
- package/dist/interceptors/ai-interceptor.d.ts.map +1 -1
- package/dist/interceptors/ai-interceptor.js +535 -48
- package/dist/interceptors/ai-interceptor.js.map +1 -1
- package/dist/interceptors/http.d.ts.map +1 -1
- package/dist/interceptors/http.js +14 -9
- package/dist/interceptors/http.js.map +1 -1
- package/dist/interceptors/workflow-ai.d.ts.map +1 -1
- package/dist/interceptors/workflow-ai.js +5 -1
- package/dist/interceptors/workflow-ai.js.map +1 -1
- package/docs/agent-integration-guide.md +9 -5
- package/docs/matchers.md +1 -1
- package/docs/quickstart.md +8 -0
- package/docs/security-compliance.md +1 -1
- package/docs/test-writing-guidelines.md +23 -0
- package/package.json +1 -1
- package/src/interceptors/ai-interceptor.ts +528 -46
- package/src/interceptors/http.ts +17 -11
- package/src/interceptors/workflow-ai.ts +5 -1
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
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|