From e85abe07eb53638743064704550213a458478da6 Mon Sep 17 00:00:00 2001 From: unknown <798648091@qq.com> Date: Tue, 28 Jul 2026 14:47:28 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=E4=B8=AD=E9=80=94=E9=80=80=E5=87=BA=E6=AF=94=E4=BB=B7=E7=9A=84?= =?UTF-8?q?LLM=E6=88=90=E6=9C=AC=E5=9B=9E=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/v1/compare.py | 14 +++++++++++--- app/services/comparison_llm_backfill.py | 2 +- tests/test_compare_harvest.py | 5 ++++- tests/test_comparison_llm_backfill.py | 4 ++++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/api/v1/compare.py b/app/api/v1/compare.py index fcb1e03..fa8268a 100644 --- a/app/api/v1/compare.py +++ b/app/api/v1/compare.py @@ -108,7 +108,7 @@ def _harvest_done_blocking( def _harvest_abort_blocking( trace_id: str, status_hint: str, reason: str | None, trace_url: str | None, -) -> None: +) -> int | None: with SessionLocal() as db: rec = crud_compare.harvest_abort( db, trace_id=trace_id, status=status_hint, reason=reason, trace_url=trace_url, @@ -118,6 +118,7 @@ def _harvest_abort_blocking( extra={"phase": "harvest_abort", "status": (rec.status if rec else None), "reason": reason}, ) + return rec.id if rec is not None else None async def _forward( @@ -291,7 +292,10 @@ async def trace_epilogue( @router.post("/trace/finalize", summary="比价 trace 收尾上云 (透传 + 夭折落库)") async def trace_finalize( - request: Request, user: OptionalUser, db: DbSession + request: Request, + background_tasks: BackgroundTasks, + user: OptionalUser, + db: DbSession, ) -> dict[str, Any]: _ensure_compare_allowed(user, db) # 用户终止 / Phase1 未识别没到 done 帧: pricebot 打包半截上云返回 {trace_url}; @@ -302,12 +306,16 @@ async def trace_finalize( request, "/api/trace/finalize", user, harvest_first_frame=False, ) try: - await run_in_threadpool( + record_id = await run_in_threadpool( _harvest_abort_blocking, trace_id, (meta.get("status") or "cancelled"), (meta.get("reason") or meta.get("information")), (resp.get("trace_url") if isinstance(resp, dict) else None), ) + if record_id is not None: + background_tasks.add_task( + backfill_comparison_llm_cost, record_id, trace_id + ) except Exception as e: # noqa: BLE001 logger.warning("harvest_abort failed trace=%s: %s", trace_id, e) return resp diff --git a/app/services/comparison_llm_backfill.py b/app/services/comparison_llm_backfill.py index c7eba8c..1687c1b 100644 --- a/app/services/comparison_llm_backfill.py +++ b/app/services/comparison_llm_backfill.py @@ -135,7 +135,7 @@ def repair_missing_comparison_llm_costs( select(ComparisonRecord.id, ComparisonRecord.trace_id) .where( *date_conditions, - ComparisonRecord.status.in_(("success", "failed")), + ComparisonRecord.status.in_(("success", "failed", "cancelled")), ComparisonRecord.llm_cost_yuan.is_(None), ) .order_by(ComparisonRecord.created_at.desc(), ComparisonRecord.id.desc()) diff --git a/tests/test_compare_harvest.py b/tests/test_compare_harvest.py index 82448f0..46a11ba 100644 --- a/tests/test_compare_harvest.py +++ b/tests/test_compare_harvest.py @@ -263,7 +263,9 @@ def test_trace_finalize_harvests_abort(client) -> None: with SessionLocal() as db: # 先有 running 行(帧0建的) crud.harvest_running(db, trace_id=tid, user_id=None) p, _cap = _mock_pricebot({"trace_url": "https://price.shaguabijia.com/traces/fin/"}) - with p: + with p, patch( + "app.api.v1.compare.backfill_comparison_llm_cost" + ) as backfill: r = client.post("/api/v1/trace/finalize", json={"trace_id": tid, "status": "cancelled", "reason": "用户终止"}) assert r.status_code == 200 @@ -271,6 +273,7 @@ def test_trace_finalize_harvests_abort(client) -> None: rec = _get(db, tid) assert rec is not None and rec.status == "cancelled" assert rec.trace_url.endswith("/fin/") + backfill.assert_called_once_with(rec.id, tid) def test_price_step_binds_user_when_authed(client) -> None: diff --git a/tests/test_comparison_llm_backfill.py b/tests/test_comparison_llm_backfill.py index fe9b925..89e2293 100644 --- a/tests/test_comparison_llm_backfill.py +++ b/tests/test_comparison_llm_backfill.py @@ -72,6 +72,7 @@ def test_backfill_retries_then_persists_cost(monkeypatch): def test_repair_batch_only_targets_terminal_missing_rows(monkeypatch): missing_id = _record("llm-repair-missing") + cancelled_id = _record("llm-repair-cancelled", status="cancelled") running_id = _record("llm-repair-running", status="running") calls = [ { @@ -96,12 +97,15 @@ def test_repair_batch_only_targets_terminal_missing_rows(monkeypatch): ) assert result["repaired"] >= 1 assert "llm-repair-missing" in seen + assert "llm-repair-cancelled" in seen assert "llm-repair-running" not in seen with SessionLocal() as db: assert db.get(ComparisonRecord, missing_id).llm_cost_yuan is not None + assert db.get(ComparisonRecord, cancelled_id).llm_cost_yuan is not None assert db.get(ComparisonRecord, running_id).llm_cost_yuan is None finally: _delete(missing_id) + _delete(cancelled_id) _delete(running_id) -- 2.52.0