From 09a0928bbce618770f2392a8863fde5552a31430 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Tue, 9 Jun 2026 16:43:41 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20resolve=20all=20flake8=20lint=20erro?= =?UTF-8?q?rs=20(118=20=E2=86=92=200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/blackboard_routes.py | 18 ++++++++------ src/api/mail_routes.py | 5 ++-- src/api/project_routes.py | 4 +-- src/api/toolchain_routes.py | 1 - src/blackboard/db.py | 1 - src/blackboard/models.py | 2 +- src/blackboard/operations.py | 2 -- src/blackboard/registry.py | 1 - src/cli/blackboard.py | 4 +-- src/daemon/bootstrap.py | 3 +-- src/daemon/counter.py | 2 +- src/daemon/dispatcher.py | 16 ++++++------ src/daemon/experience.py | 6 ++--- src/daemon/guardrails.py | 2 +- src/daemon/health.py | 6 ++--- src/daemon/inbox.py | 5 ++-- src/daemon/mail_notify.py | 2 +- src/daemon/review.py | 5 +--- src/daemon/skill_system.py | 3 +-- src/daemon/spawner.py | 27 +++++++++------------ src/daemon/sse.py | 5 +--- src/daemon/ticker.py | 47 ++++++++++++++++++------------------ src/main.py | 27 +++++++++++---------- src/utils.py | 1 - 24 files changed, 89 insertions(+), 106 deletions(-) diff --git a/src/api/blackboard_routes.py b/src/api/blackboard_routes.py index 8bf30d3..1f197b2 100644 --- a/src/api/blackboard_routes.py +++ b/src/api/blackboard_routes.py @@ -5,14 +5,14 @@ from __future__ import annotations import json import os from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, Optional from fastapi import APIRouter, HTTPException, Query from src.blackboard.operations import Blackboard from src.blackboard.models import Task, Review from src.blackboard.queries import Queries -from src.blackboard.db import VALID_STATUSES, VALID_TRANSITIONS, COMMENT_TYPES, OUTPUT_TYPES +from src.blackboard.db import VALID_STATUSES, OUTPUT_TYPES from src.blackboard.registry import ProjectRegistry from src.utils import get_data_root @@ -240,7 +240,7 @@ async def update_status(project_id: str, task_id: str, body: Dict[str, Any]): }) if not bb.update_task_status(task_id, new_status, - agent=body.get("agent")): + agent=body.get("agent")): raise HTTPException(409, { "error": "transition_failed", "detail": f"Status update failed for {task_id}", @@ -265,6 +265,7 @@ async def update_status(project_id: str, task_id: str, body: Dict[str, Any]): # --- @mention 自动提取(#04) --- _KNOWN_AGENT_IDS: list = [] + def _init_agent_ids(): """从配置文件加载 Agent ID 列表""" global _KNOWN_AGENT_IDS @@ -279,6 +280,7 @@ def _init_agent_ids(): except Exception: _KNOWN_AGENT_IDS = [] + def _extract_mentions(text: str) -> list: """从文本中自动提取 @agent-id 格式的 mention""" import re @@ -317,8 +319,8 @@ async def add_comment(project_id: str, task_id: str, body: Dict[str, Any]): merged_mentions = list(set(explicit_mentions + auto_mentions)) cid = bb.add_comment(task_id, body["author"], comment_body, - comment_type=body.get("comment_type", "general"), - mentions=merged_mentions) + comment_type=body.get("comment_type", "general"), + mentions=merged_mentions) if merged_mentions: bb.record_mentions(cid, task_id, merged_mentions) # #10: SSE 通知前端黑板有新 comment @@ -424,8 +426,8 @@ async def get_decisions(project_id: str, task_id: str): async def add_decision(project_id: str, task_id: str, body: Dict[str, Any]): bb = _bb(project_id) did = bb.add_decision(task_id, body["decider"], body["decision"], - body["rationale"], - alternatives=body.get("alternatives")) + body["rationale"], + alternatives=body.get("alternatives")) return {"ok": True, "decision_id": did} @@ -435,7 +437,7 @@ async def add_decision(project_id: str, task_id: str, body: Dict[str, Any]): async def add_observation(project_id: str, task_id: str, body: Dict[str, Any]): bb = _bb(project_id) oid = bb.add_observation(task_id, body["observer"], body["body"], - severity=body.get("severity", "info")) + severity=body.get("severity", "info")) return {"ok": True, "observation_id": oid} diff --git a/src/api/mail_routes.py b/src/api/mail_routes.py index ef83690..4a6e6d9 100644 --- a/src/api/mail_routes.py +++ b/src/api/mail_routes.py @@ -9,7 +9,7 @@ from __future__ import annotations import json from datetime import datetime from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, Optional from fastapi import APIRouter, HTTPException, Query @@ -36,6 +36,7 @@ def _get_valid_agents() -> set: # fallback:硬编码 return {"zhangfei-dev", "guanyu-dev", "zhaoyun-data", "jiangwei-infra", "pangtong-fujunshi", "simayi-challenger"} + router = APIRouter(prefix="/api/mail", tags=["mail"]) MAIL_PROJECT_ID = "_mail" @@ -222,7 +223,7 @@ async def send_mail(body: Dict[str, Any]): # A8: 只有原邮件的双方能回复(严格 1 对 1) if from_agent not in (orig_from, orig_to): - raise HTTPException(400, f"只有邮件的发送者或接收者可以回复") + raise HTTPException(400, "只有邮件的发送者或接收者可以回复") # A6/A7: 自动纠正 to → 原邮件发件者 to_agent = body.get("to", "").strip() diff --git a/src/api/project_routes.py b/src/api/project_routes.py index ff9d1f4..0e3d209 100644 --- a/src/api/project_routes.py +++ b/src/api/project_routes.py @@ -3,7 +3,7 @@ from __future__ import annotations from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict from fastapi import APIRouter, HTTPException, Query @@ -76,7 +76,7 @@ async def list_projects(): async def create_project(body: Dict[str, Any]): reg = _registry() try: - info = reg.create_project( + reg.create_project( body["id"], body["name"], agents=body.get("agents", []), description=body.get("description", ""), diff --git a/src/api/toolchain_routes.py b/src/api/toolchain_routes.py index f8e96da..73a0c44 100644 --- a/src/api/toolchain_routes.py +++ b/src/api/toolchain_routes.py @@ -141,7 +141,6 @@ def _calc_risk_level(changed_files: List[str]) -> str: # --------------------------------------------------------------------------- - MAIL_PROJECT_ID = "_mail" diff --git a/src/blackboard/db.py b/src/blackboard/db.py index f94c88c..821318e 100644 --- a/src/blackboard/db.py +++ b/src/blackboard/db.py @@ -4,7 +4,6 @@ from __future__ import annotations import sqlite3 from pathlib import Path -from typing import Optional def init_db(db_path: Path) -> None: diff --git a/src/blackboard/models.py b/src/blackboard/models.py index 617588a..b6a2dbc 100644 --- a/src/blackboard/models.py +++ b/src/blackboard/models.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import Any, Dict, List, Optional +from typing import Any, List, Optional @dataclass diff --git a/src/blackboard/operations.py b/src/blackboard/operations.py index 2d75f3e..d27e32d 100644 --- a/src/blackboard/operations.py +++ b/src/blackboard/operations.py @@ -11,7 +11,6 @@ from typing import Any, Dict, List, Optional from .db import ( VALID_TRANSITIONS, - VALID_STATUSES, COMMENT_TYPES, EVENT_TYPES, OUTPUT_TYPES, @@ -693,7 +692,6 @@ class Blackboard: finally: conn.close() - # ── Checkpoint CRUD(M3) ── def create_checkpoint( diff --git a/src/blackboard/registry.py b/src/blackboard/registry.py index af1fafd..10e227d 100644 --- a/src/blackboard/registry.py +++ b/src/blackboard/registry.py @@ -355,4 +355,3 @@ class ProjectRegistry: def reload(self) -> None: """兼容旧接口(SQLite 不需要 reload cache)""" - pass diff --git a/src/cli/blackboard.py b/src/cli/blackboard.py index 853332a..6025779 100644 --- a/src/cli/blackboard.py +++ b/src/cli/blackboard.py @@ -10,7 +10,7 @@ from typing import List, Optional from src.blackboard.operations import Blackboard from src.utils import get_data_root -from src.blackboard.models import Task, Comment, Output, Decision, Observation, Review, Experience +from src.blackboard.models import Task, Review from src.blackboard.queries import Queries from src.blackboard.registry import ProjectRegistry @@ -262,7 +262,7 @@ def build_admin_parser() -> argparse.ArgumentParser: p_pc.add_argument("--description", default="") # project list - p_pl = sub.add_parser("project-list", help="List projects") + sub.add_parser("project-list", help="List projects") # project archive p_pa = sub.add_parser("project-archive", help="Archive project") diff --git a/src/daemon/bootstrap.py b/src/daemon/bootstrap.py index e5d5aca..e142b51 100644 --- a/src/daemon/bootstrap.py +++ b/src/daemon/bootstrap.py @@ -11,8 +11,7 @@ A 类 Skill 由引擎确定性注入全文,不靠 Description 触发。 import logging import os -from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, List logger = logging.getLogger("moziplus-v2.bootstrap") diff --git a/src/daemon/counter.py b/src/daemon/counter.py index b70c209..999655f 100644 --- a/src/daemon/counter.py +++ b/src/daemon/counter.py @@ -73,7 +73,7 @@ class ActiveAgentCounter: cd = seconds if seconds is not None else self._default_cooldown_seconds self._cooldown_until[agent_id] = time.time() + cd logger.info("Cooldown set for %s: %.0fs (until %.0f)", - agent_id, cd, self._cooldown_until[agent_id]) + agent_id, cd, self._cooldown_until[agent_id]) async def can_acquire(self, agent_id: str, session_id: str = "main") -> bool: """三层检查:cooldown → global → per agent → per session key""" diff --git a/src/daemon/dispatcher.py b/src/daemon/dispatcher.py index 8d73093..a5140da 100644 --- a/src/daemon/dispatcher.py +++ b/src/daemon/dispatcher.py @@ -14,7 +14,6 @@ from __future__ import annotations import json import logging import sqlite3 -from datetime import datetime from enum import Enum from pathlib import Path from typing import Any, Dict, List, Optional @@ -22,7 +21,7 @@ from typing import Any, Dict, List, Optional from src.blackboard.models import Task from src.blackboard.db import get_connection from src.daemon.spawner import AgentBusyError -from src.daemon.router import AgentRouter, RouteDecision +from src.daemon.router import AgentRouter logger = logging.getLogger("moziplus-v2.dispatcher") @@ -194,6 +193,7 @@ class Dispatcher: _task_id = task.id _mail_db = db_path _disp = self + def _mail_on_checks_passed(): nonlocal _mail_marked_working if not _disp._mail_auto_working(_task_id, _mail_db): @@ -203,8 +203,8 @@ class Dispatcher: # 构建 spawn message message = self._build_spawn_message(task, agent_id, project_config, - mode=decision.get("mode", ""), - spawn_type=action_type or "executor") + mode=decision.get("mode", ""), + spawn_type=action_type or "executor") # v2.7.2: on_complete 只含业务逻辑,不含 counter.release # counter.release 由 spawn_full_agent 内部的 wrapped_on_complete 保证 @@ -269,8 +269,8 @@ class Dispatcher: from src.blackboard.blackboard import Blackboard bb = Blackboard(_task_db) bb.add_comment(_task_id, "daemon", - f"@{task_row['assignee']} 审查结论: {verdict_str},请查看详情并决定接受或反驳", - comment_type="review") + f"@{task_row['assignee']} 审查结论: {verdict_str},请查看详情并决定接受或反驳", + comment_type="review") logger.info("Task %s: review verdict=%s, notified assignee=%s", _task_id, verdict_str, task_row["assignee"] if task_row else "?") # 不标 done,保持 review 状态 @@ -661,7 +661,7 @@ class Dispatcher: logger.error("Mail %s: failed to revert to pending: %s", task_id, e) def _mail_auto_complete(self, task_id: str, agent_id: str, - db_path: Path, must_haves: str) -> None: + db_path: Path, must_haves: str) -> None: """Mail 任务:on_complete 后自动标 done/failed(含幻觉门控)""" try: # 解析 performative @@ -858,7 +858,7 @@ class Dispatcher: logger.error("Task %s: mark status error: %s", task_id, e) @staticmethod - def _check_crash_limit(task_id: str, db_path: pathlib.Path, limit: int = 3, + def _check_crash_limit(task_id: str, db_path: Path, limit: int = 3, window_minutes: int = 30) -> bool: """v2.8.1 Fix-3c: 检查 task 最近 window_minutes 内的 crash 次数是否超限。 diff --git a/src/daemon/experience.py b/src/daemon/experience.py index 663ef74..1745ded 100644 --- a/src/daemon/experience.py +++ b/src/daemon/experience.py @@ -14,7 +14,7 @@ import logging import re from datetime import datetime from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional logger = logging.getLogger("moziplus-v2.experience") @@ -68,7 +68,7 @@ class Experience: @classmethod def from_dict(cls, data: Dict[str, Any]) -> Experience: return cls(**{k: v for k, v in data.items() if k != "id"}, - experience_id=data.get("id")) + experience_id=data.get("id")) class ExperienceStore: @@ -284,7 +284,7 @@ class ExperienceDistiller: all_tags.append(task_type) results = self.store.search(tags=all_tags if all_tags else None, - query=query, limit=limit) + query=query, limit=limit) # 按置信度排序 results.sort(key=lambda e: e.confidence, reverse=True) diff --git a/src/daemon/guardrails.py b/src/daemon/guardrails.py index 8412b58..6de476d 100644 --- a/src/daemon/guardrails.py +++ b/src/daemon/guardrails.py @@ -4,7 +4,7 @@ from __future__ import annotations import logging import re -from dataclasses import dataclass, field +from dataclasses import dataclass from pathlib import Path from typing import Any, Dict, List, Optional diff --git a/src/daemon/health.py b/src/daemon/health.py index 50ca567..02a10b5 100644 --- a/src/daemon/health.py +++ b/src/daemon/health.py @@ -9,9 +9,9 @@ from __future__ import annotations import json import logging from pathlib import Path -from typing import Any, Dict, Optional +from typing import Any, Dict -from src.blackboard.db import get_connection, init_db +from src.blackboard.db import get_connection from src.blackboard.queries import Queries logger = logging.getLogger("moziplus-v2.health") @@ -41,7 +41,6 @@ class HealthChecker: {"healthy": bool, "zombie": bool, "stale_ticks": int, "alert_written": bool, "resolved": bool} """ - db_key = str(db_path) result: Dict[str, Any] = { "healthy": True, "zombie": False, @@ -58,7 +57,6 @@ class HealthChecker: # 用 event count 变化判断是否有真实变更 conn = queries._conn() try: - total_events = conn.execute("SELECT COUNT(*) FROM events").fetchone()[0] non_tick_events = conn.execute( "SELECT COUNT(*) FROM events WHERE event_type != 'daemon_tick' " "AND event_type != 'agent_zombie_detected'" diff --git a/src/daemon/inbox.py b/src/daemon/inbox.py index f76d9ca..eb25989 100644 --- a/src/daemon/inbox.py +++ b/src/daemon/inbox.py @@ -15,7 +15,6 @@ from __future__ import annotations import asyncio import json import logging -import os from pathlib import Path from typing import Any, Callable, Coroutine, Dict, List, Optional @@ -57,7 +56,7 @@ class InboxWatcher: self._running = True self._task = asyncio.create_task(self._loop()) logger.info("Inbox watcher started (path=%s, interval=%.1fs)", - self.inbox_path, self.watch_interval) + self.inbox_path, self.watch_interval) async def stop(self) -> None: """停止监听""" @@ -69,7 +68,7 @@ class InboxWatcher: except asyncio.CancelledError: pass logger.info("Inbox watcher stopped (processed=%d, errors=%d)", - self._total_processed, self._total_errors) + self._total_processed, self._total_errors) @property def is_running(self) -> bool: diff --git a/src/daemon/mail_notify.py b/src/daemon/mail_notify.py index 020415e..d1ee741 100644 --- a/src/daemon/mail_notify.py +++ b/src/daemon/mail_notify.py @@ -108,7 +108,7 @@ def notify_mail_failed(db_path: Path, original_mail_id: str, ) bb.create_task(notify_task) logger.info("Mail %s: sent failure notification to %s (original_sender=%s, reason=%s, notify_id=%s)", - original_mail_id, target_agent, from_agent, reason, notify_id) + original_mail_id, target_agent, from_agent, reason, notify_id) except Exception as e: logger.warning("notify_mail_failed: failed to send notification for mail %s: %s", original_mail_id, e) diff --git a/src/daemon/review.py b/src/daemon/review.py index 667923b..1bff5b1 100644 --- a/src/daemon/review.py +++ b/src/daemon/review.py @@ -8,15 +8,12 @@ from __future__ import annotations import json import logging -import re -from datetime import datetime from enum import Enum from pathlib import Path -from typing import Any, Callable, Dict, List, Optional, Tuple +from typing import Any, Callable, Dict, List, Optional from src.blackboard.models import Task from src.blackboard.operations import Blackboard -from src.blackboard.queries import Queries logger = logging.getLogger("moziplus-v2.review") diff --git a/src/daemon/skill_system.py b/src/daemon/skill_system.py index 7774763..a54afb4 100644 --- a/src/daemon/skill_system.py +++ b/src/daemon/skill_system.py @@ -10,12 +10,11 @@ from __future__ import annotations import json import logging -import re from dataclasses import dataclass, field from datetime import datetime from enum import Enum from pathlib import Path -from typing import Any, Callable, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple logger = logging.getLogger("moziplus-v2.skill") diff --git a/src/daemon/spawner.py b/src/daemon/spawner.py index 1d45bf2..aee09ea 100644 --- a/src/daemon/spawner.py +++ b/src/daemon/spawner.py @@ -15,7 +15,7 @@ from datetime import datetime from pathlib import Path from typing import Any, Dict, List, Optional -from src.blackboard.db import get_connection, init_db +from src.blackboard.db import get_connection logger = logging.getLogger("moziplus-v2.spawner") @@ -163,6 +163,7 @@ class AgentBusyError(Exception): #07: reason 字段区分具体原因,便于 dispatcher 层区分处理。 """ + def __init__(self, agent_id: str, reason: str = "busy", detail: Optional[dict] = None): self.agent_id = agent_id self.reason = reason # counter_blocked / session_locked / session_running / session_compacting / session_stuck @@ -299,7 +300,7 @@ class AgentSpawner: project_id, agent_id) def _build_minimal_fallback(self, task_id, title, description, must_haves, - project_id, agent_id): + project_id, agent_id): """最小 fallback:只有任务上下文 + API 指令""" task_section = f"""## 任务 {title} @@ -311,7 +312,7 @@ class AgentSpawner: return task_section + "\n\n---\n\n" + api_section def _build_api_section(self, project_id: str, task_id: str, - agent_id: str) -> str: + agent_id: str) -> str: """构建 API 回写操作指令(BootstrapBuilder 模式下补充)""" # mail 任务直接 done,不走 review success_status = '"done"' if project_id == "_mail" else '"review"' @@ -337,8 +338,8 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta """ def _build_discussion_prompt(self, task_id: str, title: str, - description: str, must_haves: str, - project_id: str, agent_id: str) -> str: + description: str, must_haves: str, + project_id: str, agent_id: str) -> str: """构建讨论类 spawn prompt(§3.3 框架 + Boids)""" goal_snapshot = description or title constraints = must_haves or "(无特殊约束)" @@ -379,9 +380,8 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta return router.agent_profiles.get(agent_id) return None - def _build_mail_prompt(self, task_id: str, title: str, description: str, - must_haves: str, agent_id: str) -> str: + must_haves: str, agent_id: str) -> str: """构建 Mail 专用精简模板""" # 解析 must_haves 获取 from 和 performative from_agent = agent_id @@ -575,7 +575,7 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta stderr=asyncio.subprocess.PIPE, ) self._register_session(session_id, agent_id, task_id, proc.pid, - broadcast_task_ids=broadcast_task_ids) + broadcast_task_ids=broadcast_task_ids) logger.info("Spawned agent %s (session=%s, pid=%d)", agent_id, session_id, proc.pid) @@ -878,9 +878,6 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_ except Exception: pass - stderr_text = b"".join(stderr_chunks).decode("utf-8", errors="replace") - - # 检查 session 状态 state = self._check_session_state(agent_id) # B1: 假死 - 先复活,连续假死 ≥2 次再 failed @@ -1216,7 +1213,7 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_ 实测 50KB 在长对话中不够(compact 记录被推出窗口导致漏检)。 正常扫描量不变:从尾部往前扫,遇到超过 15min 的 timestamp 即 break。 """ - if not session_file or not pathlib.Path(session_file).exists(): + if not session_file or not Path(session_file).exists(): return False try: from datetime import datetime, timezone @@ -1425,7 +1422,7 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_ return defaults def _update_retry_counts(self, db_path: Optional[Path], - task_id: Optional[str], counts: dict): + task_id: Optional[str], counts: dict): """将 retry counts 写回最新 task_attempt 的 metadata""" if not db_path or not task_id: return @@ -1485,8 +1482,8 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_ from src.blackboard.operations import Blackboard bb = Blackboard(db_path) cid = bb.add_comment(task_id, "daemon", - f"@pangtong-fujunshi 任务执行失败: {reason},请评估是否需要介入", - comment_type="system") + f"@pangtong-fujunshi 任务执行失败: {reason},请评估是否需要介入", + comment_type="system") bb.record_mentions(cid, task_id, ["pangtong-fujunshi"]) logger.info("Task %s: failure notified pangtong via comment+mention (reason=%s)", task_id, reason) except Exception as e: diff --git a/src/daemon/sse.py b/src/daemon/sse.py index d3f960b..e844bd7 100644 --- a/src/daemon/sse.py +++ b/src/daemon/sse.py @@ -9,14 +9,11 @@ from __future__ import annotations import asyncio import json import logging -import subprocess import uuid from datetime import datetime from enum import Enum -from pathlib import Path -from typing import Any, Callable, Dict, List, Optional, Set +from typing import Any, Dict, List, Optional -from src.blackboard.models import Event logger = logging.getLogger("moziplus-v2.sse") diff --git a/src/daemon/ticker.py b/src/daemon/ticker.py index 6a75264..251c0b8 100644 --- a/src/daemon/ticker.py +++ b/src/daemon/ticker.py @@ -21,7 +21,6 @@ from dataclasses import dataclass, field as dc_field from src.blackboard.operations import Blackboard from src.blackboard.db import get_connection -from src.blackboard.models import Task from src.daemon.spawner import AgentBusyError from src.blackboard.queries import Queries from src.blackboard.registry import ProjectRegistry @@ -35,6 +34,7 @@ class BroadcastRound: responded_agents: set = dc_field(default_factory=set) # 已返回反馈的 Agent(含 NO_REPLY) round_number: int = 0 # 当前第几轮(0=未开始,1=第1轮) + logger = logging.getLogger("moziplus-v2.ticker") @@ -391,7 +391,7 @@ class Ticker: MAX_ROUNDS = 5 # §4.5 防无限循环 async def _check_round_complete(self, db_path: Path, - project_id: str) -> List[str]: + project_id: str) -> List[str]: """检测 parent task 下所有 sub task 终态 → spawn 庞统 review 流程(§4.4): @@ -462,7 +462,7 @@ class Ticker: "Round %d review spawned for parent %s (subs: %s)", new_round, parent_id, summary ) - except Exception as e: + except Exception: logger.exception("Round check error for parent %s", parent_id) return reviewed @@ -531,9 +531,9 @@ Parent Task ID: {parent_task.id} """ async def _spawn_pangtong_review(self, parent_task, - review_prompt: str, - project_id: str, - new_round: int = 0) -> bool: + review_prompt: str, + project_id: str, + new_round: int = 0) -> bool: """Spawn 庞统进行 review 流程: @@ -543,7 +543,6 @@ Parent Task ID: {parent_task.id} """ try: agent_id = "pangtong-fujunshi" - session_id = f"review-{parent_task.id}-r{new_round}" # 构造 on_complete 回调:解析庞统结论,更新 parent 状态 async def _on_review_complete(aid: str, outcome: str): @@ -586,7 +585,7 @@ Parent Task ID: {parent_task.id} self._set_parent_reviewing(parent_task.id, project_id) return True return False - except Exception as e: + except Exception: logger.exception("Failed to spawn pangtong review for %s", parent_task.id) return False @@ -603,14 +602,14 @@ Parent Task ID: {parent_task.id} (parent_id,)) conn.commit() logger.info("Parent %s → reviewing (round review in progress)", - parent_id) + parent_id) finally: conn.close() except Exception: logger.exception("Failed to set parent %s to reviewing", parent_id) def _handle_review_conclusion(self, parent_id: str, project_id: str, - review_text: str, round_num: int): + review_text: str, round_num: int): """解析庞统 review 结论,更新 parent 状态 review_text 是庞统回复的文本(从 spawner session meta payloads 拼接)。 @@ -675,7 +674,7 @@ Parent Task ID: {parent_task.id} MENTION_MAX_RETRIES = 5 async def _process_mentions(self, db_path: Path, - project_id: str) -> List[str]: + project_id: str) -> List[str]: """扫描 pending mentions → spawn 被 @ 的 Agent 流程(§3.4): @@ -767,8 +766,8 @@ Parent Task ID: {parent_task.id} from src.blackboard.blackboard import Blackboard bb2 = Blackboard(rdb_path) bb2.add_comment(_t_id, "daemon", - f"@{t_row['assignee']} 审查结论: {verdict_str},请查看详情并决定接受或反驳", - comment_type="review") + f"@{t_row['assignee']} 审查结论: {verdict_str},请查看详情并决定接受或反驳", + comment_type="review") logger.info("Rebuttal: task %s still %s after rebuttal", _t_id, verdict_str) except Exception: logger.exception("Rebuttal on_complete failed for task %s", _t_id) @@ -805,7 +804,7 @@ Parent Task ID: {parent_task.id} # Agent 忙,不递增 retry_count,等下次 tick 自然重试 logger.info("Mention spawn skipped: %s busy, will retry next tick", agent_id) - except Exception as e: + except Exception: logger.exception("Mention processing error for agent %s", agent_id) for item in items: try: @@ -948,7 +947,7 @@ Parent Task ID: {parent_task.id} # ------------------------------------------------------------------ async def _dispatch_pending(self, db_path: Path, - project_id: str) -> List[str]: + project_id: str) -> List[str]: """扫描 pending 任务并调度 v3.0: 两条路径 @@ -1242,7 +1241,7 @@ Parent Task ID: {parent_task.id} return [aid for aid in all_agents if active.get(aid, 0) == 0] async def _dispatch_reviews(self, db_path: Path, - project_id: str) -> List[str]: + project_id: str) -> List[str]: """扫描 review 状态任务,检查是否有产出,调度审查 Agent""" # mail 任务不走 review 流程,直接跳过 if project_id == "_mail": @@ -1344,7 +1343,7 @@ Parent Task ID: {parent_task.id} ) reclaimed.append(task.id) logger.warning("Escalated %s: no taker after %d broadcasts", - task.id, retry_count) + task.id, retry_count) finally: conn.close() else: @@ -1423,7 +1422,7 @@ Parent Task ID: {parent_task.id} if ok: reclaimed.append(task.id) logger.info("Mail %s: ticker recheck found reply, marked done (%.1fm)", - task.id, elapsed) + task.id, elapsed) finally: conn.close() continue @@ -1440,7 +1439,7 @@ Parent Task ID: {parent_task.id} if ok: reclaimed.append(task.id) logger.warning("Task %s timed out (working %.1fm > %.1fm)", - task.id, elapsed, timeout_minutes) + task.id, elapsed, timeout_minutes) finally: conn.close() except (ValueError, TypeError): @@ -1501,7 +1500,7 @@ Parent Task ID: {parent_task.id} return True # 保守:查询失败假设有回复 def _check_recent_routing(self, db_path: Path, task_id: str, - action_type: str) -> bool: + action_type: str) -> bool: """检查最近 5 分钟内是否已 dispatch 过指定类型的路由(防重复)""" try: conn = get_connection(db_path) @@ -1579,11 +1578,11 @@ Parent Task ID: {parent_task.id} if recovery_report["total_recovered"] > 0: logger.info("Startup recovery: %d tasks recovered across %d projects", - recovery_report["total_recovered"], - len(recovery_report["projects"])) + recovery_report["total_recovered"], + len(recovery_report["projects"])) elif recovery_report["total_noop"] > 0: logger.info("Startup recovery: %d tasks kept as-is (no recovery needed)", - recovery_report["total_noop"]) + recovery_report["total_noop"]) else: logger.info("Startup recovery: no non-terminal tasks found, clean start") @@ -1629,7 +1628,7 @@ Parent Task ID: {parent_task.id} return recovered, noop_count def _determine_recovery_action(self, conn, task, status: str, - db_path: Path) -> Optional[str]: + db_path: Path) -> Optional[str]: """根据黑板线索决定恢复动作,返回 None 表示不需要干预""" task_id = task["id"] diff --git a/src/main.py b/src/main.py index 88c04f2..505f797 100644 --- a/src/main.py +++ b/src/main.py @@ -25,6 +25,14 @@ from src.daemon.inbox import InboxWatcher from src.daemon.guardrails import GuardrailEngine from src.utils import get_data_root +from src.api.blackboard_routes import router as blackboard_router +from src.api.checkpoint_routes import router as checkpoint_router +from src.api.daemon_routes import router as daemon_router +from src.api.project_routes import router as project_router +from src.api.sse_routes import router as sse_router +from src.api.mail_routes import router as mail_router +from src.api.toolchain_routes import router as toolchain_router + logger = logging.getLogger("moziplus-v2") # --------------------------------------------------------------------------- @@ -191,7 +199,7 @@ async def lifespan(app: FastAPI): ) # ExperienceDistiller(经验自动蒸馏) - experience_config = config.get("experience", {}) + config.get("experience", {}) experience_distiller = ExperienceDistiller( store=ExperienceStore(store_path=DATA_ROOT / "experiences.jsonl"), ) @@ -252,14 +260,6 @@ app.add_middleware( # API 路由注册 # --------------------------------------------------------------------------- -from src.api.blackboard_routes import router as blackboard_router -from src.api.checkpoint_routes import router as checkpoint_router -from src.api.daemon_routes import router as daemon_router -from src.api.project_routes import router as project_router -from src.api.sse_routes import router as sse_router -from src.api.mail_routes import router as mail_router -from src.api.toolchain_routes import router as toolchain_router - app.include_router(blackboard_router) app.include_router(checkpoint_router) app.include_router(daemon_router) @@ -289,16 +289,17 @@ async def list_projects_compat(): DIST_DIR = Path(__file__).parent / "frontend" / "dist" if DIST_DIR.exists(): # v3.1: 缓存策略 - HTML 不缓存(确保新版本生效),JS/CSS 长缓存(Vite content hash 已处理) - import mimetypes _static_app = StaticFiles(directory=str(DIST_DIR), html=True) - + class CachedStaticFiles: """包装 StaticFiles,添加 Cache-Control 头""" + def __init__(self, app): self._app = app - + async def __call__(self, scope, receive, send): original_send = send + async def patched_send(message): if message.get("type") == "http.response.start": headers = dict(message.get("headers", [])) @@ -310,5 +311,5 @@ if DIST_DIR.exists(): message["headers"] = list(headers.items()) await original_send(message) await self._app(scope, receive, patched_send) - + app.mount("/", CachedStaticFiles(_static_app), name="frontend") diff --git a/src/utils.py b/src/utils.py index 9c3dac7..cf0d20e 100644 --- a/src/utils.py +++ b/src/utils.py @@ -10,7 +10,6 @@ from __future__ import annotations import os from pathlib import Path -from typing import Optional def get_data_root() -> Path: From 242057dfd6ba269f1cc8e5903d396ee2cb40122f Mon Sep 17 00:00:00 2001 From: cfdaily Date: Tue, 9 Jun 2026 22:23:58 +0800 Subject: [PATCH 2/2] fix: remove dead code config.get experience --- src/api/blackboard_routes.py | 10 +++++----- src/api/checkpoint_routes.py | 4 ++-- src/api/mail_routes.py | 4 ++-- src/api/project_routes.py | 4 ++-- src/api/toolchain_routes.py | 4 ++-- src/cli/blackboard.py | 4 ++-- src/daemon/ticker.py | 4 ++-- src/main.py | 5 ++--- 8 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/api/blackboard_routes.py b/src/api/blackboard_routes.py index 1f197b2..5476ac7 100644 --- a/src/api/blackboard_routes.py +++ b/src/api/blackboard_routes.py @@ -15,7 +15,7 @@ from src.blackboard.queries import Queries from src.blackboard.db import VALID_STATUSES, OUTPUT_TYPES from src.blackboard.registry import ProjectRegistry -from src.utils import get_data_root +import src.utils as _utils router = APIRouter(prefix="/api/projects/{project_id}", tags=["blackboard"]) @@ -27,7 +27,7 @@ def _validate_project(project_id: str) -> str: """校验 project_id,已知项目/虚拟项目放行,未知项目返回 400""" if project_id in _VIRTUAL_PROJECTS: return project_id - reg = ProjectRegistry(get_data_root()) + reg = ProjectRegistry(_utils.get_data_root()) if reg.get_project(project_id): return project_id raise HTTPException(400, { @@ -43,12 +43,12 @@ def _validate_project(project_id: str) -> str: def _bb(project_id: str) -> Blackboard: _validate_project(project_id) - return Blackboard(get_data_root() / project_id / "blackboard.db") + return Blackboard(_utils.get_data_root() / project_id / "blackboard.db") def _q(project_id: str) -> Queries: _validate_project(project_id) - return Queries(get_data_root() / project_id / "blackboard.db") + return Queries(_utils.get_data_root() / project_id / "blackboard.db") # --- Tasks --- @@ -100,7 +100,7 @@ async def create_task(project_id: str, body: Dict[str, Any]): date_str = datetime.now().strftime('%Y%m%d') # seq: 查当前项目最大 seq import sqlite3 - db_path = get_data_root() / project_id / "blackboard.db" + db_path = _utils.get_data_root() / project_id / "blackboard.db" try: conn = sqlite3.connect(str(db_path), timeout=5) max_id_row = conn.execute( diff --git a/src/api/checkpoint_routes.py b/src/api/checkpoint_routes.py index c713067..0b3d357 100644 --- a/src/api/checkpoint_routes.py +++ b/src/api/checkpoint_routes.py @@ -10,7 +10,7 @@ from pydantic import BaseModel from typing import Optional from src.blackboard.operations import Blackboard -from src.utils import get_data_root +import src.utils as _utils router = APIRouter(prefix="/api/projects/{project_id}/tasks/{task_id}/checkpoints", tags=["checkpoints"]) @@ -33,7 +33,7 @@ class ResolveCheckpointRequest(BaseModel): # ── 工具 ── def _bb(project_id: str) -> Blackboard: - db_path = get_data_root() / project_id / "blackboard.db" + db_path = _utils.get_data_root() / project_id / "blackboard.db" if not db_path.exists(): raise HTTPException(status_code=404, detail="Project not found") return Blackboard(db_path) diff --git a/src/api/mail_routes.py b/src/api/mail_routes.py index 4a6e6d9..d6542ed 100644 --- a/src/api/mail_routes.py +++ b/src/api/mail_routes.py @@ -17,7 +17,7 @@ from src.blackboard.db import init_db from src.blackboard.models import Task from src.blackboard.operations import Blackboard from src.blackboard.queries import Queries -from src.utils import get_data_root +import src.utils as _utils def _get_valid_agents() -> set: @@ -43,7 +43,7 @@ MAIL_PROJECT_ID = "_mail" def _db_path() -> Path: - root = get_data_root() + root = _utils.get_data_root() db = root / MAIL_PROJECT_ID / "blackboard.db" db.parent.mkdir(parents=True, exist_ok=True) init_db(db) diff --git a/src/api/project_routes.py b/src/api/project_routes.py index 0e3d209..06d5247 100644 --- a/src/api/project_routes.py +++ b/src/api/project_routes.py @@ -8,13 +8,13 @@ from typing import Any, Dict from fastapi import APIRouter, HTTPException, Query from src.blackboard.registry import ProjectRegistry -from src.utils import get_data_root +import src.utils as _utils router = APIRouter(prefix="/api/projects", tags=["projects"]) def _registry() -> ProjectRegistry: - return ProjectRegistry(get_data_root()) + return ProjectRegistry(_utils.get_data_root()) @router.get("") diff --git a/src/api/toolchain_routes.py b/src/api/toolchain_routes.py index 73a0c44..27b9c86 100644 --- a/src/api/toolchain_routes.py +++ b/src/api/toolchain_routes.py @@ -28,7 +28,7 @@ from src.blackboard.models import Task from src.blackboard.operations import Blackboard from src.config.agents import AGENT_IDS from src.daemon.toolchain_templates import render_template -from src.utils import get_data_root +import src.utils as _utils logger = logging.getLogger(__name__) @@ -146,7 +146,7 @@ MAIL_PROJECT_ID = "_mail" def _mail_db_path() -> Path: """获取 Mail 数据库路径,确保目录存在。""" - root = get_data_root() + root = _utils.get_data_root() db = root / MAIL_PROJECT_ID / "blackboard.db" db.parent.mkdir(parents=True, exist_ok=True) init_db(db) diff --git a/src/cli/blackboard.py b/src/cli/blackboard.py index 6025779..dc5690f 100644 --- a/src/cli/blackboard.py +++ b/src/cli/blackboard.py @@ -9,14 +9,14 @@ from pathlib import Path from typing import List, Optional from src.blackboard.operations import Blackboard -from src.utils import get_data_root +import src.utils as _utils from src.blackboard.models import Task, Review from src.blackboard.queries import Queries from src.blackboard.registry import ProjectRegistry def _find_project_root() -> Path: - return get_data_root() + return _utils.get_data_root() def _get_bb(project_id: str) -> Blackboard: diff --git a/src/daemon/ticker.py b/src/daemon/ticker.py index 251c0b8..7b86317 100644 --- a/src/daemon/ticker.py +++ b/src/daemon/ticker.py @@ -664,8 +664,8 @@ Parent Task ID: {parent_task.id} def _resolve_db_path(self, project_id: str) -> Path: """解析项目 DB 路径""" - from src.utils import get_data_root - return get_data_root() / project_id / "blackboard.db" + import src.utils as _utils + return _utils.get_data_root() / project_id / "blackboard.db" # ------------------------------------------------------------------ # @mention 通知处理 (v2.9 #01) diff --git a/src/main.py b/src/main.py index 505f797..4ebeeaa 100644 --- a/src/main.py +++ b/src/main.py @@ -23,7 +23,7 @@ from src.daemon.health import HealthChecker from src.daemon.experience import ExperienceDistiller, ExperienceStore from src.daemon.inbox import InboxWatcher from src.daemon.guardrails import GuardrailEngine -from src.utils import get_data_root +import src.utils as _utils from src.api.blackboard_routes import router as blackboard_router from src.api.checkpoint_routes import router as checkpoint_router @@ -86,7 +86,7 @@ config = load_config() # 全局组件 # --------------------------------------------------------------------------- -DATA_ROOT = get_data_root() +DATA_ROOT = _utils.get_data_root() ticker: Optional[Ticker] = None @@ -199,7 +199,6 @@ async def lifespan(app: FastAPI): ) # ExperienceDistiller(经验自动蒸馏) - config.get("experience", {}) experience_distiller = ExperienceDistiller( store=ExperienceStore(store_path=DATA_ROOT / "experiences.jsonl"), )