mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-08 22:38:05 -05:00
CLI: bump agent-sdk (#11710)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import uuid
|
||||
|
||||
from openhands.sdk.conversation import visualizer
|
||||
from openhands.sdk.security.llm_analyzer import LLMSecurityAnalyzer
|
||||
from prompt_toolkit import HTML, print_formatted_text
|
||||
|
||||
from openhands.sdk import Agent, BaseConversation, Conversation, Workspace
|
||||
@@ -74,11 +75,7 @@ def setup_conversation(
|
||||
|
||||
agent = load_agent_specs(str(conversation_id))
|
||||
|
||||
if not include_security_analyzer:
|
||||
# Remove security analyzer from agent spec
|
||||
agent = agent.model_copy(
|
||||
update={"security_analyzer": None}
|
||||
)
|
||||
|
||||
|
||||
# Create conversation - agent context is now set in AgentStore.load()
|
||||
conversation: BaseConversation = Conversation(
|
||||
@@ -90,7 +87,11 @@ def setup_conversation(
|
||||
visualizer=CLIVisualizer
|
||||
)
|
||||
|
||||
if include_security_analyzer:
|
||||
# Security analyzer is set though conversation API now
|
||||
if not include_security_analyzer:
|
||||
conversation.set_security_analyzer(None)
|
||||
else:
|
||||
conversation.set_security_analyzer(LLMSecurityAnalyzer())
|
||||
conversation.set_confirmation_policy(AlwaysConfirm())
|
||||
|
||||
print_formatted_text(
|
||||
|
||||
@@ -38,6 +38,16 @@ class AgentStore:
|
||||
str_spec = self.file_store.read(AGENT_SETTINGS_PATH)
|
||||
agent = Agent.model_validate_json(str_spec)
|
||||
|
||||
|
||||
# Temporary to remove security analyzer from agent specs
|
||||
# Security analyzer is set via conversation API now
|
||||
# Doing this so that deprecation warning is thrown only the first time running CLI
|
||||
if agent.security_analyzer:
|
||||
agent = agent.model_copy(
|
||||
update={"security_analyzer": None}
|
||||
)
|
||||
self.save(agent)
|
||||
|
||||
# Update tools with most recent working directory
|
||||
updated_tools = get_default_tools(enable_browser=False)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import os
|
||||
from typing import Any
|
||||
from openhands.sdk.security.llm_analyzer import LLMSecurityAnalyzer
|
||||
from openhands.tools.preset import get_default_agent
|
||||
from openhands.sdk import LLM
|
||||
|
||||
@@ -67,10 +66,4 @@ def get_default_cli_agent(
|
||||
cli_mode=True
|
||||
)
|
||||
|
||||
agent = agent.model_copy(
|
||||
update={
|
||||
'security_analyzer': LLMSecurityAnalyzer()
|
||||
}
|
||||
)
|
||||
|
||||
return agent
|
||||
|
||||
@@ -18,8 +18,8 @@ classifiers = [
|
||||
# Using Git URLs for dependencies so installs from PyPI pull from GitHub
|
||||
# TODO: pin package versions once agent-sdk has published PyPI packages
|
||||
dependencies = [
|
||||
"openhands-sdk==1",
|
||||
"openhands-tools==1",
|
||||
"openhands-sdk==1.1",
|
||||
"openhands-tools==1.1",
|
||||
"prompt-toolkit>=3",
|
||||
"typer>=0.17.4",
|
||||
]
|
||||
@@ -102,5 +102,5 @@ ignore_missing_imports = true
|
||||
# UNCOMMENT TO USE EXACT COMMIT FROM AGENT-SDK
|
||||
|
||||
# [tool.uv.sources]
|
||||
# openhands-sdk = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-sdk", rev = "aaa0066ee078688e015fcad590393fe6771c10a1" }
|
||||
# openhands-tools = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-tools", rev = "aaa0066ee078688e015fcad590393fe6771c10a1" }
|
||||
# openhands-sdk = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-sdk", rev = "7b695dc519084e75c482b34473e714845d6cef92" }
|
||||
# openhands-tools = { git = "https://github.com/OpenHands/agent-sdk.git", subdirectory = "openhands-tools", rev = "7b695dc519084e75c482b34473e714845d6cef92" }
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
"""Test that first-time settings screen usage creates a default agent with security analyzer."""
|
||||
"""Test that first-time settings screen usage creates a default agent and conversation with security analyzer."""
|
||||
|
||||
from unittest.mock import patch
|
||||
import pytest
|
||||
from openhands_cli.tui.settings.settings_screen import SettingsScreen
|
||||
from openhands_cli.user_actions.settings_action import SettingsType
|
||||
from openhands.sdk import LLM
|
||||
from openhands.sdk import LLM, Conversation, Workspace
|
||||
from openhands.sdk.security.llm_analyzer import LLMSecurityAnalyzer
|
||||
from pydantic import SecretStr
|
||||
|
||||
|
||||
def test_first_time_settings_creates_default_agent_with_security_analyzer():
|
||||
"""Test that using the settings screen for the first time creates a default agent with a non-None security analyzer."""
|
||||
def test_first_time_settings_creates_default_agent_and_conversation_with_security_analyzer():
|
||||
"""Test that using the settings screen for the first time creates a default agent and conversation with security analyzer."""
|
||||
|
||||
# Create a settings screen instance (no conversation initially)
|
||||
screen = SettingsScreen(conversation=None)
|
||||
@@ -50,17 +51,20 @@ def test_first_time_settings_creates_default_agent_with_security_analyzer():
|
||||
assert saved_agent.llm.model == 'openai/gpt-4o-mini', f"Expected model 'openai/gpt-4o-mini', got '{saved_agent.llm.model}'"
|
||||
assert saved_agent.llm.api_key.get_secret_value() == 'sk-test-key-123', "API key should match the provided value"
|
||||
|
||||
# Verify that the agent has a security analyzer and it's not None
|
||||
assert hasattr(saved_agent, 'security_analyzer'), "Agent should have a security_analyzer attribute"
|
||||
assert saved_agent.security_analyzer is not None, "Security analyzer should not be None"
|
||||
# Test that a conversation can be created with the agent and security analyzer can be set
|
||||
conversation = Conversation(agent=saved_agent, workspace=Workspace(working_dir='/tmp'))
|
||||
|
||||
# Verify the security analyzer has the expected type/kind
|
||||
assert hasattr(saved_agent.security_analyzer, 'kind'), "Security analyzer should have a 'kind' attribute"
|
||||
assert saved_agent.security_analyzer.kind == 'LLMSecurityAnalyzer', f"Expected security analyzer kind 'LLMSecurityAnalyzer', got '{saved_agent.security_analyzer.kind}'"
|
||||
# Set security analyzer using the new API
|
||||
security_analyzer = LLMSecurityAnalyzer()
|
||||
conversation.set_security_analyzer(security_analyzer)
|
||||
|
||||
# Verify that the security analyzer was set correctly
|
||||
assert conversation.state.security_analyzer is not None, "Conversation should have a security analyzer"
|
||||
assert conversation.state.security_analyzer.kind == 'LLMSecurityAnalyzer', f"Expected security analyzer kind 'LLMSecurityAnalyzer', got '{conversation.state.security_analyzer.kind}'"
|
||||
|
||||
|
||||
def test_first_time_settings_with_advanced_configuration():
|
||||
"""Test that advanced settings also create a default agent with security analyzer."""
|
||||
"""Test that advanced settings also create a default agent and conversation with security analyzer."""
|
||||
|
||||
screen = SettingsScreen(conversation=None)
|
||||
|
||||
@@ -94,11 +98,20 @@ def test_first_time_settings_with_advanced_configuration():
|
||||
|
||||
saved_agent = screen.agent_store.load()
|
||||
|
||||
# Verify agent creation and security analyzer
|
||||
# Verify agent creation
|
||||
assert saved_agent is not None, "Agent should be created with advanced settings"
|
||||
assert saved_agent.security_analyzer is not None, "Security analyzer should not be None in advanced settings"
|
||||
assert saved_agent.security_analyzer.kind == 'LLMSecurityAnalyzer', "Security analyzer should be LLMSecurityAnalyzer"
|
||||
|
||||
# Verify advanced settings were applied
|
||||
assert saved_agent.llm.model == 'anthropic/claude-3-5-sonnet', "Custom model should be set"
|
||||
assert saved_agent.llm.base_url == 'https://api.anthropic.com', "Base URL should be set"
|
||||
assert saved_agent.llm.base_url == 'https://api.anthropic.com', "Base URL should be set"
|
||||
|
||||
# Test that a conversation can be created with the agent and security analyzer can be set
|
||||
conversation = Conversation(agent=saved_agent, workspace=Workspace(working_dir='/tmp'))
|
||||
|
||||
# Set security analyzer using the new API
|
||||
security_analyzer = LLMSecurityAnalyzer()
|
||||
conversation.set_security_analyzer(security_analyzer)
|
||||
|
||||
# Verify that the security analyzer was set correctly
|
||||
assert conversation.state.security_analyzer is not None, "Conversation should have a security analyzer"
|
||||
assert conversation.state.security_analyzer.kind == 'LLMSecurityAnalyzer', "Security analyzer should be LLMSecurityAnalyzer"
|
||||
@@ -108,15 +108,15 @@ class TestConversationRunner:
|
||||
3. If not paused, we should still ask for confirmation on actions
|
||||
4. If deferred no run call to agent should be made
|
||||
5. If accepted, run call to agent should be made
|
||||
|
||||
"""
|
||||
if final_status == ConversationExecutionStatus.FINISHED:
|
||||
agent.finish_on_step = 1
|
||||
|
||||
# Add a mock security analyzer to enable confirmation mode
|
||||
agent.security_analyzer = MagicMock()
|
||||
|
||||
convo = Conversation(agent)
|
||||
|
||||
# Set security analyzer using the new API to enable confirmation mode
|
||||
convo.set_security_analyzer(MagicMock())
|
||||
|
||||
convo.state.execution_status = (
|
||||
ConversationExecutionStatus.WAITING_FOR_CONFIRMATION
|
||||
)
|
||||
@@ -127,6 +127,7 @@ class TestConversationRunner:
|
||||
cr, '_handle_confirmation_request', return_value=confirmation
|
||||
) as mock_confirmation_request:
|
||||
cr.process_message(message=None)
|
||||
|
||||
mock_confirmation_request.assert_called_once()
|
||||
assert agent.step_count == expected_run_calls
|
||||
assert convo.state.execution_status == final_status
|
||||
|
||||
16
openhands-cli/uv.lock
generated
16
openhands-cli/uv.lock
generated
@@ -1929,8 +1929,8 @@ dev = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "openhands-sdk", specifier = "==1" },
|
||||
{ name = "openhands-tools", specifier = "==1" },
|
||||
{ name = "openhands-sdk", specifier = "==1.1.0" },
|
||||
{ name = "openhands-tools", specifier = "==1.1.0" },
|
||||
{ name = "prompt-toolkit", specifier = ">=3" },
|
||||
{ name = "typer", specifier = ">=0.17.4" },
|
||||
]
|
||||
@@ -1953,7 +1953,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "openhands-sdk"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "fastmcp" },
|
||||
@@ -1966,14 +1966,14 @@ dependencies = [
|
||||
{ name = "tenacity" },
|
||||
{ name = "websockets" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/56/58/d6117840a14d013176a7a490a74295dffac64b44dc098532d4e8526c9a87/openhands_sdk-1.0.0.tar.gz", hash = "sha256:7c3a0d77d48d7eceaa77fda90ac654697ce916431b5c905d10d9ab6c07609a1a", size = 160726, upload-time = "2025-11-06T17:05:44.545Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/90/b2/97d9deb743b266683f3e70cebaa1d34ee247c019f7d6e42c2f5de529cb47/openhands_sdk-1.1.0.tar.gz", hash = "sha256:855e0d8f3657205e4119e50520c17e65b3358b1a923f7a051a82512a54bf426c", size = 166636, upload-time = "2025-11-11T19:07:04.249Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/29/9b/4d4c356ed50e6ad87e6dc8f87af1966c51c55a22955cebd632bf62040e5b/openhands_sdk-1.0.0-py3-none-any.whl", hash = "sha256:73916e22783e2c8500f19765fa340631a0e47ae9a3c5e40fb8411ecab4a1f49a", size = 214807, upload-time = "2025-11-06T17:05:43.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/9f/a97a10447f3be53df4639e43748c4178853e958df07ba74890f4968829d6/openhands_sdk-1.1.0-py3-none-any.whl", hash = "sha256:4a984ce1687a48cf99a67fdf3d37b116f8b2840743d4807810b5024af6a1d57e", size = 221594, upload-time = "2025-11-11T19:07:02.847Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openhands-tools"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "bashlex" },
|
||||
@@ -1985,9 +1985,9 @@ dependencies = [
|
||||
{ name = "openhands-sdk" },
|
||||
{ name = "pydantic" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/45/49/3bad4d8283c76f72dacfde8fece9d1190774c87c40a011075868e8d18cbf/openhands_tools-1.0.0.tar.gz", hash = "sha256:f6bc8647149d541730520f1aeb409cd9eac96d796d19e39a40f300dcd2b0284c", size = 61997, upload-time = "2025-11-06T17:05:46.455Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d3/89/e2c5fc2d9e8dc6840ef2891ff6f76b9769b50a4c508fd3a626c1ab476fb1/openhands_tools-1.1.0.tar.gz", hash = "sha256:c2fadaa4f4e16e9a3df5781ea847565dcae7171584f09ef7c0e1d97c8dfc83f6", size = 62818, upload-time = "2025-11-11T19:07:06.527Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/15/23c5650a9470f9c125288508bf966e6b2ece479f5407801aa7fdda2ba5a0/openhands_tools-1.0.0-py3-none-any.whl", hash = "sha256:21a4ff3f37a3c71edd17b861fe1a9b86cc744ad9dc8a3626898ecdeeea7ae30f", size = 84232, upload-time = "2025-11-06T17:05:45.527Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/a3/e58d75b7bd8d5dfbe063fcfaaadbdfd24fd511d633a528cefd29f0e01056/openhands_tools-1.1.0-py3-none-any.whl", hash = "sha256:767d6746f05edade49263aa24450a037485a3dc23379f56917ef19aad22033f9", size = 85062, upload-time = "2025-11-11T19:07:05.315Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user