refactor(intelligent-eval): drop ADR-0008 transitional wrappers, callers use domain
All checks were successful
CI / test (push) Successful in 3m8s

架构审查候选④:ADR-0008 收敛调度域时为保测试兼容留下的过渡 wrapper 使命结束。

删除 8 个浅封装:task_queue 的 _is_slot_due / _calculate_session_deficit
(零调用死函数)+ _calculate_priority / _get_attention_reason,decision 的
_parse_time_slot(零调用死函数)+ _get_current_slot / _count_sessions_in_slot /
_has_high_severity_issues。调用方直接使用 domain 模块。

5 个隔着 wrapper 测 domain 行为的测试迁到新文件
test_intelligent_eval_domain.py,直接锁定 domain,覆盖零丢失。
删除测试通过:复杂度直接消失,时段/欠账/优先级知识只剩 domain 一处。
870 tests passed,零行为变化。
This commit is contained in:
sinohqb 2026-08-21 03:50:44 +08:00
parent 58c2ad0227
commit 2a0bcdd185
4 changed files with 152 additions and 186 deletions

View File

@ -6,9 +6,7 @@ Worker analyzes current situation and decides what to do:
- start_analysis: Start analysis (all sessions completed)
"""
from datetime import datetime, timedelta
from enum import Enum
from typing import Optional
from sqlmodel import Session, select
@ -45,32 +43,6 @@ class Decision:
}
def _parse_time_slot(time_slot: str) -> Optional[tuple[float, float]]:
"""Thin wrapper delegating to :func:`agenteval.intelligent_eval.domain.parse_time_slot`."""
from agenteval.intelligent_eval.domain import parse_time_slot as _impl
return _impl(time_slot)
def _get_current_slot(time_distribution: list[dict], current_offset: timedelta) -> Optional[dict]:
"""Thin wrapper delegating to :func:`agenteval.intelligent_eval.domain.get_current_slot`."""
from agenteval.intelligent_eval.domain import get_current_slot as _impl
return _impl(time_distribution, current_offset)
def _count_sessions_in_slot(
eval_id: str, slot: dict, eval_started_at: datetime, session: Session
) -> int:
"""Thin wrapper delegating to :func:`agenteval.intelligent_eval.domain.count_sessions_in_slot`."""
from agenteval.intelligent_eval.domain import count_sessions_in_slot as _impl
return _impl(eval_id, slot, eval_started_at, session)
def _has_high_severity_issues(eval_id: str, session: Session) -> bool:
"""Thin wrapper delegating to :func:`agenteval.intelligent_eval.domain.has_high_severity_issues`."""
from agenteval.intelligent_eval.domain import has_high_severity_issues as _impl
return _impl(eval_id, session)
def make_decision(eval_db: IntelligentEvalDB, session: Session) -> Decision:
"""Make a decision based on current evaluation state.
@ -81,6 +53,12 @@ def make_decision(eval_db: IntelligentEvalDB, session: Session) -> Decision:
Returns:
Decision object
"""
from agenteval.intelligent_eval.domain import (
count_sessions_in_slot,
get_current_slot,
has_high_severity_issues,
)
# Check if eval is still executing
if eval_db.status != IntelligentEvalStatus.EXECUTING.value:
return Decision(
@ -124,7 +102,7 @@ def make_decision(eval_db: IntelligentEvalDB, session: Session) -> Decision:
)
# Get current time slot
current_slot = _get_current_slot(time_distribution, current_offset)
current_slot = get_current_slot(time_distribution, current_offset)
if current_slot is None:
return Decision(
@ -136,7 +114,7 @@ def make_decision(eval_db: IntelligentEvalDB, session: Session) -> Decision:
# Check if current slot has deficit
slot_name = current_slot.get("time_slot", "")
expected_sessions = current_slot.get("sessions", 0)
current_sessions = _count_sessions_in_slot(eval_db.id, current_slot, eval_db.started_at, session)
current_sessions = count_sessions_in_slot(eval_db.id, current_slot, eval_db.started_at, session)
deficit = expected_sessions - current_sessions
@ -153,7 +131,7 @@ def make_decision(eval_db: IntelligentEvalDB, session: Session) -> Decision:
)
# Check if any high severity issues found
if _has_high_severity_issues(eval_db.id, session):
if has_high_severity_issues(eval_db.id, session):
return Decision(
DecisionType.EXECUTE_SESSION,
"发现高严重度问题,需要深入挖掘",

View File

@ -20,34 +20,6 @@ from agenteval.storage.db import (
)
def _is_slot_due(slot: dict, current_offset: timedelta) -> bool:
"""Thin wrapper delegating to :func:`agenteval.intelligent_eval.domain.is_slot_due`."""
from agenteval.intelligent_eval.domain import is_slot_due as _impl
return _impl(slot, current_offset)
def _calculate_session_deficit(eval_db: IntelligentEvalDB, session: Session) -> int:
"""Thin wrapper delegating to :func:`agenteval.intelligent_eval.domain.calculate_session_deficit`."""
from agenteval.intelligent_eval.domain import calculate_session_deficit as _impl
return _impl(eval_db, session)
def _calculate_priority(eval_db: IntelligentEvalDB, session: Session) -> int:
"""Thin wrapper delegating to :func:`agenteval.intelligent_eval.domain.calculate_priority`."""
from agenteval.intelligent_eval.domain import calculate_priority as _impl
return _impl(eval_db, session)
def _get_attention_reason(eval_db: IntelligentEvalDB, session: Session) -> Optional[str]:
"""Thin wrapper delegating to :func:`agenteval.intelligent_eval.domain.get_attention_reason`."""
from agenteval.intelligent_eval.domain import get_attention_reason as _impl
return _impl(eval_db, session)
def _has_pending_task(eval_id: str, session: Session) -> bool:
"""Check if eval already has a pending/assigned task (去重)."""
existing = session.exec(
@ -65,6 +37,8 @@ def scan_and_enqueue_tasks(session: Session) -> int:
Returns:
Number of tasks enqueued
"""
from agenteval.intelligent_eval.domain import calculate_priority, get_attention_reason
# Get all executing evals
evals = session.exec(
select(IntelligentEvalDB).where(IntelligentEvalDB.status == IntelligentEvalStatus.EXECUTING.value)
@ -74,7 +48,7 @@ def scan_and_enqueue_tasks(session: Session) -> int:
for eval_db in evals:
# Check if eval needs attention
reason = _get_attention_reason(eval_db, session)
reason = get_attention_reason(eval_db, session)
if reason is None:
continue
@ -83,7 +57,7 @@ def scan_and_enqueue_tasks(session: Session) -> int:
continue
# Calculate priority
priority = _calculate_priority(eval_db, session)
priority = calculate_priority(eval_db, session)
# Create task
task = IntelligentEvalTaskQueueDB(

View File

@ -0,0 +1,134 @@
"""Unit tests for the intelligent-eval scheduling domain (domain.py).
test_intelligent_eval_task_queue.py 迁移原先隔着 task_queue 的过渡
wrapper 测试ADR-0008 残留 wrapper 删除后直接锁定 domain 模块
时段解析欠账优先级注意原因的唯一实现
"""
from datetime import timedelta
from agenteval.intelligent_eval import domain
from agenteval.intelligent_eval.models import IntelligentEvalStatus
from agenteval.storage.db import IntelligentEvalDB, IntelligentEvalSessionDB, utc_now
from sqlmodel import Session
def test_is_slot_due():
"""Test time slot due detection."""
# Slot "8-10h" should be due after 8 hours
slot = {"time_slot": "8-10h", "sessions": 2}
assert domain.is_slot_due(slot, timedelta(hours=7)) is False
assert domain.is_slot_due(slot, timedelta(hours=8)) is True
assert domain.is_slot_due(slot, timedelta(hours=9)) is True
# Invalid slot format
assert domain.is_slot_due({"time_slot": "invalid"}, timedelta(hours=1)) is False
assert domain.is_slot_due({}, timedelta(hours=1)) is False
def test_is_slot_due_minute_format():
"""Minute-level slots (1h window: "0-20min") must be parsed and due on time."""
# 0-20min: due at 0min, not before
assert domain.is_slot_due({"time_slot": "0-20min"}, timedelta(minutes=-1)) is False
assert domain.is_slot_due({"time_slot": "0-20min"}, timedelta(minutes=0)) is True
assert domain.is_slot_due({"time_slot": "0-20min"}, timedelta(minutes=5)) is True
# 20-40min: not due before 20min, due at 20min
assert domain.is_slot_due({"time_slot": "20-40min"}, timedelta(minutes=19)) is False
assert domain.is_slot_due({"time_slot": "20-40min"}, timedelta(minutes=20)) is True
# parse both formats consistently (hours)
assert domain.parse_time_slot("8-10h") == (8.0, 10.0)
assert domain.parse_time_slot("0-20min") == (0.0, 1.0 / 3)
assert domain.parse_time_slot("20-40min") == (1.0 / 3, 2.0 / 3)
assert domain.parse_time_slot("bad") is None
def test_calculate_session_deficit(db_session: Session):
"""Test session deficit calculation."""
# Create eval with plan
eval_db = IntelligentEvalDB(
name="test",
target_id="target1",
status=IntelligentEvalStatus.EXECUTING.value,
started_at=utc_now() - timedelta(hours=9),
)
eval_db.set_plan({
"time_distribution": [
{"time_slot": "0-2h", "sessions": 1},
{"time_slot": "8-10h", "sessions": 2},
],
"estimated_sessions": 3,
})
db_session.add(eval_db)
db_session.commit()
# No sessions yet, should have 3 (1 from 0-2h, 2 from 8-10h)
deficit = domain.calculate_session_deficit(eval_db, db_session)
assert deficit == 3
# Add 1 session
session_db = IntelligentEvalSessionDB(
eval_id=eval_db.id,
target_id="target1",
status="completed",
)
db_session.add(session_db)
db_session.commit()
# Should have 3, has 1, deficit = 2
deficit = domain.calculate_session_deficit(eval_db, db_session)
assert deficit == 2
def test_calculate_priority(db_session: Session):
"""Test task priority calculation."""
# Eval with due slot and deficit
eval_db = IntelligentEvalDB(
name="test",
target_id="target1",
status=IntelligentEvalStatus.EXECUTING.value,
started_at=utc_now() - timedelta(hours=9),
)
eval_db.set_plan({
"time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
"estimated_sessions": 2,
})
db_session.add(eval_db)
db_session.commit()
priority = domain.calculate_priority(eval_db, db_session)
# Base 100 - 50 (slot due) - 20 (deficit 2 * 10) - 20 (wait 9h / 10min = 54, capped at 20)
assert priority == 10
def test_get_attention_reason(db_session: Session):
"""Test attention reason detection."""
# Eval with due slot
eval_db = IntelligentEvalDB(
name="test",
target_id="target1",
status=IntelligentEvalStatus.EXECUTING.value,
started_at=utc_now() - timedelta(hours=9),
)
eval_db.set_plan({
"time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
"estimated_sessions": 2,
})
db_session.add(eval_db)
db_session.commit()
reason = domain.get_attention_reason(eval_db, db_session)
assert reason == "slot_due"
# Add all sessions as completed
for _ in range(2):
session_db = IntelligentEvalSessionDB(
eval_id=eval_db.id,
target_id="target1",
status="completed",
)
db_session.add(session_db)
db_session.commit()
reason = domain.get_attention_reason(eval_db, db_session)
assert reason == "all_sessions_completed"

View File

@ -1,4 +1,8 @@
"""Unit tests for intelligent eval task queue."""
"""Unit tests for intelligent eval task queue.
时段/欠账/优先级等调度域的测试已随 ADR-0008 过渡 wrapper 删除迁移到
test_intelligent_eval_domain.py直接锁定 domain 模块
"""
from datetime import timedelta
@ -6,136 +10,12 @@ from agenteval.intelligent_eval import task_queue
from agenteval.intelligent_eval.models import IntelligentEvalStatus
from agenteval.storage.db import (
IntelligentEvalDB,
IntelligentEvalSessionDB,
IntelligentEvalTaskQueueDB,
utc_now,
)
from sqlmodel import Session, select
def test_is_slot_due():
"""Test time slot due detection."""
# Slot "8-10h" should be due after 8 hours
slot = {"time_slot": "8-10h", "sessions": 2}
assert task_queue._is_slot_due(slot, timedelta(hours=7)) is False
assert task_queue._is_slot_due(slot, timedelta(hours=8)) is True
assert task_queue._is_slot_due(slot, timedelta(hours=9)) is True
# Invalid slot format
assert task_queue._is_slot_due({"time_slot": "invalid"}, timedelta(hours=1)) is False
assert task_queue._is_slot_due({}, timedelta(hours=1)) is False
def test_is_slot_due_minute_format():
"""Minute-level slots (1h window: "0-20min") must be parsed and due on time."""
from agenteval.intelligent_eval import domain
# 0-20min: due at 0min, not before
assert task_queue._is_slot_due({"time_slot": "0-20min"}, timedelta(minutes=-1)) is False
assert task_queue._is_slot_due({"time_slot": "0-20min"}, timedelta(minutes=0)) is True
assert task_queue._is_slot_due({"time_slot": "0-20min"}, timedelta(minutes=5)) is True
# 20-40min: not due before 20min, due at 20min
assert task_queue._is_slot_due({"time_slot": "20-40min"}, timedelta(minutes=19)) is False
assert task_queue._is_slot_due({"time_slot": "20-40min"}, timedelta(minutes=20)) is True
# parse both formats consistently (hours)
assert domain.parse_time_slot("8-10h") == (8.0, 10.0)
assert domain.parse_time_slot("0-20min") == (0.0, 1.0 / 3)
assert domain.parse_time_slot("20-40min") == (1.0 / 3, 2.0 / 3)
assert domain.parse_time_slot("bad") is None
def test_calculate_session_deficit(db_session: Session):
"""Test session deficit calculation."""
# Create eval with plan
eval_db = IntelligentEvalDB(
name="test",
target_id="target1",
status=IntelligentEvalStatus.EXECUTING.value,
started_at=utc_now() - timedelta(hours=9),
)
eval_db.set_plan({
"time_distribution": [
{"time_slot": "0-2h", "sessions": 1},
{"time_slot": "8-10h", "sessions": 2},
],
"estimated_sessions": 3,
})
db_session.add(eval_db)
db_session.commit()
# No sessions yet, should have 3 (1 from 0-2h, 2 from 8-10h)
deficit = task_queue._calculate_session_deficit(eval_db, db_session)
assert deficit == 3
# Add 1 session
session_db = IntelligentEvalSessionDB(
eval_id=eval_db.id,
target_id="target1",
status="completed",
)
db_session.add(session_db)
db_session.commit()
# Should have 3, has 1, deficit = 2
deficit = task_queue._calculate_session_deficit(eval_db, db_session)
assert deficit == 2
def test_calculate_priority(db_session: Session):
"""Test task priority calculation."""
# Eval with due slot and deficit
eval_db = IntelligentEvalDB(
name="test",
target_id="target1",
status=IntelligentEvalStatus.EXECUTING.value,
started_at=utc_now() - timedelta(hours=9),
)
eval_db.set_plan({
"time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
"estimated_sessions": 2,
})
db_session.add(eval_db)
db_session.commit()
priority = task_queue._calculate_priority(eval_db, db_session)
# Base 100 - 50 (slot due) - 20 (deficit 2 * 10) - 20 (wait 9h / 10min = 54, capped at 20)
assert priority == 10
def test_get_attention_reason(db_session: Session):
"""Test attention reason detection."""
# Eval with due slot
eval_db = IntelligentEvalDB(
name="test",
target_id="target1",
status=IntelligentEvalStatus.EXECUTING.value,
started_at=utc_now() - timedelta(hours=9),
)
eval_db.set_plan({
"time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
"estimated_sessions": 2,
})
db_session.add(eval_db)
db_session.commit()
reason = task_queue._get_attention_reason(eval_db, db_session)
assert reason == "slot_due"
# Add all sessions as completed
for _ in range(2):
session_db = IntelligentEvalSessionDB(
eval_id=eval_db.id,
target_id="target1",
status="completed",
)
db_session.add(session_db)
db_session.commit()
reason = task_queue._get_attention_reason(eval_db, db_session)
assert reason == "all_sessions_completed"
def test_has_pending_task(db_session: Session):
"""Test pending task detection (去重)."""
eval_db = IntelligentEvalDB(