@dropout-ai/runtime 0.5.0 â 0.5.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/package.json +1 -1
- package/src/index.js +187 -329
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,441 +1,299 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @dropout-ai/runtime
|
|
3
3
|
* Universal AI Interaction Capture for Node.js
|
|
4
|
-
* Stability: High (Browser-Safe,
|
|
5
|
-
* Features: Auto-Discovery, Connectivity Check, Dual-Schema Support
|
|
4
|
+
* Stability: High (Browser-Safe, Next.js Safe)
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
let https, http, crypto;
|
|
9
8
|
|
|
10
9
|
// --- DEFAULT CONFIGURATION ---
|
|
11
|
-
const SUPABASE_FUNCTION_URL =
|
|
10
|
+
const SUPABASE_FUNCTION_URL =
|
|
11
|
+
"https://hipughmjlwmwjxzyxfzs.supabase.co/functions/v1/capture-sealed";
|
|
12
12
|
|
|
13
13
|
// Known AI Domains
|
|
14
14
|
const KNOWN_AI_DOMAINS = [
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
"api.openai.com",
|
|
16
|
+
"api.anthropic.com",
|
|
17
|
+
"generativelanguage.googleapis.com",
|
|
18
|
+
"aiplatform.googleapis.com",
|
|
19
|
+
"api.groq.com",
|
|
20
|
+
"api.mistral.ai",
|
|
21
|
+
"api.cohere.ai",
|
|
17
22
|
];
|
|
18
23
|
|
|
19
24
|
class Dropout {
|
|
20
|
-
// Singleton Instance Storage
|
|
21
25
|
static instance = null;
|
|
22
26
|
|
|
23
|
-
/**
|
|
24
|
-
* Universal Initialization Method (Idempotent)
|
|
25
|
-
* Users call this explicitly in their code.
|
|
26
|
-
*/
|
|
27
27
|
static init(config) {
|
|
28
|
-
if (!Dropout.instance)
|
|
29
|
-
new Dropout(config);
|
|
30
|
-
}
|
|
28
|
+
if (!Dropout.instance) new Dropout(config);
|
|
31
29
|
return Dropout.instance;
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
constructor(config = {}) {
|
|
35
|
-
|
|
36
|
-
if (Dropout.instance) {
|
|
37
|
-
if (config.debug) console.log("[Dropout] âšī¸ Already initialized. Returning existing instance.");
|
|
38
|
-
return Dropout.instance;
|
|
39
|
-
}
|
|
33
|
+
if (Dropout.instance) return Dropout.instance;
|
|
40
34
|
|
|
41
|
-
// đĄī¸
|
|
42
|
-
if (typeof window !==
|
|
43
|
-
if (config.debug) console.warn("[Dropout] â ī¸ Skipped: Browser detected. SDK is Server-Only.");
|
|
35
|
+
// đĄī¸ Browser Guard (prevents client execution)
|
|
36
|
+
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
|
|
44
37
|
return;
|
|
45
38
|
}
|
|
46
39
|
|
|
47
|
-
// đĄī¸
|
|
40
|
+
// đĄī¸ Credential Guard
|
|
48
41
|
if (!config.apiKey || !config.projectId) {
|
|
49
42
|
if (config.debug) {
|
|
50
|
-
console.warn("
|
|
43
|
+
console.warn("[Dropout] Missing API Key or Project ID");
|
|
51
44
|
}
|
|
52
45
|
return;
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
// đĄī¸ 3. DEPENDENCY CHECK
|
|
56
48
|
try {
|
|
57
|
-
https = require(
|
|
58
|
-
http = require(
|
|
59
|
-
crypto = require(
|
|
60
|
-
} catch
|
|
61
|
-
if (config.debug) console.warn("[Dropout] â ī¸ Skipped: Node core modules missing.");
|
|
49
|
+
https = require("https");
|
|
50
|
+
http = require("http");
|
|
51
|
+
crypto = require("crypto");
|
|
52
|
+
} catch {
|
|
62
53
|
return;
|
|
63
54
|
}
|
|
64
55
|
|
|
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
|
-
}
|
|
56
|
+
this.projectId = config.projectId;
|
|
57
|
+
this.apiKey = config.apiKey;
|
|
58
|
+
this.debug = config.debug || false;
|
|
59
|
+
this.privacy = config.privacy || "full";
|
|
60
|
+
this.captureEndpoint = config.captureEndpoint || SUPABASE_FUNCTION_URL;
|
|
61
|
+
this.maxOutputBytes = 32768;
|
|
98
62
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
63
|
+
// --------------------------------------------------
|
|
64
|
+
// â
ASYNC CONTEXT (LAZY, NODE-ONLY)
|
|
65
|
+
// --------------------------------------------------
|
|
66
|
+
this.storage = null;
|
|
102
67
|
|
|
103
|
-
|
|
104
|
-
|
|
68
|
+
try {
|
|
69
|
+
const asyncHooks = require("async_hooks");
|
|
70
|
+
this.storage = new asyncHooks.AsyncLocalStorage();
|
|
71
|
+
} catch {
|
|
72
|
+
// Edge / Browser / Unsupported â safely disabled
|
|
73
|
+
this.storage = null;
|
|
74
|
+
}
|
|
105
75
|
|
|
106
|
-
|
|
107
|
-
|
|
76
|
+
// --------------------------------------------------
|
|
77
|
+
// NETWORK PATCHING
|
|
78
|
+
// --------------------------------------------------
|
|
79
|
+
this.patchNetwork();
|
|
108
80
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (config.debug) console.error("[Dropout] â Initialization Error:", err);
|
|
81
|
+
if (this.debug) {
|
|
82
|
+
console.log(`[Dropout] đĸ Online | Project ${this.projectId}`);
|
|
112
83
|
}
|
|
113
|
-
}
|
|
114
84
|
|
|
115
|
-
|
|
116
|
-
log(msg, ...args) {
|
|
117
|
-
if (this.debug) console.log(`[Dropout] ${msg}`, ...args);
|
|
85
|
+
Dropout.instance = this;
|
|
118
86
|
}
|
|
119
87
|
|
|
88
|
+
// --------------------------------------------------
|
|
89
|
+
// UTILITIES
|
|
90
|
+
// --------------------------------------------------
|
|
120
91
|
generateSessionId() {
|
|
121
92
|
try {
|
|
122
93
|
return crypto.randomUUID();
|
|
123
|
-
} catch
|
|
124
|
-
return
|
|
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}`);
|
|
94
|
+
} catch {
|
|
95
|
+
return "sess_" + Date.now().toString(36);
|
|
143
96
|
}
|
|
144
|
-
return this.currentSessionId;
|
|
145
97
|
}
|
|
146
98
|
|
|
147
99
|
hash(text) {
|
|
148
100
|
if (!text) return null;
|
|
149
101
|
try {
|
|
150
|
-
return crypto
|
|
151
|
-
|
|
102
|
+
return crypto
|
|
103
|
+
.createHash("sha256")
|
|
104
|
+
.update(text.toLowerCase().trim())
|
|
105
|
+
.digest("hex");
|
|
106
|
+
} catch {
|
|
107
|
+
return "hash_err";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// --------------------------------------------------
|
|
112
|
+
// CONTEXT API (OPTIONAL BUT SAFE)
|
|
113
|
+
// --------------------------------------------------
|
|
114
|
+
run(sessionId, callback) {
|
|
115
|
+
if (!this.storage) return callback();
|
|
116
|
+
|
|
117
|
+
const context = {
|
|
118
|
+
sessionId: sessionId || this.generateSessionId(),
|
|
119
|
+
turnIndex: 0,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return this.storage.run(context, callback);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
getContext() {
|
|
126
|
+
if (!this.storage) return null;
|
|
127
|
+
return this.storage.getStore() || null;
|
|
152
128
|
}
|
|
153
129
|
|
|
130
|
+
getSessionId() {
|
|
131
|
+
const ctx = this.getContext();
|
|
132
|
+
return ctx?.sessionId || "global_session";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// --------------------------------------------------
|
|
136
|
+
// NORMALIZATION
|
|
137
|
+
// --------------------------------------------------
|
|
154
138
|
normalize(url, body) {
|
|
155
|
-
let provider =
|
|
156
|
-
let model =
|
|
139
|
+
let provider = "custom";
|
|
140
|
+
let model = "unknown";
|
|
141
|
+
|
|
157
142
|
try {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
else if (u.includes('localhost') || u.includes('127.0.0.1')) provider = 'local';
|
|
167
|
-
}
|
|
143
|
+
const u = url?.toLowerCase() || "";
|
|
144
|
+
if (u.includes("openai")) provider = "openai";
|
|
145
|
+
else if (u.includes("anthropic")) provider = "anthropic";
|
|
146
|
+
else if (u.includes("google")) provider = "google";
|
|
147
|
+
else if (u.includes("groq")) provider = "groq";
|
|
148
|
+
else if (u.includes("mistral")) provider = "mistral";
|
|
149
|
+
else if (u.includes("cohere")) provider = "cohere";
|
|
150
|
+
|
|
168
151
|
if (body) {
|
|
169
|
-
const parsed = typeof body ===
|
|
170
|
-
if (parsed
|
|
171
|
-
if (provider === 'custom' && (parsed.messages || parsed.prompt)) provider = 'heuristic';
|
|
152
|
+
const parsed = typeof body === "string" ? JSON.parse(body) : body;
|
|
153
|
+
if (parsed?.model) model = parsed.model;
|
|
172
154
|
}
|
|
173
|
-
} catch
|
|
155
|
+
} catch { }
|
|
156
|
+
|
|
174
157
|
return { provider, model };
|
|
175
158
|
}
|
|
176
159
|
|
|
177
|
-
isAiRequest(url,
|
|
178
|
-
if (!url) return false;
|
|
179
|
-
if (url.includes(
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
return (
|
|
183
|
-
|
|
160
|
+
isAiRequest(url, body) {
|
|
161
|
+
if (!url || url.includes(this.captureEndpoint)) return false;
|
|
162
|
+
if (KNOWN_AI_DOMAINS.some((d) => url.includes(d))) return true;
|
|
163
|
+
|
|
164
|
+
if (body) {
|
|
165
|
+
return (
|
|
166
|
+
body.includes('"model"') &&
|
|
167
|
+
(body.includes('"messages"') || body.includes('"prompt"'))
|
|
168
|
+
);
|
|
184
169
|
}
|
|
185
170
|
return false;
|
|
186
171
|
}
|
|
187
172
|
|
|
188
|
-
//
|
|
173
|
+
// --------------------------------------------------
|
|
174
|
+
// EMITTER
|
|
175
|
+
// --------------------------------------------------
|
|
189
176
|
emit(payload) {
|
|
190
177
|
try {
|
|
191
|
-
if (!this.apiKey || !this.projectId) return;
|
|
192
|
-
|
|
193
178
|
const finalPayload = {
|
|
194
179
|
project_id: this.projectId,
|
|
195
180
|
session_id: payload.session_id,
|
|
196
181
|
turn_index: payload.turn_index,
|
|
197
182
|
turn_role: payload.turn_role,
|
|
198
|
-
|
|
199
|
-
|
|
183
|
+
direction:
|
|
184
|
+
payload.turn_role === "user" ? "user_to_ai" : "ai_to_user",
|
|
200
185
|
provider: payload.provider,
|
|
201
186
|
model: payload.model,
|
|
202
187
|
latency_ms: payload.latency_ms || null,
|
|
203
188
|
content: payload.content,
|
|
204
189
|
content_hash: payload.content_hash,
|
|
205
190
|
metadata_flags: payload.metadata_flags,
|
|
206
|
-
received_at: new Date().toISOString()
|
|
191
|
+
received_at: new Date().toISOString(),
|
|
207
192
|
};
|
|
208
193
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const req = requestModule.request(this.captureEndpoint, {
|
|
215
|
-
method: 'POST',
|
|
194
|
+
const req = (this.captureEndpoint.startsWith("http:")
|
|
195
|
+
? http
|
|
196
|
+
: https
|
|
197
|
+
).request(this.captureEndpoint, {
|
|
198
|
+
method: "POST",
|
|
216
199
|
headers: {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
200
|
+
"Content-Type": "application/json",
|
|
201
|
+
"x-dropout-key": this.apiKey,
|
|
202
|
+
},
|
|
220
203
|
});
|
|
221
204
|
|
|
222
|
-
req.on(
|
|
205
|
+
req.on("error", () => { });
|
|
223
206
|
req.write(JSON.stringify(finalPayload));
|
|
224
207
|
req.end();
|
|
225
|
-
} catch
|
|
226
|
-
this.log("â ī¸ Emit Error (Silent)", e);
|
|
227
|
-
}
|
|
208
|
+
} catch { }
|
|
228
209
|
}
|
|
229
210
|
|
|
230
|
-
//
|
|
211
|
+
// --------------------------------------------------
|
|
212
|
+
// NETWORK PATCHING
|
|
213
|
+
// --------------------------------------------------
|
|
231
214
|
patchNetwork() {
|
|
232
215
|
const _this = this;
|
|
233
216
|
|
|
234
|
-
|
|
235
|
-
if (global.fetch) {
|
|
236
|
-
const originalFetch = global.fetch;
|
|
237
|
-
global.fetch = async function (input, init) {
|
|
238
|
-
let url;
|
|
239
|
-
try { url = typeof input === 'string' ? input : input?.url; } catch (e) { return originalFetch.apply(this, arguments); }
|
|
240
|
-
|
|
241
|
-
let bodyStr = "";
|
|
242
|
-
if (init && init.body) {
|
|
243
|
-
try { bodyStr = typeof init.body === 'string' ? init.body : JSON.stringify(init.body); } catch (e) { }
|
|
244
|
-
}
|
|
217
|
+
if (!global.fetch) return;
|
|
245
218
|
|
|
246
|
-
|
|
247
|
-
return originalFetch.apply(this, arguments);
|
|
248
|
-
}
|
|
219
|
+
const originalFetch = global.fetch;
|
|
249
220
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
} else {
|
|
258
|
-
activeTurn = _this.turnIndex > 0 ? _this.turnIndex - 1 : 0;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const { provider, model } = _this.normalize(url, bodyStr);
|
|
262
|
-
const pHash = _this.hash(bodyStr);
|
|
263
|
-
|
|
264
|
-
_this.emit({
|
|
265
|
-
session_id: _this.currentSessionId,
|
|
266
|
-
turn_index: activeTurn,
|
|
267
|
-
turn_role: 'user',
|
|
268
|
-
provider,
|
|
269
|
-
model,
|
|
270
|
-
content: _this.privacy === 'full' ? bodyStr : null,
|
|
271
|
-
content_hash: pHash,
|
|
272
|
-
metadata_flags: { retry_like: pHash === _this.lastPromptHash ? 1 : 0, method: 'FETCH', url: url }
|
|
273
|
-
});
|
|
274
|
-
_this.lastPromptHash = pHash;
|
|
221
|
+
global.fetch = async function (input, init) {
|
|
222
|
+
let url;
|
|
223
|
+
try {
|
|
224
|
+
url = typeof input === "string" ? input : input?.url;
|
|
225
|
+
} catch {
|
|
226
|
+
return originalFetch.apply(this, arguments);
|
|
227
|
+
}
|
|
275
228
|
|
|
276
|
-
|
|
229
|
+
let bodyStr = "";
|
|
230
|
+
if (init?.body) {
|
|
277
231
|
try {
|
|
278
|
-
|
|
279
|
-
|
|
232
|
+
bodyStr =
|
|
233
|
+
typeof init.body === "string"
|
|
234
|
+
? init.body
|
|
235
|
+
: JSON.stringify(init.body);
|
|
236
|
+
} catch { }
|
|
237
|
+
}
|
|
280
238
|
|
|
281
|
-
|
|
239
|
+
if (!_this.isAiRequest(url, bodyStr)) {
|
|
240
|
+
return originalFetch.apply(this, arguments);
|
|
241
|
+
}
|
|
282
242
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
metadata_flags: {
|
|
299
|
-
non_adaptive_response: oHash === _this.lastResponseHash ? 1 : 0,
|
|
300
|
-
turn_boundary_confirmed: 1,
|
|
301
|
-
status: response.status
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
_this.lastResponseHash = oHash;
|
|
305
|
-
_this.lastTurnConfirmed = true;
|
|
306
|
-
} catch (e) { _this.log("â ī¸ Failed to read response body"); }
|
|
307
|
-
|
|
308
|
-
return response;
|
|
309
|
-
};
|
|
310
|
-
this.log("â
Patch Applied: global.fetch");
|
|
311
|
-
}
|
|
243
|
+
const sessionId = _this.getSessionId();
|
|
244
|
+
const { provider, model } = _this.normalize(url, bodyStr);
|
|
245
|
+
const pHash = _this.hash(bodyStr);
|
|
246
|
+
const start = Date.now();
|
|
247
|
+
|
|
248
|
+
_this.emit({
|
|
249
|
+
session_id: sessionId,
|
|
250
|
+
turn_index: 0,
|
|
251
|
+
turn_role: "user",
|
|
252
|
+
provider,
|
|
253
|
+
model,
|
|
254
|
+
content: _this.privacy === "full" ? bodyStr : null,
|
|
255
|
+
content_hash: pHash,
|
|
256
|
+
metadata_flags: { method: "FETCH", url },
|
|
257
|
+
});
|
|
312
258
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
let
|
|
318
|
-
|
|
319
|
-
|
|
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);
|
|
259
|
+
const response = await originalFetch.apply(this, arguments);
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
const cloned = response.clone();
|
|
263
|
+
let oText = await cloned.text();
|
|
264
|
+
if (oText.length > _this.maxOutputBytes) {
|
|
265
|
+
oText = oText.slice(0, _this.maxOutputBytes);
|
|
332
266
|
}
|
|
333
267
|
|
|
334
|
-
_this.
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
});
|
|
268
|
+
_this.emit({
|
|
269
|
+
session_id: sessionId,
|
|
270
|
+
turn_index: 1,
|
|
271
|
+
turn_role: "assistant",
|
|
272
|
+
latency_ms: Date.now() - start,
|
|
273
|
+
provider,
|
|
274
|
+
model,
|
|
275
|
+
content: _this.privacy === "full" ? oText : null,
|
|
276
|
+
content_hash: _this.hash(oText),
|
|
277
|
+
metadata_flags: { status: response.status },
|
|
418
278
|
});
|
|
279
|
+
} catch { }
|
|
419
280
|
|
|
420
|
-
|
|
421
|
-
};
|
|
281
|
+
return response;
|
|
422
282
|
};
|
|
423
|
-
|
|
424
|
-
patchNodeRequest(https, 'https');
|
|
425
|
-
patchNodeRequest(http, 'http');
|
|
426
|
-
this.log("â
Patch Applied: http/https");
|
|
427
283
|
}
|
|
428
284
|
}
|
|
429
285
|
|
|
430
|
-
// Auto-
|
|
431
|
-
if (
|
|
286
|
+
// Auto-init via env (safe)
|
|
287
|
+
if (
|
|
288
|
+
typeof process !== "undefined" &&
|
|
289
|
+
process.env.DROPOUT_PROJECT_ID &&
|
|
290
|
+
process.env.DROPOUT_API_KEY
|
|
291
|
+
) {
|
|
432
292
|
Dropout.init({
|
|
433
293
|
projectId: process.env.DROPOUT_PROJECT_ID,
|
|
434
294
|
apiKey: process.env.DROPOUT_API_KEY,
|
|
435
|
-
debug: process.env.DROPOUT_DEBUG === 'true'
|
|
436
295
|
});
|
|
437
296
|
}
|
|
438
297
|
|
|
439
|
-
// đĄī¸ DUAL EXPORT FIX (Crucial for TypeScript + Require compatibility)
|
|
440
298
|
Dropout.default = Dropout;
|
|
441
|
-
module.exports = Dropout;
|
|
299
|
+
module.exports = Dropout;
|