Skip to content

Commit 9c2436d

Browse files
committed
Fix metadata token counting for Anthropic format responses
The _log_metadata method only supported OpenAI format usage keys (prompt_tokens, completion_tokens) but Anthropic responses use different keys (input_tokens, output_tokens). This caused null token counts in metadata.json for dedaluslabs and firmware providers when using the /v1/messages endpoint. Changes: - Add fallback from OpenAI to Anthropic format for token counts - Use explicit None checks instead of 'or' to handle 0 values - Calculate total_tokens if missing from Anthropic responses - Handle stop_reason (Anthropic) as well as finish_reason (OpenAI)
1 parent 5293b13 commit 9c2436d

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

src/rotator_library/anthropic_compat/streaming.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ async def anthropic_streaming_wrapper(
128128
stop_reason_final = stop_reason
129129

130130
# Build final usage dict with cached tokens
131-
final_usage = {"output_tokens": output_tokens}
131+
final_usage = {
132+
"input_tokens": input_tokens - cached_tokens,
133+
"output_tokens": output_tokens,
134+
}
132135
if cached_tokens > 0:
133136
final_usage["cache_read_input_tokens"] = cached_tokens
134137
final_usage["cache_creation_input_tokens"] = 0
@@ -416,7 +419,10 @@ async def anthropic_streaming_wrapper(
416419
yield f'event: content_block_stop\ndata: {{"type": "content_block_stop", "index": {current_block_index}}}\n\n'
417420

418421
# Build final usage with cached tokens
419-
final_usage = {"output_tokens": 0}
422+
final_usage = {
423+
"input_tokens": input_tokens - cached_tokens,
424+
"output_tokens": 0,
425+
}
420426
if cached_tokens > 0:
421427
final_usage["cache_read_input_tokens"] = cached_tokens
422428
final_usage["cache_creation_input_tokens"] = 0

src/rotator_library/transaction_logger.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,12 @@ def _log_metadata(
265265
model = response_data.get("model", self.model)
266266
finish_reason = "N/A"
267267

268+
# Handle OpenAI format (choices[0].finish_reason)
268269
if "choices" in response_data and response_data["choices"]:
269270
finish_reason = response_data["choices"][0].get("finish_reason", "N/A")
271+
# Handle Anthropic format (stop_reason at top level)
272+
elif "stop_reason" in response_data:
273+
finish_reason = response_data.get("stop_reason", "N/A")
270274

271275
# Check for provider subdirectory
272276
has_provider_logs = False
@@ -279,6 +283,19 @@ def _log_metadata(
279283
except OSError:
280284
has_provider_logs = False
281285

286+
# Extract token counts - support both OpenAI and Anthropic formats
287+
# Prefers OpenAI format if available: prompt_tokens, completion_tokens
288+
# Falls back to Anthropic format: input_tokens, output_tokens
289+
prompt_tokens = usage.get("prompt_tokens")
290+
if prompt_tokens is None:
291+
prompt_tokens = usage.get("input_tokens")
292+
completion_tokens = usage.get("completion_tokens")
293+
if completion_tokens is None:
294+
completion_tokens = usage.get("output_tokens")
295+
total_tokens = usage.get("total_tokens")
296+
if total_tokens is None and prompt_tokens is not None and completion_tokens is not None:
297+
total_tokens = prompt_tokens + completion_tokens
298+
282299
metadata = {
283300
"request_id": self.request_id,
284301
"timestamp_utc": datetime.utcnow().isoformat(),
@@ -288,9 +305,9 @@ def _log_metadata(
288305
"model": model,
289306
"streaming": self.streaming,
290307
"usage": {
291-
"prompt_tokens": usage.get("prompt_tokens"),
292-
"completion_tokens": usage.get("completion_tokens"),
293-
"total_tokens": usage.get("total_tokens"),
308+
"prompt_tokens": prompt_tokens,
309+
"completion_tokens": completion_tokens,
310+
"total_tokens": total_tokens,
294311
},
295312
"finish_reason": finish_reason,
296313
"has_provider_logs": has_provider_logs,

0 commit comments

Comments
 (0)