mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 05:37:20 +08:00
refactor: use AgentSettings.create_agent() for V1 agent creation
Replace manual Agent construction with settings.create_agent() when SDK AgentSettings are available. This makes settings the single source of truth for LLM, tools, condenser, critic, and agent context. Key changes: - _get_default_critic() eliminated — replaced by _apply_critic_proxy() which sets critic_server_url/critic_model_name on the settings, then lets the SDK's build_critic() do the construction. - _create_agent_with_context() settings path: populate tools + agent_context on settings, call create_agent(), apply runtime-only overrides (mcp_config, system_prompt) via model_copy. - Legacy path (no agent_settings) kept for backward compat. - Tests updated: agent_context now in Agent() constructor, mcp_config/system_prompt in model_copy. Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -884,25 +884,31 @@ class TestLiveStatusAppConversationService:
|
||||
working_dir=working_dir,
|
||||
)
|
||||
|
||||
# Assert
|
||||
# Assert — Agent() receives llm, tools, condenser, agent_context
|
||||
mock_get_tools.assert_called_once_with(
|
||||
plan_path='/workspace/project/.agents_tmp/PLAN.md'
|
||||
)
|
||||
mock_agent_class.assert_called_once()
|
||||
call_kwargs = mock_agent_class.call_args[1]
|
||||
assert call_kwargs['llm'] == mock_llm
|
||||
assert call_kwargs['system_prompt_filename'] == 'system_prompt_planning.j2'
|
||||
assert (
|
||||
call_kwargs['system_prompt_kwargs']['plan_structure']
|
||||
== 'test_plan_structure'
|
||||
)
|
||||
assert call_kwargs['mcp_config'] == mcp_config
|
||||
assert call_kwargs['security_analyzer'] is None
|
||||
assert call_kwargs['condenser'] == mock_condenser
|
||||
assert call_kwargs['agent_context'].system_message_suffix.startswith(
|
||||
PLANNING_AGENT_INSTRUCTION
|
||||
)
|
||||
mock_create_condenser.assert_called_once_with(
|
||||
mock_llm, AgentType.PLAN, self.mock_user.condenser_max_size
|
||||
)
|
||||
|
||||
# Runtime overrides applied via model_copy
|
||||
copy_kwargs = mock_agent_instance.model_copy.call_args[1]['update']
|
||||
assert copy_kwargs['mcp_config'] == mcp_config
|
||||
assert copy_kwargs['system_prompt_filename'] == 'system_prompt_planning.j2'
|
||||
assert (
|
||||
copy_kwargs['system_prompt_kwargs']['plan_structure']
|
||||
== 'test_plan_structure'
|
||||
)
|
||||
assert copy_kwargs['security_analyzer'] is None
|
||||
|
||||
@patch(
|
||||
'openhands.app_server.app_conversation.live_status_app_conversation_service.get_default_tools'
|
||||
)
|
||||
@@ -937,18 +943,21 @@ class TestLiveStatusAppConversationService:
|
||||
self.mock_user.condenser_max_size,
|
||||
)
|
||||
|
||||
# Assert
|
||||
# Assert — Agent() receives core fields
|
||||
mock_agent_class.assert_called_once()
|
||||
call_kwargs = mock_agent_class.call_args[1]
|
||||
assert call_kwargs['llm'] == mock_llm
|
||||
assert call_kwargs['system_prompt_kwargs']['cli_mode'] is False
|
||||
assert call_kwargs['mcp_config'] == mcp_config
|
||||
assert call_kwargs['condenser'] == mock_condenser
|
||||
mock_get_tools.assert_called_once_with(enable_browser=True)
|
||||
mock_create_condenser.assert_called_once_with(
|
||||
mock_llm, AgentType.DEFAULT, self.mock_user.condenser_max_size
|
||||
)
|
||||
|
||||
# Runtime overrides applied via model_copy
|
||||
copy_kwargs = mock_agent_instance.model_copy.call_args[1]['update']
|
||||
assert copy_kwargs['system_prompt_kwargs']['cli_mode'] is False
|
||||
assert copy_kwargs['mcp_config'] == mcp_config
|
||||
|
||||
@patch(
|
||||
'openhands.app_server.app_conversation.live_status_app_conversation_service.get_default_tools',
|
||||
return_value=[],
|
||||
@@ -992,6 +1001,8 @@ class TestLiveStatusAppConversationService:
|
||||
assert agent.condenser is None
|
||||
assert isinstance(agent.critic, APIBasedCritic)
|
||||
assert agent.critic.mode == 'all_actions'
|
||||
assert agent.critic.server_url == 'https://llm-proxy.app.all-hands.dev/vllm'
|
||||
assert agent.critic.model_name == 'critic'
|
||||
assert agent.critic.iterative_refinement is not None
|
||||
assert agent.critic.iterative_refinement.success_threshold == 0.75
|
||||
assert agent.critic.iterative_refinement.max_iterations == 2
|
||||
@@ -1034,10 +1045,12 @@ class TestLiveStatusAppConversationService:
|
||||
self.mock_user.condenser_max_size,
|
||||
)
|
||||
|
||||
# Assert - verify model_copy was called with agent_context containing planning instruction
|
||||
model_copy_call = mock_agent_instance.model_copy.call_args
|
||||
agent_context = model_copy_call[1]['update']['agent_context']
|
||||
assert agent_context.system_message_suffix == PLANNING_AGENT_INSTRUCTION
|
||||
# Assert — agent_context goes to Agent() constructor
|
||||
call_kwargs = mock_agent_class.call_args[1]
|
||||
assert (
|
||||
call_kwargs['agent_context'].system_message_suffix
|
||||
== PLANNING_AGENT_INSTRUCTION
|
||||
)
|
||||
|
||||
@patch(
|
||||
'openhands.app_server.app_conversation.live_status_app_conversation_service.get_planning_tools'
|
||||
@@ -1078,13 +1091,11 @@ class TestLiveStatusAppConversationService:
|
||||
self.mock_user.condenser_max_size,
|
||||
)
|
||||
|
||||
# Assert - verify planning instruction is prepended to existing suffix
|
||||
model_copy_call = mock_agent_instance.model_copy.call_args
|
||||
agent_context = model_copy_call[1]['update']['agent_context']
|
||||
assert agent_context.system_message_suffix.startswith(
|
||||
PLANNING_AGENT_INSTRUCTION
|
||||
)
|
||||
assert existing_suffix in agent_context.system_message_suffix
|
||||
# Assert — agent_context goes to Agent() constructor
|
||||
call_kwargs = mock_agent_class.call_args[1]
|
||||
suffix = call_kwargs['agent_context'].system_message_suffix
|
||||
assert suffix.startswith(PLANNING_AGENT_INSTRUCTION)
|
||||
assert existing_suffix in suffix
|
||||
|
||||
@patch(
|
||||
'openhands.app_server.app_conversation.live_status_app_conversation_service.get_default_tools'
|
||||
@@ -1120,10 +1131,9 @@ class TestLiveStatusAppConversationService:
|
||||
self.mock_user.condenser_max_size,
|
||||
)
|
||||
|
||||
# Assert - verify no planning instruction for default agent
|
||||
model_copy_call = mock_agent_instance.model_copy.call_args
|
||||
agent_context = model_copy_call[1]['update']['agent_context']
|
||||
assert agent_context.system_message_suffix is None
|
||||
# Assert — agent_context goes to Agent() constructor
|
||||
call_kwargs = mock_agent_class.call_args[1]
|
||||
assert call_kwargs['agent_context'].system_message_suffix is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_finalize_conversation_request_with_skills(self):
|
||||
|
||||
Reference in New Issue
Block a user