Fix issue #4819: [Bug]: Logs are not printed out in headless command line mode

This commit is contained in:
openhands
2024-11-07 14:02:18 +00:00
parent 0335b1a634
commit a17d06691a
4 changed files with 113 additions and 1 deletions

View File

@@ -185,6 +185,9 @@ async def run_controller(
message = fake_user_response_fn(controller.get_state())
action = MessageAction(content=message)
event_stream.add_event(action, EventSource.USER)
elif headless_mode:
# Log all events in headless mode
logger.info(event)
event_stream.subscribe(EventStreamSubscriber.MAIN, on_event, sid)

2
poetry.lock generated
View File

@@ -10127,4 +10127,4 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "2a4f90bb5c7f7d82160f57d71af7e81c7acef69426d0e1e46e1da09972a6215f"
content-hash = "bbe25c87d47a396a652969206f948907111a5850cde1abbc7ab08c9ef7112a50"

View File

@@ -61,6 +61,8 @@ protobuf = "^4.21.6,<5.0.0" # chromadb currently fails on 5.0+
opentelemetry-api = "1.25.0"
opentelemetry-exporter-otlp-proto-grpc = "1.25.0"
modal = "^0.64.145"
pytest = "^8.3.3"
pytest-asyncio = "^0.24.0"
[tool.poetry.group.llama-index.dependencies]
llama-index = "*"

View File

@@ -0,0 +1,107 @@
import asyncio
import logging
from unittest.mock import MagicMock, patch
import pytest
from openhands.core.config import AppConfig
from openhands.core.main import run_controller
from openhands.events.action import MessageAction
from openhands.events import EventSource, EventStream
from openhands.events.observation import AgentStateChangedObservation
from openhands.core.schema import AgentState
from openhands.runtime.base import Runtime
from openhands.storage.files import FileStore
class MockFileStore(FileStore):
"""Mock file store for testing"""
def __init__(self):
self.files = {}
def read(self, path: str) -> str:
return self.files.get(path, "")
def write(self, path: str, contents: str) -> None:
self.files[path] = contents
def list(self, path: str) -> list[str]:
return []
def delete(self, path: str) -> None:
if path in self.files:
del self.files[path]
class MockRuntime(Runtime):
"""Mock runtime for testing"""
async def connect(self):
pass
def run(self, action):
pass
def run_ipython(self, action):
pass
def read(self, action):
pass
def write(self, action):
pass
def browse(self, action):
pass
def browse_interactive(self, action):
pass
def copy_to(self, host_src, sandbox_dest, recursive=False):
pass
def list_files(self, path=None):
return []
def copy_from(self, path):
return b""
@pytest.mark.asyncio
async def test_headless_mode_logging():
# Mock the logger
mock_logger = MagicMock()
# Create a simple message action
message = "Test message"
action = MessageAction(content=message)
# Create a minimal config
config = AppConfig()
# Create event stream and runtime
file_store = MockFileStore()
event_stream = EventStream("test", file_store)
runtime = MockRuntime(config=config, event_stream=event_stream, sid="test")
# Mock the logger.info method
with patch("openhands.core.main.logger", mock_logger):
# Run the controller in headless mode
await run_controller(
config=config,
initial_user_action=action,
headless_mode=True,
exit_on_message=True, # Exit when agent asks for input
runtime=runtime
)
# Verify that logger.info was called with events
assert mock_logger.info.call_count > 0
# Verify that at least one call was with our test message action
found_message = False
for call in mock_logger.info.call_args_list:
args = call[0]
if isinstance(args[0], MessageAction) and args[0].content == message:
found_message = True
break
assert found_message, "Test message action was not logged"