refactor: context_builder 按 slice 拆到 src/llm/slices/ 包

单文件拆分(仅搬迁无逻辑修改):
- common.py    共享类型/头信息/_outcome/_is_stats_available
- form.py      form_slice + _get_form
- h2h.py       h2h_slice + _get_h2h
- stats.py     stats_slice(复用 form._get_form)
- home_away.py home_away_slice + _get_home_away
- standings.py standings_slice
- aggregate.py build_context

context_builder.py 改为纯 re-export 门面,公开签名不变。
同步修复测试 patch 目标(p0_home_away/h2h_perspective/multi_agent_cutoff)
与 regressions 源码断言(读 slices/*.py)。

全量测试 270 通过。
This commit is contained in:
shangfangjian
2026-09-22 01:33:14 +08:00
parent f1016b610a
commit 44816794d3
13 changed files with 677 additions and 563 deletions
+16 -9
View File
@@ -32,17 +32,24 @@ class TestEagerLoadCoverage:
models.py 已声明 lazy="selectin" 兜底,但这里同时检查显式
selectinload —— 显式声明是查询意图的固化,也被 P0 修复所依赖。
(context_builder 已按 slice 拆分到 src/llm/slices/,getter 随实现迁移。)
"""
src = _read("llm/context_builder.py")
for rel in ("llm/slices/form.py", "llm/slices/h2h.py", "llm/slices/home_away.py"):
src = _read(rel)
for fn in ("_get_form", "_get_h2h", "_get_home_away"):
# 截取函数体(仅当前文件定义了该函数才检查)
m = re.search(rf"async def {fn}\(.*?(?=\nasync def |\n# =|\Z)", src, re.S)
if not m:
continue
body = m.group(0)
assert "selectinload" in body, (
f"{fn} 查询 Match 但未 eager-load 关系 —— "
"this would raise MissingGreenlet in async SQLAlchemy (P0-2)"
)
# 守卫完整性: 三个 getter 必须都能在 slices 包中找到
all_src = "\n".join(_read(r) for r in ("llm/slices/form.py", "llm/slices/h2h.py", "llm/slices/home_away.py"))
for fn in ("_get_form", "_get_h2h", "_get_home_away"):
# 截取函数体
m = re.search(rf"async def {fn}\(.*?(?=\nasync def |\n# =|\Z)", src, re.S)
assert m, f"{fn} 未找到"
body = m.group(0)
assert "selectinload" in body, (
f"{fn} 查询 Match 但未 eager-load 关系 —— "
"this would raise MissingGreenlet in async SQLAlchemy (P0-2)"
)
assert f"async def {fn}(" in all_src, f"{fn} 未在 slices 包中找到(拆分后迁移缺失?)"
def test_backtest_candidates_eager_load(self):
"""回测取历史比赛必须 eager-load(否则 session 关闭后访问关系必炸)。"""