-
Notifications
You must be signed in to change notification settings - Fork 16
[BUGS] Prevent NullPointerException for Gemini models with empty AIMessage content #79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUGS] Prevent NullPointerException for Gemini models with empty AIMessage content #79
Conversation
Adds a check for empty message content before processing in GenericProvider, returning a default text when content is missing.
|
@luigisaetta Does this resolve your issue? |
| # Issue 78 fix: Check if original content is empty BEFORE processing | ||
| # to prevent NullPointerException in OCI backend | ||
| else: | ||
| content = [self.oci_chat_message_text_content(text=".")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so there has to be an arbitrary character in the the text?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I tried to use an empty space as the content, but empty space does not work.
|
Should this PR be merged with: #81? |
Gemini tool calls work with non-empty content field. |
|
Hey @paxiaatucsdedu, thanks for working on this fix! I wanted to share some test results that might be helpful. I ran integration tests to check the full tool calling flow with Gemini, and found that the empty content fix alone may not be enough for the complete agentic loop. Test EvidenceTest 1: GenericProvider (current approach) - FAILS Test 2: GeminiProvider (PR #81 approach) - SUCCEEDS Issues Found
SuggestionIn PR #81, I worked around these OCI translation limitations by converting tool-related messages to regular user/assistant messages. This has 38 passing integration tests including real agent workflows. Would it make sense to combine our efforts? Happy to discuss! |
|
Hi @fede-kamel, thank you for testing it. I tested with the test python script from this issue description. Gemini works with no problem as long as the content is not empty, and the Gemini model response is very similar to the grok-4 model (Grok-4 is mentioned in the issue description as working properly). This is the Jupyter notebook I used for testing with PR #79 |
|
Here's the minimal test code to reproduce: from langchain_core.messages import HumanMessage, ToolMessage
from langchain_core.tools import tool
from langchain_oci.chat_models import ChatOCIGenAI
@tool
def get_weather(city: str) -> str:
"""Get weather for a city."""
return f"Sunny, 22°C in {city}"
# Create LLM with tools
llm = ChatOCIGenAI(model_id="google.gemini-2.5-flash", ...)
llm_with_tools = llm.bind_tools([get_weather])
# Step 1: Get tool calls (works)
messages = [HumanMessage(content="What's the weather in Rome?")]
response = llm_with_tools.invoke(messages)
# Returns: tool_calls=[{'name': 'get_weather', 'args': {'city': 'Rome'}, 'id': None}]
# Step 2: Send tool result back (fails)
messages.append(response)
messages.append(ToolMessage(
content="Sunny, 22°C in Rome",
tool_call_id="call_get_weather", # Note: Gemini returned id=None
name="get_weather",
))
final = llm_with_tools.invoke(messages) # 💥 400 errorThe error occurs because OCI can't translate |
|
Hi @paxiaatucsdedu! Thanks for sharing the notebook - it helped me understand your test setup better. I did some additional testing and found something interesting. The notebook test works because it uses a manually constructed conversation history with a hardcoded # From notebook - REQUEST 2
AIMessage(content="", tool_calls=[{"id": "call_ed2635c686f449eea25915b2", ...}])
ToolMessage(content="<tool_response!>", tool_call_id="call_ed2635c686f449eea25915b2")However, when I tested the real agentic flow (asking Gemini, getting its response, executing tool, sending result back), I found that Gemini actually returns So the issue is:
Your empty content fix is valid and needed (Test 1→2 in my earlier tests), but there's this additional Happy to share the test script if you'd like to reproduce this on your end! |
|
And just to show how PR #81 addresses this - the # Instead of sending ToolMessage (which OCI can't translate for Gemini)
# GeminiProvider converts it to a UserMessage:
ToolMessage(content="Sunny, 22°C")
→ UserMessage(content="Function get_weather returned: Sunny, 22°C")
# And AIMessage with tool_calls (where id=None is a problem)
# becomes a regular AssistantMessage:
AIMessage(content="", tool_calls=[{id: None, name: "get_weather", ...}])
→ AssistantMessage(content="I'll call get_weather with arguments: {\"city\": \"Rome\"}")This bypasses both issues:
Same test with GeminiProvider: Both PRs could work together - your fix handles the empty content edge case, and the GeminiProvider handles the message format translation. What do you think? |
Refactors the logic for assigning the id field when creating a ToolCall object to ensure an id is always set, generating a new UUID if necessary.
|
Thanks @fede-kamel for the thorough testing and catching the However, we already have UUID generation logic in Current (main):
My fix: Now when tool_call.id is None, we generate a UUID ✓ This is a 3-line change that makes the existing UUID fallback logic work correctly for Gemini. No need for a separate provider or message rewriting layer — we just needed to check for null/empty IDs before using them. Your Thanks again for the detailed testing! |
|
Thanks for working on this fix! I tested locally and can confirm this solution works for the Gemini tool calling flow. Test Results:
The GenericProvider approach with your fixes appears to be sufficient for the time being. The simple 13-line fix is preferable to a more complex provider workaround. cc: @luigisaetta - this should resolve your issue! Thanks to the team for the quick turnaround on this! 🙏 |
Problem
When using Google Gemini models with tool calling, two issues prevent the full agentic tool loop from working:
Empty content error:
AIMessageswith empty content (content="") cause aTransientServiceError(HTTP 500) due to aNullPointerExceptionin the OCI backend when converting messages to Google's format.Missing tool call IDs: Gemini returns tool calls with
id: None, which causesValidationErrorwhen creatingToolMessageobjects (tool_call_idmust be a non-empty string).Issue link: #78
Solution
.) to prevent null serialization. This follows the same pattern used by the Cohere provider.convert_oci_tool_call_to_langchain()to check iftool_call.idisNonebefore using it. Previously, the code only checked if "id" existed, but didn't handle the case where the value wasNone.Testing
Tested with
google.gemini-2.5-flashmodel using AIMessage with empty content and tool calls - error is now resolved.Model response with empty content:
content='The weather in Rome is currently sunny with a temperature of 20 degrees Celsius.' additional_kwargs={'finish_reason': 'stop'} response_metadata={'finish_reason': 'stop'} id='run--15f18f69-1e8d-465d-b996-d91e208f7d49-0'Model response with
tool_call_idisNone:content='The weather in Rome is sunny with a temperature of 22°C.' additional_kwargs={'finish_reason': 'stop', 'time_created': '2025-12-17 22:09:15.398000+00:00', 'total_tokens': 56} response_metadata={'model_id': 'google.gemini-2.5-flash', 'model_version': '1.0.0', 'request_id': 'A5559EBE7F2146A7BA83C43D3F3F15B5/9AA6710DD987FB0AD1760230AD667DDA/085E0D086FF026F6C6002D3E16462892', 'content-length': '304', 'finish_reason': 'stop', 'time_created': '2025-12-17 22:09:15.398000+00:00', 'total_tokens': 56} id='run--1936f4a2-24d3-4d91-afd3-37c4f1153e6a-0'