mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
Merge dc74601797 into 08f2a98250
This commit is contained in:
commit
0ce6406941
2 changed files with 80 additions and 3 deletions
|
|
@ -255,6 +255,7 @@ def _genai_content_messages(content: object) -> tuple[AllMessageValues, ...]:
|
|||
return ()
|
||||
parts: Final = _genai_content_parts(fields.get("parts", ()))
|
||||
converted: Final = _genai_message_parts(parts)
|
||||
responses: Final = tuple(response for part in parts if (response := _genai_tool_response(part)) is not None)
|
||||
role: Final = fields.get("role", "user")
|
||||
if role == "user":
|
||||
user_message: Final = (
|
||||
|
|
@ -269,18 +270,20 @@ def _genai_content_messages(content: object) -> tuple[AllMessageValues, ...]:
|
|||
if converted
|
||||
else ()
|
||||
)
|
||||
responses: Final = tuple(response for part in parts if (response := _genai_tool_response(part)) is not None)
|
||||
return (*user_message, *responses)
|
||||
if role != "model":
|
||||
return ()
|
||||
text: Final = "".join(part["text"] for part in converted if part["type"] == "text")
|
||||
calls: Final = [call for part in parts if (call := _genai_tool_call(part)) is not None]
|
||||
assistant_content: Final = list(converted) if any(part["type"] != "text" for part in converted) else text or None
|
||||
return (
|
||||
if responses and not calls and assistant_content is None:
|
||||
return responses
|
||||
assistant_message: Final = (
|
||||
ChatCompletionAssistantMessage(role="assistant", content=assistant_content, tool_calls=calls)
|
||||
if calls
|
||||
else ChatCompletionAssistantMessage(role="assistant", content=assistant_content),
|
||||
else ChatCompletionAssistantMessage(role="assistant", content=assistant_content)
|
||||
)
|
||||
return (assistant_message, *responses)
|
||||
|
||||
|
||||
class GoogleGenAIStreamWrapper(AdapterCompletionStreamWrapper):
|
||||
|
|
|
|||
|
|
@ -194,6 +194,80 @@ def test_native_function_call_preserves_arguments_and_matching_response(args: ob
|
|||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("response_key", ["functionResponse", "function_response"])
|
||||
def test_model_turn_function_response_answers_the_preceding_call(response_key: str) -> None:
|
||||
result: Final = {"output": "return 1"}
|
||||
messages: Final = GoogleGenAIAdapter()._transform_contents_to_messages(
|
||||
[
|
||||
{"role": "user", "parts": [{"text": "Read /tmp/a.py"}]},
|
||||
{"role": "model", "parts": [{"functionCall": {"name": "view_file", "args": {"path": "/tmp/a.py"}}}]},
|
||||
{"role": "model", "parts": [{response_key: {"name": "view_file", "response": result}}]},
|
||||
]
|
||||
)
|
||||
assert [message["role"] for message in messages] == ["user", "assistant", "tool"]
|
||||
assert messages[2]["tool_call_id"] == messages[1]["tool_calls"][0]["id"]
|
||||
assert json.loads(messages[2]["content"]) == result
|
||||
|
||||
|
||||
def test_model_turn_with_call_and_response_puts_the_result_after_the_call() -> None:
|
||||
text: Final = "Checking the weather"
|
||||
result: Final = {"temp": "4C"}
|
||||
messages: Final = GoogleGenAIAdapter()._transform_contents_to_messages(
|
||||
[
|
||||
{
|
||||
"role": "model",
|
||||
"parts": [
|
||||
{"text": text},
|
||||
{"functionCall": {"name": "get_weather", "args": {"city": "Oslo"}}},
|
||||
{"functionResponse": {"name": "get_weather", "response": result}},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
assert [message["role"] for message in messages] == ["assistant", "tool"]
|
||||
assert messages[0]["content"] == text
|
||||
assert messages[1]["tool_call_id"] == messages[0]["tool_calls"][0]["id"]
|
||||
assert json.loads(messages[1]["content"]) == result
|
||||
|
||||
|
||||
def test_model_turn_text_next_to_a_function_response_is_not_dropped() -> None:
|
||||
text: Final = "Here is what the file contains"
|
||||
result: Final = {"output": "return 1"}
|
||||
messages: Final = GoogleGenAIAdapter()._transform_contents_to_messages(
|
||||
[{"role": "model", "parts": [{"text": text}, {"functionResponse": {"name": "view_file", "response": result}}]}]
|
||||
)
|
||||
assert [message["role"] for message in messages] == ["assistant", "tool"]
|
||||
assert messages[0]["content"] == text
|
||||
assert json.loads(messages[1]["content"]) == result
|
||||
|
||||
|
||||
def test_model_turn_function_response_keeps_its_media() -> None:
|
||||
result: Final = {"ok": True}
|
||||
mime: Final = "image/png"
|
||||
data: Final = "bWVkaWE="
|
||||
messages: Final = GoogleGenAIAdapter()._transform_contents_to_messages(
|
||||
[
|
||||
{"role": "model", "parts": [{"functionCall": {"name": "screenshot", "args": {}}}]},
|
||||
{
|
||||
"role": "model",
|
||||
"parts": [
|
||||
{
|
||||
"functionResponse": {
|
||||
"name": "screenshot",
|
||||
"response": result,
|
||||
"parts": [{"inlineData": {"mimeType": mime, "data": data}}],
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
]
|
||||
)
|
||||
assert [message["role"] for message in messages] == ["assistant", "tool"]
|
||||
text_part, media_part = messages[1]["content"]
|
||||
assert json.loads(text_part["text"]) == result
|
||||
assert media_part["image_url"]["url"] == f"data:{mime};base64,{data}"
|
||||
|
||||
|
||||
def test_adapter_import():
|
||||
"""Test that the adapter can be imported successfully"""
|
||||
from litellm.google_genai.adapters.handler import GenerateContentToCompletionHandler
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue