Skip to content

Latest commit

 

History

History
456 lines (339 loc) · 29 KB

File metadata and controls

456 lines (339 loc) · 29 KB

Speech Swift

KI-Sprachmodelle für Apple Silicon, basierend auf MLX Swift und CoreML.

📖 Read in: English · 中文 · 日本語 · 한국어 · Español · Deutsch · Français · हिन्दी · Português · Русский · العربية · Tiếng Việt · Türkçe · ไทย

Spracherkennung, -synthese und -verständnis auf dem Gerät für Mac und iOS. Läuft vollständig lokal auf Apple Silicon — keine Cloud, keine API-Schlüssel, keine Daten verlassen das Gerät.

📚 Vollständige Dokumentation → · 🤗 HuggingFace-Modelle · 📝 Blog · 💬 Discord

speech-swift -  The whole speech stack, on your laptop. | Product Hunt

Lokale Sprach-KI auf einem MacBook — die vierminütige Tour durch die Open-Source-Bibliothek auf YouTube ansehen

Lokale Sprach-KI auf einem MacBook — die vierminütige Tour durch die Open-Source-Bibliothek auf YouTube ansehen

Anwendungsfälle: Sprachagenten · Transkription · Sprachsynthese

  • Qwen3-ASR — Sprache-zu-Text (automatische Spracherkennung, 52 Sprachen, MLX + CoreML)
  • Parakeet TDT — Sprache-zu-Text über CoreML (Neural Engine, NVIDIA FastConformer + TDT-Decoder, 25 Sprachen)
  • Omnilingual ASR — Sprache-zu-Text (Meta wav2vec2 + CTC, 1.672 Sprachen in 32 Schriften, CoreML 300M + MLX 300M/1B/3B/7B)
  • Streaming-Diktat — Echtzeit-Diktat mit Teilergebnissen und Äußerungsende-Erkennung (Parakeet-EOU-120M)
  • Nemotron Streaming (Mehrsprachig) — Streaming-ASR mit geringer Latenz, nativer Interpunktion und Großschreibung (NVIDIA Nemotron-3.5-ASR-Streaming-0.6B, CoreML + MLX, 40 Sprach-Lokalisierungen)
  • Nemotron Streaming (Englisch) — Streaming-ASR mit geringer Latenz, nativer Interpunktion und Großschreibung (NVIDIA Nemotron-Speech-Streaming-0.6B, CoreML, nur Englisch, kleiner und schneller als die mehrsprachige Variante)
  • Qwen3-ForcedAligner — Wortgenaue Zeitstempel-Zuordnung (Audio + Text → Zeitstempel)
  • Qwen3-TTS — Sprachsynthese (höchste Qualität, Streaming, benutzerdefinierte Sprecher, 10 Sprachen)
  • CosyVoice TTS — Streaming-TTS mit Stimmklonen, Mehrsprecherdialog, Emotions-Tags (9 Sprachen)
  • VoxCPM2 — 48-kHz-Studio-TTS mit Stimmklonen und sprachbeschreibungsgesteuertem Voice Design (2B, MLX bf16/int8, 30 Sprachen)
  • Kokoro TTS — TTS auf dem Gerät (82M, CoreML/Neural Engine, 54 Stimmen, iOS-tauglich, 10 Sprachen)
  • VibeVoice TTS — Langform-/Multi-Speaker-TTS (Microsoft VibeVoice Realtime-0.5B + 1.5B, MLX, bis zu 90 Min. Podcast-/Hörbuch-Synthese, EN/ZH)
  • Magpie TTS — Mehrsprachiges TTS (NVIDIA Magpie-TTS Multilingual 357M, MLX INT8 411 MB oder CoreML INT8 342 MB, 9 Sprachen, 5 vordefinierte Sprecher, Streaming auf MLX)
  • Supertonic TTS — Flow-Matching-TTS auf dem Gerät (Supertone Supertonic-3 99M, CoreML/Neural Engine, 31 Sprachen, 10 Stimmen, G2P-frei, 44,1 kHz)
  • Qwen3.5-Chat — LLM-Chat auf dem Gerät (0.8B, MLX INT4 + CoreML INT8, DeltaNet-Hybrid, Token-Streaming)
  • FunctionGemma — On-Device-LLM für strukturierte Funktions- / Tool-Aufrufe (Gemma 3 270M, CoreML 8-Bit-Palettierung, Neural Engine, ~252 tok/s)
  • MADLAD-400 — Mehrsprachige Übersetzung über 400+ Sprachen (3B, MLX INT4 + INT8, T5 v1.1, Apache 2.0)
  • Hibiki Zero-3B — Streaming-Sprache-zu-Sprache-Übersetzung (FR/ES/PT/DE → EN, MLX INT4 + INT8, Kyutai Moshi/Mimi-Stack, CC-BY-4.0)
  • PersonaPlex — Vollduplex-Sprache-zu-Sprache (7B, Audio rein → Audio raus, 18 Stimmvoreinstellungen)
  • DeepFilterNet3 — Echtzeit-Rauschunterdrückung (2,1M Parameter, 48 kHz). Langformaudio oberhalb der 60 s Single-Shot-Grenze wird automatisch mit Crossfade in Chunks zerlegt — siehe enhanceChunked(...)
  • Quelltrennung — Musikquelltrennung mit HTDemucs (Demucs v4) + Open-Unmix (UMX-HQ / UMX-L, 4 Stems: Gesang/Drums/Bass/Rest, 44,1 kHz Stereo)
  • MAGNeT — Text-zu-Musik-Generierung (Meta MAGNeT Small 300M / Medium 1.5B, MLX INT8, 30-Sekunden-Clips, 32 kHz Mono, maskierte parallele Dekodierung)
  • FlashSR — Audio-Super-Resolution (FlashSR ICASSP 2025, MLX, 48 kHz Mono, 1-Schritt destillierte Diffusion, INT4 363 MB / INT8 720 MB)
  • Wake-Word — Schlüsselworterkennung auf dem Gerät (KWS Zipformer 3M, CoreML, 26× Echtzeit, konfigurierbare Stichwortliste)
  • VAD — Sprachaktivitätserkennung (Silero Streaming, Pyannote Offline, FireRedVAD 100+ Sprachen)
  • Sprecherdiarisierung — Wer hat wann gesprochen (Pyannote-Pipeline, durchgängiger Sortformer auf der Neural Engine)
  • Sprechereinbettungen — WeSpeaker ResNet34 (256-dim), CAM++ (192-dim)

Paper: Qwen3-ASR (Alibaba) · Qwen3-TTS (Alibaba) · Omnilingual ASR (Meta) · Parakeet TDT (NVIDIA) · CosyVoice 3 (Alibaba) · Kokoro (StyleTTS 2) · PersonaPlex (NVIDIA) · Mimi (Kyutai) · Hibiki (Kyutai) · Sortformer (NVIDIA)

Neuigkeiten

Schnellstart

Füge das Paket zu deiner Package.swift hinzu:

.package(url: "https://github.com/soniqo/speech-swift", branch: "main")

Importiere nur die Module, die du benötigst — jedes Modell ist eine eigene SPM-Bibliothek, du zahlst nicht für das, was du nicht nutzt:

.product(name: "ParakeetStreamingASR", package: "speech-swift"),
.product(name: "SpeechUI",             package: "speech-swift"),  // optionale SwiftUI-Views

Audio-Puffer in 3 Zeilen transkribieren:

import ParakeetStreamingASR

let model = try await ParakeetStreamingASRModel.fromPretrained()
let text = try model.transcribeAudio(audioSamples, sampleRate: 16000)

Live-Streaming mit Teilergebnissen:

for await partial in model.transcribeStream(audio: samples, sampleRate: 16000) {
    print(partial.isFinal ? "FINAL: \(partial.text)" : "... \(partial.text)")
}

SwiftUI-Diktat-View in ~10 Zeilen:

import SwiftUI
import ParakeetStreamingASR
import SpeechUI

@MainActor
struct DictateView: View {
    @State private var store = TranscriptionStore()

    var body: some View {
        TranscriptionView(finals: store.finalLines, currentPartial: store.currentPartial)
            .task {
                let model = try? await ParakeetStreamingASRModel.fromPretrained()
                guard let model else { return }
                for await p in model.transcribeStream(audio: samples, sampleRate: 16000) {
                    store.apply(text: p.text, isFinal: p.isFinal)
                }
            }
    }
}

SpeechUI liefert nur TranscriptionView (finale + partielle Ergebnisse) und TranscriptionStore (Streaming-ASR-Adapter). Verwende AVFoundation für Audio-Visualisierung und Wiedergabe.

Verfügbare SPM-Produkte: Qwen3ASR, Qwen3TTS, Qwen3TTSCoreML, ParakeetASR, ParakeetStreamingASR, NemotronStreamingASR, OmnilingualASR, KokoroTTS, SupertonicTTS, VibeVoiceTTS, CosyVoiceTTS, VoxCPM2TTS, MagpieTTS, MagpieTTSCoreML, MAGNeTMusicGen, FlashSR, PersonaPlex, HibikiTranslate, SpeechVAD, SpeechEnhancement, SourceSeparation, Qwen3Chat, SpeechCore, SpeechUI, AudioCommon.

Modelle

Kompakte Übersicht unten. Vollständiger Modellkatalog mit Größen, Quantisierungen, Download-URLs und Speichertabellen → soniqo.audio/architecture.

Modell Aufgabe Backends Größen Sprachen
Qwen3-ASR Sprache → Text MLX, CoreML (hybrid) 0.6B, 1.7B 52
Parakeet TDT Sprache → Text CoreML (ANE) 0.6B 25 europäisch
Parakeet EOU Sprache → Text (Streaming) CoreML (ANE) 120M 25 europäisch
Nemotron Streaming (Mehrsprachig) Sprache → Text (Streaming, mit Interpunktion) CoreML (ANE), MLX 0.6B 40
Nemotron Streaming (Englisch) Sprache → Text (Streaming, mit Interpunktion) CoreML (ANE) 0.6B EN
Omnilingual ASR Sprache → Text CoreML (ANE), MLX 300M / 1B / 3B / 7B 1.672
Qwen3-ForcedAligner Audio + Text → Zeitstempel MLX, CoreML 0.6B Multi
Qwen3-TTS Text → Sprache MLX, CoreML 0.6B, 1.7B 10
CosyVoice3 Text → Sprache MLX 0.5B 9
VoxCPM2 Text → Sprache (48 kHz, Voice Design + Klonen) MLX 2B (bf16/int8) 30
Kokoro-82M Text → Sprache CoreML (ANE) 82M 10
Supertonic-3 Text → Sprache (44,1 kHz, Flow-Matching, G2P-frei) CoreML (ANE) 99M 31
VibeVoice Realtime-0.5B Text → Sprache (Langform, Multi-Speaker) MLX 0.5B EN/ZH
VibeVoice 1.5B Text → Sprache (bis zu 90 Min. Podcast) MLX 1.5B EN/ZH
Magpie-TTS Multilingual Text → Sprache (5 vordefinierte Sprecher, Streaming) MLX / CoreML 357M (MLX INT8, CoreML INT8) 9 (CoreML ohne JA)
Qwen3.5-Chat Text → Text (LLM) MLX, CoreML 0.8B Multi
FunctionGemma Text → Tool-Aufrufe (LLM) CoreML 270M EN
MADLAD-400 Text → Text (Übersetzung) MLX 3B 400+
Hibiki Zero-3B Sprache → Sprache (Übersetzung) MLX 3B FR/ES/PT/DE → EN
PersonaPlex Sprache → Sprache MLX 7B EN
Silero VAD Sprachaktivitätserkennung MLX, CoreML 309K Sprachunabhängig
Pyannote VAD + Diarisierung MLX 1.5M Sprachunabhängig
Sortformer Diarisierung (E2E) CoreML (ANE) Sprachunabhängig
DeepFilterNet3 Sprachverbesserung CoreML 2.1M Sprachunabhängig
Sidon Sprachwiederherstellung (Rauschunterdrückung + Enthallung, 48 kHz) CoreML w2v-BERT 2.0 + DAC (fp16/int8) Sprachunabhängig
HTDemucs (Demucs v4) Quelltrennung MLX 168M Agnostic
Open-Unmix Quelltrennung MLX 8.6M Agnostic
MAGNeT Text → Musik (30 s @ 32 kHz) MLX 300M / 1.5B (int4/int8) EN-Prompts
FlashSR Audio-Super-Resolution (48 kHz) MLX 363 MB / 720 MB (int4/int8) Agnostisch
WeSpeaker Sprechereinbettung MLX, CoreML 6.6M Sprachunabhängig

Installation

Homebrew

Erfordert natives ARM-Homebrew (/opt/homebrew). Rosetta/x86_64-Homebrew wird nicht unterstützt.

brew install speech

Dann:

speech transcribe recording.wav
speech speak "Hello world"
speech translate "Hello, how are you?" --to es
speech respond --input question.wav --transcript
speech-server --port 8080            # lokaler HTTP/WebSocket-Server (OpenAI-kompatibles /v1/realtime + /v1/audio/transcriptions)

Vollständige CLI-Referenz →

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/soniqo/speech-swift", branch: "main")
]

Importiere nur, was du brauchst — jedes Modell hat sein eigenes SPM-Target:

import Qwen3ASR             // Spracherkennung (MLX)
import ParakeetASR          // Spracherkennung (CoreML, Batch)
import ParakeetStreamingASR // Streaming-Diktat mit Teilergebnissen + EOU
import NemotronStreamingASR // Mehrsprachiges Streaming-ASR mit nativer Interpunktion (0.6B, 40 Sprachen)
import OmnilingualASR       // 1.672 Sprachen (CoreML + MLX)
import Qwen3TTS             // Sprachsynthese
import CosyVoiceTTS         // Sprachsynthese mit Stimmklonen
import VoxCPM2TTS           // 48-kHz-TTS, Stimmklonen + Voice Design (2B)
import KokoroTTS            // Sprachsynthese (iOS-tauglich)
import VibeVoiceTTS         // Langform-/Multi-Speaker-TTS (EN/ZH)
import MagpieTTS            // Mehrsprachiges TTS (NVIDIA Magpie 357M, MLX, 9 Sprachen)
import MagpieTTSCoreML      // Magpie CoreML-Backend (Hybrid CoreML + MLX, 8 Sprachen)
import Qwen3Chat            // LLM-Chat auf dem Gerät
import FunctionGemma    // On-Device-LLM für Tool-Aufrufe
import MADLADTranslation    // Mehrsprachige Übersetzung über 400+ Sprachen
import HibikiTranslate      // Streaming-Sprache-zu-Sprache-Übersetzung (FR/ES/PT/DE → EN)
import PersonaPlex          // Vollduplex-Sprache-zu-Sprache
import SpeechVAD            // VAD + Sprecherdiarisierung + Einbettungen
import SpeechEnhancement    // Rauschunterdrückung
import SpeechRestoration    // Sprachwiederherstellung — Rauschunterdrückung + Enthallung (Sidon, CoreML, 48 kHz)
import SourceSeparation     // Musikquelltrennung (Open-Unmix, 4 Stems)
import MAGNeTMusicGen      // Text-zu-Musik-Generierung (30 s, 32 kHz)
import FlashSR             // Audio-Super-Resolution (48 kHz, 1-Schritt-Diffusion)
import SpeechUI             // SwiftUI-Komponenten für Streaming-Transkripte
import AudioCommon          // Geteilte Protokolle und Utilities

Voraussetzungen

  • Swift 6+, Xcode 16+ (mit Metal Toolchain)
  • macOS 15+ (Sequoia) oder iOS 18+, Apple Silicon (M1/M2/M3/M4)

Die Mindestanforderung macOS 15 / iOS 18 kommt von MLState —— Apples persistenter ANE-State-API —— die die CoreML-Pipelines (Qwen3-ASR, Qwen3-Chat, Qwen3-TTS) nutzen, um KV-Caches zwischen Token-Schritten auf der Neural Engine zu halten.

Aus dem Quellcode bauen

git clone https://github.com/soniqo/speech-swift
cd speech-swift
make build

make build kompiliert das Swift-Paket und die MLX-Metal-Shader-Bibliothek. Die Metal-Bibliothek ist für GPU-Inferenz erforderlich — ohne sie siehst du zur Laufzeit Failed to load the default metallib. make debug für Debug-Builds, make test für die Test-Suite.

Vollständige Build- und Installationsanleitung →

Demo-Apps

  • DictateDemo (Docs) — macOS-Menüleisten-Streaming-Diktat mit Live-Teilergebnissen, VAD-basierter Äußerungsende-Erkennung und Ein-Klick-Kopieren. Läuft als Hintergrund-agent (Parakeet-EOU-120M + Silero VAD).
  • iOSEchoDemo — iOS-Echo-Demo (Parakeet ASR + Kokoro TTS). Gerät und Simulator.
  • PersonaPlexDemo — Konversationeller Sprachassistent mit Mikrofoneingang, VAD und Multi-Turn-Kontext. macOS. RTF ~0.94 auf M2 Max (schneller als Echtzeit).
  • SpeechDemo — Diktat und TTS-Synthese in einer Tab-Oberfläche. macOS.

Die README jedes Demos enthält Bauanleitungen.

Codebeispiele

Die folgenden Snippets zeigen den minimalen Pfad für jede Domäne. Jeder Abschnitt verlinkt auf eine vollständige Anleitung auf soniqo.audio mit Konfigurationsoptionen, mehreren Backends, Streaming-Mustern und CLI-Rezepten.

Sprache-zu-Text — vollständige Anleitung →

import Qwen3ASR

let model = try await Qwen3ASRModel.fromPretrained()
let text = model.transcribe(audio: audioSamples, sampleRate: 16000)

Alternative Backends: Parakeet TDT (CoreML, 32× Echtzeit), Omnilingual ASR (1.672 Sprachen, CoreML oder MLX), Streaming-Diktat (Live-Teilergebnisse).

Forced Alignment — vollständige Anleitung →

import Qwen3ASR

let aligner = try await Qwen3ForcedAligner.fromPretrained()
let aligned = aligner.align(
    audio: audioSamples,
    text: "Can you guarantee that the replacement part will be shipped tomorrow?",
    sampleRate: 24000
)
for word in aligned {
    print("[\(word.startTime)s - \(word.endTime)s] \(word.text)")
}

Text-zu-Sprache — vollständige Anleitung →

import Qwen3TTS
import AudioCommon

let model = try await Qwen3TTSModel.fromPretrained()
let audio = model.synthesize(text: "Hello world", language: "english")
try WAVWriter.write(samples: audio, sampleRate: 24000, to: outputURL)

Alternative TTS-Engines: CosyVoice3 (Streaming + Stimmklonen + Emotions-Tags), Kokoro-82M (iOS-tauglich, 54 Stimmen), VibeVoice (Langform-Podcast / Multi-Speaker, EN/ZH), Stimmklonen.

Sprache-zu-Sprache — vollständige Anleitung →

import PersonaPlex

let model = try await PersonaPlexModel.fromPretrained()
let responseAudio = model.respond(userAudio: userSamples)
// 24 kHz Mono Float32-Ausgabe, bereit zur Wiedergabe
import Qwen3Chat
import FunctionGemma

let chat = try await Qwen35MLXChat.fromPretrained()
chat.chat(messages: [(.user, "Explain MLX in one sentence")]) { token, isFinal in
    print(token, terminator: "")
}
import MADLADTranslation

let translator = try await MADLADTranslator.fromPretrained()
let es = try translator.translate("Hello, how are you?", to: "es")
// → "Hola, ¿cómo estás?"

Sprachübersetzung — vollständige Anleitung →

import HibikiTranslate
import AudioCommon

let model = try await HibikiTranslateModel.fromPretrained()
let pcm = try AudioFileLoader.load(url: input, targetSampleRate: 24000)
let (englishAudio, textTokens) = model.translate(
    sourceAudio: pcm, sourceLanguage: .fr
)
// Hibiki Zero-3B — FR/ES/PT/DE → EN, auf dem Gerät, Streaming-Mimi-Codec

Sprachaktivitätserkennung — vollständige Anleitung →

import SpeechVAD

let vad = try await SileroVADModel.fromPretrained()
let segments = vad.detectSpeech(audio: samples, sampleRate: 16000)
for s in segments { print("\(s.startTime)s → \(s.endTime)s") }

Sprecherdiarisierung — vollständige Anleitung →

import SpeechVAD

let diarizer = try await DiarizationPipeline.fromPretrained()
let segments = diarizer.diarize(audio: samples, sampleRate: 16000)
for s in segments { print("Speaker \(s.speakerId): \(s.startTime)s - \(s.endTime)s") }

Sprachverbesserung — vollständige Anleitung →

import SpeechEnhancement

let denoiser = try await DeepFilterNet3Model.fromPretrained()
let clean = try denoiser.enhance(audio: noisySamples, sampleRate: 48000)

Sprachwiederherstellung — vollständige Anleitung →

Gemeinsame Rauschunterdrückung und Enthallung mit Sidon (w2v-BERT-2.0-Prädiktor + DAC-Vocoder, Core ML). Anders als ein generischer Rauschunterdrücker ist Sidon darauf trainiert, die Sprecheridentität zu bewahren, und eignet sich daher gut, um eine verrauschte oder verhallte Referenz fürs Stimmklonen vor der TTS zu säubern. Die Eingabe ist 16 kHz, die Ausgabe 48 kHz Mono.

import SpeechRestoration

let restorer = try await SpeechRestorer.fromPretrained()          // .fp16 (default) or .int8
let clean = try restorer.restore(audio: noisySamples, sampleRate: 16000)  // → 48 kHz

Über die CLI:

speech restore noisy.wav -o clean.wav            # denoise + dereverb, 48 kHz output
speech restore noisy.wav --variant int8          # smaller, lower peak RAM

# Clean a voice-cloning reference before TTS (opt-in; preserves speaker identity):
speech speak "Hello" --engine voxcpm2 --voice-sample ref.wav --clean-reference

Voice Pipeline (ASR → LLM → TTS) — vollständige Anleitung →

import SpeechCore

let pipeline = VoicePipeline(
    stt: parakeetASR,
    tts: qwen3TTS,
    vad: sileroVAD,
    config: .init(mode: .voicePipeline),
    onEvent: { event in print(event) }
)
pipeline.start()
pipeline.pushAudio(micSamples)

VoicePipeline ist die Echtzeit-Voice-agent-Zustandsmaschine (angetrieben von speech-core) mit VAD-basierter Sprecherwechsel-Erkennung, Unterbrechungsbehandlung und eager STT. Sie verbindet beliebige SpeechRecognitionModel + SpeechGenerationModel + StreamingVADProvider.

HTTP-API-Server

speech-server --port 8080

Stellt jedes Modell über HTTP-REST- + WebSocket-Endpunkte bereit, einschließlich OpenAI-kompatibler APIs: einem Realtime-WebSocket unter /v1/realtime und einem Transkriptions-REST-Endpunkt unter /v1/audio/transcriptions. Siehe Sources/AudioServer/.

Architektur

speech-swift ist in ein SPM-Target pro Modell aufgeteilt, sodass Konsumenten nur für das bezahlen, was sie importieren. Geteilte Infrastruktur lebt in AudioCommon (Protokolle, Audio-I/O, HuggingFace-Downloader, SentencePieceModel) und MLXCommon (Gewichtsladen, QuantizedLinear-Helfer, SDPA-Multi-Head-Attention-Helfer).

Vollständiges Architekturdiagramm mit Backends, Speichertabellen und Modulkarte → soniqo.audio/architecture · API-Referenz → soniqo.audio/api · Benchmarks → soniqo.audio/benchmarks

Lokale Docs (Repo):

Cache-Konfiguration

Modellgewichte werden beim ersten Gebrauch von HuggingFace heruntergeladen und in ~/Library/Caches/qwen3-speech/ zwischengespeichert. Überschreibe mit QWEN3_CACHE_DIR (CLI) oder cacheDir: (Swift-API). Alle fromPretrained()-Einstiegspunkte akzeptieren offlineMode: true, um das Netzwerk zu überspringen, wenn die Gewichte bereits im Cache sind.

Nutzer in Festlandchina (oder überall dort, wo huggingface.co langsam oder blockiert ist) können über einen Mirror laden, indem sie HF_ENDPOINT setzen, z. B. export HF_ENDPOINT=https://hf-mirror.com.

Siehe docs/inference/cache-and-offline.md für vollständige Details einschließlich sandboxed iOS-Container-Pfade.

MLX-Metal-Bibliothek

Wenn du zur Laufzeit Failed to load the default metallib siehst, fehlt die Metal-Shader-Bibliothek. Führe nach einem manuellen swift build make build oder ./scripts/build_mlx_metallib.sh release aus. Falls das Metal Toolchain fehlt, installiere es zuerst:

xcodebuild -downloadComponent MetalToolchain

Tests

make test                            # Vollständige Suite (Unit + E2E mit Modell-Downloads)
swift test --skip E2E                # Nur Unit (CI-sicher, keine Downloads)
swift test --filter Qwen3ASRTests    # Bestimmtes Modul

E2E-Testklassen verwenden das Präfix E2E, damit CI sie mit --skip E2E ausfiltern kann. Siehe CLAUDE.md für die vollständige Testkonvention.

Mitwirken

PRs willkommen — Bugfixes, neue Modellintegrationen, Dokumentation. Fork, Feature-Branch anlegen, make build && make test, PR gegen main eröffnen.

Lizenz

Apache 2.0