@zhouzhengchang/token-party 0.0.1 → 0.0.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.
Files changed (43) hide show
  1. package/config.example.yaml +3 -1
  2. package/dashboard/assets/index-BKFkELuY.css +1 -0
  3. package/dashboard/assets/index-uKblDhQo.js +130 -0
  4. package/dashboard/index.html +2 -2
  5. package/dist/cli.js +11 -32
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +17 -10
  9. package/dist/config.js.map +1 -1
  10. package/dist/metrics/collector.d.ts +16 -0
  11. package/dist/metrics/collector.d.ts.map +1 -1
  12. package/dist/metrics/collector.js +26 -7
  13. package/dist/metrics/collector.js.map +1 -1
  14. package/dist/proxy/auth.d.ts +3 -1
  15. package/dist/proxy/auth.d.ts.map +1 -1
  16. package/dist/proxy/auth.js +18 -0
  17. package/dist/proxy/auth.js.map +1 -1
  18. package/dist/proxy/forwarder.d.ts +6 -1
  19. package/dist/proxy/forwarder.d.ts.map +1 -1
  20. package/dist/proxy/forwarder.js +83 -9
  21. package/dist/proxy/forwarder.js.map +1 -1
  22. package/dist/proxy/router.d.ts +6 -1
  23. package/dist/proxy/router.d.ts.map +1 -1
  24. package/dist/proxy/router.js +19 -7
  25. package/dist/proxy/router.js.map +1 -1
  26. package/dist/routes/anthropic.js +4 -4
  27. package/dist/routes/anthropic.js.map +1 -1
  28. package/dist/routes/api.d.ts.map +1 -1
  29. package/dist/routes/api.js +13 -5
  30. package/dist/routes/api.js.map +1 -1
  31. package/dist/routes/openai.js +4 -4
  32. package/dist/routes/openai.js.map +1 -1
  33. package/dist/store/db.js +48 -2
  34. package/dist/store/db.js.map +1 -1
  35. package/dist/store/log-writer.js +2 -2
  36. package/dist/store/log-writer.js.map +1 -1
  37. package/dist/types/config.d.ts +190 -22
  38. package/dist/types/config.d.ts.map +1 -1
  39. package/dist/types/config.js +29 -2
  40. package/dist/types/config.js.map +1 -1
  41. package/package.json +10 -3
  42. package/dashboard/assets/index-BOghvbFW.js +0 -131
  43. package/dashboard/assets/index-CEcLJoXS.css +0 -1
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>TokenParty</title>
7
- <script type="module" crossorigin src="/assets/index-BOghvbFW.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-CEcLJoXS.css">
7
+ <script type="module" crossorigin src="/assets/index-uKblDhQo.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-BKFkELuY.css">
9
9
  </head>
10
10
  <body class="bg-gray-50 min-h-screen">
11
11
  <div id="root"></div>
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { serve } from "@hono/node-server";
3
3
  import path from "node:path";
4
+ import os from "node:os";
4
5
  import fs from "node:fs";
5
6
  import { loadConfig, watchConfig } from "./config.js";
6
7
  import { initDb } from "./store/db.js";
@@ -19,10 +20,11 @@ Usage: tokenparty [options]
19
20
  Options:
20
21
  --port <port> Port to listen on (default: 3456)
21
22
  --host <host> Host to bind (default: 0.0.0.0)
22
- --config <path> Path to config.yaml
23
- --init Generate a default config.yaml in current directory
23
+ --config <path> Path to config.yaml (default: ~/.tokenparty/config.yaml)
24
24
  -h, --help Show this help message
25
25
  -v, --version Show version
26
+
27
+ Data is stored in ~/.tokenparty/ (config, logs, database).
26
28
  `);
27
29
  process.exit(0);
28
30
  }
@@ -32,33 +34,9 @@ if (args.includes("--version") || args.includes("-v")) {
32
34
  console.log(`tokenparty v${pkg.version}`);
33
35
  process.exit(0);
34
36
  }
35
- if (args.includes("--init")) {
36
- const dest = path.resolve(process.cwd(), "config.yaml");
37
- if (fs.existsSync(dest)) {
38
- console.log("[tokenparty] config.yaml already exists, skipping.");
39
- }
40
- else {
41
- const examplePath = path.resolve(import.meta.dirname, "../config.example.yaml");
42
- if (fs.existsSync(examplePath)) {
43
- fs.copyFileSync(examplePath, dest);
44
- }
45
- else {
46
- const defaultConfig = `server:
47
- port: 3456
48
- host: 0.0.0.0
49
- logDir: ./logs
50
- dataDir: ./data
51
- providers: []
52
- tokens: []
53
- `;
54
- fs.writeFileSync(dest, defaultConfig, "utf-8");
55
- }
56
- console.log("[tokenparty] Created config.yaml in current directory.");
57
- console.log("[tokenparty] Edit it to add your providers and tokens, then run: tokenparty");
58
- }
59
- process.exit(0);
60
- }
61
- const configPath = getArg("config");
37
+ const homeDir = path.join(os.homedir(), ".tokenparty");
38
+ fs.mkdirSync(homeDir, { recursive: true });
39
+ const configPath = getArg("config") ?? path.join(homeDir, "config.yaml");
62
40
  const config = loadConfig(configPath);
63
41
  const port = getArg("port") ? Number(getArg("port")) : config.server.port;
64
42
  const host = getArg("host") ?? config.server.host;
@@ -68,10 +46,11 @@ watchConfig((newConfig) => {
68
46
  console.log(`[tokenparty] Config reloaded`);
69
47
  });
70
48
  serve({ fetch: app.fetch, port, hostname: host }, (info) => {
71
- console.log(`[tokenparty] Proxy running at http://${info.address}:${info.port}`);
72
- console.log(`[tokenparty] Dashboard: http://${info.address === "0.0.0.0" ? "localhost" : info.address}:${info.port}/`);
49
+ const addr = info.address === "0.0.0.0" ? "localhost" : info.address;
50
+ console.log(`[tokenparty] Proxy running at http://${addr}:${info.port}`);
51
+ console.log(`[tokenparty] Dashboard: http://${addr}:${info.port}/`);
73
52
  console.log(`[tokenparty] OpenAI endpoint: /v1/*`);
74
53
  console.log(`[tokenparty] Anthropic endpoint: /anthropic/*`);
75
- console.log(`[tokenparty] Dashboard API: /api/*`);
54
+ console.log(`[tokenparty] Config: ${configPath}`);
76
55
  });
77
56
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAuB9D,wBAAgB,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAqBpD;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,gCAY9D;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,MAAM,CAMpF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA8B9D,wBAAgB,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAuBpD;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,gCAY9D;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,MAAM,CAMpF"}
package/dist/config.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import os from "node:os";
3
4
  import { parse, stringify } from "yaml";
4
5
  import { watch } from "chokidar";
5
6
  import { ConfigSchema } from "./types/config.js";
@@ -23,23 +24,29 @@ function resolveConfigEnvVars(obj) {
23
24
  }
24
25
  return obj;
25
26
  }
27
+ const DEFAULT_CONFIG = `server:
28
+ port: 3456
29
+ host: 0.0.0.0
30
+ providers: []
31
+ tokens: []
32
+ `;
26
33
  export function loadConfig(filePath) {
27
- configPath = filePath ?? path.resolve(process.cwd(), "config.yaml");
34
+ const defaultPath = path.join(os.homedir(), ".tokenparty", "config.yaml");
35
+ configPath = filePath ?? defaultPath;
28
36
  if (!fs.existsSync(configPath)) {
29
- const examplePath = path.resolve(process.cwd(), "config.example.yaml");
30
- if (fs.existsSync(examplePath)) {
31
- fs.copyFileSync(examplePath, configPath);
32
- }
33
- else {
34
- throw new Error(`Config file not found: ${configPath}`);
35
- }
37
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
38
+ fs.writeFileSync(configPath, DEFAULT_CONFIG, "utf-8");
39
+ console.log(`[tokenparty] Created default config at ${configPath}`);
36
40
  }
37
41
  rawContent = fs.readFileSync(configPath, "utf-8");
38
42
  const parsed = parse(rawContent);
39
43
  const resolved = resolveConfigEnvVars(parsed);
40
44
  currentConfig = ConfigSchema.parse(resolved);
41
- fs.mkdirSync(path.resolve(path.dirname(configPath), currentConfig.server.logDir), { recursive: true });
42
- fs.mkdirSync(path.resolve(path.dirname(configPath), currentConfig.server.dataDir), { recursive: true });
45
+ const configDir = path.dirname(configPath);
46
+ currentConfig.server.logDir = path.resolve(configDir, currentConfig.server.logDir);
47
+ currentConfig.server.dataDir = path.resolve(configDir, currentConfig.server.dataDir);
48
+ fs.mkdirSync(currentConfig.server.logDir, { recursive: true });
49
+ fs.mkdirSync(currentConfig.server.dataDir, { recursive: true });
43
50
  return currentConfig;
44
51
  }
45
52
  export function getConfig() {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -5,10 +5,26 @@ export interface RequestRecord {
5
5
  model: string;
6
6
  inputTokens: number;
7
7
  outputTokens: number;
8
+ cacheReadTokens?: number;
9
+ cacheWriteTokens?: number;
8
10
  latencyMs: number;
9
11
  status: number;
10
12
  logFile: string;
11
13
  error?: string;
14
+ apiKeyIndex?: number;
15
+ pricing?: {
16
+ inputPrice?: number;
17
+ outputPrice?: number;
18
+ cacheReadPrice?: number;
19
+ cacheWritePrice?: number;
20
+ };
21
+ currency?: string;
12
22
  }
23
+ export declare function calculateCost(inputTokens: number, outputTokens: number, cacheReadTokens: number, cacheWriteTokens: number, pricing?: {
24
+ inputPrice?: number;
25
+ outputPrice?: number;
26
+ cacheReadPrice?: number;
27
+ cacheWritePrice?: number;
28
+ }): number;
13
29
  export declare function recordRequest(record: RequestRecord): void;
14
30
  //# sourceMappingURL=collector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/metrics/collector.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,QAuBlD"}
1
+ {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/metrics/collector.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3G,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,MAAM,EACxB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GACzG,MAAM,CAQR;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,QA+BlD"}
@@ -1,20 +1,39 @@
1
1
  import { getDb } from "../store/db.js";
2
+ export function calculateCost(inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, pricing) {
3
+ if (!pricing)
4
+ return 0;
5
+ const nonCachedInput = Math.max(0, inputTokens - cacheReadTokens);
6
+ const inputCost = (nonCachedInput / 1_000_000) * (pricing.inputPrice ?? 0);
7
+ const cacheReadCost = (cacheReadTokens / 1_000_000) * (pricing.cacheReadPrice ?? pricing.inputPrice ?? 0);
8
+ const cacheWriteCost = (cacheWriteTokens / 1_000_000) * (pricing.cacheWritePrice ?? pricing.inputPrice ?? 0);
9
+ const outputCost = (outputTokens / 1_000_000) * (pricing.outputPrice ?? 0);
10
+ return inputCost + cacheReadCost + cacheWriteCost + outputCost;
11
+ }
2
12
  export function recordRequest(record) {
3
13
  const db = getDb();
4
14
  const now = Date.now();
5
15
  const date = new Date(now).toISOString().split("T")[0];
16
+ const cacheReadTokens = record.cacheReadTokens ?? 0;
17
+ const cacheWriteTokens = record.cacheWriteTokens ?? 0;
18
+ const CNY_TO_USD = 1 / 7.2;
19
+ let cost = calculateCost(record.inputTokens, record.outputTokens, cacheReadTokens, cacheWriteTokens, record.pricing);
20
+ if (record.currency === "CNY")
21
+ cost *= CNY_TO_USD;
6
22
  db.prepare(`
7
- INSERT INTO request_index (id, timestamp, token_id, provider_id, model, input_tokens, output_tokens, latency_ms, status, log_file, error)
8
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9
- `).run(record.id, now, record.tokenId, record.providerId, record.model, record.inputTokens, record.outputTokens, record.latencyMs, record.status, record.logFile, record.error ?? null);
23
+ INSERT INTO request_index (id, timestamp, token_id, provider_id, model, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, latency_ms, status, log_file, error, api_key_index, cost)
24
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
25
+ `).run(record.id, now, record.tokenId, record.providerId, record.model, record.inputTokens, record.outputTokens, cacheReadTokens, cacheWriteTokens, record.latencyMs, record.status, record.logFile, record.error ?? null, record.apiKeyIndex ?? 0, cost);
10
26
  db.prepare(`
11
- INSERT INTO usage_daily (date, token_id, provider_id, model, request_count, input_tokens, output_tokens)
12
- VALUES (?, ?, ?, ?, 1, ?, ?)
27
+ INSERT INTO usage_daily (date, token_id, provider_id, model, request_count, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost)
28
+ VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?, ?)
13
29
  ON CONFLICT(date, token_id, provider_id, model)
14
30
  DO UPDATE SET
15
31
  request_count = request_count + 1,
16
32
  input_tokens = input_tokens + excluded.input_tokens,
17
- output_tokens = output_tokens + excluded.output_tokens
18
- `).run(date, record.tokenId, record.providerId, record.model, record.inputTokens, record.outputTokens);
33
+ output_tokens = output_tokens + excluded.output_tokens,
34
+ cache_read_tokens = cache_read_tokens + excluded.cache_read_tokens,
35
+ cache_write_tokens = cache_write_tokens + excluded.cache_write_tokens,
36
+ cost = cost + excluded.cost
37
+ `).run(date, record.tokenId, record.providerId, record.model, record.inputTokens, record.outputTokens, cacheReadTokens, cacheWriteTokens, cost);
19
38
  }
20
39
  //# sourceMappingURL=collector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"collector.js","sourceRoot":"","sources":["../../src/metrics/collector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAevC,MAAM,UAAU,aAAa,CAAC,MAAqB;IACjD,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvD,EAAE,CAAC,OAAO,CAAC;;;GAGV,CAAC,CAAC,GAAG,CACJ,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,EAC/D,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,SAAS,EACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI,CACpD,CAAC;IAEF,EAAE,CAAC,OAAO,CAAC;;;;;;;;GAQV,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;AACzG,CAAC"}
1
+ {"version":3,"file":"collector.js","sourceRoot":"","sources":["../../src/metrics/collector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAoBvC,MAAM,UAAU,aAAa,CAC3B,WAAmB,EACnB,YAAoB,EACpB,eAAuB,EACvB,gBAAwB,EACxB,OAA0G;IAE1G,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC;IACvB,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,eAAe,CAAC,CAAC;IAClE,MAAM,SAAS,GAAG,CAAC,cAAc,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;IAC3E,MAAM,aAAa,GAAG,CAAC,eAAe,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;IAC1G,MAAM,cAAc,GAAG,CAAC,gBAAgB,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;IAC7G,MAAM,UAAU,GAAG,CAAC,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IAC3E,OAAO,SAAS,GAAG,aAAa,GAAG,cAAc,GAAG,UAAU,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAqB;IACjD,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,IAAI,CAAC,CAAC;IACpD,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC;IACtD,MAAM,UAAU,GAAG,CAAC,GAAG,GAAG,CAAC;IAC3B,IAAI,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACrH,IAAI,MAAM,CAAC,QAAQ,KAAK,KAAK;QAAE,IAAI,IAAI,UAAU,CAAC;IAElD,EAAE,CAAC,OAAO,CAAC;;;GAGV,CAAC,CAAC,GAAG,CACJ,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,EAC/D,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,CAAC,SAAS,EAC5F,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI,EAAE,MAAM,CAAC,WAAW,IAAI,CAAC,EAAE,IAAI,CACnF,CAAC;IAEF,EAAE,CAAC,OAAO,CAAC;;;;;;;;;;;GAWV,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;AAClJ,CAAC"}
@@ -2,5 +2,7 @@ import type { Context, Next } from "hono";
2
2
  import type { AppEnv } from "../types/env.js";
3
3
  export declare function authMiddleware(c: Context<AppEnv>, next: Next): Promise<(Response & import("hono").TypedResponse<{
4
4
  error: string;
5
- }, 401, "json">) | undefined>;
5
+ }, 401, "json">) | (Response & import("hono").TypedResponse<{
6
+ error: string;
7
+ }, 429, "json">) | undefined>;
6
8
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/proxy/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,wBAAsB,cAAc,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI;;8BAgBlE"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/proxy/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,wBAAsB,cAAc,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI;;;;8BAwClE"}
@@ -1,4 +1,5 @@
1
1
  import { getConfig } from "../config.js";
2
+ import { getDb } from "../store/db.js";
2
3
  export async function authMiddleware(c, next) {
3
4
  const config = getConfig();
4
5
  const authHeader = c.req.header("Authorization") ?? "";
@@ -10,6 +11,23 @@ export async function authMiddleware(c, next) {
10
11
  if (!token) {
11
12
  return c.json({ error: "Invalid or disabled token" }, 401);
12
13
  }
14
+ if (token.quota) {
15
+ const db = getDb();
16
+ const today = new Date().toISOString().split("T")[0];
17
+ if (token.quota.daily) {
18
+ const row = db.prepare(`SELECT COALESCE(SUM(input_tokens + output_tokens), 0) as total FROM usage_daily WHERE token_id = ? AND date = ?`).get(token.key, today);
19
+ if (row.total >= token.quota.daily) {
20
+ return c.json({ error: "Daily token quota exceeded" }, 429);
21
+ }
22
+ }
23
+ if (token.quota.monthly) {
24
+ const monthStart = today.slice(0, 7) + "-01";
25
+ const row = db.prepare(`SELECT COALESCE(SUM(input_tokens + output_tokens), 0) as total FROM usage_daily WHERE token_id = ? AND date >= ?`).get(token.key, monthStart);
26
+ if (row.total >= token.quota.monthly) {
27
+ return c.json({ error: "Monthly token quota exceeded" }, 429);
28
+ }
29
+ }
30
+ }
13
31
  c.set("authToken", token);
14
32
  await next();
15
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/proxy/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,CAAkB,EAAE,IAAU;IACjE,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IACvD,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAElD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,2BAA2B,EAAE,EAAE,GAAG,CAAC,CAAC;IAC7D,CAAC;IAED,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC1B,MAAM,IAAI,EAAE,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/proxy/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,CAAkB,EAAE,IAAU;IACjE,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IACvD,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAElD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,2BAA2B,EAAE,EAAE,GAAG,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;QACnB,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAErD,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CACpB,iHAAiH,CAClH,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAsB,CAAC;YAC7C,IAAI,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACnC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,4BAA4B,EAAE,EAAE,GAAG,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACxB,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;YAC7C,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CACpB,kHAAkH,CACnH,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,UAAU,CAAsB,CAAC;YAClD,IAAI,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBACrC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;IACH,CAAC;IAED,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC1B,MAAM,IAAI,EAAE,CAAC;AACf,CAAC"}
@@ -2,5 +2,10 @@ import type { Context } from "hono";
2
2
  import type { AppEnv } from "../types/env.js";
3
3
  import type { Provider } from "../types/config.js";
4
4
  export type EntryProtocol = "openai" | "anthropic";
5
- export declare function forwardRequest(c: Context<AppEnv>, provider: Provider, targetPath: string, transformedBody?: unknown, entryProtocol?: EntryProtocol): Promise<Response>;
5
+ export declare function forwardRequest(c: Context<AppEnv>, provider: Provider, targetPath: string, transformedBody?: unknown, entryProtocol?: EntryProtocol, pricing?: {
6
+ inputPrice?: number;
7
+ outputPrice?: number;
8
+ cacheReadPrice?: number;
9
+ cacheWritePrice?: number;
10
+ }): Promise<Response>;
6
11
  //# sourceMappingURL=forwarder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"forwarder.d.ts","sourceRoot":"","sources":["../../src/proxy/forwarder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAOnD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEnD,wBAAsB,cAAc,CAClC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAClB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,OAAO,EACzB,aAAa,CAAC,EAAE,aAAa,qBA0O9B"}
1
+ {"version":3,"file":"forwarder.d.ts","sourceRoot":"","sources":["../../src/proxy/forwarder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AASnD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,WAAW,CAAC;AAkBnD,wBAAsB,cAAc,CAClC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAClB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,OAAO,EACzB,aAAa,CAAC,EAAE,aAAa,EAC7B,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GACzG,OAAO,CAAC,QAAQ,CAAC,CAsQnB"}
@@ -1,10 +1,27 @@
1
1
  import { streamSSE } from "hono/streaming";
2
+ import { getModelId, getModelPricing } from "../types/config.js";
3
+ import { getConfig } from "../config.js";
2
4
  import { nanoid } from "nanoid";
3
5
  import { writeLog, headersToRecord } from "../store/log-writer.js";
4
6
  import { recordRequest } from "../metrics/collector.js";
5
7
  import { createGunzip, createInflate, createBrotliDecompress, createZstdDecompress } from "node:zlib";
6
8
  import { Readable } from "node:stream";
7
- export async function forwardRequest(c, provider, targetPath, transformedBody, entryProtocol) {
9
+ const roundRobinCounters = new Map();
10
+ function selectApiKey(provider) {
11
+ const keys = Array.isArray(provider.apiKey) ? provider.apiKey : [provider.apiKey];
12
+ if (keys.length === 1)
13
+ return { key: keys[0], index: 0 };
14
+ const counter = (roundRobinCounters.get(provider.id) ?? -1) + 1;
15
+ const index = counter % keys.length;
16
+ roundRobinCounters.set(provider.id, counter);
17
+ return { key: keys[index], index };
18
+ }
19
+ function maskApiKey(key) {
20
+ if (key.length <= 8)
21
+ return "****";
22
+ return key.slice(0, 4) + "****" + key.slice(-4);
23
+ }
24
+ export async function forwardRequest(c, provider, targetPath, transformedBody, entryProtocol, pricing) {
8
25
  const requestId = nanoid();
9
26
  const startTime = Date.now();
10
27
  const body = transformedBody ?? (await c.req.json());
@@ -18,6 +35,7 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
18
35
  body.stream_options = { include_usage: true };
19
36
  }
20
37
  const targetUrl = `${provider.baseUrl}${targetPath}`;
38
+ const { key: selectedKey, index: apiKeyIndex } = selectApiKey(provider);
21
39
  const upstreamHeaders = {};
22
40
  const skipHeaders = new Set(["host", "connection", "content-length"]);
23
41
  c.req.raw.headers.forEach((value, key) => {
@@ -26,11 +44,11 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
26
44
  }
27
45
  });
28
46
  if (provider.type === "openai") {
29
- upstreamHeaders["authorization"] = `Bearer ${provider.apiKey}`;
47
+ upstreamHeaders["authorization"] = `Bearer ${selectedKey}`;
30
48
  }
31
49
  else if (provider.type === "anthropic") {
32
50
  delete upstreamHeaders["authorization"];
33
- upstreamHeaders["x-api-key"] = provider.apiKey;
51
+ upstreamHeaders["x-api-key"] = selectedKey;
34
52
  upstreamHeaders["anthropic-version"] ??= "2023-06-01";
35
53
  }
36
54
  // Capture incoming request headers (sanitize auth)
@@ -46,7 +64,7 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
46
64
  const logFile = writeLog(requestId, {
47
65
  type: "request",
48
66
  timestamp: startTime,
49
- headers: { ...reqHeaders, "x-target-url": targetUrl, "x-entry-protocol": entry, "x-provider-type": provider.type },
67
+ headers: { ...reqHeaders, "x-target-url": targetUrl, "x-entry-protocol": entry, "x-provider-type": provider.type, "x-api-key-index": String(apiKeyIndex), "x-api-key-used": maskApiKey(selectedKey) },
50
68
  body,
51
69
  });
52
70
  const token = c.get("authToken");
@@ -98,10 +116,10 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
98
116
  await s.writeSSE({ data: JSON.stringify(converted.chunk) });
99
117
  }
100
118
  if (parsed.type === "message_delta" && parsed.usage) {
101
- usage = { input_tokens: parsed.usage.input_tokens ?? 0, output_tokens: parsed.usage.output_tokens ?? 0 };
119
+ usage = { input_tokens: parsed.usage.input_tokens ?? 0, output_tokens: parsed.usage.output_tokens ?? 0, cache_read_tokens: parsed.usage.cache_read_input_tokens ?? 0, cache_write_tokens: parsed.usage.cache_creation_input_tokens ?? 0 };
102
120
  }
103
121
  if (parsed.type === "message_start" && parsed.message?.usage) {
104
- usage = { ...usage, input_tokens: parsed.message.usage.input_tokens ?? 0 };
122
+ usage = { ...usage, input_tokens: parsed.message.usage.input_tokens ?? 0, cache_read_tokens: parsed.message.usage.cache_read_input_tokens ?? 0, cache_write_tokens: parsed.message.usage.cache_creation_input_tokens ?? 0 };
105
123
  }
106
124
  }
107
125
  else if (needsStreamConversion && provider.type === "openai" && entry === "anthropic") {
@@ -114,7 +132,7 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
114
132
  fullContent += converted.content;
115
133
  }
116
134
  if (parsed.usage) {
117
- usage = { input_tokens: parsed.usage.prompt_tokens ?? 0, output_tokens: parsed.usage.completion_tokens ?? 0 };
135
+ usage = { input_tokens: parsed.usage.prompt_tokens ?? 0, output_tokens: parsed.usage.completion_tokens ?? 0, cache_read_tokens: parsed.usage.prompt_tokens_details?.cached_tokens ?? 0, cache_write_tokens: 0 };
118
136
  }
119
137
  }
120
138
  else {
@@ -153,6 +171,8 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
153
171
  usage = {
154
172
  input_tokens: evt.response.usage.input_tokens ?? 0,
155
173
  output_tokens: evt.response.usage.output_tokens ?? 0,
174
+ cache_read_tokens: evt.response.usage.cache_read_input_tokens ?? 0,
175
+ cache_write_tokens: evt.response.usage.cache_creation_input_tokens ?? 0,
156
176
  };
157
177
  break;
158
178
  }
@@ -160,6 +180,8 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
160
180
  usage = {
161
181
  input_tokens: evt.usage.prompt_tokens ?? evt.usage.input_tokens ?? 0,
162
182
  output_tokens: evt.usage.completion_tokens ?? evt.usage.output_tokens ?? 0,
183
+ cache_read_tokens: evt.usage.prompt_tokens_details?.cached_tokens ?? evt.usage.cache_read_input_tokens ?? 0,
184
+ cache_write_tokens: evt.usage.cache_creation_input_tokens ?? 0,
163
185
  };
164
186
  break;
165
187
  }
@@ -181,9 +203,14 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
181
203
  model,
182
204
  inputTokens: usage?.input_tokens ?? 0,
183
205
  outputTokens: usage?.output_tokens ?? 0,
206
+ cacheReadTokens: usage?.cache_read_tokens ?? 0,
207
+ cacheWriteTokens: usage?.cache_write_tokens ?? 0,
184
208
  latencyMs: Date.now() - startTime,
185
209
  status: response.status,
186
210
  logFile,
211
+ apiKeyIndex,
212
+ pricing,
213
+ currency: provider.currency,
187
214
  });
188
215
  }
189
216
  });
@@ -204,10 +231,20 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
204
231
  model,
205
232
  inputTokens: usage?.input_tokens ?? 0,
206
233
  outputTokens: usage?.output_tokens ?? 0,
234
+ cacheReadTokens: usage?.cache_read_tokens ?? 0,
235
+ cacheWriteTokens: usage?.cache_write_tokens ?? 0,
207
236
  latencyMs,
208
237
  status: response.status,
209
238
  logFile,
239
+ apiKeyIndex,
240
+ pricing,
241
+ currency: provider.currency,
210
242
  });
243
+ if (response.status >= 500 && provider.fallback) {
244
+ const fallbackResult = tryFallback(c, provider, model, targetPath, body, entryProtocol, requestId);
245
+ if (fallbackResult)
246
+ return fallbackResult;
247
+ }
211
248
  return c.json(responseBody, response.status);
212
249
  }
213
250
  catch (error) {
@@ -228,10 +265,39 @@ export async function forwardRequest(c, provider, targetPath, transformedBody, e
228
265
  status: 502,
229
266
  logFile,
230
267
  error: error.message,
268
+ apiKeyIndex,
269
+ pricing,
270
+ currency: provider.currency,
231
271
  });
272
+ if (provider.fallback) {
273
+ const fallbackResult = tryFallback(c, provider, model, targetPath, body, entryProtocol, requestId);
274
+ if (fallbackResult)
275
+ return fallbackResult;
276
+ }
232
277
  return c.json({ error: "Upstream request failed", detail: error.message }, 502);
233
278
  }
234
279
  }
280
+ function tryFallback(c, provider, model, targetPath, body, entryProtocol, originalRequestId) {
281
+ if (!provider.fallback)
282
+ return null;
283
+ const config = getConfig();
284
+ const fallbackProvider = config.providers.find((p) => p.id === provider.fallback && p.enabled);
285
+ if (!fallbackProvider)
286
+ return null;
287
+ const modelConfig = fallbackProvider.models.find((m) => getModelId(m) === model);
288
+ if (!modelConfig)
289
+ return null;
290
+ const fallbackPricing = getModelPricing(modelConfig);
291
+ let fallbackPath = targetPath;
292
+ if (fallbackProvider.type !== provider.type) {
293
+ if (fallbackProvider.type === "anthropic")
294
+ fallbackPath = "/v1/messages";
295
+ else
296
+ fallbackPath = "/chat/completions";
297
+ }
298
+ console.log(`[tokenparty] Falling back from ${provider.id} to ${fallbackProvider.id} for model ${model}`);
299
+ return forwardRequest(c, fallbackProvider, fallbackPath, body, entryProtocol, fallbackPricing);
300
+ }
235
301
  // --- Anthropic SSE chunk → OpenAI SSE chunk ---
236
302
  function convertAnthropicChunkToOpenai(parsed, model, id) {
237
303
  if (parsed.type === "content_block_delta" && parsed.delta?.type === "text_delta") {
@@ -370,12 +436,16 @@ function extractUsage(body, providerType) {
370
436
  return {
371
437
  input_tokens: body.usage.prompt_tokens ?? body.usage.input_tokens ?? 0,
372
438
  output_tokens: body.usage.completion_tokens ?? body.usage.output_tokens ?? 0,
439
+ cache_read_tokens: body.usage.prompt_tokens_details?.cached_tokens ?? body.usage.cache_read_input_tokens ?? 0,
440
+ cache_write_tokens: body.usage.cache_creation_input_tokens ?? 0,
373
441
  };
374
442
  }
375
443
  if (providerType === "anthropic") {
376
444
  return {
377
445
  input_tokens: body.usage.input_tokens ?? 0,
378
446
  output_tokens: body.usage.output_tokens ?? 0,
447
+ cache_read_tokens: body.usage.cache_read_input_tokens ?? 0,
448
+ cache_write_tokens: body.usage.cache_creation_input_tokens ?? 0,
379
449
  };
380
450
  }
381
451
  return undefined;
@@ -386,21 +456,25 @@ function extractUsageFromChunk(parsed, providerType) {
386
456
  return {
387
457
  input_tokens: parsed.response.usage.input_tokens ?? 0,
388
458
  output_tokens: parsed.response.usage.output_tokens ?? 0,
459
+ cache_read_tokens: parsed.response.usage.cache_read_input_tokens ?? 0,
460
+ cache_write_tokens: parsed.response.usage.cache_creation_input_tokens ?? 0,
389
461
  };
390
462
  }
391
463
  if (parsed.usage) {
392
464
  return {
393
465
  input_tokens: parsed.usage.prompt_tokens ?? parsed.usage.input_tokens ?? 0,
394
466
  output_tokens: parsed.usage.completion_tokens ?? parsed.usage.output_tokens ?? 0,
467
+ cache_read_tokens: parsed.usage.prompt_tokens_details?.cached_tokens ?? parsed.usage.cache_read_input_tokens ?? 0,
468
+ cache_write_tokens: parsed.usage.cache_creation_input_tokens ?? 0,
395
469
  };
396
470
  }
397
471
  }
398
472
  if (providerType === "anthropic") {
399
473
  if (parsed.type === "message_delta" && parsed.usage) {
400
- return { input_tokens: parsed.usage.input_tokens ?? 0, output_tokens: parsed.usage.output_tokens ?? 0 };
474
+ return { input_tokens: parsed.usage.input_tokens ?? 0, output_tokens: parsed.usage.output_tokens ?? 0, cache_read_tokens: parsed.usage.cache_read_input_tokens ?? 0, cache_write_tokens: parsed.usage.cache_creation_input_tokens ?? 0 };
401
475
  }
402
476
  if (parsed.type === "message_start" && parsed.message?.usage) {
403
- return { input_tokens: parsed.message.usage.input_tokens ?? 0, output_tokens: 0 };
477
+ return { input_tokens: parsed.message.usage.input_tokens ?? 0, output_tokens: 0, cache_read_tokens: parsed.message.usage.cache_read_input_tokens ?? 0, cache_write_tokens: parsed.message.usage.cache_creation_input_tokens ?? 0 };
404
478
  }
405
479
  }
406
480
  return undefined;