fix(backend): 落实 PR #12 评审意见

- P1 事件循环让出:run_rag 在样本边界 await asyncio.sleep(0),运行中取消/进度/SSE 可及时调度
- P2 SSE 终止事件:历史回放期间识别终止事件并结束流,try/finally 保证订阅清理
- P2 FTS 截断:fts 走数据库侧精确分页与计数,阈值经 bm25 截止值换算,不再受 5000 条固定截断
- P2 仅块标注:expected_block_ids 从块反查所属笔记,避免合法样本被判零分

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
yxx
2026-09-03 23:37:09 +08:00
co-authored by Claude Code
parent 3898530585
commit 3bd475dc15
6 changed files with 331 additions and 48 deletions
+23 -15
View File
@@ -961,30 +961,38 @@ async def benchmark_events(
async def stream() -> AsyncIterator[str]:
# 先订阅(保证订阅之后产生的事件也能收到),再回放历史事件,最后实时输出新事件
terminal = (
BenchmarkEventType.run_completed,
BenchmarkEventType.run_failed,
BenchmarkEventType.run_cancelled,
)
queue = benchmark_service.subscribe(run_id)
last_sequence = cursor
for event in benchmark_service.get_events(run_id):
if event.sequence <= cursor:
continue
yield as_sse(event.event.value, event.model_dump_json(), event_id=event.sequence)
last_sequence = event.sequence
if queue is None:
return
try:
last_sequence = cursor
# 回放按订阅时刻的快照长度遍历,避免列表在回放期间被追加;终止事件同样要结束流,
# 防止回放完成后进入实时队列却因序号去重跳过同一终止事件而永久等待。
history = benchmark_service.get_events(run_id)
for index in range(len(history)):
event = history[index]
if event.sequence <= cursor:
continue
yield as_sse(event.event.value, event.model_dump_json(), event_id=event.sequence)
last_sequence = event.sequence
if event.event in terminal:
return
if queue is None:
return
while True:
event = await queue.get()
if event.sequence <= last_sequence:
continue
yield as_sse(event.event.value, event.model_dump_json(), event_id=event.sequence)
last_sequence = event.sequence
if event.event in (
BenchmarkEventType.run_completed,
BenchmarkEventType.run_failed,
BenchmarkEventType.run_cancelled,
):
break
if event.event in terminal:
return
finally:
benchmark_service.unsubscribe(run_id, queue)
if queue is not None:
benchmark_service.unsubscribe(run_id, queue)
return StreamingResponse(stream(), media_type="text/event-stream")