Skip to content

Commit ae5b4bb

Browse files
committed
Merge branch 'main' into kevin
2 parents 6f405ed + a18e0db commit ae5b4bb

37 files changed

Lines changed: 428 additions & 327 deletions

config.template.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ llm_config = 'gpt3'
319319
# Additional Docker runtime kwargs
320320
#docker_runtime_kwargs = {}
321321

322+
# Specific port to use for VSCode. If not set, a random port will be chosen.
323+
# Useful when deploying OpenHands in a remote machine where you need to expose a specific port.
324+
#vscode_port = 41234
325+
322326
#################################### Security ###################################
323327
# Configuration for security features
324328
##############################################################################

docs/modules/usage/troubleshooting/troubleshooting.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,38 @@
44
OpenHands only supports Windows via WSL. Please be sure to run all commands inside your WSL terminal.
55
:::
66

7+
### Unable to access VS Code tab via local IP
8+
9+
**Description**
10+
11+
When accessing OpenHands through a non-localhost URL (such as a LAN IP address), the VS Code tab shows a "Forbidden" error, while other parts of the UI work fine.
12+
13+
**Resolution**
14+
15+
This happens because VS Code runs on a random high port that may not be exposed or accessible from other machines. To fix this:
16+
17+
1. Set a specific port for VS Code using the `SANDBOX_VSCODE_PORT` environment variable:
18+
```bash
19+
docker run -it --rm \
20+
-e SANDBOX_VSCODE_PORT=41234 \
21+
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:latest \
22+
-v /var/run/docker.sock:/var/run/docker.sock \
23+
-v ~/.openhands-state:/.openhands-state \
24+
-p 3000:3000 \
25+
-p 41234:41234 \
26+
--add-host host.docker.internal:host-gateway \
27+
--name openhands-app \
28+
docker.all-hands.dev/all-hands-ai/openhands:latest
29+
```
30+
31+
2. Make sure to expose the same port with `-p 41234:41234` in your Docker command.
32+
33+
3. Alternatively, you can set this in your `config.toml` file:
34+
```toml
35+
[sandbox]
36+
vscode_port = 41234
37+
```
38+
739
### Launch docker client failed
840

941
**Description**

openhands/agenthub/codeact_agent/codeact_agent.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import copy
22
import os
33
from collections import deque
4+
from typing import TYPE_CHECKING
45

5-
from litellm import ChatCompletionToolParam
6+
if TYPE_CHECKING:
7+
from litellm import ChatCompletionToolParam
8+
from openhands.events.action import Action
9+
from openhands.llm.llm import ModelResponse
610

711
import openhands.agenthub.codeact_agent.function_calling as codeact_function_calling
812
from openhands.agenthub.codeact_agent.tools.bash import create_cmd_run_tool
@@ -20,7 +24,7 @@
2024
from openhands.core.config import AgentConfig
2125
from openhands.core.logger import openhands_logger as logger
2226
from openhands.core.message import Message
23-
from openhands.events.action import Action, AgentFinishAction, MessageAction
27+
from openhands.events.action import AgentFinishAction, MessageAction
2428
from openhands.events.event import Event
2529
from openhands.llm.llm import LLM
2630
from openhands.memory.condenser import Condenser
@@ -75,23 +79,26 @@ def __init__(
7579
- config (AgentConfig): The configuration for this agent
7680
"""
7781
super().__init__(llm, config)
78-
self.pending_actions: deque[Action] = deque()
82+
self.pending_actions: deque['Action'] = deque()
7983
self.reset()
8084
self.tools = self._get_tools()
8185

82-
self.prompt_manager = PromptManager(
83-
prompt_dir=os.path.join(os.path.dirname(__file__), 'prompts'),
84-
)
85-
8686
# Create a ConversationMemory instance
8787
self.conversation_memory = ConversationMemory(self.config, self.prompt_manager)
8888

8989
self.condenser = Condenser.from_config(self.config.condenser)
9090
logger.debug(f'Using condenser: {type(self.condenser)}')
9191

92-
self.response_to_actions_fn = codeact_function_calling.response_to_actions
92+
@property
93+
def prompt_manager(self) -> PromptManager:
94+
if self._prompt_manager is None:
95+
self._prompt_manager = PromptManager(
96+
prompt_dir=os.path.join(os.path.dirname(__file__), 'prompts'),
97+
)
9398

94-
def _get_tools(self) -> list[ChatCompletionToolParam]:
99+
return self._prompt_manager
100+
101+
def _get_tools(self) -> list['ChatCompletionToolParam']:
95102
# For these models, we use short tool descriptions ( < 1024 tokens)
96103
# to avoid hitting the OpenAI token limit for tool descriptions.
97104
SHORT_TOOL_DESCRIPTION_LLM_SUBSTRS = ['gpt-', 'o3', 'o1', 'o4']
@@ -130,7 +137,7 @@ def reset(self) -> None:
130137
super().reset()
131138
self.pending_actions.clear()
132139

133-
def step(self, state: State) -> Action:
140+
def step(self, state: State) -> 'Action':
134141
"""Performs one step using the CodeAct Agent.
135142
136143
This includes gathering info on previous steps and prompting the model to make a command to execute.
@@ -198,9 +205,7 @@ def step(self, state: State) -> Action:
198205
params['extra_body'] = {'metadata': state.to_llm_metadata(agent_name=self.name)}
199206
response = self.llm.completion(**params)
200207
logger.debug(f'Response from LLM: {response}')
201-
actions = self.response_to_actions_fn(
202-
response, mcp_tool_names=list(self.mcp_tools.keys())
203-
)
208+
actions = self.response_to_actions(response)
204209
logger.debug(f'Actions after response_to_actions: {actions}')
205210
for action in actions:
206211
self.pending_actions.append(action)
@@ -273,4 +278,9 @@ def _get_messages(
273278
if self.llm.is_caching_prompt_active():
274279
self.conversation_memory.apply_prompt_caching(messages)
275280

276-
return messages
281+
return messages
282+
283+
def response_to_actions(self, response: 'ModelResponse') -> list['Action']:
284+
return codeact_function_calling.response_to_actions(
285+
response, mcp_tool_names=list(self.mcp_tools.keys())
286+
)

openhands/agenthub/readonly_agent/readonly_agent.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
import os
66

7+
from typing import TYPE_CHECKING
8+
9+
if TYPE_CHECKING:
10+
from litellm import ChatCompletionToolParam
11+
from openhands.events.action import Action
12+
from openhands.llm.llm import ModelResponse
13+
714
from openhands.agenthub.codeact_agent.codeact_agent import CodeActAgent
815
from openhands.agenthub.readonly_agent import (
916
function_calling as readonly_function_calling,
@@ -41,24 +48,27 @@ def __init__(
4148
- llm (LLM): The llm to be used by this agent
4249
- config (AgentConfig): The configuration for this agent
4350
"""
44-
# Initialize the CodeActAgent class but we'll override some of its behavior
51+
# Initialize the CodeActAgent class; some of it is overridden with class methods
4552
super().__init__(llm, config)
4653

47-
# Override the tools to only include read-only tools
48-
# Get the read-only tools from our own function_calling module
49-
self.tools = readonly_function_calling.get_tools()
50-
51-
# Set up our own prompt manager
52-
self.prompt_manager = PromptManager(
53-
prompt_dir=os.path.join(os.path.dirname(__file__), 'prompts'),
54-
)
55-
56-
self.response_to_actions_fn = readonly_function_calling.response_to_actions
57-
5854
logger.debug(
5955
f"TOOLS loaded for ReadOnlyAgent: {', '.join([tool.get('function').get('name') for tool in self.tools])}"
6056
)
6157

58+
@property
59+
def prompt_manager(self) -> PromptManager:
60+
# Set up our own prompt manager
61+
if self._prompt_manager is None:
62+
self._prompt_manager = PromptManager(
63+
prompt_dir=os.path.join(os.path.dirname(__file__), 'prompts'),
64+
)
65+
return self._prompt_manager
66+
67+
def _get_tools(self) -> list['ChatCompletionToolParam']:
68+
# Override the tools to only include read-only tools
69+
# Get the read-only tools from our own function_calling module
70+
return readonly_function_calling.get_tools()
71+
6272
def set_mcp_tools(self, mcp_tools: list[dict]) -> None:
6373
"""Sets the list of MCP tools for the agent.
6474
@@ -68,3 +78,8 @@ def set_mcp_tools(self, mcp_tools: list[dict]) -> None:
6878
logger.warning(
6979
'ReadOnlyAgent does not support MCP tools. MCP tools will be ignored by the agent.'
7080
)
81+
82+
def response_to_actions(self, response: 'ModelResponse') -> list['Action']:
83+
return readonly_function_calling.response_to_actions(
84+
response, mcp_tool_names=list(self.mcp_tools.keys())
85+
)
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55
from prompt_toolkit.shortcuts import clear, print_container
66
from prompt_toolkit.widgets import Frame, TextArea
77

8-
from openhands.core.cli_settings import (
8+
from openhands.cli.settings import (
99
display_settings,
1010
modify_llm_settings_advanced,
1111
modify_llm_settings_basic,
1212
)
13-
from openhands.core.cli_tui import (
13+
from openhands.cli.tui import (
1414
COLOR_GREY,
1515
UsageMetrics,
1616
cli_confirm,
1717
display_help,
1818
display_shutdown_message,
1919
display_status,
2020
)
21-
from openhands.core.cli_utils import (
21+
from openhands.cli.utils import (
2222
add_local_config_trusted_dir,
2323
get_local_config_trusted_dirs,
2424
read_file,
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@
77
from prompt_toolkit.shortcuts import clear
88

99
import openhands.agenthub # noqa F401 (we import this to get the agents registered)
10-
from openhands.controller import AgentController
11-
from openhands.controller.agent import Agent
12-
from openhands.core.cli_commands import (
10+
from openhands.cli.commands import (
1311
check_folder_security_agreement,
1412
handle_commands,
1513
)
16-
from openhands.core.cli_tui import (
14+
from openhands.cli.tui import (
1715
UsageMetrics,
1816
display_agent_running_message,
1917
display_banner,
@@ -26,9 +24,11 @@
2624
read_confirmation_input,
2725
read_prompt_input,
2826
)
29-
from openhands.core.cli_utils import (
27+
from openhands.cli.utils import (
3028
update_usage_metrics,
3129
)
30+
from openhands.controller import AgentController
31+
from openhands.controller.agent import Agent
3232
from openhands.core.config import (
3333
AppConfig,
3434
parse_arguments,
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
from prompt_toolkit.widgets import Frame, TextArea
66
from pydantic import SecretStr
77

8-
from openhands.controller.agent import Agent
9-
from openhands.core.cli_tui import (
8+
from openhands.cli.tui import (
109
COLOR_GREY,
1110
UserCancelledError,
1211
cli_confirm,
1312
kb_cancel,
1413
)
15-
from openhands.core.cli_utils import (
14+
from openhands.cli.utils import (
1615
VERIFIED_ANTHROPIC_MODELS,
1716
VERIFIED_OPENAI_MODELS,
1817
VERIFIED_PROVIDERS,
1918
organize_models_and_providers,
2019
)
20+
from openhands.controller.agent import Agent
2121
from openhands.core.config import AppConfig
2222
from openhands.core.config.condenser_config import NoOpCondenserConfig
2323
from openhands.core.config.utils import OH_DEFAULT_AGENT
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import toml
55

6-
from openhands.core.cli_tui import (
6+
from openhands.cli.tui import (
77
UsageMetrics,
88
)
99
from openhands.events.event import Event

openhands/controller/agent.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from openhands.core.config import AgentConfig
99
from openhands.events.action import Action
1010
from openhands.events.action.message import SystemMessageAction
11+
from openhands.utils.prompt import PromptManager
1112
from litellm import ChatCompletionToolParam
1213

1314
from openhands.core.exceptions import (
@@ -22,9 +23,6 @@
2223
from openhands.llm.llm import LLM
2324
from openhands.runtime.plugins import PluginRequirement
2425

25-
if TYPE_CHECKING:
26-
from openhands.utils.prompt import PromptManager
27-
2826

2927
class Agent(ABC):
3028
DEPRECATED = False
@@ -46,11 +44,17 @@ def __init__(
4644
self.llm = llm
4745
self.config = config
4846
self._complete = False
49-
self.prompt_manager: 'PromptManager' | None = None
47+
self._prompt_manager: 'PromptManager' | None = None
5048
self.event_stream: 'EventStream' | None = None
5149
self.mcp_tools: dict[str, ChatCompletionToolParam] = {}
5250
self.tools: list = []
5351

52+
@property
53+
def prompt_manager(self) -> 'PromptManager':
54+
if self._prompt_manager is None:
55+
raise ValueError(f'Prompt manager not initialized for agent {self.name}')
56+
return self._prompt_manager
57+
5458
def get_system_message(self) -> 'SystemMessageAction | None':
5559
"""
5660
Returns a SystemMessageAction containing the system message and tools.

0 commit comments

Comments
 (0)