Files
Find-Word/.agent/evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screen-final.py
T
2026-10-14 10:01:46 +00:00

87 lines
6.9 KiB
Python

"""Offline tiny-model heuristic screening of the exact final WAV bytes; never clearance."""
from dataclasses import asdict
from datetime import datetime, timezone
import hashlib
from importlib.metadata import version
import inspect
import json
import math
from pathlib import Path
import platform
import sys
import ctranslate2
from faster_whisper import WhisperModel
ROOT = Path(__file__).resolve().parents[4]
EVIDENCE = Path(__file__).resolve().parent.parent
REVISION = 'd90ca5fe260221311c53c58e660288d3deb8d356'
SNAPSHOT = Path.home() / '.cache/huggingface/hub/models--Systran--faster-whisper-tiny/snapshots' / REVISION
def json_safe(value):
# JSON has no infinity; retain its exact semantic value as an explicit tagged object.
if isinstance(value, float) and not math.isfinite(value):
return {'nonFiniteFloat': 'NaN' if math.isnan(value) else 'Infinity' if value > 0 else '-Infinity'}
if isinstance(value, dict): return {key: json_safe(item) for key, item in value.items()}
if isinstance(value, (list, tuple)): return [json_safe(item) for item in value]
return value
def sha(path):
with path.open('rb') as stream: return hashlib.file_digest(stream, 'sha256').hexdigest()
assert version('faster-whisper') == '1.2.1'
started = datetime.now(timezone.utc).isoformat()
manifest_path = ROOT / 'public/ambience/manifest.json'
manifest_hash = sha(manifest_path)
manifest = json.loads(manifest_path.read_text())
# Preserve all effective API defaults rather than relying on undocumented invocation settings.
parameters = {name: parameter.default for name, parameter in inspect.signature(WhisperModel.transcribe).parameters.items()
if name not in ('self', 'audio')}
parameters.update(language=None, task='transcribe', beam_size=5, best_of=5, vad_filter=True, vad_parameters=None, condition_on_previous_text=True)
prior_path = EVIDENCE / 'prior-attempt-market60-cafe180/final-asr.json'
prior = json.loads(prior_path.read_text())
assert prior['requiredEmptyPassed'] is False
assert json_safe(parameters) == prior['method']['parameters'], 'Decoder/VAD settings must remain identical'
ctranslate2.set_random_seed(0)
model = WhisperModel(str(SNAPSHOT), device='cpu', compute_type='int8', cpu_threads=4, num_workers=1, local_files_only=True)
assert model.model.is_multilingual
results = []
for entry in manifest['files']:
path = ROOT / 'public/ambience' / entry['file']
before = sha(path)
assert before == entry['sha256'] and path.stat().st_size == entry['bytes']
segments, info = model.transcribe(str(path), **parameters)
segments = [asdict(segment) for segment in segments] # Exhaust lazy inference; retain exact unedited results.
assert sha(path) == before
record = {'environmentId': entry['environmentId'], 'file': str(path.relative_to(ROOT)), 'sha256': before,
'bytes': path.stat().st_size, 'durationSeconds': entry['duration'],
'selectedSegmentStartSeconds': entry['selectedSegmentStartSeconds'], 'sourceSha256': entry['sourceSha256'],
'language': info.language, 'languageProbability': info.language_probability,
'segments': segments, 'text': ''.join(segment['text'] for segment in segments), 'info': asdict(info)}
results.append(record)
print(json.dumps({k: record[k] for k in ('environmentId','sha256','language','languageProbability','segments','text')}, ensure_ascii=False), flush=True)
assert sha(manifest_path) == manifest_hash
required = ['mall', 'market', 'cafe']
selected = {result['environmentId']: result for result in results if result['environmentId'] in required}
assert set(selected) == set(required)
assert {key: result['selectedSegmentStartSeconds'] for key, result in selected.items()} == {'mall': 36, 'market': 24, 'cafe': 0}
passed = all(result['segments'] == [] and result['text'] == '' for result in selected.values())
report = {'schemaVersion': 1, 'startedAtUtc': started, 'completedAtUtc': datetime.now(timezone.utc).isoformat(),
'tool': {'name': 'faster-whisper', 'version': version('faster-whisper'), 'python': platform.python_version(),
'dependencies': {name: version(name) for name in ('ctranslate2', 'av', 'numpy', 'tokenizers', 'huggingface-hub')}},
'model': {'name': 'tiny', 'multilingual': True, 'repository': 'Systran/faster-whisper-tiny', 'revision': REVISION,
'files': {path.name: sha(path) for path in sorted(SNAPSHOT.iterdir()) if path.is_file()}},
'method': {'input': 'Exact final 12-second public/ambience WAV files, hash verified before and after inference; built-in Silero VAD with package defaults, no text editing or manual filtering.',
'device': 'cpu', 'computeType': 'int8', 'cpuThreads': 4, 'numWorkers': 1, 'randomSeed': 0,
'nonFiniteNumberEncoding': 'Non-finite default options use {nonFiniteFloat: Infinity|-Infinity|NaN} tagged objects; no ASR segments or text are discarded.', 'offline': True, 'parameters': parameters, 'manifestSha256': manifest_hash,
'vadModelSha256': sha(Path(__import__('faster_whisper').__file__).parent / 'assets/silero_vad_v6.onnx'), 'scriptSha256': sha(Path(__file__)), 'command': '/tmp/findword-asr-venv/bin/python .agent/evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screen-final.py'},
'limitations': {'screeningOnly': True, 'humanListeningAcceptance': False, 'legalPrivacyClearance': False,
'textIsVerifiedTranscript': False,
'explanation': 'Tiny-model ASR is only a screening signal. Empty segments do not prove absence of speech, music or private conversation. Candidate text and language guesses may be hallucinated and are not faithful transcripts. This is NOT human listening acceptance or legal/privacy clearance.'},
'priorFailedAttempt': {'file': 'prior-attempt-market60-cafe180/final-asr.json', 'sha256': sha(prior_path), 'requiredEmptyPassed': False},
'supplementaryNoVadRun': {'file': 'no-vad-asr.json', 'sha256': sha(EVIDENCE / 'no-vad-asr.json'), 'requiredEmptyPassed': False, 'explanation': 'Historical no-VAD run on mall 36s, market 60s and cafe 180s emitted candidate text that may be hallucinated. It is retained unchanged, is not a screen of the replacement market/cafe WAVs, and does not override the specified VAD gate. Neither empty VAD results nor no-VAD text proves absence or presence of speech/music or provides human listening acceptance or legal/privacy clearance.'}, 'requiredEmptyEnvironmentIds': required, 'requiredEmptyPassed': passed, 'files': results}
serialized = json.dumps(json_safe(report), ensure_ascii=False, indent=2, allow_nan=False) + '\n'
with (EVIDENCE / 'final-asr.json').open('x') as stream:
stream.write(serialized)
if not passed: sys.exit('Required changed excerpts produced ASR segments; screening criterion failed.')
print('All three changed excerpts have zero emitted segments. Screening signal only; no listening acceptance or legal/privacy clearance.', flush=True)