caplyr 0.2.4 → 0.3.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/dist/index.js +78 -21
- package/dist/index.mjs +78 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36,20 +36,32 @@ var LogShipper = class {
|
|
|
36
36
|
constructor(config) {
|
|
37
37
|
this.buffer = [];
|
|
38
38
|
this.timer = null;
|
|
39
|
+
// Store bound handlers so we can remove them on shutdown
|
|
40
|
+
this.processHandlers = [];
|
|
39
41
|
this.endpoint = config.endpoint ?? "https://api.caplyr.com";
|
|
40
42
|
this.apiKey = config.apiKey;
|
|
41
43
|
this.batchSize = config.batchSize ?? 10;
|
|
42
44
|
this.flushInterval = config.flushInterval ?? 3e4;
|
|
45
|
+
this.maxBufferSize = 1e3;
|
|
43
46
|
this.onError = config.onError;
|
|
44
47
|
this.timer = setInterval(() => this.flush(), this.flushInterval);
|
|
48
|
+
this.timer.unref?.();
|
|
45
49
|
if (typeof process !== "undefined" && process.on) {
|
|
46
|
-
const
|
|
50
|
+
const onBeforeExit = () => {
|
|
51
|
+
this.flush();
|
|
52
|
+
};
|
|
53
|
+
const onSignal = () => {
|
|
47
54
|
this.flush().finally(() => {
|
|
48
55
|
});
|
|
49
56
|
};
|
|
50
|
-
process.on("beforeExit",
|
|
51
|
-
process.on("SIGTERM",
|
|
52
|
-
process.on("SIGINT",
|
|
57
|
+
process.on("beforeExit", onBeforeExit);
|
|
58
|
+
process.on("SIGTERM", onSignal);
|
|
59
|
+
process.on("SIGINT", onSignal);
|
|
60
|
+
this.processHandlers = [
|
|
61
|
+
{ event: "beforeExit", handler: onBeforeExit },
|
|
62
|
+
{ event: "SIGTERM", handler: onSignal },
|
|
63
|
+
{ event: "SIGINT", handler: onSignal }
|
|
64
|
+
];
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
67
|
/**
|
|
@@ -57,6 +69,10 @@ var LogShipper = class {
|
|
|
57
69
|
* Auto-flushes when batch size is reached.
|
|
58
70
|
*/
|
|
59
71
|
push(log) {
|
|
72
|
+
if (this.buffer.length >= this.maxBufferSize) {
|
|
73
|
+
const excess = this.buffer.length - this.maxBufferSize + 1;
|
|
74
|
+
this.buffer.splice(0, excess);
|
|
75
|
+
}
|
|
60
76
|
this.buffer.push(log);
|
|
61
77
|
if (this.buffer.length >= this.batchSize) {
|
|
62
78
|
this.flush();
|
|
@@ -88,7 +104,18 @@ var LogShipper = class {
|
|
|
88
104
|
}
|
|
89
105
|
}
|
|
90
106
|
/**
|
|
91
|
-
*
|
|
107
|
+
* Remove process signal handlers registered in the constructor.
|
|
108
|
+
*/
|
|
109
|
+
removeProcessHandlers() {
|
|
110
|
+
if (typeof process !== "undefined" && process.removeListener) {
|
|
111
|
+
for (const { event, handler } of this.processHandlers) {
|
|
112
|
+
process.removeListener(event, handler);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
this.processHandlers = [];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Stop the periodic flush timer and remove signal handlers.
|
|
92
119
|
* Call this when tearing down the SDK.
|
|
93
120
|
*/
|
|
94
121
|
destroy() {
|
|
@@ -96,13 +123,19 @@ var LogShipper = class {
|
|
|
96
123
|
clearInterval(this.timer);
|
|
97
124
|
this.timer = null;
|
|
98
125
|
}
|
|
126
|
+
this.removeProcessHandlers();
|
|
99
127
|
this.flush();
|
|
100
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Await the final log flush, stop timers, and remove signal handlers.
|
|
131
|
+
* Preferred over destroy() for clean shutdown.
|
|
132
|
+
*/
|
|
101
133
|
async shutdown() {
|
|
102
134
|
if (this.timer) {
|
|
103
135
|
clearInterval(this.timer);
|
|
104
136
|
this.timer = null;
|
|
105
137
|
}
|
|
138
|
+
this.removeProcessHandlers();
|
|
106
139
|
await this.flush();
|
|
107
140
|
}
|
|
108
141
|
};
|
|
@@ -187,6 +220,7 @@ var Heartbeat = class {
|
|
|
187
220
|
start() {
|
|
188
221
|
this.beat();
|
|
189
222
|
this.timer = setInterval(() => this.beat(), this.interval);
|
|
223
|
+
this.timer.unref?.();
|
|
190
224
|
}
|
|
191
225
|
/**
|
|
192
226
|
* Send a single heartbeat and update local state.
|
|
@@ -206,21 +240,20 @@ var Heartbeat = class {
|
|
|
206
240
|
throw new Error(`Heartbeat failed: ${res.status}`);
|
|
207
241
|
}
|
|
208
242
|
const data = await res.json();
|
|
209
|
-
const localDailyUsed = this.budgetStatus.daily_used;
|
|
210
|
-
const localMonthlyUsed = this.budgetStatus.monthly_used;
|
|
211
243
|
const serverDailyUsed = Number(data.daily_used) || 0;
|
|
212
244
|
const serverMonthlyUsed = Number(data.monthly_used) || 0;
|
|
213
245
|
const serverDailyLimit = data.daily_limit != null ? Number(data.daily_limit) : null;
|
|
214
246
|
const serverMonthlyLimit = data.monthly_limit != null ? Number(data.monthly_limit) : null;
|
|
215
|
-
this.budgetStatus
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
247
|
+
const snapshotDaily = this.budgetStatus.daily_used;
|
|
248
|
+
const snapshotMonthly = this.budgetStatus.monthly_used;
|
|
249
|
+
const mergedDaily = Math.max(serverDailyUsed, snapshotDaily);
|
|
250
|
+
const mergedMonthly = Math.max(serverMonthlyUsed, snapshotMonthly);
|
|
251
|
+
this.budgetStatus.daily_used = mergedDaily + (this.budgetStatus.daily_used - snapshotDaily);
|
|
252
|
+
this.budgetStatus.monthly_used = mergedMonthly + (this.budgetStatus.monthly_used - snapshotMonthly);
|
|
253
|
+
this.budgetStatus.daily_limit = this.pickStricterLimit(serverDailyLimit, this.localDailyLimit);
|
|
254
|
+
this.budgetStatus.monthly_limit = this.pickStricterLimit(serverMonthlyLimit, this.localMonthlyLimit);
|
|
255
|
+
this.budgetStatus.status = data.status;
|
|
256
|
+
this.budgetStatus.kill_switch_active = data.kill_switch_active;
|
|
224
257
|
this.consecutiveFailures = 0;
|
|
225
258
|
const newStatus = data.kill_switch_active ? "OFF" : data.status;
|
|
226
259
|
if (newStatus !== this.status) {
|
|
@@ -325,7 +358,12 @@ var MODEL_PRICING = {
|
|
|
325
358
|
"gpt-3.5-turbo": { input: 0.5, output: 1.5 },
|
|
326
359
|
"o1": { input: 15, output: 60 },
|
|
327
360
|
"o1-mini": { input: 3, output: 12 },
|
|
328
|
-
"o3-mini": { input: 1.1, output: 4.4 }
|
|
361
|
+
"o3-mini": { input: 1.1, output: 4.4 },
|
|
362
|
+
// ---- Groq (OpenAI-compatible) ----
|
|
363
|
+
"llama-3.3-70b-versatile": { input: 0.59, output: 0.79 },
|
|
364
|
+
"llama-3.1-8b-instant": { input: 0.05, output: 0.08 },
|
|
365
|
+
"mixtral-8x7b-32768": { input: 0.24, output: 0.24 },
|
|
366
|
+
"gemma2-9b-it": { input: 0.2, output: 0.2 }
|
|
329
367
|
};
|
|
330
368
|
var DEFAULT_FALLBACKS = {
|
|
331
369
|
// Anthropic downgrades
|
|
@@ -340,7 +378,11 @@ var DEFAULT_FALLBACKS = {
|
|
|
340
378
|
"gpt-4-turbo": "gpt-4o-mini",
|
|
341
379
|
"gpt-4": "gpt-3.5-turbo",
|
|
342
380
|
"o1": "o1-mini",
|
|
343
|
-
"o1-mini": "o3-mini"
|
|
381
|
+
"o1-mini": "o3-mini",
|
|
382
|
+
// Groq downgrades
|
|
383
|
+
"llama-3.3-70b-versatile": "llama-3.1-8b-instant",
|
|
384
|
+
"mixtral-8x7b-32768": "llama-3.1-8b-instant",
|
|
385
|
+
"gemma2-9b-it": "llama-3.1-8b-instant"
|
|
344
386
|
};
|
|
345
387
|
function calculateCost(model, inputTokens, outputTokens) {
|
|
346
388
|
const pricing = MODEL_PRICING[model];
|
|
@@ -765,6 +807,7 @@ function protect(client, config) {
|
|
|
765
807
|
...config
|
|
766
808
|
};
|
|
767
809
|
let shared = instances.get(resolvedConfig.apiKey);
|
|
810
|
+
const isExisting = !!shared;
|
|
768
811
|
if (!shared) {
|
|
769
812
|
const shipper2 = new LogShipper(resolvedConfig);
|
|
770
813
|
const heartbeat2 = new Heartbeat(resolvedConfig);
|
|
@@ -774,7 +817,19 @@ function protect(client, config) {
|
|
|
774
817
|
}
|
|
775
818
|
const { shipper, heartbeat } = shared;
|
|
776
819
|
if (resolvedConfig.budget) {
|
|
777
|
-
const
|
|
820
|
+
const raw = typeof resolvedConfig.budget === "number" ? { monthly: resolvedConfig.budget } : resolvedConfig.budget;
|
|
821
|
+
const budgetConfig = { ...raw };
|
|
822
|
+
if (isExisting) {
|
|
823
|
+
const current = heartbeat.budgetStatus;
|
|
824
|
+
if (budgetConfig.daily !== void 0) {
|
|
825
|
+
const existing = current.daily_limit;
|
|
826
|
+
budgetConfig.daily = existing !== null ? Math.min(existing, budgetConfig.daily) : budgetConfig.daily;
|
|
827
|
+
}
|
|
828
|
+
if (budgetConfig.monthly !== void 0) {
|
|
829
|
+
const existing = current.monthly_limit;
|
|
830
|
+
budgetConfig.monthly = existing !== null ? Math.min(existing, budgetConfig.monthly) : budgetConfig.monthly;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
778
833
|
heartbeat.applyLocalLimits(budgetConfig);
|
|
779
834
|
}
|
|
780
835
|
const provider = detectProvider(client);
|
|
@@ -816,14 +871,16 @@ async function shutdown(apiKey) {
|
|
|
816
871
|
const shared = instances.get(apiKey);
|
|
817
872
|
if (shared) {
|
|
818
873
|
shared.heartbeat.destroy();
|
|
819
|
-
shared.shipper.
|
|
874
|
+
await shared.shipper.shutdown();
|
|
820
875
|
instances.delete(apiKey);
|
|
821
876
|
}
|
|
822
877
|
} else {
|
|
878
|
+
const shutdowns = [];
|
|
823
879
|
for (const [key, shared] of instances) {
|
|
824
880
|
shared.heartbeat.destroy();
|
|
825
|
-
shared.shipper.
|
|
881
|
+
shutdowns.push(shared.shipper.shutdown());
|
|
826
882
|
}
|
|
883
|
+
await Promise.all(shutdowns);
|
|
827
884
|
instances.clear();
|
|
828
885
|
}
|
|
829
886
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -3,20 +3,32 @@ var LogShipper = class {
|
|
|
3
3
|
constructor(config) {
|
|
4
4
|
this.buffer = [];
|
|
5
5
|
this.timer = null;
|
|
6
|
+
// Store bound handlers so we can remove them on shutdown
|
|
7
|
+
this.processHandlers = [];
|
|
6
8
|
this.endpoint = config.endpoint ?? "https://api.caplyr.com";
|
|
7
9
|
this.apiKey = config.apiKey;
|
|
8
10
|
this.batchSize = config.batchSize ?? 10;
|
|
9
11
|
this.flushInterval = config.flushInterval ?? 3e4;
|
|
12
|
+
this.maxBufferSize = 1e3;
|
|
10
13
|
this.onError = config.onError;
|
|
11
14
|
this.timer = setInterval(() => this.flush(), this.flushInterval);
|
|
15
|
+
this.timer.unref?.();
|
|
12
16
|
if (typeof process !== "undefined" && process.on) {
|
|
13
|
-
const
|
|
17
|
+
const onBeforeExit = () => {
|
|
18
|
+
this.flush();
|
|
19
|
+
};
|
|
20
|
+
const onSignal = () => {
|
|
14
21
|
this.flush().finally(() => {
|
|
15
22
|
});
|
|
16
23
|
};
|
|
17
|
-
process.on("beforeExit",
|
|
18
|
-
process.on("SIGTERM",
|
|
19
|
-
process.on("SIGINT",
|
|
24
|
+
process.on("beforeExit", onBeforeExit);
|
|
25
|
+
process.on("SIGTERM", onSignal);
|
|
26
|
+
process.on("SIGINT", onSignal);
|
|
27
|
+
this.processHandlers = [
|
|
28
|
+
{ event: "beforeExit", handler: onBeforeExit },
|
|
29
|
+
{ event: "SIGTERM", handler: onSignal },
|
|
30
|
+
{ event: "SIGINT", handler: onSignal }
|
|
31
|
+
];
|
|
20
32
|
}
|
|
21
33
|
}
|
|
22
34
|
/**
|
|
@@ -24,6 +36,10 @@ var LogShipper = class {
|
|
|
24
36
|
* Auto-flushes when batch size is reached.
|
|
25
37
|
*/
|
|
26
38
|
push(log) {
|
|
39
|
+
if (this.buffer.length >= this.maxBufferSize) {
|
|
40
|
+
const excess = this.buffer.length - this.maxBufferSize + 1;
|
|
41
|
+
this.buffer.splice(0, excess);
|
|
42
|
+
}
|
|
27
43
|
this.buffer.push(log);
|
|
28
44
|
if (this.buffer.length >= this.batchSize) {
|
|
29
45
|
this.flush();
|
|
@@ -55,7 +71,18 @@ var LogShipper = class {
|
|
|
55
71
|
}
|
|
56
72
|
}
|
|
57
73
|
/**
|
|
58
|
-
*
|
|
74
|
+
* Remove process signal handlers registered in the constructor.
|
|
75
|
+
*/
|
|
76
|
+
removeProcessHandlers() {
|
|
77
|
+
if (typeof process !== "undefined" && process.removeListener) {
|
|
78
|
+
for (const { event, handler } of this.processHandlers) {
|
|
79
|
+
process.removeListener(event, handler);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
this.processHandlers = [];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Stop the periodic flush timer and remove signal handlers.
|
|
59
86
|
* Call this when tearing down the SDK.
|
|
60
87
|
*/
|
|
61
88
|
destroy() {
|
|
@@ -63,13 +90,19 @@ var LogShipper = class {
|
|
|
63
90
|
clearInterval(this.timer);
|
|
64
91
|
this.timer = null;
|
|
65
92
|
}
|
|
93
|
+
this.removeProcessHandlers();
|
|
66
94
|
this.flush();
|
|
67
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Await the final log flush, stop timers, and remove signal handlers.
|
|
98
|
+
* Preferred over destroy() for clean shutdown.
|
|
99
|
+
*/
|
|
68
100
|
async shutdown() {
|
|
69
101
|
if (this.timer) {
|
|
70
102
|
clearInterval(this.timer);
|
|
71
103
|
this.timer = null;
|
|
72
104
|
}
|
|
105
|
+
this.removeProcessHandlers();
|
|
73
106
|
await this.flush();
|
|
74
107
|
}
|
|
75
108
|
};
|
|
@@ -154,6 +187,7 @@ var Heartbeat = class {
|
|
|
154
187
|
start() {
|
|
155
188
|
this.beat();
|
|
156
189
|
this.timer = setInterval(() => this.beat(), this.interval);
|
|
190
|
+
this.timer.unref?.();
|
|
157
191
|
}
|
|
158
192
|
/**
|
|
159
193
|
* Send a single heartbeat and update local state.
|
|
@@ -173,21 +207,20 @@ var Heartbeat = class {
|
|
|
173
207
|
throw new Error(`Heartbeat failed: ${res.status}`);
|
|
174
208
|
}
|
|
175
209
|
const data = await res.json();
|
|
176
|
-
const localDailyUsed = this.budgetStatus.daily_used;
|
|
177
|
-
const localMonthlyUsed = this.budgetStatus.monthly_used;
|
|
178
210
|
const serverDailyUsed = Number(data.daily_used) || 0;
|
|
179
211
|
const serverMonthlyUsed = Number(data.monthly_used) || 0;
|
|
180
212
|
const serverDailyLimit = data.daily_limit != null ? Number(data.daily_limit) : null;
|
|
181
213
|
const serverMonthlyLimit = data.monthly_limit != null ? Number(data.monthly_limit) : null;
|
|
182
|
-
this.budgetStatus
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
214
|
+
const snapshotDaily = this.budgetStatus.daily_used;
|
|
215
|
+
const snapshotMonthly = this.budgetStatus.monthly_used;
|
|
216
|
+
const mergedDaily = Math.max(serverDailyUsed, snapshotDaily);
|
|
217
|
+
const mergedMonthly = Math.max(serverMonthlyUsed, snapshotMonthly);
|
|
218
|
+
this.budgetStatus.daily_used = mergedDaily + (this.budgetStatus.daily_used - snapshotDaily);
|
|
219
|
+
this.budgetStatus.monthly_used = mergedMonthly + (this.budgetStatus.monthly_used - snapshotMonthly);
|
|
220
|
+
this.budgetStatus.daily_limit = this.pickStricterLimit(serverDailyLimit, this.localDailyLimit);
|
|
221
|
+
this.budgetStatus.monthly_limit = this.pickStricterLimit(serverMonthlyLimit, this.localMonthlyLimit);
|
|
222
|
+
this.budgetStatus.status = data.status;
|
|
223
|
+
this.budgetStatus.kill_switch_active = data.kill_switch_active;
|
|
191
224
|
this.consecutiveFailures = 0;
|
|
192
225
|
const newStatus = data.kill_switch_active ? "OFF" : data.status;
|
|
193
226
|
if (newStatus !== this.status) {
|
|
@@ -292,7 +325,12 @@ var MODEL_PRICING = {
|
|
|
292
325
|
"gpt-3.5-turbo": { input: 0.5, output: 1.5 },
|
|
293
326
|
"o1": { input: 15, output: 60 },
|
|
294
327
|
"o1-mini": { input: 3, output: 12 },
|
|
295
|
-
"o3-mini": { input: 1.1, output: 4.4 }
|
|
328
|
+
"o3-mini": { input: 1.1, output: 4.4 },
|
|
329
|
+
// ---- Groq (OpenAI-compatible) ----
|
|
330
|
+
"llama-3.3-70b-versatile": { input: 0.59, output: 0.79 },
|
|
331
|
+
"llama-3.1-8b-instant": { input: 0.05, output: 0.08 },
|
|
332
|
+
"mixtral-8x7b-32768": { input: 0.24, output: 0.24 },
|
|
333
|
+
"gemma2-9b-it": { input: 0.2, output: 0.2 }
|
|
296
334
|
};
|
|
297
335
|
var DEFAULT_FALLBACKS = {
|
|
298
336
|
// Anthropic downgrades
|
|
@@ -307,7 +345,11 @@ var DEFAULT_FALLBACKS = {
|
|
|
307
345
|
"gpt-4-turbo": "gpt-4o-mini",
|
|
308
346
|
"gpt-4": "gpt-3.5-turbo",
|
|
309
347
|
"o1": "o1-mini",
|
|
310
|
-
"o1-mini": "o3-mini"
|
|
348
|
+
"o1-mini": "o3-mini",
|
|
349
|
+
// Groq downgrades
|
|
350
|
+
"llama-3.3-70b-versatile": "llama-3.1-8b-instant",
|
|
351
|
+
"mixtral-8x7b-32768": "llama-3.1-8b-instant",
|
|
352
|
+
"gemma2-9b-it": "llama-3.1-8b-instant"
|
|
311
353
|
};
|
|
312
354
|
function calculateCost(model, inputTokens, outputTokens) {
|
|
313
355
|
const pricing = MODEL_PRICING[model];
|
|
@@ -732,6 +774,7 @@ function protect(client, config) {
|
|
|
732
774
|
...config
|
|
733
775
|
};
|
|
734
776
|
let shared = instances.get(resolvedConfig.apiKey);
|
|
777
|
+
const isExisting = !!shared;
|
|
735
778
|
if (!shared) {
|
|
736
779
|
const shipper2 = new LogShipper(resolvedConfig);
|
|
737
780
|
const heartbeat2 = new Heartbeat(resolvedConfig);
|
|
@@ -741,7 +784,19 @@ function protect(client, config) {
|
|
|
741
784
|
}
|
|
742
785
|
const { shipper, heartbeat } = shared;
|
|
743
786
|
if (resolvedConfig.budget) {
|
|
744
|
-
const
|
|
787
|
+
const raw = typeof resolvedConfig.budget === "number" ? { monthly: resolvedConfig.budget } : resolvedConfig.budget;
|
|
788
|
+
const budgetConfig = { ...raw };
|
|
789
|
+
if (isExisting) {
|
|
790
|
+
const current = heartbeat.budgetStatus;
|
|
791
|
+
if (budgetConfig.daily !== void 0) {
|
|
792
|
+
const existing = current.daily_limit;
|
|
793
|
+
budgetConfig.daily = existing !== null ? Math.min(existing, budgetConfig.daily) : budgetConfig.daily;
|
|
794
|
+
}
|
|
795
|
+
if (budgetConfig.monthly !== void 0) {
|
|
796
|
+
const existing = current.monthly_limit;
|
|
797
|
+
budgetConfig.monthly = existing !== null ? Math.min(existing, budgetConfig.monthly) : budgetConfig.monthly;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
745
800
|
heartbeat.applyLocalLimits(budgetConfig);
|
|
746
801
|
}
|
|
747
802
|
const provider = detectProvider(client);
|
|
@@ -783,14 +838,16 @@ async function shutdown(apiKey) {
|
|
|
783
838
|
const shared = instances.get(apiKey);
|
|
784
839
|
if (shared) {
|
|
785
840
|
shared.heartbeat.destroy();
|
|
786
|
-
shared.shipper.
|
|
841
|
+
await shared.shipper.shutdown();
|
|
787
842
|
instances.delete(apiKey);
|
|
788
843
|
}
|
|
789
844
|
} else {
|
|
845
|
+
const shutdowns = [];
|
|
790
846
|
for (const [key, shared] of instances) {
|
|
791
847
|
shared.heartbeat.destroy();
|
|
792
|
-
shared.shipper.
|
|
848
|
+
shutdowns.push(shared.shipper.shutdown());
|
|
793
849
|
}
|
|
850
|
+
await Promise.all(shutdowns);
|
|
794
851
|
instances.clear();
|
|
795
852
|
}
|
|
796
853
|
}
|