mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-28 01:32:17 +00:00
fix(oci/cohere): preserve trailing tool result in chatHistory
When the last message in the OpenAI-format input is a tool result (the standard agentic continuation pattern), the prior messages[:-1] slice silently dropped that tool result from chatHistory and the model never saw it. Excluding the last user message by index instead keeps tool results that trail the last user turn intact.
This commit is contained in:
parent
bf133568c0
commit
49ba871f71
2 changed files with 42 additions and 19 deletions
|
|
@ -60,7 +60,11 @@ def adapt_messages_to_cohere_standard(
|
|||
) -> List[CohereMessage]:
|
||||
"""Build a Cohere ``chatHistory`` list from an OpenAI-format message array.
|
||||
|
||||
- All messages except the last are included (the last becomes ``message``).
|
||||
- All messages except the *last user message* are included. The caller pulls
|
||||
the last user message into the request's top-level ``message`` field, so
|
||||
trailing tool results (the standard agentic continuation pattern) still
|
||||
appear in ``chatHistory`` and reach the model.
|
||||
- If no user message exists, every message is included (no slice).
|
||||
- Tool results are expressed as OCI ``CohereToolMessage.toolResults`` entries,
|
||||
with the originating call's name and parameters resolved from the preceding
|
||||
assistant message via a ``tool_call_id`` lookup.
|
||||
|
|
@ -85,8 +89,22 @@ def adapt_messages_to_cohere_standard(
|
|||
parameters=params,
|
||||
)
|
||||
|
||||
last_user_index = next(
|
||||
(
|
||||
i
|
||||
for i in range(len(messages) - 1, -1, -1)
|
||||
if messages[i].get("role") == "user"
|
||||
),
|
||||
None,
|
||||
)
|
||||
history_source = (
|
||||
messages
|
||||
if last_user_index is None
|
||||
else [m for i, m in enumerate(messages) if i != last_user_index]
|
||||
)
|
||||
|
||||
chat_history: List[CohereMessage] = []
|
||||
for msg in messages[:-1]:
|
||||
for msg in history_source:
|
||||
role = msg.get("role")
|
||||
content = _extract_text_content(msg.get("content"))
|
||||
|
||||
|
|
|
|||
|
|
@ -326,20 +326,24 @@ class TestOCICohereToolCalls:
|
|||
|
||||
chat_history = adapt_messages_to_cohere_standard(messages)
|
||||
|
||||
# First message is the user message
|
||||
assert chat_history[0].role == "USER"
|
||||
assert chat_history[0].message == "What's the weather?"
|
||||
# The last user message is consumed by the request's top-level `message`
|
||||
# field, so chatHistory carries the assistant tool call and tool result.
|
||||
assert len(chat_history) == 2
|
||||
|
||||
# Second message is the assistant with tool calls and no text
|
||||
assistant_msg = chat_history[1]
|
||||
assistant_msg = chat_history[0]
|
||||
assert assistant_msg.role == "CHATBOT"
|
||||
assert assistant_msg.message is None or assistant_msg.message == ""
|
||||
assert assistant_msg.toolCalls is not None
|
||||
assert len(assistant_msg.toolCalls) == 1
|
||||
assert assistant_msg.toolCalls[0].name == "get_weather"
|
||||
|
||||
tool_msg = chat_history[1]
|
||||
assert tool_msg.role == "TOOL"
|
||||
assert tool_msg.toolResults[0].call.name == "get_weather"
|
||||
assert tool_msg.toolResults[0].outputs[0]["output"] == "Sunny, 25C"
|
||||
|
||||
def test_cohere_chat_history_with_tool_calls(self):
|
||||
"""Test chat history transformation with tool calls"""
|
||||
"""Tool results trailing the last user turn must be preserved in chatHistory."""
|
||||
config = OCIChatConfig()
|
||||
|
||||
messages = [
|
||||
|
|
@ -367,26 +371,27 @@ class TestOCICohereToolCalls:
|
|||
|
||||
chat_history = adapt_messages_to_cohere_standard(messages)
|
||||
|
||||
# Verify chat history structure (excludes last message)
|
||||
# The last user message becomes the request's top-level `message`.
|
||||
# Everything else — including the trailing tool result — must remain in
|
||||
# chatHistory so the model can see the tool output.
|
||||
assert len(chat_history) == 2
|
||||
|
||||
# Check user message
|
||||
user_msg = chat_history[0]
|
||||
assert user_msg.role == "USER"
|
||||
assert user_msg.message == "What's the weather like in Tokyo?"
|
||||
|
||||
# Check assistant message with tool calls
|
||||
assistant_msg = chat_history[1]
|
||||
assistant_msg = chat_history[0]
|
||||
assert assistant_msg.role == "CHATBOT"
|
||||
assert assistant_msg.message == "I will look up the weather in Tokyo."
|
||||
assert assistant_msg.toolCalls is not None
|
||||
assert len(assistant_msg.toolCalls) == 1
|
||||
assert assistant_msg.toolCalls[0].name == "get_weather"
|
||||
# The parameters should be parsed as JSON
|
||||
assert assistant_msg.toolCalls[0].parameters == {"location": "Tokyo"}
|
||||
|
||||
# Note: The tool message (last message) is excluded from chat history
|
||||
# This is the expected behavior for Cohere models
|
||||
tool_msg = chat_history[1]
|
||||
assert tool_msg.role == "TOOL"
|
||||
assert tool_msg.toolResults[0].call.name == "get_weather"
|
||||
assert tool_msg.toolResults[0].call.parameters == {"location": "Tokyo"}
|
||||
assert (
|
||||
tool_msg.toolResults[0].outputs[0]["output"]
|
||||
== "The weather in Tokyo is 22°C with partly cloudy skies."
|
||||
)
|
||||
|
||||
def test_cohere_streaming_chunk_handling(self):
|
||||
"""Test Cohere streaming chunk handling"""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue