vocero-s2s/docs/PROJECT_OVERVIEW.es.md
valenti b5f82fb48c
Some checks are pending
CI / ruff (push) Waiting to run
CI / mypy (push) Waiting to run
CI / pytest (push) Waiting to run
CI / package (push) Waiting to run
CI / Install smoke (${{ matrix.label }}) (linux, ubuntu-latest) (push) Blocked by required conditions
CI / Install smoke (${{ matrix.label }}) (macos-arm64, macos-14) (push) Blocked by required conditions
first git
2026-08-26 11:30:14 +00:00

21 KiB
Raw Blame History

speech-to-speech — Descripción del Proyecto

Paquete: speech-to-speech Versión: 0.2.11 Autor: Hugging Face Licencia: Apache-2.0 Python: 3.10 3.12 Lema: Pipeline Speech-to-Speech de baja latencia end-to-end para construir agentes de voz en tiempo real.


1. ¿Qué es speech-to-speech?

speech-to-speech es una pipeline de audio modular y de baja latencia que convierte la entrada hablada del usuario en una respuesta hablada encadenando cuatro etapas de IA: VAD → STT → LLM → TTS.

De fábrica expone un endpoint WebSocket compatible con el protocolo OpenAI Realtime (ws://host:port/v1/realtime), por lo que cualquier navegador o SDK que sepa hablar el protocolo Realtime de OpenAI puede conectarse y empezar a tener conversaciones naturales en cuestión de segundos — sin escribir una sola línea de código en el servidor.

La pipeline está diseñada para ser agnóstica del backend en cada etapa: puedes intercambiar distintos modelos de STT / LLM / TTS según el hardware disponible (NVIDIA CUDA, Apple Silicon MLX o solo CPU), los idiomas de tus usuarios y el compromiso entre latencia y calidad que necesites.

Un subsistema opcional integrado de RAG (Retrieval Augmented Generation) del lado servidor permite que las respuestas del LLM se basen en tu propia base de conocimientos privada (documentos Markdown / TXT / JSONL), mientras permanece 100 % transparente para el cliente. Las actualizaciones dinámicas de la base de conocimientos se exponen en el mismo servidor HTTP mediante una API REST de 11 endpoints.


2. Características principales

Característica Descripción
Protocolo de voz en tiempo real Soporte nativo del WebSocket OpenAI v1/realtime: session.update, response.create, llamadas a funciones, deltas de audio, interrupciones. Compatible directamente con el SDK Realtime oficial y con los "playgrounds" web.
4 modos de ejecución local (micrófono → altavoces), socket (IPC TCP), websocket (WS audio crudo), realtime (protocolo OpenAI — predeterminado).
6 backends de STT intercambiables Whisper / Whisper-MLX / MLX-Audio-Whisper / Faster-Whisper / Parakeet TDT (predeterminado) / Paraformer.
4 backends de LLM intercambiables transformers (local) · mlx-lm (Apple Silicon) · responses-api (endpoint OpenAI con tool-calling) · chat-completions (cualquier servidor /v1/chat/completions compatible con OpenAI).
5 backends de TTS intercambiables ChatTTS · Facebook MMS · Pocket (muy pequeño, CPU) · Kokoro · Qwen3-TTS (predeterminado, voz personalizada de 1.7B).
Multiplataforma NVIDIA CUDA (Linux), Apple Silicon MLX + MPS (macOS), fallback CPU en cada etapa.
Pool de N pipelines aisladas --num_pipelines N ejecuta N sesiones independientes en paralelo (cada una con sus propios VAD / STT / LLM / TTS y estado de conversación). Ideal para despliegues pequeños con múltiples conexiones simultáneas.
VAD e interrupción integrados Detección de actividad vocal con umbral configurable; finalización del STT por silencio; el LLM se puede interrumpir mientras habla y se cancela de forma limpia mediante un CancelScope.
Detección automática de idioma A través de lingua-language-detector para el idioma de la respuesta del asistente.
Transcripción parcial en vivo Parakeet-TDT emite transcripciones parciales cada 500 ms para que los clientes muestren el texto "el usuario está hablando…".
Soporte completo de tool-calling Con el backend responses-api: entrega en streaming de argumentos de función, tool calls paralelos, voces personalizadas sobre TTS — todo conforme a la superficie de la Responses API de OpenAI.
Inyección RAG opcional en el servidor Hook transparente de recuperación en cada turno del LLM. Embeddings con Sentence-Transformers, persistencia del índice NPZ, top-k / umbral coseno configurables, inyección como mensaje del sistema o del usuario. Multilingüe por defecto (español / italiano / inglés listos para usar).
API REST para base de conocimientos dinámica 11 endpoints para listar, buscar, hacer upsert, actualizar, eliminar y recargar contenidos de la KB en tiempo real — con persistencia entre reinicios mediante kb/_dynamic.jsonl.
Configuración estructurada CLI / JSON Cada parámetro es un argumento @dataclass de HfArgumentParser con valores predeterminados razonables, o un fichero JSON de configuración completo que puedes pasar como único argumento.
Empaquetado y flujo de publicación PyPI Configurado uv build + twine check + GitHub Actions (la etiqueta vX.Y.Z activa la subida).

3. Arquitectura a vista de pájaro

Un turno único de conversación en modo realtime se ve así:

                         ┌─────────────────────────────────────────────────────────┐
                         │                    uvicorn + FastAPI                    │
                         │  ┌───────────────────────────────────────────────────┐  │
 Mic / Navegador   ──► WS │  │   RealtimeService (sesión + rutas + eventos)     │  │
 (audio entradas +  │   │  └───────────────────────────────────────────────────┘  │
  eventos)          │   │                                                           │
                     │   │  Pool de pipelines  ─► PipelineUnit #1  ─► PipelineUnit #N│
                     │   └─────────────────────────────────────────────────────────┘
                     │                      │
                     ▼                      ▼
              ┌──────────────────────────────────────────────┐
              │   Una unidad de pipeline (una por usuario)    │
              │                                                │
              │   1. VAD            ──►  inicio / fin voz     │
              │   2. STT (5 sabores) ──►  último texto usuario│
              │        │                                     │
              │        ▼                                     │
              │   ┌────────────────────────┐                  │
              │   │  HOOK BÚSQUEDA RAG       │ ◄─── kb/_index.npz
              │   │  (inyecta solo si ≥ N)   │      + md/txt/jsonl
              │   └────────────────────────┘      + _dynamic.jsonl
              │        │                                     │
              │        ▼                                     │
              │   3. LLM (4 sabores) ──►  texto + tool calls│
              │        │                                     │
              │        ▼                                     │
              │   4. TTS (5 sabores) ──►  PCM audio streaming│
              └──────────────────────────────────────────────┘
                                                      │
                                                      ▼
                                              ◄── WS audio / delta events

Invariantes clave:

  • Cada conexión = una PipelineUnit (asignada de forma atómica; colas y estado aislados; si las N unidades están ocupadas, la conexión N+1 se rechaza).
  • La etapa LLM nunca ve audio crudo. La pipeline solo envía buffers de texto al manejador del LLM, por lo que cambiar de proveedor o de modelo sigue siendo transparente.
  • La etapa RAG es un efecto lateral puro sobre el búfer de texto: lee el texto más reciente del usuario, ejecuta un embedding + top-k por coseno + filtro por umbral, y antepone el conocimiento relevante como un mensaje adicional del sistema (o del usuario) — con una línea de registro clara para ver siempre exactamente qué se inyectó.
  • TTS en streaming (todos los backends modernos): los deltas de audio se emiten a medida que se sintetizan, por lo que el primer byte de una respuesta llega al altavoz mucho antes de que el LLM termine de generar el texto.

4. Modos de ejecución (--mode)

Configurables mediante ModuleArguments.mode (predeterminado: realtime).

Modo Entrada Salida Ideal para
local Micrófono local vía sounddevice / miniaudio Altavoces locales Demos de escritorio, prototipado rápido en portátil.
socket Chunks PCM crudos en socket TCP PCM crudos por socket TCP Sistemas legacy / embebidos, transporte personalizado.
websocket PCM crudo sobre endpoint WS PCM crudo sobre WS Frontend a medida mínimo que solo envía audio.
realtime WS con protocolo OpenAI Realtime (/v1/realtime) Mismo protocolo + API REST RAG en el mismo servidor HTTP Producción e integración con SDK. Todos los clientes que soportan la Realtime API (OpenAI SDK, playgrounds web, wrappers Swift / Kotlin / JS) se conectan aquí.

5. Backends soportados

5.1 STT — Speech to Text

Nombre --stt Familia de modelos Modelo predeterminado Ventajas hardware
whisper HuggingFace Transformers Whisper distil-whisper/distil-large-v3 CUDA / CPU
whisper-mlx MLX Whisper mlx-community/whisper-large-v3-turbo Apple Silicon (macOS)
mlx-audio-whisper Apple mlx-audio mlx-community/whisper-large-v3-turbo Apple Silicon
faster-whisper Whisper cuantizada CTranslate2 tiny.en CPU con latencia muy baja
parakeet-tdt (predeterminado) HuggingFace Parakeet TDT parakeet-tdt-1.1b Streaming amigable + transcripción parcial en vivo sobre CUDA
paraformer Alibaba Paraformer paraformer-zh Despliegues solo chino

5.2 LLM — Modelo de lenguaje

--llm_backend Descripción Modelo predeterminado
transformers Generación local HF Transformers en proceso Qwen/Qwen3-4B-Instruct-2507
mlx-lm Inferencia local cuantizada 4-bit / 8-bit en Apple Silicon mlx-community/...
responses-api (predeterminado) Endpoint remoto compatible con OpenAI Responses API (superficie completa de tool-calling + argumentos de función en streaming) gpt-5.4-mini
chat-completions Cualquier remoto /v1/chat/completions compatible con OpenAI — enchufa vLLM, TGI, Ollama, servidor llama.cpp, SGLang, TabbyAPI, etc. (auto-detecta vía base_url + /v1/models)

5.3 TTS — Text to Speech

Nombre --tts Backend Modelo predeterminado Puntos fuertes
chatTTS ChatTTS (grupo opcional chattts) Muy conversacional, inglés + chino, prosodia muy expresiva
facebookMMS Facebook MMS (grupo opcional facebook-mms) facebook/mms-tts-eng Ultraligero, cubre más de 1.100 idiomas
pocket PocketTTS (pequeño CPU) Sin dependencias en dispositivo, ideal para embebidos / poca RAM
kokoro Kokoro TTS (grupo opcional kokoro) Estado del arte de TTS neural en inglés y japonés
qwen3 (predeterminado) Qwen3-TTS vía GGML faster-qwen3-tts Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice Multilingüe, soporta clonación por voz de referencia, tasa de token 12 Hz → latencia ultrabaja.

Todos los backends de TTS se acceden a través de una interfaz de streaming común, por lo que el resto de la pipeline es agnóstica al backend.


6. RAG del lado servidor (complemento opcional)

El subsistema RAG está completamente documentado en:

En resumen:

$ ./start_pipeline_rag.sh                         # arranca pipeline + API REST RAG
RAG: Inizializzazione modello embedding=paraphrase-multilingual-MiniLM-L12-v2 device=cuda su kb_path=/home/.../kb
RAG: Índice cargado desde disco: 9 chunk (shape=(9, 384)).
RAG: Activo. kb=... top_k=3 umbral=0.250 inject_as=system idioma=es
RAG HTTP API montata su prefix='/v1/rag'

Puntos clave:

  • Cero cambios en el cliente — la inyección ocurre en el servidor, dentro del búfer de chat del LLM, antes de cada turno.
  • Modelos de embedding intercambiables con un modelo multilingüe predeterminado (paraphrase-multilingual-MiniLM-L12-v2, 384 dims) y alternativas recomendadas para español-only y para bases de conocimientos muy grandes.
  • Persistencia del índice NPZ — se reconstruye solo si el contenido cambió. Soporte de --rag_force_rebuild.
  • Tres formatos de fuente: Markdown / TXT con chunking recursivo automático (tamaño + solapamiento configurables), o JSONL para chunks artesanales.
  • Comportamiento de recuperación configurable: --rag_top_k, --rag_threshold, --rag_inject_as (system/user), --rag_language (es/it/en).
  • API REST dinámica thread-safe (11 endpoints): /status, /sources, /chunks, /chunks/list, /chunks/update, /upsert/document, /search, /add/document, /add/chunks, /remove, /reload.
  • Persistencia entre reinicios: las llamadas a la API con persist=true se adjuntan atómicamente a kb/_dynamic.jsonl y se re-indexan automáticamente en el siguiente arranque.
  • Patrón CRUD idempotente: /upsert/document gestiona crear / reemplazar / eliminar (text="") de forma atómica para que los clientes solo necesiten una URL.
  • Falla con elegancia: cualquier excepción de recuperación se registra pero nunca rompe el turno de conversación.

7. Arranque rápido

7.1 Instalación (instalación editable estilo PyPI)

git clone https://github.com/huggingface/speech-to-speech.git
cd speech-to-speech

# Paquete base (incluye STT parakeet-tdt + TTS qwen3 por defecto)
uv pip install -e .

# Opcional: añade el subsistema RAG para recuperación en la KB
uv pip install -e ".[rag]"

# Opcional: selecciona los extras de TTS / STT que quieras
uv pip install -e ".[rag,chattts,kokoro,faster-whisper]"

7.2 Arrancar en modo Realtime (predeterminado) con un LLM externo

Esta es la configuración más común: el LLM corre en un servidor remoto compatible con OpenAI (p.ej. http://127.0.0.1:8001/v1 con vLLM o TGI), STT + TTS corren localmente sobre CUDA / MLX.

Crea un pequeño wrapper de shell (véase start_pipeline.sh para la plantilla completa):

#!/bin/bash
set -euo pipefail

LLM_BASE_URL="${LLM_BASE_URL:-http://127.0.0.1:8001/v1}"
LLM_MODEL="${LLM_MODEL:-Qwen/Qwen2.5-14B-Instruct-GPTQ-Int4}"
LLM_API_KEY="${LLM_API_KEY:-placeholder}"
TTS_MODEL="${TTS_MODEL:-Qwen/Qwen3-TTS-12Hz-1.7B-Base}"
STT_MODEL="${STT_MODEL:-distil-whisper/distil-large-v3}"

cd /home/azurian/speech-to-speech

uv run speech-to-speech \
  --mode realtime \
  --ws_host 0.0.0.0 \
  --ws_port 12345 \
  --num_pipelines 2 \
  \
  --llm_backend chat-completions \
  --chat_completions_handler_base_url "$LLM_BASE_URL" \
  --chat_completions_handler_model_name "$LLM_MODEL" \
  --chat_completions_handler_api_key "$LLM_API_KEY" \
  \
  --stt whisper \
  --whisper_stt_model_name "$STT_MODEL" \
  \
  --tts qwen3 \
  --qwen3_tts_model_name "$TTS_MODEL"

Ejecútalo y después conecta cualquier cliente Realtime de OpenAI a:

ws://<host>:12345/v1/realtime

7.3 Misma pipeline + RAG activado

Añade estos flags (véase §6 para la referencia completa y el script auxiliar start_pipeline_rag.sh):

  --rag_enabled \
  --rag_kb_path ./kb \
  --rag_top_k 3 \
  --rag_threshold 0.25 \
  --rag_language es \
  --rag_inject_as system

La API REST de RAG aparece inmediatamente en el mismo servidor HTTP:

curl http://127.0.0.1:12345/v1/rag/status | jq

8. Estructura del proyecto (puntos destacados)

speech-to-speech/
├── pyproject.toml                                     ← metadatos del paquete + grupos de deps opcionales
├── LICENSE                                            ← Apache-2.0
├── start_pipeline.sh                                  ← script de arranque de referencia
├── start_pipeline_rag.sh                              ← script de arranque con RAG activado
│
├── src/speech_to_speech/
│   ├── s2s_pipeline.py                                ← punto de entrada (main), constructor de pipeline, pool realtime
│   ├── baseHandler.py
│   ├── chat.py
│   ├── pipeline/                                      ← tipos de cola, CancelScope, tipos de handler
│   │
│   ├── arguments_classes/                             ← args HfArgumentParser @dataclass (uno por handler)
│   │   ├── module_arguments.py                        ← mode / stt / tts / llm_backend / num_pipelines
│   │   ├── rag_arguments.py                           ← 12 flags específicos de RAG
│   │   └── ... (Whisper, Qwen3, ChatTTS, VAD, …)
│   │
│   ├── STT/                                           ← seis manejadores STT
│   ├── TTS/                                           ← cinco manejadores TTS
│   ├── LLM/
│   │   ├── base_openai_compatible_language_model.py   ← Hook de inyección RAG + tool-calling completo
│   │   ├── chat_completions_language_model.py
│   │   ├── responses_api_language_model.py
│   │   └── language_model.py                          ← manejadores locales transformers / mlx-lm
│   │
│   ├── RAG/
│   │   ├── retriever.py                               ← singleton, embeddings, NPZ, search, CRUD completo
│   │   └── router.py                                  ← 11 endpoints FastAPI /v1/rag
│   │
│   └── api/openai_realtime/
│       ├── websocket_router.py                        ← FastAPI + Realtime + montaje condicional RAG
│       ├── service.py                                 ← Bucle de eventos Realtime + rutas de sesión / handlers
│       └── ...
│
├── kb/
│   ├── 01_faq_producto.md                             ← ejemplo FAQ en español (incluido en scaffold)
│   ├── 02_politicas_internas.md                       ← ejemplo políticas en español
│   ├── README.md                                      ← guía de formatos
│   └── _dynamic.jsonl                                 ← chunks añadidos por API (auto-generado)
│
└── docs/
    ├── RAG_SERVER_SIDE.md                             ← Guía RAG en profundidad IT/EN (más de 600 líneas)
    └── RAG_SERVER_SIDE.es.md                          ← Traducción al español

9. Versionado y publicación

El repositorio trae una pipeline completa de lanzamiento a PyPI en .github/workflows/publish.yml:

  1. Sube la version en pyproject.toml y __version__ en src/speech_to_speech/init.py.
  2. Fusiona una PR de release que contenga solo esos dos cambios.
  3. Etiqueta y sube:
    git checkout main && git pull origin main
    git tag -a vX.Y.Z -m "Release vX.Y.Z"
    git push origin vX.Y.Z
    
  4. El workflow ejecuta automáticamente uv build + twine check --strict + subida a PyPI.

Consulta AGENTS.md para las reglas de release a nivel de repositorio.


10. Siguientes pasos