mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
* fix(agents): treat a null max_total_subagents override as unset An API caller sending "max_total_subagents": null in the run context failed that run with a TypeError: the key was present, so dict.get(key, default) returned None and building SubagentLimitMiddleware compared None with an int. Resolve the per-run delegation cap through one helper, effective_total_subagents_per_run, which treats None as "use subagents.max_total_per_run" and clamps to 1-50. The lead agent's middleware and assembly paths, DeerFlowClient, and the system prompt all use it, so the extension host policy and release policy now report the enforced cap instead of an out-of-range request. * docs(changelog): reference #6088 in the null max_total_subagents entry
68 lines
3.0 KiB
Python
68 lines
3.0 KiB
Python
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from deerflow.config.app_config import AppConfig
|
|
from deerflow.config.reload_boundary import STARTUP_ONLY_FIELDS, STARTUP_ONLY_PREFIX
|
|
from deerflow.config.subagent_batches_config import SubagentBatchesConfig
|
|
from deerflow.config.subagent_runtime_config import SubagentRuntimeConfig
|
|
from deerflow.config.subagents_config import SubagentsAppConfig, effective_subagent_concurrency, effective_total_subagents_per_run
|
|
|
|
|
|
def test_subagent_runtime_defaults_are_safe_and_bounded() -> None:
|
|
config = SubagentRuntimeConfig()
|
|
assert config.max_running == 3
|
|
assert config.max_queued == 64
|
|
assert config.admission_policy == "queue"
|
|
assert config.queue_timeout_seconds == 300
|
|
|
|
with pytest.raises(ValidationError):
|
|
SubagentRuntimeConfig(max_running=0)
|
|
with pytest.raises(ValidationError):
|
|
SubagentRuntimeConfig(max_running=65)
|
|
with pytest.raises(ValidationError):
|
|
SubagentRuntimeConfig(max_queued=10_001)
|
|
|
|
|
|
def test_subagent_batch_defaults_separate_total_live_and_running() -> None:
|
|
config = SubagentBatchesConfig()
|
|
assert config.enabled is False
|
|
assert config.max_items_per_batch == 5_000
|
|
assert config.default_max_live_items == 100
|
|
assert config.default_max_running_items == 3
|
|
|
|
with pytest.raises(ValidationError):
|
|
SubagentBatchesConfig(default_max_live_items=5, default_max_running_items=6)
|
|
with pytest.raises(ValidationError):
|
|
SubagentBatchesConfig(max_live_items_per_batch=4, default_max_live_items=5)
|
|
|
|
|
|
def test_subagent_capacity_sections_are_startup_only() -> None:
|
|
for name in ("subagent_runtime", "subagent_batches"):
|
|
assert name in STARTUP_ONLY_FIELDS
|
|
description = AppConfig.model_fields[name].description or ""
|
|
assert description.startswith(STARTUP_ONLY_PREFIX)
|
|
|
|
|
|
def test_ordinary_task_limit_never_advertises_more_than_real_process_slots() -> None:
|
|
config = SimpleNamespace(subagent_runtime=SubagentRuntimeConfig(max_running=8))
|
|
assert effective_subagent_concurrency(None, config) == 8
|
|
assert effective_subagent_concurrency(4, config) == 4
|
|
assert effective_subagent_concurrency(50, config) == 8
|
|
|
|
|
|
def test_ordinary_task_limit_uses_frozen_execution_capacity_after_reload() -> None:
|
|
reloaded_config = SimpleNamespace(subagent_runtime=SubagentRuntimeConfig(max_running=12))
|
|
assert effective_subagent_concurrency(None, reloaded_config, execution_capacity=3) == 3
|
|
assert effective_subagent_concurrency(10, reloaded_config, execution_capacity=3) == 3
|
|
|
|
|
|
def test_per_run_delegation_cap_treats_explicit_null_as_unset() -> None:
|
|
config = SimpleNamespace(subagents=SubagentsAppConfig(max_total_per_run=7))
|
|
assert effective_total_subagents_per_run(None, config) == 7
|
|
assert effective_total_subagents_per_run(5, config) == 5
|
|
assert effective_total_subagents_per_run(99, config) == 50
|
|
assert effective_total_subagents_per_run(0, config) == 1
|
|
assert effective_total_subagents_per_run(None, SimpleNamespace()) == 6
|