openhands-sdk 1.10.0__py3-none-any.whl → 1.11.0__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.
openhands/sdk/llm/llm.py CHANGED
@@ -27,8 +27,11 @@ from openhands.sdk.utils.pydantic_secrets import serialize_secret, validate_secr
27
27
 
28
28
 
29
29
  if TYPE_CHECKING: # type hints only, avoid runtime import cycle
30
+ from openhands.sdk.llm.auth import SupportedVendor
30
31
  from openhands.sdk.tool.tool import ToolDefinition
31
32
 
33
+ from openhands.sdk.llm.auth.openai import transform_for_subscription
34
+
32
35
 
33
36
  with warnings.catch_warnings():
34
37
  warnings.simplefilter("ignore")
@@ -50,8 +53,20 @@ from litellm.exceptions import (
50
53
  Timeout as LiteLLMTimeout,
51
54
  )
52
55
  from litellm.responses.main import responses as litellm_responses
53
- from litellm.types.llms.openai import ResponsesAPIResponse
54
- from litellm.types.utils import ModelResponse
56
+ from litellm.responses.streaming_iterator import SyncResponsesAPIStreamingIterator
57
+ from litellm.types.llms.openai import (
58
+ OutputTextDeltaEvent,
59
+ ReasoningSummaryTextDeltaEvent,
60
+ RefusalDeltaEvent,
61
+ ResponseCompletedEvent,
62
+ ResponsesAPIResponse,
63
+ )
64
+ from litellm.types.utils import (
65
+ Delta,
66
+ ModelResponse,
67
+ ModelResponseStream,
68
+ StreamingChoices,
69
+ )
55
70
  from litellm.utils import (
56
71
  create_pretrained_tokenizer,
57
72
  supports_vision,
@@ -335,6 +350,7 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
335
350
  _model_info: Any = PrivateAttr(default=None)
336
351
  _tokenizer: Any = PrivateAttr(default=None)
337
352
  _telemetry: Telemetry | None = PrivateAttr(default=None)
353
+ _is_subscription: bool = PrivateAttr(default=False)
338
354
 
339
355
  model_config: ClassVar[ConfigDict] = ConfigDict(
340
356
  extra="ignore", arbitrary_types_allowed=True
@@ -499,6 +515,19 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
499
515
  )
500
516
  return self._telemetry
501
517
 
518
+ @property
519
+ def is_subscription(self) -> bool:
520
+ """Check if this LLM uses subscription-based authentication.
521
+
522
+ Returns True when the LLM was created via `LLM.subscription_login()`,
523
+ which uses the ChatGPT subscription Codex backend rather than the
524
+ standard OpenAI API.
525
+
526
+ Returns:
527
+ bool: True if using subscription-based transport, False otherwise.
528
+ """
529
+ return self._is_subscription
530
+
502
531
  def restore_metrics(self, metrics: Metrics) -> None:
503
532
  # Only used by ConversationStats to seed metrics
504
533
  self._metrics = metrics
@@ -662,7 +691,7 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
662
691
  raise
663
692
 
664
693
  # =========================================================================
665
- # Responses API (non-stream, v1)
694
+ # Responses API (v1)
666
695
  # =========================================================================
667
696
  def responses(
668
697
  self,
@@ -686,16 +715,19 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
686
715
  store: Whether to store the conversation
687
716
  _return_metrics: Whether to return usage metrics
688
717
  add_security_risk_prediction: Add security_risk field to tool schemas
689
- on_token: Optional callback for streaming tokens (not yet supported)
718
+ on_token: Optional callback for streaming deltas
690
719
  **kwargs: Additional arguments passed to the API
691
720
 
692
721
  Note:
693
722
  Summary field is always added to tool schemas for transparency and
694
723
  explainability of agent actions.
695
724
  """
696
- # Streaming not yet supported
697
- if kwargs.get("stream", False) or self.stream or on_token is not None:
698
- raise ValueError("Streaming is not supported for Responses API yet")
725
+ user_enable_streaming = bool(kwargs.get("stream", False)) or self.stream
726
+ if user_enable_streaming:
727
+ if on_token is None and not self.is_subscription:
728
+ # We allow on_token to be None for subscription mode
729
+ raise ValueError("Streaming requires an on_token callback")
730
+ kwargs["stream"] = True
699
731
 
700
732
  # Build instructions + input list using dedicated Responses formatter
701
733
  instructions, input_items = self.format_messages_for_responses(messages)
@@ -771,12 +803,67 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
771
803
  seed=self.seed,
772
804
  **final_kwargs,
773
805
  )
774
- assert isinstance(ret, ResponsesAPIResponse), (
806
+ if isinstance(ret, ResponsesAPIResponse):
807
+ if user_enable_streaming:
808
+ logger.warning(
809
+ "Responses streaming was requested, but the provider "
810
+ "returned a non-streaming response; no on_token deltas "
811
+ "will be emitted."
812
+ )
813
+ self._telemetry.on_response(ret)
814
+ return ret
815
+
816
+ # When stream=True, LiteLLM returns a streaming iterator rather than
817
+ # a single ResponsesAPIResponse. Drain the iterator and use the
818
+ # completed response.
819
+ if final_kwargs.get("stream", False):
820
+ if not isinstance(ret, SyncResponsesAPIStreamingIterator):
821
+ raise AssertionError(
822
+ f"Expected Responses stream iterator, got {type(ret)}"
823
+ )
824
+
825
+ stream_callback = on_token if user_enable_streaming else None
826
+ for event in ret:
827
+ if stream_callback is None:
828
+ continue
829
+ if isinstance(
830
+ event,
831
+ (
832
+ OutputTextDeltaEvent,
833
+ RefusalDeltaEvent,
834
+ ReasoningSummaryTextDeltaEvent,
835
+ ),
836
+ ):
837
+ delta = event.delta
838
+ if delta:
839
+ stream_callback(
840
+ ModelResponseStream(
841
+ choices=[
842
+ StreamingChoices(
843
+ delta=Delta(content=delta)
844
+ )
845
+ ]
846
+ )
847
+ )
848
+
849
+ completed_event = ret.completed_response
850
+ if completed_event is None:
851
+ raise LLMNoResponseError(
852
+ "Responses stream finished without a completed response"
853
+ )
854
+ if not isinstance(completed_event, ResponseCompletedEvent):
855
+ raise LLMNoResponseError(
856
+ f"Unexpected completed event: {type(completed_event)}"
857
+ )
858
+
859
+ completed_resp = completed_event.response
860
+
861
+ self._telemetry.on_response(completed_resp)
862
+ return completed_resp
863
+
864
+ raise AssertionError(
775
865
  f"Expected ResponsesAPIResponse, got {type(ret)}"
776
866
  )
777
- # telemetry (latency, cost). Token usage mapping we handle after.
778
- self._telemetry.on_response(ret)
779
- return ret
780
867
 
781
868
  try:
782
869
  resp: ResponsesAPIResponse = _one_attempt()
@@ -1046,8 +1133,9 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
1046
1133
 
1047
1134
  - Skips prompt caching flags and string serializer concerns
1048
1135
  - Uses Message.to_responses_value to get either instructions (system)
1049
- or input items (others)
1136
+ or input items (others)
1050
1137
  - Concatenates system instructions into a single instructions string
1138
+ - For subscription mode, system prompts are prepended to user content
1051
1139
  """
1052
1140
  msgs = copy.deepcopy(messages)
1053
1141
 
@@ -1057,18 +1145,26 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
1057
1145
  # Assign system instructions as a string, collect input items
1058
1146
  instructions: str | None = None
1059
1147
  input_items: list[dict[str, Any]] = []
1148
+ system_chunks: list[str] = []
1149
+
1060
1150
  for m in msgs:
1061
1151
  val = m.to_responses_value(vision_enabled=vision_active)
1062
1152
  if isinstance(val, str):
1063
1153
  s = val.strip()
1064
- if not s:
1065
- continue
1066
- instructions = (
1067
- s if instructions is None else f"{instructions}\n\n---\n\n{s}"
1068
- )
1069
- else:
1070
- if val:
1071
- input_items.extend(val)
1154
+ if s:
1155
+ if self.is_subscription:
1156
+ system_chunks.append(s)
1157
+ else:
1158
+ instructions = (
1159
+ s
1160
+ if instructions is None
1161
+ else f"{instructions}\n\n---\n\n{s}"
1162
+ )
1163
+ elif val:
1164
+ input_items.extend(val)
1165
+
1166
+ if self.is_subscription:
1167
+ return transform_for_subscription(system_chunks, input_items)
1072
1168
  return instructions, input_items
1073
1169
 
1074
1170
  def get_token_count(self, messages: list[Message]) -> int:
@@ -1159,3 +1255,62 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
1159
1255
  if v is not None:
1160
1256
  data[field_name] = v
1161
1257
  return cls(**data)
1258
+
1259
+ @classmethod
1260
+ def subscription_login(
1261
+ cls,
1262
+ vendor: SupportedVendor,
1263
+ model: str,
1264
+ force_login: bool = False,
1265
+ open_browser: bool = True,
1266
+ **llm_kwargs,
1267
+ ) -> LLM:
1268
+ """Authenticate with a subscription service and return an LLM instance.
1269
+
1270
+ This method provides subscription-based access to LLM models that are
1271
+ available through chat subscriptions (e.g., ChatGPT Plus/Pro) rather
1272
+ than API credits. It handles credential caching, token refresh, and
1273
+ the OAuth login flow.
1274
+
1275
+ Currently supported vendors:
1276
+ - "openai": ChatGPT Plus/Pro subscription for Codex models
1277
+
1278
+ Supported OpenAI models:
1279
+ - gpt-5.1-codex-max
1280
+ - gpt-5.1-codex-mini
1281
+ - gpt-5.2
1282
+ - gpt-5.2-codex
1283
+
1284
+ Args:
1285
+ vendor: The vendor/provider. Currently only "openai" is supported.
1286
+ model: The model to use. Must be supported by the vendor's
1287
+ subscription service.
1288
+ force_login: If True, always perform a fresh login even if valid
1289
+ credentials exist.
1290
+ open_browser: Whether to automatically open the browser for the
1291
+ OAuth login flow.
1292
+ **llm_kwargs: Additional arguments to pass to the LLM constructor.
1293
+
1294
+ Returns:
1295
+ An LLM instance configured for subscription-based access.
1296
+
1297
+ Raises:
1298
+ ValueError: If the vendor or model is not supported.
1299
+ RuntimeError: If authentication fails.
1300
+
1301
+ Example:
1302
+ >>> from openhands.sdk import LLM
1303
+ >>> # First time: opens browser for OAuth login
1304
+ >>> llm = LLM.subscription_login(vendor="openai", model="gpt-5.2-codex")
1305
+ >>> # Subsequent calls: reuses cached credentials
1306
+ >>> llm = LLM.subscription_login(vendor="openai", model="gpt-5.2-codex")
1307
+ """
1308
+ from openhands.sdk.llm.auth.openai import subscription_login
1309
+
1310
+ return subscription_login(
1311
+ vendor=vendor,
1312
+ model=model,
1313
+ force_login=force_login,
1314
+ open_browser=open_browser,
1315
+ **llm_kwargs,
1316
+ )
@@ -15,15 +15,16 @@ def select_responses_options(
15
15
  ) -> dict[str, Any]:
16
16
  """Behavior-preserving extraction of _normalize_responses_kwargs."""
17
17
  # Apply defaults for keys that are not forced by policy
18
- out = apply_defaults_if_absent(
19
- user_kwargs,
20
- {
21
- "max_output_tokens": llm.max_output_tokens,
22
- },
23
- )
18
+ # Note: max_output_tokens is not supported in subscription mode
19
+ defaults = {}
20
+ if not llm.is_subscription:
21
+ defaults["max_output_tokens"] = llm.max_output_tokens
22
+ out = apply_defaults_if_absent(user_kwargs, defaults)
24
23
 
25
24
  # Enforce sampling/tool behavior for Responses path
26
- out["temperature"] = 1.0
25
+ # Note: temperature is not supported in subscription mode
26
+ if not llm.is_subscription:
27
+ out["temperature"] = 1.0
27
28
  out["tool_choice"] = "auto"
28
29
 
29
30
  # If user didn't set extra_headers, propagate from llm config
@@ -155,6 +155,7 @@ FORCE_STRING_SERIALIZER_MODELS: list[str] = [
155
155
  # in the message input
156
156
  SEND_REASONING_CONTENT_MODELS: list[str] = [
157
157
  "kimi-k2-thinking",
158
+ "kimi-k2.5",
158
159
  "openrouter/minimax-m2", # MiniMax-M2 via OpenRouter (interleaved thinking)
159
160
  "deepseek/deepseek-reasoner",
160
161
  ]
@@ -181,6 +182,7 @@ def get_features(model: str) -> ModelFeatures:
181
182
  # Each entry: (pattern, default_temperature)
182
183
  DEFAULT_TEMPERATURE_MODELS: list[tuple[str, float]] = [
183
184
  ("kimi-k2-thinking", 1.0),
185
+ ("kimi-k2.5", 1.0),
184
186
  ]
185
187
 
186
188
 
@@ -92,7 +92,19 @@ class LookupSecret(SecretSource):
92
92
  return result
93
93
 
94
94
 
95
- _SECRET_HEADERS = ["AUTHORIZATION", "KEY", "SECRET"]
95
+ # Patterns used for substring matching against header names (case-insensitive).
96
+ # Headers containing any of these patterns will be redacted during serialization.
97
+ # Examples: X-Access-Token, Cookie, Authorization, X-API-Key, X-API-Secret
98
+ _SECRET_HEADERS = [
99
+ "AUTHORIZATION",
100
+ "COOKIE",
101
+ "CREDENTIAL",
102
+ "KEY",
103
+ "PASSWORD",
104
+ "SECRET",
105
+ "SESSION",
106
+ "TOKEN",
107
+ ]
96
108
 
97
109
 
98
110
  def _is_secret_header(key: str):
@@ -50,12 +50,17 @@ class RemoteWorkspace(RemoteWorkspaceMixin, BaseWorkspace):
50
50
  if client is None:
51
51
  # Configure reasonable timeouts for HTTP requests
52
52
  # - connect: 10 seconds to establish connection
53
- # - read: 60 seconds to read response (for LLM operations)
53
+ # - read: 600 seconds (10 minutes) to read response (for LLM operations)
54
54
  # - write: 10 seconds to send request
55
55
  # - pool: 10 seconds to get connection from pool
56
- timeout = httpx.Timeout(connect=10.0, read=60.0, write=10.0, pool=10.0)
56
+ timeout = httpx.Timeout(
57
+ connect=10.0, read=self.read_timeout, write=10.0, pool=10.0
58
+ )
57
59
  client = httpx.Client(
58
- base_url=self.host, timeout=timeout, headers=self._headers
60
+ base_url=self.host,
61
+ timeout=timeout,
62
+ headers=self._headers,
63
+ limits=httpx.Limits(max_connections=self.max_connections),
59
64
  )
60
65
  self._client = client
61
66
  return client
@@ -25,6 +25,15 @@ class RemoteWorkspaceMixin(BaseModel):
25
25
  working_dir: str = Field(
26
26
  description="The working directory for agent operations and tool execution."
27
27
  )
28
+ read_timeout: float = Field(
29
+ default=600.0,
30
+ description="Timeout in seconds for reading operations of httpx.Client.",
31
+ )
32
+ max_connections: int | None = Field(
33
+ default=None,
34
+ description="Maximum number of connections for httpx.Client. "
35
+ "None means no limit, useful for running many conversations in parallel.",
36
+ )
28
37
 
29
38
  def model_post_init(self, context: Any) -> None:
30
39
  # Set up remote host
@@ -87,26 +96,50 @@ class RemoteWorkspaceMixin(BaseModel):
87
96
  stdout_parts = []
88
97
  stderr_parts = []
89
98
  exit_code = None
99
+ last_order = -1 # Track highest order seen to fetch only new events
100
+ seen_event_ids: set[str] = set() # Track seen IDs to detect duplicates
90
101
 
91
102
  while time.time() - start_time < timeout:
92
- # Search for all events
103
+ # Search for new events (order > last_order)
104
+ params: dict[str, str | int] = {
105
+ "command_id__eq": command_id,
106
+ "sort_order": "TIMESTAMP",
107
+ "limit": 100,
108
+ "kind__eq": "BashOutput",
109
+ }
110
+ if last_order >= 0:
111
+ params["order__gt"] = last_order
112
+
93
113
  response = yield {
94
114
  "method": "GET",
95
115
  "url": f"{self.host}/api/bash/bash_events/search",
96
- "params": {
97
- "command_id__eq": command_id,
98
- "sort_order": "TIMESTAMP",
99
- "limit": 100,
100
- },
116
+ "params": params,
101
117
  "headers": self._headers,
102
118
  "timeout": timeout,
103
119
  }
104
120
  response.raise_for_status()
105
121
  search_result = response.json()
106
122
 
107
- # Filter for BashOutput events for this command
123
+ # Process BashOutput events
108
124
  for event in search_result.get("items", []):
109
125
  if event.get("kind") == "BashOutput":
126
+ # Check for duplicates - safety check in case caller
127
+ # forgets to add kind__eq filter or API has a bug
128
+ event_id = event.get("id")
129
+ if event_id is not None:
130
+ if event_id in seen_event_ids:
131
+ raise RuntimeError(
132
+ f"Duplicate event received: {event_id}. "
133
+ "This should not happen with order__gt "
134
+ "filtering and kind filtering."
135
+ )
136
+ seen_event_ids.add(event_id)
137
+
138
+ # Track the highest order we've seen
139
+ event_order = event.get("order")
140
+ if event_order is not None and event_order > last_order:
141
+ last_order = event_order
142
+
110
143
  if event.get("stdout"):
111
144
  stdout_parts.append(event["stdout"])
112
145
  if event.get("stderr"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-sdk
3
- Version: 1.10.0
3
+ Version: 1.11.0
4
4
  Summary: OpenHands SDK - Core functionality for building 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
@@ -1,8 +1,8 @@
1
1
  openhands/sdk/__init__.py,sha256=FNRcPyCbvwYGrbSCxox_UWIHK5JuyCLHCyPLPZRd5sA,2589
2
2
  openhands/sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  openhands/sdk/agent/__init__.py,sha256=yOn1ZCgTTq2VJlTzKDSzmWVPli1siBzqV89vlEHCwOg,137
4
- openhands/sdk/agent/agent.py,sha256=BVbXw3L7V8sUuxtlBOjEg0t7ESJLY1kp6W8C3Qossf8,31020
5
- openhands/sdk/agent/base.py,sha256=yo1uCd7Iyp7-CidkIKn5E05LwbRrABW1CxNK_cT6DMM,19313
4
+ openhands/sdk/agent/agent.py,sha256=VOlGmKyiZ6Dzd3LPMua4Vhd9YpY_ju9RDgaw7B5aZ1g,32610
5
+ openhands/sdk/agent/base.py,sha256=Fv_d0e9LhaiV-QylziCVdOmPGqec5wPaUFIq5XbI3S0,19314
6
6
  openhands/sdk/agent/utils.py,sha256=AY_7VBFaJ2iorfh0MiS-mXaK6f8ONDejNcbkde_xTGg,8274
7
7
  openhands/sdk/agent/prompts/in_context_learning_example.j2,sha256=MGB0dPUlh6pwLoR_dBK-M3e5dtETX6C6WNjPcPixZmU,5512
8
8
  openhands/sdk/agent/prompts/in_context_learning_example_suffix.j2,sha256=k3Zwnd7Iq7kL4lo307RDuu1mxWXn6pSLsEdvKEXN3BU,164
@@ -22,8 +22,8 @@ openhands/sdk/context/__init__.py,sha256=r2YtpZMVFIIaKYVh2yjicTXYPURGhIUUdbliaLY
22
22
  openhands/sdk/context/agent_context.py,sha256=TUQB-RBmkKvnwMLKvG9eOc5TVlRnDtBOSeoXRA28JoA,12425
23
23
  openhands/sdk/context/view.py,sha256=xSgwWiA7Omcz7bpXm_7-ANRlO590PjDAQvMggvr-sK0,19757
24
24
  openhands/sdk/context/condenser/__init__.py,sha256=peHKPk51AujZhXvR2H0j1vBUJsCc8D6-OMHZ4Vk-pxU,568
25
- openhands/sdk/context/condenser/base.py,sha256=4_heKNni0BW3Lib3WMVRpzm8G03opW6VcHH9M_mV-j0,5869
26
- openhands/sdk/context/condenser/llm_summarizing_condenser.py,sha256=5GPeICwDKSa5QWZ2mDGWDJXnxNBvSmZGV1hu4j6Q6r8,9836
25
+ openhands/sdk/context/condenser/base.py,sha256=4AO59S44ZeX7eRZLocgFXjmHZcs3_TwwQdTG_witWHs,7450
26
+ openhands/sdk/context/condenser/llm_summarizing_condenser.py,sha256=2-yAfUTgC-lh_Qy8HmdFzfWavQC2o4PjLJ-mBRBNpfM,12592
27
27
  openhands/sdk/context/condenser/no_op_condenser.py,sha256=T87bTtJw4dqlOIZZZ4R_JFPXeSymDqlbsZtH6ng7N1E,474
28
28
  openhands/sdk/context/condenser/pipeline_condenser.py,sha256=wkbEA6R8u8u3Wi1AQmx1AKF9hQ25dLSeKuvB56N1Ohc,2145
29
29
  openhands/sdk/context/condenser/utils.py,sha256=kI4oechGeozHRTFPcq6UVbGgLL-6msR3D2-4fPssFVU,5599
@@ -32,16 +32,16 @@ openhands/sdk/context/prompts/__init__.py,sha256=wC1Qiak4RHY3YS0TuZRmhFGAJ7Vc6CL
32
32
  openhands/sdk/context/prompts/prompt.py,sha256=8ZV9C0lg9Lnw7lF5zLnLTF9nL6gTnW5vGIw1YxtRQBU,3724
33
33
  openhands/sdk/context/prompts/templates/ask_agent_template.j2,sha256=VRKWdF2VTJ_Tyway_Wexp8_KlNgAkME94eZelbbsEZI,212
34
34
  openhands/sdk/context/prompts/templates/skill_knowledge_info.j2,sha256=boqEsAIszJ2Cy7Zc7UfFLbwSXgFybZ4mU8YCCW-zXYc,423
35
- openhands/sdk/context/prompts/templates/system_message_suffix.j2,sha256=4OWDu6YAXLQwmFLiIRJ7gEaR8z_RXYlvasmn1zeFP2g,2608
35
+ openhands/sdk/context/prompts/templates/system_message_suffix.j2,sha256=MJYiCSm8gq--6HKt74baDTarexbG4vQdo6Ba1nvjSHA,2911
36
36
  openhands/sdk/context/skills/__init__.py,sha256=dS6XJYLHlm_wboD-C42bRw4HCFu-o9dzCiFRpoyR1h0,938
37
37
  openhands/sdk/context/skills/exceptions.py,sha256=tVBSbXTXG32nb1TebVAuzbNNHvn8GScpSM1bHCnQzvY,305
38
- openhands/sdk/context/skills/skill.py,sha256=s_krfMmqELS1YD24EZyU_vqUs-pmNUO1jZn8txeR8pY,36473
38
+ openhands/sdk/context/skills/skill.py,sha256=rSXEW8VN0Fw4SDI1PU6JY3MiCT3EIv9AR2DUumLBguw,35348
39
39
  openhands/sdk/context/skills/trigger.py,sha256=ZGaDmMpJghnAEuTTYX6UepsA5nX1CSz83zK1Ox46vMk,756
40
40
  openhands/sdk/context/skills/types.py,sha256=LvyCveHBSt2-g9Lbpr_eQMvOd4eEBjJb3irAWL-OzE0,1813
41
41
  openhands/sdk/context/skills/utils.py,sha256=kpJjVz_BQGjFrgO8QpBwTAqHbAU8spD6crOLI_ollac,11870
42
42
  openhands/sdk/conversation/__init__.py,sha256=USxX0PTUI9ufA8CJ8iyDkIaKR5iFbDo3L9G5tBx3k94,1509
43
43
  openhands/sdk/conversation/base.py,sha256=btRjqHk4FkAJjIAi34fgkNUml9R-0qfsDsJgomlN-kI,9143
44
- openhands/sdk/conversation/conversation.py,sha256=7wvXabfbb1iT1th8ntsjnduTdZSwM93yhdYz1wWKa4M,6449
44
+ openhands/sdk/conversation/conversation.py,sha256=PPoR13vFo_9DeIyeW82cxGonDpoOvKjSsQi5OrqR4Zk,6660
45
45
  openhands/sdk/conversation/conversation_stats.py,sha256=ZlQ99kgG5YVCrZ4rqJlq63JaiInxX8jqv-q5lS7RN68,3038
46
46
  openhands/sdk/conversation/event_store.py,sha256=JZF6AibFezcIzEw4IQqKHG8T8s53SfD_LkZycsOH6xY,7992
47
47
  openhands/sdk/conversation/events_list_base.py,sha256=n_YvgbhBPOPDbw4Kp68J0EKFM39vg95ng09GMfTz29s,505
@@ -52,12 +52,12 @@ openhands/sdk/conversation/response_utils.py,sha256=rPlC3cDSmoQte6NZ0kK6h6-9ho5c
52
52
  openhands/sdk/conversation/secret_registry.py,sha256=6fY1zRxb55rC4uIMFcR0lDssIyyjaPh9pCWGqDikrek,4446
53
53
  openhands/sdk/conversation/serialization_diff.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  openhands/sdk/conversation/state.py,sha256=VF3PD2THqgci6mA-meMaNGpB1KuCEW_4AOtHEIQ0flQ,17557
55
- openhands/sdk/conversation/stuck_detector.py,sha256=4UNU7Bh9M2yrw3RavMzZeFGQ5RutqavkckW3exFPqQw,11673
55
+ openhands/sdk/conversation/stuck_detector.py,sha256=p8DljC-YwAVpAUTUqcLy2CY1hTSdpKzN9f6iKJbxs8I,12185
56
56
  openhands/sdk/conversation/title_utils.py,sha256=j40-dP-Oes-mhU2xUC7fCC8cB0wkMdbbDJU7WLHiVIo,7063
57
57
  openhands/sdk/conversation/types.py,sha256=q_yc3VNc8r3cdmvPXzpj7HvdLeDqv-37hCgOWMU65a4,1507
58
58
  openhands/sdk/conversation/impl/__init__.py,sha256=DmDFyNR4RU8eiMocKf2j9eBQomipP-rrJgU1LoVWTDA,220
59
- openhands/sdk/conversation/impl/local_conversation.py,sha256=wqTmwB7TvNeB1wEosv93xcVH08h4TqGcht-xWSdTfsI,36930
60
- openhands/sdk/conversation/impl/remote_conversation.py,sha256=E9JGKtELXWVRCVptgBnRD1M_1QaSc_m7QugvHQ8L5DY,43536
59
+ openhands/sdk/conversation/impl/local_conversation.py,sha256=p7ETqzDhwxdW076TCOKHcxZuEDP7NrbERkSJSG2tZZg,37183
60
+ openhands/sdk/conversation/impl/remote_conversation.py,sha256=g5cQu0WCIWHjAJz3Zp0cMtItVCd81mfLpPgvnbLdyrU,43960
61
61
  openhands/sdk/conversation/visualizer/__init__.py,sha256=0LXpKlt2eJcrqP1z6jQP_nLx23V8ErnQkKYSxvUp0_A,275
62
62
  openhands/sdk/conversation/visualizer/base.py,sha256=oMg-JvQc34ebYdC3J9itHraoB2u3MdQ6E77AKiTmu30,3198
63
63
  openhands/sdk/conversation/visualizer/default.py,sha256=k-3-l1j8H_EOEn_pfzsUczcc4JDhQni7AUQZgZ2TpB4,11885
@@ -104,12 +104,15 @@ openhands/sdk/io/base.py,sha256=m-tMq3Em4DOd17cP0T6-gc9c_7FE-HakcPLScGGycVE,2958
104
104
  openhands/sdk/io/cache.py,sha256=TZD9Px-WK303WGjm5nin9n3TCKGiAJ-PIqrM5MFeg5c,2908
105
105
  openhands/sdk/io/local.py,sha256=5ZibVqYj7hV5gpgYr2SKq2aSsAL1m2JykhtmRyVKyf8,5189
106
106
  openhands/sdk/io/memory.py,sha256=_GQ8WqilLJgdxtRccfVS653VDy1bo6CyZJDHGUQXEm8,2793
107
- openhands/sdk/llm/__init__.py,sha256=k8UneyfoDUMe0lSP4GSlYzrL2Fe3MkDUKpSg2OIDi_I,1206
108
- openhands/sdk/llm/llm.py,sha256=V1zOje01vwlsVmtJIswvWWOP2firwM9sIZNxojqc_l0,45808
107
+ openhands/sdk/llm/__init__.py,sha256=t7kEGnNq4eYREz5h2n1Pizte8glUnruHTt_G_6DguYk,1525
108
+ openhands/sdk/llm/llm.py,sha256=rQpiHy-clgGqN9fms4RaSIkJ7nV5HdXvgMCq95di89Y,52102
109
109
  openhands/sdk/llm/llm_registry.py,sha256=DL9yqSbAM7OBkzdIChLuxG2qk_oElW2tC2xem6mq0F8,3530
110
110
  openhands/sdk/llm/llm_response.py,sha256=DaBVBkij4Sz-RsYhRb3UUcvJCTzCBcOYQ9IhFwN4ukI,1988
111
111
  openhands/sdk/llm/message.py,sha256=YK--c42j6Pb7wjUrPeIfiRRIt0w0pmJMHvGIZC3ugO4,27085
112
112
  openhands/sdk/llm/streaming.py,sha256=tFJ7B0AjJ-e8Xv13DTtc2FdrsLRUCG8wxQex8fDlOp4,214
113
+ openhands/sdk/llm/auth/__init__.py,sha256=GDGU9D6a-o6bXSlVC8NUMxViXJfmEr55HMRosog9F_k,698
114
+ openhands/sdk/llm/auth/credentials.py,sha256=uPWqBCd26snW5Afuinzxy6I-HlG38qXZ9o_hD5iKc64,5167
115
+ openhands/sdk/llm/auth/openai.py,sha256=TqXKl9MdmaI_j3RP-3ayYRnudlOmsafrpfRPVj8go28,26228
113
116
  openhands/sdk/llm/exceptions/__init__.py,sha256=6iMJah2nS6BboU06HqgAM2JT6aykCWY8muoUwaaJpR8,1144
114
117
  openhands/sdk/llm/exceptions/classifier.py,sha256=pu5fVNubUrB3eXV1i5W7m4-D4Ik2Z-fGe1ba2t0SSc4,1456
115
118
  openhands/sdk/llm/exceptions/mapping.py,sha256=D68duh85HPONw5P0Mv4e8Ji-ubi6qWEb2d9FkOKf8cA,1656
@@ -119,13 +122,13 @@ openhands/sdk/llm/mixins/non_native_fc.py,sha256=KL2-rCh9uNz5WDskP7rc2uv1xOzZWgv
119
122
  openhands/sdk/llm/options/__init__.py,sha256=EntvOWC5kwDoTMXXMkYuoWMQ13hD8YtC9CEMCtnKj7o,54
120
123
  openhands/sdk/llm/options/chat_options.py,sha256=9xB4I4DXBFirM0ZCrc8-KwW5eD07IAbYHglkJjTczuE,3784
121
124
  openhands/sdk/llm/options/common.py,sha256=qFcPuZF_c4rmH1bgGG8Qp6TJ4YWpv9IFzfLZRRiik9M,580
122
- openhands/sdk/llm/options/responses_options.py,sha256=1ff_06XcORneDQUjwrEM6tel9HLI9_veMcoKHYAKLG8,2302
125
+ openhands/sdk/llm/options/responses_options.py,sha256=EYdzhBEJ7H722yNBOaiBz_slfaxgq3nnDSYqBg-CFRM,2498
123
126
  openhands/sdk/llm/router/__init__.py,sha256=N8qldpGdLLCWZzn5Rz2y8AheSoCTQLGkLOBDCFNMJRA,261
124
127
  openhands/sdk/llm/router/base.py,sha256=izGHV17UctI70t2OjhuLgVAV7gGZ51vbZ4WpWJxblJA,4678
125
128
  openhands/sdk/llm/router/impl/multimodal.py,sha256=uKFVm7b3Jr0xCC1lvP-cVn-fIkTv24-pK3xKlOJahz4,3034
126
129
  openhands/sdk/llm/router/impl/random.py,sha256=oBHoFTBMa9OeDyg-rV4siLCkN6rKYL0uDlZMEseB3ro,656
127
130
  openhands/sdk/llm/utils/metrics.py,sha256=4zD0Hkc9Oc4qcDcVZUX13RyggyObshUbz4Ik9W1uIw4,11592
128
- openhands/sdk/llm/utils/model_features.py,sha256=y-l501P5tbIvY-Vxi5TeE4QyobW9-xRMW0AzCKTidMw,6295
131
+ openhands/sdk/llm/utils/model_features.py,sha256=SDEAn4YCYFkw4QTQmtiOyKIw9AI895nrhzYBxFfaP3g,6336
129
132
  openhands/sdk/llm/utils/model_info.py,sha256=1mFYA7OcEyUB6k1doao8_w1XT7UMM_DAm57HcTpKkLw,2628
130
133
  openhands/sdk/llm/utils/model_prompt_spec.py,sha256=onw9-y7x0aJS8IOjNzeqhdvcFNwK1l_s0XgurWlnj5o,2587
131
134
  openhands/sdk/llm/utils/retry_mixin.py,sha256=M-hXp8EwP1FjNN6tgHiv133BtUQgRr9Kz_ZWxeAJLGA,4765
@@ -150,7 +153,7 @@ openhands/sdk/plugin/loader.py,sha256=PcrCOn5WH_d5cf_qXEDopD2dLx7K0i3sErGuN_Xl35
150
153
  openhands/sdk/plugin/plugin.py,sha256=_QrrLl6xjHxcF31jnsohjb6R3Sb6Q8IZwblSdzY8sPE,17851
151
154
  openhands/sdk/plugin/types.py,sha256=hcpLaYz-sTJ7Dby1_BwTdm3Uq-UnIlHWHacjT0nXXyo,28581
152
155
  openhands/sdk/secret/__init__.py,sha256=Y-M2WPfYLfVYZSFZGTf6MDOnjsL5SayETtA4t9X0gjw,348
153
- openhands/sdk/secret/secrets.py,sha256=RFhyTBxwGgeYoojD56DtI_4CL43R8emo8WzvUH-8Z4w,3281
156
+ openhands/sdk/secret/secrets.py,sha256=e9ClIvP7sqxOeaazUjGSzzof1HCxzRwoJikYJUhtRQY,3609
154
157
  openhands/sdk/security/__init__.py,sha256=x2-a0fsImxSM2msHl7gV0fENWyZbpY7-HW_CBwNrPZY,89
155
158
  openhands/sdk/security/analyzer.py,sha256=V6Fowh83sfEr1SdwK3UW1PNQHahHxwHfla0KndTombk,3994
156
159
  openhands/sdk/security/confirmation_policy.py,sha256=bsrIOfo3QOXNyKrmUVzQz070F3xbJ7uZp9WTr4RMdl8,2145
@@ -185,9 +188,9 @@ openhands/sdk/workspace/models.py,sha256=ORFn70kXvD0urjeq59d_XqXBYSHbDYkBMg9KYQK
185
188
  openhands/sdk/workspace/workspace.py,sha256=Of1r2z3W_pXSgV8u10sUQ87ChtaA5khD6HJ_PtJz6aY,1296
186
189
  openhands/sdk/workspace/remote/__init__.py,sha256=eKkj6NOESMUBGDVC6_L2Wfuc4K6G-mpnJDNHKBkSeUI,114
187
190
  openhands/sdk/workspace/remote/async_remote_workspace.py,sha256=MfnYoXvx_tZ7MKDGJCofnkYAJxfBKqNtM2Qprx3QQRk,5608
188
- openhands/sdk/workspace/remote/base.py,sha256=t-qbouLxkAsPKvVblXWWHKixHsLFz4bw3l9LW_n9_6o,6152
189
- openhands/sdk/workspace/remote/remote_workspace_mixin.py,sha256=CzHfnLUIra5sgPkP9kcggb1vHGOPpYQzLsHvGO2rRt0,10963
190
- openhands_sdk-1.10.0.dist-info/METADATA,sha256=c60-7ef_Nqg959qhqZ9lCr8Dv2FMaD0xpICRop3QX-Q,859
191
- openhands_sdk-1.10.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
192
- openhands_sdk-1.10.0.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
193
- openhands_sdk-1.10.0.dist-info/RECORD,,
191
+ openhands/sdk/workspace/remote/base.py,sha256=5xqhJf_Hi3kMdNfL4u0XScnVShTRUSeMtvIAemR6Q3A,6317
192
+ openhands/sdk/workspace/remote/remote_workspace_mixin.py,sha256=29Mwe6lVZ-annN0lThRY2cM-FrqXhLm1uT_4j72e7fw,12600
193
+ openhands_sdk-1.11.0.dist-info/METADATA,sha256=L9sD54xo2JltGA6PFXQtMuJRpOZkTxPpoTH4HRa3u44,859
194
+ openhands_sdk-1.11.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
195
+ openhands_sdk-1.11.0.dist-info/top_level.txt,sha256=jHgVu9I0Blam8BXFgedoGKfglPF8XvW1TsJFIjcgP4E,10
196
+ openhands_sdk-1.11.0.dist-info/RECORD,,