troxy-cli 1.30.0 → 1.30.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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "troxy-cli",
|
|
3
|
-
"version": "1.30.
|
|
3
|
+
"version": "1.30.1",
|
|
4
4
|
"description": "A secure control layer for AI agents: policies across payments, messages, logins, destructive actions, model usage, and secrets, all enforceable from the CLI",
|
|
5
5
|
"homepage": "https://troxy.io",
|
|
6
6
|
"bugs": {
|
|
@@ -181,13 +181,19 @@ function sessionLine(t) {
|
|
|
181
181
|
Object.entries(t.meter).map(([kind, percentUsed]) => ({ kind, percentUsed })));
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
// This session's Troxy work, from the server's count of its calls: how many ran on a
|
|
185
|
+
// lighter model than asked, plus tokens kept out of requests (compression + trimming,
|
|
186
|
+
// API-key setups through the proxy only). A holdout session says so: it is never routed.
|
|
187
|
+
// undefined until there is something to say.
|
|
188
|
+
function savingsText(s, arm) {
|
|
189
|
+
const calls = (s && s.calls) || 0, routed = (s && s.routed_calls) || 0;
|
|
190
|
+
const kept = ((s && s.compressed_tokens) || 0) + ((s && s.trimmed_tokens) || 0);
|
|
188
191
|
const k = n => n >= 1e6 ? `${Math.round(n / 1e5) / 10}M`.replace('.0M', 'M') : n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
if (arm === 'holdout') return calls ? 'comparison session: not routed, so Troxy can measure the difference' : undefined;
|
|
193
|
+
const parts = [];
|
|
194
|
+
if (calls) parts.push(`${routed} of ${calls} calls on a lighter model`);
|
|
195
|
+
if (kept > 0) parts.push(`${k(kept)} tokens not re-sent`);
|
|
196
|
+
return parts.length ? parts.join(' · ') : undefined;
|
|
191
197
|
}
|
|
192
198
|
|
|
193
199
|
// This session's savings from the server, or null when there is no key, no session id or
|
|
@@ -229,7 +235,7 @@ const REPORT_KEEP_ROWS = 1000; // kept for the next try while Troxy is unreachab
|
|
|
229
235
|
let pending = [];
|
|
230
236
|
let compacting = false;
|
|
231
237
|
|
|
232
|
-
function reportRow({ sid, usage, requested, arm, cold, score, compact, rateLimits }) {
|
|
238
|
+
function reportRow({ sid, usage, requested, arm, cold, score, compact, rateLimits, ts }) {
|
|
233
239
|
const rl = {};
|
|
234
240
|
for (const w of rateLimits || []) {
|
|
235
241
|
if (!WINDOW_LABEL[w.kind]) continue;
|
|
@@ -241,7 +247,7 @@ function reportRow({ sid, usage, requested, arm, cold, score, compact, rateLimit
|
|
|
241
247
|
arm: arm || null, switch: arm ? (cold ? 'cold' : 'warm') : null, score: score == null ? null : score,
|
|
242
248
|
compact: Boolean(compact), input_tokens: usage.input_tokens, output_tokens: usage.output_tokens,
|
|
243
249
|
cache_read_tokens: usage.cache_read_input_tokens, cache_creation_tokens: usage.cache_creation_input_tokens,
|
|
244
|
-
rate_limits: rl,
|
|
250
|
+
rate_limits: rl, ts,
|
|
245
251
|
};
|
|
246
252
|
}
|
|
247
253
|
|
|
@@ -250,7 +256,7 @@ async function noteStep($, result, meta) {
|
|
|
250
256
|
if (!usage || !usage.model) return;
|
|
251
257
|
let rateLimits = [];
|
|
252
258
|
try { rateLimits = (await $.session.usage()).rateLimits; } catch {}
|
|
253
|
-
pending.push(reportRow({ ...meta, sid: await $.session.id(), usage, compact: compacting, rateLimits }));
|
|
259
|
+
pending.push(reportRow({ ...meta, sid: await $.session.id(), usage, compact: compacting, rateLimits, ts: await $.clock.now() }));
|
|
254
260
|
if (pending.length > REPORT_KEEP_ROWS) pending = pending.slice(-REPORT_KEEP_ROWS);
|
|
255
261
|
}
|
|
256
262
|
|
|
@@ -601,7 +607,7 @@ export function register(on) {
|
|
|
601
607
|
let savings = null;
|
|
602
608
|
try {
|
|
603
609
|
savings = await fetchSavings($);
|
|
604
|
-
if (savings) await $.state.set(SAVINGS, savingsText(savings));
|
|
610
|
+
if (savings) await $.state.set(SAVINGS, savingsText(savings, (await $.state.get(ARM)).value) || null);
|
|
605
611
|
} catch {} // a missed fetch only leaves the band as it was
|
|
606
612
|
if (turn) {
|
|
607
613
|
const { start, ratio } = turn;
|
|
@@ -128,14 +128,14 @@ test('compactPlan: big idle sessions compact, small ones and a nearly full bar d
|
|
|
128
128
|
assert.deepEqual(compactPlan({ tokens: 210000, now, rateLimits: [{ kind: 'seven_day', percentUsed: 99 }] }), { action: 'skip' });
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
test('savingsText:
|
|
131
|
+
test('savingsText: calls on a lighter model, kept tokens only when there are some, holdout says so', () => {
|
|
132
132
|
const { savingsText } = _internal;
|
|
133
|
-
assert.equal(savingsText({ compressed_tokens:
|
|
134
|
-
assert.equal(savingsText({ compressed_tokens:
|
|
135
|
-
assert.equal(savingsText({
|
|
136
|
-
assert.equal(savingsText({
|
|
137
|
-
assert.equal(savingsText({
|
|
138
|
-
assert.equal(savingsText(null),
|
|
133
|
+
assert.equal(savingsText({ calls: 5, routed_calls: 5, compressed_tokens: 0, trimmed_tokens: 0 }), '5 of 5 calls on a lighter model');
|
|
134
|
+
assert.equal(savingsText({ calls: 4, routed_calls: 1, compressed_tokens: 5200, trimmed_tokens: 40100 }), '1 of 4 calls on a lighter model · 45k tokens not re-sent');
|
|
135
|
+
assert.equal(savingsText({ calls: 9, routed_calls: 0, compressed_tokens: 123277, trimmed_tokens: 154512861 }), '0 of 9 calls on a lighter model · 154.6M tokens not re-sent');
|
|
136
|
+
assert.equal(savingsText({ calls: 3, routed_calls: 0 }, 'holdout'), 'comparison session: not routed, so Troxy can measure the difference');
|
|
137
|
+
assert.equal(savingsText({ calls: 0 }), undefined);
|
|
138
|
+
assert.equal(savingsText(null), undefined);
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
test('fetchSavings: sends the saved key and the session id; null without a key or on an error', async () => {
|
|
@@ -364,6 +364,7 @@ test('usage report: each request is queued with model, tokens, bar and arm, then
|
|
|
364
364
|
const row = pendingRows().at(-1);
|
|
365
365
|
assert.equal(row.model, M.sonnet); assert.equal(row.model_requested, M.opus);
|
|
366
366
|
assert.equal(row.arm, 'treated'); assert.equal(row.switch, 'cold'); assert.equal(row.cache_read_tokens, 900);
|
|
367
|
+
assert.equal(typeof row.ts, 'number'); // when the request ran, not when the turn ended
|
|
367
368
|
assert.deepEqual(row.rate_limits, { five_hour: { percent_used: 42, resets_at: '2026-10-20T10:00:00Z' } });
|
|
368
369
|
assert.equal(JSON.stringify(row).includes('reply only ok'), false); // never prompt text
|
|
369
370
|
await flushReport($);
|