myapikey 0.19.2 → 0.19.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myapikey",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Personal LLM API gateway & proxy — one address + one API key for all your models. Forwards OpenAI & Anthropic calls to your backends with failover and a circuit breaker. Pure passthrough, no format translation. Self-hosted (CLI + web UI).",
|
|
6
6
|
"keywords": [
|
|
@@ -70,10 +70,19 @@ type UsageFields = Partial<Pick<Usage, "input" | "output" | "cacheRead" | "cache
|
|
|
70
70
|
* `message_delta.usage.output_tokens` (output); a non-streaming Message
|
|
71
71
|
* carries `usage` directly (both).
|
|
72
72
|
* - openai chat: only the final chunk (with stream_options.include_usage) has
|
|
73
|
-
* `usage` (prompt/completion tokens
|
|
73
|
+
* `usage` (prompt/completion tokens + `prompt_tokens_details.cached_tokens`
|
|
74
|
+
* / DeepSeek's `prompt_cache_hit_tokens` for cache hits); the non-streaming
|
|
75
|
+
* body has it too.
|
|
74
76
|
* - responses: `response.usage` on response.completed/in-progress, or top-level
|
|
75
77
|
* `usage` on a non-streaming Response. Accepts both the `*_tokens` and bare
|
|
76
|
-
* `input`/`output` spellings the API has used over time
|
|
78
|
+
* `input`/`output` spellings the API has used over time, plus
|
|
79
|
+
* `input_tokens_details.cached_tokens` for cache hits.
|
|
80
|
+
*
|
|
81
|
+
* Cache-hit fields (`cacheRead` etc.) are captured on every wire that reports
|
|
82
|
+
* them. On the OpenAI-family wires the reported `input`/`prompt_tokens`
|
|
83
|
+
* INCLUDES the cached subset, so it is subtracted here — `Usage.input` means
|
|
84
|
+
* UNCACHED prompt tokens on every wire (Anthropic's native semantics), which
|
|
85
|
+
* is what the store's cache-hit-rate formula assumes. */
|
|
77
86
|
function extractUsage(obj: any, key: RouteKey): UsageFields | null {
|
|
78
87
|
if (!obj || typeof obj !== "object") return null;
|
|
79
88
|
|
|
@@ -94,13 +103,31 @@ function extractUsage(obj: any, key: RouteKey): UsageFields | null {
|
|
|
94
103
|
if (u) {
|
|
95
104
|
const input = num(u.input_tokens) ?? num(u.input);
|
|
96
105
|
const output = num(u.output_tokens) ?? num(u.output);
|
|
97
|
-
|
|
106
|
+
// OpenAI Responses reports cache hits inside `input_tokens_details` — a
|
|
107
|
+
// SUBSET of input_tokens (like chat/completions, unlike Anthropic where
|
|
108
|
+
// input_tokens excludes cache), so subtract to keep Usage.input = uncached.
|
|
109
|
+
const details = u.input_tokens_details as { cached_tokens?: unknown } | undefined;
|
|
110
|
+
const cached = num(details?.cached_tokens) ?? 0;
|
|
111
|
+
if (typeof input === "number" || typeof output === "number") {
|
|
112
|
+
return { input: input === undefined ? undefined : input - cached, output, cacheRead: cached > 0 ? cached : undefined };
|
|
113
|
+
}
|
|
98
114
|
}
|
|
99
115
|
return null;
|
|
100
116
|
}
|
|
101
|
-
// openai /chat/completions
|
|
117
|
+
// openai /chat/completions (also its OpenAI-compatible clones)
|
|
102
118
|
if (obj.usage) {
|
|
103
|
-
|
|
119
|
+
const input = num(obj.usage.prompt_tokens);
|
|
120
|
+
const output = num(obj.usage.completion_tokens);
|
|
121
|
+
// Prompt-cache hits on this wire: `prompt_tokens_details.cached_tokens`
|
|
122
|
+
// (OpenAI, Ark, Qwen, GLM, …) or DeepSeek's `prompt_cache_hit_tokens`.
|
|
123
|
+
// Both spell the cached tokens as a SUBSET of prompt_tokens (cached +
|
|
124
|
+
// uncached = prompt), so — unlike Anthropic's separate counters — the hit
|
|
125
|
+
// part is subtracted from input. That keeps Usage.input = UNCACHED prompt
|
|
126
|
+
// tokens on every wire, which is what the stats hit-rate formula
|
|
127
|
+
// (cacheRead / (input + cacheRead + cacheCreation)) assumes.
|
|
128
|
+
const details = obj.usage.prompt_tokens_details as { cached_tokens?: unknown } | undefined;
|
|
129
|
+
const cached = num(details?.cached_tokens) ?? num(obj.usage.prompt_cache_hit_tokens) ?? 0;
|
|
130
|
+
return { input: input === undefined ? undefined : input - cached, output, cacheRead: cached > 0 ? cached : undefined };
|
|
104
131
|
}
|
|
105
132
|
return null;
|
|
106
133
|
}
|
|
@@ -118,8 +118,13 @@ export interface GateConfig {
|
|
|
118
118
|
* streams where the upstream omits usage (most agents don't set
|
|
119
119
|
* stream_options.include_usage), `estimated` is set and input/output come from
|
|
120
120
|
* a local tokenizer approximation (gpt-tokenizer, o200k_base) instead — the UI
|
|
121
|
-
* renders those with a ≈ marker. cacheRead/cacheCreation (prompt-
|
|
122
|
-
*
|
|
121
|
+
* renders those with a ≈ marker. cacheRead/cacheCreation (prompt-cache hit /
|
|
122
|
+
* write tokens) are surfaced separately from `input`, which always means
|
|
123
|
+
* UNCACHED prompt tokens: Anthropic reports them that way natively, while on
|
|
124
|
+
* the OpenAI-family wires the cached subset (chat `prompt_tokens_details.
|
|
125
|
+
* cached_tokens`, DeepSeek `prompt_cache_hit_tokens`, /responses
|
|
126
|
+
* `input_tokens_details.cached_tokens`) arrives INCLUDED in prompt_tokens /
|
|
127
|
+
* input_tokens and is subtracted at capture time to match. */
|
|
123
128
|
export interface Usage {
|
|
124
129
|
input: number;
|
|
125
130
|
output: number;
|