mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 13:47:19 +08:00
Fix issue #4819: [Bug]: Logs are not printed out in headless command line mode
This commit is contained in:
@@ -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
2
poetry.lock
generated
@@ -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"
|
||||
|
||||
@@ -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 = "*"
|
||||
|
||||
107
tests/unit/test_headless_logging.py
Normal file
107
tests/unit/test_headless_logging.py
Normal 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"
|
||||
Reference in New Issue
Block a user