diff --git a/backend/app/engine/judge.py b/backend/app/engine/judge.py index c9f6bc90..27077ded 100644 --- a/backend/app/engine/judge.py +++ b/backend/app/engine/judge.py @@ -856,7 +856,7 @@ JUDGE_DEFAULTS: dict[str, Any] = { "daily_max_usd": 2.0, # 全局每日裁判金额上限(按本地日期) } #: 每日计数存在设置表的这一行:{date: 本地日期, usd, calls, unpriced_calls}。不从设置接口进出(账本见 engine/spend.py) -SPEND_KEY = spend.judge_spend.key +SPEND_KEY = "judge_spend" _COUNTS = frozenset({"report_max_claims"}) _SECONDS = frozenset({"report_timeout_s"}) @@ -1024,8 +1024,14 @@ async def source_masks(catalog: dict[str, Any], *, loader: Callable[[str], Any] # 每日计数 # -------------------------------------------------------------------------- +def _today() -> str: + """裁判这本账的日历:本地日期(同 engine/spend._today)。单独留一个名字:测试换掉它来模拟裁判这边跨天。""" + return spend._today() + + #: 裁判这本每日账(engine/spend.py 的 Ledger:预留、找补、按本地日期滚动和核查、评测是同一套代码,各记各的键) -_LEDGER = spend.judge_spend +judge_spend = spend.Ledger(SPEND_KEY, today=lambda: _today()) +_LEDGER = judge_spend async def daily_spend() -> dict[str, Any]: diff --git a/backend/app/engine/spend.py b/backend/app/engine/spend.py index 900324fd..87510ba7 100644 --- a/backend/app/engine/spend.py +++ b/backend/app/engine/spend.py @@ -7,7 +7,7 @@ 要是再抄一份就是第三份。现在都是 Ledger(key) 的实例,各记各的键: - verify_spend:核查——双盲复算、反方审稿、审批质询里的模型调用; -- judge_spend:结论句裁判(engine/judge.py); +- judge_spend:结论句裁判(实例在 engine/judge.py,键 judge_spend:日历跟着 judge._today,裁判的测试换它模拟跨天); - eval_spend:评测(预留,第 3 波的评测执行接上;设置和接口在那时加)。 **为什么各记各的。** 核查是一个 agent 查库、来回好几轮,一次花的钱比裁判判一份报告多得多;评测一次几十道题。记在同一本 @@ -111,9 +111,8 @@ class Ledger: await session.commit() -#: 核查、裁判、评测各一本 +#: 核查、评测各一本(裁判那本在 engine/judge.py 的 judge_spend) verify_spend = Ledger("verify_spend") -judge_spend = Ledger("judge_spend") eval_spend = Ledger("eval_spend") #: 核查那本账的老名字(verify_agent、设置接口、已有测试从这里取) @@ -135,4 +134,4 @@ async def settle(date: str, delta: float) -> None: await verify_spend.settle(date, delta) -__all__ = ["Ledger", "SPEND_KEY", "charge", "daily_spend", "eval_spend", "judge_spend", "settle", "verify_spend"] +__all__ = ["Ledger", "SPEND_KEY", "charge", "daily_spend", "eval_spend", "settle", "verify_spend"] diff --git a/backend/app/tools/datasource.py b/backend/app/tools/datasource.py index d38ae483..255be841 100644 --- a/backend/app/tools/datasource.py +++ b/backend/app/tools/datasource.py @@ -754,7 +754,7 @@ async def _store_schema(source: Any, ctx: ToolContext, *, # 「本次运行目录:关」。不进内容哈希,缺省视图不记,快照和以前一字不差 from app.data.catalog_view import CatalogView, describe - if (view := describe(CatalogView.parse(ctx.catalog_view))) is not None: + if (view := describe(CatalogView.parse(getattr(ctx, "catalog_view", None)))) is not None: meta["catalog_view"] = view try: return await put_json(content, kind=SCHEMA_SNAPSHOT, run_id=ctx.run_id or "", node_id=ctx.node_id or "", diff --git a/backend/tests/test_evidence_judge.py b/backend/tests/test_evidence_judge.py index c742ccf1..f46d6a62 100644 --- a/backend/tests/test_evidence_judge.py +++ b/backend/tests/test_evidence_judge.py @@ -30,7 +30,6 @@ from app.core.events import EventType from app.db.base import SessionLocal from app.db.models import Setting from app.engine import judge as judge_mod -from app.engine import spend from app.engine.evidence import build_catalog, compose_doc, iter_units from app.engine.judge import ( BATCH_SIZE, @@ -504,7 +503,7 @@ async def test_unlimited_never_truncates(monkeypatch, stored): raw = "".join(f"第 {i} 个区域的销售额增长来自新客。[[see:m:gmv]]" for i in range(BATCH_SIZE + 5)) doc = compose_doc(raw, catalog) async with SessionLocal() as session: # 今天已经花掉很多:不限就是不限 - session.add(Setting(key=SPEND_KEY, value={"date": spend._today(), "usd": 999.0, "calls": 9})) + session.add(Setting(key=SPEND_KEY, value={"date": judge_mod._today(), "usd": 999.0, "calls": 9})) await session.commit() outcome = await judge_doc(doc, catalog, spec=SPEC, budget=UNLIMITED) assert [len(units) for units in judge.judged] == [BATCH_SIZE, 5] @@ -518,8 +517,7 @@ async def test_the_daily_cap_resets_the_next_day(monkeypatch, stored): cost = prepare(doc, catalog).cost(PRICED, prepare(doc, catalog).cands) budget = Budget(max_claims=None, max_cost_usd=None, timeout_s=None, daily_max_usd=cost * 1.05) day = {"today": "2026-09-28"} - # 裁判的账和核查、评测按同一个本地日期滚动(engine/spend.py 的 Ledger) - monkeypatch.setattr(spend, "_today", lambda: day["today"]) + monkeypatch.setattr(judge_mod, "_today", lambda: day["today"]) judge = Judge(monkeypatch) first = await judge_doc(doc, catalog, spec=SPEC, budget=budget) diff --git a/backend/tests/test_spend_ledger.py b/backend/tests/test_spend_ledger.py index a847ed7e..364f9f12 100644 --- a/backend/tests/test_spend_ledger.py +++ b/backend/tests/test_spend_ledger.py @@ -12,7 +12,8 @@ from sqlalchemy import delete from app.db.base import SessionLocal from app.db.models import Setting from app.engine import judge, spend -from app.engine.spend import Ledger, eval_spend, judge_spend, verify_spend +from app.engine.judge import judge_spend +from app.engine.spend import Ledger, eval_spend, verify_spend @pytest.fixture(autouse=True) @@ -70,9 +71,12 @@ async def test_one_book_hitting_its_cap_does_not_squeeze_the_others(): assert (await eval_spend.daily())["usd"] == pytest.approx(1.5) -async def test_all_books_roll_over_on_the_same_clock(monkeypatch): - """三本账按同一个本地日期滚动(spend._today):裁判不再自己另有一份时钟。""" +async def test_all_books_roll_over_on_the_local_date(monkeypatch): + """三本账按同一个本地日期滚动(spend._today);裁判那本另留一个 judge._today,换它只动裁判这一本。""" monkeypatch.setattr(spend, "_today", lambda: "2026-01-02") assert await judge_spend.charge(0.1, limit=None, priced=True) == "2026-01-02" assert (await judge.daily_spend())["date"] == "2026-01-02" assert (await eval_spend.daily())["date"] == "2026-01-02" + monkeypatch.setattr(judge, "_today", lambda: "2026-01-03") + assert (await judge.daily_spend())["date"] == "2026-01-03" + assert (await verify_spend.daily())["date"] == "2026-01-02"