Skip to content

ViewCode Message Removal in ReAct Message History Type #53

@alancelestino

Description

@alancelestino

Hey,

First of all, thanks for this repo---amazing work!

Here's a bug or a feature:

When running

from moatless.benchmark.swebench import create_repository
from moatless.benchmark.utils import get_moatless_instance
from moatless.agent.code_agent import CodingAgent
from moatless.index import CodeIndex
from moatless.loop import AgenticLoop
from moatless.file_context import FileContext
from moatless.completion.base import LLMResponseFormat
from moatless.schema import MessageHistoryType
from moatless.completion.base import BaseCompletionModel, LLMResponseFormat
from moatless.schema import MessageHistoryType

index_store_dir = os.getenv("INDEX_STORE_DIR", "/tmp/index_store")
repo_base_dir = os.getenv("REPO_DIR", "/tmp/repos")
persist_path = "trajectory_qwen.json"

instance = get_moatless_instance("django__django-16379")
repository = create_repository(instance)
code_index = CodeIndex.from_index_name(
    instance["instance_id"], 
    index_store_dir=index_store_dir, 
    file_repo=repository
)
file_context = FileContext(repo=repository)

# Create the completion model directly instead of using model name
completion_model = BaseCompletionModel.create(
    # Required parameters
    response_format=LLMResponseFormat.REACT,
    model="openrouter/qwen/qwen-2.5-coder-32b-instruct",
    
    # OpenRouter connection parameters
    model_base_url="https://openrouter.ai/api/v1",
    model_api_key=os.environ["OPENAI_API_KEY"],
    
    # Model behavior parameters
    temperature=0.0,
    max_tokens=4000,
    
    # Message history configuration
    message_history_type=MessageHistoryType.REACT,
    few_shot_examples=True
)

# Create agent with the custom completion model
agent = CodingAgent.create(
    repository=repository,
    code_index=code_index,
    completion_model=completion_model  # Use the custom model we created
)

loop = AgenticLoop.create(
    message=instance["problem_statement"],
    agent=agent,
    file_context=file_context,
    repository=repository,
    persist_path=persist_path,
    max_iterations=50,
    max_cost=2.0
)

final_node = loop.run()
if final_node:
    print(final_node.observation.message)

I get the following trajectory:
trajectory_qwen.json

In node 1, the model tries StringReplace before ever calling ViewCode and gets scolded by the scaffolding. In node 2, the model calls ViewCode. This history is properly reflected in node 3 (completions --> build_action --> input) and the model calls StringReplace. In node 4, however, the first ViewCode is removed and the message history becomes inconsistent.

I believe the reason for this is that shown_files from moatless/message_history/compact.py gets updated via

                if self.include_file_context and previous_node.action.name != "ViewCode":
                    files_to_show = set()
                    has_edits = False
                    for context_file in previous_node.file_context.get_context_files():
                        if (
                            context_file.was_edited or context_file.was_viewed
                        ) and context_file.file_path not in shown_files:
                            files_to_show.add(context_file.file_path)
                        if context_file.was_edited:
                            has_edits = True

                    shown_files.update(files_to_show)

And then the ViewCode action never gets added to the history because the file was already shown:

                    if action_step.action.name == "ViewCode":
                        # Always include ViewCode actions
                        file_path = action_step.action.files[0].file_path

                        if file_path not in shown_files:
                            context_file = previous_node.file_context.get_context_file(file_path)
                            if context_file and (context_file.span_ids or context_file.show_all_spans):
                                shown_files.add(context_file.file_path)
                                observation = context_file.to_prompt(
                                    show_span_ids=False,
                                    show_line_numbers=True,
                                    exclude_comments=False,
                                    show_outcommented_code=True,
                                    outcomment_code_comment="... rest of the code",
                                )
                            else:
                                observation = action_step.observation.message
                            current_messages.append(NodeMessage(action=action_step.action, observation=observation))

I wonder if this is a compromise between conversation flow logic and message length or rather a bug.

Originally posted by @alancelestino in #52

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions