-
Notifications
You must be signed in to change notification settings - Fork 56
Refactor: Always include risk fields #1052
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
da4ca8a
0e5b697
e087266
7b5a9dd
c5e6329
90eeb48
9c4a85a
42a2bfc
e1e612c
951a405
35ce7db
625e509
1106bdf
51ad9dc
e3ab2a2
40d02df
9ac3455
f5c0d4d
e188cf3
480e53d
160a6a2
8405598
01646be
248648f
2402da2
4b3e817
4d58824
e222455
66439c7
2077d67
7d33f82
a0b1869
17ae5fe
503e577
360ee72
fdfedb5
4690734
e42ad06
83bfee7
32447c9
0ddccd1
e4b2aea
2da10e7
9a5cc2c
ba70f91
307e650
abc199a
7db47ad
f00c4ac
f62ff13
a3303e3
d8c66e5
72b3255
4ffa36e
f260741
7ac1017
51d30cb
eb8626c
816bb10
b1662d0
533f7b0
f314857
090ae58
eca4672
f973187
b5774dd
d60da37
41101cc
c7d5438
7b7a325
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,20 @@ | ||
| import os | ||
| import re | ||
| import sys | ||
| import warnings | ||
| from abc import ABC, abstractmethod | ||
| from collections.abc import Generator, Iterable | ||
| from typing import TYPE_CHECKING, Any | ||
|
|
||
| from pydantic import BaseModel, ConfigDict, Field, PrivateAttr | ||
| from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, model_validator | ||
|
|
||
| import openhands.sdk.security.analyzer as analyzer | ||
| from openhands.sdk.context.agent_context import AgentContext | ||
| from openhands.sdk.context.condenser import CondenserBase, LLMSummarizingCondenser | ||
| from openhands.sdk.context.prompts.prompt import render_template | ||
| from openhands.sdk.llm import LLM | ||
| from openhands.sdk.logger import get_logger | ||
| from openhands.sdk.mcp import create_mcp_tools | ||
| from openhands.sdk.security.llm_analyzer import LLMSecurityAnalyzer | ||
| from openhands.sdk.security import analyzer | ||
| from openhands.sdk.tool import BUILT_IN_TOOLS, Tool, ToolDefinition, resolve_tool | ||
| from openhands.sdk.utils.models import DiscriminatedUnionMixin | ||
| from openhands.sdk.utils.pydantic_diff import pretty_pydantic_diff | ||
|
|
@@ -27,6 +27,13 @@ | |
| logger = get_logger(__name__) | ||
|
|
||
|
|
||
| AGENT_SECURITY_ANALYZER_DEPRECATION_WARNING = ( | ||
| "Agent.security_analyzer is deprecated and will be removed " | ||
| "in a future release.\n\n use `conversation = Conversation();" | ||
| "conversation.set_security_analyzer(...)` instead." | ||
| ) | ||
|
|
||
|
|
||
| class AgentBase(DiscriminatedUnionMixin, ABC): | ||
| """Abstract base class for OpenHands agents. | ||
|
|
||
|
|
@@ -122,11 +129,13 @@ class AgentBase(DiscriminatedUnionMixin, ABC): | |
| description="Optional kwargs to pass to the system prompt Jinja2 template.", | ||
| examples=[{"cli_mode": True}], | ||
| ) | ||
|
|
||
| security_analyzer: analyzer.SecurityAnalyzerBase | None = Field( | ||
| default=None, | ||
| description="Optional security analyzer to evaluate action risks.", | ||
| examples=[{"kind": "LLMSecurityAnalyzer"}], | ||
| ) | ||
|
|
||
| condenser: CondenserBase | None = Field( | ||
| default=None, | ||
| description="Optional condenser to use for condensing conversation history.", | ||
|
|
@@ -147,6 +156,22 @@ class AgentBase(DiscriminatedUnionMixin, ABC): | |
| # Runtime materialized tools; private and non-serializable | ||
| _tools: dict[str, ToolDefinition] = PrivateAttr(default_factory=dict) | ||
|
|
||
| @model_validator(mode="before") | ||
| @classmethod | ||
| def _coerce_inputs(cls, data): | ||
| if not isinstance(data, dict): | ||
| return data | ||
| d = dict(data) | ||
|
|
||
| if "security_analyzer" in d and d["security_analyzer"]: | ||
| warnings.warn( | ||
| AGENT_SECURITY_ANALYZER_DEPRECATION_WARNING, | ||
| DeprecationWarning, | ||
| stacklevel=3, | ||
| ) | ||
|
Comment on lines
+166
to
+171
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @OpenHands is there any good python library that allow us to manage deprecation warning in a more systematic way? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm on it! xingyaoww can track my progress at all-hands.dev There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SummaryI answered your question about Python libraries for managing deprecation warnings systematically. Here are the recommendations: Top 3 Recommended Libraries:
Why
|
||
|
|
||
| return d | ||
|
|
||
| @property | ||
| def prompt_dir(self) -> str: | ||
| """Returns the directory where this class's module file is located.""" | ||
|
|
@@ -164,13 +189,7 @@ def name(self) -> str: | |
| @property | ||
| def system_message(self) -> str: | ||
| """Compute system message on-demand to maintain statelessness.""" | ||
| # Prepare template kwargs, including cli_mode if available | ||
| template_kwargs = dict(self.system_prompt_kwargs) | ||
| if self.security_analyzer: | ||
| template_kwargs["llm_security_analyzer"] = bool( | ||
| isinstance(self.security_analyzer, LLMSecurityAnalyzer) | ||
| ) | ||
|
|
||
| system_message = render_template( | ||
| prompt_dir=self.prompt_dir, | ||
| template_name=self.system_prompt_filename, | ||
|
|
@@ -198,6 +217,16 @@ def init_state( | |
|
|
||
| def _initialize(self, state: "ConversationState"): | ||
| """Create an AgentBase instance from an AgentSpec.""" | ||
|
|
||
| # 1) Migrate deprecated analyzer → state (if present) | ||
| if self.security_analyzer and not state.security_analyzer: | ||
| state.security_analyzer = self.security_analyzer | ||
| # 2) Clear on the immutable model (allowed via object.__setattr__) | ||
| try: | ||
| object.__setattr__(self, "security_analyzer", None) | ||
| except Exception: | ||
| logger.warning("Could not clear deprecated Agent.security_analyzer") | ||
|
|
||
| if self._tools: | ||
| logger.warning("Agent already initialized; skipping re-initialization.") | ||
| return | ||
|
|
@@ -297,8 +326,6 @@ def resolve_diff_from_deserialized(self, persisted: "AgentBase") -> "AgentBase": | |
| updates["condenser"] = new_condenser | ||
|
|
||
| # Allow security_analyzer to differ - use the runtime (self) version | ||
| # This allows users to add/remove security analyzers mid-conversation | ||
| # (e.g., when switching to weaker LLMs that can't handle security_risk field) | ||
| updates["security_analyzer"] = self.security_analyzer | ||
|
|
||
| # Create maps by tool name for easy lookup | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.