Coverage for src\genesis\state_machine.py: 100%
40 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 14:20 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 14:20 +0800
1from __future__ import annotations
3from typing import Literal
5# 会话级状态集(9 个):8 个设计态 + cancelled(T3 架构审查整改)
6SessionState = Literal[
7 "uploading",
8 "parsing",
9 "awaiting_parse_confirm",
10 "impact_running",
11 "awaiting_impact_confirm",
12 "writing",
13 "qa",
14 "done",
15 "cancelled",
16]
18STATES: frozenset[str] = frozenset({
19 "uploading",
20 "parsing",
21 "awaiting_parse_confirm",
22 "impact_running",
23 "awaiting_impact_confirm",
24 "writing",
25 "qa",
26 "done",
27 "cancelled",
28})
30# 合法转移白名单(不含 cancelled 的动态 resume 转移,见 SessionStateMachine.resume)
31_TRANSITIONS: dict[str, frozenset[str]] = {
32 "uploading": frozenset({"parsing"}),
33 "parsing": frozenset({"awaiting_parse_confirm", "cancelled"}),
34 "awaiting_parse_confirm": frozenset({"impact_running", "parsing", "cancelled"}),
35 "impact_running": frozenset({"awaiting_impact_confirm", "cancelled"}),
36 "awaiting_impact_confirm": frozenset({"writing", "impact_running", "awaiting_parse_confirm", "cancelled"}),
37 "writing": frozenset({"qa", "awaiting_impact_confirm", "cancelled"}),
38 "qa": frozenset({"done", "writing", "cancelled"}),
39 "done": frozenset(),
40 "cancelled": frozenset(),
41}
43# 可被取消的(非终态、非人工等待确认态之外全部执行中;done 不可取消)
44_CANCELLABLE: frozenset[str] = frozenset({
45 "parsing",
46 "impact_running",
47 "writing",
48 "qa",
49})
52class StateTransitionError(Exception):
53 """非法状态转移(对应 api-design §7 STATE_TRANSITION_INVALID 409)。"""
56class SessionStateMachine:
57 """会话级流程状态机:白名单转移 + cancelled/resume(T3)。
59 取消(cancel)从任意执行中状态进入 cancelled 终态,并记录中断前状态
60 (cancelled_from);resume 从 cancelled 回到中断前状态,恢复后继续
61 正常白名单流转。人工等待确认态(awaiting_*)与 done 不可取消。
62 """
64 def __init__(self, initial: str = "uploading") -> None:
65 if initial not in STATES:
66 raise StateTransitionError(f"未知初始状态: {initial}")
67 self._state: str = initial
68 self._cancelled_from: str | None = None
70 @property
71 def state(self) -> str:
72 return self._state
74 @property
75 def cancelled_from(self) -> str | None:
76 """取消前的中断状态(resume 目标);仅 cancelled 态非 None。"""
77 return self._cancelled_from
79 def transition(self, target: str) -> str:
80 """按白名单推进状态机;非法转移抛 StateTransitionError。"""
81 if target not in STATES:
82 raise StateTransitionError(f"未知目标状态: {target}")
83 allowed = _TRANSITIONS[self._state]
84 if target not in allowed:
85 raise StateTransitionError(
86 f"非法状态转移: {self._state} → {target}(白名单外)"
87 )
88 self._state = target
89 return self._state
91 def cancel(self) -> str:
92 """取消当前执行:进入 cancelled 终态,记录中断前状态。"""
93 if self._state not in _CANCELLABLE:
94 raise StateTransitionError(
95 f"当前状态不可取消: {self._state}(仅执行中状态可取消)"
96 )
97 self._cancelled_from = self._state
98 self._state = "cancelled"
99 return self._state
101 def resume(self) -> str:
102 """从 cancelled 恢复:回到中断前状态(cancelled_from)。"""
103 if self._state != "cancelled":
104 raise StateTransitionError(
105 f"仅 cancelled 状态可 resume(当前: {self._state})"
106 )
107 assert self._cancelled_from is not None # cancelled 态必有记录
108 self._state = self._cancelled_from
109 self._cancelled_from = None
110 return self._state