Preserve Gemini tool context during provider fallback
This commit is contained in:
parent
56924eb956
commit
d2ce88737d
|
|
@ -159,12 +159,44 @@ def _build_gemini_contents_from_messages(
|
|||
continue
|
||||
|
||||
if isinstance(msg.get("content"), list):
|
||||
text_parts: list[dict[str, str]] = []
|
||||
parts: list[dict[str, Any]] = []
|
||||
for block in cast(list[dict[str, Any]], msg["content"]):
|
||||
if block.get("type") == "text" and isinstance(block.get("text"), str):
|
||||
text_parts.append({"text": block["text"]})
|
||||
if text_parts:
|
||||
gemini_contents.append({"role": role, "parts": text_parts})
|
||||
block_type = block.get("type")
|
||||
|
||||
if block_type == "text" and isinstance(block.get("text"), str):
|
||||
parts.append({"text": block["text"]})
|
||||
continue
|
||||
|
||||
if block_type == "tool_use" and isinstance(block.get("name"), str):
|
||||
tool_args = block.get("input")
|
||||
parts.append(
|
||||
{
|
||||
"function_call": {
|
||||
"name": block["name"],
|
||||
"args": tool_args if isinstance(tool_args, dict) else {},
|
||||
}
|
||||
}
|
||||
)
|
||||
continue
|
||||
|
||||
if block_type == "tool_result":
|
||||
tool_result = block.get("content")
|
||||
if not isinstance(tool_result, str):
|
||||
tool_result = json.dumps(tool_result)
|
||||
parts.append(
|
||||
{
|
||||
"function_response": {
|
||||
"name": str(block.get("tool_use_id", "tool_result")),
|
||||
"response": {
|
||||
"result": tool_result,
|
||||
"is_error": bool(block.get("is_error", False)),
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if parts:
|
||||
gemini_contents.append({"role": role, "parts": parts})
|
||||
|
||||
system_instruction = (
|
||||
"\n\n".join(system_instruction_parts) if system_instruction_parts else None
|
||||
|
|
|
|||
|
|
@ -905,6 +905,94 @@ class TestGoogleClient:
|
|||
"stable instructions\n\nrolling session context"
|
||||
)
|
||||
|
||||
async def test_google_preserves_non_text_content_blocks(self):
|
||||
"""Gemini should preserve tool context when converting content blocks."""
|
||||
from google import genai
|
||||
|
||||
mock_client = Mock(spec=genai.Client)
|
||||
mock_response = Mock()
|
||||
mock_part = Mock()
|
||||
mock_part.text = "ok"
|
||||
mock_part.function_call = None
|
||||
mock_content = Mock()
|
||||
mock_content.parts = [mock_part]
|
||||
mock_finish_reason = Mock()
|
||||
mock_finish_reason.name = "STOP"
|
||||
mock_candidate = Mock()
|
||||
mock_candidate.content = mock_content
|
||||
mock_candidate.finish_reason = mock_finish_reason
|
||||
mock_response.candidates = [mock_candidate]
|
||||
mock_usage_metadata = Mock()
|
||||
mock_usage_metadata.prompt_token_count = 10
|
||||
mock_usage_metadata.candidates_token_count = 5
|
||||
mock_response.usage_metadata = mock_usage_metadata
|
||||
mock_aio = Mock()
|
||||
mock_aio.models.generate_content = AsyncMock(return_value=mock_response)
|
||||
mock_client.aio = mock_aio
|
||||
|
||||
with patch.dict(CLIENTS, {"google": mock_client}):
|
||||
await honcho_llm_call_inner(
|
||||
provider="google",
|
||||
model="gemini-1.5-pro",
|
||||
prompt="ignored",
|
||||
max_tokens=100,
|
||||
messages=[
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{"type": "text", "text": "Tool call context"},
|
||||
{
|
||||
"type": "tool_use",
|
||||
"id": "toolu_1",
|
||||
"name": "search_memory",
|
||||
"input": {"query": "tea"},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "tool_result",
|
||||
"tool_use_id": "toolu_1",
|
||||
"content": "Alice likes tea",
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
call_args = mock_aio.models.generate_content.call_args
|
||||
assert call_args is not None
|
||||
assert call_args.kwargs["contents"] == [
|
||||
{
|
||||
"role": "model",
|
||||
"parts": [
|
||||
{"text": "Tool call context"},
|
||||
{
|
||||
"function_call": {
|
||||
"name": "search_memory",
|
||||
"args": {"query": "tea"},
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"parts": [
|
||||
{
|
||||
"function_response": {
|
||||
"name": "toolu_1",
|
||||
"response": {
|
||||
"result": "Alice likes tea",
|
||||
"is_error": False,
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
async def test_google_streaming(self):
|
||||
"""Test Google/Gemini streaming response"""
|
||||
from google import genai
|
||||
|
|
|
|||
Loading…
Reference in New Issue