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
package/README.md CHANGED
@@ -1,476 +1,345 @@
1
- # Lupis JavaScript SDK
1
+ # Lupis Labs JavaScript SDK
2
2
 
3
- [![npm version](https://badge.fury.io/js/%40lupislabs%2Fjs-sdk.svg)](https://badge.fury.io/js/%40lupislabs%2Fjs-sdk)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
+ JavaScript SDK for LupisLabs with OpenTelemetry tracing and custom event tracking.
5
4
 
6
- A pure OpenTelemetry-based SDK for tracing HTTP requests in JavaScript applications with automatic instrumentation.
5
+ ## Installation
7
6
 
8
- ## ✨ Features
7
+ ```bash
8
+ npm install lupislabs
9
+ ```
9
10
 
10
- - 🔭 **OpenTelemetry Native** - Uses official OpenTelemetry auto-instrumentation
11
- - 🚀 **Zero Manual Patching** - Automatic HTTP/fetch interception
12
- - 📊 **OTLP Export** - Standard protocol for any observability backend
13
- - 💬 **Conversation Grouping** - Group related requests with chatId
14
- - 🎯 **Smart Provider Detection** - Automatically detects AI providers (OpenAI, Claude, etc.)
15
- - 📝 **Full Request/Response Capture** - Automatically captures request & response bodies, headers, and status (extends OpenTelemetry's capabilities)
16
- - 🔄 **No Conflicts** - Works alongside other OpenTelemetry instrumentation
17
- - ⚡ **Lightweight** - Clean, minimal codebase
11
+ ### Development-only install
18
12
 
19
- ## 📦 Installation
13
+ If you only need Lupis for local debugging, install it as a dev dependency:
20
14
 
21
15
  ```bash
22
- npm install @lupislabs/js-sdk
16
+ npm install --save-dev lupislabs
23
17
  ```
24
18
 
25
- ## 🚀 Quick Start
19
+ When the SDK is not explicitly enabled it stays idle, so keeping it in `devDependencies` will not impact your production runtime.
20
+
21
+ ## Features
22
+
23
+ - 🔍 **Automatic HTTP Tracing**: Captures AI API calls automatically
24
+ - 💬 **Chat ID Support**: Group traces by conversation/chat ID
25
+ - 🎯 **TypeScript Support**: Full TypeScript definitions included
26
+ - 🔒 **Privacy-First**: Never collects request/response bodies, only analytics data
27
+
28
+ ## Quick Start
26
29
 
27
30
  ```javascript
28
- import LupisSDK from '@lupislabs/js-sdk';
31
+ import LupisSDK from 'lupislabs';
29
32
 
30
- const sdk = new LupisSDK({
33
+ const lupis = LupisSDK.init({
31
34
  projectId: 'your-project-id',
32
- otlpEndpoint: 'http://localhost:3010/v1/traces',
35
+ enabled: true, // Explicitly enable the SDK
33
36
  });
34
37
 
35
- await sdk.run(async () => {
36
- const response = await fetch('https://api.openai.com/v1/chat/completions', {
37
- method: 'POST',
38
- headers: {
39
- 'Authorization': `Bearer ${API_KEY}`,
40
- 'Content-Type': 'application/json',
41
- },
42
- body: JSON.stringify({
43
- model: 'gpt-4',
44
- messages: [{ role: 'user', content: 'Hello!' }],
45
- }),
46
- });
47
-
48
- return await response.json();
49
- }, { chatId: 'conversation-123' });
50
38
  ```
51
39
 
52
- ## 📝 Request & Response Capture
40
+ The SDK caches the configured instance globally. Subsequent calls to `LupisSDK.init(...)` return the same instance, and you can fetch it anywhere with `LupisSDK.getInstance()`.
53
41
 
54
- Unlike standard OpenTelemetry implementations that only capture metadata, Lupis SDK automatically captures full request and response bodies, headers, and status codes. This is achieved through a custom `ResponseCaptureProcessor` that extends OpenTelemetry's capabilities.
55
-
56
- ### What Gets Captured
57
-
58
- **Request Data:**
59
-
60
- - Request body (JSON, text, etc.)
61
- - Request headers
62
- - HTTP method and URL
63
-
64
- **Response Data:**
65
-
66
- - Response body (full content)
67
- - Response headers
68
- - HTTP status code
69
- - Response timing
42
+ ```typescript
43
+ // In another module
44
+ import LupisSDK from 'lupislabs';
70
45
 
71
- ### How It Works
46
+ const lupis = LupisSDK.getInstance();
47
+ if (lupis?.isEnabled()) {
48
+ lupis.trackEvent('feature_used', { feature: 'export_data' });
49
+ }
50
+ ```
72
51
 
73
- The SDK uses a custom span processor that:
52
+ ## Checking the active state
74
53
 
75
- 1. Intercepts the fetch API at the earliest point
76
- 2. Captures request data before the request is sent
77
- 3. Clones and reads the response body without affecting the original response
78
- 4. Attaches all data to the OpenTelemetry span as attributes
54
+ You can verify whether the SDK activated:
79
55
 
80
- ### Example
56
+ ```typescript
57
+ const lupis = LupisSDK.init({ projectId: 'your-project-id' });
81
58
 
82
- ```javascript
83
- await sdk.run(async () => {
84
- const response = await fetch('https://api.openai.com/v1/chat/completions', {
85
- method: 'POST',
86
- headers: {
87
- 'Content-Type': 'application/json',
88
- 'Authorization': `Bearer ${API_KEY}`,
89
- },
90
- body: JSON.stringify({
91
- model: 'gpt-4',
92
- messages: [{ role: 'user', content: 'Hello!' }],
93
- }),
94
- });
95
-
96
- const data = await response.json();
97
- }, { chatId: 'my-conversation' });
59
+ if (!lupis.isEnabled()) {
60
+ console.log('Lupis SDK is currently disabled.');
61
+ }
98
62
  ```
99
63
 
100
- The span will automatically include:
101
-
102
- - `http.request.body` - The full request payload
103
- - `http.request.headers` - All request headers (as JSON string)
104
- - `http.response.body` - The complete response body
105
- - `http.response.headers` - All response headers (as JSON string)
106
- - `http.response.status` - HTTP status code (e.g., 200, 404, 500)
107
-
108
- See `examples/response-capture-example.js` for more detailed examples.
109
-
110
- ## 📖 Configuration
64
+ ## Configuration
111
65
 
112
66
  ```typescript
113
67
  interface LupisConfig {
114
- projectId: string; // Required: Your project identifier
115
- enabled?: boolean; // Default: true
116
- otlpEndpoint?: string; // Default: 'http://localhost:4318/v1/traces'
117
- serviceName?: string; // Default: 'lupis-sdk'
118
- serviceVersion?: string; // Default: '1.0.0'
68
+ projectId: string;
69
+ enabled?: boolean;
70
+ serviceName?: string;
71
+ serviceVersion?: string;
72
+ filterSensitiveData?: boolean;
73
+ sensitiveDataPatterns?: string[];
74
+ redactionMode?: 'mask' | 'remove' | 'hash';
119
75
  }
120
-
121
- const sdk = new LupisSDK(config);
122
76
  ```
123
77
 
124
- ## 🔭 OpenTelemetry Integration
125
-
126
- This SDK uses **pure OpenTelemetry** with automatic instrumentation:
78
+ - `projectId` (required): Your project identifier
79
+ - `enabled` (optional): Enable/disable tracking. When omitted, the SDK checks `LUPIS_SDK_ENABLED` first, then enables automatically when `NODE_ENV` is defined and not `'production'`. Without those hints it stays disabled (ideal for devDependency usage).
80
+ - `serviceName` (optional): Service name for traces (default: 'lupis-sdk')
81
+ - `serviceVersion` (optional): Service version (default: '1.0.0')
82
+ - `filterSensitiveData` (optional): Enable sensitive data filtering (default: true)
83
+ - `sensitiveDataPatterns` (optional): Custom regex patterns to filter (default: common API keys, tokens, etc.)
84
+ - `redactionMode` (optional): How to redact sensitive data: 'mask', 'remove', or 'hash' (default: 'mask')
127
85
 
128
- - **Browser**: `@opentelemetry/instrumentation-fetch` auto-instruments `fetch()`
129
- - **Node.js**: `@opentelemetry/instrumentation-http` auto-instruments `http` and `https`
86
+ All telemetry is sent to `http://127.0.0.1:9009` by default:
130
87
 
131
- ### Features
88
+ - Traces → `http://127.0.0.1:9009/api/traces`
132
89
 
133
- **Automatic span creation** for all HTTP requests
134
- ✅ **Semantic conventions** (http.method, http.url, http.status_code)
135
- ✅ **W3C Trace Context** propagation
136
- ✅ **OTLP export** to any backend (Jaeger, Tempo, Datadog, etc.)
137
- ✅ **Custom attributes** (projectId, chatId, provider)
90
+ ## Conversation Grouping
138
91
 
139
- ### Architecture
140
-
141
- ```
142
- HTTP Request (fetch/http)
143
-
144
- Custom Fetch Patch (captures request/response data)
145
-
146
- OpenTelemetry Auto-Instrumentation
147
- ├─ FetchInstrumentation (browser)
148
- └─ HttpInstrumentation (Node.js)
149
-
150
- Span Created with Attributes
151
-
152
- TracerProvider
153
- ├─ ChatIdSpanProcessor (adds chatId)
154
- ├─ ResponseCaptureProcessor (adds request/response bodies)
155
- └─ BatchSpanProcessor + OTLPExporter
156
-
157
- Observability Backend
158
- ```
159
-
160
- ## 📋 Usage Examples
161
-
162
- ### Basic HTTP Tracing
92
+ Group traces by conversation/thread using `chatId`:
163
93
 
164
94
  ```javascript
165
- await sdk.run(async () => {
166
- const response = await fetch('https://api.example.com/data');
167
- return await response.json();
168
- }, { chatId: 'my-conversation' });
169
- ```
95
+ // Set global chat ID for all subsequent traces
96
+ lupis.setChatId('conversation_123');
170
97
 
171
- ### Multiple Requests
98
+ // Or set per-operation chat ID
99
+ lupis.run(async () => {
100
+ // Your AI conversation code here
101
+ }, { chatId: 'conversation_123' });
172
102
 
173
- ```javascript
174
- await sdk.run(async () => {
175
- const user = await fetch('/api/user').then(r => r.json());
176
- const posts = await fetch('/api/posts').then(r => r.json());
177
- return { user, posts };
178
- }, { chatId: 'user-session-123' });
103
+ lupis.clearChatId();
179
104
  ```
180
105
 
181
- ### AI Provider Requests
106
+ ## Metadata Types
182
107
 
183
- ```javascript
184
- // OpenAI
185
- await sdk.run(async () => {
186
- const response = await fetch('https://api.openai.com/v1/chat/completions', {
187
- method: 'POST',
188
- headers: { 'Authorization': `Bearer ${API_KEY}` },
189
- body: JSON.stringify({
190
- model: 'gpt-4',
191
- messages: [{ role: 'user', content: 'Hello!' }],
192
- }),
193
- });
194
- }, { chatId: 'openai-conversation' });
195
-
196
- // Claude
197
- await sdk.run(async () => {
198
- const response = await fetch('https://api.anthropic.com/v1/messages', {
199
- method: 'POST',
200
- headers: { 'x-api-key': API_KEY },
201
- body: JSON.stringify({
202
- model: 'claude-3-sonnet-20240229',
203
- messages: [{ role: 'user', content: 'Hello!' }],
204
- }),
205
- });
206
- }, { chatId: 'claude-conversation' });
207
- ```
208
-
209
- ### Custom Spans
108
+ ### sessionId vs chatId
210
109
 
211
- ```javascript
212
- import { otel } from '@lupislabs/js-sdk';
213
-
214
- const span = sdk.createSpan('data-processing', {
215
- 'processing.type': 'batch',
216
- 'batch.size': 100,
217
- }, otel.SpanKind.INTERNAL);
218
-
219
- try {
220
- // Your processing logic
221
- span.end();
222
- } catch (error) {
223
- span.recordException(error);
224
- span.setStatus({
225
- code: otel.SpanStatusCode.ERROR,
226
- message: error.message
227
- });
228
- span.end();
229
- }
230
- ```
110
+ - **`sessionId`**: Browser/app session identifier that persists across conversations
111
+ - Used for analytics and user journey tracking
112
+ - Example: `'browser_session_abc123'`
113
+
114
+ - **`chatId`**: Individual conversation/thread identifier
115
+ - Used for grouping related traces within a conversation
116
+ - Changes for each new conversation
117
+ - Example: `'chat_thread_xyz789'`
231
118
 
232
- ### Using OpenTelemetry API Directly
119
+ ### Example Usage
233
120
 
234
121
  ```javascript
235
- const tracer = sdk.getTracer();
236
-
237
- const span = tracer.startSpan('custom-operation', {
238
- attributes: {
239
- 'operation.type': 'ai-inference',
240
- },
122
+ // Set user context (persists across conversations)
123
+ lupis.setMetadata({
124
+ userId: 'user_123',
125
+ organizationId: 'org_456',
126
+ sessionId: 'browser_session_abc123', // Same across conversations
241
127
  });
242
128
 
243
- // Your code
244
- span.end();
245
- ```
129
+ // Start a new conversation
130
+ lupis.setChatId('conversation_1');
246
131
 
247
- ## 🔌 Export to Observability Backends
132
+ await lupis.run(async () => {
133
+ // AI conversation code
134
+ }, { chatId: 'conversation_1' });
248
135
 
249
- ### Jaeger
136
+ // Start another conversation (same session, different chat)
137
+ lupis.setChatId('conversation_2');
250
138
 
251
- ```javascript
252
- const sdk = new LupisSDK({
253
- projectId: 'my-project',
254
- otlpEndpoint: 'http://localhost:4318/v1/traces',
255
- });
139
+ await lupis.run(async () => {
140
+ // Another AI conversation code
141
+ }, { chatId: 'conversation_2' });
256
142
  ```
257
143
 
258
- ### Grafana Tempo
144
+ ## OpenTelemetry Integration
145
+
146
+ The SDK automatically instruments HTTP requests and creates traces. Access the tracer:
259
147
 
260
148
  ```javascript
261
- const sdk = new LupisSDK({
262
- projectId: 'my-project',
263
- otlpEndpoint: 'https://tempo.example.com/v1/traces',
149
+ const tracer = lupis.getTracer();
150
+
151
+ const span = lupis.createSpan('custom-operation', {
152
+ 'custom.attribute': 'value',
264
153
  });
265
- ```
266
154
 
267
- ### Datadog
155
+ // Your code here
268
156
 
269
- ```javascript
270
- const sdk = new LupisSDK({
271
- projectId: 'my-project',
272
- otlpEndpoint: 'https://http-intake.logs.datadoghq.com/v1/traces',
273
- });
157
+ span.end();
274
158
  ```
275
159
 
276
- ### New Relic
160
+ ## Data Collection
277
161
 
278
- ```javascript
279
- const sdk = new LupisSDK({
280
- projectId: 'my-project',
281
- otlpEndpoint: 'https://otlp.nr-data.net/v1/traces',
282
- });
283
- ```
162
+ The SDK collects only analytics-focused data while protecting sensitive information:
284
163
 
285
- ## 💬 Conversation Grouping
164
+ ### **Collected Data**
286
165
 
287
- Group related requests with `chatId`:
166
+ - **HTTP Metadata**: URL, method, status code, duration, headers (filtered)
167
+ - **Token Usage**: Input/output/cache tokens from AI providers
168
+ - **Cost Analytics**: Calculated costs based on token usage and provider pricing
169
+ - **Model Information**: AI model used for requests
170
+ - **User Context**: User ID, organization ID, session ID, chat ID
171
+ - **Performance Metrics**: Response times, error rates, success/failure status
288
172
 
289
- ```javascript
290
- // All requests in this block will have the same chatId
291
- await sdk.run(async () => {
292
- await fetch('/api/chat', {
293
- method: 'POST',
294
- body: JSON.stringify({ message: 'Hello' })
295
- });
296
- await fetch('/api/chat', {
297
- method: 'POST',
298
- body: JSON.stringify({ message: 'How are you?' })
299
- });
300
- }, { chatId: 'conversation-123' });
301
-
302
- // Or set/clear chatId manually
303
- sdk.setChatId('conversation-123');
304
- // Make requests...
305
- sdk.clearChatId();
306
- ```
173
+ ### ❌ **Never Collected**
307
174
 
308
- ## 🏷️ Span Attributes
175
+ - **Request Bodies**: Full request payloads are never captured
176
+ - **Response Bodies**: Full response content is never captured
177
+ - **Sensitive Data**: API keys, tokens, passwords (filtered by default)
178
+ - **Personal Information**: PII is not collected by default
309
179
 
310
- All HTTP spans automatically include:
180
+ ### 🔒 **Privacy Protection**
311
181
 
312
- **Standard OpenTelemetry:**
182
+ - Sensitive data filtering enabled by default
183
+ - Request/response bodies skipped to reduce span size
184
+ - Focus on analytics and cost tracking only
185
+ - User-controlled data collection
313
186
 
314
- - `http.method` - HTTP method (GET, POST, etc.)
315
- - `http.url` - Full URL
316
- - `http.status_code` - Response status code
187
+ ## Sensitive Data Filtering
317
188
 
318
- **Custom Lupis Attributes:**
189
+ The SDK automatically filters sensitive data in production to protect API keys, tokens, and other sensitive information. This feature is **enabled by default** for security.
319
190
 
320
- - `lupis.project.id` - Your project ID
321
- - `lupis.chat.id` - Conversation ID (when set)
322
- - `http.provider` - Detected provider (openai, claude, cohere, huggingface, google)
191
+ ### Default Filtering
323
192
 
324
- **Request/Response Capture Attributes (Custom Extension):**
193
+ The SDK automatically filters these common sensitive patterns:
325
194
 
326
- - `http.request.body` - Full request body content
327
- - `http.request.headers` - Request headers as JSON string
328
- - `http.response.body` - Full response body content
329
- - `http.response.headers` - Response headers as JSON string
330
- - `http.response.status` - HTTP response status code
195
+ #### API Keys & Tokens
331
196
 
332
- ## 🔧 API Reference
197
+ - `sk-[a-zA-Z0-9]{20,}` - OpenAI API keys
198
+ - `pk_[a-zA-Z0-9]{20,}` - Paddle API keys
199
+ - `ak-[a-zA-Z0-9]{20,}` - Anthropic API keys
200
+ - `Bearer [a-zA-Z0-9._-]+` - Bearer tokens
201
+ - `x-api-key`, `authorization` - API key headers
333
202
 
334
- ### LupisSDK
203
+ #### Authentication
335
204
 
336
- ```typescript
337
- class LupisSDK {
338
- constructor(config: LupisConfig)
339
-
340
- async run<T>(fn: () => Promise<T> | T, options?: { chatId?: string }): Promise<T>
341
-
342
- setChatId(chatId: string): void
343
- clearChatId(): void
344
-
345
- getTracer(): Tracer
346
- createSpan(name: string, attributes?: Attributes, spanKind?: SpanKind): Span
347
-
348
- async shutdown(): Promise<void>
349
- }
350
- ```
205
+ - `password`, `passwd`, `pwd` - Password fields
206
+ - `token`, `access_token`, `refresh_token`, `session_token` - Various tokens
207
+ - `secret`, `private_key`, `api_secret` - Secret fields
351
208
 
352
- ### Methods
209
+ #### Personal Data
353
210
 
354
- #### `sdk.run(fn, options)`
211
+ - `ssn`, `social_security` - Social Security Numbers
212
+ - `credit_card`, `card_number` - Credit card numbers
213
+ - `cvv`, `cvc` - Security codes
355
214
 
356
- Execute a function with automatic HTTP tracing:
215
+ ### Redaction Modes
357
216
 
358
- ```typescript
359
- await sdk.run(async () => {
360
- // Your code
361
- }, { chatId: 'optional-chat-id' });
362
- ```
217
+ Choose how sensitive data is replaced when you initialize the SDK:
363
218
 
364
- #### `sdk.setChatId(chatId)` / `sdk.clearChatId()`
219
+ #### Mask Mode (Default)
365
220
 
366
- Manually set/clear the chatId for subsequent requests:
221
+ ```javascript
222
+ const lupis = LupisSDK.init({
223
+ projectId: 'your-project-id',
224
+ redactionMode: 'mask', // Default
225
+ });
367
226
 
368
- ```typescript
369
- sdk.setChatId('conversation-123');
370
- // All requests will have this chatId
371
- sdk.clearChatId();
227
+ // Examples:
228
+ // sk-1234567890abcdef1234567890abcdef12345678 → sk-1***5678
229
+ // Bearer sk-1234567890abcdef1234567890abcdef12345678 Bear***5678
230
+ // password: 'secret-password' → password: '***'
372
231
  ```
373
232
 
374
- #### `sdk.getTracer()`
233
+ #### Remove Mode
375
234
 
376
- Get the OpenTelemetry tracer for advanced usage:
235
+ ```javascript
236
+ const lupis = LupisSDK.init({
237
+ projectId: 'your-project-id',
238
+ redactionMode: 'remove',
239
+ });
377
240
 
378
- ```typescript
379
- const tracer = sdk.getTracer();
380
- const span = tracer.startSpan('my-operation');
241
+ // Examples:
242
+ // sk-1234567890abcdef1234567890abcdef12345678 [REDACTED]
243
+ // password: 'secret-password' → password: [REDACTED]
381
244
  ```
382
245
 
383
- #### `sdk.createSpan(name, attributes, spanKind)`
246
+ #### Hash Mode
384
247
 
385
- Create a custom span:
248
+ ```javascript
249
+ const lupis = LupisSDK.init({
250
+ projectId: 'your-project-id',
251
+ redactionMode: 'hash',
252
+ });
386
253
 
387
- ```typescript
388
- const span = sdk.createSpan('operation-name', {
389
- 'custom.attribute': 'value',
390
- }, SpanKind.INTERNAL);
254
+ // Examples:
255
+ // sk-1234567890abcdef1234567890abcdef12345678 [HASH:2dd0e9d5]
256
+ // password: 'secret-password' → password: [HASHED]
391
257
  ```
392
258
 
393
- #### `sdk.shutdown()`
259
+ ### Custom Patterns
394
260
 
395
- Gracefully shutdown and flush all spans:
261
+ Add your own sensitive data patterns during initialization:
396
262
 
397
- ```typescript
398
- await sdk.shutdown();
263
+ ```javascript
264
+ const lupis = LupisSDK.init({
265
+ projectId: 'your-project-id',
266
+ filterSensitiveData: true,
267
+ sensitiveDataPatterns: [
268
+ 'sk-[a-zA-Z0-9]{20,}', // OpenAI API keys
269
+ 'Bearer [a-zA-Z0-9._-]+', // Bearer tokens
270
+ 'custom_secret', // Your custom field
271
+ 'my_api_key', // Your custom field
272
+ 'email', // Email addresses
273
+ ],
274
+ redactionMode: 'mask',
275
+ });
399
276
  ```
400
277
 
401
- ## 🎯 Provider Detection
278
+ ### What Gets Filtered
402
279
 
403
- The SDK automatically detects AI providers based on URL patterns:
280
+ The SDK filters sensitive data in:
404
281
 
405
- | Provider | URL Pattern | Attribute Value |
406
- |----------|-------------|-----------------|
407
- | OpenAI | `api.openai.com` | `openai` |
408
- | Anthropic (Claude) | `api.anthropic.com` | `claude` |
409
- | Cohere | `api.cohere.ai` | `cohere` |
410
- | HuggingFace | `api.huggingface.co` | `huggingface` |
411
- | Google AI | `generativelanguage.googleapis.com` | `google` |
412
- | Others | - | `unknown` |
282
+ - **Request Headers**: Authorization, API keys, tokens
283
+ - **Span Attributes**: All OpenTelemetry span attributes
413
284
 
414
- ## ⚙️ Best Practices
285
+ **Note**: Request and response bodies are never collected, so no filtering is needed for them.
415
286
 
416
- ### 1. Meaningful ChatIds
287
+ ### Disable Filtering (Development Only)
417
288
 
418
- ```javascript
419
- // Good: Descriptive and unique
420
- { chatId: 'user-login-flow-2024-03-15' }
421
- { chatId: 'document-analysis-task-456' }
422
-
423
- // Avoid: Generic or unclear
424
- { chatId: 'test' }
425
- { chatId: '1' }
426
- ```
427
-
428
- ### 2. Always Shutdown
289
+ ⚠️ **Warning**: Only disable filtering in development environments:
429
290
 
430
291
  ```javascript
431
- process.on('SIGTERM', async () => {
432
- await sdk.shutdown();
433
- process.exit(0);
292
+ const lupis = LupisSDK.init({
293
+ projectId: 'your-project-id',
294
+ filterSensitiveData: false, // ⚠️ Sensitive data will be exposed!
434
295
  });
435
296
  ```
436
297
 
437
- ### 3. Error Handling
298
+ ### Production Security
438
299
 
439
- ```javascript
440
- await sdk.run(async () => {
441
- try {
442
- const response = await fetch('/api/data');
443
- if (!response.ok) {
444
- throw new Error(`HTTP error! status: ${response.status}`);
445
- }
446
- return await response.json();
447
- } catch (error) {
448
- console.error('Request failed:', error);
449
- throw error;
450
- }
451
- }, { chatId: 'error-handling-example' });
452
- ```
300
+ - ✅ **Enabled by default** - No configuration needed
301
+ - **Comprehensive coverage** - Common sensitive patterns included
302
+ - ✅ **Configurable** - Add custom patterns as needed
303
+ - **Performance optimized** - Minimal impact when enabled
304
+ - **Debugging friendly** - Mask mode preserves partial data for debugging
305
+
306
+ ## Shutdown
453
307
 
454
- ## 📚 Documentation
308
+ The SDK automatically flushes buffered telemetry on an interval and registers Node.js signal hooks so traces are shipped during normal shutdown—no manual shutdown call is required for normal apps. Only call `await lupis.shutdown()` when you need to force an immediate flush (for example, in short-lived scripts or tests).
455
309
 
456
- - [ARCHITECTURE.md](./ARCHITECTURE.md) - Detailed architecture
457
- - [OPENTELEMETRY.md](./OPENTELEMETRY.md) - OpenTelemetry integration guide
458
- - [examples/](./examples/) - Code examples
310
+ ## Testing
459
311
 
460
- ## 🤝 Contributing
312
+ The SDK includes comprehensive tests for sensitive data filtering:
313
+
314
+ ```bash
315
+ # Run unit tests
316
+ npm run test:sensitive:unit
317
+
318
+ # Run integration tests
319
+ npm run test:sensitive:detailed
320
+
321
+ # Run all tests
322
+ npm run test:all
323
+ ```
461
324
 
462
- Contributions are welcome! Please feel free to submit a Pull Request.
325
+ ### Test Coverage
463
326
 
464
- ## 📄 License
327
+ - **Unit Tests**: Filter utility with all redaction modes
328
+ - ✅ **Integration Tests**: SDK with HTTP interception
329
+ - ✅ **Custom Patterns**: User-defined sensitive data patterns
330
+ - ✅ **Disabled Filtering**: Development mode verification
331
+ - ✅ **Request/Response**: Headers, bodies, and span attributes
465
332
 
466
- This project is licensed under the MIT License.
333
+ ## Examples
467
334
 
468
- ## 🆘 Support
335
+ See the `examples/` directory for more usage examples:
469
336
 
470
- If you encounter any issues:
471
- 1. Check the [Issues](https://github.com/lupislabs/lupis/issues) page
472
- 2. Create a new issue with detailed information
337
+ - `event-tracking-example.js` - Custom event tracking
338
+ - `anthropic-example.js` - Anthropic API integration
339
+ - `openai-example.js` - OpenAI API integration
340
+ - `langchain-example.js` - LangChain integration
341
+ - `streaming-example.js` - Streaming responses
473
342
 
474
- ---
343
+ ## License
475
344
 
476
345
  **Made with ❤️ by the Lupis team**