anthropic-gateway 1.0.0 → 1.1.0
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/lib/server.js +34 -4
- package/package.json +1 -1
package/lib/server.js
CHANGED
|
@@ -57,6 +57,18 @@ function makeServer(rawCfg) {
|
|
|
57
57
|
if (logStream) logStream.write(s + '\n');
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// Token totals for the lifetime of this gateway process (resets on restart).
|
|
61
|
+
const totals = { requests: 0, input: 0, output: 0, cached: 0 };
|
|
62
|
+
function recordUsage(input, output, cached) {
|
|
63
|
+
totals.requests += 1;
|
|
64
|
+
totals.input += input;
|
|
65
|
+
totals.output += output;
|
|
66
|
+
totals.cached += cached;
|
|
67
|
+
}
|
|
68
|
+
function usageSummary() {
|
|
69
|
+
return 'session ' + totals.requests + ' req in=' + totals.input + ' out=' + totals.output + ' total=' + (totals.input + totals.output);
|
|
70
|
+
}
|
|
71
|
+
|
|
60
72
|
const app = fastify({ logger: false });
|
|
61
73
|
const isAzure = cfg.upstreamBaseUrl.includes('.openai.azure.com');
|
|
62
74
|
const openai = new OpenAI({ baseURL: cfg.upstreamBaseUrl, apiKey: cfg.upstreamApiKey });
|
|
@@ -98,14 +110,32 @@ function makeServer(rawCfg) {
|
|
|
98
110
|
try {
|
|
99
111
|
const openaiRequest = convertRequestToOpenAI(anthropicRequest, cfg.upstreamModel, cfg.toolFormat, isAzure);
|
|
100
112
|
if (isStreaming) {
|
|
101
|
-
const stream = await openai.chat.completions.create({ ...openaiRequest, stream: true });
|
|
113
|
+
const stream = await openai.chat.completions.create({ ...openaiRequest, stream: true, stream_options: { include_usage: true } });
|
|
114
|
+
// Capture token usage from the stream without altering what the vendored
|
|
115
|
+
// converter sees — it just iterates chunks, so a pass-through works.
|
|
116
|
+
const usage = { input: 0, output: 0, cached: 0 };
|
|
117
|
+
const wrapped = (async function* () {
|
|
118
|
+
for await (const chunk of stream) {
|
|
119
|
+
if (chunk && chunk.usage) {
|
|
120
|
+
usage.input = chunk.usage.prompt_tokens ?? usage.input;
|
|
121
|
+
usage.output = chunk.usage.completion_tokens ?? usage.output;
|
|
122
|
+
usage.cached = chunk.usage.prompt_tokens_details?.cached_tokens ?? usage.cached;
|
|
123
|
+
}
|
|
124
|
+
yield chunk;
|
|
125
|
+
}
|
|
126
|
+
})();
|
|
102
127
|
reply.hijack();
|
|
103
|
-
await streamOpenAIToAnthropic(
|
|
128
|
+
await streamOpenAIToAnthropic(wrapped, reply, clientModel, cfg.upstreamBaseUrl);
|
|
129
|
+
recordUsage(usage.input, usage.output, usage.cached);
|
|
130
|
+
log('<- ' + clientModel + ' ok in=' + usage.input + ' out=' + usage.output + ' total=' + (usage.input + usage.output) + ' | ' + usageSummary());
|
|
104
131
|
} else {
|
|
105
132
|
const response = await openai.chat.completions.create({ ...openaiRequest, stream: false });
|
|
106
|
-
|
|
133
|
+
const anthropicResponse = convertResponseToAnthropic(response, clientModel);
|
|
134
|
+
reply.send(anthropicResponse);
|
|
135
|
+
const u = anthropicResponse.usage || {};
|
|
136
|
+
recordUsage(u.input_tokens || 0, u.output_tokens || 0, u.cache_read_input_tokens || 0);
|
|
137
|
+
log('<- ' + clientModel + ' ok in=' + (u.input_tokens || 0) + ' out=' + (u.output_tokens || 0) + ' total=' + ((u.input_tokens || 0) + (u.output_tokens || 0)) + ' | ' + usageSummary());
|
|
107
138
|
}
|
|
108
|
-
log('<- ' + clientModel + ' ok');
|
|
109
139
|
} catch (caught) {
|
|
110
140
|
const error = caught instanceof Error ? caught : new Error(String(caught));
|
|
111
141
|
const rawStatus = caught && typeof caught === 'object' && 'status' in caught ? caught.status : undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anthropic-gateway",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Anthropic-compatible local gateway for any OpenAI-compatible model provider — make Claude Code (GUI/CLI) work with kilo, OpenAI, DeepSeek, local models, etc.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|