@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.
- package/config.example.yaml +3 -1
- package/dashboard/assets/index-BKFkELuY.css +1 -0
- package/dashboard/assets/index-uKblDhQo.js +130 -0
- package/dashboard/index.html +2 -2
- package/dist/cli.js +11 -32
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -10
- package/dist/config.js.map +1 -1
- package/dist/metrics/collector.d.ts +16 -0
- package/dist/metrics/collector.d.ts.map +1 -1
- package/dist/metrics/collector.js +26 -7
- package/dist/metrics/collector.js.map +1 -1
- package/dist/proxy/auth.d.ts +3 -1
- package/dist/proxy/auth.d.ts.map +1 -1
- package/dist/proxy/auth.js +18 -0
- package/dist/proxy/auth.js.map +1 -1
- package/dist/proxy/forwarder.d.ts +6 -1
- package/dist/proxy/forwarder.d.ts.map +1 -1
- package/dist/proxy/forwarder.js +83 -9
- package/dist/proxy/forwarder.js.map +1 -1
- package/dist/proxy/router.d.ts +6 -1
- package/dist/proxy/router.d.ts.map +1 -1
- package/dist/proxy/router.js +19 -7
- package/dist/proxy/router.js.map +1 -1
- package/dist/routes/anthropic.js +4 -4
- package/dist/routes/anthropic.js.map +1 -1
- package/dist/routes/api.d.ts.map +1 -1
- package/dist/routes/api.js +13 -5
- package/dist/routes/api.js.map +1 -1
- package/dist/routes/openai.js +4 -4
- package/dist/routes/openai.js.map +1 -1
- package/dist/store/db.js +48 -2
- package/dist/store/db.js.map +1 -1
- package/dist/store/log-writer.js +2 -2
- package/dist/store/log-writer.js.map +1 -1
- package/dist/types/config.d.ts +190 -22
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +29 -2
- package/dist/types/config.js.map +1 -1
- package/package.json +10 -3
- package/dashboard/assets/index-BOghvbFW.js +0 -131
- package/dashboard/assets/index-CEcLJoXS.css +0 -1
package/dashboard/index.html
CHANGED
|
@@ -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-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
72
|
-
console.log(`[tokenparty]
|
|
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]
|
|
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":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,SAAS,MAAM,CAAC,IAAY;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACtC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
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
|
-
|
|
34
|
+
const defaultPath = path.join(os.homedir(), ".tokenparty", "config.yaml");
|
|
35
|
+
configPath = filePath ?? defaultPath;
|
|
28
36
|
if (!fs.existsSync(configPath)) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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() {
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,YAAY,EAAe,MAAM,mBAAmB,CAAC;AAE9D,IAAI,aAAqB,CAAC;AAC1B,IAAI,UAAkB,CAAC;AACvB,IAAI,UAAkB,CAAC;AAEvB,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,oBAAoB,CAAC,GAAY;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;IACxD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAC7D,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACzC,MAAM,CAAC,CAAC,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAiB;IAC1C,
|
|
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;
|
|
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
|
-
|
|
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;
|
|
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"}
|
package/dist/proxy/auth.d.ts
CHANGED
|
@@ -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">) |
|
|
5
|
+
}, 401, "json">) | (Response & import("hono").TypedResponse<{
|
|
6
|
+
error: string;
|
|
7
|
+
}, 429, "json">) | undefined>;
|
|
6
8
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/proxy/auth.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/proxy/auth.js
CHANGED
|
@@ -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
|
}
|
package/dist/proxy/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/proxy/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,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
|
|
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;
|
|
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"}
|
package/dist/proxy/forwarder.js
CHANGED
|
@@ -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
|
-
|
|
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 ${
|
|
47
|
+
upstreamHeaders["authorization"] = `Bearer ${selectedKey}`;
|
|
30
48
|
}
|
|
31
49
|
else if (provider.type === "anthropic") {
|
|
32
50
|
delete upstreamHeaders["authorization"];
|
|
33
|
-
upstreamHeaders["x-api-key"] =
|
|
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;
|