api: reprocess accepts ?model= to switch the saved override on re-run
Validates through validate_model_name (422, never silently substituted) and persists rec.transcription_model — the worker reads the row, so the switch survives into the queued job. 2 new inline-queue tests: in-place re-run with model switch (same id, one extra transcribe call), and validation/ownership errors (409 nothing-to-summarize, 422 bad model, 404 not owner).
This commit is contained in:
parent
e7610d3901
commit
0cba721ada
2 changed files with 65 additions and 1 deletions
|
|
@ -337,6 +337,7 @@ async def reprocess_recording(
|
|||
user: CurrentUser,
|
||||
session: SessionDep,
|
||||
job: str = Query(default="summarize", pattern="^(transcribe|summarize)$"),
|
||||
model: str | None = Query(default=None, max_length=64),
|
||||
):
|
||||
"""Force one pipeline stage to run again (Summarize / Re-transcribe).
|
||||
|
||||
|
|
@ -345,7 +346,8 @@ async def reprocess_recording(
|
|||
deliberate request. Running jobs are left alone (409 instead of a
|
||||
duplicate).
|
||||
"""
|
||||
from shonar.db.models import JobStatus, JobType as JT, ProcessingStatus
|
||||
from shonar.db.models import JobStatus, ProcessingStatus
|
||||
from shonar.db.models import JobType as JT
|
||||
from shonar.services import processing as proc
|
||||
|
||||
rec = await _owned_recording(session, user, recording_id)
|
||||
|
|
@ -378,6 +380,16 @@ async def reprocess_recording(
|
|||
existing.started_at = None
|
||||
existing.finished_at = None
|
||||
if job_type is JT.transcribe:
|
||||
if model is not None:
|
||||
# A re-transcribe may switch models; the saved per-recording
|
||||
# override is what the worker reads, so persist it here.
|
||||
from shonar.services.ai import ProviderConfigError
|
||||
from shonar.services.ai.model_registry import validate_model_name
|
||||
|
||||
try:
|
||||
rec.transcription_model = validate_model_name(model)
|
||||
except ProviderConfigError as e:
|
||||
raise HTTPException(422, str(e)) from e
|
||||
rec.processing_status = ProcessingStatus.processing
|
||||
rec.processing_error = None
|
||||
await session.flush()
|
||||
|
|
|
|||
|
|
@ -99,3 +99,55 @@ async def test_inline_retries_transient_failure(client, monkeypatch):
|
|||
assert state["tries"] >= 2 # the retry actually happened
|
||||
finally:
|
||||
await inline_queue.stop()
|
||||
|
||||
|
||||
async def test_reprocess_reruns_in_place_with_model(client, monkeypatch):
|
||||
"""POST /reprocess?job=transcribe&model= re-runs the pipeline on the
|
||||
SAME recording (no re-upload) and persists the model override."""
|
||||
stt = FakeTranscriber()
|
||||
use_fakes(monkeypatch, stt, FakeLlm())
|
||||
use_inline(monkeypatch)
|
||||
await inline_queue.start()
|
||||
try:
|
||||
token = await user_tokens(client, email="reproc@shonar.dev")
|
||||
h = {"Authorization": f"Bearer {token}"}
|
||||
rec = await upload_recording(client, token, client_id="reproc-1")
|
||||
await _wait_terminal(rec["id"], want=2)
|
||||
calls_before = stt.calls
|
||||
|
||||
r = await client.post(
|
||||
f"/api/v1/recordings/{rec['id']}/reprocess?job=transcribe&model=small",
|
||||
headers=h,
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
statuses = await _wait_terminal(rec["id"], want=2)
|
||||
assert statuses[JobType.transcribe] == JobStatus.succeeded
|
||||
assert stt.calls == calls_before + 1 # re-ran, no new recording
|
||||
|
||||
# Same recording id, model override persisted on the row.
|
||||
r = await client.get(f"/api/v1/recordings/{rec['id']}", headers=h)
|
||||
assert r.json()["id"] == rec["id"]
|
||||
assert r.json()["transcription_model"] == "small"
|
||||
finally:
|
||||
await inline_queue.stop()
|
||||
|
||||
|
||||
async def test_reprocess_validation(client, monkeypatch):
|
||||
token = await user_tokens(client, email="reproc2@shonar.dev")
|
||||
h = {"Authorization": f"Bearer {token}"}
|
||||
rec = await upload_recording(client, token, client_id="reproc-2")
|
||||
# Nothing transcribed yet -> summarize refuses.
|
||||
r = await client.post(
|
||||
f"/api/v1/recordings/{rec['id']}/reprocess?job=summarize", headers=h)
|
||||
assert r.status_code == 409
|
||||
# Bad model name -> 422 (never silently substituted).
|
||||
r = await client.post(
|
||||
f"/api/v1/recordings/{rec['id']}/reprocess?job=transcribe&model=not-a-model",
|
||||
headers=h)
|
||||
assert r.status_code == 422
|
||||
# Not the owner -> 404.
|
||||
other = await user_tokens(client, email="reproc3@shonar.dev")
|
||||
r = await client.post(
|
||||
f"/api/v1/recordings/{rec['id']}/reprocess?job=transcribe",
|
||||
headers={"Authorization": f"Bearer {other}"})
|
||||
assert r.status_code == 404
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue