fix: preserve upload recovery and harden speech indexing

This commit is contained in:
2026-08-12 19:49:25 +08:00
parent 10f2c078ec
commit a0a185b96c
16 changed files with 13234 additions and 62 deletions
+23
View File
@@ -16,9 +16,32 @@ from imagefind.jobs import JobRetry
from imagefind.main import create_app
from imagefind.media import MediaInput
from imagefind.speech import SPEECH_INDEX_REVISION, SpeechService, SpeechStageError
from imagefind.speech_quality import aggregate_transcript_quality
from imagefind.text import fts_query
def test_ending_hallucination_is_risky_twice_or_once_in_weak_speech():
repeated = aggregate_transcript_quality([
{"text": "拜拜"},
{"text": "正常对话"},
{"text": "拜拜"},
])
assert "whole_ending_hallucination" in repeated.flags
assert ("拜拜", 2) in repeated.repeated_phrases
weak = aggregate_transcript_quality([
{"text": "谢谢大家收看", "speech_ratio": 0.1},
{"text": "正常对话", "speech_ratio": 0.8},
])
assert "whole_ending_hallucination" in weak.flags
single = aggregate_transcript_quality([
{"text": "拜拜", "speech_ratio": 0.8},
{"text": "正常对话", "speech_ratio": 0.8},
])
assert "whole_ending_hallucination" not in single.flags
def _app(tmp_path: Path):
settings = Settings(data_dir=tmp_path / "data", embedding_backend="hash", upload_reserve_gb=0)
settings.prepare()
+34
View File
@@ -1,4 +1,5 @@
from contextlib import nullcontext
from pathlib import Path
from types import SimpleNamespace
import httpx
@@ -114,6 +115,39 @@ def test_native_catalog_uses_physical_object_but_keeps_logical_media_identity():
assert item.fingerprint
def test_native_prepare_and_cancel_preserve_original_and_local_recovery(tmp_path: Path):
original = tmp_path / "upload.part"
original.write_bytes(b"data")
staging = tmp_path / "native"
class Sources:
rclone = SimpleNamespace()
def get(self, _source_id):
return {}
service = object.__new__(OpenListNativeService)
service.sources = Sources()
service.configuration = lambda _source: SimpleNamespace(
local_staging_path=staging,
encrypted=False,
source_path="local-stage",
target_path="cloud",
)
paths = service.prepare("source", "upload-id", "folder/movie.mp4", original)
assert original.read_bytes() == b"data"
assert Path(paths["local_path"]).read_bytes() == b"data"
cancelled: list[str] = []
service.client = lambda _source_id: SimpleNamespace(
cancel_copy_task=lambda task_id: cancelled.append(task_id)
)
service.cancel("source", "upload-id", "task-id", paths["staged_path"])
assert cancelled == ["task-id"]
assert original.is_file()
assert Path(paths["local_path"]).is_file()
class _MovingClient:
def __init__(self, files: dict[str, int], *, fail_after_move: bool = False):
self.files = dict(files)
+29
View File
@@ -221,6 +221,11 @@ def test_upload_cancel_is_consistent_and_commit_window_returns_conflict(tmp_path
job_state = conn.execute("SELECT status FROM jobs WHERE id=?", (job_id,)).fetchone()
assert upload_state["status"] == "cancelled"
assert job_state["status"] == "cancelled"
with app.state.services.db.read() as conn:
retained_path = Path(
conn.execute("SELECT temp_path FROM uploads WHERE id=?", (upload["id"],)).fetchone()[0]
)
assert retained_path.read_bytes() == b"data"
committed = app.state.services.uploads.create(source_id, "", "committed.mp4", 4)
with app.state.services.db.transaction() as conn:
@@ -239,6 +244,30 @@ def test_upload_cancel_is_consistent_and_commit_window_returns_conflict(tmp_path
asyncio.run(scenario())
def test_cancelled_upload_requires_explicit_discard_to_delete_recovery_copy(tmp_path: Path):
app, _, source_id, headers = _app(tmp_path)
uploads = app.state.services.uploads
upload = uploads.create(source_id, "", "recoverable.mp4", 4)
uploads.receive_chunk(upload["id"], 0, b"data")
with uploads.db.read() as conn:
staging = Path(conn.execute("SELECT temp_path FROM uploads WHERE id=?", (upload["id"],)).fetchone()[0])
uploads.cancel(upload["id"])
assert staging.is_file()
async def scenario():
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.delete(
f"/api/v1/uploads/{upload['id']}/recovery", headers=headers
)
assert response.status_code == 204
asyncio.run(scenario())
assert not staging.exists()
with uploads.db.read() as conn:
assert conn.execute("SELECT 1 FROM uploads WHERE id=?", (upload["id"],)).fetchone() is None
def test_upload_catalog_failure_retries_without_retransmitting(tmp_path: Path, monkeypatch):
app, media, source_id, _ = _app(tmp_path)
uploads = app.state.services.uploads