mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 13:52:43 +08:00
Add POST /api/conversations/{id}/message endpoint (#11177)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
37daf068c5
commit
df4d30addf
@ -3,6 +3,7 @@ from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.events.action.message import MessageAction
|
||||
from openhands.events.event_filter import EventFilter
|
||||
from openhands.events.event_store import EventStore
|
||||
from openhands.events.serialization.event import event_to_dict
|
||||
@ -177,6 +178,53 @@ async def add_event(
|
||||
return JSONResponse({'success': True})
|
||||
|
||||
|
||||
class AddMessageRequest(BaseModel):
|
||||
"""Request model for adding a message to a conversation."""
|
||||
|
||||
message: str
|
||||
|
||||
|
||||
@app.post('/message')
|
||||
async def add_message(
|
||||
data: AddMessageRequest,
|
||||
conversation: ServerConversation = Depends(get_conversation),
|
||||
):
|
||||
"""Add a message to an existing conversation.
|
||||
|
||||
This endpoint allows adding a user message to an existing conversation.
|
||||
The message will be processed by the agent in the conversation.
|
||||
|
||||
Args:
|
||||
data: The request data containing the message text
|
||||
conversation: The conversation to add the message to (injected by dependency)
|
||||
|
||||
Returns:
|
||||
JSONResponse: A JSON response indicating the success of the operation
|
||||
"""
|
||||
try:
|
||||
# Create a MessageAction from the provided message text
|
||||
message_action = MessageAction(content=data.message)
|
||||
|
||||
# Convert the action to a dictionary for sending to the conversation
|
||||
message_data = event_to_dict(message_action)
|
||||
|
||||
# Send the message to the conversation
|
||||
await conversation_manager.send_event_to_conversation(
|
||||
conversation.sid, message_data
|
||||
)
|
||||
|
||||
return JSONResponse({'success': True})
|
||||
except Exception as e:
|
||||
logger.error(f'Error adding message to conversation: {e}')
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
content={
|
||||
'success': False,
|
||||
'error': f'Error adding message to conversation: {e}',
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class MicroagentResponse(BaseModel):
|
||||
"""Response model for microagents endpoint."""
|
||||
|
||||
|
||||
@ -8,7 +8,11 @@ from fastapi.responses import JSONResponse
|
||||
|
||||
from openhands.microagent.microagent import KnowledgeMicroagent, RepoMicroagent
|
||||
from openhands.microagent.types import MicroagentMetadata, MicroagentType
|
||||
from openhands.server.routes.conversation import get_microagents
|
||||
from openhands.server.routes.conversation import (
|
||||
AddMessageRequest,
|
||||
add_message,
|
||||
get_microagents,
|
||||
)
|
||||
from openhands.server.routes.manage_conversations import (
|
||||
UpdateConversationRequest,
|
||||
update_conversation,
|
||||
@ -619,3 +623,112 @@ async def test_update_conversation_no_user_id_no_metadata_user_id():
|
||||
# Verify success (should work when both are None)
|
||||
assert result is True
|
||||
mock_conversation_store.save_metadata.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_message_success():
|
||||
"""Test successful message addition to conversation."""
|
||||
# Create a mock ServerConversation
|
||||
mock_conversation = MagicMock(spec=ServerConversation)
|
||||
mock_conversation.sid = 'test_conversation_123'
|
||||
|
||||
# Create message request
|
||||
message_request = AddMessageRequest(message='Hello, this is a test message!')
|
||||
|
||||
# Mock the conversation manager
|
||||
with patch(
|
||||
'openhands.server.routes.conversation.conversation_manager'
|
||||
) as mock_manager:
|
||||
mock_manager.send_event_to_conversation = AsyncMock()
|
||||
|
||||
# Call the function directly
|
||||
response = await add_message(
|
||||
data=message_request, conversation=mock_conversation
|
||||
)
|
||||
|
||||
# Verify the response
|
||||
assert isinstance(response, JSONResponse)
|
||||
assert response.status_code == 200
|
||||
|
||||
# Parse the JSON content
|
||||
content = json.loads(response.body)
|
||||
assert content['success'] is True
|
||||
|
||||
# Verify that send_event_to_conversation was called
|
||||
mock_manager.send_event_to_conversation.assert_called_once()
|
||||
call_args = mock_manager.send_event_to_conversation.call_args
|
||||
assert call_args[0][0] == 'test_conversation_123' # conversation ID
|
||||
|
||||
# Verify the message data structure
|
||||
message_data = call_args[0][1]
|
||||
assert message_data['action'] == 'message'
|
||||
assert message_data['args']['content'] == 'Hello, this is a test message!'
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_message_conversation_manager_error():
|
||||
"""Test add_message when conversation manager raises an exception."""
|
||||
# Create a mock ServerConversation
|
||||
mock_conversation = MagicMock(spec=ServerConversation)
|
||||
mock_conversation.sid = 'test_conversation_123'
|
||||
|
||||
# Create message request
|
||||
message_request = AddMessageRequest(message='Test message')
|
||||
|
||||
# Mock the conversation manager to raise an exception
|
||||
with patch(
|
||||
'openhands.server.routes.conversation.conversation_manager'
|
||||
) as mock_manager:
|
||||
mock_manager.send_event_to_conversation = AsyncMock(
|
||||
side_effect=Exception('Conversation manager error')
|
||||
)
|
||||
|
||||
# Call the function directly
|
||||
response = await add_message(
|
||||
data=message_request, conversation=mock_conversation
|
||||
)
|
||||
|
||||
# Verify the response
|
||||
assert isinstance(response, JSONResponse)
|
||||
assert response.status_code == 500
|
||||
|
||||
# Parse the JSON content
|
||||
content = json.loads(response.body)
|
||||
assert content['success'] is False
|
||||
assert 'Conversation manager error' in content['error']
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_message_empty_message():
|
||||
"""Test add_message with an empty message."""
|
||||
# Create a mock ServerConversation
|
||||
mock_conversation = MagicMock(spec=ServerConversation)
|
||||
mock_conversation.sid = 'test_conversation_123'
|
||||
|
||||
# Create message request with empty message
|
||||
message_request = AddMessageRequest(message='')
|
||||
|
||||
# Mock the conversation manager
|
||||
with patch(
|
||||
'openhands.server.routes.conversation.conversation_manager'
|
||||
) as mock_manager:
|
||||
mock_manager.send_event_to_conversation = AsyncMock()
|
||||
|
||||
# Call the function directly
|
||||
response = await add_message(
|
||||
data=message_request, conversation=mock_conversation
|
||||
)
|
||||
|
||||
# Verify the response
|
||||
assert isinstance(response, JSONResponse)
|
||||
assert response.status_code == 200
|
||||
|
||||
# Parse the JSON content
|
||||
content = json.loads(response.body)
|
||||
assert content['success'] is True
|
||||
|
||||
# Verify that send_event_to_conversation was called with empty content
|
||||
mock_manager.send_event_to_conversation.assert_called_once()
|
||||
call_args = mock_manager.send_event_to_conversation.call_args
|
||||
message_data = call_args[0][1]
|
||||
assert message_data['args']['content'] == ''
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user