Compare commits

...

2 Commits

Author SHA1 Message Date
Greyson LaLonde
98249b0d6a Merge branch 'main' into lorenze/fix-google-when-tool-response-not-a-string 2026-01-23 18:28:02 -05:00
lorenzejay
63d00d07c0 Enhance Gemini LLM Tool Handling and Add Test for Float Responses
- Updated the GeminiCompletion class to handle non-dict values returned from tools, ensuring that floats are wrapped in a dictionary format for consistent response handling.
- Introduced a new YAML cassette to test the Gemini LLM's ability to process tools that return float values, verifying that the agent can correctly utilize the sum_numbers tool and return the expected results.
- Added a comprehensive test case to validate the integration of the sum_numbers tool within the Gemini LLM, ensuring accurate calculations and proper response formatting.

These changes improve the robustness of tool interactions within the Gemini LLM and enhance testing coverage for float return values.
2026-01-23 10:08:15 -08:00
3 changed files with 372 additions and 1 deletions

View File

@@ -555,7 +555,11 @@ class GeminiCompletion(BaseLLM):
response_data: dict[str, Any]
try:
response_data = json.loads(text_content) if text_content else {}
parsed = json.loads(text_content) if text_content else {}
if isinstance(parsed, dict):
response_data = parsed
else:
response_data = {"result": parsed}
except (json.JSONDecodeError, TypeError):
response_data = {"result": text_content}

View File

@@ -0,0 +1,319 @@
interactions:
- request:
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: What is 10000 + 20000?
Use the sum_numbers tool to calculate this.\n\nThis is the expected criteria
for your final answer: The sum of the two numbers\nyou MUST return the actual
complete content as the final answer, not a summary.\n\nThis is VERY important
to you, your job depends on it!"}], "role": "user"}], "systemInstruction": {"parts":
[{"text": "You are Calculator. You are a calculator that adds numbers.\nYour
personal goal is: Calculate numbers accurately"}], "role": "user"}, "tools":
[{"functionDeclarations": [{"description": "Add two numbers together and return
the result", "name": "sum_numbers", "parameters": {"properties": {"a": {"description":
"The first number to add", "title": "A", "type": "NUMBER"}, "b": {"description":
"The second number to add", "title": "B", "type": "NUMBER"}}, "required": ["a",
"b"], "type": "OBJECT"}}]}], "generationConfig": {"stopSequences": ["\nObservation:"]}}'
headers:
User-Agent:
- X-USER-AGENT-XXX
accept:
- '*/*'
accept-encoding:
- ACCEPT-ENCODING-XXX
connection:
- keep-alive
content-length:
- '962'
content-type:
- application/json
host:
- generativelanguage.googleapis.com
x-goog-api-client:
- google-genai-sdk/1.49.0 gl-python/3.13.3
x-goog-api-key:
- X-GOOG-API-KEY-XXX
method: POST
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-001:generateContent
response:
body:
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
[\n {\n \"functionCall\": {\n \"name\": \"sum_numbers\",\n
\ \"args\": {\n \"a\": 10000,\n \"b\":
20000\n }\n }\n }\n ],\n \"role\":
\"model\"\n },\n \"finishReason\": \"STOP\",\n \"avgLogprobs\":
-0.00059548033667462211\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
127,\n \"candidatesTokenCount\": 7,\n \"totalTokenCount\": 134,\n \"promptTokensDetails\":
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 127\n
\ }\n ],\n \"candidatesTokensDetails\": [\n {\n \"modality\":
\"TEXT\",\n \"tokenCount\": 7\n }\n ]\n },\n \"modelVersion\":
\"gemini-2.0-flash-001\",\n \"responseId\": \"bLBzabiACaP3-8YP7s-P6QI\"\n}\n"
headers:
Alt-Svc:
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
Content-Type:
- application/json; charset=UTF-8
Date:
- Fri, 23 Jan 2026 17:31:24 GMT
Server:
- scaffolding on HTTPServer2
Server-Timing:
- gfet4t7; dur=673
Transfer-Encoding:
- chunked
Vary:
- Origin
- X-Origin
- Referer
X-Content-Type-Options:
- X-CONTENT-TYPE-XXX
X-Frame-Options:
- X-FRAME-OPTIONS-XXX
X-XSS-Protection:
- '0'
status:
code: 200
message: OK
- request:
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: What is 10000 + 20000?
Use the sum_numbers tool to calculate this.\n\nThis is the expected criteria
for your final answer: The sum of the two numbers\nyou MUST return the actual
complete content as the final answer, not a summary.\n\nThis is VERY important
to you, your job depends on it!"}], "role": "user"}, {"parts": [{"functionCall":
{"args": {"a": 10000, "b": 20000}, "name": "sum_numbers"}}], "role": "model"},
{"parts": [{"functionResponse": {"name": "sum_numbers", "response": {"result":
30000}}}], "role": "user"}, {"parts": [{"text": "Analyze the tool result. If
requirements are met, provide the Final Answer. Otherwise, call the next tool.
Deliver only the answer without meta-commentary."}], "role": "user"}], "systemInstruction":
{"parts": [{"text": "You are Calculator. You are a calculator that adds numbers.\nYour
personal goal is: Calculate numbers accurately"}], "role": "user"}, "tools":
[{"functionDeclarations": [{"description": "Add two numbers together and return
the result", "name": "sum_numbers", "parameters": {"properties": {"a": {"description":
"The first number to add", "title": "A", "type": "NUMBER"}, "b": {"description":
"The second number to add", "title": "B", "type": "NUMBER"}}, "required": ["a",
"b"], "type": "OBJECT"}}]}], "generationConfig": {"stopSequences": ["\nObservation:"]}}'
headers:
User-Agent:
- X-USER-AGENT-XXX
accept:
- '*/*'
accept-encoding:
- ACCEPT-ENCODING-XXX
connection:
- keep-alive
content-length:
- '1374'
content-type:
- application/json
host:
- generativelanguage.googleapis.com
x-goog-api-client:
- google-genai-sdk/1.49.0 gl-python/3.13.3
x-goog-api-key:
- X-GOOG-API-KEY-XXX
method: POST
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-001:generateContent
response:
body:
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
[\n {\n \"text\": \"\"\n }\n ],\n \"role\":
\"model\"\n },\n \"finishReason\": \"STOP\"\n }\n ],\n \"usageMetadata\":
{\n \"promptTokenCount\": 171,\n \"totalTokenCount\": 171,\n \"promptTokensDetails\":
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 171\n
\ }\n ]\n },\n \"modelVersion\": \"gemini-2.0-flash-001\",\n \"responseId\":
\"bLBzaaKgMc-ajrEPk7bIuQ8\"\n}\n"
headers:
Alt-Svc:
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
Content-Type:
- application/json; charset=UTF-8
Date:
- Fri, 23 Jan 2026 17:31:25 GMT
Server:
- scaffolding on HTTPServer2
Server-Timing:
- gfet4t7; dur=382
Transfer-Encoding:
- chunked
Vary:
- Origin
- X-Origin
- Referer
X-Content-Type-Options:
- X-CONTENT-TYPE-XXX
X-Frame-Options:
- X-FRAME-OPTIONS-XXX
X-XSS-Protection:
- '0'
status:
code: 200
message: OK
- request:
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: What is 10000 + 20000?
Use the sum_numbers tool to calculate this.\n\nThis is the expected criteria
for your final answer: The sum of the two numbers\nyou MUST return the actual
complete content as the final answer, not a summary.\n\nThis is VERY important
to you, your job depends on it!"}], "role": "user"}, {"parts": [{"functionCall":
{"args": {"a": 10000, "b": 20000}, "name": "sum_numbers"}}], "role": "model"},
{"parts": [{"functionResponse": {"name": "sum_numbers", "response": {"result":
30000}}}], "role": "user"}, {"parts": [{"text": "Analyze the tool result. If
requirements are met, provide the Final Answer. Otherwise, call the next tool.
Deliver only the answer without meta-commentary."}], "role": "user"}, {"parts":
[{"text": "\nCurrent Task: What is 10000 + 20000? Use the sum_numbers tool to
calculate this.\n\nThis is the expected criteria for your final answer: The
sum of the two numbers\nyou MUST return the actual complete content as the final
answer, not a summary.\n\nThis is VERY important to you, your job depends on
it!"}], "role": "user"}], "systemInstruction": {"parts": [{"text": "You are
Calculator. You are a calculator that adds numbers.\nYour personal goal is:
Calculate numbers accurately\n\nYou are Calculator. You are a calculator that
adds numbers.\nYour personal goal is: Calculate numbers accurately"}], "role":
"user"}, "tools": [{"functionDeclarations": [{"description": "Add two numbers
together and return the result", "name": "sum_numbers", "parameters": {"properties":
{"a": {"description": "The first number to add", "title": "A", "type": "NUMBER"},
"b": {"description": "The second number to add", "title": "B", "type": "NUMBER"}},
"required": ["a", "b"], "type": "OBJECT"}}]}], "generationConfig": {"stopSequences":
["\nObservation:"]}}'
headers:
User-Agent:
- X-USER-AGENT-XXX
accept:
- '*/*'
accept-encoding:
- ACCEPT-ENCODING-XXX
connection:
- keep-alive
content-length:
- '1837'
content-type:
- application/json
host:
- generativelanguage.googleapis.com
x-goog-api-client:
- google-genai-sdk/1.49.0 gl-python/3.13.3
x-goog-api-key:
- X-GOOG-API-KEY-XXX
method: POST
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-001:generateContent
response:
body:
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
[\n {\n \"text\": \"\"\n }\n ],\n \"role\":
\"model\"\n },\n \"finishReason\": \"STOP\"\n }\n ],\n \"usageMetadata\":
{\n \"promptTokenCount\": 271,\n \"totalTokenCount\": 271,\n \"promptTokensDetails\":
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 271\n
\ }\n ]\n },\n \"modelVersion\": \"gemini-2.0-flash-001\",\n \"responseId\":
\"bbBzaczHDcW7jrEPgaj1CA\"\n}\n"
headers:
Alt-Svc:
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
Content-Type:
- application/json; charset=UTF-8
Date:
- Fri, 23 Jan 2026 17:31:25 GMT
Server:
- scaffolding on HTTPServer2
Server-Timing:
- gfet4t7; dur=410
Transfer-Encoding:
- chunked
Vary:
- Origin
- X-Origin
- Referer
X-Content-Type-Options:
- X-CONTENT-TYPE-XXX
X-Frame-Options:
- X-FRAME-OPTIONS-XXX
X-XSS-Protection:
- '0'
status:
code: 200
message: OK
- request:
body: '{"contents": [{"parts": [{"text": "\nCurrent Task: What is 10000 + 20000?
Use the sum_numbers tool to calculate this.\n\nThis is the expected criteria
for your final answer: The sum of the two numbers\nyou MUST return the actual
complete content as the final answer, not a summary.\n\nThis is VERY important
to you, your job depends on it!"}], "role": "user"}, {"parts": [{"functionCall":
{"args": {"a": 10000, "b": 20000}, "name": "sum_numbers"}}], "role": "model"},
{"parts": [{"functionResponse": {"name": "sum_numbers", "response": {"result":
30000}}}], "role": "user"}, {"parts": [{"text": "Analyze the tool result. If
requirements are met, provide the Final Answer. Otherwise, call the next tool.
Deliver only the answer without meta-commentary."}], "role": "user"}, {"parts":
[{"text": "\nCurrent Task: What is 10000 + 20000? Use the sum_numbers tool to
calculate this.\n\nThis is the expected criteria for your final answer: The
sum of the two numbers\nyou MUST return the actual complete content as the final
answer, not a summary.\n\nThis is VERY important to you, your job depends on
it!"}], "role": "user"}, {"parts": [{"text": "\nCurrent Task: What is 10000
+ 20000? Use the sum_numbers tool to calculate this.\n\nThis is the expected
criteria for your final answer: The sum of the two numbers\nyou MUST return
the actual complete content as the final answer, not a summary.\n\nThis is VERY
important to you, your job depends on it!"}], "role": "user"}], "systemInstruction":
{"parts": [{"text": "You are Calculator. You are a calculator that adds numbers.\nYour
personal goal is: Calculate numbers accurately\n\nYou are Calculator. You are
a calculator that adds numbers.\nYour personal goal is: Calculate numbers accurately\n\nYou
are Calculator. You are a calculator that adds numbers.\nYour personal goal
is: Calculate numbers accurately"}], "role": "user"}, "tools": [{"functionDeclarations":
[{"description": "Add two numbers together and return the result", "name": "sum_numbers",
"parameters": {"properties": {"a": {"description": "The first number to add",
"title": "A", "type": "NUMBER"}, "b": {"description": "The second number to
add", "title": "B", "type": "NUMBER"}}, "required": ["a", "b"], "type": "OBJECT"}}]}],
"generationConfig": {"stopSequences": ["\nObservation:"]}}'
headers:
User-Agent:
- X-USER-AGENT-XXX
accept:
- '*/*'
accept-encoding:
- ACCEPT-ENCODING-XXX
connection:
- keep-alive
content-length:
- '2300'
content-type:
- application/json
host:
- generativelanguage.googleapis.com
x-goog-api-client:
- google-genai-sdk/1.49.0 gl-python/3.13.3
x-goog-api-key:
- X-GOOG-API-KEY-XXX
method: POST
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-001:generateContent
response:
body:
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
[\n {\n \"text\": \"\\n{\\\"sum_numbers_response\\\":
{\\\"result\\\": 30000}}\\n\"\n }\n ],\n \"role\":
\"model\"\n },\n \"finishReason\": \"STOP\",\n \"avgLogprobs\":
-0.0038021293125654523\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
371,\n \"candidatesTokenCount\": 19,\n \"totalTokenCount\": 390,\n \"promptTokensDetails\":
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 371\n
\ }\n ],\n \"candidatesTokensDetails\": [\n {\n \"modality\":
\"TEXT\",\n \"tokenCount\": 19\n }\n ]\n },\n \"modelVersion\":
\"gemini-2.0-flash-001\",\n \"responseId\": \"bbBzaauxJ_SgjrEP7onK2Ak\"\n}\n"
headers:
Alt-Svc:
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
Content-Type:
- application/json; charset=UTF-8
Date:
- Fri, 23 Jan 2026 17:31:26 GMT
Server:
- scaffolding on HTTPServer2
Server-Timing:
- gfet4t7; dur=454
Transfer-Encoding:
- chunked
Vary:
- Origin
- X-Origin
- Referer
X-Content-Type-Options:
- X-CONTENT-TYPE-XXX
X-Frame-Options:
- X-FRAME-OPTIONS-XXX
X-XSS-Protection:
- '0'
status:
code: 200
message: OK
version: 1

View File

@@ -635,6 +635,54 @@ def test_gemini_token_usage_tracking():
assert usage.total_tokens > 0
@pytest.mark.vcr()
def test_gemini_tool_returning_float():
"""
Test that Gemini properly handles tools that return non-dict values like floats.
This is an end-to-end test that verifies the agent can use a tool that returns
a float (which gets wrapped in {"result": value} for Gemini's FunctionResponse).
"""
from pydantic import BaseModel, Field
from typing import Type
from crewai.tools import BaseTool
class SumNumbersToolInput(BaseModel):
a: float = Field(..., description="The first number to add")
b: float = Field(..., description="The second number to add")
class SumNumbersTool(BaseTool):
name: str = "sum_numbers"
description: str = "Add two numbers together and return the result"
args_schema: Type[BaseModel] = SumNumbersToolInput
def _run(self, a: float, b: float) -> float:
return a + b
sum_tool = SumNumbersTool()
agent = Agent(
role="Calculator",
goal="Calculate numbers accurately",
backstory="You are a calculator that adds numbers.",
llm=LLM(model="google/gemini-2.0-flash-001"),
tools=[sum_tool],
verbose=True,
)
task = Task(
description="What is 10000 + 20000? Use the sum_numbers tool to calculate this.",
expected_output="The sum of the two numbers",
agent=agent,
)
crew = Crew(agents=[agent], tasks=[task], verbose=True)
result = crew.kickoff()
# The result should contain 30000 (the sum)
assert "30000" in result.raw
def test_gemini_stop_sequences_sync():
"""Test that stop and stop_sequences attributes stay synchronized."""
llm = LLM(model="google/gemini-2.0-flash-001")