Tłumaczenie automatyczne · Oryginał angielski
Speech Server
Native Apple runtime · PockettTS
PocketTTS dodaje rodzimą syntezę mowy CoreML do speech-swift. Wszystkie etapy domyślne do wykonania tylko CPU. angielskie strumienie Alba preset 1,920- próbki w 24 kHz (80 ms per frame).
Użyj pocket- tts z głosem alba. Streaming HTTP wymaga PCM; WebSocket emituje deltas audio. Istniejące modele domyślne są niezmienione.
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 jest lokalnym serwerem HTTP + WebSocket, który eksponuje każdy model Soniqo poprzez prosty REST API oraz OpenAI Realtime API- kompatybilny WebSocket at /v1/realtime. Statek w tej samej butelce Homebrew jak speech CLI - brew install speech Krople na twoim PATH.
Zainstaluj i uruchom
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)
Flagi linii komend
| Flaga | Domyślne | Opis |
|---|---|---|
--host | 127.0.0.1 | Zmień adres 0.0.0.0 do ujawnienia w sieci LAN. |
--port | 8080 | Port TCP. |
--preload | wyłączony | Wczytaj wszystkie modele z niecierpliwością przy starcie. Wolniejszy boot (~ 30- 60 s), ale zero pierwszy-żądanie latencji. |
Modele są pobierane przy pierwszym użyciu i buforowane w ~/Library/Caches/qwen3-speech/. Pierwsze żądanie dla danego modelu płaci pobierz + koszt obciążenia (30 s - 2 min w zależności od wielkości modelu); kolejne wnioski są ciepłe.
Punkty końcowe REST
POST /transcribe - Przemówienie do tekstu
Akceptuje albo surowe ciało WAV lub kopertę JSON z audio base64-encoded.
# 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"}'
Odpowiedź: {"text": "…", "language": "en", "confidence": 0.93}.
POST /speak - Text- to- przemówienie
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
Ciało odpowiedzi jest blob WAV. Obsługiwane engine wartości: qwen3 (domyślnie), cosyvoice, kokoro.
POST /respond - Przemówienie-to- przemówienie
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
Runs PersonaPlex 7B - przemowa, przemowa. Transcript jest zwracany jako X-Response-Text nagłówek. Zobacz Przewodnik PersonaPlex dla nazw głosowych.
POST /enhance - Wzmocnienie mowy
curl -X POST http://127.0.0.1:8080/enhance \
-H "Content-Type: audio/wav" \
--data-binary @noisy.wav \
--output clean.wav
DeepFilterNet3 przy 48 kHz. Wejście jest resegregowane w razie potrzeby.
GET /health - Sonda liwitacyjna
curl http://127.0.0.1:8080/health
# {"status":"ok"}
Testy HTTP i realtime WebSocket czekać /health aby zwrócić HTTP 200 z {"status":"ok"}, z 10-second startup deadline. Błędy startowe nie powiodły się podczas testu, a teardown czeka na zamknięcie serwera przed ponownym użyciem portu.
WebSocket: /v1/realtime
Drop- in kompatybilny z OpenAI Realtime API - ten sam schemat zdarzeń JSON (session.update, input_audio_buffer.append, response.create, response.audio.delta,...) z audio base64-encoded PCM16 przy 24 kHz. Klienci napisani przeciwko OpenAI Realtime SDK działają przeciwko speech-server bez zmian kodu (po prostu przełącz URL WebSocket).
Automatyczne zakręty z serwerem VAD
Podręcznik input_audio_buffer.commit pozostaje domyślny. Ustaw session.turn_detection do server_vad do uruchomienia Silero VAD na serwerze i automatycznej transkrypcji każdej zakończonej mowy. Prefiks audio jest zachowany, bezczynności buforowanie pozostaje ograniczone, a ciągły aktywny obrót jest zablokowany przy skonfigurowanym maksimum.
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.
Przykład 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);
}
};
Przykład 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())
Notatki wdrożeniowe
- Domyślnie nie ma auth.
speech-serverwiąże się127.0.0.1i ufa każdemu dzwoniącemu. Umieść go za odwrotnym proxy (Caddy, nginx, tailscale), jeśli odsłonić go poza localhost. - Modele są ładowane. Pierwszy wniosek
/transcribeuruchamia pobieranie ASR + obciążenie (~ 700 MB, 30- 60 s).--preloaddla zera Cold-start, ale wolniejszy boot. - Nie ma jeszcze transkrypcji strumieniowej.
POST /transcribeoczekuje pełnego WAV z przodu./v1/realtimeza strumieniowanie. - Systemd / launchd. Nie ma dziś jednostek obsługi statków.
nohup speech-server &lub zwykły nadzorca procesu działa.
Źródło
- Źródła / Serwer AudioServer - Ruter HTTP oparty na Hummingbird, rejestr leniwych modeli,
/v1/realtimeWebSocket. - Źródła / AudioServerCLI —
@mainPunkt wejścia, argument parser. - Upstream: OpenAI Realtime API reference.
Lokalne zgłoszenie rodzime
Dla 12B, powtarzające się przyległe znaczniki zamykające wywołanie narzędzi natychmiast po zakończeniu, zwalidowanego połączenia są konsumowane z tym wywołaniem. Nie tworzą one innego wywołania ani nie zmieniają jego argumentów. Markery standalone i zniekształcone argumenty pozostają błędami; E4B zachowuje ścisłą obsługę.
Aby zachować nieudany natywny chat, ustaw failureCaptureDirectory w manifeście referencyjnym. Domyślnie jest wyłączona przechwytywanie. Katalog musi odmówić dostępu grupowego i innego; nowe katalogi używają 0700 i plików błędów używać 0600. Pliki zawierają dokładny organ żądania, ID wniosku i błąd. Ukończone generacje 12B zachowują również renderowany sygnał, token ID, wyjściowe i stop przed parsowaniem. Niepowodzenia E4B nie mają surowego zapisu generacji. Te pliki lokalne zawierają zawartość rozmowy i narzędzia; błędy HTTP i udane żądania nie zawierają ich.
Fakultatywny agentDirectory ładuje ustawione Gemma 4 12B Unifikacja INT4 model tekstowy obok E4B. Użyj modelu: "gemma4-12b-it"dla czatu z natywnymi narzędziami, historii wyników i anulowania. E4B pozostaje dostępna dla innych wniosków. Gemma4UnifiedChat wykorzystuje przypinane MLX implementacja tekstu i uwalnia każdy podręcznik KV. Parsing responsing usuwa separatory kanałów z zwróconego tekstu. Osierocone terminatory połączeń z narzędziami są odrzucane jako nieprawidłowo utworzone odpowiedzi. Nie powiodły się niestrumieniowe żądania logowania ich ID żądanie, działanie i błąd bazowy w procesie serwera. Gemma 4 12B, zawartość wiadomości narzędziowej zachowuje JSON jako tekst, dopasowując szablon czatu punktu kontrolnego.
Budowy źródeł akceptują --inference-manifest models.json do wczytania ustawione Gemma 4 E4B INT4, VoxCPM2, pełny Whisper Large-v3oraz Silero VADUsługa ta rozpoczyna się w trybie offline i odrzuca niekompatybilne tożsamości modelu.
Native service serializuje czat, syntezę i transkrypcję; lekki VAD ma oddzielny stan strumienia. Gemma prefill ocenia każdy 256-token wsadowe przed rozpoczęciem następnego, żądanie stanu KV jest zwolniony, a MLX cache jest zablokowany 128 MiB z 20 Limit przydziału GiB.
Przemowa ze strumieniem: True zwraca zdarzenia audio NDJSON w 48 kHz, po którym następuje wykonanie lub błąd. Anuluj requestID na /v1/ wniosek / anulować. GET /v1/ wnioskowanie / możliwości i /v1/ inference / prience report model identifies, parametr dtypes, asset pin, and memory counters.
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"
}Opcjonalny model własności
SpeechRuntime zapewnia wspólne ładowanie, anulowalne wstęp FIFO i seryjne wykonanie. Qwen3ASRRuntime wybiera MLX lub pełne CoreML za pomocą wyraźnych deskryptorów; krajowe API i flagi CLI pozostają dostępne.
Serwer audio posiada jedno zezwolenie na wykonanie za pomocą każdego pełnego żądania, w tym wyjścia strumieniowego, z żądaniami do 32 oczekujących. Niepowodzenia ASR powodują błędy HTTP lub zdarzenia błędów WebSocket; prawidłowe puste transkrypcje pozostają skuteczne. Odtwarzanie zachowuje próbki przepełnienia.
Voxtral TTS
Voxtral 4B TTS 2603 działa lokalnie Apple Silicon przez moduł Voxtral TTS: 20 preset voices, nine languages, and 24 KHz mono mowy.
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.wavWyłącznie głosy preset; klonowanie referencji audio jest niedostępne. Strumienie wyjściowe audio przez SDK, CLI, WebSocket i HTTP PCM. Dostarczają kompletny tekst wejściowy z przodu. Wagi modelu używają CC BY- NC 4.0.
Gemma 4 12B prefill
Usługa tubylczego wniosku ładuje model Gemma 4 12B Unified INT4 za pomocą agentDirectoryWybierz go na czat z identyfikatorem modelu gemma4-12b-it.
Gemma 4 12B wypełnia stan KV w partiach do żetonów 256 i oblicza pierwsze logi pobierania próbek z końcowego żetonu. Projekcje słownictwa pośredniego są pomijane. KV cache żądanie jest zwolniony po zakończeniu lub anulowaniu.
Jeden niezmieniony przedrostek systemu / narzędzia może pozostać w kompletnych blokach symbolu 256-. Każde żądanie rozciąga osobną kopię roboczą. Komunikaty użytkownika, historia rozmów i wyniki narzędzia nigdy nie wchodzą w zatrzymany migawkę. SPEECH_SWIFT_GEMMA4_PREFIX_CACHE_MB ograniczenia zatrzymanego MiB, lub 0 wyłączyć ponowne użycie. clearPrefixCache() uwalnia migawkę. Raporty HTTP ponownie używane żetony w cachedPrefixTokens; rejestry pochodzenia gemma4_12b_prefix_cache_budget_bytesZimne prośby nadal przetwarzają cały czas.
Native Gemma historia narzędzia utrzymuje odpowiedzi narzędzia i asystent kontynuacyjny wewnątrz tego samego modelu skrętu. Następująca wiadomość użytkownika zamyka ten model skręcić przed otwarciem następnego skrętu użytkownika. Wyślij każdy wynik z jego oryginalnego toolCallID.