mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 14:57:59 -05:00
Handle error observations in codeact (#3383)
* Handle error observations in codeact * Remove comments
This commit is contained in:
@@ -22,6 +22,7 @@ from opendevin.events.observation import (
|
||||
CmdOutputObservation,
|
||||
IPythonRunCellObservation,
|
||||
)
|
||||
from opendevin.events.observation.error import ErrorObservation
|
||||
from opendevin.events.observation.observation import Observation
|
||||
from opendevin.events.serialization.event import truncate_content
|
||||
from opendevin.llm.llm import LLM
|
||||
@@ -169,7 +170,14 @@ class CodeActAgent(Agent):
|
||||
str(obs.outputs), max_message_chars
|
||||
)
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
return None
|
||||
elif isinstance(obs, ErrorObservation):
|
||||
text = 'OBSERVATION:\n' + truncate_content(obs.content, max_message_chars)
|
||||
text += '\n[Error occurred in processing last action]'
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
else:
|
||||
# If an observation message is not returned, it will cause an error
|
||||
# when the LLM tries to return the next message
|
||||
raise ValueError(f'Unknown observation type: {type(obs)}')
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Resets the CodeAct Agent."""
|
||||
|
||||
@@ -176,11 +176,11 @@ class AgentController:
|
||||
elif isinstance(event, ModifyTaskAction):
|
||||
self.state.root_task.set_subtask_state(event.task_id, event.state)
|
||||
elif isinstance(event, AgentFinishAction):
|
||||
self.state.outputs = event.outputs # type: ignore[attr-defined]
|
||||
self.state.outputs = event.outputs
|
||||
self.state.metrics.merge(self.state.local_metrics)
|
||||
await self.set_agent_state_to(AgentState.FINISHED)
|
||||
elif isinstance(event, AgentRejectAction):
|
||||
self.state.outputs = event.outputs # type: ignore[attr-defined]
|
||||
self.state.outputs = event.outputs
|
||||
self.state.metrics.merge(self.state.local_metrics)
|
||||
await self.set_agent_state_to(AgentState.REJECTED)
|
||||
elif isinstance(event, Observation):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
from opendevin.core.schema import ActionType
|
||||
|
||||
@@ -35,7 +36,15 @@ class AgentSummarizeAction(Action):
|
||||
|
||||
@dataclass
|
||||
class AgentFinishAction(Action):
|
||||
outputs: dict = field(default_factory=dict)
|
||||
"""An action where the agent finishes the task.
|
||||
|
||||
Attributes:
|
||||
outputs (dict): The outputs of the agent, for instance "content".
|
||||
thought (str): The agent's explanation of its actions.
|
||||
action (str): The action type, namely ActionType.FINISH.
|
||||
"""
|
||||
|
||||
outputs: dict[str, Any] = field(default_factory=dict)
|
||||
thought: str = ''
|
||||
action: str = ActionType.FINISH
|
||||
|
||||
|
||||
@@ -7,7 +7,13 @@ from .observation import Observation
|
||||
|
||||
@dataclass
|
||||
class AgentDelegateObservation(Observation):
|
||||
"""This data class represents the result from delegating to another agent"""
|
||||
"""This data class represents the result from delegating to another agent.
|
||||
|
||||
Attributes:
|
||||
content (str): The content of the observation.
|
||||
outputs (dict): The outputs of the delegated agent.
|
||||
observation (str): The type of observation.
|
||||
"""
|
||||
|
||||
outputs: dict
|
||||
observation: str = ObservationType.DELEGATE
|
||||
|
||||
97
tests/unit/test_codeact_agent.py
Normal file
97
tests/unit/test_codeact_agent.py
Normal file
@@ -0,0 +1,97 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from agenthub.codeact_agent.codeact_agent import CodeActAgent
|
||||
from opendevin.core.config import LLMConfig
|
||||
from opendevin.core.message import TextContent
|
||||
from opendevin.events.observation.commands import (
|
||||
CmdOutputObservation,
|
||||
IPythonRunCellObservation,
|
||||
)
|
||||
from opendevin.events.observation.delegate import AgentDelegateObservation
|
||||
from opendevin.events.observation.error import ErrorObservation
|
||||
from opendevin.llm.llm import LLM
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def agent() -> CodeActAgent:
|
||||
agent = CodeActAgent(llm=LLM(LLMConfig()))
|
||||
agent.llm = Mock()
|
||||
agent.llm.config = Mock()
|
||||
agent.llm.config.max_message_chars = 100
|
||||
return agent
|
||||
|
||||
|
||||
def test_cmd_output_observation_message(agent: CodeActAgent):
|
||||
obs = CmdOutputObservation(
|
||||
command='echo hello', content='Command output', command_id=1, exit_code=0
|
||||
)
|
||||
|
||||
result = agent.get_observation_message(obs)
|
||||
|
||||
assert result is not None
|
||||
assert result.role == 'user'
|
||||
assert len(result.content) == 1
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert 'OBSERVATION:' in result.content[0].text
|
||||
assert 'Command output' in result.content[0].text
|
||||
assert 'Command 1 finished with exit code 0' in result.content[0].text
|
||||
|
||||
|
||||
def test_ipython_run_cell_observation_message(agent: CodeActAgent):
|
||||
obs = IPythonRunCellObservation(
|
||||
code='plt.plot()',
|
||||
content='IPython output\n',
|
||||
)
|
||||
|
||||
result = agent.get_observation_message(obs)
|
||||
|
||||
assert result is not None
|
||||
assert result.role == 'user'
|
||||
assert len(result.content) == 1
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert 'OBSERVATION:' in result.content[0].text
|
||||
assert 'IPython output' in result.content[0].text
|
||||
assert (
|
||||
' already displayed to user'
|
||||
in result.content[0].text
|
||||
)
|
||||
assert 'ABC123' not in result.content[0].text
|
||||
|
||||
|
||||
def test_agent_delegate_observation_message(agent: CodeActAgent):
|
||||
obs = AgentDelegateObservation(
|
||||
content='Content', outputs={'content': 'Delegated agent output'}
|
||||
)
|
||||
|
||||
result = agent.get_observation_message(obs)
|
||||
|
||||
assert result is not None
|
||||
assert result.role == 'user'
|
||||
assert len(result.content) == 1
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert 'OBSERVATION:' in result.content[0].text
|
||||
assert 'Delegated agent output' in result.content[0].text
|
||||
|
||||
|
||||
def test_error_observation_message(agent: CodeActAgent):
|
||||
obs = ErrorObservation('Error message')
|
||||
|
||||
result = agent.get_observation_message(obs)
|
||||
|
||||
assert result is not None
|
||||
assert result.role == 'user'
|
||||
assert len(result.content) == 1
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert 'OBSERVATION:' in result.content[0].text
|
||||
assert 'Error message' in result.content[0].text
|
||||
assert 'Error occurred in processing last action' in result.content[0].text
|
||||
|
||||
|
||||
def test_unknown_observation_message(agent: CodeActAgent):
|
||||
obs = Mock()
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
agent.get_observation_message(obs)
|
||||
assert 'Unknown observation type:' in str(excinfo.value)
|
||||
Reference in New Issue
Block a user