From 81829289ab7305eb8e15fcf2f08c5bcd91c5ce7c Mon Sep 17 00:00:00 2001 From: "Ryan H. Tran" Date: Thu, 28 Aug 2025 20:22:28 +0700 Subject: [PATCH] Add support for passing list of `Message` into LLM `completion` (#10671) --- .../agenthub/browsing_agent/browsing_agent.py | 2 +- .../agenthub/codeact_agent/codeact_agent.py | 2 +- .../visualbrowsing_agent.py | 4 +--- openhands/llm/llm.py | 18 +++++++++++++++--- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/openhands/agenthub/browsing_agent/browsing_agent.py b/openhands/agenthub/browsing_agent/browsing_agent.py index bf2c0960a0..cd1decd261 100644 --- a/openhands/agenthub/browsing_agent/browsing_agent.py +++ b/openhands/agenthub/browsing_agent/browsing_agent.py @@ -217,7 +217,7 @@ class BrowsingAgent(Agent): messages.append(Message(role='user', content=[TextContent(text=prompt)])) response = self.llm.completion( - messages=self.llm.format_messages_for_llm(messages), + messages=messages, stop=[')```', ')\n```'], ) return self.response_parser.parse(response) diff --git a/openhands/agenthub/codeact_agent/codeact_agent.py b/openhands/agenthub/codeact_agent/codeact_agent.py index 402d4ba1d1..1f4686c0f0 100644 --- a/openhands/agenthub/codeact_agent/codeact_agent.py +++ b/openhands/agenthub/codeact_agent/codeact_agent.py @@ -204,7 +204,7 @@ class CodeActAgent(Agent): initial_user_message = self._get_initial_user_message(state.history) messages = self._get_messages(condensed_history, initial_user_message) params: dict = { - 'messages': self.llm.format_messages_for_llm(messages), + 'messages': messages, } params['tools'] = check_tools(self.tools, self.llm.config) params['extra_body'] = { diff --git a/openhands/agenthub/visualbrowsing_agent/visualbrowsing_agent.py b/openhands/agenthub/visualbrowsing_agent/visualbrowsing_agent.py index 322629d3a3..0603da38f9 100644 --- a/openhands/agenthub/visualbrowsing_agent/visualbrowsing_agent.py +++ b/openhands/agenthub/visualbrowsing_agent/visualbrowsing_agent.py @@ -301,10 +301,8 @@ You are an agent trying to solve a web task based on the content of the page and messages.append(Message(role='system', content=[TextContent(text=system_msg)])) messages.append(Message(role='user', content=human_prompt)) - flat_messages = self.llm.format_messages_for_llm(messages) - response = self.llm.completion( - messages=flat_messages, + messages=messages, temperature=0.0, stop=[')```', ')\n```'], ) diff --git a/openhands/llm/llm.py b/openhands/llm/llm.py index d3926848cc..fab3de1e40 100644 --- a/openhands/llm/llm.py +++ b/openhands/llm/llm.py @@ -3,7 +3,7 @@ import os import time import warnings from functools import partial -from typing import Any, Callable +from typing import Any, Callable, cast import httpx @@ -220,7 +220,9 @@ class LLM(RetryMixin, DebugMixin): """Wrapper for the litellm completion function. Logs the input and output of the completion function.""" from openhands.io import json - messages_kwarg: list[dict[str, Any]] | dict[str, Any] = [] + messages_kwarg: ( + dict[str, Any] | Message | list[dict[str, Any]] | list[Message] + ) = [] mock_function_calling = not self.is_function_calling_active() # some callers might send the model and messages directly @@ -239,9 +241,19 @@ class LLM(RetryMixin, DebugMixin): messages_kwarg = kwargs['messages'] # ensure we work with a list of messages - messages: list[dict[str, Any]] = ( + messages_list = ( messages_kwarg if isinstance(messages_kwarg, list) else [messages_kwarg] ) + # Format Message objects to dict format if needed + messages: list[dict] = [] + if messages_list and isinstance(messages_list[0], Message): + messages = self.format_messages_for_llm( + cast(list[Message], messages_list) + ) + else: + messages = cast(list[dict[str, Any]], messages_list) + + kwargs['messages'] = messages # handle conversion of to non-function calling messages if needed original_fncall_messages = copy.deepcopy(messages)