@gaodan-fang @gjt-prog was doing an analysis on cuga and altk-evolve, and seems that we have missed some messages. Can we validate this issue?
Problem
The hand-rolled _convert_messages() in src/cuga/backend/evolve/integration.py (lines 100-119) only handles HumanMessage and AIMessage. It has two deficiencies:
ToolMessage skipped — the else: continue branch drops all tool results
AIMessage.tool_calls lost — only .content is extracted; for tool-calling turns, .content is often empty while the actual function name and arguments live in .tool_calls
The exported trajectory is incomplete: Evolve sees a conversation with gaps where tool interactions should be.
Solution
LangChain provides convert_to_openai_messages() which correctly handles all message types — including AIMessage with tool_calls and ToolMessage with tool_call_id. The entire hand-rolled method can be replaced.
File to Change
src/cuga/backend/evolve/integration.py — Replace _convert_messages()
Current implementation (lines 100-119):
@staticmethod
def _convert_messages(chat_messages: List[BaseMessage]) -> list:
"""Convert LangChain BaseMessage list to OpenAI conversation format."""
result = []
for i, msg in enumerate(chat_messages):
if isinstance(msg, HumanMessage):
role = "user"
elif isinstance(msg, AIMessage):
role = "assistant"
else:
logger.debug(f"Evolve: Skipping message {i} of type {type(msg).__name__}")
continue
content = msg.content if isinstance(msg.content, str) else str(msg.content)
if content:
result.append({"role": role, "content": content})
else:
logger.debug(f"Evolve: Skipping empty {role} message {i}")
logger.debug(f"Evolve: Converted {len(result)}/{len(chat_messages)} messages")
return result
Proposed implementation:
@staticmethod
def _convert_messages(chat_messages: List[BaseMessage]) -> list:
"""Convert LangChain BaseMessage list to OpenAI conversation format."""
from langchain_core.messages import convert_to_openai_messages
try:
result = convert_to_openai_messages(chat_messages)
logger.debug(f"Evolve: Converted {len(result)}/{len(chat_messages)} messages")
return result
except Exception as e:
logger.warning(f"Evolve: Message conversion failed (non-fatal): {e}")
return []
This produces correct OpenAI-format output for all message types:
| LangChain Type |
OpenAI Output |
What's Preserved |
HumanMessage |
{"role": "user", "content": "..."} |
Content |
AIMessage (text only) |
{"role": "assistant", "content": "..."} |
Content |
AIMessage (tool call) |
{"role": "assistant", "tool_calls": [...], "content": ""} |
Function name, arguments, call ID |
ToolMessage |
{"role": "tool", "tool_call_id": "...", "name": "...", "content": "..."} |
Tool result, call ID, tool name |
cc: @jayaramkr @vinodmut
@gaodan-fang @gjt-prog was doing an analysis on cuga and altk-evolve, and seems that we have missed some messages. Can we validate this issue?
Problem
The hand-rolled
_convert_messages()insrc/cuga/backend/evolve/integration.py(lines 100-119) only handlesHumanMessageandAIMessage. It has two deficiencies:ToolMessageskipped — theelse: continuebranch drops all tool resultsAIMessage.tool_callslost — only.contentis extracted; for tool-calling turns,.contentis often empty while the actual function name and arguments live in.tool_callsThe exported trajectory is incomplete: Evolve sees a conversation with gaps where tool interactions should be.
Solution
LangChain provides
convert_to_openai_messages()which correctly handles all message types — includingAIMessagewithtool_callsandToolMessagewithtool_call_id. The entire hand-rolled method can be replaced.File to Change
src/cuga/backend/evolve/integration.py— Replace_convert_messages()Current implementation (lines 100-119):
Proposed implementation:
This produces correct OpenAI-format output for all message types:
HumanMessage{"role": "user", "content": "..."}AIMessage(text only){"role": "assistant", "content": "..."}AIMessage(tool call){"role": "assistant", "tool_calls": [...], "content": ""}ToolMessage{"role": "tool", "tool_call_id": "...", "name": "...", "content": "..."}cc: @jayaramkr @vinodmut