From 167a62926750de95f56a94c094b04a9474baa0b1 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 19 May 2026 07:20:08 +0000 Subject: [PATCH] fix(oci/cohere): emit tool calls in streaming and null content when text empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit handle_cohere_response now sets message.content to None when the Cohere response text is empty, matching the OpenAI convention for tool-call-only responses. handle_cohere_stream_chunk now extracts toolCalls — both directly from the chunk and from the terminal chunk's chatHistory CHATBOT message — and emits them in the delta. Previously, CohereStreamChunk lacked a toolCalls field, so any tool calls in the stream were silently dropped. --- litellm/llms/oci/chat/cohere.py | 27 +++++++++++++++++++++++++-- litellm/types/llms/oci.py | 1 + 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/litellm/llms/oci/chat/cohere.py b/litellm/llms/oci/chat/cohere.py index f13a371c85c..ce3560cd2bb 100644 --- a/litellm/llms/oci/chat/cohere.py +++ b/litellm/llms/oci/chat/cohere.py @@ -215,12 +215,14 @@ def handle_cohere_response( for tc in cohere_response.chatResponse.toolCalls ] + content: Optional[str] = response_text if response_text else None + model_response.choices = [ Choices( index=0, message={ "role": "assistant", - "content": response_text, + "content": content, "tool_calls": tool_calls, }, finish_reason=finish_reason, @@ -264,6 +266,27 @@ def handle_cohere_stream_chunk(dict_chunk: dict) -> ModelResponseStream: ) text = "" if is_terminal_consolidation else (typed_chunk.text or "") + cohere_tool_calls = typed_chunk.toolCalls + if cohere_tool_calls is None and is_terminal_consolidation: + for history_msg in typed_chunk.chatHistory or []: + if history_msg.role == "CHATBOT" and history_msg.toolCalls: + cohere_tool_calls = history_msg.toolCalls + break + + tool_calls: Optional[List[Dict[str, Any]]] = None + if cohere_tool_calls: + tool_calls = [ + { + "id": f"call_{uuid.uuid4().hex[:24]}", + "type": "function", + "function": { + "name": tc.name, + "arguments": json.dumps(tc.parameters), + }, + } + for tc in cohere_tool_calls + ] + finish_reason = typed_chunk.finishReason if finish_reason == "COMPLETE": finish_reason = "stop" @@ -278,7 +301,7 @@ def handle_cohere_stream_chunk(dict_chunk: dict) -> ModelResponseStream: index=typed_chunk.index if typed_chunk.index else 0, delta=Delta( content=text, - tool_calls=None, + tool_calls=tool_calls, provider_specific_fields=None, thinking_blocks=None, reasoning_content=None, diff --git a/litellm/types/llms/oci.py b/litellm/types/llms/oci.py index 22ca3fa90cf..d9dbd4fcb4b 100644 --- a/litellm/types/llms/oci.py +++ b/litellm/types/llms/oci.py @@ -215,6 +215,7 @@ class CohereStreamChunk(BaseModel): text: Optional[str] = None chatHistory: Optional[List[CohereMessage]] = None finishReason: Optional[str] = None + toolCalls: Optional[List[CohereToolCall]] = None pad: Optional[str] = None index: Optional[int] = None