openhands-sdk 1.7.3__py3-none-any.whl

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.
Files changed (180) hide show
  1. openhands/sdk/__init__.py +111 -0
  2. openhands/sdk/agent/__init__.py +8 -0
  3. openhands/sdk/agent/agent.py +650 -0
  4. openhands/sdk/agent/base.py +457 -0
  5. openhands/sdk/agent/prompts/in_context_learning_example.j2 +169 -0
  6. openhands/sdk/agent/prompts/in_context_learning_example_suffix.j2 +3 -0
  7. openhands/sdk/agent/prompts/model_specific/anthropic_claude.j2 +3 -0
  8. openhands/sdk/agent/prompts/model_specific/google_gemini.j2 +1 -0
  9. openhands/sdk/agent/prompts/model_specific/openai_gpt/gpt-5-codex.j2 +2 -0
  10. openhands/sdk/agent/prompts/model_specific/openai_gpt/gpt-5.j2 +3 -0
  11. openhands/sdk/agent/prompts/security_policy.j2 +22 -0
  12. openhands/sdk/agent/prompts/security_risk_assessment.j2 +21 -0
  13. openhands/sdk/agent/prompts/self_documentation.j2 +15 -0
  14. openhands/sdk/agent/prompts/system_prompt.j2 +132 -0
  15. openhands/sdk/agent/prompts/system_prompt_interactive.j2 +14 -0
  16. openhands/sdk/agent/prompts/system_prompt_long_horizon.j2 +40 -0
  17. openhands/sdk/agent/prompts/system_prompt_planning.j2 +40 -0
  18. openhands/sdk/agent/prompts/system_prompt_tech_philosophy.j2 +122 -0
  19. openhands/sdk/agent/utils.py +228 -0
  20. openhands/sdk/context/__init__.py +28 -0
  21. openhands/sdk/context/agent_context.py +264 -0
  22. openhands/sdk/context/condenser/__init__.py +18 -0
  23. openhands/sdk/context/condenser/base.py +100 -0
  24. openhands/sdk/context/condenser/llm_summarizing_condenser.py +248 -0
  25. openhands/sdk/context/condenser/no_op_condenser.py +14 -0
  26. openhands/sdk/context/condenser/pipeline_condenser.py +56 -0
  27. openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +59 -0
  28. openhands/sdk/context/condenser/utils.py +149 -0
  29. openhands/sdk/context/prompts/__init__.py +6 -0
  30. openhands/sdk/context/prompts/prompt.py +114 -0
  31. openhands/sdk/context/prompts/templates/ask_agent_template.j2 +11 -0
  32. openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +8 -0
  33. openhands/sdk/context/prompts/templates/system_message_suffix.j2 +32 -0
  34. openhands/sdk/context/skills/__init__.py +28 -0
  35. openhands/sdk/context/skills/exceptions.py +11 -0
  36. openhands/sdk/context/skills/skill.py +720 -0
  37. openhands/sdk/context/skills/trigger.py +36 -0
  38. openhands/sdk/context/skills/types.py +48 -0
  39. openhands/sdk/context/view.py +503 -0
  40. openhands/sdk/conversation/__init__.py +40 -0
  41. openhands/sdk/conversation/base.py +281 -0
  42. openhands/sdk/conversation/conversation.py +152 -0
  43. openhands/sdk/conversation/conversation_stats.py +85 -0
  44. openhands/sdk/conversation/event_store.py +157 -0
  45. openhands/sdk/conversation/events_list_base.py +17 -0
  46. openhands/sdk/conversation/exceptions.py +50 -0
  47. openhands/sdk/conversation/fifo_lock.py +133 -0
  48. openhands/sdk/conversation/impl/__init__.py +5 -0
  49. openhands/sdk/conversation/impl/local_conversation.py +665 -0
  50. openhands/sdk/conversation/impl/remote_conversation.py +956 -0
  51. openhands/sdk/conversation/persistence_const.py +9 -0
  52. openhands/sdk/conversation/response_utils.py +41 -0
  53. openhands/sdk/conversation/secret_registry.py +126 -0
  54. openhands/sdk/conversation/serialization_diff.py +0 -0
  55. openhands/sdk/conversation/state.py +392 -0
  56. openhands/sdk/conversation/stuck_detector.py +311 -0
  57. openhands/sdk/conversation/title_utils.py +191 -0
  58. openhands/sdk/conversation/types.py +45 -0
  59. openhands/sdk/conversation/visualizer/__init__.py +12 -0
  60. openhands/sdk/conversation/visualizer/base.py +67 -0
  61. openhands/sdk/conversation/visualizer/default.py +373 -0
  62. openhands/sdk/critic/__init__.py +15 -0
  63. openhands/sdk/critic/base.py +38 -0
  64. openhands/sdk/critic/impl/__init__.py +12 -0
  65. openhands/sdk/critic/impl/agent_finished.py +83 -0
  66. openhands/sdk/critic/impl/empty_patch.py +49 -0
  67. openhands/sdk/critic/impl/pass_critic.py +42 -0
  68. openhands/sdk/event/__init__.py +42 -0
  69. openhands/sdk/event/base.py +149 -0
  70. openhands/sdk/event/condenser.py +82 -0
  71. openhands/sdk/event/conversation_error.py +25 -0
  72. openhands/sdk/event/conversation_state.py +104 -0
  73. openhands/sdk/event/llm_completion_log.py +39 -0
  74. openhands/sdk/event/llm_convertible/__init__.py +20 -0
  75. openhands/sdk/event/llm_convertible/action.py +139 -0
  76. openhands/sdk/event/llm_convertible/message.py +142 -0
  77. openhands/sdk/event/llm_convertible/observation.py +141 -0
  78. openhands/sdk/event/llm_convertible/system.py +61 -0
  79. openhands/sdk/event/token.py +16 -0
  80. openhands/sdk/event/types.py +11 -0
  81. openhands/sdk/event/user_action.py +21 -0
  82. openhands/sdk/git/exceptions.py +43 -0
  83. openhands/sdk/git/git_changes.py +249 -0
  84. openhands/sdk/git/git_diff.py +129 -0
  85. openhands/sdk/git/models.py +21 -0
  86. openhands/sdk/git/utils.py +189 -0
  87. openhands/sdk/hooks/__init__.py +30 -0
  88. openhands/sdk/hooks/config.py +180 -0
  89. openhands/sdk/hooks/conversation_hooks.py +227 -0
  90. openhands/sdk/hooks/executor.py +155 -0
  91. openhands/sdk/hooks/manager.py +170 -0
  92. openhands/sdk/hooks/types.py +40 -0
  93. openhands/sdk/io/__init__.py +6 -0
  94. openhands/sdk/io/base.py +48 -0
  95. openhands/sdk/io/cache.py +85 -0
  96. openhands/sdk/io/local.py +119 -0
  97. openhands/sdk/io/memory.py +54 -0
  98. openhands/sdk/llm/__init__.py +45 -0
  99. openhands/sdk/llm/exceptions/__init__.py +45 -0
  100. openhands/sdk/llm/exceptions/classifier.py +50 -0
  101. openhands/sdk/llm/exceptions/mapping.py +54 -0
  102. openhands/sdk/llm/exceptions/types.py +101 -0
  103. openhands/sdk/llm/llm.py +1140 -0
  104. openhands/sdk/llm/llm_registry.py +122 -0
  105. openhands/sdk/llm/llm_response.py +59 -0
  106. openhands/sdk/llm/message.py +656 -0
  107. openhands/sdk/llm/mixins/fn_call_converter.py +1288 -0
  108. openhands/sdk/llm/mixins/non_native_fc.py +97 -0
  109. openhands/sdk/llm/options/__init__.py +1 -0
  110. openhands/sdk/llm/options/chat_options.py +93 -0
  111. openhands/sdk/llm/options/common.py +19 -0
  112. openhands/sdk/llm/options/responses_options.py +67 -0
  113. openhands/sdk/llm/router/__init__.py +10 -0
  114. openhands/sdk/llm/router/base.py +117 -0
  115. openhands/sdk/llm/router/impl/multimodal.py +76 -0
  116. openhands/sdk/llm/router/impl/random.py +22 -0
  117. openhands/sdk/llm/streaming.py +9 -0
  118. openhands/sdk/llm/utils/metrics.py +312 -0
  119. openhands/sdk/llm/utils/model_features.py +192 -0
  120. openhands/sdk/llm/utils/model_info.py +90 -0
  121. openhands/sdk/llm/utils/model_prompt_spec.py +98 -0
  122. openhands/sdk/llm/utils/retry_mixin.py +128 -0
  123. openhands/sdk/llm/utils/telemetry.py +362 -0
  124. openhands/sdk/llm/utils/unverified_models.py +156 -0
  125. openhands/sdk/llm/utils/verified_models.py +65 -0
  126. openhands/sdk/logger/__init__.py +22 -0
  127. openhands/sdk/logger/logger.py +195 -0
  128. openhands/sdk/logger/rolling.py +113 -0
  129. openhands/sdk/mcp/__init__.py +24 -0
  130. openhands/sdk/mcp/client.py +76 -0
  131. openhands/sdk/mcp/definition.py +106 -0
  132. openhands/sdk/mcp/exceptions.py +19 -0
  133. openhands/sdk/mcp/tool.py +270 -0
  134. openhands/sdk/mcp/utils.py +83 -0
  135. openhands/sdk/observability/__init__.py +4 -0
  136. openhands/sdk/observability/laminar.py +166 -0
  137. openhands/sdk/observability/utils.py +20 -0
  138. openhands/sdk/py.typed +0 -0
  139. openhands/sdk/secret/__init__.py +19 -0
  140. openhands/sdk/secret/secrets.py +92 -0
  141. openhands/sdk/security/__init__.py +6 -0
  142. openhands/sdk/security/analyzer.py +111 -0
  143. openhands/sdk/security/confirmation_policy.py +61 -0
  144. openhands/sdk/security/llm_analyzer.py +29 -0
  145. openhands/sdk/security/risk.py +100 -0
  146. openhands/sdk/tool/__init__.py +34 -0
  147. openhands/sdk/tool/builtins/__init__.py +34 -0
  148. openhands/sdk/tool/builtins/finish.py +106 -0
  149. openhands/sdk/tool/builtins/think.py +117 -0
  150. openhands/sdk/tool/registry.py +184 -0
  151. openhands/sdk/tool/schema.py +286 -0
  152. openhands/sdk/tool/spec.py +39 -0
  153. openhands/sdk/tool/tool.py +481 -0
  154. openhands/sdk/utils/__init__.py +22 -0
  155. openhands/sdk/utils/async_executor.py +115 -0
  156. openhands/sdk/utils/async_utils.py +39 -0
  157. openhands/sdk/utils/cipher.py +68 -0
  158. openhands/sdk/utils/command.py +90 -0
  159. openhands/sdk/utils/deprecation.py +166 -0
  160. openhands/sdk/utils/github.py +44 -0
  161. openhands/sdk/utils/json.py +48 -0
  162. openhands/sdk/utils/models.py +570 -0
  163. openhands/sdk/utils/paging.py +63 -0
  164. openhands/sdk/utils/pydantic_diff.py +85 -0
  165. openhands/sdk/utils/pydantic_secrets.py +64 -0
  166. openhands/sdk/utils/truncate.py +117 -0
  167. openhands/sdk/utils/visualize.py +58 -0
  168. openhands/sdk/workspace/__init__.py +17 -0
  169. openhands/sdk/workspace/base.py +158 -0
  170. openhands/sdk/workspace/local.py +189 -0
  171. openhands/sdk/workspace/models.py +35 -0
  172. openhands/sdk/workspace/remote/__init__.py +8 -0
  173. openhands/sdk/workspace/remote/async_remote_workspace.py +149 -0
  174. openhands/sdk/workspace/remote/base.py +164 -0
  175. openhands/sdk/workspace/remote/remote_workspace_mixin.py +323 -0
  176. openhands/sdk/workspace/workspace.py +49 -0
  177. openhands_sdk-1.7.3.dist-info/METADATA +17 -0
  178. openhands_sdk-1.7.3.dist-info/RECORD +180 -0
  179. openhands_sdk-1.7.3.dist-info/WHEEL +5 -0
  180. openhands_sdk-1.7.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,122 @@
1
+ from collections.abc import Callable
2
+ from typing import ClassVar
3
+ from uuid import uuid4
4
+
5
+ from pydantic import BaseModel, ConfigDict
6
+
7
+ from openhands.sdk.llm.llm import LLM
8
+ from openhands.sdk.logger import get_logger
9
+
10
+
11
+ logger = get_logger(__name__)
12
+
13
+
14
+ class RegistryEvent(BaseModel):
15
+ llm: LLM
16
+
17
+ model_config: ClassVar[ConfigDict] = ConfigDict(
18
+ arbitrary_types_allowed=True,
19
+ )
20
+
21
+
22
+ class LLMRegistry:
23
+ """A minimal LLM registry for managing LLM instances by usage ID.
24
+
25
+ This registry provides a simple way to manage multiple LLM instances,
26
+ avoiding the need to recreate LLMs with the same configuration.
27
+ """
28
+
29
+ registry_id: str
30
+ retry_listener: Callable[[int, int], None] | None
31
+
32
+ def __init__(
33
+ self,
34
+ retry_listener: Callable[[int, int], None] | None = None,
35
+ ):
36
+ """Initialize the LLM registry.
37
+
38
+ Args:
39
+ retry_listener: Optional callback for retry events.
40
+ """
41
+ self.registry_id = str(uuid4())
42
+ self.retry_listener = retry_listener
43
+ self._usage_to_llm: dict[str, LLM] = {}
44
+ self.subscriber: Callable[[RegistryEvent], None] | None = None
45
+
46
+ def subscribe(self, callback: Callable[[RegistryEvent], None]) -> None:
47
+ """Subscribe to registry events.
48
+
49
+ Args:
50
+ callback: Function to call when LLMs are created or updated.
51
+ """
52
+ self.subscriber = callback
53
+
54
+ def notify(self, event: RegistryEvent) -> None:
55
+ """Notify subscribers of registry events.
56
+
57
+ Args:
58
+ event: The registry event to notify about.
59
+ """
60
+ if self.subscriber:
61
+ try:
62
+ self.subscriber(event)
63
+ except Exception as e:
64
+ logger.warning(f"Failed to emit event: {e}")
65
+
66
+ @property
67
+ def usage_to_llm(self) -> dict[str, LLM]:
68
+ """Access the internal usage-ID-to-LLM mapping."""
69
+
70
+ return self._usage_to_llm
71
+
72
+ def add(self, llm: LLM) -> None:
73
+ """Add an LLM instance to the registry.
74
+
75
+ Args:
76
+ llm: The LLM instance to register.
77
+
78
+ Raises:
79
+ ValueError: If llm.usage_id already exists in the registry.
80
+ """
81
+ usage_id = llm.usage_id
82
+ if usage_id in self._usage_to_llm:
83
+ message = (
84
+ f"Usage ID '{usage_id}' already exists in registry. "
85
+ "Use a different usage_id on the LLM or "
86
+ "call get() to retrieve the existing LLM."
87
+ )
88
+ raise ValueError(message)
89
+
90
+ self._usage_to_llm[usage_id] = llm
91
+ self.notify(RegistryEvent(llm=llm))
92
+ logger.debug(
93
+ f"[LLM registry {self.registry_id}]: Added LLM for usage {usage_id}"
94
+ )
95
+
96
+ def get(self, usage_id: str) -> LLM:
97
+ """Get an LLM instance from the registry.
98
+
99
+ Args:
100
+ usage_id: Unique identifier for the LLM usage slot.
101
+
102
+ Returns:
103
+ The LLM instance.
104
+
105
+ Raises:
106
+ KeyError: If usage_id is not found in the registry.
107
+ """
108
+ if usage_id not in self._usage_to_llm:
109
+ raise KeyError(
110
+ f"Usage ID '{usage_id}' not found in registry. "
111
+ "Use add() to register an LLM first."
112
+ )
113
+
114
+ logger.info(
115
+ f"[LLM registry {self.registry_id}]: Retrieved LLM for usage {usage_id}"
116
+ )
117
+ return self._usage_to_llm[usage_id]
118
+
119
+ def list_usage_ids(self) -> list[str]:
120
+ """List all registered usage IDs."""
121
+
122
+ return list(self._usage_to_llm.keys())
@@ -0,0 +1,59 @@
1
+ """LLMResponse type for LLM completion responses.
2
+
3
+ This module provides the LLMResponse type that wraps LLM completion responses
4
+ with OpenHands-native types, eliminating the need for consumers to work directly
5
+ with LiteLLM types.
6
+ """
7
+
8
+ import warnings
9
+ from typing import ClassVar
10
+
11
+ from litellm import ResponsesAPIResponse
12
+ from litellm.types.utils import ModelResponse
13
+ from pydantic import BaseModel, ConfigDict
14
+
15
+ from openhands.sdk.llm.message import Message
16
+ from openhands.sdk.llm.utils.metrics import MetricsSnapshot
17
+
18
+
19
+ # Suppress Pydantic serializer warnings from litellm
20
+ # These warnings occur when Pydantic serializes litellm's ModelResponse objects
21
+ # that have mismatched field counts, which is expected behavior in litellm
22
+ warnings.filterwarnings("ignore", message="Pydantic serializer warnings")
23
+
24
+
25
+ __all__ = ["LLMResponse"]
26
+
27
+
28
+ class LLMResponse(BaseModel):
29
+ """Result of an LLM completion request.
30
+
31
+ This type provides a clean interface for LLM completion results, exposing
32
+ only OpenHands-native types to consumers while preserving access to the
33
+ raw LiteLLM response for internal use.
34
+
35
+ Attributes:
36
+ message: The completion message converted to OpenHands Message type
37
+ metrics: Snapshot of metrics from the completion request
38
+ raw_response: The original LiteLLM response (ModelResponse or
39
+ ResponsesAPIResponse) for internal use
40
+ """
41
+
42
+ message: Message
43
+ metrics: MetricsSnapshot
44
+ raw_response: ModelResponse | ResponsesAPIResponse
45
+
46
+ model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
47
+
48
+ @property
49
+ def id(self) -> str:
50
+ """Get the response ID from the underlying LLM response.
51
+
52
+ This property provides a clean interface to access the response ID,
53
+ supporting both completion mode (ModelResponse) and response API modes
54
+ (ResponsesAPIResponse).
55
+
56
+ Returns:
57
+ The response ID from the LLM response
58
+ """
59
+ return self.raw_response.id