修复: 同窗统一消费实体出场章
This commit is contained in:
parent
d015f1d321
commit
7f675922b2
@ -2398,6 +2398,13 @@ def _run(work_id, max_windows, max_calls, model, redo_window, semantic_on):
|
||||
did = name_map[nm][0]
|
||||
to_update.setdefault(did, []).append(it["观察点"])
|
||||
pres_add.setdefault(did, set()).update(_int_chaps(it.get("出场章")))
|
||||
# 在任何实体更新写入前汇总纯出场,确保同卡全部章节由一次 merge 或机械收尾消费。
|
||||
for it in obs.get("纯出场", []):
|
||||
nm = (it.get("名称") or "").strip()
|
||||
if nm in name_map:
|
||||
pres_add.setdefault(name_map[nm][0], set()).update(
|
||||
_int_chaps(it.get("出场章"))
|
||||
)
|
||||
# ④ 卡更新(分批≤6)
|
||||
items = sorted(to_update.items())
|
||||
for i in range(0, len(items), UPDATE_BATCH):
|
||||
@ -2501,13 +2508,7 @@ def _run(work_id, max_windows, max_calls, model, redo_window, semantic_on):
|
||||
relation_touched.add(
|
||||
_insert_relation_card(conn, work_id, win_no, rp)
|
||||
)
|
||||
# ⑥ 机械收尾:出场章统一并入(纯出场 + 被更新卡,抽检#2)+ 窗置 done
|
||||
for it in obs.get("纯出场", []):
|
||||
nm = (it.get("名称") or "").strip()
|
||||
if nm in name_map:
|
||||
pres_add.setdefault(name_map[nm][0], set()).update(
|
||||
_int_chaps(it.get("出场章"))
|
||||
)
|
||||
# ⑥ 机械收尾:仅处理未被更新模型消费的出场章,然后窗置 done。
|
||||
presence_touched = set()
|
||||
for did, chs in pres_add.items():
|
||||
if not chs:
|
||||
|
||||
@ -2438,53 +2438,71 @@ def test_run_pure_presence_rejects_external_state_and_revision_conflicts():
|
||||
|
||||
|
||||
def test_run_presence_paths_accept_numeric_strings():
|
||||
"""run 的已知更新归并卡与纯出场都把数字字符串章号落成整数。"""
|
||||
"""同卡横跨新信息与纯出场时,有无模型更新都只走一次实体写入并落全章节。"""
|
||||
|
||||
state = {
|
||||
"window_status": "pending",
|
||||
"payload": {
|
||||
"type": "character",
|
||||
"名称": "安若雪",
|
||||
"别名": [],
|
||||
"字段": {},
|
||||
"出场章": [],
|
||||
"_work_id": 8,
|
||||
},
|
||||
}
|
||||
def run_case(updates):
|
||||
state = {
|
||||
"window_status": "pending",
|
||||
"revisions": {701: 0},
|
||||
"payload": {
|
||||
"type": "character",
|
||||
"名称": "安若雪",
|
||||
"别名": [],
|
||||
"字段": {},
|
||||
"出场章": [],
|
||||
"_work_id": 8,
|
||||
},
|
||||
}
|
||||
|
||||
def fake_m3_json(prompt, model, need_keys, system=None):
|
||||
if need_keys == ("新名字", "已知实体新信息", "纯出场"):
|
||||
return ({
|
||||
"新名字": [],
|
||||
"已知实体新信息": [
|
||||
{"名称": "安若雪", "观察点": "出现新变化", "出场章": ["70"]}
|
||||
],
|
||||
"纯出场": [{"名称": "安若雪", "出场章": ["71"]}],
|
||||
}, {})
|
||||
return ({"更新": []}, {})
|
||||
def fake_m3_json(prompt, model, need_keys, system=None):
|
||||
if need_keys == ("新名字", "已知实体新信息", "纯出场"):
|
||||
return ({
|
||||
"新名字": [],
|
||||
"已知实体新信息": [
|
||||
{"名称": "安若雪", "观察点": "出现新变化", "出场章": ["70"]}
|
||||
],
|
||||
"纯出场": [{"名称": "安若雪", "出场章": ["71"]}],
|
||||
}, {})
|
||||
return ({"更新": deepcopy(updates)}, {})
|
||||
|
||||
with patch.object(pu.psycopg, "connect", side_effect=lambda *_: _RunConn(state)), \
|
||||
patch.object(pu, "load_entity_contracts", return_value=_fake_contracts()), \
|
||||
patch.object(
|
||||
pu,
|
||||
"load_window_material",
|
||||
return_value=("安若雪连续出现", {70: "安若雪出现新变化。", 71: "安若雪继续前进。"}),
|
||||
), \
|
||||
patch.object(
|
||||
pu,
|
||||
"load_known",
|
||||
return_value=({"安若雪": (701, "character", "旧摘要")}, {}, {}, {701: 0}),
|
||||
), \
|
||||
patch.object(pu, "m3_json", side_effect=fake_m3_json):
|
||||
pu._run(
|
||||
work_id=8,
|
||||
max_windows=0,
|
||||
max_calls=0,
|
||||
model="MiniMax-M3",
|
||||
redo_window=0,
|
||||
semantic_on=False,
|
||||
)
|
||||
check("run-presence-归并与纯出场数字字符串均落库", state["payload"]["出场章"] == [70, 71])
|
||||
with patch.object(pu.psycopg, "connect", side_effect=lambda *_: _RunConn(state)), \
|
||||
patch.object(pu, "load_entity_contracts", return_value=_fake_contracts()), \
|
||||
patch.object(
|
||||
pu,
|
||||
"load_window_material",
|
||||
return_value=("安若雪连续出现", {70: "安若雪出现新变化。", 71: "安若雪继续前进。"}),
|
||||
), \
|
||||
patch.object(
|
||||
pu,
|
||||
"load_known",
|
||||
return_value=({"安若雪": (701, "character", "旧摘要")}, {}, {}, {701: 0}),
|
||||
), \
|
||||
patch.object(pu, "m3_json", side_effect=fake_m3_json), \
|
||||
patch.object(pu, "undo_window"), \
|
||||
patch.object(pu, "merge_card", wraps=pu.merge_card) as merge_mock, \
|
||||
patch.object(
|
||||
pu, "_write_locked_upgrade_draft", wraps=pu._write_locked_upgrade_draft
|
||||
) as write_mock:
|
||||
pu._run(8, 0, 0, "MiniMax-M3", 0, False)
|
||||
return state, merge_mock.call_count, write_mock.call_count
|
||||
|
||||
updated, merge_calls, write_calls = run_case([
|
||||
{"draft_id": 701, "变更字段": {"一句话摘要": "新摘要"}, "别名新增": []},
|
||||
])
|
||||
check("run-presence-有更新时只写一次",
|
||||
merge_calls == 1 and write_calls == 1 and updated["revisions"][701] == 1,
|
||||
detail=f"merge={merge_calls}, write={write_calls}, state={updated}")
|
||||
check("run-presence-有更新时一次消费两数组章节",
|
||||
updated["window_status"] == "done" and updated["payload"]["出场章"] == [70, 71],
|
||||
detail=str(updated))
|
||||
|
||||
unchanged, merge_calls, write_calls = run_case([])
|
||||
check("run-presence-无更新时由机械收尾写一次",
|
||||
merge_calls == 0 and write_calls == 1 and unchanged["revisions"][701] == 1,
|
||||
detail=f"merge={merge_calls}, write={write_calls}, state={unchanged}")
|
||||
check("run-presence-无更新时两数组章节仍落库",
|
||||
unchanged["window_status"] == "done" and unchanged["payload"]["出场章"] == [70, 71],
|
||||
detail=str(unchanged))
|
||||
|
||||
|
||||
def test_load_known_projection():
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user