Browser chat (mix athena.web)

Copy Markdown View Source

mix athena.web starts a Phoenix LiveView chat UI backed by the same agent loop as mix athena.chat. The server binds to all interfaces so you can reach it from a phone, a second machine, or any browser on the same network — no native install required.

Prerequisites

The web UI requires three optional dependencies:

# mix.exs
{:phoenix, "~> 1.7"},
{:phoenix_live_view, "~> 1.0"},
{:bandit, "~> 1.5"}

ExAthena declares them optional: true so the core library stays lean. Without them, mix athena.web prints an error and exits.

The JS bundle is served directly from the installed Hex packages — there is no npm or esbuild step.

Launching

mix athena.web                  # http://0.0.0.0:4000
mix athena.web --port 8080      # custom port

Flags

FlagDescription
--port PORTHTTP port. Default: 4000.

The server listens on 0.0.0.0, so any device on the same network can reach it at http://<your-ip>:<port>.

Layout


  ExAthena                                         Reading · foo.ex   
                You: refactor the auth module                             
 Provider                                          call  read            
 [Ollama    ]  Assistant: I'll start by            {"path":"lib/…"}   
                                                   result                
 Model           read(lib/auth.ex)                  defmodule Auth    
 [qwen2.5  ]     defmodule Auth do                                    
                    4 more lines                   call  edit            
 Mode                                                {"path":"lib/…"}   
 [ReAct     ]   edit(lib/auth.ex)                result                
                   patch applied (14 lines)          patch applied     
 + New session                                                           
  Sessions                               
                 /                                                     
 Recent                                   
 my_app                                                                  
  • Sidebar (left) — provider, model, and mode selectors; session list and recent projects; live token/cost status block while the model is running.
  • Messages (center) — streaming assistant text with inline tool-call blocks showing collapsible output previews.
  • Details (right) — chronological log of every tool call and result, thinking blocks, and iteration markers for the current session.
  • Right panels (optional, toggled via ⊟ / ± buttons) — color-coded diff viewer for file edits, raw stdout for Bash calls, and a live git diff HEAD panel.

The sidebar dropdown lists every provider the registry knows about at startup — the five built-in providers plus any JSON-defined providers loaded from ~/.config/ex_athena/providers/:


 Provider     
  
  Ollama   
  
  llama.cpp   
  Ollama      
  Claude      
  OpenAI-compat
  Gemini      
  groq           JSON-defined provider
  deepseek       JSON-defined provider

This is the same registry the TUI (mix athena.chat) reads. A provider defined in ~/.config/ex_athena/providers/groq.json appears in both front-ends automatically. See providers.md for the JSON schema and example files.

Switching provider triggers an async model fetch — the model selector shows "fetching models…" for local providers (Ollama, llama.cpp) that expose a /api/tags or /v1/models endpoint. Cloud providers (Claude, Gemini, OpenAI-compatible) fall back to a free-text input so you can type the model name directly.

Inline API key prompt

A JSON provider definition can include "api_key_prompt": true to tell the web sidebar to show an inline password field for that provider whenever it is selected:

{
  "name": "my-openrouter",
  "adapter": "req_llm",
  "req_llm_provider_tag": "openai",
  "base_url": "https://openrouter.ai/api/v1",
  "api_key_prompt": true,
  "default_model": "anthropic/claude-opus-4-8"
}

When a user selects my-openrouter from the dropdown, the sidebar renders:


 Provider     
 [my-openrouter]
              
 API key      
 [] 

The key is held in LiveView socket state for the duration of the browser session. It is sent to the server only over the WebSocket connection and is never written to disk — each page load starts with an empty key field.

For providers where the key is set via api_key_env or config.exs, the prompt field is not shown. api_key_prompt: true is intended for shared deployments where different users supply their own keys.

Session persistence

Every completed turn is auto-saved. The persistence layout under ~/.ex_athena/web/:

~/.ex_athena/web/
 secret.key          # stable secret_key_base — generated once
 recent.json         # recently opened project directories (max 20)
 sessions/
     <id>.session    # full session binary
     <id>.session
     

Session files are Erlang binary terms written with :erlang.term_to_binary/1. Each file contains:

FieldDescription
idUUID — stable across saves
titleFirst user message, truncated
cwdWorking directory for this session
providerProvider name at save time
modelModel name at save time
modeAgent mode at save time
created_at / updated_atDateTime.t()
display_messagesRendered message list for fast reload
ex_messagesRaw ExAthena.Messages for resuming the loop
tool_uisDiff / file / process payloads keyed by tool call ID
details_streamFull right-pane event log

Sessions survive server restarts because secret.key persists the secret_key_base — the same key is used every run, so existing browser cookies remain valid. A fresh random key would cause the old session cookie to fail CSRF validation, triggering a LiveView reload with a multi-second delay.

recent.json tracks the working directories you have opened, newest first, capped at 20 entries. The sidebar "Recent" section and the empty-state screen both read from this file.

Loading and managing sessions

Click ▼ Sessions in the sidebar to expand the session list. Sessions are filtered to the active working directory; click a session title to load it, or × to delete the file.

Fork: every assistant message has a ⑂ fork button. It duplicates the conversation history up to that point and opens a new session — the original session is untouched.

Features at a glance

FeatureDescription
StreamingTokens stream in real time over the LiveView WebSocket.
ThinkingModels that emit <think> blocks show reasoning in the details pane while the answer builds in the main area.
Action indicatorWhile the model runs, a ⚡ Reading · foo.ex pill tracks the current tool call in real time.
Diff viewerFile edits show a ▼ view button. Click it for a color-coded line diff (+ green / red) computed server-side.
Bash outputBash tool calls show exit code, runtime, and stdout in a collapsible block.
MarkdownCompleted responses render headings, fenced code blocks (with language label), inline code, bold/italic, lists, links, and horizontal rules — no CDN needed.
Git panelThe ± button opens a live git diff HEAD panel, refreshed after each tool result.
ForkSnapshot any assistant message and branch the conversation from that point.
Session recallReload any past session; the full conversation and tool-call details restore instantly.

Tips

  • Working directory: click the + button at the top of the sidebar (or select a recent project from the empty state) to open a folder. The agent's filesystem tools are scoped to that directory.
  • Multiple providers: drop several JSON files into ~/.config/ex_athena/providers/ before starting — they all appear in the sidebar dropdown immediately.
  • Shared server: because the UI binds to 0.0.0.0, team members on the same network can reach it at http://<host-ip>:4000. Use api_key_prompt: true in provider JSON files to let each user supply their own key without sharing credentials.
  • Session cleanup: sessions accumulate in ~/.ex_athena/web/sessions/. Delete them from the sidebar × button or remove *.session files directly.