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.
- package/README.md +227 -358
- package/dist/cost-utils.d.ts +5 -0
- package/dist/cost-utils.d.ts.map +1 -0
- package/dist/cost-utils.js +55 -0
- package/dist/cost-utils.js.map +1 -0
- package/dist/endpoints.d.ts +2 -0
- package/dist/endpoints.d.ts.map +1 -0
- package/dist/endpoints.js +2 -0
- package/dist/endpoints.js.map +1 -0
- package/dist/http-interceptor.d.ts +18 -8
- package/dist/http-interceptor.d.ts.map +1 -1
- package/dist/http-interceptor.js +164 -416
- package/dist/http-interceptor.js.map +1 -1
- package/dist/http-types.d.ts +1 -1
- package/dist/http-types.d.ts.map +1 -1
- package/dist/index.d.ts +36 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +174 -9
- package/dist/index.js.map +1 -1
- package/dist/interceptors/axios-interceptor.d.ts +18 -0
- package/dist/interceptors/axios-interceptor.d.ts.map +1 -0
- package/dist/interceptors/axios-interceptor.js +115 -0
- package/dist/interceptors/axios-interceptor.js.map +1 -0
- package/dist/interceptors/fetch-interceptor.d.ts +18 -0
- package/dist/interceptors/fetch-interceptor.d.ts.map +1 -0
- package/dist/interceptors/fetch-interceptor.js +228 -0
- package/dist/interceptors/fetch-interceptor.js.map +1 -0
- package/dist/interceptors/got-interceptor.d.ts +18 -0
- package/dist/interceptors/got-interceptor.d.ts.map +1 -0
- package/dist/interceptors/got-interceptor.js +103 -0
- package/dist/interceptors/got-interceptor.js.map +1 -0
- package/dist/interceptors/node-http-interceptor.d.ts +21 -0
- package/dist/interceptors/node-http-interceptor.d.ts.map +1 -0
- package/dist/interceptors/node-http-interceptor.js +301 -0
- package/dist/interceptors/node-http-interceptor.js.map +1 -0
- package/dist/providers/anthropic-handler.d.ts +3 -0
- package/dist/providers/anthropic-handler.d.ts.map +1 -0
- package/dist/providers/anthropic-handler.js +50 -0
- package/dist/providers/anthropic-handler.js.map +1 -0
- package/dist/providers/gemini-handler.d.ts +3 -0
- package/dist/providers/gemini-handler.d.ts.map +1 -0
- package/dist/providers/gemini-handler.js +149 -0
- package/dist/providers/gemini-handler.js.map +1 -0
- package/dist/providers/openai-handler.d.ts +3 -0
- package/dist/providers/openai-handler.d.ts.map +1 -0
- package/dist/providers/openai-handler.js +46 -0
- package/dist/providers/openai-handler.js.map +1 -0
- package/dist/providers/provider-detector.d.ts +4 -0
- package/dist/providers/provider-detector.d.ts.map +1 -0
- package/dist/providers/provider-detector.js +40 -0
- package/dist/providers/provider-detector.js.map +1 -0
- package/dist/sensitive-data-filter.d.ts +20 -0
- package/dist/sensitive-data-filter.d.ts.map +1 -0
- package/dist/sensitive-data-filter.js +280 -0
- package/dist/sensitive-data-filter.js.map +1 -0
- package/dist/trace-collector.d.ts +40 -0
- package/dist/trace-collector.d.ts.map +1 -0
- package/dist/trace-collector.js +59 -0
- package/dist/trace-collector.js.map +1 -0
- package/dist/tracer.d.ts +30 -7
- package/dist/tracer.d.ts.map +1 -1
- package/dist/tracer.js +76 -70
- package/dist/tracer.js.map +1 -1
- package/dist/types.d.ts +83 -7
- package/dist/types.d.ts.map +1 -1
- 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
|
-
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
JavaScript SDK for LupisLabs with OpenTelemetry tracing and custom event tracking.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
## Installation
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install lupislabs
|
|
9
|
+
```
|
|
9
10
|
|
|
10
|
-
|
|
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
|
-
|
|
13
|
+
If you only need Lupis for local debugging, install it as a dev dependency:
|
|
20
14
|
|
|
21
15
|
```bash
|
|
22
|
-
npm install
|
|
16
|
+
npm install --save-dev lupislabs
|
|
23
17
|
```
|
|
24
18
|
|
|
25
|
-
|
|
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 '
|
|
31
|
+
import LupisSDK from 'lupislabs';
|
|
29
32
|
|
|
30
|
-
const
|
|
33
|
+
const lupis = LupisSDK.init({
|
|
31
34
|
projectId: 'your-project-id',
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
46
|
+
const lupis = LupisSDK.getInstance();
|
|
47
|
+
if (lupis?.isEnabled()) {
|
|
48
|
+
lupis.trackEvent('feature_used', { feature: 'export_data' });
|
|
49
|
+
}
|
|
50
|
+
```
|
|
72
51
|
|
|
73
|
-
|
|
52
|
+
## Checking the active state
|
|
74
53
|
|
|
75
|
-
|
|
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
|
-
|
|
56
|
+
```typescript
|
|
57
|
+
const lupis = LupisSDK.init({ projectId: 'your-project-id' });
|
|
81
58
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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;
|
|
115
|
-
enabled?: boolean;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
+
- Traces → `http://127.0.0.1:9009/api/traces`
|
|
132
89
|
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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
|
-
|
|
98
|
+
// Or set per-operation chat ID
|
|
99
|
+
lupis.run(async () => {
|
|
100
|
+
// Your AI conversation code here
|
|
101
|
+
}, { chatId: 'conversation_123' });
|
|
172
102
|
|
|
173
|
-
|
|
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
|
-
|
|
106
|
+
## Metadata Types
|
|
182
107
|
|
|
183
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
###
|
|
119
|
+
### Example Usage
|
|
233
120
|
|
|
234
121
|
```javascript
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
//
|
|
244
|
-
|
|
245
|
-
```
|
|
129
|
+
// Start a new conversation
|
|
130
|
+
lupis.setChatId('conversation_1');
|
|
246
131
|
|
|
247
|
-
|
|
132
|
+
await lupis.run(async () => {
|
|
133
|
+
// AI conversation code
|
|
134
|
+
}, { chatId: 'conversation_1' });
|
|
248
135
|
|
|
249
|
-
|
|
136
|
+
// Start another conversation (same session, different chat)
|
|
137
|
+
lupis.setChatId('conversation_2');
|
|
250
138
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
144
|
+
## OpenTelemetry Integration
|
|
145
|
+
|
|
146
|
+
The SDK automatically instruments HTTP requests and creates traces. Access the tracer:
|
|
259
147
|
|
|
260
148
|
```javascript
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
149
|
+
const tracer = lupis.getTracer();
|
|
150
|
+
|
|
151
|
+
const span = lupis.createSpan('custom-operation', {
|
|
152
|
+
'custom.attribute': 'value',
|
|
264
153
|
});
|
|
265
|
-
```
|
|
266
154
|
|
|
267
|
-
|
|
155
|
+
// Your code here
|
|
268
156
|
|
|
269
|
-
|
|
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
|
-
|
|
160
|
+
## Data Collection
|
|
277
161
|
|
|
278
|
-
|
|
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
|
-
|
|
164
|
+
### ✅ **Collected Data**
|
|
286
165
|
|
|
287
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
180
|
+
### 🔒 **Privacy Protection**
|
|
311
181
|
|
|
312
|
-
|
|
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
|
-
|
|
315
|
-
- `http.url` - Full URL
|
|
316
|
-
- `http.status_code` - Response status code
|
|
187
|
+
## Sensitive Data Filtering
|
|
317
188
|
|
|
318
|
-
**
|
|
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
|
-
|
|
321
|
-
- `lupis.chat.id` - Conversation ID (when set)
|
|
322
|
-
- `http.provider` - Detected provider (openai, claude, cohere, huggingface, google)
|
|
191
|
+
### Default Filtering
|
|
323
192
|
|
|
324
|
-
|
|
193
|
+
The SDK automatically filters these common sensitive patterns:
|
|
325
194
|
|
|
326
|
-
|
|
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
|
-
|
|
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
|
-
|
|
203
|
+
#### Authentication
|
|
335
204
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
|
|
209
|
+
#### Personal Data
|
|
353
210
|
|
|
354
|
-
|
|
211
|
+
- `ssn`, `social_security` - Social Security Numbers
|
|
212
|
+
- `credit_card`, `card_number` - Credit card numbers
|
|
213
|
+
- `cvv`, `cvc` - Security codes
|
|
355
214
|
|
|
356
|
-
|
|
215
|
+
### Redaction Modes
|
|
357
216
|
|
|
358
|
-
|
|
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
|
-
####
|
|
219
|
+
#### Mask Mode (Default)
|
|
365
220
|
|
|
366
|
-
|
|
221
|
+
```javascript
|
|
222
|
+
const lupis = LupisSDK.init({
|
|
223
|
+
projectId: 'your-project-id',
|
|
224
|
+
redactionMode: 'mask', // Default
|
|
225
|
+
});
|
|
367
226
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
//
|
|
371
|
-
|
|
227
|
+
// Examples:
|
|
228
|
+
// sk-1234567890abcdef1234567890abcdef12345678 → sk-1***5678
|
|
229
|
+
// Bearer sk-1234567890abcdef1234567890abcdef12345678 → Bear***5678
|
|
230
|
+
// password: 'secret-password' → password: '***'
|
|
372
231
|
```
|
|
373
232
|
|
|
374
|
-
####
|
|
233
|
+
#### Remove Mode
|
|
375
234
|
|
|
376
|
-
|
|
235
|
+
```javascript
|
|
236
|
+
const lupis = LupisSDK.init({
|
|
237
|
+
projectId: 'your-project-id',
|
|
238
|
+
redactionMode: 'remove',
|
|
239
|
+
});
|
|
377
240
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
241
|
+
// Examples:
|
|
242
|
+
// sk-1234567890abcdef1234567890abcdef12345678 → [REDACTED]
|
|
243
|
+
// password: 'secret-password' → password: [REDACTED]
|
|
381
244
|
```
|
|
382
245
|
|
|
383
|
-
####
|
|
246
|
+
#### Hash Mode
|
|
384
247
|
|
|
385
|
-
|
|
248
|
+
```javascript
|
|
249
|
+
const lupis = LupisSDK.init({
|
|
250
|
+
projectId: 'your-project-id',
|
|
251
|
+
redactionMode: 'hash',
|
|
252
|
+
});
|
|
386
253
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}, SpanKind.INTERNAL);
|
|
254
|
+
// Examples:
|
|
255
|
+
// sk-1234567890abcdef1234567890abcdef12345678 → [HASH:2dd0e9d5]
|
|
256
|
+
// password: 'secret-password' → password: [HASHED]
|
|
391
257
|
```
|
|
392
258
|
|
|
393
|
-
|
|
259
|
+
### Custom Patterns
|
|
394
260
|
|
|
395
|
-
|
|
261
|
+
Add your own sensitive data patterns during initialization:
|
|
396
262
|
|
|
397
|
-
```
|
|
398
|
-
|
|
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
|
-
|
|
278
|
+
### What Gets Filtered
|
|
402
279
|
|
|
403
|
-
The SDK
|
|
280
|
+
The SDK filters sensitive data in:
|
|
404
281
|
|
|
405
|
-
|
|
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
|
-
|
|
285
|
+
**Note**: Request and response bodies are never collected, so no filtering is needed for them.
|
|
415
286
|
|
|
416
|
-
###
|
|
287
|
+
### Disable Filtering (Development Only)
|
|
417
288
|
|
|
418
|
-
|
|
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
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
###
|
|
298
|
+
### Production Security
|
|
438
299
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
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
|
-
|
|
457
|
-
- [OPENTELEMETRY.md](./OPENTELEMETRY.md) - OpenTelemetry integration guide
|
|
458
|
-
- [examples/](./examples/) - Code examples
|
|
310
|
+
## Testing
|
|
459
311
|
|
|
460
|
-
|
|
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
|
-
|
|
325
|
+
### Test Coverage
|
|
463
326
|
|
|
464
|
-
|
|
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
|
-
|
|
333
|
+
## Examples
|
|
467
334
|
|
|
468
|
-
|
|
335
|
+
See the `examples/` directory for more usage examples:
|
|
469
336
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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**
|