feat: 完善模型用量趋势与全局手账卡片并补齐阶段验收

This commit is contained in:
2026-09-05 20:40:36 +08:00
parent 02dd585a4e
commit 8d626ee16b
38 changed files with 733 additions and 86 deletions
+69
View File
@@ -0,0 +1,69 @@
import asyncio
import sys
from contextlib import nullcontext
from types import SimpleNamespace
import pytest
from app.errors import ApiError
from app.providers.routing import ModelRoutingService, MAX_LOCAL_MEDIA_BYTES, MAX_MEDIA_BYTES, RoutedTranscript
from app.services import transcription_service as jobs
from app.config import get_settings
def test_large_media_requires_local_only_and_respects_size_limit():
path = get_settings().attachments_path / 'large.mp3'
path.parent.mkdir(parents=True, exist_ok=True)
with path.open('wb') as file:
file.truncate(MAX_MEDIA_BYTES + 1)
with pytest.raises(ApiError):
ModelRoutingService._media_file(path)
with ModelRoutingService._media_file(path, local_only=True):
pass
with pytest.raises(ApiError):
asyncio.run(jobs.create_transcription('large.mp3', local_only=False))
with path.open('wb') as file:
file.truncate(MAX_LOCAL_MEDIA_BYTES + 1)
with pytest.raises(ApiError):
ModelRoutingService._media_file(path, local_only=True)
def test_decode_recovers_one_corrupt_packet_without_shifting_following_audio(monkeypatch):
from app.local_models.worker import decode
class Samples(list):
def reshape(self, *_): return self
def astype(self, *_): return self
def to_ndarray(self): return self
class InvalidDataError(Exception): pass
def broken(): raise InvalidDataError()
packets = [SimpleNamespace(decode=lambda: [Samples([1] * 3200)]),
SimpleNamespace(decode=broken, duration=100, time_base=.001),
SimpleNamespace(decode=lambda: [Samples([2] * 3200)])]
container = SimpleNamespace(streams=SimpleNamespace(audio=[1]), demux=lambda **_: iter(packets))
fake_av = SimpleNamespace(open=lambda *_a, **_kw: nullcontext(container),
error=SimpleNamespace(InvalidDataError=InvalidDataError),
AudioResampler=lambda **_: SimpleNamespace(resample=lambda frame: [] if frame is None else [frame]))
fake_numpy = SimpleNamespace(float32=float, zeros=lambda count, **_: Samples([0] * count),
concatenate=lambda frames: Samples(value for frame in frames for value in frame),
isfinite=lambda _: SimpleNamespace(all=lambda: True))
monkeypatch.setitem(sys.modules, 'av', fake_av)
monkeypatch.setitem(sys.modules, 'numpy', fake_numpy)
warnings = []
output = decode('test.mp3', warnings=warnings)
assert output == [1] * 3200 + [0] * 1600 + [2] * 3200
assert warnings == ['MEDIA_CORRUPT_PACKETS_SKIPPED:1']
with pytest.raises(ValueError, match='one hour'):
decode('test.mp3', limit_seconds=.25)
def test_decode_warning_reaches_persisted_job(monkeypatch):
from app.container import container
path = get_settings().attachments_path / 'audio.mp3'
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(b'audio')
async def transcribe(*_args, **_kwargs):
return RoutedTranscript(text='decoded', source='local', warnings=['MEDIA_CORRUPT_PACKETS_SKIPPED:1'])
monkeypatch.setattr(container.model_routing, 'transcribe', transcribe)
job = asyncio.run(jobs.create_transcription('audio.mp3', local_only=True))
assert job.status == 'completed'
assert jobs.require_job(job.job_id).warnings == ['MEDIA_CORRUPT_PACKETS_SKIPPED:1']