Skip to content

Commit 3890fc4

Browse files
fix(sdk): hide legacy verification fields from dumps
Co-authored-by: openhands <openhands@all-hands.dev>
1 parent 9caf8c8 commit 3890fc4

File tree

2 files changed

+54
-79
lines changed

2 files changed

+54
-79
lines changed

openhands-sdk/openhands/sdk/settings/model.py

Lines changed: 47 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,16 @@
99
from pydantic import (
1010
BaseModel,
1111
Field,
12-
PrivateAttr,
1312
SecretStr,
1413
field_serializer,
1514
field_validator,
15+
model_serializer,
1616
)
1717
from pydantic.fields import FieldInfo
1818

1919
from openhands.sdk.context.agent_context import AgentContext
2020
from openhands.sdk.llm import LLM
2121
from openhands.sdk.tool import Tool
22-
from openhands.sdk.utils.deprecation import warn_deprecated
2322

2423
from .metadata import (
2524
SETTINGS_METADATA_KEY,
@@ -109,9 +108,6 @@ class CondenserSettings(BaseModel):
109108
class VerificationSettings(BaseModel):
110109
"""Critic and iterative-refinement settings for the agent."""
111110

112-
_deprecated_confirmation_mode: bool = PrivateAttr(default=False)
113-
_deprecated_security_analyzer: str | None = PrivateAttr(default=None)
114-
115111
# -- Critic --
116112
critic_enabled: bool = Field(
117113
default=False,
@@ -202,63 +198,36 @@ class VerificationSettings(BaseModel):
202198
},
203199
)
204200

205-
@property
206-
def confirmation_mode(self) -> bool:
207-
warn_deprecated(
208-
"VerificationSettings.confirmation_mode",
209-
deprecated_in="1.16.1",
210-
removed_in="1.18.0",
211-
details=(
212-
"Use ConversationSettings.confirmation_mode; "
213-
"verification settings no longer serialize "
214-
"conversation approval controls."
215-
),
216-
)
217-
return self._deprecated_confirmation_mode
218-
219-
@confirmation_mode.setter
220-
def confirmation_mode(self, value: bool) -> None:
221-
warn_deprecated(
222-
"VerificationSettings.confirmation_mode",
223-
deprecated_in="1.16.1",
224-
removed_in="1.18.0",
225-
details=(
226-
"Use ConversationSettings.confirmation_mode; "
227-
"verification settings no longer serialize "
228-
"conversation approval controls."
229-
),
230-
stacklevel=3,
231-
)
232-
self._deprecated_confirmation_mode = value
233-
234-
@property
235-
def security_analyzer(self) -> str | None:
236-
warn_deprecated(
237-
"VerificationSettings.security_analyzer",
238-
deprecated_in="1.16.1",
239-
removed_in="1.18.0",
240-
details=(
241-
"Use ConversationSettings.security_analyzer; "
242-
"verification settings no longer serialize "
243-
"conversation security controls."
244-
),
245-
)
246-
return self._deprecated_security_analyzer
247-
248-
@security_analyzer.setter
249-
def security_analyzer(self, value: str | None) -> None:
250-
warn_deprecated(
251-
"VerificationSettings.security_analyzer",
252-
deprecated_in="1.16.1",
253-
removed_in="1.18.0",
254-
details=(
255-
"Use ConversationSettings.security_analyzer; "
256-
"verification settings no longer serialize "
257-
"conversation security controls."
258-
),
259-
stacklevel=3,
260-
)
261-
self._deprecated_security_analyzer = value
201+
# -- Security --
202+
confirmation_mode: bool = Field(
203+
default=False,
204+
description="Require user confirmation before executing risky actions.",
205+
json_schema_extra={
206+
SETTINGS_METADATA_KEY: SettingsFieldMetadata(
207+
label="Confirmation mode",
208+
prominence=SettingProminence.MAJOR,
209+
).model_dump()
210+
},
211+
)
212+
security_analyzer: SecurityAnalyzerType | None = Field(
213+
default=None,
214+
description="Security analyzer that evaluates actions before execution.",
215+
json_schema_extra={
216+
SETTINGS_METADATA_KEY: SettingsFieldMetadata(
217+
label="Security analyzer",
218+
prominence=SettingProminence.MAJOR,
219+
depends_on=("confirmation_mode",),
220+
).model_dump()
221+
},
222+
)
223+
224+
@model_serializer(mode="wrap")
225+
def _serialize(self, handler):
226+
payload = handler(self)
227+
if isinstance(payload, dict):
228+
payload.pop("confirmation_mode", None)
229+
payload.pop("security_analyzer", None)
230+
return payload
262231

263232

264233
def _default_llm_settings() -> LLM:
@@ -550,6 +519,19 @@ def settings_metadata(field: FieldInfo) -> SettingsFieldMetadata | None:
550519
_GENERAL_SECTION_LABEL = "General"
551520

552521

522+
_HIDDEN_SETTINGS_SCHEMA_FIELDS: dict[type[BaseModel], set[str]] = {
523+
VerificationSettings: {"confirmation_mode", "security_analyzer"},
524+
}
525+
526+
527+
def _is_hidden_settings_schema_field(
528+
model: type[BaseModel], field_name: str, field: FieldInfo
529+
) -> bool:
530+
if field.exclude:
531+
return True
532+
return field_name in _HIDDEN_SETTINGS_SCHEMA_FIELDS.get(model, set())
533+
534+
553535
def export_settings_schema(model: type[BaseModel]) -> SettingsSchema:
554536
"""Export a structured settings schema for a Pydantic settings model.
555537
@@ -582,7 +564,9 @@ def ensure_section(key: str, label: str) -> SettingsSectionSchema:
582564
)
583565
section = ensure_section(section_metadata.key, section_label)
584566
for nested_key, nested_field in nested_model.model_fields.items():
585-
if nested_field.exclude:
567+
if _is_hidden_settings_schema_field(
568+
nested_model, nested_key, nested_field
569+
):
586570
continue
587571
metadata = settings_metadata(nested_field)
588572
default_value = None

tests/sdk/test_settings.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import pytest
21
from fastmcp.mcp_config import MCPConfig
32
from pydantic import SecretStr
43

@@ -145,22 +144,14 @@ def test_conversation_settings_export_schema_groups_sections() -> None:
145144
assert verification_fields["security_analyzer"].depends_on == ["confirmation_mode"]
146145

147146

148-
def test_verification_settings_legacy_controls_remain_deprecated_accessors() -> None:
149-
settings = VerificationSettings()
150-
151-
with pytest.deprecated_call():
152-
assert settings.confirmation_mode is False
153-
with pytest.deprecated_call():
154-
settings.confirmation_mode = True
155-
with pytest.deprecated_call():
156-
assert settings.confirmation_mode is True
147+
def test_verification_settings_legacy_controls_are_excluded_from_dump() -> None:
148+
settings = VerificationSettings(
149+
confirmation_mode=True,
150+
security_analyzer="llm",
151+
)
157152

158-
with pytest.deprecated_call():
159-
assert settings.security_analyzer is None
160-
with pytest.deprecated_call():
161-
settings.security_analyzer = "llm"
162-
with pytest.deprecated_call():
163-
assert settings.security_analyzer == "llm"
153+
assert settings.confirmation_mode is True
154+
assert settings.security_analyzer == "llm"
164155

165156
dumped = settings.model_dump(mode="json")
166157
assert "confirmation_mode" not in dumped

0 commit comments

Comments
 (0)