65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
"""任务状态: DESIGN.md §7.1 规约,落 `<task_dir>/state.json`。
|
|
|
|
Task 是 Session 的上层概念 —— Session 只管对话消息,Task 还管 mode/description/
|
|
status/tokens/cost/timestamps,这些是跨轮次共享、和文件系统状态对齐的元数据。
|
|
|
|
文件路径约定(workspace/ 下):
|
|
tasks/<task_id>/state.json ← 此模块负责
|
|
tasks/<task_id>/messages.json ← Session 落
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import asdict, dataclass, fields
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from .session import atomic_write_text
|
|
|
|
|
|
@dataclass
|
|
class TaskState:
|
|
task_id: str
|
|
mode: str = "" # 自由形式: coding / ppt / proposal / general / 自定
|
|
description: str = "" # 一句话描述,便于列表识别
|
|
status: str = "active" # active / completed / abandoned
|
|
model: str = "" # caps.model_id
|
|
model_profile: str = "" # 档案名,如 deepseek_v4.flash
|
|
reasoning_effort: str = ""
|
|
cwd: str = "" # 任务的工作基目录
|
|
created_at: str = "" # ISO 时间戳
|
|
updated_at: str = ""
|
|
tokens_prompt: int = 0
|
|
tokens_completion: int = 0
|
|
cost_usd: float = 0.0 # 暂不算,留位
|
|
|
|
@property
|
|
def tokens_total(self) -> int:
|
|
return self.tokens_prompt + self.tokens_completion
|
|
|
|
def save(self, task_dir: Path) -> None:
|
|
self.updated_at = datetime.now().isoformat(timespec="seconds")
|
|
atomic_write_text(
|
|
task_dir / "state.json",
|
|
json.dumps(asdict(self), ensure_ascii=False, indent=2),
|
|
)
|
|
|
|
@classmethod
|
|
def load(cls, task_dir: Path) -> Optional["TaskState"]:
|
|
p = task_dir / "state.json"
|
|
if not p.exists():
|
|
return None
|
|
try:
|
|
data = json.loads(p.read_text(encoding="utf-8"))
|
|
except Exception:
|
|
return None
|
|
if not isinstance(data, dict):
|
|
return None
|
|
# 容忍 schema 演化:只取已知字段,缺失字段用 dataclass 默认
|
|
known = {f.name for f in fields(cls)}
|
|
kwargs = {k: v for k, v in data.items() if k in known}
|
|
if "task_id" not in kwargs:
|
|
kwargs["task_id"] = task_dir.name
|
|
return cls(**kwargs)
|