@dropout-ai/runtime 0.4.5 â 0.5.1
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/package.json +1 -1
- package/src/index.js +138 -293
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @dropout-ai/runtime
|
|
3
3
|
* Universal AI Interaction Capture for Node.js
|
|
4
|
-
* Stability:
|
|
5
|
-
* Features: Auto-Discovery, Connectivity Check, Dual-Schema Support
|
|
4
|
+
* Stability: Production (Async-Safe, Zero Config)
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
let https, http, crypto;
|
|
8
|
+
const { AsyncLocalStorage } = require('async_hooks');
|
|
9
9
|
|
|
10
10
|
// --- DEFAULT CONFIGURATION ---
|
|
11
|
-
const SUPABASE_FUNCTION_URL =
|
|
11
|
+
const SUPABASE_FUNCTION_URL =
|
|
12
|
+
"https://hipughmjlwmwjxzyxfzs.supabase.co/functions/v1/capture-sealed";
|
|
12
13
|
|
|
13
14
|
// Known AI Domains
|
|
14
15
|
const KNOWN_AI_DOMAINS = [
|
|
15
|
-
'api.openai.com',
|
|
16
|
-
'
|
|
16
|
+
'api.openai.com',
|
|
17
|
+
'api.anthropic.com',
|
|
18
|
+
'generativelanguage.googleapis.com',
|
|
19
|
+
'aiplatform.googleapis.com',
|
|
20
|
+
'api.groq.com',
|
|
21
|
+
'api.mistral.ai',
|
|
22
|
+
'api.cohere.ai'
|
|
17
23
|
];
|
|
18
24
|
|
|
19
25
|
class Dropout {
|
|
20
|
-
// Singleton Instance Storage
|
|
21
26
|
static instance = null;
|
|
22
27
|
|
|
23
|
-
/**
|
|
24
|
-
* Universal Initialization Method (Idempotent)
|
|
25
|
-
* Users call this explicitly in their code.
|
|
26
|
-
*/
|
|
27
28
|
static init(config) {
|
|
28
29
|
if (!Dropout.instance) {
|
|
29
30
|
new Dropout(config);
|
|
@@ -32,87 +33,49 @@ class Dropout {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
constructor(config = {}) {
|
|
35
|
-
|
|
36
|
-
if (Dropout.instance) {
|
|
37
|
-
if (config.debug) console.log("[Dropout] âšī¸ Already initialized. Returning existing instance.");
|
|
38
|
-
return Dropout.instance;
|
|
39
|
-
}
|
|
36
|
+
if (Dropout.instance) return Dropout.instance;
|
|
40
37
|
|
|
41
|
-
// đĄī¸
|
|
42
|
-
if (typeof window !== 'undefined'
|
|
43
|
-
if (config.debug) console.warn("[Dropout] â ī¸ Skipped: Browser detected. SDK is Server-Only.");
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
38
|
+
// đĄī¸ Browser Guard
|
|
39
|
+
if (typeof window !== 'undefined') return;
|
|
46
40
|
|
|
47
|
-
// đĄī¸
|
|
41
|
+
// đĄī¸ Credentials Guard
|
|
48
42
|
if (!config.apiKey || !config.projectId) {
|
|
49
43
|
if (config.debug) {
|
|
50
|
-
console.warn("
|
|
44
|
+
console.warn("[Dropout] đ´ Missing API Key or Project ID.");
|
|
51
45
|
}
|
|
52
46
|
return;
|
|
53
47
|
}
|
|
54
48
|
|
|
55
|
-
// đĄī¸ 3. DEPENDENCY CHECK
|
|
56
49
|
try {
|
|
57
50
|
https = require('https');
|
|
58
51
|
http = require('http');
|
|
59
52
|
crypto = require('crypto');
|
|
60
|
-
} catch
|
|
61
|
-
if (config.debug) console.warn("[Dropout] â ī¸ Skipped: Node core modules missing.");
|
|
53
|
+
} catch {
|
|
62
54
|
return;
|
|
63
55
|
}
|
|
64
56
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.privacy = config.privacy || 'full';
|
|
72
|
-
this.captureEndpoint = config.captureEndpoint || SUPABASE_FUNCTION_URL;
|
|
73
|
-
this.maxOutputBytes = 32768;
|
|
74
|
-
|
|
75
|
-
// State
|
|
76
|
-
this.turnIndex = 0;
|
|
77
|
-
this.lastTurnConfirmed = true;
|
|
78
|
-
this.lastPromptHash = null;
|
|
79
|
-
this.lastResponseHash = null;
|
|
80
|
-
|
|
81
|
-
// Global Guard (Extra safety for HMR environments like Next.js)
|
|
82
|
-
if (global.__dropout_initialized__) {
|
|
83
|
-
if (this.debug) console.log("[Dropout] âšī¸ Global flag found. Skipping re-initialization.");
|
|
84
|
-
Dropout.instance = this; // Re-bind if lost
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
global.__dropout_initialized__ = true;
|
|
88
|
-
|
|
89
|
-
// â
Session Identity (Instance-Level, Not Global)
|
|
90
|
-
// Accept session_id from config OR generate a default
|
|
91
|
-
// Host app controls session lifecycle via startNewSession()
|
|
92
|
-
this.currentSessionId = config.sessionId || this.generateSessionId();
|
|
93
|
-
|
|
94
|
-
// â
VISUAL SUCCESS INDICATOR
|
|
95
|
-
if (this.debug) {
|
|
96
|
-
console.log('\x1b[32m%s\x1b[0m', `[Dropout] đĸ Online | Project: ${this.projectId}`);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Bindings
|
|
100
|
-
this.log = this.log.bind(this);
|
|
101
|
-
this.emit = this.emit.bind(this);
|
|
57
|
+
this.projectId = config.projectId;
|
|
58
|
+
this.apiKey = config.apiKey;
|
|
59
|
+
this.debug = config.debug || false;
|
|
60
|
+
this.privacy = config.privacy || 'full';
|
|
61
|
+
this.captureEndpoint = config.captureEndpoint || SUPABASE_FUNCTION_URL;
|
|
62
|
+
this.maxOutputBytes = 32768;
|
|
102
63
|
|
|
103
|
-
|
|
104
|
-
|
|
64
|
+
// â
ASYNC CONTEXT STORE (THE FIX)
|
|
65
|
+
this.ctx = new AsyncLocalStorage();
|
|
105
66
|
|
|
106
|
-
|
|
107
|
-
Dropout.instance = this;
|
|
67
|
+
this.patchNetwork();
|
|
108
68
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (config.debug) console.error("[Dropout] â Initialization Error:", err);
|
|
69
|
+
if (this.debug) {
|
|
70
|
+
console.log(`[Dropout] đĸ Online | Project: ${this.projectId}`);
|
|
112
71
|
}
|
|
72
|
+
|
|
73
|
+
Dropout.instance = this;
|
|
113
74
|
}
|
|
114
75
|
|
|
115
|
-
//
|
|
76
|
+
// -------------------------
|
|
77
|
+
// Utilities
|
|
78
|
+
// -------------------------
|
|
116
79
|
log(msg, ...args) {
|
|
117
80
|
if (this.debug) console.log(`[Dropout] ${msg}`, ...args);
|
|
118
81
|
}
|
|
@@ -120,315 +83,198 @@ class Dropout {
|
|
|
120
83
|
generateSessionId() {
|
|
121
84
|
try {
|
|
122
85
|
return crypto.randomUUID();
|
|
123
|
-
} catch
|
|
124
|
-
return 'sess_' +
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* đ START NEW SESSION
|
|
130
|
-
* Call this when user clicks "New Chat" or starts a new conversation thread.
|
|
131
|
-
* Resets turn counter and generates a fresh session_id.
|
|
132
|
-
* @param {string} [customSessionId] - Optional custom session ID, otherwise auto-generates
|
|
133
|
-
* @returns {string} The new session ID
|
|
134
|
-
*/
|
|
135
|
-
startNewSession(customSessionId) {
|
|
136
|
-
this.currentSessionId = customSessionId || this.generateSessionId();
|
|
137
|
-
this.turnIndex = 0; // Reset turn counter for new session
|
|
138
|
-
this.lastTurnConfirmed = true;
|
|
139
|
-
this.lastPromptHash = null;
|
|
140
|
-
this.lastResponseHash = null;
|
|
141
|
-
if (this.debug) {
|
|
142
|
-
console.log(`[Dropout] đ New Session Started: ${this.currentSessionId}`);
|
|
86
|
+
} catch {
|
|
87
|
+
return 'sess_' + Date.now().toString(36);
|
|
143
88
|
}
|
|
144
|
-
return this.currentSessionId;
|
|
145
89
|
}
|
|
146
90
|
|
|
147
91
|
hash(text) {
|
|
148
92
|
if (!text) return null;
|
|
149
93
|
try {
|
|
150
|
-
return crypto.createHash('sha256')
|
|
151
|
-
|
|
94
|
+
return crypto.createHash('sha256')
|
|
95
|
+
.update(text.toLowerCase().trim())
|
|
96
|
+
.digest('hex');
|
|
97
|
+
} catch {
|
|
98
|
+
return 'hash_err';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// -------------------------
|
|
103
|
+
// Session Context Helpers
|
|
104
|
+
// -------------------------
|
|
105
|
+
getSession() {
|
|
106
|
+
let store = this.ctx.getStore();
|
|
107
|
+
|
|
108
|
+
// đ AUTO-CREATE SESSION IF MISSING
|
|
109
|
+
if (!store) {
|
|
110
|
+
store = {
|
|
111
|
+
session_id: this.generateSessionId(),
|
|
112
|
+
turn_index: 0,
|
|
113
|
+
last_prompt_hash: null,
|
|
114
|
+
last_response_hash: null,
|
|
115
|
+
last_turn_confirmed: true
|
|
116
|
+
};
|
|
117
|
+
this.ctx.enterWith(store);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return store;
|
|
152
121
|
}
|
|
153
122
|
|
|
123
|
+
// -------------------------
|
|
124
|
+
// AI Detection
|
|
125
|
+
// -------------------------
|
|
154
126
|
normalize(url, body) {
|
|
155
127
|
let provider = 'custom';
|
|
156
128
|
let model = 'unknown';
|
|
129
|
+
|
|
157
130
|
try {
|
|
158
131
|
if (url) {
|
|
159
132
|
const u = url.toLowerCase();
|
|
160
133
|
if (u.includes('openai')) provider = 'openai';
|
|
161
134
|
else if (u.includes('anthropic')) provider = 'anthropic';
|
|
162
|
-
else if (u.includes('google')
|
|
135
|
+
else if (u.includes('google')) provider = 'google';
|
|
163
136
|
else if (u.includes('groq')) provider = 'groq';
|
|
164
137
|
else if (u.includes('mistral')) provider = 'mistral';
|
|
165
138
|
else if (u.includes('cohere')) provider = 'cohere';
|
|
166
|
-
else if (u.includes('localhost') || u.includes('127.0.0.1')) provider = 'local';
|
|
167
139
|
}
|
|
140
|
+
|
|
168
141
|
if (body) {
|
|
169
142
|
const parsed = typeof body === 'string' ? JSON.parse(body) : body;
|
|
170
|
-
if (parsed
|
|
171
|
-
if (provider === 'custom' && (parsed.messages || parsed.prompt)) provider = 'heuristic';
|
|
143
|
+
if (parsed?.model) model = parsed.model;
|
|
172
144
|
}
|
|
173
|
-
} catch
|
|
145
|
+
} catch { }
|
|
146
|
+
|
|
174
147
|
return { provider, model };
|
|
175
148
|
}
|
|
176
149
|
|
|
177
|
-
isAiRequest(url,
|
|
150
|
+
isAiRequest(url, body) {
|
|
178
151
|
if (!url) return false;
|
|
179
152
|
if (url.includes(this.captureEndpoint)) return false;
|
|
180
153
|
if (KNOWN_AI_DOMAINS.some(d => url.includes(d))) return true;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
154
|
+
|
|
155
|
+
if (body) {
|
|
156
|
+
return (
|
|
157
|
+
body.includes('"model"') &&
|
|
158
|
+
(body.includes('"messages"') || body.includes('"prompt"'))
|
|
159
|
+
);
|
|
184
160
|
}
|
|
161
|
+
|
|
185
162
|
return false;
|
|
186
163
|
}
|
|
187
164
|
|
|
188
|
-
//
|
|
165
|
+
// -------------------------
|
|
166
|
+
// Emit
|
|
167
|
+
// -------------------------
|
|
189
168
|
emit(payload) {
|
|
190
169
|
try {
|
|
191
|
-
|
|
170
|
+
const req = (this.captureEndpoint.startsWith('http:') ? http : https)
|
|
171
|
+
.request(this.captureEndpoint, {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
headers: {
|
|
174
|
+
'Content-Type': 'application/json',
|
|
175
|
+
'x-dropout-key': this.apiKey
|
|
176
|
+
}
|
|
177
|
+
});
|
|
192
178
|
|
|
193
|
-
|
|
179
|
+
req.on('error', () => { });
|
|
180
|
+
req.write(JSON.stringify({
|
|
194
181
|
project_id: this.projectId,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// â
DIRECTION LOGIC APPLIED HERE
|
|
199
|
-
direction: payload.turn_role === 'user' ? 'user_to_ai' : 'ai_to_user',
|
|
200
|
-
provider: payload.provider,
|
|
201
|
-
model: payload.model,
|
|
202
|
-
latency_ms: payload.latency_ms || null,
|
|
203
|
-
content: payload.content,
|
|
204
|
-
content_hash: payload.content_hash,
|
|
205
|
-
metadata_flags: payload.metadata_flags,
|
|
206
|
-
received_at: new Date().toISOString()
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
this.log(`đ Sending Capture [${payload.turn_role}]`);
|
|
210
|
-
|
|
211
|
-
const isHttp = this.captureEndpoint.startsWith('http:');
|
|
212
|
-
const requestModule = isHttp ? http : https;
|
|
213
|
-
|
|
214
|
-
const req = requestModule.request(this.captureEndpoint, {
|
|
215
|
-
method: 'POST',
|
|
216
|
-
headers: {
|
|
217
|
-
'Content-Type': 'application/json',
|
|
218
|
-
'x-dropout-key': this.apiKey
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
req.on('error', (e) => this.log("â Upload Failed (Silent)", e.message));
|
|
223
|
-
req.write(JSON.stringify(finalPayload));
|
|
182
|
+
received_at: new Date().toISOString(),
|
|
183
|
+
...payload
|
|
184
|
+
}));
|
|
224
185
|
req.end();
|
|
225
|
-
} catch
|
|
226
|
-
this.log("â ī¸ Emit Error (Silent)", e);
|
|
227
|
-
}
|
|
186
|
+
} catch { }
|
|
228
187
|
}
|
|
229
188
|
|
|
230
|
-
//
|
|
189
|
+
// -------------------------
|
|
190
|
+
// Network Patching
|
|
191
|
+
// -------------------------
|
|
231
192
|
patchNetwork() {
|
|
232
193
|
const _this = this;
|
|
233
194
|
|
|
234
|
-
//
|
|
195
|
+
// ---- FETCH ----
|
|
235
196
|
if (global.fetch) {
|
|
236
197
|
const originalFetch = global.fetch;
|
|
198
|
+
|
|
237
199
|
global.fetch = async function (input, init) {
|
|
238
200
|
let url;
|
|
239
|
-
try { url = typeof input === 'string' ? input : input?.url; }
|
|
201
|
+
try { url = typeof input === 'string' ? input : input?.url; }
|
|
202
|
+
catch { return originalFetch.apply(this, arguments); }
|
|
240
203
|
|
|
241
|
-
let bodyStr =
|
|
242
|
-
if (init
|
|
243
|
-
try { bodyStr = typeof init.body === 'string' ? init.body : JSON.stringify(init.body); }
|
|
204
|
+
let bodyStr = '';
|
|
205
|
+
if (init?.body) {
|
|
206
|
+
try { bodyStr = typeof init.body === 'string' ? init.body : JSON.stringify(init.body); }
|
|
207
|
+
catch { }
|
|
244
208
|
}
|
|
245
209
|
|
|
246
210
|
if (!_this.isAiRequest(url, bodyStr)) {
|
|
247
211
|
return originalFetch.apply(this, arguments);
|
|
248
212
|
}
|
|
249
213
|
|
|
250
|
-
_this.
|
|
214
|
+
const session = _this.getSession();
|
|
251
215
|
const start = Date.now();
|
|
252
|
-
|
|
253
|
-
let activeTurn;
|
|
254
|
-
if (_this.lastTurnConfirmed) {
|
|
255
|
-
activeTurn = _this.turnIndex++;
|
|
256
|
-
_this.lastTurnConfirmed = false;
|
|
257
|
-
} else {
|
|
258
|
-
activeTurn = _this.turnIndex > 0 ? _this.turnIndex - 1 : 0;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
216
|
const { provider, model } = _this.normalize(url, bodyStr);
|
|
262
217
|
const pHash = _this.hash(bodyStr);
|
|
263
218
|
|
|
219
|
+
const turn =
|
|
220
|
+
session.last_turn_confirmed
|
|
221
|
+
? session.turn_index++
|
|
222
|
+
: Math.max(session.turn_index - 1, 0);
|
|
223
|
+
|
|
224
|
+
session.last_turn_confirmed = false;
|
|
225
|
+
|
|
264
226
|
_this.emit({
|
|
265
|
-
session_id:
|
|
266
|
-
turn_index:
|
|
227
|
+
session_id: session.session_id,
|
|
228
|
+
turn_index: turn,
|
|
267
229
|
turn_role: 'user',
|
|
230
|
+
direction: 'user_to_ai',
|
|
268
231
|
provider,
|
|
269
232
|
model,
|
|
270
233
|
content: _this.privacy === 'full' ? bodyStr : null,
|
|
271
234
|
content_hash: pHash,
|
|
272
|
-
metadata_flags: {
|
|
235
|
+
metadata_flags: {
|
|
236
|
+
retry_like: pHash === session.last_prompt_hash ? 1 : 0
|
|
237
|
+
}
|
|
273
238
|
});
|
|
274
|
-
_this.lastPromptHash = pHash;
|
|
275
239
|
|
|
276
|
-
|
|
277
|
-
try {
|
|
278
|
-
response = await originalFetch.apply(this, arguments);
|
|
279
|
-
} catch (err) { throw err; }
|
|
240
|
+
session.last_prompt_hash = pHash;
|
|
280
241
|
|
|
242
|
+
const response = await originalFetch.apply(this, arguments);
|
|
281
243
|
const latency = Date.now() - start;
|
|
282
244
|
|
|
283
245
|
try {
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
if (oText && oText.length > _this.maxOutputBytes) oText = oText.slice(0, _this.maxOutputBytes);
|
|
287
|
-
const oHash = _this.hash(oText);
|
|
246
|
+
const text = await response.clone().text();
|
|
247
|
+
const oHash = _this.hash(text);
|
|
288
248
|
|
|
289
249
|
_this.emit({
|
|
290
|
-
session_id:
|
|
291
|
-
turn_index:
|
|
250
|
+
session_id: session.session_id,
|
|
251
|
+
turn_index: turn,
|
|
292
252
|
turn_role: 'assistant',
|
|
293
|
-
|
|
253
|
+
direction: 'ai_to_user',
|
|
294
254
|
provider,
|
|
295
255
|
model,
|
|
296
|
-
|
|
256
|
+
latency_ms: latency,
|
|
257
|
+
content: _this.privacy === 'full' ? text.slice(0, _this.maxOutputBytes) : null,
|
|
297
258
|
content_hash: oHash,
|
|
298
259
|
metadata_flags: {
|
|
299
|
-
non_adaptive_response: oHash ===
|
|
260
|
+
non_adaptive_response: oHash === session.last_response_hash ? 1 : 0,
|
|
300
261
|
turn_boundary_confirmed: 1,
|
|
301
262
|
status: response.status
|
|
302
263
|
}
|
|
303
264
|
});
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
265
|
+
|
|
266
|
+
session.last_response_hash = oHash;
|
|
267
|
+
session.last_turn_confirmed = true;
|
|
268
|
+
} catch { }
|
|
307
269
|
|
|
308
270
|
return response;
|
|
309
271
|
};
|
|
310
|
-
this.log("â
Patch Applied: global.fetch");
|
|
311
272
|
}
|
|
312
|
-
|
|
313
|
-
// --- B. PATCH NODE HTTP/HTTPS ---
|
|
314
|
-
const patchNodeRequest = (module, moduleName) => {
|
|
315
|
-
const originalRequest = module.request;
|
|
316
|
-
module.request = function (...args) {
|
|
317
|
-
let url;
|
|
318
|
-
try {
|
|
319
|
-
if (typeof args[0] === 'string') {
|
|
320
|
-
url = args[0];
|
|
321
|
-
} else {
|
|
322
|
-
const opts = args[0] || {};
|
|
323
|
-
const protocol = opts.protocol || (moduleName === 'https' ? 'https:' : 'http:');
|
|
324
|
-
const host = opts.hostname || opts.host || 'localhost';
|
|
325
|
-
const path = opts.path || '/';
|
|
326
|
-
url = `${protocol}//${host}${path}`;
|
|
327
|
-
}
|
|
328
|
-
} catch (e) { return originalRequest.apply(this, args); }
|
|
329
|
-
|
|
330
|
-
if (!_this.isAiRequest(url, null)) {
|
|
331
|
-
return originalRequest.apply(this, args);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
_this.log(`⥠[${moduleName.toUpperCase()}] Intercepting: ${url}`);
|
|
335
|
-
const start = Date.now();
|
|
336
|
-
|
|
337
|
-
const clientRequest = originalRequest.apply(this, args);
|
|
338
|
-
const reqChunks = [];
|
|
339
|
-
const originalWrite = clientRequest.write;
|
|
340
|
-
const originalEnd = clientRequest.end;
|
|
341
|
-
|
|
342
|
-
clientRequest.write = function (...writeArgs) {
|
|
343
|
-
try {
|
|
344
|
-
const chunk = writeArgs[0];
|
|
345
|
-
if (chunk && (typeof chunk === 'string' || Buffer.isBuffer(chunk))) {
|
|
346
|
-
reqChunks.push(Buffer.from(chunk));
|
|
347
|
-
}
|
|
348
|
-
} catch (e) { }
|
|
349
|
-
return originalWrite.apply(this, writeArgs);
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
clientRequest.end = function (...endArgs) {
|
|
353
|
-
try {
|
|
354
|
-
const chunk = endArgs[0];
|
|
355
|
-
if (chunk && (typeof chunk === 'string' || Buffer.isBuffer(chunk))) {
|
|
356
|
-
reqChunks.push(Buffer.from(chunk));
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const reqBody = Buffer.concat(reqChunks).toString('utf8');
|
|
360
|
-
const { provider, model } = _this.normalize(url, reqBody);
|
|
361
|
-
const pHash = _this.hash(reqBody);
|
|
362
|
-
|
|
363
|
-
let activeTurn;
|
|
364
|
-
if (_this.lastTurnConfirmed) {
|
|
365
|
-
activeTurn = _this.turnIndex++;
|
|
366
|
-
_this.lastTurnConfirmed = false;
|
|
367
|
-
} else {
|
|
368
|
-
activeTurn = _this.turnIndex > 0 ? _this.turnIndex - 1 : 0;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
clientRequest._dropout_turn = activeTurn;
|
|
372
|
-
clientRequest._dropout_provider = provider;
|
|
373
|
-
clientRequest._dropout_model = model;
|
|
374
|
-
|
|
375
|
-
_this.emit({
|
|
376
|
-
session_id: _this.currentSessionId,
|
|
377
|
-
turn_index: activeTurn,
|
|
378
|
-
turn_role: 'user',
|
|
379
|
-
provider,
|
|
380
|
-
model,
|
|
381
|
-
content: _this.privacy === 'full' ? reqBody : null,
|
|
382
|
-
content_hash: pHash,
|
|
383
|
-
metadata_flags: { retry_like: pHash === _this.lastPromptHash ? 1 : 0, method: moduleName.toUpperCase(), url: url }
|
|
384
|
-
});
|
|
385
|
-
_this.lastPromptHash = pHash;
|
|
386
|
-
} catch (e) { _this.log("â ī¸ Request Capture Failed"); }
|
|
387
|
-
|
|
388
|
-
return originalEnd.apply(this, endArgs);
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
clientRequest.on('response', (res) => {
|
|
392
|
-
const resChunks = [];
|
|
393
|
-
res.on('data', (chunk) => resChunks.push(chunk));
|
|
394
|
-
res.on('end', () => {
|
|
395
|
-
try {
|
|
396
|
-
let resBody = Buffer.concat(resChunks).toString('utf8');
|
|
397
|
-
if (resBody && resBody.length > _this.maxOutputBytes) resBody = resBody.slice(0, _this.maxOutputBytes);
|
|
398
|
-
|
|
399
|
-
const latency = Date.now() - start;
|
|
400
|
-
const oHash = _this.hash(resBody);
|
|
401
|
-
|
|
402
|
-
_this.emit({
|
|
403
|
-
session_id: _this.currentSessionId,
|
|
404
|
-
turn_index: clientRequest._dropout_turn || 0,
|
|
405
|
-
turn_role: 'assistant',
|
|
406
|
-
latency_ms: latency,
|
|
407
|
-
provider: clientRequest._dropout_provider,
|
|
408
|
-
model: clientRequest._dropout_model,
|
|
409
|
-
content: _this.privacy === 'full' ? resBody : null,
|
|
410
|
-
content_hash: oHash,
|
|
411
|
-
metadata_flags: { status: res.statusCode, non_adaptive_response: oHash === _this.lastResponseHash ? 1 : 0 }
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
_this.lastResponseHash = oHash;
|
|
415
|
-
_this.lastTurnConfirmed = true;
|
|
416
|
-
} catch (e) { _this.log("â ī¸ Response Capture Failed"); }
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
return clientRequest;
|
|
421
|
-
};
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
patchNodeRequest(https, 'https');
|
|
425
|
-
patchNodeRequest(http, 'http');
|
|
426
|
-
this.log("â
Patch Applied: http/https");
|
|
427
273
|
}
|
|
428
274
|
}
|
|
429
275
|
|
|
430
|
-
// Auto-
|
|
431
|
-
if (
|
|
276
|
+
// Auto-init
|
|
277
|
+
if (process?.env?.DROPOUT_PROJECT_ID && process?.env?.DROPOUT_API_KEY) {
|
|
432
278
|
Dropout.init({
|
|
433
279
|
projectId: process.env.DROPOUT_PROJECT_ID,
|
|
434
280
|
apiKey: process.env.DROPOUT_API_KEY,
|
|
@@ -436,6 +282,5 @@ if (typeof process !== 'undefined' && process.env.DROPOUT_PROJECT_ID && process.
|
|
|
436
282
|
});
|
|
437
283
|
}
|
|
438
284
|
|
|
439
|
-
// đĄī¸ DUAL EXPORT FIX (Crucial for TypeScript + Require compatibility)
|
|
440
285
|
Dropout.default = Dropout;
|
|
441
|
-
module.exports = Dropout;
|
|
286
|
+
module.exports = Dropout;
|