Show HN: Self-hosted voice AI agent for Asterisk/FreePBX
Hacker News

Show HN: Self-hosted voice AI agent for Asterisk/FreePBX

Quick Start

Get the Admin UI running in 2 minutes. For a complete first successful call walkthrough (dialplan + transport selection + verification), see the documentation.

# Clone repository
git clone https://github.com/hkjarral/AVA-AI-Voice-Agent-for-Asterisk.git
cd AVA-AI-Voice-Agent-for-Asterisk

# Run preflight with auto-fix (creates .env, generates JWT_SECRET)
sudo ./preflight.sh --apply-fixes

Important: Preflight creates your .env file and generates a secure JWT_SECRET. Always run this first!

# Start the Admin UI container
docker compose -p asterisk-ai-voice-agent up -d --build --force-recreate admin_ui

Open in your browser:

  • Local: http://localhost:3003
  • Remote server: http://<your-server-ip>:3003

Default Login: On first start, a one-time admin password is printed to the container logs. Retrieve it with:

docker compose -p asterisk-ai-voice-agent logs admin_ui | grep -i password

You must change it at first login. Restrict port 3003 via firewall, VPN, or reverse proxy for production use. Follow the Setup Wizard to configure your providers and make a test call.

⚠️ Security: The Admin UI is accessible on the network. Restrict port 3003 via firewall, VPN, or reverse proxy for production use.

GPU users: If you have an NVIDIA GPU for local AI inference, see docs/LOCAL_ONLY_SETUP.md for the GPU compose overlay (docker-compose.gpu.yml) before building.

# Start ai_engine (required for health checks)
docker compose -p asterisk-ai-voice-agent up -d --build ai_engine

# Check ai_engine health
curl http://localhost:15000/health
# Expected: {"status":"healthy"} ("degraded" is also possible if a subsystem is unhealthy)

# View logs for any errors
docker compose -p asterisk-ai-voice-agent logs ai_engine | tail -20

The wizard will generate the necessary dialplan configuration for your Asterisk server. Transport selection is configuration-dependent (not strictly "pipelines vs full agents"). Use the validated matrix in the documentation.

Headless Setup

For users who prefer the command line or need headless setup:

./install.sh agent setup

Note: Legacy commands agent init, agent quickstart, agent doctor, agent troubleshoot, and agent demo remain as hidden compatibility aliases. New workflows should use the visible commands documented in docs/CLI_TOOLS_GUIDE.md.

# Configure environment
cp .env.example .env
# Edit .env with your API keys

# Start services
docker compose -p asterisk-ai-voice-agent up -d

FreePBX Integration

Add this to your FreePBX (extensions_custom.conf):

[from-ai-agent]
exten => s,1,NoOp(Asterisk AI Voice Agent)
; AI_AGENT selects an operator-managed agent by slug.
same => n,Set(AI_AGENT=sales-agent)
; Optional: override that agent's configured provider/pipeline for this call.
; same => n,Set(AI_PROVIDER=google_live)
same => n,Stasis(asterisk-ai-voice-agent)
same => n,Hangup()

Notes:

  • Use AI_AGENT to select an operator-managed agent. Its configured target is authoritative unless AI_PROVIDER is intentionally set as a per-call override.
  • Generate a current snippet with agent dialplan --agent <name>.
  • See docs/FreePBX-Integration-Guide.md for channel variable precedence and examples.

Agent CLI Tools

# Health check
agent check

# View logs
docker compose -p asterisk-ai-voice-agent logs -f ai_engine

Release Notes

v7.3.3 - Local AI stabilization 🧠

v7.3.3 is a Local-AI-only stabilization release. It adds no providers and keeps the cloud-provider call paths unchanged.

  • Calls are isolated by session - agent prompts and conversation state no longer mutate shared Local AI Server configuration or leak across reused WebSocket connections. AI Engine and Local AI Server should be upgraded together; the legacy unscoped switch remains temporarily compatible.
  • Barge-in abandons interrupted output - late LLM/TTS work is quarantined, the interrupted exchange is removed from weak-model history, and the replacement turn stays focused on what the caller just said.
  • Farewells finish exactly once - Local hangup_call speaks the selected Kokoro/Piper/etc. farewell without a second LLM rewrite, drains partial AudioSocket or RTP tails, records agent_hangup, and then disconnects.
  • CPU/GPU deployment is safer - dependency pins, CUDA/cuDNN validation, optional llama.cpp architecture targeting, and idempotent preflight checks reduce first-build and rerun failures.
  • Community GPU evidence - Tesla V100S testing passed Faster-Whisper CUDA float16, Llama 3.1 8B Q4_K_M, Kokoro, AudioSocket, ExternalMedia, barge-in, terminal hangup, concurrent session isolation, and restart recovery.

See the Local AI community test matrix and the Unreleased changelog for the complete scope.

v7.3.2 - stabilization release πŸ›‘οΈ

v7.3.2 is a stabilization-only patch release built from the supervised AudioSocket and ExternalMedia validation cycle.

  • No new providers - scope is limited to reliability, deployment safety, documentation, and contributor-facing CI.
  • Grok ExternalMedia repaired - clean barge-in, cancelled-output quarantine, named-instance runtime inheritance, complete replacement turns, and exact inactivity announcements through xAI force_message.
  • AudioSocket and modular pipelines hardened - terminal playback, pipeline producer ownership, talk-detect echo, and inactivity-grace regressions are covered by focused tests and supervised calls.
  • Updater and provider-failure recovery hardened - safer ownership, rollback/stash handling, readiness validation, and an opt-in dialplan redirect.
  • PR quality gates expanded - Admin backend/frontend checks and CLI cross-compilation now run before merge.

Release evidence and remaining gates are tracked in the v7.3.2 validation matrix.

v7.3.1 - Silence watchdog & safe call endings ☎️

AVA now protects silent calls and finishes every terminal message before disconnecting.

  • 30-second inbound inactivity protection by default - AVA asks "Are you still there?", waits 15 seconds for a reply, then speaks a configurable final warning and ends the call. Outbound agents remain opt-in.
  • The agent keeps its configured voice - check-ins and final warnings are synthesized by the active Google Live, OpenAI Realtime, Grok, Deepgram, ElevenLabs, local full-agent, or pipeline voice.
  • Transport-safe hangup - watchdog and hangup_call farewells drain AudioSocket or ExternalMedia/RTP streaming buffers and ARI file playback before ARI disconnects the caller. Fixed sleeps no longer clip long final sentences.
  • Deepgram and ElevenLabs lifecycle fixes - Deepgram control frames no longer split greetings, and ElevenLabs response-completion plus hosted-silence handling keeps AVA's watchdog authoritative.
  • Global and per-agent controls - configure defaults under Advanced Settings β†’ Voice Activity Detection β†’ Caller Inactivity, then optionally override them per agent. Call History labels watchdog endings as "No input timeout".

See Caller inactivity configuration, ElevenLabs setup, and the full v7.3.1 changelog.

v7.3.0 - Per-agent voices πŸŽ™οΈ

Voice now belongs to agents. Configure one provider, create multiple agents that share it - each with its own voice.

  • Provider-aware voice picker in the Agent form: a dropdown of OpenAI's 10 GA voices, suggestions + custom clone IDs for Grok, Google Live's 30 prebuilt voices, Deepgram's Aura models - the control adapts to the agent's selected AI Engine.
  • Safe by default - the provider-level voice becomes the default voice; agents without one behave exactly as before. Unrecognized values (OpenAI/Google/Deepgram catalogs are validated) log a warning and fall back - a bad voice value never fails a call.
  • Observable - every call logs the resolved voice and its source, and Call History shows "Voice: marin (from agent)" per call.
  • Agent voice changes apply instantly - no engine restart. Thanks @foytech for seeding this feature (#497).

Full guide: docs/VOICE_SELECTION.md.

v7.2.0 - Live-status dashboard πŸ“‘

Real-time system status for the Admin UI - pushed, not polled.

  • Live-status hub - a single /api/live-status snapshot endpoint plus an SSE stream (/api/live-status/stream) aggregates AI Engine health, Local AI connectivity, active sessions, audio directories, platform checks, and Asterisk ARI into one normalized status feed.
  • Push-first - ai_engine and local_ai_server push their own readiness to the Admin UI (POST /api/live-status/publish, authenticated with LIVE_STATUS_PUSH_TOKEN), so the dashboard converges in sub-second time after a restart instead of waiting on staggered polls. Legacy /api/system/* probes remain as fallback/enrichment.
  • Configurable - LIVE_STATUS_POLL_INTERVAL_SECONDS (default 30 s, min 2 s) and LIVE_STATUS_INITIAL_PROBE_TIMEOUT_SECONDS (default 2 s), read live from .env.

Full notes in CHANGELOG.md.

v7.1.1 - Dashboard reliability & Admin UI polish πŸ› οΈ

A focused quality release across the Admin UI - no call-path changes.

  • Dashboard reliability - the Asterisk status pill no longer flaps on a transient ARI blip: it reads the engine's authoritative, reconnect-supervised ARI state and applies hysteresis. The system endpoints the Dashboard polls every 5s no longer block the admin event loop, the heaviest is TTL-cached, polling backs off on errors, failed polls surface in the error banner, and a single bad poll no longer flashes cards to "Loading…".
  • No more "Loading configuration…" flash - ~11 config pages now seed from a shared stale-while-revalidate cache of the config document, so revisiting a settings page is instant.
  • Accessibility (WCAG AA) - form labels programmatically associated with inputs, a focus-trapping modal, a navigation landmark + "skip to content" link, accessible names on icon-only buttons, non-colour status cues on the topology, a visible dark-mode toggle on-state, and light-mode contrast fixes. Debug console.logs (including one that leaked the auth token to the browser console) were removed.
  • Prompt editor - configured tool names are colour-coded by their in-call status (enabled / global / not-enabled) as you type.
  • Fix (#436) - a canonical google_live: { type: full } provider can be edited and saved again.

Full notes in CHANGELOG.md.

v7.0.0 - the Agents release 🎯

The biggest release yet: manage your AI agents from the Admin UI, not a config file.

  • πŸ€– Agents tab - create, edit, and manage agents in the UI. Start from a template (receptionist, after-hours, appointment booker, and more), set the prompt and provider, and copy a ready-to-paste dialplan snippet. (Voice is configured on the provider, not per agent.)
  • πŸ“Š Multi-agent dashboard - live KPIs (active agents, active calls, calls routed, transfers), per-agent stats, and routing breakdowns at a glance.
  • ☎️ New AI_AGENT dialplan variable - route a call to an agent by name. Your existing AI_CONTEXT dialplans keep working unchanged.
  • πŸ”„ Automatic migration - your existing contexts move into a local agents database on first start. Nothing to do, and rollback is one command.
  • πŸ”’ Security hardening - no more admin/admin: a one-time admin password is generated and must be changed at first login. Config exports no longer bundle your .env by default.

v6.5.4 (2026-05-25) - OpenAI Realtime GA cleanup across every code path

Follow-up to the v6.5.3 hotfix. v6.5.3 only flipped config/ai-agent.yaml; v6.5.4 brings the rest of the codebase in line:

  • Pydantic defaults in src/config.py now default to api_version: ga + model: gpt-realtime (so fresh wizard installs are correct).
  • Admin UI "Add Provider" template for OpenAI Realtime no longer seeds the sunset preview model.
  • Model dropdown removes the 5 sunset preview options and adds 3 new GA models - gpt-realtime-1.5 (best audio-in/audio-out quality), gpt-realtime-2 (reasoning voice model, GPT-5-class), and gpt-realtime-mini (cost-optimized) - alongside the existing gpt-realtime.
  • Legacy preview values in operator YAML now render in a "Custom (legacy - will not connect)" optgroup with a yellow warning banner above the form so the broken state is visible without silently swapping the operator's config.
  • Engine emits a one-shot warning when api_version: beta is detected in config (exactly once per provider lifetime, not per reconnect attempt).
  • Docs: full rewrite of docs/Provider-OpenAI-Setup.md model section + fix to docs/TROUBLESHOOTING_GUIDE.md.

v6.5.3 hotfix (2026-05-25) - OpenAI Realtime restored

OpenAI sunset the Realtime Beta API on 2026-05-12 and removed the gpt-4o-realtime-preview-2024-12-17 model on 2026-05-07. Shipped config/ai-agent.yaml still pinned api_version: beta + that preview model, so every operator using OpenAI Realtime hit error.code: beta_api_shape_disabled and the WebSocket closed immediately.

Two-line config flip - no code change required. The provider's GA wire-protocol path has shipped since v6.0.0; v6.5.3 just makes it the default everyone gets:

api_version: ga  # was beta
model: gpt-realtime  # was gpt-4o-realtime-preview-2024-12-17

If you have an ai-agent.local.yaml that explicitly pins api_version: beta, remove the override or change it to ga.

Refs: OpenAI deprecations, gpt-realtime.

v6.5.2 (2026-05-24) - xAI Grok + multi-instance full-agent providers

  • Fifth full-agent realtime provider - structurally parallel to OpenAI Realtime and Google Live, built on a multi-instance foundation from day one.
  • ΞΌ-law @ 8 kHz caller input with no input resampling; observed xAI output is PCM16 @ 24 kHz and AAVA converts it to the configured Asterisk transport format.
  • Five named voices (eve, ara, rex, sal, leo) plus custom voice ID free-text for cloned voices.
  • Custom function-tools identical to OpenAI Realtime; xAI-native tools (web_search, x_search, file_search, mcp) accepted via YAML extra_tools escape hatch.

Comments

No comments yet. Start the discussion.