Skip to content

Commit 9bcaf5c

Browse files
rbrenopenhands-agentxingyaowwenyst
authored
Add Sphinx-compatible docstrings to core SDK classes (#1006)
Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: Xingyao Wang <xingyao@all-hands.dev> Co-authored-by: Engel Nyst <engel.nyst@gmail.com>
1 parent e190add commit 9bcaf5c

File tree

10 files changed

+167
-25
lines changed

10 files changed

+167
-25
lines changed

openhands-sdk/openhands/sdk/agent/agent.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@
4747

4848

4949
class Agent(AgentBase):
50+
"""Main agent implementation for OpenHands.
51+
52+
The Agent class provides the core functionality for running AI agents that can
53+
interact with tools, process messages, and execute actions. It inherits from
54+
AgentBase and implements the agent execution logic.
55+
56+
Example:
57+
>>> from openhands.sdk import LLM, Agent, Tool
58+
>>> llm = LLM(model="claude-sonnet-4-20250514", api_key=SecretStr("key"))
59+
>>> tools = [Tool(name="BashTool"), Tool(name="FileEditorTool")]
60+
>>> agent = Agent(llm=llm, tools=tools)
61+
"""
62+
5063
@property
5164
def _add_security_risk_prediction(self) -> bool:
5265
return isinstance(self.security_analyzer, LLMSecurityAnalyzer)

openhands-sdk/openhands/sdk/agent/base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@
2828

2929

3030
class AgentBase(DiscriminatedUnionMixin, ABC):
31-
"""Abstract base class for agents.
31+
"""Abstract base class for OpenHands agents.
32+
3233
Agents are stateless and should be fully defined by their configuration.
34+
This base class provides the common interface and functionality that all
35+
agent implementations must follow.
3336
"""
3437

3538
model_config = ConfigDict(

openhands-sdk/openhands/sdk/conversation/base.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ def agent(self) -> "AgentBase":
6969

7070

7171
class BaseConversation(ABC):
72+
"""Abstract base class for conversation implementations.
73+
74+
This class defines the interface that all conversation implementations must follow.
75+
Conversations manage the interaction between users and agents, handling message
76+
exchange, execution control, and state management.
77+
"""
78+
7279
@property
7380
@abstractmethod
7481
def id(self) -> ConversationID: ...
@@ -82,13 +89,23 @@ def state(self) -> ConversationStateProtocol: ...
8289
def conversation_stats(self) -> ConversationStats: ...
8390

8491
@abstractmethod
85-
def send_message(self, message: str | Message) -> None: ...
92+
def send_message(self, message: str | Message) -> None:
93+
"""Send a message to the agent."""
94+
...
8695

8796
@abstractmethod
88-
def run(self) -> None: ...
97+
def run(self) -> None:
98+
"""Execute the agent to process messages and perform actions.
99+
100+
This method runs the agent until it finishes processing the current
101+
message or reaches the maximum iteration limit.
102+
"""
103+
...
89104

90105
@abstractmethod
91-
def set_confirmation_policy(self, policy: ConfirmationPolicyBase) -> None: ...
106+
def set_confirmation_policy(self, policy: ConfirmationPolicyBase) -> None:
107+
"""Set the confirmation policy for the conversation."""
108+
...
92109

93110
@property
94111
def confirmation_policy_active(self) -> bool:

openhands-sdk/openhands/sdk/conversation/conversation.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,23 @@
1616

1717

1818
class Conversation:
19-
"""Factory entrypoint that returns a LocalConversation or RemoteConversation.
19+
"""Factory class for creating conversation instances with OpenHands agents.
2020
21-
Usage:
22-
- Conversation(agent=...) -> LocalConversation
23-
- Conversation(agent=..., host="http://...") -> RemoteConversation
21+
This factory automatically creates either a LocalConversation or RemoteConversation
22+
based on the workspace type provided. LocalConversation runs the agent locally,
23+
while RemoteConversation connects to a remote agent server.
24+
25+
Returns:
26+
LocalConversation if workspace is local, RemoteConversation if workspace
27+
is remote.
28+
29+
Example:
30+
>>> from openhands.sdk import LLM, Agent, Conversation
31+
>>> llm = LLM(model="claude-sonnet-4-20250514", api_key=SecretStr("key"))
32+
>>> agent = Agent(llm=llm, tools=[])
33+
>>> conversation = Conversation(agent=agent, workspace="./workspace")
34+
>>> conversation.send_message("Hello!")
35+
>>> conversation.run()
2436
"""
2537

2638
@overload

openhands-sdk/openhands/sdk/llm/llm.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,23 @@
101101

102102

103103
class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
104-
"""Refactored LLM: simple `completion()`, centralized Telemetry, tiny helpers."""
104+
"""Language model interface for OpenHands agents.
105+
106+
The LLM class provides a unified interface for interacting with various
107+
language models through the litellm library. It handles model configuration,
108+
API authentication,
109+
retry logic, and tool calling capabilities.
110+
111+
Example:
112+
>>> from openhands.sdk import LLM
113+
>>> from pydantic import SecretStr
114+
>>> llm = LLM(
115+
... model="claude-sonnet-4-20250514",
116+
... api_key=SecretStr("your-api-key"),
117+
... usage_id="my-agent"
118+
... )
119+
>>> # Use with agent or conversation
120+
"""
105121

106122
# =========================================================================
107123
# Config fields
@@ -388,6 +404,15 @@ def service_id(self, value: str) -> None:
388404

389405
@property
390406
def metrics(self) -> Metrics:
407+
"""Get usage metrics for this LLM instance.
408+
409+
Returns:
410+
Metrics object containing token usage, costs, and other statistics.
411+
412+
Example:
413+
>>> cost = llm.metrics.accumulated_cost
414+
>>> print(f"Total cost: ${cost}")
415+
"""
391416
assert self._metrics is not None, (
392417
"Metrics should be initialized after model validation"
393418
)
@@ -405,9 +430,22 @@ def completion(
405430
add_security_risk_prediction: bool = False,
406431
**kwargs,
407432
) -> LLMResponse:
408-
"""Single entry point for LLM completion.
433+
"""Generate a completion from the language model.
434+
435+
This is the method for getting responses from the model via Completion API.
436+
It handles message formatting, tool calling, and response processing.
437+
438+
Returns:
439+
LLMResponse containing the model's response and metadata.
440+
441+
Raises:
442+
ValueError: If streaming is requested (not supported).
409443
410-
Normalize → (maybe) mock tools → transport → postprocess.
444+
Example:
445+
>>> from openhands.sdk.llm import Message, TextContent
446+
>>> messages = [Message(role="user", content=[TextContent(text="Hello")])]
447+
>>> response = llm.completion(messages)
448+
>>> print(response.content)
411449
"""
412450
# Check if streaming is requested
413451
if kwargs.get("stream", False):

openhands-sdk/openhands/sdk/logger/logger.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,24 @@ def setup_logging(
160160

161161

162162
def get_logger(name: str) -> logging.Logger:
163-
"""Return a logger for the given module name."""
163+
"""Get a logger instance for the specified module.
164+
165+
This function returns a configured logger that inherits from the root logger
166+
setup. The logger supports both Rich formatting for human-readable output
167+
and JSON formatting for machine processing, depending on environment configuration.
168+
169+
Args:
170+
name: The name of the module, typically __name__.
171+
172+
Returns:
173+
A configured Logger instance.
174+
175+
Example:
176+
>>> from openhands.sdk.logger import get_logger
177+
>>> logger = get_logger(__name__)
178+
>>> logger.info("This is an info message")
179+
>>> logger.error("This is an error message")
180+
"""
164181
logger = logging.getLogger(name)
165182
logger.propagate = True
166183
return logger

openhands-sdk/openhands/sdk/tool/tool.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,26 @@ def __call__(
123123

124124

125125
class ToolBase[ActionT, ObservationT](DiscriminatedUnionMixin, ABC):
126-
"""Tool that wraps an executor function with input/output validation and schema.
127-
128-
- Normalize input/output schemas (class or dict) into both model+schema.
129-
- Validate inputs before execute.
130-
- Coerce outputs only if an output model is defined; else return vanilla JSON.
131-
- Export MCP tool description.
126+
"""Base class for tools that agents can use to perform actions.
127+
128+
Tools wrap executor functions with input/output validation and schema definition.
129+
They provide a standardized interface for agents to interact with external systems,
130+
APIs, or perform specific operations.
131+
132+
Features:
133+
- Normalize input/output schemas (class or dict) into both model+schema
134+
- Validate inputs before execution
135+
- Coerce outputs only if an output model is defined; else return vanilla JSON
136+
- Export MCP (Model Context Protocol) tool descriptions
137+
138+
Example:
139+
>>> from openhands.sdk.tool import ToolDefinition
140+
>>> tool = ToolDefinition(
141+
... name="echo",
142+
... description="Echo the input message",
143+
... action_type=EchoAction,
144+
... executor=echo_executor
145+
... )
132146
"""
133147

134148
model_config: ClassVar[ConfigDict] = ConfigDict(

openhands-sdk/openhands/sdk/workspace/base.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@
1414

1515

1616
class BaseWorkspace(DiscriminatedUnionMixin, ABC):
17-
"""Abstract base mixin for workspace.
17+
"""Abstract base class for workspace implementations.
1818
19-
All workspace implementations support the context manager protocol,
20-
allowing safe resource management:
19+
Workspaces provide a sandboxed environment where agents can execute commands,
20+
read/write files, and perform other operations. All workspace implementations
21+
support the context manager protocol for safe resource management.
2122
22-
with workspace:
23-
workspace.execute_command("echo 'hello'")
23+
Example:
24+
>>> with workspace:
25+
... result = workspace.execute_command("echo 'hello'")
26+
... content = workspace.read_file("example.txt")
2427
"""
2528

2629
working_dir: str = Field(

openhands-sdk/openhands/sdk/workspace/local.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,18 @@
1414

1515

1616
class LocalWorkspace(BaseWorkspace):
17-
"""Mixin providing local workspace operations."""
17+
"""Local workspace implementation that operates on the host filesystem.
18+
19+
LocalWorkspace provides direct access to the local filesystem and command execution
20+
environment. It's suitable for development and testing scenarios where the agent
21+
should operate directly on the host system.
22+
23+
Example:
24+
>>> workspace = LocalWorkspace(working_dir="/path/to/project")
25+
>>> with workspace:
26+
... result = workspace.execute_command("ls -la")
27+
... content = workspace.read_file("README.md")
28+
"""
1829

1930
def execute_command(
2031
self,

openhands-sdk/openhands/sdk/workspace/remote/base.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,21 @@
1212

1313

1414
class RemoteWorkspace(RemoteWorkspaceMixin, BaseWorkspace):
15-
"""Remote Workspace Implementation."""
15+
"""Remote workspace implementation that connects to an OpenHands agent server.
16+
17+
RemoteWorkspace provides access to a sandboxed environment running on a remote
18+
OpenHands agent server. This is the recommended approach for production deployments
19+
as it provides better isolation and security.
20+
21+
Example:
22+
>>> workspace = RemoteWorkspace(
23+
... host="https://agent-server.example.com",
24+
... working_dir="/workspace"
25+
... )
26+
>>> with workspace:
27+
... result = workspace.execute_command("ls -la")
28+
... content = workspace.read_file("README.md")
29+
"""
1630

1731
_client: httpx.Client | None = PrivateAttr(default=None)
1832

0 commit comments

Comments
 (0)