|
9 | 9 | from pydantic import ( |
10 | 10 | BaseModel, |
11 | 11 | Field, |
12 | | - PrivateAttr, |
13 | 12 | SecretStr, |
14 | 13 | field_serializer, |
15 | 14 | field_validator, |
| 15 | + model_serializer, |
16 | 16 | ) |
17 | 17 | from pydantic.fields import FieldInfo |
18 | 18 |
|
19 | 19 | from openhands.sdk.context.agent_context import AgentContext |
20 | 20 | from openhands.sdk.llm import LLM |
21 | 21 | from openhands.sdk.tool import Tool |
22 | | -from openhands.sdk.utils.deprecation import warn_deprecated |
23 | 22 |
|
24 | 23 | from .metadata import ( |
25 | 24 | SETTINGS_METADATA_KEY, |
@@ -109,9 +108,6 @@ class CondenserSettings(BaseModel): |
109 | 108 | class VerificationSettings(BaseModel): |
110 | 109 | """Critic and iterative-refinement settings for the agent.""" |
111 | 110 |
|
112 | | - _deprecated_confirmation_mode: bool = PrivateAttr(default=False) |
113 | | - _deprecated_security_analyzer: str | None = PrivateAttr(default=None) |
114 | | - |
115 | 111 | # -- Critic -- |
116 | 112 | critic_enabled: bool = Field( |
117 | 113 | default=False, |
@@ -202,63 +198,36 @@ class VerificationSettings(BaseModel): |
202 | 198 | }, |
203 | 199 | ) |
204 | 200 |
|
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 |
262 | 231 |
|
263 | 232 |
|
264 | 233 | def _default_llm_settings() -> LLM: |
@@ -550,6 +519,19 @@ def settings_metadata(field: FieldInfo) -> SettingsFieldMetadata | None: |
550 | 519 | _GENERAL_SECTION_LABEL = "General" |
551 | 520 |
|
552 | 521 |
|
| 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 | + |
553 | 535 | def export_settings_schema(model: type[BaseModel]) -> SettingsSchema: |
554 | 536 | """Export a structured settings schema for a Pydantic settings model. |
555 | 537 |
|
@@ -582,7 +564,9 @@ def ensure_section(key: str, label: str) -> SettingsSectionSchema: |
582 | 564 | ) |
583 | 565 | section = ensure_section(section_metadata.key, section_label) |
584 | 566 | 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 | + ): |
586 | 570 | continue |
587 | 571 | metadata = settings_metadata(nested_field) |
588 | 572 | default_value = None |
|
0 commit comments