Compare commits
3 Commits
84627a3c6a
...
5a81c570c0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a81c570c0 | ||
|
|
09ff2ed123 | ||
|
|
da7dd434dd |
@ -130,6 +130,92 @@ def list_decision_logs(eval_id: str, session: Session) -> list[dict]:
|
|||||||
return [_log_to_dict(log) for log in logs]
|
return [_log_to_dict(log) for log in logs]
|
||||||
|
|
||||||
|
|
||||||
|
def _supplement_executing(
|
||||||
|
ev: IntelligentEvalDB,
|
||||||
|
sessions: list,
|
||||||
|
completed: int,
|
||||||
|
estimated: int,
|
||||||
|
types: set[str],
|
||||||
|
session: Session,
|
||||||
|
) -> int:
|
||||||
|
"""Supplement decision logs for EXECUTING evals."""
|
||||||
|
added = 0
|
||||||
|
if "execute_session" not in types and completed < estimated:
|
||||||
|
_append_row(
|
||||||
|
ev.id,
|
||||||
|
"execute_session",
|
||||||
|
"平台兜底:时段欠账需执行会话",
|
||||||
|
"platform",
|
||||||
|
{"platform_supplemented": True, "completed": completed, "estimated": estimated},
|
||||||
|
session,
|
||||||
|
)
|
||||||
|
added += 1
|
||||||
|
elif "start_analysis" not in types and sessions and completed >= estimated:
|
||||||
|
_append_row(
|
||||||
|
ev.id,
|
||||||
|
"start_analysis",
|
||||||
|
"平台兜底:所有会话已完成开始分析",
|
||||||
|
"platform",
|
||||||
|
{"platform_supplemented": True, "completed": completed, "estimated": estimated},
|
||||||
|
session,
|
||||||
|
)
|
||||||
|
added += 1
|
||||||
|
return added
|
||||||
|
|
||||||
|
|
||||||
|
def _supplement_completed(
|
||||||
|
ev: IntelligentEvalDB,
|
||||||
|
sessions: list,
|
||||||
|
completed: int,
|
||||||
|
estimated: int,
|
||||||
|
plan: dict,
|
||||||
|
types: set[str],
|
||||||
|
session: Session,
|
||||||
|
) -> int:
|
||||||
|
"""Supplement decision logs for COMPLETED evals (historical backfill)."""
|
||||||
|
added = 0
|
||||||
|
if "execute_session" not in types:
|
||||||
|
slots = plan.get("time_distribution") or []
|
||||||
|
if slots:
|
||||||
|
for slot in slots:
|
||||||
|
_append_row(
|
||||||
|
ev.id,
|
||||||
|
"execute_session",
|
||||||
|
f"平台兜底:时段{slot.get('time_slot', '')}执行会话(历史回填)",
|
||||||
|
"platform",
|
||||||
|
{
|
||||||
|
"platform_supplemented": True,
|
||||||
|
"time_slot": slot.get("time_slot"),
|
||||||
|
"sessions": slot.get("sessions"),
|
||||||
|
"completed": completed,
|
||||||
|
"estimated": estimated,
|
||||||
|
},
|
||||||
|
session,
|
||||||
|
)
|
||||||
|
added += 1
|
||||||
|
else:
|
||||||
|
_append_row(
|
||||||
|
ev.id,
|
||||||
|
"execute_session",
|
||||||
|
"平台兜底:执行会话(历史回填)",
|
||||||
|
"platform",
|
||||||
|
{"platform_supplemented": True, "completed": completed, "estimated": estimated},
|
||||||
|
session,
|
||||||
|
)
|
||||||
|
added += 1
|
||||||
|
if "start_analysis" not in types and sessions:
|
||||||
|
_append_row(
|
||||||
|
ev.id,
|
||||||
|
"start_analysis",
|
||||||
|
"平台兜底:所有会话已完成开始分析(历史回填)",
|
||||||
|
"platform",
|
||||||
|
{"platform_supplemented": True, "completed": completed, "estimated": estimated},
|
||||||
|
session,
|
||||||
|
)
|
||||||
|
added += 1
|
||||||
|
return added
|
||||||
|
|
||||||
|
|
||||||
def supplement_decision_logs(session: Session) -> int:
|
def supplement_decision_logs(session: Session) -> int:
|
||||||
"""Platform audit backfill for decision logs.
|
"""Platform audit backfill for decision logs.
|
||||||
|
|
||||||
@ -162,7 +248,9 @@ def supplement_decision_logs(session: Session) -> int:
|
|||||||
for ev in evals:
|
for ev in evals:
|
||||||
plan = ev.get_plan() if ev.plan else {}
|
plan = ev.get_plan() if ev.plan else {}
|
||||||
estimated = plan.get("estimated_sessions", 0)
|
estimated = plan.get("estimated_sessions", 0)
|
||||||
sessions = session.exec(select(IntelligentEvalSessionDB).where(IntelligentEvalSessionDB.eval_id == ev.id)).all()
|
sessions = session.exec(
|
||||||
|
select(IntelligentEvalSessionDB).where(IntelligentEvalSessionDB.eval_id == ev.id)
|
||||||
|
).all()
|
||||||
completed = sum(1 for s in sessions if s.status == "completed")
|
completed = sum(1 for s in sessions if s.status == "completed")
|
||||||
types = {
|
types = {
|
||||||
x.decision_type
|
x.decision_type
|
||||||
@ -170,66 +258,10 @@ def supplement_decision_logs(session: Session) -> int:
|
|||||||
select(IntelligentEvalDecisionLogDB).where(IntelligentEvalDecisionLogDB.eval_id == ev.id)
|
select(IntelligentEvalDecisionLogDB).where(IntelligentEvalDecisionLogDB.eval_id == ev.id)
|
||||||
).all()
|
).all()
|
||||||
}
|
}
|
||||||
|
|
||||||
if ev.status == IntelligentEvalStatus.EXECUTING.value:
|
if ev.status == IntelligentEvalStatus.EXECUTING.value:
|
||||||
if "execute_session" not in types and completed < estimated:
|
added += _supplement_executing(ev, sessions, completed, estimated, types, session)
|
||||||
_append_row(
|
|
||||||
ev.id,
|
|
||||||
"execute_session",
|
|
||||||
"平台兜底:时段欠账需执行会话",
|
|
||||||
"platform",
|
|
||||||
{"platform_supplemented": True, "completed": completed, "estimated": estimated},
|
|
||||||
session,
|
|
||||||
)
|
|
||||||
added += 1
|
|
||||||
elif "start_analysis" not in types and sessions and completed >= estimated:
|
|
||||||
_append_row(
|
|
||||||
ev.id,
|
|
||||||
"start_analysis",
|
|
||||||
"平台兜底:所有会话已完成开始分析",
|
|
||||||
"platform",
|
|
||||||
{"platform_supplemented": True, "completed": completed, "estimated": estimated},
|
|
||||||
session,
|
|
||||||
)
|
|
||||||
added += 1
|
|
||||||
elif ev.status == IntelligentEvalStatus.COMPLETED.value:
|
elif ev.status == IntelligentEvalStatus.COMPLETED.value:
|
||||||
# 历史回填:completed 评估决策日志全缺失时,按时段补 execute_session
|
added += _supplement_completed(ev, sessions, completed, estimated, plan, types, session)
|
||||||
if "execute_session" not in types:
|
|
||||||
slots = plan.get("time_distribution") or []
|
|
||||||
if slots:
|
|
||||||
for slot in slots:
|
|
||||||
_append_row(
|
|
||||||
ev.id,
|
|
||||||
"execute_session",
|
|
||||||
f"平台兜底:时段{slot.get('time_slot', '')}执行会话(历史回填)",
|
|
||||||
"platform",
|
|
||||||
{
|
|
||||||
"platform_supplemented": True,
|
|
||||||
"time_slot": slot.get("time_slot"),
|
|
||||||
"sessions": slot.get("sessions"),
|
|
||||||
"completed": completed,
|
|
||||||
"estimated": estimated,
|
|
||||||
},
|
|
||||||
session,
|
|
||||||
)
|
|
||||||
added += 1
|
|
||||||
else:
|
|
||||||
_append_row(
|
|
||||||
ev.id,
|
|
||||||
"execute_session",
|
|
||||||
"平台兜底:执行会话(历史回填)",
|
|
||||||
"platform",
|
|
||||||
{"platform_supplemented": True, "completed": completed, "estimated": estimated},
|
|
||||||
session,
|
|
||||||
)
|
|
||||||
added += 1
|
|
||||||
if "start_analysis" not in types and sessions:
|
|
||||||
_append_row(
|
|
||||||
ev.id,
|
|
||||||
"start_analysis",
|
|
||||||
"平台兜底:所有会话已完成开始分析(历史回填)",
|
|
||||||
"platform",
|
|
||||||
{"platform_supplemented": True, "completed": completed, "estimated": estimated},
|
|
||||||
session,
|
|
||||||
)
|
|
||||||
added += 1
|
|
||||||
return added
|
return added
|
||||||
|
|||||||
@ -226,8 +226,8 @@ def submit_report(session: Session, eval_id: str, report: dict[str, Any]) -> Int
|
|||||||
(不完整证据),不再阻塞报告提交。
|
(不完整证据),不再阻塞报告提交。
|
||||||
"""
|
"""
|
||||||
repo = IntelligentEvalRepository(session)
|
repo = IntelligentEvalRepository(session)
|
||||||
sessions = IntelligentEvalSessionRepository(session).list_by_eval(eval_id)
|
eval_sessions = IntelligentEvalSessionRepository(session).list_by_eval(eval_id)
|
||||||
if any(s.status == IntelligentEvalSessionStatus.RUNNING for s in sessions):
|
if any(s.status == IntelligentEvalSessionStatus.RUNNING for s in eval_sessions):
|
||||||
raise IntelligentEvalTransitionError("存在进行中的会话,不能提交报告")
|
raise IntelligentEvalTransitionError("存在进行中的会话,不能提交报告")
|
||||||
# ADR-0011:submit 边界把 scores 归一到 {overall, dimensions} 单一规范结构
|
# ADR-0011:submit 边界把 scores 归一到 {overall, dimensions} 单一规范结构
|
||||||
if report.get("scores"):
|
if report.get("scores"):
|
||||||
@ -432,29 +432,36 @@ def expire_stale_running_sessions(session: Session) -> int:
|
|||||||
now = utc_now()
|
now = utc_now()
|
||||||
# SQLite 读出为 naive datetime,阈值须同为 naive 才能在 Python 侧比较
|
# SQLite 读出为 naive datetime,阈值须同为 naive 才能在 Python 侧比较
|
||||||
threshold = now.replace(tzinfo=None) - timedelta(minutes=SESSION_IDLE_EXPIRE_MINUTES)
|
threshold = now.replace(tzinfo=None) - timedelta(minutes=SESSION_IDLE_EXPIRE_MINUTES)
|
||||||
rows = session.exec(
|
|
||||||
select(IntelligentEvalSessionDB).where(IntelligentEvalSessionDB.status == "running")
|
# 单次查询获取所有 running 会话及其最后消息时间(避免 N+1 查询)
|
||||||
).all()
|
stmt = (
|
||||||
|
select(
|
||||||
|
IntelligentEvalSessionDB,
|
||||||
|
func.max(IntelligentEvalMessageDB.created_at).label("last_message_at"),
|
||||||
|
)
|
||||||
|
.outerjoin(
|
||||||
|
IntelligentEvalMessageDB,
|
||||||
|
IntelligentEvalMessageDB.session_id == IntelligentEvalSessionDB.id,
|
||||||
|
)
|
||||||
|
.where(IntelligentEvalSessionDB.status == "running")
|
||||||
|
.group_by(IntelligentEvalSessionDB.id)
|
||||||
|
)
|
||||||
|
rows = session.exec(stmt).all()
|
||||||
|
|
||||||
expired = 0
|
expired = 0
|
||||||
for row in rows:
|
for session_row, last_message_at in rows:
|
||||||
last_message_at = session.exec(
|
last_activity = last_message_at or session_row.created_at
|
||||||
select(func.max(IntelligentEvalMessageDB.created_at)).where(
|
|
||||||
IntelligentEvalMessageDB.session_id == row.id
|
|
||||||
)
|
|
||||||
).one()
|
|
||||||
last_activity = last_message_at or row.created_at
|
|
||||||
if last_activity is None or last_activity >= threshold:
|
if last_activity is None or last_activity >= threshold:
|
||||||
continue
|
continue
|
||||||
row.status = IntelligentEvalSessionStatus.EXPIRED.value
|
session_row.status = IntelligentEvalSessionStatus.EXPIRED.value
|
||||||
row.closed_at = now
|
session_row.closed_at = now
|
||||||
expired += 1
|
expired += 1
|
||||||
append_decision_log(
|
append_decision_log(
|
||||||
row.eval_id,
|
session_row.eval_id,
|
||||||
"session_expired",
|
"session_expired",
|
||||||
f"平台兜底:会话 {SESSION_IDLE_EXPIRE_MINUTES} 分钟无新轮次,置为过期(不完整证据)",
|
f"平台兜底:会话 {SESSION_IDLE_EXPIRE_MINUTES} 分钟无新轮次,置为过期(不完整证据)",
|
||||||
"platform",
|
"platform",
|
||||||
{"platform_supplemented": True, "session_id": row.id, "turn_count": row.turn_count},
|
{"platform_supplemented": True, "session_id": session_row.id, "turn_count": session_row.turn_count},
|
||||||
session,
|
session,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -674,16 +681,16 @@ def evals_needing_analyst_nudge(session: Session) -> list[str]:
|
|||||||
|
|
||||||
needing: list[str] = []
|
needing: list[str] = []
|
||||||
for row in executing:
|
for row in executing:
|
||||||
sessions = session.exec(
|
eval_sessions = session.exec(
|
||||||
select(IntelligentEvalSessionDB).where(IntelligentEvalSessionDB.eval_id == row.id)
|
select(IntelligentEvalSessionDB).where(IntelligentEvalSessionDB.eval_id == row.id)
|
||||||
).all()
|
).all()
|
||||||
if not sessions or any(s.status not in _TERMINAL_SESSION_STATUSES for s in sessions):
|
if not eval_sessions or any(s.status not in _TERMINAL_SESSION_STATUSES for s in eval_sessions):
|
||||||
continue
|
continue
|
||||||
# 冒烟教训:窗口未结束且会话数未达计划时,未来时段到期后还要建会话,
|
# 冒烟教训:窗口未结束且会话数未达计划时,未来时段到期后还要建会话,
|
||||||
# 此时催促 analyst 会让报告提前收敛(漏掉后续时段的证据)
|
# 此时催促 analyst 会让报告提前收敛(漏掉后续时段的证据)
|
||||||
if _window_has_pending_future_slots(row, sessions, now):
|
if _window_has_pending_future_slots(row, eval_sessions, now):
|
||||||
continue
|
continue
|
||||||
closed_moments = [s.closed_at for s in sessions if s.closed_at is not None]
|
closed_moments = [s.closed_at for s in eval_sessions if s.closed_at is not None]
|
||||||
if not closed_moments:
|
if not closed_moments:
|
||||||
continue
|
continue
|
||||||
last_closed = max(closed_moments)
|
last_closed = max(closed_moments)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user