mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
* refactor(config): converge the boolean guards into one shared helper Willem-bd's #6171 review follow-up: a tiny shared helper lets the next numeric field get the guard in one line and unifies the messages. Consolidates 14 validator copies across 9 config files into deerflow/config/_boolean_guards.reject_boolean(value, info, kind=...); the bare per-file messages gain their field name prefix, command_timeout keeps its distinct wording. Signed-off-by: yetuge <2219677952@qq.com> * refactor(config): converge stream_bridge_config and pin the guard wording Review follow-ups on #6226: convert the last surviving per-file copy (stream_bridge_config) so deerflow/config is fully converged, make kind required keyword-only so a float field cannot silently inherit integer wording, and pin the unified field-prefixed messages with direct tests. Signed-off-by: yetuge <2219677952@qq.com> * test(config): cast the boolean-guard test probe to ValidationInfo The _info probe was annotated -> object, so a type checker flags every call site that passes its result to reject_boolean's info: ValidationInfo parameter (runtime was fine since the helper only reads .field_name). Return cast(ValidationInfo, _Probe(field_name)) to keep the probe honest for both. Signed-off-by: yetuge <2219677952@qq.com> --------- Signed-off-by: yetuge <2219677952@qq.com>
94 lines
3.3 KiB
Python
94 lines
3.3 KiB
Python
"""Configuration for loop detection middleware."""
|
|
|
|
from pydantic import BaseModel, Field, ValidationInfo, field_validator, model_validator
|
|
|
|
from deerflow.config._boolean_guards import reject_boolean
|
|
|
|
|
|
class ToolFreqOverride(BaseModel):
|
|
"""Per-tool frequency threshold override.
|
|
|
|
Can be higher or lower than the global defaults. Commonly used to raise
|
|
thresholds for high-frequency tools like bash in batch workflows (e.g.
|
|
RNA-seq pipelines) without weakening protection on every other tool.
|
|
"""
|
|
|
|
warn: int = Field(ge=1)
|
|
hard_limit: int = Field(ge=1)
|
|
|
|
@field_validator("warn", "hard_limit", mode="before")
|
|
@classmethod
|
|
def reject_boolean_thresholds(cls, value: object, info: ValidationInfo) -> object:
|
|
return reject_boolean(value, info, kind="an integer")
|
|
|
|
@model_validator(mode="after")
|
|
def _validate(self) -> "ToolFreqOverride":
|
|
if self.hard_limit < self.warn:
|
|
raise ValueError("hard_limit must be >= warn")
|
|
return self
|
|
|
|
|
|
class LoopDetectionConfig(BaseModel):
|
|
"""Configuration for repetitive tool-call loop detection."""
|
|
|
|
enabled: bool = Field(
|
|
default=True,
|
|
description="Whether to enable repetitive tool-call loop detection",
|
|
)
|
|
warn_threshold: int = Field(
|
|
default=3,
|
|
ge=1,
|
|
description="Number of identical tool-call sets before injecting a warning",
|
|
)
|
|
hard_limit: int = Field(
|
|
default=5,
|
|
ge=1,
|
|
description="Number of identical tool-call sets before forcing a stop",
|
|
)
|
|
window_size: int = Field(
|
|
default=20,
|
|
ge=1,
|
|
description="Number of recent tool-call sets to track per thread",
|
|
)
|
|
max_tracked_threads: int = Field(
|
|
default=100,
|
|
ge=1,
|
|
description="Maximum number of thread/run histories to keep in memory (legacy field name)",
|
|
)
|
|
tool_freq_warn: int = Field(
|
|
default=30,
|
|
ge=1,
|
|
description="Number of calls to the same tool type before injecting a frequency warning",
|
|
)
|
|
tool_freq_hard_limit: int = Field(
|
|
default=50,
|
|
ge=1,
|
|
description="Number of calls to the same tool type before forcing a stop",
|
|
)
|
|
tool_freq_overrides: dict[str, ToolFreqOverride] = Field(
|
|
default_factory=dict,
|
|
description=("Per-tool overrides for tool_freq_warn / tool_freq_hard_limit, keyed by tool name. Values can be higher or lower than the global defaults. Commonly used to raise thresholds for high-frequency tools like bash."),
|
|
)
|
|
|
|
@field_validator(
|
|
"warn_threshold",
|
|
"hard_limit",
|
|
"window_size",
|
|
"max_tracked_threads",
|
|
"tool_freq_warn",
|
|
"tool_freq_hard_limit",
|
|
mode="before",
|
|
)
|
|
@classmethod
|
|
def reject_boolean_thresholds(cls, value: object, info: ValidationInfo) -> object:
|
|
return reject_boolean(value, info, kind="an integer")
|
|
|
|
@model_validator(mode="after")
|
|
def validate_thresholds(self) -> "LoopDetectionConfig":
|
|
"""Ensure hard stop cannot happen before the warning threshold."""
|
|
if self.hard_limit < self.warn_threshold:
|
|
raise ValueError("hard_limit must be greater than or equal to warn_threshold")
|
|
if self.tool_freq_hard_limit < self.tool_freq_warn:
|
|
raise ValueError("tool_freq_hard_limit must be greater than or equal to tool_freq_warn")
|
|
return self
|