lupislabs 1.0.0 → 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.
Files changed (66) hide show
  1. package/README.md +227 -358
  2. package/dist/cost-utils.d.ts +5 -0
  3. package/dist/cost-utils.d.ts.map +1 -0
  4. package/dist/cost-utils.js +55 -0
  5. package/dist/cost-utils.js.map +1 -0
  6. package/dist/endpoints.d.ts +2 -0
  7. package/dist/endpoints.d.ts.map +1 -0
  8. package/dist/endpoints.js +2 -0
  9. package/dist/endpoints.js.map +1 -0
  10. package/dist/http-interceptor.d.ts +18 -8
  11. package/dist/http-interceptor.d.ts.map +1 -1
  12. package/dist/http-interceptor.js +164 -416
  13. package/dist/http-interceptor.js.map +1 -1
  14. package/dist/http-types.d.ts +1 -1
  15. package/dist/http-types.d.ts.map +1 -1
  16. package/dist/index.d.ts +36 -6
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +174 -9
  19. package/dist/index.js.map +1 -1
  20. package/dist/interceptors/axios-interceptor.d.ts +18 -0
  21. package/dist/interceptors/axios-interceptor.d.ts.map +1 -0
  22. package/dist/interceptors/axios-interceptor.js +115 -0
  23. package/dist/interceptors/axios-interceptor.js.map +1 -0
  24. package/dist/interceptors/fetch-interceptor.d.ts +18 -0
  25. package/dist/interceptors/fetch-interceptor.d.ts.map +1 -0
  26. package/dist/interceptors/fetch-interceptor.js +228 -0
  27. package/dist/interceptors/fetch-interceptor.js.map +1 -0
  28. package/dist/interceptors/got-interceptor.d.ts +18 -0
  29. package/dist/interceptors/got-interceptor.d.ts.map +1 -0
  30. package/dist/interceptors/got-interceptor.js +103 -0
  31. package/dist/interceptors/got-interceptor.js.map +1 -0
  32. package/dist/interceptors/node-http-interceptor.d.ts +21 -0
  33. package/dist/interceptors/node-http-interceptor.d.ts.map +1 -0
  34. package/dist/interceptors/node-http-interceptor.js +301 -0
  35. package/dist/interceptors/node-http-interceptor.js.map +1 -0
  36. package/dist/providers/anthropic-handler.d.ts +3 -0
  37. package/dist/providers/anthropic-handler.d.ts.map +1 -0
  38. package/dist/providers/anthropic-handler.js +50 -0
  39. package/dist/providers/anthropic-handler.js.map +1 -0
  40. package/dist/providers/gemini-handler.d.ts +3 -0
  41. package/dist/providers/gemini-handler.d.ts.map +1 -0
  42. package/dist/providers/gemini-handler.js +149 -0
  43. package/dist/providers/gemini-handler.js.map +1 -0
  44. package/dist/providers/openai-handler.d.ts +3 -0
  45. package/dist/providers/openai-handler.d.ts.map +1 -0
  46. package/dist/providers/openai-handler.js +46 -0
  47. package/dist/providers/openai-handler.js.map +1 -0
  48. package/dist/providers/provider-detector.d.ts +4 -0
  49. package/dist/providers/provider-detector.d.ts.map +1 -0
  50. package/dist/providers/provider-detector.js +40 -0
  51. package/dist/providers/provider-detector.js.map +1 -0
  52. package/dist/sensitive-data-filter.d.ts +20 -0
  53. package/dist/sensitive-data-filter.d.ts.map +1 -0
  54. package/dist/sensitive-data-filter.js +280 -0
  55. package/dist/sensitive-data-filter.js.map +1 -0
  56. package/dist/trace-collector.d.ts +40 -0
  57. package/dist/trace-collector.d.ts.map +1 -0
  58. package/dist/trace-collector.js +59 -0
  59. package/dist/trace-collector.js.map +1 -0
  60. package/dist/tracer.d.ts +30 -7
  61. package/dist/tracer.d.ts.map +1 -1
  62. package/dist/tracer.js +76 -70
  63. package/dist/tracer.js.map +1 -1
  64. package/dist/types.d.ts +83 -7
  65. package/dist/types.d.ts.map +1 -1
  66. package/package.json +3 -17
@@ -1,12 +1,63 @@
1
- import * as api from '@opentelemetry/api';
1
+ import { SensitiveDataFilterUtil } from './sensitive-data-filter.js';
2
2
  import { createRequire } from 'module';
3
+ import { patchFetch } from './interceptors/fetch-interceptor.js';
4
+ import { patchNodeHttp } from './interceptors/node-http-interceptor.js';
5
+ import { patchAxios } from './interceptors/axios-interceptor.js';
6
+ import { patchGot } from './interceptors/got-interceptor.js';
7
+ import { detectProvider, resolveHandler } from './providers/provider-detector.js';
3
8
  const requireFunc = createRequire(import.meta.url);
4
9
  export class HttpInterceptor {
5
- constructor(tracer, provider, projectId) {
10
+ constructor(traceCollector, projectId, sensitiveDataFilter) {
6
11
  this.isIntercepting = false;
7
- this.tracer = tracer;
8
- this.provider = provider;
12
+ this.currentMetadata = {};
13
+ this.traceCollector = traceCollector;
9
14
  this.projectId = projectId;
15
+ this.sensitiveDataFilter = new SensitiveDataFilterUtil(sensitiveDataFilter || {
16
+ filterSensitiveData: true,
17
+ sensitiveDataPatterns: [],
18
+ redactionMode: 'mask',
19
+ });
20
+ }
21
+ setChatId(chatId) {
22
+ this.currentChatId = chatId;
23
+ }
24
+ clearChatId() {
25
+ this.currentChatId = undefined;
26
+ }
27
+ setMetadata(metadata) {
28
+ this.currentMetadata = { ...this.currentMetadata, ...metadata };
29
+ }
30
+ clearMetadata() {
31
+ this.currentMetadata = {};
32
+ }
33
+ createTrace(url, method, statusCode, duration, provider, requestHeaders, responseHeaders, tokenUsage, costBreakdown, model, requestBody, responseBody, error) {
34
+ return {
35
+ id: `${Date.now()}-${Math.random().toString(36).substring(7)}`,
36
+ projectId: this.projectId,
37
+ timestamp: Date.now(),
38
+ type: 'http_request',
39
+ duration,
40
+ url,
41
+ method,
42
+ statusCode,
43
+ provider,
44
+ requestHeaders,
45
+ responseHeaders,
46
+ tokenUsage,
47
+ costBreakdown,
48
+ model,
49
+ requestBody,
50
+ responseBody,
51
+ chatId: this.currentChatId,
52
+ metadata: Object.keys(this.currentMetadata).length > 0 ? { ...this.currentMetadata } : undefined,
53
+ error,
54
+ };
55
+ }
56
+ sanitizeRequestBody(body) {
57
+ return this.sensitiveDataFilter.sanitizeRequestBody(body);
58
+ }
59
+ sanitizeResponseBody(body) {
60
+ return this.sensitiveDataFilter.sanitizeResponseBody(body);
10
61
  }
11
62
  startIntercepting() {
12
63
  if (this.isIntercepting) {
@@ -14,7 +65,13 @@ export class HttpInterceptor {
14
65
  }
15
66
  this.isIntercepting = true;
16
67
  this.patchFetch();
17
- this.patchNodeHttp();
68
+ const enableNodeHttpEnv = typeof process !== 'undefined' ? process.env?.LUPIS_ENABLE_NODE_HTTP : undefined;
69
+ const shouldPatchNodeHttp = enableNodeHttpEnv === 'true' || (enableNodeHttpEnv !== 'false' && (typeof globalThis.fetch !== 'function' || enableNodeHttpEnv === undefined));
70
+ if (shouldPatchNodeHttp) {
71
+ this.patchNodeHttp();
72
+ }
73
+ this.patchAxios();
74
+ this.patchGot();
18
75
  }
19
76
  stopIntercepting() {
20
77
  if (!this.isIntercepting)
@@ -35,6 +92,22 @@ export class HttpInterceptor {
35
92
  }
36
93
  catch (e) {
37
94
  }
95
+ try {
96
+ const axios = requireFunc('axios');
97
+ if (this.originalAxiosRequest && axios.default) {
98
+ axios.default.request = this.originalAxiosRequest;
99
+ }
100
+ }
101
+ catch (e) {
102
+ }
103
+ try {
104
+ const got = requireFunc('got');
105
+ if (this.originalGotRequest && got.default) {
106
+ got.default = this.originalGotRequest;
107
+ }
108
+ }
109
+ catch (e) {
110
+ }
38
111
  }
39
112
  patchFetch() {
40
113
  try {
@@ -42,177 +115,21 @@ export class HttpInterceptor {
42
115
  return;
43
116
  }
44
117
  this.originalFetch = globalThis.fetch;
45
- const self = this;
46
- globalThis.fetch = async function (input, init) {
47
- const startTime = Date.now();
48
- const url = typeof input === 'string' ? input : input.toString();
49
- const method = init?.method || 'GET';
50
- const provider = self.detectProvider(url);
51
- const handler = self.resolveHandler(provider);
52
- if (url.includes('/v1/traces')) {
53
- return self.originalFetch(input, init);
54
- }
55
- const span = self.tracer.startSpan(`HTTP ${method}`, {
56
- kind: api.SpanKind.CLIENT,
57
- attributes: {
58
- 'http.method': method,
59
- 'http.url': url,
60
- 'url.full': url,
61
- 'http.provider': provider,
62
- 'lupis.project.id': self.projectId,
63
- 'lupis.complete': 'true',
64
- },
65
- });
66
- if (init?.body) {
67
- const requestBody = typeof init.body === 'string' ? init.body : JSON.stringify(init.body);
68
- span.setAttribute('http.request.body', requestBody);
69
- }
70
- if (init?.headers) {
71
- const headers = {};
72
- if (init.headers instanceof Headers) {
73
- init.headers.forEach((value, key) => {
74
- headers[key] = value;
75
- });
76
- }
77
- else if (Array.isArray(init.headers)) {
78
- init.headers.forEach(([key, value]) => {
79
- headers[key] = value;
80
- });
81
- }
82
- else {
83
- Object.assign(headers, init.headers);
84
- }
85
- span.setAttribute('http.request.headers', JSON.stringify(headers));
86
- }
87
- try {
88
- const response = await self.originalFetch(input, init);
89
- const duration = Date.now() - startTime;
90
- span.setAttribute('http.status_code', response.status);
91
- span.setAttribute('http.response.status_code', response.status);
92
- span.setAttribute('http.response.status', response.status);
93
- const responseHeaders = Object.fromEntries(response.headers.entries());
94
- span.setAttribute('http.response.headers', JSON.stringify(responseHeaders));
95
- // Try tee() first - if it works, we have a true streaming response
96
- if (response.body && typeof response.body.tee === 'function') {
97
- const bodyStream = response.body;
98
- const [clientStream, traceStream] = bodyStream.tee();
99
- const decoder = new TextDecoder();
100
- const chunks = [];
101
- let providerState = undefined;
102
- const trackStream = async () => {
103
- const reader = traceStream.getReader();
104
- try {
105
- while (true) {
106
- const { done, value } = await reader.read();
107
- if (done) {
108
- const remaining = decoder.decode();
109
- if (remaining) {
110
- chunks.push(remaining);
111
- }
112
- break;
113
- }
114
- if (value) {
115
- const chunkText = decoder.decode(value, { stream: true });
116
- if (chunkText) {
117
- chunks.push(chunkText);
118
- if (handler && handler.isStreamingChunk(chunkText)) {
119
- const { state, result } = handler.accumulateChunk(providerState, chunkText);
120
- providerState = state;
121
- if (result) {
122
- // no-op for now; we only send final aggregated trace to minimize noise
123
- }
124
- }
125
- }
126
- }
127
- }
128
- // Send only final aggregated trace
129
- let bodyText = chunks.join('');
130
- let normalized = bodyText;
131
- try {
132
- if (handler) {
133
- if (providerState && Array.isArray(providerState.__rawChunks)) {
134
- const data = providerState.__rawChunks;
135
- const aggregatedText = providerState.__aggregatedText;
136
- const toolCalls = providerState.__toolCalls;
137
- const usage = providerState.__usage;
138
- normalized = {
139
- type: 'streaming_response',
140
- provider,
141
- aggregatedText,
142
- data,
143
- toolCalls,
144
- usage,
145
- contentType: response.headers.get('content-type') || undefined,
146
- totalChunks: data.length,
147
- totalLength: data.reduce((n, s) => n + s.length, 0),
148
- isComplete: true,
149
- };
150
- span.setAttribute('http.response.body', JSON.stringify(normalized));
151
- }
152
- else {
153
- normalized = handler.normalizeFinal(bodyText);
154
- span.setAttribute('http.response.body', typeof normalized === 'string' ? normalized : JSON.stringify(normalized));
155
- }
156
- }
157
- else {
158
- span.setAttribute('http.response.body', bodyText);
159
- }
160
- }
161
- catch (e) {
162
- span.setAttribute('http.response.body', bodyText);
163
- }
164
- span.setStatus({ code: response.ok ? api.SpanStatusCode.OK : api.SpanStatusCode.ERROR });
165
- span.end();
166
- }
167
- catch (error) {
168
- span.recordException(error);
169
- span.setStatus({
170
- code: api.SpanStatusCode.ERROR,
171
- message: error.message
172
- });
173
- span.end();
174
- }
175
- };
176
- trackStream().catch(console.warn);
177
- return new Response(clientStream, {
178
- headers: response.headers,
179
- status: response.status,
180
- statusText: response.statusText,
181
- });
182
- }
183
- // For non-streaming responses or when tee() isn't available, handle as regular response
184
- if (response.body && typeof response.body.getReader === 'function') {
185
- const clone = response.clone();
186
- const responseText = await self.getRawResponseText(clone);
187
- let normalizedBody = responseText;
188
- try {
189
- if (handler) {
190
- normalizedBody = handler.normalizeFinal(responseText);
191
- }
192
- }
193
- catch (e) {
194
- normalizedBody = responseText;
195
- }
196
- span.setAttribute('http.response.body', typeof normalizedBody === 'string' ? normalizedBody : JSON.stringify(normalizedBody));
197
- span.setStatus({ code: response.ok ? api.SpanStatusCode.OK : api.SpanStatusCode.ERROR });
198
- span.end();
199
- return response;
200
- }
201
- span.setAttribute('http.response.body', '');
202
- span.setStatus({ code: response.ok ? api.SpanStatusCode.OK : api.SpanStatusCode.ERROR });
203
- span.end();
204
- return response;
205
- }
206
- catch (error) {
207
- span.recordException(error);
208
- span.setStatus({
209
- code: api.SpanStatusCode.ERROR,
210
- message: error.message
211
- });
212
- span.end();
213
- throw error;
214
- }
118
+ const context = {
119
+ originalFetch: this.originalFetch,
120
+ traceCollector: this.traceCollector,
121
+ projectId: this.projectId,
122
+ currentChatId: this.currentChatId,
123
+ currentMetadata: this.currentMetadata,
124
+ sensitiveDataFilter: this.sensitiveDataFilter,
125
+ createTrace: this.createTrace.bind(this),
126
+ detectProvider: detectProvider,
127
+ resolveHandler: resolveHandler,
128
+ getRawResponseText: this.getRawResponseText.bind(this),
129
+ sanitizeRequestBody: this.sanitizeRequestBody.bind(this),
130
+ sanitizeResponseBody: this.sanitizeResponseBody.bind(this),
215
131
  };
132
+ patchFetch(context);
216
133
  }
217
134
  catch (e) {
218
135
  console.warn('[Lupis SDK] Failed to patch fetch:', e);
@@ -223,169 +140,88 @@ export class HttpInterceptor {
223
140
  const http = requireFunc('http');
224
141
  const https = requireFunc('https');
225
142
  const zlib = requireFunc('zlib');
143
+ console.log('[Lupis SDK] Patching Node.js http/https modules');
226
144
  this.zlib = zlib;
227
145
  this.originalHttpRequest = http.request;
228
146
  this.originalHttpsRequest = https.request;
229
- const self = this;
230
- const createPatchedRequest = (original, protocol) => {
231
- return function (options, callback) {
232
- const url = typeof options === 'string' ? options :
233
- `${protocol}://${options.hostname || options.host || 'localhost'}${options.port ? ':' + options.port : ''}${options.path || '/'}`;
234
- const method = options.method || 'GET';
235
- const provider = self.detectProvider(url);
236
- const span = self.tracer.startSpan(`HTTP ${method}`, {
237
- kind: api.SpanKind.CLIENT,
238
- attributes: {
239
- 'http.method': method,
240
- 'http.url': url,
241
- 'url.full': url,
242
- 'http.provider': provider,
243
- 'lupis.project.id': self.projectId,
244
- 'lupis.complete': 'true',
245
- },
246
- });
247
- const startTime = Date.now();
248
- let requestBody = '';
249
- const req = original.call(this, options, (res) => {
250
- const duration = Date.now() - startTime;
251
- span.setAttribute('http.status_code', res.statusCode);
252
- span.setAttribute('http.response.status_code', res.statusCode);
253
- span.setAttribute('http.response.status', res.statusCode);
254
- const responseHeaders = {};
255
- Object.keys(res.headers || {}).forEach(key => {
256
- responseHeaders[key] = res.headers[key];
257
- });
258
- span.setAttribute('http.response.headers', JSON.stringify(responseHeaders));
259
- const responseChunks = [];
260
- const originalOn = res.on.bind(res);
261
- const providerHandler = self.resolveHandler(provider);
262
- const contentType = res.headers['content-type'] || '';
263
- const isStreaming = contentType.includes('text/event-stream');
264
- let streamState = undefined;
265
- res.on = function (event, handler) {
266
- if (event === 'data') {
267
- return originalOn(event, (chunk) => {
268
- responseChunks.push(Buffer.from(chunk));
269
- if (isStreaming && providerHandler) {
270
- const chunkText = chunk.toString('utf8');
271
- if (providerHandler.isStreamingChunk(chunkText)) {
272
- const { state } = providerHandler.accumulateChunk(streamState, chunkText);
273
- streamState = state;
274
- }
275
- }
276
- return handler(chunk);
277
- });
278
- }
279
- if (event === 'end') {
280
- return originalOn(event, () => {
281
- if (responseChunks.length > 0) {
282
- try {
283
- const buffer = Buffer.concat(responseChunks);
284
- const contentEncoding = res.headers['content-encoding'];
285
- const decompressed = self.decompressIfNeeded(buffer, contentEncoding);
286
- const limitedText = decompressed.length > 1000000
287
- ? decompressed.substring(0, 1000000) + '...[truncated]'
288
- : decompressed;
289
- let normalizedBody = limitedText;
290
- if (providerHandler) {
291
- if (isStreaming && streamState && Array.isArray(streamState.__rawChunks)) {
292
- normalizedBody = {
293
- type: 'streaming_response',
294
- provider,
295
- aggregatedText: streamState.__aggregatedText,
296
- data: streamState.__rawChunks,
297
- toolCalls: streamState.__toolCalls,
298
- usage: streamState.__usage,
299
- contentType,
300
- totalChunks: streamState.__rawChunks.length,
301
- totalLength: streamState.__rawChunks.reduce((n, s) => n + s.length, 0),
302
- isComplete: true,
303
- };
304
- }
305
- else {
306
- normalizedBody = providerHandler.normalizeFinal(limitedText);
307
- }
308
- }
309
- span.setAttribute('http.response.body', typeof normalizedBody === 'string' ? normalizedBody : JSON.stringify(normalizedBody));
310
- }
311
- catch (e) {
312
- span.setAttribute('http.response.body', `[Unable to process response: ${e instanceof Error ? e.message : String(e)}]`);
313
- }
314
- }
315
- span.setStatus({
316
- code: res.statusCode >= 200 && res.statusCode < 400 ?
317
- api.SpanStatusCode.OK : api.SpanStatusCode.ERROR
318
- });
319
- span.end();
320
- return handler();
321
- });
322
- }
323
- return originalOn(event, handler);
324
- };
325
- if (callback) {
326
- callback(res);
327
- }
328
- });
329
- const originalWrite = req.write.bind(req);
330
- req.write = function (chunk, encoding, callback) {
331
- if (chunk) {
332
- requestBody += chunk.toString();
333
- }
334
- return originalWrite(chunk, encoding, callback);
335
- };
336
- const originalEnd = req.end.bind(req);
337
- req.end = function (chunk, encoding, callback) {
338
- if (chunk) {
339
- requestBody += chunk.toString();
340
- }
341
- if (requestBody) {
342
- span.setAttribute('http.request.body', requestBody);
343
- }
344
- if (options.headers) {
345
- span.setAttribute('http.request.headers', JSON.stringify(options.headers));
346
- }
347
- return originalEnd(chunk, encoding, callback);
348
- };
349
- req.on('error', (error) => {
350
- span.recordException(error);
351
- span.setStatus({
352
- code: api.SpanStatusCode.ERROR,
353
- message: error.message
354
- });
355
- span.end();
356
- });
357
- return req;
358
- };
147
+ const context = {
148
+ originalHttpRequest: this.originalHttpRequest,
149
+ originalHttpsRequest: this.originalHttpsRequest,
150
+ zlib: this.zlib,
151
+ traceCollector: this.traceCollector,
152
+ projectId: this.projectId,
153
+ currentChatId: this.currentChatId,
154
+ currentMetadata: this.currentMetadata,
155
+ sensitiveDataFilter: this.sensitiveDataFilter,
156
+ createTrace: this.createTrace.bind(this),
157
+ detectProvider: detectProvider,
158
+ resolveHandler: resolveHandler,
159
+ decompressIfNeeded: this.decompressIfNeeded.bind(this),
160
+ requireFunc: requireFunc,
161
+ sanitizeRequestBody: this.sanitizeRequestBody.bind(this),
162
+ sanitizeResponseBody: this.sanitizeResponseBody.bind(this),
359
163
  };
360
- http.request = createPatchedRequest(this.originalHttpRequest, 'http');
361
- https.request = createPatchedRequest(this.originalHttpsRequest, 'https');
164
+ console.log('[Lupis SDK] Patching Node.js http/https modules with context:', context);
165
+ patchNodeHttp(context);
362
166
  }
363
167
  catch (e) {
364
168
  console.warn('[Lupis SDK] Failed to patch Node.js http/https:', e);
365
169
  }
366
170
  }
367
- detectProvider(url) {
368
- if (url.includes('api.openai.com'))
369
- return 'openai';
370
- if (url.includes('api.anthropic.com'))
371
- return 'claude';
372
- if (url.includes('api.cohere.ai'))
373
- return 'cohere';
374
- if (url.includes('api.huggingface.co'))
375
- return 'huggingface';
376
- if (url.includes('api.google.com'))
377
- return 'google';
378
- return 'unknown';
171
+ patchAxios() {
172
+ try {
173
+ const axios = requireFunc('axios');
174
+ if (!axios || (!axios.default && !axios.request)) {
175
+ return;
176
+ }
177
+ const axiosInstance = axios.default || axios;
178
+ if (!axiosInstance.request) {
179
+ return;
180
+ }
181
+ this.originalAxiosRequest = axiosInstance.request;
182
+ const context = {
183
+ originalAxiosRequest: this.originalAxiosRequest,
184
+ traceCollector: this.traceCollector,
185
+ projectId: this.projectId,
186
+ currentChatId: this.currentChatId,
187
+ currentMetadata: this.currentMetadata,
188
+ sensitiveDataFilter: this.sensitiveDataFilter,
189
+ createTrace: this.createTrace.bind(this),
190
+ detectProvider: detectProvider,
191
+ resolveHandler: resolveHandler,
192
+ requireFunc: requireFunc,
193
+ sanitizeRequestBody: this.sanitizeRequestBody.bind(this),
194
+ sanitizeResponseBody: this.sanitizeResponseBody.bind(this),
195
+ };
196
+ patchAxios(context);
197
+ }
198
+ catch (e) {
199
+ }
379
200
  }
380
- resolveHandler(provider) {
381
- const p = provider;
382
- switch (p) {
383
- case 'openai':
384
- return OpenAIHandler;
385
- case 'claude':
386
- return AnthropicHandler;
387
- default:
388
- return undefined;
201
+ patchGot() {
202
+ try {
203
+ const got = requireFunc('got');
204
+ if (!got.default) {
205
+ return;
206
+ }
207
+ this.originalGotRequest = got.default;
208
+ const context = {
209
+ originalGotRequest: this.originalGotRequest,
210
+ traceCollector: this.traceCollector,
211
+ projectId: this.projectId,
212
+ currentChatId: this.currentChatId,
213
+ currentMetadata: this.currentMetadata,
214
+ sensitiveDataFilter: this.sensitiveDataFilter,
215
+ createTrace: this.createTrace.bind(this),
216
+ detectProvider: detectProvider,
217
+ resolveHandler: resolveHandler,
218
+ requireFunc: requireFunc,
219
+ sanitizeRequestBody: this.sanitizeRequestBody.bind(this),
220
+ sanitizeResponseBody: this.sanitizeResponseBody.bind(this),
221
+ };
222
+ patchGot(context);
223
+ }
224
+ catch (e) {
389
225
  }
390
226
  }
391
227
  async getRawResponseText(response) {
@@ -426,92 +262,4 @@ export class HttpInterceptor {
426
262
  }
427
263
  }
428
264
  }
429
- const OpenAIHandler = {
430
- provider: 'openai',
431
- detect: (url) => url.includes('api.openai.com'),
432
- isStreamingChunk: (textChunk) => textChunk.includes('\ndata: '),
433
- accumulateChunk: (state, textChunk) => {
434
- const next = state || { __rawChunks: [], __aggregatedText: '', __toolCalls: [], __usage: null };
435
- next.__rawChunks.push(textChunk);
436
- const lines = textChunk.split('\n');
437
- for (const line of lines) {
438
- if (!line.startsWith('data: '))
439
- continue;
440
- const payload = line.substring(6).trim();
441
- if (payload === '[DONE]')
442
- continue;
443
- try {
444
- const json = JSON.parse(payload);
445
- if (Array.isArray(json.choices)) {
446
- for (const choice of json.choices) {
447
- if (choice.delta?.content)
448
- next.__aggregatedText += choice.delta.content;
449
- if (Array.isArray(choice.delta?.tool_calls))
450
- next.__toolCalls.push(...choice.delta.tool_calls);
451
- }
452
- }
453
- if (json?.usage) {
454
- next.__usage = json.usage;
455
- }
456
- }
457
- catch { }
458
- }
459
- return { state: next };
460
- },
461
- normalizeFinal: (rawBodyText) => {
462
- try {
463
- const parsed = JSON.parse(rawBodyText);
464
- const aggregatedText = parsed.choices?.[0]?.message?.content || '';
465
- return { ...parsed, aggregatedText };
466
- }
467
- catch {
468
- return rawBodyText;
469
- }
470
- },
471
- };
472
- const AnthropicHandler = {
473
- provider: 'claude',
474
- detect: (url) => url.includes('api.anthropic.com'),
475
- isStreamingChunk: (textChunk) => textChunk.includes('\nevent: ') || textChunk.includes('\ndata: '),
476
- accumulateChunk: (state, textChunk) => {
477
- const next = state || { __rawChunks: [], __aggregatedText: '', __toolCalls: [], __usage: null };
478
- next.__rawChunks.push(textChunk);
479
- const lines = textChunk.split('\n');
480
- for (const line of lines) {
481
- if (!line.startsWith('data: '))
482
- continue;
483
- try {
484
- const json = JSON.parse(line.substring(6));
485
- if (json?.delta?.text)
486
- next.__aggregatedText += json.delta.text;
487
- if (json?.type === 'tool_use') {
488
- next.__toolCalls.push({
489
- id: json.id,
490
- name: json.name,
491
- type: 'function',
492
- function: { name: json.name, arguments: JSON.stringify(json.input || {}) },
493
- });
494
- }
495
- if (json?.usage) {
496
- next.__usage = json.usage;
497
- }
498
- if (json?.type === 'message_delta' && json?.usage) {
499
- next.__usage = json.usage;
500
- }
501
- }
502
- catch { }
503
- }
504
- return { state: next };
505
- },
506
- normalizeFinal: (rawBodyText) => {
507
- try {
508
- const parsed = JSON.parse(rawBodyText);
509
- const aggregatedText = parsed.content?.[0]?.text || '';
510
- return { ...parsed, aggregatedText };
511
- }
512
- catch {
513
- return rawBodyText;
514
- }
515
- },
516
- };
517
265
  //# sourceMappingURL=http-interceptor.js.map