mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
feat(backend): Support CreateMicroagent in the “Create New Conversation” API (#9765)
This commit is contained in:
parent
cb910e6863
commit
67edc66da7
@ -22,6 +22,7 @@ class TaskType(str, Enum):
|
||||
UNRESOLVED_COMMENTS = 'UNRESOLVED_COMMENTS'
|
||||
OPEN_ISSUE = 'OPEN_ISSUE'
|
||||
OPEN_PR = 'OPEN_PR'
|
||||
CREATE_MICROAGENT = 'CREATE_MICROAGENT'
|
||||
|
||||
|
||||
class OwnerType(str, Enum):
|
||||
@ -98,6 +99,12 @@ class SuggestedTask(BaseModel):
|
||||
return template.render(issue_number=issue_number, repo=repo, **terms)
|
||||
|
||||
|
||||
class CreateMicroagent(BaseModel):
|
||||
repo: str
|
||||
git_provider: ProviderType | None = None
|
||||
title: str | None = None
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: str
|
||||
login: str
|
||||
|
||||
@ -27,6 +27,7 @@ from openhands.integrations.provider import (
|
||||
)
|
||||
from openhands.integrations.service_types import (
|
||||
AuthenticationError,
|
||||
CreateMicroagent,
|
||||
ProviderType,
|
||||
SuggestedTask,
|
||||
)
|
||||
@ -84,6 +85,7 @@ class InitSessionRequest(BaseModel):
|
||||
image_urls: list[str] | None = None
|
||||
replay_json: str | None = None
|
||||
suggested_task: SuggestedTask | None = None
|
||||
create_microagent: CreateMicroagent | None = None
|
||||
conversation_instructions: str | None = None
|
||||
# Only nested runtimes require the ability to specify a conversation id, and it could be a security risk
|
||||
if os.getenv('ALLOW_SET_CONVERSATION_ID', '0') == '1':
|
||||
@ -123,6 +125,7 @@ async def new_conversation(
|
||||
image_urls = data.image_urls or []
|
||||
replay_json = data.replay_json
|
||||
suggested_task = data.suggested_task
|
||||
create_microagent = data.create_microagent
|
||||
git_provider = data.git_provider
|
||||
conversation_instructions = data.conversation_instructions
|
||||
|
||||
@ -131,6 +134,13 @@ async def new_conversation(
|
||||
if suggested_task:
|
||||
initial_user_msg = suggested_task.get_prompt_for_task()
|
||||
conversation_trigger = ConversationTrigger.SUGGESTED_TASK
|
||||
elif create_microagent:
|
||||
conversation_trigger = ConversationTrigger.MICROAGENT_MANAGEMENT
|
||||
# Set repository and git_provider from create_microagent if not already set
|
||||
if not repository and create_microagent.repo:
|
||||
repository = create_microagent.repo
|
||||
if not git_provider and create_microagent.git_provider:
|
||||
git_provider = create_microagent.git_provider
|
||||
|
||||
if auth_type == AuthType.BEARER:
|
||||
conversation_trigger = ConversationTrigger.REMOTE_API_KEY
|
||||
|
||||
@ -11,6 +11,7 @@ class ConversationTrigger(Enum):
|
||||
SUGGESTED_TASK = 'suggested_task'
|
||||
REMOTE_API_KEY = 'openhands_api'
|
||||
SLACK = 'slack'
|
||||
MICROAGENT_MANAGEMENT = 'microagent_management'
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@ -11,6 +11,7 @@ from fastapi.testclient import TestClient
|
||||
|
||||
from openhands.integrations.service_types import (
|
||||
AuthenticationError,
|
||||
CreateMicroagent,
|
||||
ProviderType,
|
||||
SuggestedTask,
|
||||
TaskType,
|
||||
@ -608,3 +609,174 @@ async def test_new_conversation_with_unsupported_params():
|
||||
# Verify that the error message mentions the unsupported parameter
|
||||
assert 'Extra inputs are not permitted' in str(excinfo.value)
|
||||
assert 'unsupported_param' in str(excinfo.value)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_new_conversation_with_create_microagent(provider_handler_mock):
|
||||
"""Test creating a new conversation with a CreateMicroagent object."""
|
||||
with _patch_store():
|
||||
# Mock the create_new_conversation function directly
|
||||
with patch(
|
||||
'openhands.server.routes.manage_conversations.create_new_conversation'
|
||||
) as mock_create_conversation:
|
||||
# Set up the mock to return a conversation ID
|
||||
mock_create_conversation.return_value = MagicMock(
|
||||
conversation_id='test_conversation_id',
|
||||
url='https://my-conversation.com',
|
||||
session_api_key=None,
|
||||
status=ConversationStatus.RUNNING,
|
||||
)
|
||||
|
||||
# Create the CreateMicroagent object
|
||||
create_microagent = CreateMicroagent(
|
||||
repo='test/repo',
|
||||
git_provider=ProviderType.GITHUB,
|
||||
title='Create a new microagent',
|
||||
)
|
||||
|
||||
test_request = InitSessionRequest(
|
||||
repository=None, # Not set in request, should be set from create_microagent
|
||||
selected_branch='main',
|
||||
initial_user_msg='Hello, agent!',
|
||||
create_microagent=create_microagent,
|
||||
)
|
||||
|
||||
# Call new_conversation
|
||||
response = await create_new_test_conversation(test_request)
|
||||
|
||||
# Verify the response
|
||||
assert isinstance(response, ConversationResponse)
|
||||
assert response.status == 'ok'
|
||||
assert response.conversation_id is not None
|
||||
assert isinstance(response.conversation_id, str)
|
||||
|
||||
# Verify that create_new_conversation was called with the correct arguments
|
||||
mock_create_conversation.assert_called_once()
|
||||
call_args = mock_create_conversation.call_args[1]
|
||||
assert call_args['user_id'] == 'test_user'
|
||||
assert (
|
||||
call_args['selected_repository'] == 'test/repo'
|
||||
) # Should be set from create_microagent
|
||||
assert call_args['selected_branch'] == 'main'
|
||||
assert call_args['initial_user_msg'] == 'Hello, agent!'
|
||||
assert (
|
||||
call_args['conversation_trigger']
|
||||
== ConversationTrigger.MICROAGENT_MANAGEMENT
|
||||
)
|
||||
assert (
|
||||
call_args['git_provider'] == ProviderType.GITHUB
|
||||
) # Should be set from create_microagent
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_new_conversation_with_create_microagent_repository_override(
|
||||
provider_handler_mock,
|
||||
):
|
||||
"""Test creating a new conversation with CreateMicroagent when repository is already set."""
|
||||
with _patch_store():
|
||||
# Mock the create_new_conversation function directly
|
||||
with patch(
|
||||
'openhands.server.routes.manage_conversations.create_new_conversation'
|
||||
) as mock_create_conversation:
|
||||
# Set up the mock to return a conversation ID
|
||||
mock_create_conversation.return_value = MagicMock(
|
||||
conversation_id='test_conversation_id',
|
||||
url='https://my-conversation.com',
|
||||
session_api_key=None,
|
||||
status=ConversationStatus.RUNNING,
|
||||
)
|
||||
|
||||
# Create the CreateMicroagent object
|
||||
create_microagent = CreateMicroagent(
|
||||
repo='microagent/repo',
|
||||
git_provider=ProviderType.GITLAB,
|
||||
title='Create a new microagent',
|
||||
)
|
||||
|
||||
test_request = InitSessionRequest(
|
||||
repository='existing/repo', # Already set in request
|
||||
selected_branch='main',
|
||||
initial_user_msg='Hello, agent!',
|
||||
create_microagent=create_microagent,
|
||||
)
|
||||
|
||||
# Call new_conversation
|
||||
response = await create_new_test_conversation(test_request)
|
||||
|
||||
# Verify the response
|
||||
assert isinstance(response, ConversationResponse)
|
||||
assert response.status == 'ok'
|
||||
assert response.conversation_id is not None
|
||||
assert isinstance(response.conversation_id, str)
|
||||
|
||||
# Verify that create_new_conversation was called with the correct arguments
|
||||
mock_create_conversation.assert_called_once()
|
||||
call_args = mock_create_conversation.call_args[1]
|
||||
assert call_args['user_id'] == 'test_user'
|
||||
assert (
|
||||
call_args['selected_repository'] == 'existing/repo'
|
||||
) # Should keep existing value
|
||||
assert call_args['selected_branch'] == 'main'
|
||||
assert call_args['initial_user_msg'] == 'Hello, agent!'
|
||||
assert (
|
||||
call_args['conversation_trigger']
|
||||
== ConversationTrigger.MICROAGENT_MANAGEMENT
|
||||
)
|
||||
assert (
|
||||
call_args['git_provider'] == ProviderType.GITLAB
|
||||
) # Should be set from create_microagent
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_new_conversation_with_create_microagent_minimal(provider_handler_mock):
|
||||
"""Test creating a new conversation with minimal CreateMicroagent object (only repo field)."""
|
||||
with _patch_store():
|
||||
# Mock the create_new_conversation function directly
|
||||
with patch(
|
||||
'openhands.server.routes.manage_conversations.create_new_conversation'
|
||||
) as mock_create_conversation:
|
||||
# Set up the mock to return a conversation ID
|
||||
mock_create_conversation.return_value = MagicMock(
|
||||
conversation_id='test_conversation_id',
|
||||
url='https://my-conversation.com',
|
||||
session_api_key=None,
|
||||
status=ConversationStatus.RUNNING,
|
||||
)
|
||||
|
||||
# Create the CreateMicroagent object with only required field
|
||||
create_microagent = CreateMicroagent(
|
||||
repo='minimal/repo',
|
||||
)
|
||||
|
||||
test_request = InitSessionRequest(
|
||||
repository=None,
|
||||
selected_branch='main',
|
||||
initial_user_msg='Hello, agent!',
|
||||
create_microagent=create_microagent,
|
||||
)
|
||||
|
||||
# Call new_conversation
|
||||
response = await create_new_test_conversation(test_request)
|
||||
|
||||
# Verify the response
|
||||
assert isinstance(response, ConversationResponse)
|
||||
assert response.status == 'ok'
|
||||
assert response.conversation_id is not None
|
||||
assert isinstance(response.conversation_id, str)
|
||||
|
||||
# Verify that create_new_conversation was called with the correct arguments
|
||||
mock_create_conversation.assert_called_once()
|
||||
call_args = mock_create_conversation.call_args[1]
|
||||
assert call_args['user_id'] == 'test_user'
|
||||
assert (
|
||||
call_args['selected_repository'] == 'minimal/repo'
|
||||
) # Should be set from create_microagent
|
||||
assert call_args['selected_branch'] == 'main'
|
||||
assert call_args['initial_user_msg'] == 'Hello, agent!'
|
||||
assert (
|
||||
call_args['conversation_trigger']
|
||||
== ConversationTrigger.MICROAGENT_MANAGEMENT
|
||||
)
|
||||
assert (
|
||||
call_args['git_provider'] is None
|
||||
) # Should remain None since not set in create_microagent
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user