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).

Wykorzystanie Swift i CLI

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.pcm

speech-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

FlagaDomyślneOpis
--host127.0.0.1Zmień adres 0.0.0.0 do ujawnienia w sieci LAN.
--port8080Port TCP.
--preloadwyłączonyWczytaj 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

Źródło

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"
}

Szczegóły API

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.wav

Wyłą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.

Przeczytaj przewodnik Voxtral TTS

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.