Eugenememe/idolcall-demo
IdolCall Demo
Browser-side preview of the IdolCall voice-chat experience: on pickup the character plays a lip-synced in-character greeting, then you type a message, the character replies via a selectable LLM (Groq, OpenAI, or Gemini), an on-device Piper VITS finetune speaks the reply, and a portrait video loop swaps between idle and speaking states.
The video swap respects the anchor-frame contract: both idle and speak clips begin and end on the same anchor frame, and we only swap when the current clip finishes its iteration — making every transition a clean hard cut with no visible seam.
Scope
Fifteen characters spanning Holiday, Romance, Celebrity, and Fantasy. Two have DSP effects ported from the sibling tts-voice-demo Space: Alien (ring modulation) and Zombie Santa (pitch resample + tanh saturation). Text input only. Conversation history dies on page refresh (per-session gr.State).
Greeting on pickup
When a call connects (page load or character switch) the character plays a short, lip-synced greeting once — characters/<slug>_greet.mp4, with its own audio — then hands off to the idle loop. The greeting line (from characters.json) is also seeded into the transcript and the LLM history so the conversation starts already in motion. If the browser blocks unmuted autoplay (no user gesture yet), playback falls straight through to idle.
These greeting clips are generated outside the demo: characters.json greeting text → python/gen_greetings.py (audio) → video_loop/p_video_test.py (prunaai/p-video talking portrait) → copied here. Full pipeline: `../video_loop/GREETING_PIPELINE.md`.
Models
Pick the LLM per turn from the Model dropdown — switching mid-chat keeps the conversation (only switching character resets it). All three are fast/cheap tiers driven with the same config (TEMPERATURE=0.8, MAX_TOKENS=80, 30-message history); Gemini runs with thinking disabled (thinking_budget=0) to stay low-latency.
Prices as of May 2026 — they churn, so verify at each provider's pricing page (Groq · OpenAI · Gemini). At demo volume (≤80 output tokens/turn) every reply costs a fraction of a cent; Groq is cheapest and fastest, the other two are quality/fallback options.
Secrets
Set GROQ_API_KEY, OPENAI_API_KEY, and GEMINI_API_KEY as Space secrets (Settings → Repository secrets). Locally, copy .env.example to .env and fill them in. A model whose key is missing errors only when you select it.
Local run
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # fill in GROQ_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY
python app.pyOpen the printed URL.
Dependency note
gradio==4.44.1 (via gradio-client) caps websockets<13. The google-genai SDK requires websockets>=13 for its Live API — an irreconcilable conflict that fails the Space's strict pip resolver (ResolutionImpossible), even though we never touch the Live API. So Gemini is called via its REST `generateContent` endpoint in app.py (stdlib urllib, no SDK), and google-genai is intentionally absent from requirements.txt. The websockets<13.0 pin stays to satisfy gradio-client — don't bump it.
