Chunked text output previously joined every word into one line and discarded speaker labels. Add a shared plain-text formatter that wraps lines, breaks paragraphs at speaker changes and pauses, and splits long turns while preserving word order. Display provider speaker IDs with chunk scope where needed, without implying that IDs identify the same person across requests. Request structured output for short diarized recordings too, and warn when speaker labels are missing or incomplete. Keep SRT formatting separate and preserve existing line breaks for short requests without diarization. Test Plan: - python3 -B -m unittest -q: all 43 tests passed. - bash -n transcribe and git diff --cached --check: passed. - Reflowed the existing transcript locally and verified all 15,888 words were preserved; source audio and generated transcripts are not committed. - A live excerpt check was blocked before upload by unavailable 1Password access; actual provider diarization remains unverified.
83 lines
3.3 KiB
Python
83 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Test-only replacements for op/curl; no network or real credentials."""
|
|
|
|
import base64
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
if Path(sys.argv[0]).name == "op":
|
|
print("test-api-key")
|
|
sys.exit(0)
|
|
|
|
args = sys.argv[1:]
|
|
request = json.loads(Path(args[args.index("--data-binary") + 1][1:]).read_text())
|
|
output = Path(args[args.index("-o") + 1])
|
|
index = int(output.stem.split("-")[1])
|
|
manifest = json.loads((output.parent / "chunks.json").read_text())
|
|
chunk = manifest[index]
|
|
assert request["model"] == "microsoft/mai-transcribe-2"
|
|
assert request["input_audio"]["format"] == "mp3"
|
|
if len(manifest) > 1:
|
|
assert request["response_format"] == "verbose_json"
|
|
assert request["timestamp_granularities"] == ["segment", "word"]
|
|
|
|
audio = output.parent / "uploaded.mp3"
|
|
audio.write_bytes(base64.b64decode(request["input_audio"]["data"], validate=True))
|
|
info = json.loads(subprocess.check_output([
|
|
"ffprobe", "-v", "error", "-show_entries",
|
|
"format=duration:stream=codec_name,sample_rate,channels", "-of", "json", str(audio),
|
|
], text=True))
|
|
assert info["streams"][0] == {"codec_name": "mp3", "sample_rate": "16000", "channels": 1}
|
|
assert abs(float(info["format"]["duration"]) - (chunk["end"] - chunk["start"])) < 0.1
|
|
with open(os.environ["TRANSCRIBE_TEST_LOG"], "a") as log:
|
|
log.write(json.dumps({
|
|
"index": index, "chunk": chunk, "staging": str(output.parent),
|
|
"upload_duration": float(info["format"]["duration"]),
|
|
"provider": request.get("provider"),
|
|
"response_format": request.get("response_format"),
|
|
"timestamp_granularities": request.get("timestamp_granularities"),
|
|
}) + "\n")
|
|
|
|
mode = os.environ.get("TRANSCRIBE_TEST_MODE", "ok")
|
|
if index == 1:
|
|
if mode == "http-error":
|
|
output.write_text('{"error":{"message":"simulated timeout"}}')
|
|
sys.exit(22)
|
|
if mode == "bad-json":
|
|
output.write_text("not json")
|
|
sys.exit(0)
|
|
if mode == "api-error":
|
|
output.write_text('{"error":{"message":"simulated API error"},"text":""}')
|
|
sys.exit(0)
|
|
if mode == "missing-words":
|
|
output.write_text('{"text":"speech without timestamps"}')
|
|
sys.exit(0)
|
|
|
|
if "response_format" not in request:
|
|
response = {"text": "Provider's original formatting.\nNext line."}
|
|
else:
|
|
words = [
|
|
{"word": text, "start": start - chunk["start"], "end": end - chunk["start"]}
|
|
for text, start, end in [
|
|
("Hello.", 1, 1.4), ("boundary", 56.7, 57.1),
|
|
("continues.", 57.4, 57.8), ("Last.", 65, 65.4),
|
|
] if start >= chunk["start"] and end <= chunk["end"]
|
|
]
|
|
if mode in ("speakers", "partial-speakers"):
|
|
if len(manifest) == 1:
|
|
words = [
|
|
{"word": "Hello", "start": 1, "end": 1.4},
|
|
{"word": "there", "start": 1.5, "end": 1.9},
|
|
]
|
|
if request["provider"]["options"]["azure"]["diarization"]["enabled"]:
|
|
for speaker, word in enumerate(words):
|
|
if mode == "speakers" or speaker == 0:
|
|
word["speaker"] = (speaker if len(manifest) == 1 else
|
|
int(word["start"] + chunk["start"] >= 57.3))
|
|
response = {"text": " ".join(w["word"] for w in words), "words": words}
|
|
output.write_text(json.dumps(response))
|