mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-07 15:18:29 +00:00
Compare commits
3 Commits
devin/1765
...
devin/1754
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
735d51c7ed | ||
|
|
530668f37f | ||
|
|
d0c5120b32 |
@@ -313,6 +313,7 @@ class LLM(BaseLLM):
|
||||
api_key: Optional[str] = None,
|
||||
callbacks: List[Any] = [],
|
||||
reasoning_effort: Optional[Literal["none", "low", "medium", "high"]] = None,
|
||||
thinking_budget: Optional[int] = None,
|
||||
stream: bool = False,
|
||||
**kwargs,
|
||||
):
|
||||
@@ -337,10 +338,14 @@ class LLM(BaseLLM):
|
||||
self.callbacks = callbacks
|
||||
self.context_window_size = 0
|
||||
self.reasoning_effort = reasoning_effort
|
||||
self.thinking_budget = thinking_budget
|
||||
self.additional_params = kwargs
|
||||
self.is_anthropic = self._is_anthropic_model(model)
|
||||
self.stream = stream
|
||||
|
||||
if self.thinking_budget is not None and (not isinstance(self.thinking_budget, int) or self.thinking_budget <= 0):
|
||||
raise ValueError("thinking_budget must be a positive integer")
|
||||
|
||||
litellm.drop_params = True
|
||||
|
||||
# Normalize self.stop to always be a List[str]
|
||||
@@ -414,6 +419,9 @@ class LLM(BaseLLM):
|
||||
**self.additional_params,
|
||||
}
|
||||
|
||||
if self.thinking_budget is not None:
|
||||
params["thinking"] = {"budget_tokens": self.thinking_budget}
|
||||
|
||||
# Remove None values from params
|
||||
return {k: v for k, v in params.items() if v is not None}
|
||||
|
||||
|
||||
58
tests/cassettes/test_gemini_thinking_budget.yaml
Normal file
58
tests/cassettes/test_gemini_thinking_budget.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: '{"contents": [{"role": "user", "parts": [{"text": "What is the capital
|
||||
of France?"}]}], "generationConfig": {"stop_sequences": []}}'
|
||||
headers:
|
||||
accept:
|
||||
- '*/*'
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '131'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- generativelanguage.googleapis.com
|
||||
user-agent:
|
||||
- litellm/1.74.9
|
||||
method: POST
|
||||
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-thinking-exp-01-21:generateContent
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAC/52RwUvDMBTG7/0rHj3PuIMnTxYsUtzGUCeISHk0byHYJiXJCnP0fzdJ4+g87pS8
|
||||
L1++/N7LKQPIyRht8ns4+cKXjebkq7vlcjEJHVmLImh5sa3gm46gtIMBW8kZbFtCS9CjtYCTCMnG
|
||||
8pRgHbqDDQHV5r1YVY918fK0W5ebtz8HJ4eyDZbPKECiiYcP7tjH58PKhNbCv9lLyxrd3U4lM33D
|
||||
ytBIpfY6pcbLxuNpleDr5/KjTgxzE9cdymi6TJ97Oo/I0eF5Ukm3ZAbZRD5Bigw6OVCLShz80P7R
|
||||
5ueLY9qNi+v7XenGj/uH+Dr90Iy2DWcxhNTN7vWykav/M5vDf2VhN2a/7rke3kUCAAA=
|
||||
headers:
|
||||
Alt-Svc:
|
||||
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json; charset=UTF-8
|
||||
Date:
|
||||
- Sat, 09 Aug 2025 10:15:44 GMT
|
||||
Server:
|
||||
- scaffolding on HTTPServer2
|
||||
Server-Timing:
|
||||
- gfet4t7; dur=50
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Origin
|
||||
- X-Origin
|
||||
- Referer
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
X-XSS-Protection:
|
||||
- '0'
|
||||
status:
|
||||
code: 400
|
||||
message: Bad Request
|
||||
version: 1
|
||||
@@ -354,6 +354,69 @@ def test_context_window_validation():
|
||||
assert "must be between 1024 and 2097152" in str(excinfo.value)
|
||||
|
||||
|
||||
@pytest.mark.vcr(filter_headers=["authorization"], filter_query_parameters=["key"])
|
||||
def test_gemini_thinking_budget():
|
||||
llm = LLM(
|
||||
model="gemini/gemini-2.0-flash-thinking-exp-01-21",
|
||||
thinking_budget=1024,
|
||||
)
|
||||
try:
|
||||
result = llm.call("What is the capital of France?")
|
||||
assert isinstance(result, str)
|
||||
assert "Paris" in result
|
||||
except Exception as e:
|
||||
if "API key not valid" in str(e) or "AuthenticationError" in str(e):
|
||||
pytest.skip("Skipping test due to missing API key")
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def test_thinking_budget_validation():
|
||||
# Test valid thinking_budget
|
||||
llm = LLM(model="gemini/gemini-2.0-flash-thinking-exp-01-21", thinking_budget=1024)
|
||||
assert llm.thinking_budget == 1024
|
||||
|
||||
# Test invalid thinking_budget (negative)
|
||||
with pytest.raises(ValueError, match="thinking_budget must be a positive integer"):
|
||||
LLM(model="gemini/gemini-2.0-flash-thinking-exp-01-21", thinking_budget=-1)
|
||||
|
||||
# Test invalid thinking_budget (zero)
|
||||
with pytest.raises(ValueError, match="thinking_budget must be a positive integer"):
|
||||
LLM(model="gemini/gemini-2.0-flash-thinking-exp-01-21", thinking_budget=0)
|
||||
|
||||
# Test invalid thinking_budget (non-integer)
|
||||
with pytest.raises(ValueError, match="thinking_budget must be a positive integer"):
|
||||
LLM(model="gemini/gemini-2.0-flash-thinking-exp-01-21", thinking_budget=1024.5)
|
||||
|
||||
|
||||
def test_thinking_budget_parameter_passing():
|
||||
llm = LLM(
|
||||
model="gemini/gemini-2.0-flash-thinking-exp-01-21",
|
||||
thinking_budget=2048,
|
||||
)
|
||||
|
||||
with patch("litellm.completion") as mocked_completion:
|
||||
mock_message = MagicMock()
|
||||
mock_message.content = "Test response"
|
||||
mock_choice = MagicMock()
|
||||
mock_choice.message = mock_message
|
||||
mock_response = MagicMock()
|
||||
mock_response.choices = [mock_choice]
|
||||
mock_response.usage = {
|
||||
"prompt_tokens": 5,
|
||||
"completion_tokens": 5,
|
||||
"total_tokens": 10,
|
||||
}
|
||||
mocked_completion.return_value = mock_response
|
||||
|
||||
result = llm.call("Test message")
|
||||
|
||||
_, kwargs = mocked_completion.call_args
|
||||
assert "thinking" in kwargs
|
||||
assert kwargs["thinking"]["budget_tokens"] == 2048
|
||||
assert result == "Test response"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def get_weather_tool_schema():
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user