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:
@@ -70,7 +70,7 @@ class TestH2HCurrentHomePerspective:
|
||||
_make_h2h_match(101, home_id=2, away_id=1, home_goals=3, away_goals=1,
|
||||
home_name="阿森纳", away_name="利物浦"),
|
||||
]
|
||||
import src.llm.context_builder as cb
|
||||
import src.llm.slices.h2h as cb
|
||||
orig = cb._get_h2h
|
||||
async def mock_get_h2h(db, home_id, away_id, before, *, limit):
|
||||
return matches
|
||||
@@ -100,7 +100,7 @@ class TestH2HCurrentHomePerspective:
|
||||
_make_h2h_match(201, home_id=1, away_id=2, home_goals=2, away_goals=1,
|
||||
home_name="曼城", away_name="诺维奇"),
|
||||
]
|
||||
import src.llm.context_builder as cb
|
||||
import src.llm.slices.h2h as cb
|
||||
|
||||
async def mock_get_h2h(db, h, a, before, **kw):
|
||||
# 真实契约是 async(见 context_builder.py 的 `h2h = await _get_h2h(...)`),
|
||||
@@ -122,7 +122,7 @@ class TestH2HCurrentHomePerspective:
|
||||
_make_h2h_match(301, home_id=2, away_id=1, home_goals=0, away_goals=2,
|
||||
home_name="热刺", away_name="切尔西"), # 切尔西客场 2-0 赢
|
||||
]
|
||||
import src.llm.context_builder as cb
|
||||
import src.llm.slices.h2h as cb
|
||||
orig = cb._get_h2h
|
||||
async def mock_get_h2h(db, h, a, before, *, limit):
|
||||
return matches
|
||||
|
||||
@@ -206,7 +206,7 @@ class TestBacktestXgNotVisible:
|
||||
|
||||
header = _make_header(match_dt)
|
||||
|
||||
import src.llm.context_builder as cb
|
||||
import src.llm.slices.stats as cb
|
||||
|
||||
async def mock_get_form(db, team_id, before, *, limit=10):
|
||||
# before=cutoff(1月13日),比赛在1月15日,满足 before 条件
|
||||
|
||||
@@ -101,7 +101,7 @@ class TestFormSliceHomeAwayIdentity:
|
||||
home_name="曼城",
|
||||
away_name="利物浦",
|
||||
)
|
||||
import src.llm.context_builder as cb
|
||||
import src.llm.slices.form as cb
|
||||
orig_get_form = cb._get_form
|
||||
async def mock_get_form(db, team_id, before, *, limit):
|
||||
return [hist_match] if team_id == 1 else []
|
||||
@@ -132,7 +132,7 @@ class TestFormSliceHomeAwayIdentity:
|
||||
home_name="阿森纳",
|
||||
away_name="切尔西",
|
||||
)
|
||||
import src.llm.context_builder as cb
|
||||
import src.llm.slices.form as cb
|
||||
orig_get_form = cb._get_form
|
||||
async def mock_get_form(db, team_id, before, *, limit):
|
||||
return [hist_match] if team_id == 2 else []
|
||||
@@ -169,7 +169,7 @@ class TestStatsSliceHomeAwayIdentity:
|
||||
stats=_make_stats(home_xg=2.5, away_xg=0.8, home_shots=15, away_shots=5,
|
||||
home_sot=6, away_sot=2, home_poss=60.0),
|
||||
)
|
||||
import src.llm.context_builder as cb
|
||||
import src.llm.slices.stats as cb
|
||||
orig_get_form = cb._get_form
|
||||
async def mock_get_form(db, team_id, before, *, limit):
|
||||
return [hist_match] if team_id == 1 else []
|
||||
|
||||
@@ -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 关闭后访问关系必炸)。"""
|
||||
|
||||
Reference in New Issue
Block a user