parlor

fikrikarimApache-2.0活跃维护

在本地设备上跑实时多模态 AI 的 Python 项目,整合语音识别、语音合成和本地大模型,实现类似 GPT-Live 的实时对话体验,支持 Apple Silicon 和 MLX。

On-device, real-time multimodal AI with features similar to GPT-Live

v2.0.0发布于 2026-08-02 19:20 UTC2,112star281fork同步于 2026-11-07 07:30 UTC

怎么上手

由 README 提炼,执行前请确认
git clone https://github.com/fikrikarim/parlor.git
cd parlor
curl -LsSf https://astral.sh/uv/install.sh | sh
brew install llama.cpp
uv sync
uv run parlor
硬件要求按 README 说明

macOS 需 Apple Silicon,Linux 需受支持的 GPU;默认 E4B 模型约需 6 GB 空闲内存,e2b 约 4 GB,12b 约 8 GB。README 未说明具体 GPU 型号。

支持 Apple Silicon
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

本地实时多模态语音助手,摄像头加麦克风对话,类似 GPT-Live 但全在设备上跑

在本地设备上跑实时多模态 AI 的 Python 项目,整合语音识别、语音合成和本地大模型,实现类似 GPT-Live 的实时对话体验,支持 Apple Silicon 和 MLX。

核心能力

  • 浏览器端 Silero VAD 加 smart-turn-v3 判断是否说完
  • Gemma 4 经 llama.cpp 同时听和看并流式回复
  • 动作走独立语法约束 JSON 头,不混入语音
  • 支持打断、定时器、实时翻译和只听模式
  • 可选接入 OpenAI 兼容端点做后台联网研究

容易踩的坑

llama.cpp 需 b9503(2026 年 6 月)或更新版本,MODEL=12b 需 b9512,旧版缺 Gemma 4 音频支持或加载 mmproj 崩溃;首次运行会自动下载约 5.7 GB 模型和 TTS 模型;默认端口 8000,需在浏览器授予摄像头和麦克风权限。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

2 个版本v2.0.0
  1. v2.0.0最新正式版2026-08-02 · 2026-08-02 19:20 UTC

    Parlor v2 is a near-complete rebuild of the pipeline toward one goal: GPT-Live-class conversation, fully on-device. Inference moved from LiteRT-LM to llama.cpp, turn-taking moved from LLM judgment to a dedicated audio classifier, and actions moved out of the speech stream into a grammar-forced JSON head — each decision measured before it was made (see benchmarks/).

    New features

    • Background research. "Find the best pizza in Rome right now" hands the task to a frontier model on any OpenAI-compatible endpoint (OpenRouter + web search by default) while the conversation continues; the answer is woven back in as a spoken delivery at the next idle moment. Off unless REASONER_API_KEY is set — without it Parlor stays fully on-device.
    • Timers. "Set a timer for three minutes for the pasta" — the server owns the clock (a turn-based model can't ring into silence; benchmarks/timerprobe.py), the model announces the ring in any mode, and a countdown chip with a cancel button tracks it.
    • Live translation mode. "Translate everything I say into English" turns Parlor into a consecutive interpreter: each utterance rendered after a short silence, no conversational replies, until you say "stop translating" or hit the stop chip.
    • Just-listen mode. "Just listen for a while, I want to think out loud" makes it a silent scribe: every utterance transcribed on screen, nothing spoken back, until you address it again.
    • A sense of time. The model is told how much quiet preceded a turn, how long research took, and when the session started — so "how long was I gone?" gets a real answer.
    • Turn-taking you can see. While an utterance is held mid-thought, its bubble shows the classifier's confidence ("sounds unfinished (12%) — still listening").

    Engine and architecture

    • llama.cpp backend, replacing LiteRT-LM. Runs Google's official Gemma 4 QAT q4_0 GGUFs through a spawned llama-server and its prefix cache, which unlocks speech-overlap prefill: audio chunks and the camera frame are pushed through the cache while you're still talking, so long questions start answering almost as fast as short ones. Barge-in aborts generation server-side. Startup discovers both llama-server and the newer unified llama binary, and enforces the build floor Gemma 4 audio needs (b9503+, b9512 for MODEL=12b) with install-method-aware upgrade hints.
    • Model switch. MODEL=e2b|e4b|12b picks the Gemma 4 size; the default is now E4B (noticeably better answers, ~1.8x E2B latency, ~6 GB RAM).
    • smart-turn-v3 turn detection, replacing LLM FINISHED/WAIT judgment. benchmarks/turnbench.py on labelled human speech: the 8M-parameter classifier scores 0.96 accuracy at 19ms; every Gemma variant is at or near chance while costing 0.6–3.6s. Incomplete utterances are held and merged into the next turn; a false hold is answered anyway after 2.5s of continued silence.
    • Decoupled JSON action head, replacing in-band control tags. The spoken reply is now pure speech; timers, mode switches, and research requests are decided by a second grammar-forced JSON request over the same prompt cache, hidden under TTS playback. benchmarks/archbench.py: head recall 1.0 vs tags 0.955 — and the tags' miss is a spoken promise the server never keeps, the worst failure a voice assistant has. Markup leaking into TTS is now structurally impossible. Client and wire protocol unchanged.
    • Transcript-first streaming. The reply opens with a transcript of what was heard (leading beats trailing: WER 0.00 vs 0.39 on long utterances), shown on screen ~0.3s in, then is spoken sentence-by-sentence while still generating.
    • Context. 32k window, with rotation driven by real prompt_tokens from llama-server instead of an estimate, dropping whole exchanges — no more silent truncation-induced "forgetting".

    Latency (Apple M3 Pro, MODEL=e2b, end of utterance → first audio)

    Turnv1.0.0v2.0.0
    Short question (~2s speech)~1.
  2. v1.0.02026-07-29 · 2026-07-29 06:59 UTC
    查看更新说明

    Initial release. This is a retroactive release, v1.0.0 shipped on April 7, 2026.

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Parlor

    Fully on-device, real-time multimodal AI, with features similar to GPT-Live.

    https://github.com/user-attachments/assets/0833b0ec-9e48-46dc-9fde-c3ac4a70abf9

    Research preview. This is an early experiment. Expect rough edges and bugs.

    Why?

    I'm self-hosting a totally free voice AI on my home server to help people learn speaking English. It has hundreds of monthly active users, and I spent weeks improving it until it's almost on par with ChatGPT Advanced Voice.

    Then, my heart broke when OpenAI released GPT-Live, which is miles ahead of ChatGPT Advanced Voice. GPT-Live is so good that I use it almost every day. It made my app feel obsolete. I either need to redirect my users to GPT-Live, or update the app to match it. I chose to try, with the additional constraint that it had to run 100% locally on my MacBook M3 Pro.

    My first attempt was to fine-tune Gemma 4 12B to behave like a full-duplex model. Something like grafting a decision tick + speech head to the model. It failed after multiple trials. For now, I think a classic cascade system is still better. We just need to wait until a benevolent frontier AI company releases a full-duplex model that's on par with GPT-Live.

    AI disclosure

    This software is developed with strong assistance from Claude and with humans leading the ideas, testing, and debugging. We say this openly because it shaped how the project was built. If you are not happy with AI-developed code, this software is not for you. This disclosure is inspired by ds4.

    Everything above this section is human-written, everything from here down is AI-generated.

    How it works

    Browser (mic + camera)
        │
        │  WebSocket (audio PCM + JPEG frames)
        ▼
    FastAPI server
        ├── smart-turn-v3 (~20ms)                   →  did you finish your thought?
        ├── Gemma 4 E4B via llama.cpp (QAT q4_0)    →  hears + sees, streams the reply
        │     └── action head (same model, JSON)    →  timers · modes · research
        ├── Kokoro TTS (MLX on Mac, ONNX on Linux)  →  speaks sentence-by-sentence
        └── background reasoner (optional)          →  frontier model for web research
        │
        │  WebSocket (transcript + streamed audio chunks)
        ▼
    Browser (playback + transcript)
    
    • Background research (optional). "Find the best pizza in Rome right now" hands the task to a frontier model on any OpenAI-compatible endpoint (OpenRouter + web search by default) while the conversation continues; the answer is spoken when it arrives. Off unless REASONER_API_KEY is set — without it Parlor stays fully on-device.
    • Hands-free turn-taking. Browser-side Silero VAD with a ~200ms silence cutoff — no push-to-talk. Pipecat's smart-turn-v3 then judges whether you actually finished your thought: mid-thought pauses are held silently until you continue, or answered once you stay quiet.
    • Everything streams. The reply opens with a transcript of what was heard (on screen immediately — committing to it first measurably improves accuracy), then is spoken sentence-by-sentence while still generating. Your camera frame and speech are pushed through llama.cpp's prompt cache while you're still talking, so even long questions start answering almost instantly.
    • Barge-in. Speak over the AI to interrupt it; generation is aborted server-side. Echo is handled without the browser's echo canceller — or just wear headphones.
    • Actions never ride the speech. Timers, mode switches, and research requests are decided by a separate grammar-forced JSON request over the same prompt cache, hidden under TTS playback. The spoken reply stays pure speech, so control markup can never leak into TTS and a promise made aloud is always kept (recall 1.0 vs 0.955 for in-band tags — benchmarks/archbench.py).
    • Timers. "Set a timer for three minutes for the pasta" — the server owns the clock (a turn-based model can't ring into silence — benchmarks/timerprobe.py), the model announces the ring out loud in any mode, and a countdown chip with a cancel button tracks it.
    • Live translation mode. "Translate everything I say into English" turns Parlor into a consecutive interpreter: each utterance rendered after a short silence, no conversational replies, in any language Gemma understands — until you say "stop translating" or hit the stop chip.
    • Just-listen mode. "Just listen for a while, I want to think out loud" makes it a silent scribe: every utterance transcribed on screen, nothing spoken back, until you address it again.
    • A sense of time. The model is told how much quiet preceded a turn, how long research took, and when the session started — so "how long was I gone?" gets a real answer.

    Requirements

    • Python 3.12+
    • llama.cpp (brew install llama.cpp on macOS; other platforms: install guide). Needs build b9503 (June 2026) or newer, b9512 for MODEL=12b — older builds lack Gemma 4 audio or crash loading its mmproj
    • macOS with Apple Silicon, or Linux with a supported GPU
    • ~6 GB free RAM for the default E4B model (MODEL=e2b fits in ~4 GB)

    Quick start

    git clone https://github.com/fikrikarim/parlor.git
    cd parlor
    
    # Install uv and llama.cpp if you don't have them
    curl -LsSf https://astral.sh/uv/install.sh | sh
    brew install llama.cpp
    
    uv sync
    uv run parlor
    

    Open http://localhost:8000, grant camera and microphone access, and start talking.

    Models are downloaded automatically on first run (~5.7 GB for Gemma 4 E4B QAT + its multimodal projector, plus TTS models).

    Configuration

    Set these in your shell or a .env at the repo root. The common ones:

    VariableDefaultDescription
    MODELe4bGemma 4 size: e2b (fastest), e4b (better answers, ~1.8x e2b latency), 12b (needs ~8GB)
    PORT8000Server port
    REASONER_API_KEY(unset — research off)API key enabling background research
    REASONER_BASE_URLhttps://openrouter.ai/api/v1Any OpenAI-compatible chat endpoint
    REASONER_MODELopenai/gpt-5.6-lunaModel the endpoint should run

    The full list — local model paths, llama.cpp tuning, TTS backend, test hooks — is in docs/configuration.md.

    Performance (Apple M3 Pro)

    Measured with MODEL=e2b from end of utterance to first audio heard (add ~200ms of VAD silence detection on top; the default E4B is roughly 1.8x these numbers, with noticeably better answers). The camera frame and the speech itself are prefilled while you're still speaking, and the reply opens with the transcript line (its decode time is included below — the price of accurate transcripts):

    TurnFirst audioTurn complete
    Short question (~2s speech)~0.7s~1.3s
    Short question + camera~0.8s~1.3s
    Long question (~9s speech), streamed~1.3-1.4s~2.1-2.3s
    Long question + camera~1.5s~2.3s

    Reproduce with the end-to-end benchmark (real spoken audio, synthesized locally). Run it before and after a change to see the impact:

    uv run parlor                    # terminal 1
    uv run python benchmarks/bench.py --label before --out benchmarks/results/before.json   # terminal 2
    # ...make changes, restart the server...
    uv run python benchmarks/bench.py --label after --out benchmarks/results/after.json
    uv run python benchmarks/compare.py benchmarks/results/before.json benchmarks/results/after.json
    

    Testing

    An end-to-end suite spawns the real server (llama.cpp, TTS, turn detector) and drives it over WebSocket with synthesized speech — including degraded audio (clipped word endings, noise, other voices) that reproduces live-mic failure modes:

    uv run pytest            # ~1 minute + model load
    

    Set PARLOR_TEST_URL=ws://localhost:8000/ws to run it against an already-running server. Browser-only behavior (echo at speaker volume, VAD feel, multilingual speech) still needs a live mic and ears.

    Project structure

    src/parlor/
    ├── server.py              # FastAPI app + per-connection conversation loop
    ├── llama.py               # llama-server lifecycle + chat API client
    ├── pipeline.py            # Streaming turn pipeline (decode → sentences → TTS)
    ├── actions.py             # Action decider (grammar-forced JSON: timers, modes, research)
    ├── reasoner.py            # Background research delegation (OpenAI-compatible)
    ├── modes.py               # Session modes (conversation, translate, listen)
    ├── turn_detector.py       # smart-turn-v3 end-of-turn classifier
    ├── tts.py                 # Platform-aware TTS (MLX on Mac, ONNX on Linux)
    └── web/                   # Frontend (markup, styles, app logic: VAD, camera, playback)
    tests/                     # End-to-end test suite (uv run pytest)
    benchmarks/
    ├── bench.py               # End-to-end latency benchmark
    ├── fixtures.py            # Spoken-audio fixtures (synthesized locally)
    ├── compare.py             # Diff two benchmark result files
    ├── turnbench.py           # Turn-detection accuracy benchmark
    ├── archbench.py           # In-band control tags vs decoupled action head
    ├── camerabench.py         # Attach-every-turn vs camera-as-tool-call
    └── timerprobe.py          # Why the server owns the timer clock
    

    Acknowledgments

    License

    Apache 2.0