Files
Yaojia Wang 5fb7bfb1de feat: M3 — 伏笔账本 + 节奏引擎 + 大纲(含并发记账 bugfix)
- 伏笔账本:纯函数状态机(OPEN/PARTIAL/CLOSED/OVERDUE) + ForeshadowLedger repo;验收后到期扫描(BackgroundTask 自建 session 置 OVERDUE);登记/状态变更端点
- 节奏 + 三审齐:foreshadow-analyst + pace-checker 并入 LangGraph 并行审(REVIEW_SPECS),collect 分列落 chapter_reviews(conflicts/foreshadow_sug/pace),review SSE 加 foreshadow/pace 事件
- 大纲:outliner Agent 产 OutlineResult(含 foreshadow_windows),POST /outline 逐章 upsert outline 表;GET /foreshadow?status= 看板
- 前端:伏笔四泳道看板(OVERDUE 琥珀) + 大纲编辑器(窗口徽标) + 节奏节拍图(▁▃▅) + 审稿页消费 foreshadow/pace SSE
- bugfix(T3.8):并行三审共用请求 session 记账触发 'Session is already flushing' → foreshadow/pace 静默丢失;SqlAlchemyLedgerSink.record 改 add-only(靠端点/事务 commit),加并发回归测试
- M3 E2E:真实 DB + mock 网关零 token 走通 埋设→进展→验收后扫描 OVERDUE→看板 + 大纲含窗口 + 三审齐 SSE/留痕;E2E 暴露并钉住上述 bug
- 门禁绿:mypy 111 / pytest 228(0 xfailed) / alembic 无漂移;前端 gen:api/lint/tsc/vitest 69/build
2026-06-18 14:21:17 +02:00

52 lines
2.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""用量记账落库ARCH §4.8)。
`LedgerSink` 为接口,便于测试注入内存替身;生产用 SQLAlchemy 实现写 usage_ledger。
"""
from __future__ import annotations
from typing import Protocol
from sqlalchemy.ext.asyncio import AsyncSession
from ww_db.models import UsageLedger
from .types import Scope, Usage
class LedgerSink(Protocol):
async def record(self, scope: Scope, usage: Usage) -> None: ...
class SqlAlchemyLedgerSink:
"""把每次调用写入 usage_ledgerowner_id 取 scope.user_id单用户 stub"""
def __init__(self, session: AsyncSession) -> None:
self._session = session
async def record(self, scope: Scope, usage: Usage) -> None:
"""把一次调用追加进 session**只 add不 flush/commit**)。
并发安全T3.8 bugfix三审作为同一 LangGraph superstep 的并行分支
共用一个请求 session 的本 sink。`AsyncSession` 非并发安全——若在此
`await self._session.flush()`,第二/三审的 flush 会撞上「Session is already
flushing」flush 重入),被 `run_review` 失败隔离吞成 `incomplete`
foreshadow/pace 结果静默丢失。`session.add()` 是同步、无 await、不让出
控制权,故并行协程不会在 add 中途交错。
持久化靠调用方:每个端点/验收事务在流耗尽/事务末尾 `commit()`commit
自动 flush 全部待决行),与「网关 ledger 只 add/flush、端点或事务负责
commit」的既有边界一致见 gotcha「网关 ledger 只 flush、调用方必须 commit」
"""
row = UsageLedger(
owner_id=scope.user_id,
project_id=scope.project_id,
provider=usage.provider,
model=usage.model,
input_tokens=usage.input_tokens,
output_tokens=usage.output_tokens,
cache_read=usage.cache_read_tokens,
cost_minor=usage.cost_minor,
currency=usage.currency,
)
self._session.add(row)