feat: Abschreiben im Cluster mit Whisper-Image und Voice-CronJob (T7 L3 Task 13)
ApiCore-Whisper: transcribe.py liest das Manifest im RAM-Arbeitsordner, dekodiert mit festem Demuxer je Endung, misst die Länge vor dem Abschreiben (TOO_LONG > 240 s, DECODE_FAILED bei Länge 0), sperrt das Netz vor dem Modell-Laden und schreibt nur Zähler auf stdout. Image: python:3.12-slim per Digest, Nutzer 1000, alle Pakete mit Hash (faster-whisper 1.2.1, av 18.1.0 wegen Inkompatibilität mit av 19), Modell faster-whisper-small auf fester Revision mit SHA-256 je Datei. CI-Workflow nur bei Änderungen unter ApiCore-Whisper/, Actions per SHA. deploy/k8s/components/voice: CronJob alle 2 Minuten mit fetch, whisper, ingest (gleiche UID, getrennte /tmp-Volumes, damit der Decoder dem Container mit den Secrets nichts unterschieben kann; whisper ohne Secrets und Umgebungsvariablen außer WHISPER_CPU_THREADS), Overlay mit Platzhalter-Digest, ConfigMap-Werte für Sprache, Policy-Tests (55). Die Anbindung des Anthropic-Schlüssels bleibt offen (Ruling 9). Refs #27 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
e614ded967
commit
c53810b8a6
16 files changed
+1448
-5
No files matched your search
@@ -0,0 +1,47 @@
|
||||
"""Tests for the build-time model download (no network)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
import fetch_model # noqa: E402
|
||||
|
||||
|
||||
def test_verify_accepts_the_pinned_hash(tmp_path: Path):
|
||||
file = tmp_path / "a.bin"
|
||||
file.write_bytes(b"model")
|
||||
fetch_model.verify(file, hashlib.sha256(b"model").hexdigest())
|
||||
|
||||
|
||||
def test_verify_rejects_a_changed_file(tmp_path: Path):
|
||||
file = tmp_path / "a.bin"
|
||||
file.write_bytes(b"tampered")
|
||||
with pytest.raises(fetch_model.ChecksumMismatch):
|
||||
fetch_model.verify(file, hashlib.sha256(b"model").hexdigest())
|
||||
|
||||
|
||||
def test_download_refuses_plain_http(tmp_path: Path):
|
||||
with pytest.raises(ValueError):
|
||||
fetch_model.download("http://example.invalid/x", tmp_path / "x")
|
||||
|
||||
|
||||
def test_every_pinned_file_has_a_sha256_and_the_revision_is_a_commit():
|
||||
assert len(fetch_model.REVISION) == 40
|
||||
assert set(fetch_model.FILES) >= {"model.bin", "config.json", "tokenizer.json", "vocabulary.txt"}
|
||||
for digest in fetch_model.FILES.values():
|
||||
assert len(digest) == 64 and int(digest, 16) >= 0
|
||||
|
||||
|
||||
def test_fetch_all_stops_on_a_mismatch(tmp_path: Path, monkeypatch):
|
||||
def fake_download(url: str, target: Path) -> None:
|
||||
target.write_bytes(b"evil")
|
||||
|
||||
monkeypatch.setattr(fetch_model, "download", fake_download)
|
||||
with pytest.raises(fetch_model.ChecksumMismatch):
|
||||
fetch_model.fetch_all(tmp_path / "m", {"model.bin": hashlib.sha256(b"good").hexdigest()})
|
||||
Reference in new issue
Block a user