| #!/usr/bin/env bash |
| |
| |
| set -euo pipefail |
|
|
| VLLM_PORT=50000 |
| VLLM_URL="http://127.0.0.1:${VLLM_PORT}/v1" |
| MODEL="qwen3.5-9b" |
| VENV="$HOME/.venvs/s2s-qwen" |
| WS_PORT=8765 |
| API_KEY="" |
|
|
| |
| |
| TTS_MODEL="Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice" |
| TTS_TALKER_QUANT="Q8_0" |
| TTS_CODEC_QUANT="BF16" |
|
|
| [ -d "$VENV" ] || python3 -m venv "$VENV" |
| source "$VENV/bin/activate" |
| pip install -qU pip |
|
|
| |
| pip install -q "qwentts-cpp-python==0.3.0+cpu" \ |
| -f https://huggingface.co/datasets/andito/qwentts-cpp-python-wheels/tree/main/whl/cpu |
|
|
| |
| |
| |
| |
| pip uninstall -yq speech-to-speech 2>/dev/null || true |
| pip install -q "speech-to-speech @ git+https://github.com/huggingface/speech-to-speech.git" |
|
|
| |
| |
| pip install -q --force-reinstall torch torchaudio --index-url https://download.pytorch.org/whl/cpu |
| pip list --format=freeze | { grep -i '^nvidia-' || true; } | cut -d= -f1 | xargs -r pip uninstall -yq |
| python -c 'import torch; assert "+cpu" in torch.__version__, torch.__version__; print("torch", torch.__version__, "- CPU only, OK")' |
|
|
| curl -fsS "$VLLM_URL/models" >/dev/null || { echo "vLLM not reachable at $VLLM_URL" >&2; exit 1; } |
|
|
| |
| |
| cat > "$VENV/s2s_launch.py" <<'PY' |
| import os, sys |
| from faster_qwen3_tts import FasterQwen3TTS |
|
|
| def _gguf_paths(model_name): |
| from huggingface_hub import hf_hub_download |
| from qwentts_cpp.models import GGUF_REPO, _MODEL_TO_TALKER_STEM, _normalize_quant |
| talker_q = _normalize_quant(os.environ["QWEN3_TTS_TALKER_QUANT"]) |
| codec_q = _normalize_quant(os.environ["QWEN3_TTS_CODEC_QUANT"]) |
| stem = _MODEL_TO_TALKER_STEM[model_name] |
| talker = hf_hub_download(GGUF_REPO, f"{stem}-{talker_q}.gguf") |
| codec = hf_hub_download(GGUF_REPO, f"qwen-tokenizer-12hz-{codec_q}.gguf") |
| print(f"TTS GGUFs: talker {talker_q}, codec {codec_q}", file=sys.stderr) |
| return talker, codec |
|
|
| _orig = FasterQwen3TTS.from_pretrained.__func__ |
| def _patched(cls, *args, **kwargs): |
| if kwargs.get("backend") == "ggml": |
| talker, codec = _gguf_paths(args[0] if args else kwargs["model_name"]) |
| kwargs["gguf_talker_path"] = talker |
| kwargs["gguf_codec_path"] = codec |
| return _orig(cls, *args, **kwargs) |
| FasterQwen3TTS.from_pretrained = classmethod(_patched) |
|
|
| |
| |
| |
| from speech_to_speech.api.openai_realtime.runtime_config import RuntimeConfig |
| RuntimeConfig.interrupt_response_enabled = property(lambda self: False) |
|
|
| from speech_to_speech.s2s_pipeline import main |
| sys.exit(main()) |
| PY |
|
|
| export CUDA_VISIBLE_DEVICES="" |
| export QWEN3_TTS_TALKER_QUANT="$TTS_TALKER_QUANT" QWEN3_TTS_CODEC_QUANT="$TTS_CODEC_QUANT" |
|
|
| |
| |
| exec python "$VENV/s2s_launch.py" \ |
| --mode realtime \ |
| --ws_port "$WS_PORT" \ |
| --thresh 0.3 --min_speech_ms 256 --min_speech_continuation_ms 128 \ |
| --device cpu \ |
| --stt parakeet-tdt \ |
| --llm_backend responses-api \ |
| --tts qwen3 \ |
| --qwen3_tts_backend ggml \ |
| --qwen3_tts_model_name "$TTS_MODEL" \ |
| --model_name "$MODEL" \ |
| --responses_api_base_url "$VLLM_URL" \ |
| --responses_api_api_key "$API_KEY" \ |
| --responses_api_stream |
|
|