feat: 手术视频消耗、待确认与持久化改造
- 新增 Alembic 初始迁移、领域明细模型及归档持久化与重试链路\n- 拆分视频会话注册表、分类处理、推理时间窗聚合与流处理\n- 消耗日志:TSV/Markdown 含 top2/top3;item_id 优先产品编码;待确认记「待确认」行,语音确认后落正式行并更新汇总\n- 待确认时内存/DB 明细为占位行,确认后替换;拒绝时移除占位\n- 分类 probs 先 detach/cpu 再转 NumPy,修复 MPS/CUDA 上推理被静默跳过\n- 补充集成测试、归档与设备张量等单测 Made-with: Cursor
This commit is contained in:
@@ -42,12 +42,7 @@ def _make_service(
|
||||
minio: MagicMock,
|
||||
baidu: MagicMock,
|
||||
sqlite_factory,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> VoiceConfirmationService:
|
||||
monkeypatch.setattr(
|
||||
"app.services.voice_resolution.AsyncSessionLocal",
|
||||
sqlite_factory,
|
||||
)
|
||||
audits = VoiceAuditRepository()
|
||||
return VoiceConfirmationService(
|
||||
settings=settings,
|
||||
@@ -55,6 +50,7 @@ def _make_service(
|
||||
baidu=baidu,
|
||||
minio=minio,
|
||||
audits=audits,
|
||||
session_factory=sqlite_factory,
|
||||
)
|
||||
|
||||
|
||||
@@ -86,7 +82,7 @@ def _active_session_with_pending(
|
||||
)
|
||||
st.pending_fifo.append(confirmation_id)
|
||||
|
||||
mgr._active[surgery_id] = RunningSurgery(
|
||||
mgr._registry._active[surgery_id] = RunningSurgery(
|
||||
stop_event=asyncio.Event(), state=st, tasks=[]
|
||||
)
|
||||
return mgr, confirmation_id
|
||||
@@ -131,7 +127,6 @@ async def test_resolve_recognized_appends_voice_detail_and_audit(
|
||||
minio=minio,
|
||||
baidu=baidu,
|
||||
sqlite_factory=sqlite_session_factory,
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
wav = _minimal_wav_16k_mono()
|
||||
result = await svc.resolve_from_wav(
|
||||
@@ -145,7 +140,7 @@ async def test_resolve_recognized_appends_voice_detail_and_audit(
|
||||
assert result.resolved_label == "纱布"
|
||||
assert result.asr_text == "第一个"
|
||||
assert result.audio_object_key is not None
|
||||
st = sessions._active["123456"].state
|
||||
st = sessions._registry._active["123456"].state
|
||||
assert len(st.details) == 1
|
||||
assert st.details[0].source == "voice"
|
||||
assert await _audit_count(sqlite_session_factory, surgery_id="123456") == 1
|
||||
@@ -178,7 +173,6 @@ async def test_resolve_rejected_audit(
|
||||
minio=minio,
|
||||
baidu=baidu,
|
||||
sqlite_factory=sqlite_session_factory,
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
result = await svc.resolve_from_wav(
|
||||
surgery_id="123456",
|
||||
@@ -189,7 +183,7 @@ async def test_resolve_rejected_audit(
|
||||
)
|
||||
assert result.rejected is True
|
||||
assert result.resolved_label is None
|
||||
assert len(sessions._active["123456"].state.details) == 0
|
||||
assert len(sessions._registry._active["123456"].state.details) == 0
|
||||
async with sqlite_session_factory() as session:
|
||||
async with session.begin():
|
||||
res = await session.execute(select(VoiceConfirmationAudit))
|
||||
@@ -229,7 +223,6 @@ async def test_resolve_recognizes_label_not_in_topk_but_in_surgery_candidates(
|
||||
minio=minio,
|
||||
baidu=baidu,
|
||||
sqlite_factory=sqlite_session_factory,
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
result = await svc.resolve_from_wav(
|
||||
surgery_id="123456",
|
||||
@@ -240,7 +233,7 @@ async def test_resolve_recognizes_label_not_in_topk_but_in_surgery_candidates(
|
||||
)
|
||||
assert result.rejected is False
|
||||
assert result.resolved_label == "止血钳"
|
||||
st = sessions._active["123456"].state
|
||||
st = sessions._registry._active["123456"].state
|
||||
assert len(st.details) == 1
|
||||
assert st.details[0].item_name == "止血钳"
|
||||
assert st.details[0].source == "voice"
|
||||
@@ -263,7 +256,6 @@ async def test_audio_too_large_audit(
|
||||
minio=minio,
|
||||
baidu=baidu,
|
||||
sqlite_factory=sqlite_session_factory,
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
with pytest.raises(SurgeryPipelineError) as ei:
|
||||
await svc.resolve_from_wav(
|
||||
@@ -298,7 +290,6 @@ async def test_minio_not_configured_no_audit(
|
||||
minio=minio,
|
||||
baidu=baidu,
|
||||
sqlite_factory=sqlite_session_factory,
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
with pytest.raises(SurgeryPipelineError) as ei:
|
||||
await svc.resolve_from_wav(
|
||||
@@ -331,7 +322,6 @@ async def test_upload_failed_audit(
|
||||
minio=minio,
|
||||
baidu=baidu,
|
||||
sqlite_factory=sqlite_session_factory,
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
with pytest.raises(SurgeryPipelineError) as ei:
|
||||
await svc.resolve_from_wav(
|
||||
@@ -371,7 +361,6 @@ async def test_asr_failed_audit(
|
||||
minio=minio,
|
||||
baidu=baidu,
|
||||
sqlite_factory=sqlite_session_factory,
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
with pytest.raises(SurgeryPipelineError) as ei:
|
||||
await svc.resolve_from_wav(
|
||||
@@ -412,7 +401,6 @@ async def test_parse_failed_audit(
|
||||
minio=minio,
|
||||
baidu=baidu,
|
||||
sqlite_factory=sqlite_session_factory,
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
with pytest.raises(SurgeryPipelineError) as ei:
|
||||
await svc.resolve_from_wav(
|
||||
@@ -451,7 +439,6 @@ async def test_invalid_wav_decode_audit(
|
||||
minio=minio,
|
||||
baidu=baidu,
|
||||
sqlite_factory=sqlite_session_factory,
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
with pytest.raises(SurgeryPipelineError) as ei:
|
||||
await svc.resolve_from_wav(
|
||||
|
||||
Reference in New Issue
Block a user