Skip to content

Commit a288fae

Browse files
mathisluckaclaude
andauthored
fix: Anthropic reports input tokens in first message delta (#2001)
* fix: Anthropic reports input tokens in first message delta * fix: improve Anthropic streaming usage token collection - Extract first_chunk_meta variable for better readability and performance - Add explicit chunks check before accessing first chunk for safety - Combine input tokens from message_start and output tokens from message_delta 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent a65926e commit a288fae

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

integrations/anthropic/src/haystack_integrations/components/generators/anthropic/chat/chat_generator.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,31 @@ def _convert_streaming_chunks_to_chat_message(
414414

415415
# Update meta information
416416
last_chunk_meta = chunks[-1].meta
417-
usage = self._get_openai_compatible_usage(last_chunk_meta)
417+
418+
# Combine usage from first chunk (input_tokens) and last chunk (output_tokens)
419+
combined_usage = {}
420+
421+
# Get input tokens from first chunk (message_start)
422+
if chunks:
423+
first_chunk_meta = chunks[0].meta
424+
if first_chunk_meta.get("type") == "message_start":
425+
first_chunk_usage = first_chunk_meta.get("message", {}).get("usage", {})
426+
if "input_tokens" in first_chunk_usage:
427+
combined_usage["input_tokens"] = first_chunk_usage["input_tokens"]
428+
429+
# Get output tokens from last chunk (message_delta)
430+
last_chunk_usage = last_chunk_meta.get("usage", {})
431+
if "output_tokens" in last_chunk_usage:
432+
combined_usage["output_tokens"] = last_chunk_usage["output_tokens"]
433+
elif "completion_tokens" in last_chunk_usage:
434+
combined_usage["output_tokens"] = last_chunk_usage["completion_tokens"]
435+
436+
# Add any other usage fields from the last chunk
437+
for key, value in last_chunk_usage.items():
438+
if key not in combined_usage:
439+
combined_usage[key] = value
440+
441+
usage = self._get_openai_compatible_usage({"usage": combined_usage})
418442
message._meta.update(
419443
{
420444
"model": model,

integrations/anthropic/tests/test_chat_generator.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,23 @@ def test_convert_streaming_chunks_to_chat_message(self):
403403
"""
404404
# Create a sequence of streaming chunks that simulate Anthropic's response
405405
chunks = [
406+
# Message start with input tokens
407+
StreamingChunk(
408+
content="",
409+
meta={
410+
"type": "message_start",
411+
"message": {
412+
"id": "msg_123",
413+
"type": "message",
414+
"role": "assistant",
415+
"content": [],
416+
"model": "claude-3-sonnet",
417+
"stop_reason": None,
418+
"stop_sequence": None,
419+
"usage": {"input_tokens": 25, "output_tokens": 0},
420+
},
421+
},
422+
),
406423
# Initial text content
407424
StreamingChunk(
408425
content="",
@@ -479,7 +496,7 @@ def test_convert_streaming_chunks_to_chat_message(self):
479496
assert message._meta["model"] == "claude-3-sonnet"
480497
assert message._meta["index"] == 0
481498
assert message._meta["finish_reason"] == "tool_use"
482-
assert message._meta["usage"] == {"completion_tokens": 40}
499+
assert message._meta["usage"] == {"prompt_tokens": 25, "completion_tokens": 40}
483500

484501
def test_convert_streaming_chunks_to_chat_message_malformed_json(self, caplog):
485502
"""
@@ -558,6 +575,23 @@ def test_convert_streaming_chunks_to_chat_message_tool_call_with_empty_arguments
558575
Test converting streaming chunks with an empty tool call arguments
559576
"""
560577
chunks = [
578+
# Message start with input tokens
579+
StreamingChunk(
580+
content="",
581+
meta={
582+
"type": "message_start",
583+
"message": {
584+
"id": "msg_456",
585+
"type": "message",
586+
"role": "assistant",
587+
"content": [],
588+
"model": "claude-3-sonnet",
589+
"stop_reason": None,
590+
"stop_sequence": None,
591+
"usage": {"input_tokens": 50, "output_tokens": 0},
592+
},
593+
},
594+
),
561595
StreamingChunk(
562596
content="",
563597
meta={
@@ -659,7 +693,7 @@ def test_convert_streaming_chunks_to_chat_message_tool_call_with_empty_arguments
659693
"cache_creation_input_tokens": None,
660694
"cache_read_input_tokens": None,
661695
"completion_tokens": 69,
662-
"prompt_tokens": None,
696+
"prompt_tokens": 50,
663697
"server_tool_use": None,
664698
}
665699

0 commit comments

Comments
 (0)