Skip to content

feat: transcribe on Intel integrated GPUs via OpenVINO (#131) - #132

Open
raphaelm22 wants to merge 2 commits into
ObscureAintSecure:masterfrom
raphaelm22:feat/openvino-intel-gpu
Open

raphaelm22 wants to merge 2 commits into
ObscureAintSecure:masterfrom
raphaelm22:feat/openvino-intel-gpu

Conversation

@raphaelm22

@raphaelm22 raphaelm22 commented Oct 14, 2026 •

Copy link
Copy Markdown

Draft: I'm running this on my real meetings before marking it ready. Design feedback is welcome now.

Summary

Implements #131. A third Compute Device, Intel GPU (OpenVINO), runs Whisper on Intel integrated GPUs through OpenVINO GenAI's WhisperPipeline. faster-whisper (CTranslate2) only supports the CPU and NVIDIA.

On a Core Ultra 7 265U (Intel Graphics iGPU), the test was a real 7.4 min pt-BR meeting with large-v3-turbo:

Transcription
CPU, faster-whisper (today) 7.7 min
Intel GPU, this PR (in the app's worker) 40–49 s
first use: +34 s model download, +~40 s GPU compile (cached afterwards) 83 s

On a 60 s excerpt the text matched faster-whisper's word for word, and segment start/end times are the same.

How it works

  • app/transcription/openvino_whisper.py (no Qt; OpenVINO and faster-whisper imported inside functions):
    • Intel's int8 builds OpenVINO/whisper-<size>-int8-ov, one per model size in Settings, downloaded to the normal HF cache.
    • A resident pipeline cache like _MODEL_CACHE. The compiled model is cached in ~/.talktrack/openvino_cache.
  • VAD is ours to do. WhisperPipeline has none, and Whisper invents text over silence: 20 s of silence came back as "E aí". faster-whisper's own Silero VAD picks the speech, only speech is decoded, and a TimeMap maps times back to the recording.
  • Batches of ≤ 60 s of speech. generate() blocks, and its streamer is not available with timestamps on long audio, so batches are the only progress and cancel points.
    • Meeting speech rarely pauses for the VAD's 2 s; one region ran 264 s. So the VAD also gets max_speech_duration_s=60, which splits long speech at a short pause, as the batched pipeline does at 30 s.
    • This brought Cancel from ~20 s to 5 s at the same total time. Compared with 120 s batches, it also stopped some phrases from being dropped.
  • Language is detected on the first batch, then pinned, as faster-whisper detects once per recording.
  • TranscriptionWorker._run_openvino: any OpenVINO failure falls back to the CPU, at load (package missing, no GPU) or mid-run (driver hang); the pipeline is evicted and the user sees the reason in the progress line. Unreadable audio still errors, since the CPU would fail too.
    • This is broader than the diarizer's OOM-only retry, on purpose. These failures are environmental, and the transcript matters more than surfacing them.
  • Settings: new device entry with inline status (ready / not installed / no Intel GPU). openvino-genai (~90 MB download) is installed on demand through the existing package_installer, as the AI SDKs are.
  • System Status:
    • An Intel GPU (OpenVINO) row appears only when that device is selected, since checks may now return None.
    • The Whisper model check looks for the OpenVINO build.
    • GPU Acceleration no longer tells Intel GPU users to switch to CPU.
  • Packaging:
    • openvino extra in pyproject.toml, plus a commented line in requirements.txt. One test keeps the three pip specs equal.
    • uv.lock adds only the four new packages. As in fix: cap PyAV below 19 so faster-whisper can decode audio (#127) #130, I hand-applied them over master's lock to avoid uv's marker reordering, and uv lock --check passes on uv 0.12.7 and 0.13.0.
    • The module is added to the CI import smoke.
  • Diarization stays on the CPU (_resolve_device("intel_gpu") → cpu). OpenVINO ships TBB rather than another libiomp5md.dll, so App deadlocks at zero CPU when diarization follows transcription (duplicate libiomp5md.dll) #70's duplicate-OpenMP deadlock doesn't apply. pyannote ran normally in the same process after OpenVINO.

Worth reviewing

  • Config.set() saves immediately. The OpenVINO install prompt therefore runs before any config.set() in _save_and_close, so declining leaves settings.json untouched. The existing AI-SDK prompt sits after the sets, so its return # User cancelled install, don't save does not actually prevent the save. I left that alone since it's out of scope here.
  • confidence stays 0.0 for these segments. OpenVINO reports no per-segment log-probability, and nothing reads the field today.
  • No NPU option. On this machine's NPU, large-v3-turbo hangs the device (ZE_RESULT_ERROR_DEVICE_LOST, 2/2 runs), and only small ran (35 s for the same meeting).

Tests

  • New tests (49):
    • tests/test_openvino_whisper.py: model repos, batching, time mapping, the transcribe loop against a fake pipeline, language pinning, GPU detection, the pipeline cache and download, the pip-spec sync and the Settings prompt order.
    • Worker tests in test_transcriber.py: GPU path, fallback at load and mid-run, cancel, unreadable audio.
    • System Status tests in test_dependency_checker.py.
  • Full suite: 457 passed.
  • End to end: inside an offscreen QApplication with torch loaded first, as main.py does, I ran the real TranscriptionWorker on the meeting:
    • first use (download and compile) and a warm run
    • cancel after 8 s
    • CPU fallback with openvino_genai blocked
    • DiarizationWorker afterwards in the same process
  • Settings: exercised with real widgets offscreen, with the config's save disabled so nothing was written.

Closes #131

🤖 Generated with Claude Code

…cure#131)

faster-whisper (CTranslate2) only runs on the CPU or NVIDIA GPUs. A new
"Intel GPU (OpenVINO)" compute device runs Whisper through OpenVINO
GenAI's WhisperPipeline on Intel's int8 model builds. On a Core Ultra 7
265U, large-v3-turbo transcribed a 7.4 min meeting in ~45 s instead of
7.7 min on the CPU.

- openvino_whisper.py (no Qt): model repos, pipeline cache with a
  compiled-model cache, faster-whisper's Silero VAD (WhisperPipeline has
  none and hallucinates over silence), 60 s speech batches as the only
  progress/cancel points, language detected once then pinned.
- TranscriptionWorker._run_openvino: any OpenVINO failure, at load or
  mid-run, falls back to the CPU; unreadable audio still errors.
- Settings: new device entry with inline status; openvino-genai is
  installed on demand, asked before any config write since
  Config.set() saves immediately.
- System Status: Intel GPU row when selected, and the Whisper model
  check looks for the OpenVINO build.
- openvino extra in pyproject (uv.lock: only the four new packages),
  commented line in requirements.txt, module in the CI import smoke.
@raphaelm22

Copy link
Copy Markdown
Author

Ready for review. Used daily since 15/10 on a Core Ultra 7 265U (Intel GPU selected, live transcription on): 7 real Teams meetings, 11 to 115 min, pt-BR, 4 to 6 speakers. No fallbacks or errors in the log. Whisper large-v3-turbo on the iGPU throughout, no CPU fallback needed.

@raphaelm22
raphaelm22 marked this pull request as ready for review October 22, 2026 13:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: transcribe on Intel integrated GPUs via OpenVINO (~8x faster than CPU on Core Ultra)

1 participant