Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 16 additions & 21 deletions sdk/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,37 +74,32 @@ Here's a complete example that creates an agent and asks it to perform a simple
```python icon="python" expandable examples/01_standalone_sdk/01_hello_world.py
import os

from pydantic import SecretStr
from openhands.sdk import LLM, Agent, Conversation, Tool
from openhands.tools.execute_bash import BashTool
from openhands.tools.file_editor import FileEditorTool
from openhands.tools.task_tracker import TaskTrackerTool

from openhands.sdk import LLM, Conversation
from openhands.tools.preset.default import get_default_agent


# Configure LLM and agent
# You can get an API key from https://app.all-hands.dev/settings/api-keys
api_key = os.getenv("LLM_API_KEY")
assert api_key is not None, "LLM_API_KEY environment variable is not set."
model = os.getenv("LLM_MODEL", "openhands/claude-sonnet-4-5-20250929")
base_url = os.getenv("LLM_BASE_URL")
llm = LLM(
model=model,
api_key=SecretStr(api_key),
base_url=base_url,
usage_id="agent",
model="anthropic/claude-sonnet-4-5-20250929",
api_key=os.getenv("LLM_API_KEY"),
)

agent = Agent(
llm=llm,
tools=[
Tool(name=BashTool.name),
Tool(name=FileEditorTool.name),
Tool(name=TaskTrackerTool.name),
],
)
agent = get_default_agent(llm=llm, cli_mode=True)

# Start a conversation and send some messages
cwd = os.getcwd()
conversation = Conversation(agent=agent, workspace=cwd)

# Send a message and let the agent run
conversation.send_message("Write 3 facts about the current project into FACTS.txt.")
conversation.run()

# Report cost
cost = llm.metrics.accumulated_cost
print(f"EXAMPLE_COST: {cost}")
print("All done!")
```

Run the example:
Expand Down
12 changes: 4 additions & 8 deletions sdk/guides/agent-browser-use.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ from openhands.sdk import (
LLMConvertibleEvent,
get_logger,
)
from openhands.sdk.tool import Tool, register_tool
from openhands.sdk.tool import Tool
from openhands.tools.browser_use import BrowserToolSet
from openhands.tools.execute_bash import BashTool
from openhands.tools.file_editor import FileEditorTool
Expand All @@ -44,15 +44,12 @@ llm = LLM(

# Tools
cwd = os.getcwd()
register_tool("BashTool", BashTool)
register_tool("FileEditorTool", FileEditorTool)
register_tool("BrowserToolSet", BrowserToolSet)
tools = [
Tool(
name="BashTool",
name=BashTool.name,
),
Tool(name="FileEditorTool"),
Tool(name="BrowserToolSet"),
Tool(name=FileEditorTool.name),
Tool(name=BrowserToolSet.name),
]

# If you need fine-grained browser control, you can manually register individual browser
Expand Down Expand Up @@ -80,7 +77,6 @@ conversation.send_message(
)
conversation.run()


print("=" * 100)
print("Conversation finished. Got the following LLM messages:")
for i, message in enumerate(llm_messages):
Expand Down
5 changes: 2 additions & 3 deletions sdk/guides/agent-interactive-terminal.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ from openhands.sdk import (
LLMConvertibleEvent,
get_logger,
)
from openhands.sdk.tool import Tool, register_tool
from openhands.sdk.tool import Tool
from openhands.tools.execute_bash import BashTool


Expand All @@ -43,10 +43,9 @@ llm = LLM(

# Tools
cwd = os.getcwd()
register_tool("BashTool", BashTool)
tools = [
Tool(
name="BashTool",
name=BashTool.name,
params={"no_change_timeout_seconds": 3},
)
]
Expand Down
1 change: 0 additions & 1 deletion sdk/guides/agent-server/api-sandbox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ if not runtime_api_key:
logger.error("RUNTIME_API_KEY required")
exit(1)


with APIRemoteWorkspace(
runtime_api_url=os.getenv("RUNTIME_API_URL", "https://runtime.eval.all-hands.dev"),
runtime_api_key=runtime_api_key,
Expand Down
43 changes: 26 additions & 17 deletions sdk/guides/agent-server/docker-sandbox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ from openhands.workspace import DockerWorkspace

logger = get_logger(__name__)


# 1) Ensure we have LLM API key
api_key = os.getenv("LLM_API_KEY")
assert api_key is not None, "LLM_API_KEY environment variable is not set."
Expand All @@ -61,9 +60,9 @@ def detect_platform():
# the Docker container automatically
with DockerWorkspace(
# dynamically build agent-server image
# base_image="nikolaik/python-nodejs:python3.12-nodejs22",
base_image="nikolaik/python-nodejs:python3.12-nodejs22",
# use pre-built image for faster startup
server_image="ghcr.io/openhands/agent-server:main-python",
# server_image="ghcr.io/openhands/agent-server:main-python",
host_port=8010,
platform=detect_platform(),
) as workspace:
Expand Down Expand Up @@ -109,7 +108,7 @@ with DockerWorkspace(
logger.info("🚀 Running conversation...")
conversation.run()
logger.info("✅ First task completed!")
logger.info(f"Agent status: {conversation.state.agent_status}")
logger.info(f"Agent status: {conversation.state.execution_status}")

# Wait for events to settle (no events for 2 seconds)
logger.info("⏳ Waiting for events to stop...")
Expand All @@ -123,6 +122,9 @@ with DockerWorkspace(
logger.info("✅ Second task completed!")

# Report cost (must be before conversation.close())
conversation.state._cached_state = (
None # Invalidate cache to fetch latest stats
)
cost = conversation.conversation_stats.get_combined_metrics().accumulated_cost
print(f"EXAMPLE_COST: {cost}")
finally:
Expand Down Expand Up @@ -255,7 +257,6 @@ from openhands.workspace import DockerWorkspace

logger = get_logger(__name__)


api_key = os.getenv("LLM_API_KEY")
assert api_key is not None, "LLM_API_KEY environment variable is not set."

Expand Down Expand Up @@ -447,7 +448,6 @@ from openhands.workspace import DockerWorkspace

logger = get_logger(__name__)


api_key = os.getenv("LLM_API_KEY")
assert api_key is not None, "LLM_API_KEY environment variable is not set."

Expand All @@ -470,7 +470,7 @@ def detect_platform():
# Create a Docker-based remote workspace with extra ports for browser access
with DockerWorkspace(
base_image="nikolaik/python-nodejs:python3.12-nodejs22",
host_port=8010,
host_port=8011,
platform=detect_platform(),
extra_ports=True, # Expose extra ports for VSCode and VNC
) as workspace:
Expand Down Expand Up @@ -509,17 +509,26 @@ with DockerWorkspace(
)
conversation.run()

# Wait for user confirm to exit
y = None
while y != "y":
y = input(
"Because you've enabled extra_ports=True in DockerWorkspace, "
"you can open a browser tab to see the *actual* browser OpenHands "
"is interacting with via VNC.\n\n"
"Link: http://localhost:8012/vnc.html?autoconnect=1&resize=remote\n\n"
"Press 'y' and Enter to exit and terminate the workspace.\n"
">> "
conversation.state._cached_state = None # Invalidate cache to fetch latest stats
cost = conversation.conversation_stats.get_combined_metrics().accumulated_cost
print(f"EXAMPLE_COST: {cost}")

if os.getenv("CI"):
logger.info(
"CI environment detected; skipping interactive prompt and closing workspace." # noqa: E501
)
else:
# Wait for user confirm to exit when running locally
y = None
while y != "y":
y = input(
"Because you've enabled extra_ports=True in DockerWorkspace, "
"you can open a browser tab to see the *actual* browser OpenHands "
"is interacting with via VNC.\n\n"
"Link: http://localhost:8012/vnc.html?autoconnect=1&resize=remote\n\n"
"Press 'y' and Enter to exit and terminate the workspace.\n"
">> "
)
```

```bash Running the Example
Expand Down
5 changes: 4 additions & 1 deletion sdk/guides/agent-server/local-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ with ManagedAPIServer(port=8001) as server:
conversation.run()

logger.info("✅ First task completed!")
logger.info(f"Agent status: {conversation.state.agent_status}")
logger.info(f"Agent status: {conversation.state.execution_status}")

# Wait for events to stop coming (no events for 2 seconds)
logger.info("⏳ Waiting for events to stop...")
Expand Down Expand Up @@ -254,6 +254,9 @@ with ManagedAPIServer(port=8001) as server:
logger.info(f" - {event}")

# Report cost (must be before conversation.close())
conversation.state._cached_state = (
None # Invalidate cache to fetch latest stats
)
cost = conversation.conversation_stats.get_combined_metrics().accumulated_cost
print(f"EXAMPLE_COST: {cost}")

Expand Down
13 changes: 4 additions & 9 deletions sdk/guides/context-condenser.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ from openhands.sdk import (
get_logger,
)
from openhands.sdk.context.condenser import LLMSummarizingCondenser
from openhands.sdk.tool import Tool, register_tool
from openhands.sdk.tool import Tool
from openhands.tools.execute_bash import BashTool
from openhands.tools.file_editor import FileEditorTool
from openhands.tools.task_tracker import TaskTrackerTool
Expand All @@ -98,15 +98,12 @@ llm = LLM(

# Tools
cwd = os.getcwd()
register_tool("BashTool", BashTool)
register_tool("FileEditorTool", FileEditorTool)
register_tool("TaskTrackerTool", TaskTrackerTool)
tools = [
Tool(
name="BashTool",
name=BashTool.name,
),
Tool(name="FileEditorTool"),
Tool(name="TaskTrackerTool"),
Tool(name=FileEditorTool.name),
Tool(name=TaskTrackerTool.name),
]

# Create a condenser to manage the context. The condenser will automatically truncate
Expand Down Expand Up @@ -163,7 +160,6 @@ conversation.send_message(
)
conversation.run()


print("=" * 100)
print("Conversation finished. Got the following LLM messages:")
for i, message in enumerate(llm_messages):
Expand All @@ -187,7 +183,6 @@ print("Sending message to deserialized conversation...")
conversation.send_message("Finally, clean up by deleting both files.")
conversation.run()


print("=" * 100)
print("Conversation finished with LLM Summarizing Condenser.")
print(f"Total LLM messages collected: {len(llm_messages)}")
Expand Down
11 changes: 4 additions & 7 deletions sdk/guides/convo-async.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ from openhands.sdk import (
get_logger,
)
from openhands.sdk.conversation.types import ConversationCallbackType
from openhands.sdk.tool import Tool, register_tool
from openhands.sdk.tool import Tool
from openhands.sdk.utils.async_utils import AsyncCallbackWrapper
from openhands.tools.execute_bash import BashTool
from openhands.tools.file_editor import FileEditorTool
Expand All @@ -53,15 +53,12 @@ llm = LLM(

# Tools
cwd = os.getcwd()
register_tool("BashTool", BashTool)
register_tool("FileEditorTool", FileEditorTool)
register_tool("TaskTrackerTool", TaskTrackerTool)
tools = [
Tool(
name="BashTool",
name=BashTool.name,
),
Tool(name="FileEditorTool"),
Tool(name="TaskTrackerTool"),
Tool(name=FileEditorTool.name),
Tool(name=TaskTrackerTool.name),
]

# Agent
Expand Down
18 changes: 7 additions & 11 deletions sdk/guides/convo-pause-and-resume.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ from openhands.sdk import (
Agent,
Conversation,
)
from openhands.sdk.tool import Tool, register_tool
from openhands.sdk.tool import Tool
from openhands.tools.execute_bash import BashTool
from openhands.tools.file_editor import FileEditorTool

Expand All @@ -40,20 +40,17 @@ llm = LLM(
)

# Tools
register_tool("BashTool", BashTool)
register_tool("FileEditorTool", FileEditorTool)
tools = [
Tool(
name="BashTool",
name=BashTool.name,
),
Tool(name="FileEditorTool"),
Tool(name=FileEditorTool.name),
]

# Agent
agent = Agent(llm=llm, tools=tools)
conversation = Conversation(agent, workspace=os.getcwd())


print("=" * 60)
print("Pause and Continue Example")
print("=" * 60)
Expand All @@ -66,7 +63,7 @@ conversation.send_message(
"one number per line. After you finish, summarize what you did."
)

print(f"Initial status: {conversation.state.agent_status}")
print(f"Initial status: {conversation.state.execution_status}")
print()

# Start the agent in a background thread
Expand All @@ -85,10 +82,9 @@ conversation.pause()
# Wait for the thread to finish (it will stop when paused)
thread.join()

print(f"Agent status after pause: {conversation.state.agent_status}")
print(f"Agent status after pause: {conversation.state.execution_status}")
print()


# Phase 3: Send a new message while paused
print("Phase 3: Sending a new message while agent is paused...")
conversation.send_message(
Expand All @@ -99,12 +95,12 @@ print()

# Phase 4: Resume the agent with .run()
print("Phase 4: Resuming agent with .run()...")
print(f"Status before resume: {conversation.state.agent_status}")
print(f"Status before resume: {conversation.state.execution_status}")

# Resume execution
conversation.run()

print(f"Final status: {conversation.state.agent_status}")
print(f"Final status: {conversation.state.execution_status}")

# Report cost
cost = llm.metrics.accumulated_cost
Expand Down
Loading