When off air, callers hear a message and get disconnected. When on
air, calls route normally. Toggle button added to frontend header
with pulsing red ON AIR indicator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove gpt-4o-realtime (WebSocket-only) from OpenRouter models
- Increase OpenRouter timeout to 60s and max_tokens to 150
- Handle empty LLM responses
- Fix publish_episode.py for current Castopod API fields
- Add port conflict check and graceful shutdown to run.sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add streamSid tracking and per-caller send locks for SignalWire
- Improve TTS streaming with real-time pacing and detailed logging
- Block host audio to caller during TTS playback
- Randomize caller names between sessions from name pools
- Update page title and show phone number in UI
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace aggressive sentence-count limiting with ensure_complete_thought()
which only trims if the LLM was actually cut off mid-sentence
- Softer prompt guidance for natural brevity instead of rigid sentence count
- max_tokens at 100 as natural length cap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- max_tokens back to 150 so LLM can finish thoughts
- New limit_sentences() keeps only first 2 complete sentences
- Never cuts mid-sentence — always ends at punctuation
- Applied to both chat and auto-respond paths
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Log chunk count and peak audio level on recording stop
- Add null check on _recorded_audio in callback
- Small delay after stopping piggybacked recording for callback to finish
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Reduce max_tokens from 100 to 75 for shorter output
- Add truncate_to_complete_sentence() to trim at last punctuation
- Applied to both chat and auto-respond paths
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Much stronger prompt language: "no more than 2 sentences EVER"
- Added "DO NOT ramble" instruction
- Reduced max_tokens back to 100 as hard limit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Increase max_tokens from 100 to 150 to avoid mid-sentence truncation
- Tighten prompt to 1-2 short sentences with emphasis on completing them
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Mute host mic forwarding while TTS is streaming to prevent interleaving
both audio sources into the same playback buffer
- Replace nearest-neighbor downsampling with box-filter averaging on both
server (host mic) and browser (caller mic) for anti-aliased resampling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TTS audio was sent as a single huge WebSocket frame that overflowed the
browser's 3s ring buffer. Now streams in 60ms chunks at real-time rate.
Also increased browser ring buffer from 3s to 10s as safety net.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The auto-respond function played AI TTS to the local Loopback channel
but didn't send it over WebSocket to the live caller in the browser.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace per-callback async task spawning with persistent queue-based sender
- Buffer host mic to 60ms chunks (was 21ms) to reduce WebSocket frame rate
- Reduce server ring buffer prebuffer from 150ms to 80ms
- Reduce browser playback jitter buffer from 150ms to 100ms
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a live caller is on air, the host stream already has an InputStream
open. Opening a second one for push-to-talk recording causes a conflict.
Now recording piggybacks on the host stream callback instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Reduce capture chunk from 4096 to 640 samples (256ms → 40ms)
- Replace BufferSource scheduling with AudioWorklet playback ring buffer
- Add 80ms jitter buffer with linear interpolation upsampling
- Reduce host mic and live caller stream blocksizes from 4096/2048 to 1024
- Replace librosa.resample with numpy interpolation in send_audio_to_caller
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use persistent callback-based output stream instead of opening/closing per chunk
- Replace librosa.resample with simple decimation in real-time audio callbacks
- Move host stream initialization to background thread to avoid blocking
- Change live caller channel default to 9
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add CallRecord dataclass for tracking call history
- Extend Session with call_history, active_real_caller, ai_respond_mode, auto_followup
- Add get_show_history() for AI caller context about earlier calls
- Update get_conversation_summary() to handle host/real_caller/ai_caller roles
- Update reset() to clear new fields
- Add tests for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>