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 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: no cached tokens
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 both provider formats:
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 l__default from "node:readline";
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 = l__default.createInterface({
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.3";
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
- if (typeof usage.cache_read_input_tokens === "number") result.cacheRead = usage.cache_read_input_tokens;
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 usage = (parsed.message ?? parsed).usage;
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 u = usage;
24625
- let found = false;
24626
- found = applyAnthropicFields(u, result) || found;
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
- logger.info(withReq(reqId, parts.length > 0 ? `Cache ${parts.join(", ")} tokens` : "Cache: no cached tokens"));
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 = TTL_SECONDS[ttl];
24824
+ if (ttl && isAnthropic) result.ttl = ttl;
24827
24825
  return result;
24828
24826
  }
24829
24827
  //#endregion