Skip to content

Commit 33b8f2e

Browse files
author
jinsongwang
committed
feat: add type annotations to all 117 functions across 14 modules (#7)
- 100% coverage: all function parameters and return values annotated - Add to all modules that need it - Python 3.9+ compatible: use Optional[X] not X | None, list[str] not List[str] - Include nested functions (_run_one, parse_and_log, _on_interrupt, etc.) - 163 tests pass with no regressions Closes #7
1 parent da1ac04 commit 33b8f2e

14 files changed

Lines changed: 121 additions & 106 deletions

agent_go/api.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from concurrent.futures import ThreadPoolExecutor, as_completed
33
from pathlib import Path
44
from datetime import datetime
5+
from typing import Any, Optional
56

67
from .config import get_api_key, log_event, DECOMPOSE_RULES, AGENT_GO_DIR
78
from .git_utils import analyze_project, get_git_info, get_resource_map
@@ -15,7 +16,7 @@
1516
"list_cache_entries", "clean_expired_cache",
1617
]
1718

18-
def call_api(config, messages, logger):
19+
def call_api(config: dict[str, Any], messages: list[dict[str, Any]], logger: logging.Logger) -> str:
1920
api_cfg = config["plan_api"]
2021
provider = api_cfg.get("provider", "anthropic")
2122
base_url = api_cfg["base_url"]
@@ -94,7 +95,7 @@ def call_api(config, messages, logger):
9495
})
9596
raise RuntimeError(f"连接超时或 IO 错误 ({provider}): {e}") from e
9697

97-
def generate_plan(task, repo, config, logger, supplement="", reference_docs="", iteration=1, skill_context="", no_cache=False) -> dict:
98+
def generate_plan(task: str, repo: Path, config: dict[str, Any], logger: logging.Logger, supplement: str = "", reference_docs: str = "", iteration: int = 1, skill_context: str = "", no_cache: bool = False) -> dict[str, Any]:
9899
plan_start = time.time()
99100
logger.info("[PLAN] ═══ PLAN MODE ═══")
100101
logger.info(f"[PLAN] 第 {iteration} 次生成")
@@ -247,7 +248,7 @@ def generate_plan(task, repo, config, logger, supplement="", reference_docs="",
247248
save_cached_plan(cache_key, plan, task, repo, config)
248249
return plan
249250

250-
def decompose_fallback(task, repo, config, logger):
251+
def decompose_fallback(task: str, repo: Path, config: dict[str, Any], logger: logging.Logger) -> list[dict[str, Any]]:
251252
logger.warning("Plan Mode 失败,降级")
252253
local_url = config.get("fallback", {}).get("local_model_url", "http://localhost:8000/v1/chat/completions")
253254
local_name = config.get("fallback", {}).get("local_model_name", "qwen")
@@ -284,13 +285,13 @@ def decompose_fallback(task, repo, config, logger):
284285
# Plan Cache
285286
# ═══════════════════════════════════════════════════════════════
286287

287-
def _cache_dir():
288+
def _cache_dir() -> Path:
288289
d = AGENT_GO_DIR / "cache" / "plans"
289290
d.mkdir(parents=True, exist_ok=True)
290291
return d
291292

292293

293-
def get_cache_key(task, repo):
294+
def get_cache_key(task: str, repo: Path) -> str:
294295
"""SHA256(task + project_files[0:100] + remote + branch)。"""
295296
project_files = analyze_project(repo)
296297
git_info = get_git_info(repo)
@@ -304,7 +305,7 @@ def get_cache_key(task, repo):
304305
return hashlib.sha256("|".join(key_parts).encode()).hexdigest()
305306

306307

307-
def load_cached_plan(cache_key, config, logger):
308+
def load_cached_plan(cache_key: str, config: dict[str, Any], logger: logging.Logger) -> Optional[dict[str, Any]]:
308309
cache_dir = _cache_dir()
309310
cache_file = cache_dir / cache_key[:2] / f"{cache_key}.json"
310311
if not cache_file.exists():
@@ -343,7 +344,7 @@ def load_cached_plan(cache_key, config, logger):
343344
return plan
344345

345346

346-
def save_cached_plan(cache_key, plan, task, repo, config):
347+
def save_cached_plan(cache_key: str, plan: dict[str, Any], task: str, repo: Path, config: dict[str, Any]) -> None:
347348
cache_cfg = config.get("cache", {})
348349
if not cache_cfg.get("enabled", True):
349350
return
@@ -367,7 +368,7 @@ def save_cached_plan(cache_key, plan, task, repo, config):
367368
json.dumps(entry, indent=2, ensure_ascii=False), encoding="utf-8")
368369

369370

370-
def _format_age(iso_str):
371+
def _format_age(iso_str: str) -> str:
371372
try:
372373
age = time.time() - datetime.strptime(iso_str, "%Y-%m-%dT%H:%M:%S").timestamp()
373374
if age < 3600:
@@ -379,7 +380,7 @@ def _format_age(iso_str):
379380
return "?"
380381

381382

382-
def list_cache_entries():
383+
def list_cache_entries() -> list[dict[str, Any]]:
383384
entries = []
384385
cache_dir = _cache_dir()
385386
for subdir in sorted(cache_dir.glob("*")):
@@ -393,7 +394,7 @@ def list_cache_entries():
393394
return sorted(entries, key=lambda e: e.get("meta", {}).get("created_at", ""), reverse=True)
394395

395396

396-
def clean_expired_cache(config):
397+
def clean_expired_cache(config: dict[str, Any]) -> int:
397398
ttl = config.get("cache", {}).get("plan_ttl", 86400)
398399
now = time.time()
399400
removed = 0

agent_go/cli.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from concurrent.futures import ThreadPoolExecutor, as_completed
33
from pathlib import Path
44
from datetime import datetime
5+
from typing import Any, Optional
56

67
from .config import load_config, safe_input, setup_logger, AGENT_GO_DIR
78
from .api import generate_plan, decompose_fallback
@@ -19,7 +20,7 @@
1920
"cmd_status", "cmd_config", "cmd_clean", "cmd_pr", "cmd_review",
2021
]
2122

22-
def cmd_run():
23+
def cmd_run() -> None:
2324
# 解析参数
2425
repo_idx = 2
2526
task_idx = 3
@@ -249,7 +250,7 @@ def cmd_run():
249250

250251
_run_pipeline(confirmed, repo, task_dir, logger, config, headless, parallel, issue_ref, meta, remote_url=remote_url)
251252

252-
def cmd_resume():
253+
def cmd_resume() -> None:
253254
"""恢复被中断的任务。"""
254255
if len(sys.argv) < 3:
255256
print("Usage: agent_go resume <task-id> [--yes] [--headless] [--parallel N] [--remote <url>]")
@@ -329,7 +330,7 @@ def cmd_resume():
329330
_run_pipeline(confirmed, repo, task_dir, logger, config, headless, parallel, issue_ref, meta,
330331
worktree_map, results_map, completed_ids, remote_url=remote_url)
331332

332-
def cmd_list():
333+
def cmd_list() -> None:
333334
tasks = sorted(AGENT_GO_DIR.glob("task-*"))
334335
if not tasks:
335336
print("暂无任务")
@@ -346,7 +347,7 @@ def cmd_list():
346347
docs = ",".join(meta.get("reference_docs", []))[:15]
347348
print(f"{t.name:<25} {icon} {status:<10} {len(meta.get('subtasks',[])):<8} {docs:<12} {meta.get('task','')[:30]}")
348349

349-
def cmd_show():
350+
def cmd_show() -> None:
350351
if len(sys.argv) < 3:
351352
print("Usage: agent_go show <task-id>")
352353
sys.exit(1)
@@ -382,7 +383,7 @@ def cmd_show():
382383
if r:
383384
print(f" 📊 {r['summary']}")
384385

385-
def cmd_review():
386+
def cmd_review() -> None:
386387
"""代码审查:使用 Claude 审查项目变更。"""
387388
if len(sys.argv) < 3:
388389
print("Usage: agent_go review <repo-path> [--pr <N>] [--yes]")
@@ -415,7 +416,7 @@ def cmd_review():
415416
subprocess.run(["claude", str(repo)])
416417

417418

418-
def cmd_pr():
419+
def cmd_pr() -> None:
419420
"""根据已完成任务的 meta.json + git log 生成 PR 描述。"""
420421
if len(sys.argv) < 3:
421422
print("Usage: agent_go pr <task-id> [--offline]")
@@ -481,20 +482,20 @@ def cmd_pr():
481482
print(f"PR 描述已备份到 {task_dir}/PR.md")
482483
os.unlink(pr_file)
483484

484-
def cmd_status():
485+
def cmd_status() -> None:
485486
"""实时监控所有任务状态。默认 TUI 模式。--no-tui 回退文本模式。"""
486487
if "--no-tui" in sys.argv:
487488
_cmd_status_text()
488489
else:
489490
cmd_status_tui()
490491

491492

492-
def _cmd_status_text():
493+
def _cmd_status_text() -> None:
493494
"""文本模式(原有实现)。--watch 持续刷新,--verbose 显示 Claude 事件。"""
494495
watch = "--watch" in sys.argv or "-w" in sys.argv
495496
verbose = "--verbose" in sys.argv or "-v" in sys.argv
496497

497-
def _get_task_tail_lines(log_path, count=2):
498+
def _get_task_tail_lines(log_path: Path, count: int = 2) -> list[str]:
498499
"""从执行日志尾部提取最后 count 条 Claude 事件。"""
499500
if not log_path.exists():
500501
return []
@@ -506,7 +507,7 @@ def _get_task_tail_lines(log_path, count=2):
506507
or "[tool_result]" in l or "[result]" in l]
507508
return claude_lines[-count:]
508509

509-
def _get_task_status(task_dir):
510+
def _get_task_status(task_dir: Path) -> Optional[dict[str, Any]]:
510511
meta_path = task_dir / "meta.json"
511512
if not meta_path.exists():
512513
return None
@@ -599,11 +600,11 @@ def _get_task_status(task_dir):
599600
break
600601
time.sleep(5)
601602

602-
def cmd_config():
603+
def cmd_config() -> None:
603604
config = load_config()
604605
print(json.dumps(config, indent=2, ensure_ascii=False))
605606

606-
def cmd_clean():
607+
def cmd_clean() -> None:
607608
import shutil as _shutil
608609
tasks = sorted(AGENT_GO_DIR.glob("task-*"))
609610
if not tasks:
@@ -647,7 +648,7 @@ def cmd_clean():
647648
else:
648649
print("已取消")
649650

650-
def cmd_skills():
651+
def cmd_skills() -> None:
651652
"""列出所有可用的 Skill。"""
652653
skills = list_skills()
653654
if not skills:
@@ -661,7 +662,7 @@ def cmd_skills():
661662
print(f" {s['name']:<30} {desc}")
662663
print("─" * 55)
663664

664-
def cmd_cache():
665+
def cmd_cache() -> None:
665666
"""Plan 缓存管理。"""
666667
from .api import list_cache_entries, clean_expired_cache
667668

@@ -708,7 +709,7 @@ def cmd_cache():
708709
print(f"未知子命令: {sub}。可用: list, clean, clear, stats")
709710

710711

711-
def _cache_size():
712+
def _cache_size() -> str:
712713
from .api import _cache_dir
713714
d = _cache_dir()
714715
total = 0
@@ -721,7 +722,7 @@ def _cache_size():
721722
return f"{total / 1024 / 1024:.1f}MB"
722723

723724

724-
def cmd_agents():
725+
def cmd_agents() -> None:
725726
"""列出所有可用的 Agent 类型。"""
726727
agents = list_agent_types()
727728
print(f"\n🤖 Agent 类型 ({len(agents)} 种)")
@@ -732,7 +733,7 @@ def cmd_agents():
732733
print(f" {a['type']:<25} [{src}] {desc}")
733734
print("─" * 55)
734735

735-
def main():
736+
def main() -> None:
736737
try:
737738
cmd = sys.argv[1] if len(sys.argv) > 1 else "help"
738739
if cmd == "run": cmd_run()

agent_go/config.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from concurrent.futures import ThreadPoolExecutor, as_completed
33
from pathlib import Path
44
from datetime import datetime
5+
from typing import Any
56

67
__all__ = [
78
"AGENT_GO_DIR", "CONFIG_PATH", "DEFAULT_CONFIG", "DECOMPOSE_RULES",
@@ -66,15 +67,15 @@
6667
},
6768
]
6869

69-
def safe_input(prompt=""):
70+
def safe_input(prompt: str = "") -> str:
7071
"""包装 input(),在非交互模式下返回空字符串(触发默认确认路径)。"""
7172
try:
7273
return input(prompt)
7374
except EOFError:
7475
print()
7576
return ""
7677

77-
def load_config():
78+
def load_config() -> dict[str, Any]:
7879
if CONFIG_PATH.exists():
7980
saved = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
8081
merged = json.loads(json.dumps(DEFAULT_CONFIG)) # deep copy
@@ -89,10 +90,10 @@ def load_config():
8990
print(f"⚙️ 已创建默认配置: {CONFIG_PATH}")
9091
return DEFAULT_CONFIG
9192

92-
def get_api_key(config):
93+
def get_api_key(config: dict[str, Any]) -> str:
9394
return os.environ.get("AGENT_GO_API_KEY", "") or config.get("plan_api", {}).get("api_key", "")
9495

95-
def setup_logger(task_id, task_dir):
96+
def setup_logger(task_id: str, task_dir: Path) -> logging.Logger:
9697
logger = logging.getLogger(f"agent_go.{task_id}")
9798
logger.setLevel(logging.DEBUG)
9899
for h in list(logger.handlers):
@@ -108,5 +109,5 @@ def setup_logger(task_id, task_dir):
108109
logger.addHandler(ch)
109110
return logger
110111

111-
def log_event(logger, event, data):
112+
def log_event(logger: logging.Logger, event: str, data: dict[str, Any]) -> None:
112113
logger.debug(json.dumps({"timestamp": datetime.now().isoformat(), "event": event, **data}, ensure_ascii=False))

0 commit comments

Comments
 (0)