Skip to content

Cuga Integration Analysis. #221

@visahak

Description

@visahak

@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:

  1. ToolMessage skipped — the else: continue branch drops all tool results
  2. 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

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions