Traduzione automatica · Originale inglese
Speech Server
Tempo di esecuzione nativo di Apple · PocketTTS
PocketTTS aggiunge la sintesi vocale nativo CoreML a speech-swift. Tutte le fasi di default all'esecuzione della CPU. I flussi preimpostati di Alba inglese 1,920-sample frame a 24 kHz (80 ms per frame).
Utilizzare taschino-tt con voce alba. Streaming HTTP richiede PCM; WebSocket emette delta audio. I modelli predefiniti esistenti sono invariati.
curl http://localhost:8080/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{"model":"pocket-tts","voice":"alba","input":"Hello world.","response_format":"pcm","stream":true}' \
-o hello.pcmspeech-server è un server HTTP locale + WebSocket che espone ogni modello Soniqo attraverso un semplice REST API, più un OpenAI compatibile con API in tempo reale WebSocket a) /v1/realtime. Naviga nella stessa bottiglia Homebrew come la speech CLI — brew install speech gocce entrambe sul tuo PATH.
Installare ed eseguire
brew install speech
speech-server --port 8080
# Starting server on http://127.0.0.1:8080
# Endpoints:
# POST /transcribe - Speech-to-text (WAV body or JSON with audio_base64)
# POST /speak - Text-to-speech (JSON: {text, engine?, language?})
# POST /respond - Speech-to-speech (WAV body, voice/max_steps via query)
# POST /enhance - Speech enhancement (WAV body)
# GET /health - Health check
# WS /v1/realtime - OpenAI Realtime API (JSON events, base64 PCM16 audio)
Bandiere di linea di comando
| Bandiera | Predefinito | Designazione |
|---|---|---|
--host | 127.0.0.1 | Indirizzo binario. Cambiare a 0.0.0.0 per esporre su LAN. |
--port | 8080 | Porta TCP. |
--preload | Fuori! | Caricare tutti i modelli con ansia all'avvio. Stivale più lento (~30–60 s) ma zero latenza di prima richiesta. |
I modelli vengono scaricati in primo utilizzo e memorizzati nella cache ~/Library/Caches/qwen3-speech/. La prima richiesta di un dato modello paga il costo di download + carico (30 s – 2 min a seconda della dimensione del modello); le richieste successive sono calde.
REST endpoint
POST /transcribe — Discorso-testo
Accetta un corpo grezzo WAV o una busta JSON con base64-encoded audio.
# WAV body (preferred — lower overhead)
curl -X POST http://127.0.0.1:8080/transcribe \
-H "Content-Type: audio/wav" \
--data-binary @recording.wav
# JSON with base64
curl -X POST http://127.0.0.1:8080/transcribe \
-H "Content-Type: application/json" \
-d '{"audio_base64":"'"$(base64 -i recording.wav)"'","language":"en"}'
Risposta: {"text": "…", "language": "en", "confidence": 0.93}
POST /speak — Testo a schermo
curl -X POST http://127.0.0.1:8080/speak \
-H "Content-Type: application/json" \
-d '{"text":"Hello, world!","engine":"kokoro","language":"en"}' \
--output hello.wav
L'organismo di risposta è un WAV blob. Supportato engine valori: qwen3 (default), cosyvoice, kokoro.
POST /respond
curl -X POST "http://127.0.0.1:8080/respond?voice=en_female_calm&max_steps=256" \
-H "Content-Type: audio/wav" \
--data-binary @question.wav \
--output answer.wav
Esegue PersonaPlex 7B — discorso in, discorso fuori. Transcript viene restituito come un X-Response-Text Intestazione. Guida PersonaPlex per i nomi preset vocali.
POST /enhance — Miglioramento del discorso
curl -X POST http://127.0.0.1:8080/enhance \
-H "Content-Type: audio/wav" \
--data-binary @noisy.wav \
--output clean.wav
DeepFilterNet3 a 48 kHz. L'input viene riprodotto se necessario.
GET /health — Sonda della vita
curl http://127.0.0.1:8080/health
# {"status":"ok"}
I test HTTP e in tempo reale WebSocket aspettano che /health restituisca HTTP 200 con {"status":"ok"}, con una scadenza di 10 secondi di avvio.
WebSocket: /v1/realtime
Goccia compatibile con il OpenAI in tempo reale API — lo stesso schema di eventi JSON (session.update, input_audio_buffer.append, response.create, response.audio.delta, ...) con base64-encoded PCM16 audio a 24 kHz. I clienti scritti contro OpenAI in tempo reale SDK lavoro contro speech-server senza modifiche di codice (solo cambiare l'URL WebSocket).
Turni automatici con server VAD
Manuale input_audio_buffer.commit rimane l'impostazione predefinita. session.turn_detection a server_vad per eseguire Silero VAD sul server e trascrivere automaticamente ogni turno di discorso completato. L'audio prefisso viene mantenuto, il buffering inattivo rimane limitato, e una svolta attiva continua è chiuso in forza al massimo configurato.
ws.send(JSON.stringify({
type: "session.update",
session: {
turn_detection: {
type: "server_vad",
threshold: 0.5,
prefix_padding_ms: 300,
silence_duration_ms: 500,
max_turn_duration_ms: 120000
}
}
}));
// Event order for each detected turn:
// input_audio_buffer.speech_started
// input_audio_buffer.speech_stopped
// input_audio_buffer.committed
// conversation.item.input_audio_transcription.completed
// Set turn_detection to null (or { type: "none" }) for manual commits.
Esempio di JavaScript
const ws = new WebSocket("ws://127.0.0.1:8080/v1/realtime");
ws.onopen = () => {
ws.send(JSON.stringify({
type: "session.update",
session: { modalities: ["audio", "text"] }
}));
// Stream PCM16 mono 24kHz audio from the mic:
const audioBase64 = await capturePCM16Chunk();
ws.send(JSON.stringify({
type: "input_audio_buffer.append",
audio: audioBase64
}));
ws.send(JSON.stringify({ type: "response.create" }));
};
ws.onmessage = ev => {
const msg = JSON.parse(ev.data);
if (msg.type === "response.audio.delta") {
playPCM16Base64(msg.delta);
}
};
Esempio di Python
import asyncio, base64, json, wave, websockets
async def main():
async with websockets.connect("ws://127.0.0.1:8080/v1/realtime") as ws:
await ws.send(json.dumps({"type": "session.update",
"session": {"modalities": ["audio", "text"]}}))
with wave.open("question.wav", "rb") as wav:
pcm16 = wav.readframes(wav.getnframes())
await ws.send(json.dumps({"type": "input_audio_buffer.append",
"audio": base64.b64encode(pcm16).decode()}))
await ws.send(json.dumps({"type": "response.create"}))
async for raw in ws:
msg = json.loads(raw)
if msg["type"] == "response.audio.delta":
open("answer.pcm", "ab").write(base64.b64decode(msg["delta"]))
elif msg["type"] == "response.done":
break
asyncio.run(main())
Note di distribuzione
- No auth per impostazione predefinita.
speech-serverlega127.0.0.1e si fida di ogni chiamante. Mettilo dietro un proxy inverso (Caddy, nginx, codascala) se lo espongono al di là di localhost. - I modelli sono carichi pigri. Prima richiesta
/transcribeattiva il download + caricamento ASR (~700 MB, 30–60 s).--preloadper zero avviamento a freddo ma più lento avvio. - Ancora nessuna trascrizione in streaming.
POST /transcribesi aspetta il pieno WAV davanti. Usa/v1/realtimeper lo streaming. - Systemd / launchd. Nessuna unità di servizio navi oggi. Un semplice
nohup speech-server &o il vostro solito processo di supervisione lavori.
Fonte
- Fonti/AudioServer — router HTTP basato su Hummingbird, registro del modello pigro,
/v1/realtimeWebSocket handler. - Sources/AudioServerCLI —
@mainpunto di entrata, argomento parser. - A monte: OpenAI Realtime API reference
Inferenza locale
Per 12B, i marcatori di chiusura adiacenti ripetuti immediatamente dopo una chiamata completa e validata vengono consumati con tale chiamata. Non creano un'altra chiamata o modificano i suoi argomenti. I marcatori standalone e gli argomenti malformati rimangono errori; E4B mantiene una gestione rigorosa.
Per mantenere le richieste di chat native fallite, impostare failureCaptureDirectory La cattura è disattivata per impostazione predefinita. La directory deve negare il gruppo e l'altro accesso; nuove directory utilizzano 0700 e l'uso di file di guasto 0600. I file includono l'esatto corpo di richiesta, ID di richiesta ed errore. Le generazioni complete di 12B mantengono anche il prompt reso, ID di token, output grezzo e ragione di arresto prima di parsing. I guasti E4B non hanno record di generazione grezza. Questi file locali contengono contenuto di conversazione e strumento; errori HTTP e richieste di successo non includono.
Agenti opzionaliLa direzione carica un Gemma 4 12B Unificata INT4 modello di testo insieme E4B. Modello d'uso: "gemma4-12b-it" per chattare con strumenti nativi, storia dei risultati e cancellazione. E4B rimane disponibile per altre richieste. Gemma4UnifiedChat utilizza il pinned MLX L'applicazione del testo e rilascia la cache KV di ogni richiesta. La parsing di risposta rimuove i delimitatori del canale dal testo restituito. I terminatori di chiamata degli strumenti orfani vengono rifiutati come risposte malformate. Le richieste non-streaming non registrano il loro ID di richiesta, il funzionamento e l'errore sottostante nel processo del server. Gemma 4 12B, contenuto di messaggio-utensile mantiene JSON come testo, corrispondente al modello di chat di checkpoint.
Le costruzioni di origine accettano --inference-manifest modelli.json per caricare in scena Gemma 4 E4B INT4, VoxCPM2♪ Whisper Large-v3e Silero VAD. Questo servizio inizia offline e rifiuta identità di modello incompatibili.
Il servizio nativo serializza chat, sintesi e trascrizione; leggero VAD ha uno stato di flusso separato. Gemma prefill valuta ogni lotto 256-token prima di iniziare il successivo, chiede lo stato KV viene rilasciato, e la cache MLX è bloccata a 128 MiB con un 20 allocator GiB.
Discorso con flusso: restituisce veri eventi audio NDJSON a 48 kHz, seguito da fatto o errore. Annullare da requestID a /v1/inference/cancel. GET /v1/v1/inferenza/capabilità e /v1/Inference/provenance report identities, parametro dtypes, asset pins, e contatori di memoria.
speech-server --inference-manifest models.json --port 8080{
"runtimeRevision": "your-source-revision",
"gemmaDirectory": "/models/gemma4-e4b-it-int4",
"voxDirectory": "/models/voxcpm2-unquantized",
"whisperDirectory": "/models/whisper-large-v3-coreml",
"vadDirectory": "/models/silero-vad-coreml"
}Proprietà del modello opzionale
SpeechRuntime fornisce il caricamento condiviso, l'ammissione FIFO cancellabile e l'esecuzione serializzata. Qwen3ASRRuntime seleziona MLX o CoreML completo attraverso descrittori espliciti; le API native e le bandiere CLI rimangono disponibili.
Il server audio contiene un permesso di esecuzione per ogni richiesta completa, inclusa l'uscita in streaming, con fino a 32 richieste di attesa. ASR guasti producono errori HTTP o WebSocket
Voxtral TTS
Voxtral 4B TTS 2603 funziona localmente su Apple Silicon attraverso il modulo VoxtraLTTS: 20 voci predefinite, nove lingue e 24 kHz monolingua.
curl http://localhost:8080/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{"model":"voxtral-tts","input":"Hello, welcome to Voxtral.","voice":"casual_male","response_format":"wav"}' \
-o hello.wavSolo le voci preimpostate; la clonazione audio di riferimento non è disponibile. I flussi di output audio attraverso il SDK, CLI, WebSocket e HTTP PCM. Fornire il testo di input completo sul fronte. I pesi del modello utilizzano CC BY-NC 4.0.
precarica del prompt Gemma 4 12B
Il servizio di inferenza nativo carica il modello messo in scena Gemma 4 12B Unified INT4 modello utilizzando agentDirectory. Selezionare per chattare con il modello ID gemma4-12b-it.
Gemma 4 12B prefill riempie lo stato KV in lotti fino a 256 token e calcola i primi logits di campionamento dal token finale del prompt. Le proiezioni di vocabolario intermedio vengono ignorate. La cache KV della richiesta viene rilasciata dopo il completamento o la cancellazione.
Un prefisso di sistema/tool invariato può rimanere memorizzato nella cache completa dei blocchi 256-token. Ogni richiesta estende una copia di lavoro separata. Messaggi utente, cronologia delle conversazioni e risultati degli strumenti non entrano mai nell'istantanea conservata. SPEECH_SWIFT_GEMMA4_PREFIX_CACHE_MB limitare MiB mantenuto, o 0 per disabilitare il riutilizzo. clearPrefixCache() rilascia l'istantanea. relazioni HTTP riusati gettoni in cachedPrefixTokens; documenti di prova gemma4_12b_prefix_cache_budget_bytes. Le richieste fredde elaborano ancora il prompt completo.
La cronologia degli strumenti di Native Gemma mantiene le risposte degli strumenti e una continuazione dell'assistente all'interno dello stesso modello. Un seguente messaggio utente chiude quel modello prima di aprire il turno successivo dell'utente. toolCallID.