SPB Git

spb/zyquo-mlx Public MIT

The local MLX foundry for your Mac — run, fine-tune, quantize, and ship models. Nothing leaves your machine.

Swift 93.4% Python 3.8% Makefile 2.2% Shell 0.5%
2.1 KB · 70 lines swift
Raw Blame History
1//2//  SpeechService.swift3//  Zyquo MLX4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//89import Foundation1011/// Transcription result from a speech model.12struct TranscriptionResult: Sendable {13    var text: String14    var language: String?15    var segments: Int16    var duration: TimeInterval17}1819/// Speech-to-text via the Python bridge (mlx-whisper — Python-only per20/// docs/MLX-RESEARCH.md §2). Installs the pinned package on first use.21actor SpeechService {2223    static let shared = SpeechService()2425    /// Exact pin per docs/MLX-RESEARCH.md version snapshot.26    static let whisperPin = "mlx-whisper==0.4.3"2728    private var whisperInstalled = false2930    func transcribe(model: LocalModel, audio: URL) async throws -> TranscriptionResult {31        try await ensureWhisper()3233        let start = Date()34        let stream = try await PythonRunner.shared.stream(35            script: "zyquo_transcribe",36            arguments: ["--model", model.directory.path, "--audio", audio.path])3738        var text = ""39        var language: String?40        var segments = 041        for try await event in stream {42            switch event.event {43            case "done":44                text = event.string("text") ?? ""45                language = event.string("language")46                segments = event.int("segments") ?? 047            case "error":48                throw PythonRunnerError.processFailed(49                    exitCode: 1, stderr: event.string("message") ?? "transcription failed")50            default:51                break52            }53        }54        return TranscriptionResult(55            text: text, language: language, segments: segments,56            duration: Date().timeIntervalSince(start))57    }5859    private func ensureWhisper() async throws {60        guard !whisperInstalled else { return }61        let env = PythonEnvironment.shared62        if await !env.isProvisioned {63            try await env.provision()64        }65        // Cheap idempotent install (uv resolves instantly when satisfied).66        try await env.install(pins: [Self.whisperPin])67        whisperInstalled = true68    }69}70