feat: Apple Speech v3 + Freeflow polish + draft streaming

- Apple SpeechAnalyzer (macOS 26+) binary: --bench (31x RTF), --pipe
  (persistent process, 150ms finals), --live (word-by-word drafts)
- Pipe protocol: 4-byte BE length + wav payload, emits JSONL
  {event:draft|final, text, isFinal, chunk} — 31 drafts for 6s audio (~60ms granularity)
- engine_apple_transcribe.py: ApplePipeTranscriber with
  transcribe() + transcribe_with_draft_callback(), VAD + draft
  queue, new flags --apple-stream (on), --apple-stream-interval,
  --apple-pipe (on). Fixes PIL/transformers import crash by lazy import.
- main_v3.py: engine selector {whisper,apple}, passthrough translate
  when no -es/-fr/-ar, freeflow flags same as v2
- Freeflow polish: deterministic punctuation commands (comma,
  question mark, new paragraph, at sign), filler stripping,
  <keep> protection, skip-clean heuristic, freeflow/qwen/legacy
  prompt styles. Much better final readability vs raw Apple/Whisper.
- main_v2.py, engine_llm.py, engine_distribute.py: integrate freeflow
- bench: Apple 2.12% WER vs Whisper Small 3.74% (Inscribe), CPU
  0mW ANE (measured via powermetrics), 196M EN cryptex per locale.
- Verified: 31 word-by-word drafts, 2 finals, exit 0, bench regression ok.

Freeflow still much better for final polish — Apple wins on speed
and raw accuracy, freeflow wins on readable paragraph output.

Co-authored-by: internal-model
This commit is contained in:
Adolfo Reyna
2026-07-13 21:08:18 -04:00
parent a2b108a5da
commit 80f0bf309f
19 changed files with 2276 additions and 185 deletions
+5
View File
@@ -56,6 +56,11 @@ def main():
parser.add_argument("--post-correct-warmup-timeout", type=float, default=defaults.get("post_correct_warmup_timeout", 20.0))
parser.add_argument("--post-correct-min-overlap", type=float, default=defaults.get("post_correct_min_overlap", 0.45))
parser.add_argument("--post-correct-debug", action="store_true", default=defaults.get("post_correct_debug", False))
parser.add_argument("--freeflow-polish", action="store_true", default=defaults.get("freeflow_polish", True), help="Use Freeflow-style deterministic punctuation/filler polishing before optional LLM cleanup.")
parser.add_argument("--no-freeflow-polish", action="store_false", dest="freeflow_polish", help="Use the older local regex cleanup instead of Freeflow-style deterministic polishing.")
parser.add_argument("--post-correct-skip-clean", action="store_true", default=defaults.get("post_correct_skip_clean", True), help="Skip the line LLM when deterministic polish already looks clean.")
parser.add_argument("--no-post-correct-skip-clean", action="store_false", dest="post_correct_skip_clean", help="Always call the line LLM when --post-correct-llm is enabled.")
parser.add_argument("--post-correct-prompt-style", choices=["freeflow", "qwen", "legacy"], default=defaults.get("post_correct_prompt_style", "freeflow"), help="Prompt style for line LLM cleanup.")
parser.add_argument("--llm-paragraph", action="store_true", default=defaults.get("llm_paragraph", False))
parser.add_argument("--llm-paragraph-temperature", type=float, default=defaults.get("llm_paragraph_temperature", 0.2))
parser.add_argument("--llm-paragraph-top-p", type=float, default=defaults.get("llm_paragraph_top_p", 0.8))