proxitor 0.9.0-beta.3 → 0.9.0-beta.4
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 +6 -4
- package/dist/cli.mjs +49 -51
- package/dist/cli.mjs.map +1 -1
- package/dist/prompt.mjs +3 -3
- package/dist/prompt.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -314,16 +314,18 @@ curl http://localhost:8828/health
|
|
|
314
314
|
Proxitor automatically logs cache token usage from upstream responses — both non-streaming JSON and streaming SSE. No configuration needed.
|
|
315
315
|
|
|
316
316
|
```
|
|
317
|
-
[abc123] Cache read: 50000, write: 25000 tokens
|
|
318
|
-
[def456] Cache:
|
|
317
|
+
[abc123] Cache read: 50000, write: 25000 tokens (99.6% hit)
|
|
318
|
+
[def456] Cache read: 1088 tokens (90.0% hit)
|
|
319
|
+
[ghi789] Cache: no cached tokens
|
|
319
320
|
```
|
|
320
321
|
|
|
321
|
-
Supports
|
|
322
|
+
Supports all three provider formats:
|
|
322
323
|
|
|
323
324
|
| Provider format | Fields |
|
|
324
|
-
|
|
325
|
+
| --- | --- |
|
|
325
326
|
| Anthropic | `usage.cache_read_input_tokens` / `usage.cache_creation_input_tokens` |
|
|
326
327
|
| OpenAI / OpenRouter | `usage.prompt_tokens_details.cached_tokens` / `cache_write_tokens` |
|
|
328
|
+
| Responses API | `usage.input_tokens_details.cached_tokens` / `cache_write_tokens` |
|
|
327
329
|
|
|
328
330
|
When both formats are present (e.g., OpenRouter relaying an Anthropic response), Anthropic fields take priority.
|
|
329
331
|
|
package/dist/cli.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { formatWithOptions, styleText } from "node:util";
|
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { dirname, join, resolve, sep } from "node:path";
|
|
10
10
|
import * as l$1 from "node:readline";
|
|
11
|
-
import
|
|
11
|
+
import f from "node:readline";
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
13
|
import { createServer } from "node:net";
|
|
14
14
|
import { STATUS_CODES, createServer as createServer$1 } from "node:http";
|
|
@@ -11463,7 +11463,7 @@ var V = class {
|
|
|
11463
11463
|
this.state = "cancel", this.close();
|
|
11464
11464
|
}, { once: true });
|
|
11465
11465
|
}
|
|
11466
|
-
this.rl =
|
|
11466
|
+
this.rl = f.createInterface({
|
|
11467
11467
|
input: this.input,
|
|
11468
11468
|
tabSize: 2,
|
|
11469
11469
|
prompt: "",
|
|
@@ -19607,7 +19607,7 @@ const r = Object.create(null), i = (e) => globalThis.process?.env || import.meta
|
|
|
19607
19607
|
const e = i(true);
|
|
19608
19608
|
return Object.keys(e);
|
|
19609
19609
|
}
|
|
19610
|
-
}), t = typeof process < "u" && process.env && process.env.NODE_ENV || "", f = [
|
|
19610
|
+
}), t = typeof process < "u" && process.env && process.env.NODE_ENV || "", f$1 = [
|
|
19611
19611
|
["APPVEYOR"],
|
|
19612
19612
|
[
|
|
19613
19613
|
"AWS_AMPLIFY",
|
|
@@ -19699,7 +19699,7 @@ const r = Object.create(null), i = (e) => globalThis.process?.env || import.meta
|
|
|
19699
19699
|
]
|
|
19700
19700
|
];
|
|
19701
19701
|
function b() {
|
|
19702
|
-
if (globalThis.process?.env) for (const e of f) {
|
|
19702
|
+
if (globalThis.process?.env) for (const e of f$1) {
|
|
19703
19703
|
const s = e[1] || e[0];
|
|
19704
19704
|
if (globalThis.process?.env[s]) return {
|
|
19705
19705
|
name: e[0].toLowerCase(),
|
|
@@ -21544,7 +21544,7 @@ async function runConfigMenu(client) {
|
|
|
21544
21544
|
}
|
|
21545
21545
|
//#endregion
|
|
21546
21546
|
//#region src/version.ts
|
|
21547
|
-
const version = "0.9.0-beta.
|
|
21547
|
+
const version = "0.9.0-beta.4";
|
|
21548
21548
|
//#endregion
|
|
21549
21549
|
//#region src/commands/doctor.ts
|
|
21550
21550
|
const DEFAULT_TIMEOUT_MS = 3e3;
|
|
@@ -24571,6 +24571,37 @@ const buildUpstreamReq = createMiddleware(async (c, next) => {
|
|
|
24571
24571
|
});
|
|
24572
24572
|
//#endregion
|
|
24573
24573
|
//#region src/proxy/cache-logging.ts
|
|
24574
|
+
function applyOpenAIDetails(details, result) {
|
|
24575
|
+
let found = false;
|
|
24576
|
+
if (typeof details.cached_tokens === "number" && details.cached_tokens > 0) {
|
|
24577
|
+
result.cacheRead = details.cached_tokens;
|
|
24578
|
+
found = true;
|
|
24579
|
+
}
|
|
24580
|
+
if (typeof details.cache_write_tokens === "number" && details.cache_write_tokens > 0) {
|
|
24581
|
+
result.cacheCreate = details.cache_write_tokens;
|
|
24582
|
+
found = true;
|
|
24583
|
+
}
|
|
24584
|
+
return found;
|
|
24585
|
+
}
|
|
24586
|
+
function applyAnthropicUsage(usage, result) {
|
|
24587
|
+
if (typeof usage.cache_read_input_tokens === "number" && usage.cache_read_input_tokens > 0) result.cacheRead = usage.cache_read_input_tokens;
|
|
24588
|
+
if (typeof usage.cache_creation_input_tokens === "number" && usage.cache_creation_input_tokens > 0) result.cacheCreate = usage.cache_creation_input_tokens;
|
|
24589
|
+
if (typeof usage.input_tokens === "number" && usage.input_tokens > 0) result.inputTokens = usage.input_tokens + result.cacheRead + result.cacheCreate;
|
|
24590
|
+
}
|
|
24591
|
+
function applyOpenAIUsage(usage, result) {
|
|
24592
|
+
const promptDetails = usage.prompt_tokens_details;
|
|
24593
|
+
if (typeof promptDetails === "object" && promptDetails !== null) applyOpenAIDetails(promptDetails, result);
|
|
24594
|
+
if (result.cacheRead === 0 && result.cacheCreate === 0) {
|
|
24595
|
+
const inputDetails = usage.input_tokens_details;
|
|
24596
|
+
if (typeof inputDetails === "object" && inputDetails !== null) applyOpenAIDetails(inputDetails, result);
|
|
24597
|
+
}
|
|
24598
|
+
if (typeof usage.prompt_tokens === "number" && usage.prompt_tokens > 0) result.inputTokens = usage.prompt_tokens;
|
|
24599
|
+
else if (typeof usage.input_tokens === "number" && usage.input_tokens > 0) result.inputTokens = usage.input_tokens;
|
|
24600
|
+
}
|
|
24601
|
+
function extractFromUsage(usage, result) {
|
|
24602
|
+
if (typeof usage.cache_read_input_tokens === "number" || typeof usage.cache_creation_input_tokens === "number") applyAnthropicUsage(usage, result);
|
|
24603
|
+
else applyOpenAIUsage(usage, result);
|
|
24604
|
+
}
|
|
24574
24605
|
function extractCacheUsage(bodyText) {
|
|
24575
24606
|
try {
|
|
24576
24607
|
const parsed = JSON.parse(bodyText);
|
|
@@ -24579,59 +24610,29 @@ function extractCacheUsage(bodyText) {
|
|
|
24579
24610
|
if (typeof usage !== "object" || usage === null) return void 0;
|
|
24580
24611
|
const result = {
|
|
24581
24612
|
cacheRead: 0,
|
|
24582
|
-
cacheCreate: 0
|
|
24613
|
+
cacheCreate: 0,
|
|
24614
|
+
inputTokens: 0
|
|
24583
24615
|
};
|
|
24584
|
-
|
|
24585
|
-
if (typeof usage.cache_creation_input_tokens === "number") result.cacheCreate = usage.cache_creation_input_tokens;
|
|
24586
|
-
const details = usage.prompt_tokens_details;
|
|
24587
|
-
if (typeof details === "object" && details !== null) {
|
|
24588
|
-
if (typeof details.cached_tokens === "number" && details.cached_tokens > 0 && result.cacheRead === 0) result.cacheRead = details.cached_tokens;
|
|
24589
|
-
if (typeof details.cache_write_tokens === "number" && details.cache_write_tokens > 0 && result.cacheCreate === 0) result.cacheCreate = details.cache_write_tokens;
|
|
24590
|
-
}
|
|
24616
|
+
extractFromUsage(usage, result);
|
|
24591
24617
|
return result;
|
|
24592
24618
|
} catch {
|
|
24593
24619
|
return;
|
|
24594
24620
|
}
|
|
24595
24621
|
}
|
|
24596
|
-
function applyAnthropicFields(u, result) {
|
|
24597
|
-
let found = false;
|
|
24598
|
-
if (typeof u.cache_read_input_tokens === "number" && u.cache_read_input_tokens > 0) {
|
|
24599
|
-
result.cacheRead = u.cache_read_input_tokens;
|
|
24600
|
-
found = true;
|
|
24601
|
-
}
|
|
24602
|
-
if (typeof u.cache_creation_input_tokens === "number" && u.cache_creation_input_tokens > 0) {
|
|
24603
|
-
result.cacheCreate = u.cache_creation_input_tokens;
|
|
24604
|
-
found = true;
|
|
24605
|
-
}
|
|
24606
|
-
return found;
|
|
24607
|
-
}
|
|
24608
|
-
function applyOpenAIFields(details, result) {
|
|
24609
|
-
let found = false;
|
|
24610
|
-
if (typeof details.cached_tokens === "number" && details.cached_tokens > 0) {
|
|
24611
|
-
result.cacheRead = details.cached_tokens;
|
|
24612
|
-
found = true;
|
|
24613
|
-
}
|
|
24614
|
-
if (typeof details.cache_write_tokens === "number" && details.cache_write_tokens > 0) {
|
|
24615
|
-
result.cacheCreate = details.cache_write_tokens;
|
|
24616
|
-
found = true;
|
|
24617
|
-
}
|
|
24618
|
-
return found;
|
|
24619
|
-
}
|
|
24620
24622
|
function extractFromEvent(parsed, result) {
|
|
24621
24623
|
if (typeof parsed !== "object" || parsed === null) return false;
|
|
24622
|
-
const
|
|
24624
|
+
const record = parsed;
|
|
24625
|
+
const usage = (record.message ?? record.response ?? parsed).usage;
|
|
24623
24626
|
if (typeof usage !== "object" || usage === null) return false;
|
|
24624
|
-
const
|
|
24625
|
-
|
|
24626
|
-
|
|
24627
|
-
const details = u.prompt_tokens_details;
|
|
24628
|
-
if (typeof details === "object" && details !== null) found = applyOpenAIFields(details, result) || found;
|
|
24629
|
-
return found;
|
|
24627
|
+
const before = result.cacheRead + result.cacheCreate;
|
|
24628
|
+
extractFromUsage(usage, result);
|
|
24629
|
+
return result.cacheRead + result.cacheCreate > before;
|
|
24630
24630
|
}
|
|
24631
24631
|
function extractCacheUsageFromSSE(fullText) {
|
|
24632
24632
|
const result = {
|
|
24633
24633
|
cacheRead: 0,
|
|
24634
|
-
cacheCreate: 0
|
|
24634
|
+
cacheCreate: 0,
|
|
24635
|
+
inputTokens: 0
|
|
24635
24636
|
};
|
|
24636
24637
|
let found = false;
|
|
24637
24638
|
for (const line of fullText.split("\n")) {
|
|
@@ -24648,7 +24649,8 @@ function formatCacheUsage(usage, reqId) {
|
|
|
24648
24649
|
const parts = [];
|
|
24649
24650
|
if (usage.cacheRead > 0) parts.push(`read: ${usage.cacheRead}`);
|
|
24650
24651
|
if (usage.cacheCreate > 0) parts.push(`write: ${usage.cacheCreate}`);
|
|
24651
|
-
|
|
24652
|
+
const pct = usage.inputTokens > 0 && usage.cacheRead > 0 ? ` (${(usage.cacheRead / usage.inputTokens * 100).toFixed(1)}% hit)` : "";
|
|
24653
|
+
logger.info(withReq(reqId, parts.length > 0 ? `Cache ${parts.join(", ")} tokens${pct}` : "Cache: no cached tokens"));
|
|
24652
24654
|
}
|
|
24653
24655
|
function createLoggingStream(contentType, reqId) {
|
|
24654
24656
|
const chunks = [];
|
|
@@ -24811,10 +24813,6 @@ function shouldInjectCacheControl(mode, modelName, path) {
|
|
|
24811
24813
|
if (mode === "always") return true;
|
|
24812
24814
|
return isAnthropicEndpoint(modelName, path);
|
|
24813
24815
|
}
|
|
24814
|
-
const TTL_SECONDS = {
|
|
24815
|
-
"5m": 300,
|
|
24816
|
-
"1h": 3600
|
|
24817
|
-
};
|
|
24818
24816
|
/**
|
|
24819
24817
|
* Build cache_control value for injection.
|
|
24820
24818
|
* Merges existing cache_control with configured TTL.
|
|
@@ -24823,7 +24821,7 @@ const TTL_SECONDS = {
|
|
|
24823
24821
|
function buildCacheControl(existing, ttl, isAnthropic) {
|
|
24824
24822
|
const result = existing !== null && typeof existing === "object" && !Array.isArray(existing) ? { ...existing } : { type: "ephemeral" };
|
|
24825
24823
|
if (!("type" in result)) result.type = "ephemeral";
|
|
24826
|
-
if (ttl && isAnthropic) result.ttl =
|
|
24824
|
+
if (ttl && isAnthropic) result.ttl = ttl;
|
|
24827
24825
|
return result;
|
|
24828
24826
|
}
|
|
24829
24827
|
//#endregion
|