Skip to content

Commit bdafe0f

Browse files
fix: ensure token usage recording, validate response model on stream
1 parent 8e99d49 commit bdafe0f

File tree

312 files changed

+7781
-33059
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

312 files changed

+7781
-33059
lines changed

conftest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ def _filter_request_headers(request: Request) -> Request: # type: ignore[no-any
136136

137137
def _filter_response_headers(response: dict[str, Any]) -> dict[str, Any]:
138138
"""Filter sensitive headers from response before recording."""
139+
# Remove Content-Encoding to prevent decompression issues on replay
140+
for encoding_header in ["Content-Encoding", "content-encoding"]:
141+
response["headers"].pop(encoding_header, None)
142+
139143
for header_name, replacement in HEADERS_TO_FILTER.items():
140144
for variant in [header_name, header_name.upper(), header_name.title()]:
141145
if variant in response["headers"]:

lib/crewai/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ bedrock = [
8484
"boto3~=1.40.45",
8585
]
8686
google-genai = [
87-
"google-genai~=1.2.0",
87+
"google-genai~=1.49.0",
8888
]
8989
azure-ai-inference = [
9090
"azure-ai-inference~=1.0.0b9",

lib/crewai/src/crewai/llms/providers/anthropic/completion.py

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -679,40 +679,38 @@ def _handle_streaming_completion(
679679
params["messages"], full_response, from_agent
680680
)
681681

682-
def _handle_tool_use_conversation(
682+
def _execute_tools_and_collect_results(
683683
self,
684-
initial_response: Message,
685684
tool_uses: list[ToolUseBlock],
686-
params: dict[str, Any],
687685
available_functions: dict[str, Any],
688686
from_task: Any | None = None,
689687
from_agent: Any | None = None,
690-
) -> str:
691-
"""Handle the complete tool use conversation flow.
688+
) -> list[dict[str, Any]]:
689+
"""Execute tools and collect results in Anthropic format.
692690
693-
This implements the proper Anthropic tool use pattern:
694-
1. Claude requests tool use
695-
2. We execute the tools
696-
3. We send tool results back to Claude
697-
4. Claude processes results and generates final response
691+
Args:
692+
tool_uses: List of tool use blocks from Claude's response
693+
available_functions: Available functions for tool calling
694+
from_task: Task that initiated the call
695+
from_agent: Agent that initiated the call
696+
697+
Returns:
698+
List of tool result dictionaries in Anthropic format
698699
"""
699-
# Execute all requested tools and collect results
700700
tool_results = []
701701

702702
for tool_use in tool_uses:
703703
function_name = tool_use.name
704704
function_args = tool_use.input
705705

706-
# Execute the tool
707706
result = self._handle_tool_execution(
708707
function_name=function_name,
709-
function_args=function_args,
708+
function_args=cast(dict[str, Any], function_args),
710709
available_functions=available_functions,
711710
from_task=from_task,
712711
from_agent=from_agent,
713712
)
714713

715-
# Create tool result in Anthropic format
716714
tool_result = {
717715
"type": "tool_result",
718716
"tool_use_id": tool_use.id,
@@ -722,7 +720,29 @@ def _handle_tool_use_conversation(
722720
}
723721
tool_results.append(tool_result)
724722

725-
# Prepare follow-up conversation with tool results
723+
return tool_results
724+
725+
def _handle_tool_use_conversation(
726+
self,
727+
initial_response: Message,
728+
tool_uses: list[ToolUseBlock],
729+
params: dict[str, Any],
730+
available_functions: dict[str, Any],
731+
from_task: Any | None = None,
732+
from_agent: Any | None = None,
733+
) -> str:
734+
"""Handle the complete tool use conversation flow.
735+
736+
This implements the proper Anthropic tool use pattern:
737+
1. Claude requests tool use
738+
2. We execute the tools
739+
3. We send tool results back to Claude
740+
4. Claude processes results and generates final response
741+
"""
742+
tool_results = self._execute_tools_and_collect_results(
743+
tool_uses, available_functions, from_task, from_agent
744+
)
745+
726746
follow_up_params = params.copy()
727747

728748
# Add Claude's tool use response to conversation
@@ -810,7 +830,7 @@ def _handle_tool_use_conversation(
810830
logging.error(f"Tool follow-up conversation failed: {e}")
811831
# Fallback: return the first tool result if follow-up fails
812832
if tool_results:
813-
return tool_results[0]["content"]
833+
return cast(str, tool_results[0]["content"])
814834
raise e
815835

816836
async def _ahandle_completion(
@@ -1003,28 +1023,9 @@ async def _ahandle_tool_use_conversation(
10031023
3. We send tool results back to Claude
10041024
4. Claude processes results and generates final response
10051025
"""
1006-
tool_results = []
1007-
1008-
for tool_use in tool_uses:
1009-
function_name = tool_use.name
1010-
function_args = tool_use.input
1011-
1012-
result = self._handle_tool_execution(
1013-
function_name=function_name,
1014-
function_args=function_args,
1015-
available_functions=available_functions,
1016-
from_task=from_task,
1017-
from_agent=from_agent,
1018-
)
1019-
1020-
tool_result = {
1021-
"type": "tool_result",
1022-
"tool_use_id": tool_use.id,
1023-
"content": str(result)
1024-
if result is not None
1025-
else "Tool execution completed",
1026-
}
1027-
tool_results.append(tool_result)
1026+
tool_results = self._execute_tools_and_collect_results(
1027+
tool_uses, available_functions, from_task, from_agent
1028+
)
10281029

10291030
follow_up_params = params.copy()
10301031

@@ -1079,7 +1080,7 @@ async def _ahandle_tool_use_conversation(
10791080

10801081
logging.error(f"Tool follow-up conversation failed: {e}")
10811082
if tool_results:
1082-
return tool_results[0]["content"]
1083+
return cast(str, tool_results[0]["content"])
10831084
raise e
10841085

10851086
def supports_function_calling(self) -> bool:
@@ -1115,7 +1116,8 @@ def get_context_window_size(self) -> int:
11151116
# Default context window size for Claude models
11161117
return int(200000 * CONTEXT_WINDOW_USAGE_RATIO)
11171118

1118-
def _extract_anthropic_token_usage(self, response: Message) -> dict[str, Any]:
1119+
@staticmethod
1120+
def _extract_anthropic_token_usage(response: Message) -> dict[str, Any]:
11191121
"""Extract token usage from Anthropic response."""
11201122
if hasattr(response, "usage") and response.usage:
11211123
usage = response.usage

0 commit comments

Comments
 (0)