openhands-tools 1.43.0__tar.gz → 1.44.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/PKG-INFO +1 -1
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/__init__.py +2 -0
- openhands_tools-1.44.0/openhands/tools/ask_oracle/__init__.py +36 -0
- openhands_tools-1.44.0/openhands/tools/ask_oracle/definition.py +114 -0
- openhands_tools-1.44.0/openhands/tools/ask_oracle/impl.py +120 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/browser_use/impl.py +12 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/__init__.py +6 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/default.py +19 -0
- openhands_tools-1.44.0/openhands/tools/preset/task_outcome.py +35 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands_tools.egg-info/PKG-INFO +1 -1
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands_tools.egg-info/SOURCES.txt +8 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/pyproject.toml +1 -1
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/apply_patch/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/apply_patch/core.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/apply_patch/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/browser_use/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/browser_use/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/browser_use/event_storage.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/browser_use/logging_fix.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/browser_use/recording.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/browser_use/server.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/delegate/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/delegate/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/delegate/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/delegate/visualizer.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/editor.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/exceptions.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/config.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/constants.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/diff.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/encoding.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/file_cache.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/history.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/shell.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/edit/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/edit/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/edit/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/file_change.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/list_directory/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/list_directory/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/list_directory/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/read_file/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/read_file/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/read_file/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/write_file/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/write_file/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/write_file/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/glob/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/glob/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/glob/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/grep/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/grep/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/grep/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/planning_file_editor/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/planning_file_editor/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/planning_file_editor/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/gemini.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/gpt5.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/planning.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/subagents/bash_runner.md +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/subagents/code_explorer.md +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/subagents/default.md +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/subagents/web_researcher.md +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/py.typed +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/task/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/task/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/task/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/task/manager.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/task_tracker/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/task_tracker/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/constants.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/descriptions.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/env.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/metadata.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/factory.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/interface.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/subprocess_terminal.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/terminal_session.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/tmux_pane_pool.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/tmux_terminal.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/windows_terminal.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/timeout_policy.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/utils/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/utils/command.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/utils/escape_filter.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/tom_consult/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/tom_consult/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/tom_consult/executor.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/utils/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/utils/timeout.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/workflow/__init__.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/workflow/definition.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/workflow/impl.py +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands_tools.egg-info/dependency_links.txt +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands_tools.egg-info/requires.txt +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands_tools.egg-info/top_level.txt +0 -0
- {openhands_tools-1.43.0 → openhands_tools-1.44.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-tools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.44.0
|
|
4
4
|
Summary: OpenHands Tools - Runtime tools for AI agents
|
|
5
5
|
Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
|
|
6
6
|
Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
|
|
@@ -18,6 +18,7 @@ from ``openhands.tools.browser_use``.
|
|
|
18
18
|
|
|
19
19
|
from importlib.metadata import PackageNotFoundError, version
|
|
20
20
|
|
|
21
|
+
from openhands.tools.ask_oracle import AskOracleTool
|
|
21
22
|
from openhands.tools.delegate import DelegationVisualizer
|
|
22
23
|
from openhands.tools.file_editor import FileEditorTool
|
|
23
24
|
from openhands.tools.preset.default import (
|
|
@@ -40,6 +41,7 @@ except PackageNotFoundError:
|
|
|
40
41
|
|
|
41
42
|
__all__ = [
|
|
42
43
|
"__version__",
|
|
44
|
+
"AskOracleTool",
|
|
43
45
|
"DelegationVisualizer",
|
|
44
46
|
"FileEditorTool",
|
|
45
47
|
"TaskToolSet",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Ask-Oracle tool package.
|
|
2
|
+
|
|
3
|
+
Provides a read-only ``ask_oracle`` tool: a one-shot, tool-less sub-agent that
|
|
4
|
+
consults a stronger/more-capable LLM for a second opinion. The Oracle model is a
|
|
5
|
+
saved LLM profile, resolved by convention under the name ``oracle`` (see
|
|
6
|
+
``ORACLE_PROFILE_NAME``).
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from openhands.tools.ask_oracle import AskOracleTool
|
|
10
|
+
|
|
11
|
+
agent = Agent(
|
|
12
|
+
llm=llm,
|
|
13
|
+
tools=[Tool(name=AskOracleTool.name)],
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
The agent's active conversation LLM is never switched. The Oracle call sends only
|
|
17
|
+
the Oracle system prompt plus the agent's question and optional context, without
|
|
18
|
+
forwarding conversation history or tools.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from openhands.tools.ask_oracle.definition import (
|
|
22
|
+
ORACLE_PROFILE_NAME,
|
|
23
|
+
AskOracleAction,
|
|
24
|
+
AskOracleObservation,
|
|
25
|
+
AskOracleTool,
|
|
26
|
+
)
|
|
27
|
+
from openhands.tools.ask_oracle.impl import AskOracleExecutor
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"ORACLE_PROFILE_NAME",
|
|
32
|
+
"AskOracleAction",
|
|
33
|
+
"AskOracleObservation",
|
|
34
|
+
"AskOracleExecutor",
|
|
35
|
+
"AskOracleTool",
|
|
36
|
+
]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Action, observation, and tool definitions for the ask_oracle tool."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from typing import TYPE_CHECKING, Final, Self
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
from rich.text import Text
|
|
8
|
+
|
|
9
|
+
from openhands.sdk.tool.registry import register_tool
|
|
10
|
+
from openhands.sdk.tool.tool import (
|
|
11
|
+
Action,
|
|
12
|
+
Observation,
|
|
13
|
+
ToolAnnotations,
|
|
14
|
+
ToolDefinition,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from openhands.sdk.conversation.state import ConversationState
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# The Oracle model is a saved LLM profile resolved by convention under this
|
|
23
|
+
# name. Save a profile named "oracle" (e.g. via LLMProfileStore.save("oracle",
|
|
24
|
+
# llm)) and the tool will consult it. No agent setting or wiring is required.
|
|
25
|
+
ORACLE_PROFILE_NAME: Final[str] = "oracle"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AskOracleAction(Action):
|
|
29
|
+
"""Action for asking the Oracle for advice."""
|
|
30
|
+
|
|
31
|
+
question: str = Field(
|
|
32
|
+
description=(
|
|
33
|
+
"The specific question or dilemma to ask the Oracle about. Use this "
|
|
34
|
+
"when you are stuck, uncertain, or need a second opinion."
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
context: str | None = Field(
|
|
38
|
+
default=None,
|
|
39
|
+
description=(
|
|
40
|
+
"Optional extra context, such as approaches already tried, constraints, "
|
|
41
|
+
"or the recommendation you are considering."
|
|
42
|
+
),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def visualize(self) -> Text:
|
|
47
|
+
content = Text()
|
|
48
|
+
content.append("Ask Oracle: ", style="bold cyan")
|
|
49
|
+
content.append(self.question)
|
|
50
|
+
if self.context:
|
|
51
|
+
content.append("\nContext: ", style="bold")
|
|
52
|
+
content.append(self.context)
|
|
53
|
+
return content
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class AskOracleObservation(Observation):
|
|
57
|
+
"""Observation returned by the Oracle consultation."""
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def visualize(self) -> Text:
|
|
61
|
+
content = Text()
|
|
62
|
+
if self.is_error:
|
|
63
|
+
content.append("Oracle consultation failed", style="bold red")
|
|
64
|
+
else:
|
|
65
|
+
content.append("Oracle recommendation", style="bold green")
|
|
66
|
+
if self.text:
|
|
67
|
+
content.append("\n")
|
|
68
|
+
content.append(self.text)
|
|
69
|
+
return content
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
_DESCRIPTION = (
|
|
73
|
+
"Ask the Oracle for a second opinion. The Oracle is a smart model intended "
|
|
74
|
+
"to help with difficult reasoning.\n\n"
|
|
75
|
+
"Use this when you are stuck, uncertain, comparing approaches, or need a "
|
|
76
|
+
"higher-quality recommendation before proceeding.\n\n"
|
|
77
|
+
"Treat the Oracle's response as strong guidance and follow its recommendation "
|
|
78
|
+
"unless you have a clear reason not to."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AskOracleTool(ToolDefinition[AskOracleAction, AskOracleObservation]):
|
|
83
|
+
"""Tool for consulting the Oracle (a saved LLM profile named "oracle")."""
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def create(
|
|
87
|
+
cls,
|
|
88
|
+
conv_state: "ConversationState | None" = None, # noqa: ARG003
|
|
89
|
+
**params,
|
|
90
|
+
) -> Sequence[Self]:
|
|
91
|
+
if params:
|
|
92
|
+
raise ValueError("AskOracleTool does not accept parameters")
|
|
93
|
+
|
|
94
|
+
# Import here to keep module import light and avoid any import cycles.
|
|
95
|
+
from openhands.tools.ask_oracle.impl import AskOracleExecutor
|
|
96
|
+
|
|
97
|
+
return [
|
|
98
|
+
cls(
|
|
99
|
+
description=_DESCRIPTION,
|
|
100
|
+
action_type=AskOracleAction,
|
|
101
|
+
observation_type=AskOracleObservation,
|
|
102
|
+
executor=AskOracleExecutor(),
|
|
103
|
+
annotations=ToolAnnotations(
|
|
104
|
+
readOnlyHint=True,
|
|
105
|
+
destructiveHint=False,
|
|
106
|
+
idempotentHint=False,
|
|
107
|
+
openWorldHint=True,
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# Automatically register when this module is imported.
|
|
114
|
+
register_tool(AskOracleTool.name, AskOracleTool)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Executor for the ask_oracle tool."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from openhands.sdk.agent.utils import make_llm_completion
|
|
6
|
+
from openhands.sdk.llm import Message, TextContent
|
|
7
|
+
from openhands.sdk.tool.tool import ToolExecutor
|
|
8
|
+
from openhands.tools.ask_oracle.definition import (
|
|
9
|
+
ORACLE_PROFILE_NAME,
|
|
10
|
+
AskOracleAction,
|
|
11
|
+
AskOracleObservation,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from openhands.sdk.conversation.impl.local_conversation import LocalConversation
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
ORACLE_USAGE_PREFIX = "oracle"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_ORACLE_SYSTEM_PROMPT = """\
|
|
23
|
+
You are the Oracle: a highly capable reviewer giving a second opinion to an \
|
|
24
|
+
OpenHands agent.
|
|
25
|
+
|
|
26
|
+
Answer the agent's question directly. Do not call tools. Do not perform work \
|
|
27
|
+
directly. Give a concrete recommendation the agent can follow, including important \
|
|
28
|
+
risks or caveats."""
|
|
29
|
+
|
|
30
|
+
_ORACLE_USER_PROMPT_TEMPLATE = """\
|
|
31
|
+
Question:
|
|
32
|
+
{question}
|
|
33
|
+
{context_section}"""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AskOracleExecutor(ToolExecutor[AskOracleAction, AskOracleObservation]):
|
|
37
|
+
"""Consult the Oracle: a saved LLM profile named ``oracle``.
|
|
38
|
+
|
|
39
|
+
The Oracle is resolved by convention from the conversation's LLM profile
|
|
40
|
+
store under the name ``oracle`` (``ORACLE_PROFILE_NAME``). The call is
|
|
41
|
+
stateless: it sends only the Oracle system prompt plus the agent's question
|
|
42
|
+
and optional context, with no conversation history and no tools, and returns
|
|
43
|
+
the Oracle's text. The active conversation LLM is never switched.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __call__(
|
|
47
|
+
self,
|
|
48
|
+
action: AskOracleAction,
|
|
49
|
+
conversation: "LocalConversation | None" = None,
|
|
50
|
+
) -> AskOracleObservation:
|
|
51
|
+
if conversation is None:
|
|
52
|
+
return AskOracleObservation.from_text(
|
|
53
|
+
text="Cannot ask the Oracle without an active conversation.",
|
|
54
|
+
is_error=True,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
oracle_llm = conversation.get_or_create_profile_llm(
|
|
59
|
+
profile_name=ORACLE_PROFILE_NAME,
|
|
60
|
+
usage_id=f"{ORACLE_USAGE_PREFIX}:{ORACLE_PROFILE_NAME}",
|
|
61
|
+
)
|
|
62
|
+
except FileNotFoundError:
|
|
63
|
+
return AskOracleObservation.from_text(
|
|
64
|
+
text=(
|
|
65
|
+
"The Oracle is not available because no profile named "
|
|
66
|
+
f"'{ORACLE_PROFILE_NAME}' was found. Save one to enable it."
|
|
67
|
+
),
|
|
68
|
+
is_error=True,
|
|
69
|
+
)
|
|
70
|
+
except ValueError as exc:
|
|
71
|
+
return AskOracleObservation.from_text(
|
|
72
|
+
text=f"The Oracle is not available: {exc}",
|
|
73
|
+
is_error=True,
|
|
74
|
+
)
|
|
75
|
+
except Exception as exc:
|
|
76
|
+
return AskOracleObservation.from_text(
|
|
77
|
+
text=f"The Oracle is not available: {type(exc).__name__}: {exc}",
|
|
78
|
+
is_error=True,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
context_section = (
|
|
82
|
+
f"\nAdditional context from the agent:\n{action.context}\n"
|
|
83
|
+
if action.context
|
|
84
|
+
else ""
|
|
85
|
+
)
|
|
86
|
+
user_prompt = _ORACLE_USER_PROMPT_TEMPLATE.format(
|
|
87
|
+
question=action.question,
|
|
88
|
+
context_section=context_section,
|
|
89
|
+
)
|
|
90
|
+
messages = [
|
|
91
|
+
Message(
|
|
92
|
+
role="system",
|
|
93
|
+
content=[TextContent(text=_ORACLE_SYSTEM_PROMPT)],
|
|
94
|
+
),
|
|
95
|
+
Message(role="user", content=[TextContent(text=user_prompt)]),
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
llm_response = make_llm_completion(oracle_llm, messages)
|
|
100
|
+
except Exception as exc:
|
|
101
|
+
return AskOracleObservation.from_text(
|
|
102
|
+
text=(
|
|
103
|
+
"The Oracle encountered an error and did not return a "
|
|
104
|
+
f"response: {type(exc).__name__}: {exc}"
|
|
105
|
+
),
|
|
106
|
+
is_error=True,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
oracle_text = "".join(
|
|
110
|
+
content.text
|
|
111
|
+
for content in llm_response.message.content
|
|
112
|
+
if isinstance(content, TextContent)
|
|
113
|
+
).strip()
|
|
114
|
+
if not oracle_text:
|
|
115
|
+
return AskOracleObservation.from_text(
|
|
116
|
+
text="The Oracle did not return a response.",
|
|
117
|
+
is_error=True,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return AskOracleObservation.from_text(text=oracle_text)
|
|
@@ -13,6 +13,7 @@ import threading
|
|
|
13
13
|
from collections.abc import Callable, Coroutine
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from typing import TYPE_CHECKING, Any, Final, TypeVar
|
|
16
|
+
from uuid import uuid4
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
@@ -356,6 +357,9 @@ class BrowserToolExecutor(ToolExecutor[BrowserAction, BrowserObservation]):
|
|
|
356
357
|
"allowed_domains": allowed_domains or [],
|
|
357
358
|
"executable_path": executable_path,
|
|
358
359
|
"chromium_sandbox": not running_as_root,
|
|
360
|
+
"user_data_dir": str(
|
|
361
|
+
Path.home() / ".config" / "browseruse" / "profiles" / uuid4().hex
|
|
362
|
+
),
|
|
359
363
|
**config,
|
|
360
364
|
}
|
|
361
365
|
|
|
@@ -692,6 +696,14 @@ class BrowserToolExecutor(ToolExecutor[BrowserAction, BrowserObservation]):
|
|
|
692
696
|
except Exception as e:
|
|
693
697
|
logger.warning(f"Error during browser cleanup: {e}")
|
|
694
698
|
finally:
|
|
699
|
+
# Remove the browser profile directory to avoid disk accumulation.
|
|
700
|
+
# browser_use doesn't clean up the user_data_dir on shutdown.
|
|
701
|
+
user_data_dir = self._config.get("user_data_dir")
|
|
702
|
+
if user_data_dir and "browseruse/profiles/" in user_data_dir:
|
|
703
|
+
try:
|
|
704
|
+
shutil.rmtree(user_data_dir, ignore_errors=True)
|
|
705
|
+
except Exception:
|
|
706
|
+
pass
|
|
695
707
|
try:
|
|
696
708
|
# Always close the async executor
|
|
697
709
|
self._async_executor.close()
|
|
@@ -26,6 +26,10 @@ from .default import (
|
|
|
26
26
|
from .gemini import get_gemini_agent, get_gemini_tools
|
|
27
27
|
from .gpt5 import get_gpt5_agent
|
|
28
28
|
from .planning import get_planning_agent
|
|
29
|
+
from .task_outcome import (
|
|
30
|
+
TaskOutcome,
|
|
31
|
+
TaskOutcomeStatus,
|
|
32
|
+
)
|
|
29
33
|
|
|
30
34
|
|
|
31
35
|
__all__ = [
|
|
@@ -36,4 +40,6 @@ __all__ = [
|
|
|
36
40
|
"get_gpt5_agent",
|
|
37
41
|
"get_planning_agent",
|
|
38
42
|
"register_builtins_agents",
|
|
43
|
+
"TaskOutcome",
|
|
44
|
+
"TaskOutcomeStatus",
|
|
39
45
|
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Default preset configuration for OpenHands agents."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
from openhands.sdk import Agent, agent_definition_to_factory, load_agents_from_dir
|
|
6
7
|
from openhands.sdk.context.condenser import default_condenser
|
|
@@ -9,6 +10,8 @@ from openhands.sdk.llm.llm import LLM
|
|
|
9
10
|
from openhands.sdk.logger import get_logger
|
|
10
11
|
from openhands.sdk.subagent import AgentDefinition, register_agent_if_absent
|
|
11
12
|
from openhands.sdk.tool import Tool
|
|
13
|
+
from openhands.sdk.tool.builtins import BUILT_IN_TOOLS, FinishTool
|
|
14
|
+
from openhands.sdk.tool.registry import list_registered_tools, register_tool
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
logger = get_logger(__name__)
|
|
@@ -73,11 +76,26 @@ def get_default_condenser(llm: LLM) -> CondenserBase:
|
|
|
73
76
|
def get_default_agent(
|
|
74
77
|
llm: LLM,
|
|
75
78
|
cli_mode: bool = False,
|
|
79
|
+
finish_tool_response_schema: Any | None = None,
|
|
76
80
|
) -> Agent:
|
|
77
81
|
tools = get_default_tools(
|
|
78
82
|
# Disable browser tools in CLI mode
|
|
79
83
|
enable_browser=not cli_mode,
|
|
80
84
|
)
|
|
85
|
+
agent_kwargs: dict[str, Any] = {}
|
|
86
|
+
if finish_tool_response_schema is not None:
|
|
87
|
+
if FinishTool.__name__ not in list_registered_tools():
|
|
88
|
+
register_tool(FinishTool.__name__, FinishTool)
|
|
89
|
+
tools.append(
|
|
90
|
+
Tool(
|
|
91
|
+
name=FinishTool.__name__,
|
|
92
|
+
params={"response_schema": finish_tool_response_schema},
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
agent_kwargs["include_default_tools"] = [
|
|
96
|
+
tool.__name__ for tool in BUILT_IN_TOOLS if tool is not FinishTool
|
|
97
|
+
]
|
|
98
|
+
|
|
81
99
|
agent = Agent(
|
|
82
100
|
llm=llm,
|
|
83
101
|
tools=tools,
|
|
@@ -85,6 +103,7 @@ def get_default_agent(
|
|
|
85
103
|
condenser=get_default_condenser(
|
|
86
104
|
llm=llm.model_copy(update={"usage_id": "condenser"})
|
|
87
105
|
),
|
|
106
|
+
**agent_kwargs,
|
|
88
107
|
)
|
|
89
108
|
return agent
|
|
90
109
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Experimental structured task outcome models for conversation finish actions."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
TaskOutcomeStatus = Literal[
|
|
9
|
+
"success",
|
|
10
|
+
"partial_success",
|
|
11
|
+
"blocked",
|
|
12
|
+
"failed",
|
|
13
|
+
"unknown",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TaskOutcome(BaseModel):
|
|
18
|
+
"""Latest semantic outcome reported for a conversation task.
|
|
19
|
+
|
|
20
|
+
Experimental: this structured response model may change as task outcome
|
|
21
|
+
reporting is refined.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
25
|
+
|
|
26
|
+
status: TaskOutcomeStatus = Field(
|
|
27
|
+
description="Agent's semantic assessment of task completion."
|
|
28
|
+
)
|
|
29
|
+
summary: str = Field(
|
|
30
|
+
alias="outcome_summary",
|
|
31
|
+
description=(
|
|
32
|
+
"Concise outcome summary. Include what was completed, blockers, "
|
|
33
|
+
"required user action, and relevant next steps."
|
|
34
|
+
),
|
|
35
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-tools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.44.0
|
|
4
4
|
Summary: OpenHands Tools - Runtime tools for AI agents
|
|
5
5
|
Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
|
|
6
6
|
Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
|
|
@@ -4,6 +4,9 @@ pyproject.toml
|
|
|
4
4
|
./openhands/tools/apply_patch/__init__.py
|
|
5
5
|
./openhands/tools/apply_patch/core.py
|
|
6
6
|
./openhands/tools/apply_patch/definition.py
|
|
7
|
+
./openhands/tools/ask_oracle/__init__.py
|
|
8
|
+
./openhands/tools/ask_oracle/definition.py
|
|
9
|
+
./openhands/tools/ask_oracle/impl.py
|
|
7
10
|
./openhands/tools/browser_use/__init__.py
|
|
8
11
|
./openhands/tools/browser_use/definition.py
|
|
9
12
|
./openhands/tools/browser_use/event_storage.py
|
|
@@ -56,6 +59,7 @@ pyproject.toml
|
|
|
56
59
|
./openhands/tools/preset/gemini.py
|
|
57
60
|
./openhands/tools/preset/gpt5.py
|
|
58
61
|
./openhands/tools/preset/planning.py
|
|
62
|
+
./openhands/tools/preset/task_outcome.py
|
|
59
63
|
./openhands/tools/preset/subagents/bash_runner.md
|
|
60
64
|
./openhands/tools/preset/subagents/code_explorer.md
|
|
61
65
|
./openhands/tools/preset/subagents/default.md
|
|
@@ -98,6 +102,9 @@ openhands/tools/py.typed
|
|
|
98
102
|
openhands/tools/apply_patch/__init__.py
|
|
99
103
|
openhands/tools/apply_patch/core.py
|
|
100
104
|
openhands/tools/apply_patch/definition.py
|
|
105
|
+
openhands/tools/ask_oracle/__init__.py
|
|
106
|
+
openhands/tools/ask_oracle/definition.py
|
|
107
|
+
openhands/tools/ask_oracle/impl.py
|
|
101
108
|
openhands/tools/browser_use/__init__.py
|
|
102
109
|
openhands/tools/browser_use/definition.py
|
|
103
110
|
openhands/tools/browser_use/event_storage.py
|
|
@@ -150,6 +157,7 @@ openhands/tools/preset/default.py
|
|
|
150
157
|
openhands/tools/preset/gemini.py
|
|
151
158
|
openhands/tools/preset/gpt5.py
|
|
152
159
|
openhands/tools/preset/planning.py
|
|
160
|
+
openhands/tools/preset/task_outcome.py
|
|
153
161
|
openhands/tools/preset/subagents/bash_runner.md
|
|
154
162
|
openhands/tools/preset/subagents/code_explorer.md
|
|
155
163
|
openhands/tools/preset/subagents/default.md
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/browser_use/event_storage.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/browser_use/logging_fix.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/__init__.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/config.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/constants.py
RENAMED
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/encoding.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/file_cache.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/history.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/file_editor/utils/shell.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/list_directory/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/list_directory/impl.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/read_file/__init__.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/read_file/definition.py
RENAMED
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/write_file/__init__.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/gemini/write_file/definition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/planning_file_editor/__init__.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/planning_file_editor/definition.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/planning_file_editor/impl.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/subagents/bash_runner.md
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/subagents/code_explorer.md
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/subagents/default.md
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/preset/subagents/web_researcher.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/task_tracker/definition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/__init__.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/factory.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/interface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/terminal/tmux_terminal.py
RENAMED
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/timeout_policy.py
RENAMED
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/utils/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands/tools/terminal/utils/escape_filter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openhands_tools-1.43.0 → openhands_tools-1.44.0}/openhands_tools.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|