Tested Configuration: The Agent UI has been tested on AMD Ryzen AI MAX+ 395 with Qwen3.5-35B-A3B-GGUF. Other configurations are not officially verified. See the User Guide for full details and how to report issues on other hardware.
The Agent UI SDK is the Python backend that powers the GAIA Agent UI. It provides:
FastAPI REST server with session, chat, document, and memory endpoints
SQLite database for persistent sessions, messages, and document metadata
SSE streaming for real-time chat responses
RAG integration for document Q&A
Memory dashboard with knowledge browser, tool stats, and observability (Memory SDK)
Pydantic models for request/response validation
The backend runs on port 4200 by default and serves both the Electron desktop app and browser-based clients.
End users don’t need to interact with this SDK directly — prebuilt .exe /
.deb desktop installers are on the
GitHub Releases page, and
npm install -g @amd-gaia/agent-ui gives the same app on any Node-capable
platform. This page is for developers embedding or extending the backend.
See the Agent UI guide for install
options.
db = ChatDatabase()# Create session with attached documentssession = db.create_session( title="Project Review", model="Qwen3.5-35B-A3B-GGUF", system_prompt="You are a code review assistant.", document_ids=["doc-abc123"],)# Update titledb.update_session(session["id"], title="Sprint 42 Review")# List recent sessionsfor s in db.list_sessions(limit=10): print(f"{s['title']} ({s['message_count']} messages)")
(session_id, role, content, rag_sources?, tokens_prompt?, tokens_completion?) -> int
Add a message, returns message ID
get_messages
(session_id, limit=100, offset=0) -> List[Dict]
Get messages oldest-first
count_messages
(session_id) -> int
Count messages in a session
Example:
# Add a user messagedb.add_message(session_id, "user", "What does this function do?")# Add an assistant message with RAG sourcesdb.add_message( session_id, "assistant", "This function initializes the database connection...", rag_sources=[ {"document_id": "doc-123", "filename": "main.py", "chunk": "def init_db()...", "score": 0.92} ], tokens_prompt=150, tokens_completion=87,)# Retrieve conversationmessages = db.get_messages(session_id)for msg in messages: print(f"[{msg['role']}] {msg['content'][:80]}...")
import hashlib# Add a documentdoc = db.add_document( filename="manual.pdf", filepath="/home/user/docs/manual.pdf", file_hash=hashlib.sha256(open("manual.pdf", "rb").read()).hexdigest(), file_size=1_234_567, chunk_count=45,)# Attach to a sessiondb.attach_document(session_id, doc["id"])# Check which documents are in a sessiondocs = db.get_session_documents(session_id)for d in docs: print(f"{d['filename']} ({d['chunk_count']} chunks)")
Factory function that creates and configures the FastAPI application with all endpoints.
from gaia.ui.server import create_appdef create_app(db_path: str = None, webui_dist: str = None) -> FastAPI: """Create and configure the FastAPI application. Args: db_path: Path to SQLite database. None for default (~/.gaia/chat/gaia_chat.db). ":memory:" for testing. webui_dist: Path to a prebuilt Agent UI bundle to serve as static files. None falls back to the bundle that ships with the installed package (used by `gaia --ui-dist`). Returns: Configured FastAPI application with all routers (agents, chat, documents, files, mcp, sessions, system, tunnel) registered. """
Shared state is stored on app.state and is accessible in tests:
app = create_app(db_path=":memory:")db = app.state.db # ChatDatabaseregistry = app.state.agent_registry # AgentRegistry (see /spec/plugin-registry)dispatch = app.state.dispatch_queue # background job queuemonitor = app.state.document_monitor # watches the library for new files
Trigger loading a model on the Lemonade server. Returns 202 immediately; loading proceeds in the background. Poll GET /api/system/status to detect when loading completes.Request:
Trigger downloading a model via the Lemonade server. Returns 202 immediately; the download proceeds in the background. Poll GET /api/system/status to detect when the model becomes available. Set force to true to re-download even if the file already exists (repairs corrupted or incomplete downloads).Request:
HuggingFace model ID overriding the default, or null if using the default model.
model_status
object or null
Status of the custom model on Lemonade. null when no custom model is set. Contains found (in catalog), downloaded (on disk), and loaded (currently active).
PUT /api/settings
Update user settings. Set custom_model to a model ID to override the default, or to an empty string / null to clear the override and revert to the default model.Request:
Send a message and receive a response. Supports both streaming (SSE) and non-streaming modes.Request:
{ "session_id": "abc-123", "message": "What does this code do?", "document_ids": ["doc-456"], "stream": true}
Streaming response (SSE events):When stream: true, the server returns a text/event-stream response. Each line follows the SSE format data: <JSON>. The SSEOutputHandler (src/gaia/ui/sse_handler.py) bridges agent console events to the following typed events:Thinking and Progress
Event type
Fields
Description
thinking
content (string)
Agent reasoning or progress message. Emitted when the agent starts processing, thinks through a problem, or begins a long-running operation.
step
step (int), total (int), status (string)
Agent step progress. step is the current step number, total is the step limit, and status is "started".
status
status (string), message (string)
General status update. status is one of "working", "complete", "warning", or "info". May also include steps (int) and elapsed (number) when status is "complete".
plan
steps (string[]), current_step (int or null)
Agent execution plan. Each entry in steps is a human-readable description of a planned action.
Tool Execution
Event type
Fields
Description
tool_start
tool (string), detail (string)
Tool invocation started. tool is the tool function name (e.g., "query_documents", "search_file"). detail is a human-readable description of the operation.
tool_args
tool (string), args (object), detail (string)
Tool arguments. args is the raw arguments dict passed to the tool. detail is a formatted human-readable summary of the arguments.
tool_end
success (boolean)
Tool invocation completed.
tool_result
title (string or null), summary (string), success (boolean), result_data (object or null), command_output (object or null)
Tool result with structured data. summary is a human-readable result. result_data contains typed results (see below). command_output contains shell command output (see below).
Governance policy blocked a tool before execution. No user action is required; use this to show a visible policy refusal instead of treating the denial as a generic tool failure.
result_data variants in tool_result:
File list:{"type": "file_list", "files": [...], "total": int} — up to 20 file entries
Final complete answer from the agent. elapsed is wall-clock seconds. steps and tools_used are execution totals. Double-escaped newlines/tabs from LLM output are automatically corrected.
agent_error
content (string)
Error message from the agent.
Stream Termination
Event type
Fields
Description
done
message_id (int), content (string)
Signals the end of the stream. message_id is the database ID of the saved assistant message. content is the full response text.
Example stream showing a typical multi-step interaction:
Open a file or folder in the system file explorer. On Windows this launches Explorer, on macOS it uses open, and on Linux it uses xdg-open. Symbolic links are rejected for security.Request:
Invalid path (empty or contains null bytes), or path is a symbolic link
404
Path does not exist
500
Failed to launch the system file explorer
POST /api/files/upload
Upload an arbitrary file for use as a chat attachment (not added to the
RAG library). Use POST /api/documents/upload instead if you want the
file indexed for Q&A.
GET /api/files/browse
Browse filesystem contents. Returns entries under a directory with size,
mtime, and type metadata.
GET /api/files/search
Search files by name/pattern across watched or allowed locations.
GET /api/files/preview
Return a preview (first N bytes / first N lines) of a text file.
GET /api/files/image
Stream an image file from the filesystem for display in the UI.
List all agents registered via AgentRegistry — built-in agents plus any
custom agents discovered under ~/.gaia/agents/. See
plugin-registry for the registration format.
GET /api/agents/{agent_id}
Return details for a single registered agent (description, models,
conversation starters, source = builtin or custom_python).
Tool-confirmation handshake. When TOOLS_REQUIRING_CONFIRMATION
triggers the agent to pause, the UI prompts the user and posts the
outcome (approve / deny) back on this endpoint.
-- Global document libraryCREATE TABLE documents ( id TEXT PRIMARY KEY, filename TEXT NOT NULL, filepath TEXT NOT NULL, file_hash TEXT UNIQUE NOT NULL, file_size INTEGER DEFAULT 0, chunk_count INTEGER DEFAULT 0, indexed_at TEXT, last_accessed_at TEXT, indexing_status TEXT DEFAULT 'complete', -- pending | indexing | complete | failed | cancelled file_mtime REAL -- file modification time (Unix epoch));-- Sessions (conversations)CREATE TABLE sessions ( id TEXT PRIMARY KEY, title TEXT NOT NULL DEFAULT 'New Chat', created_at TEXT, updated_at TEXT, model TEXT NOT NULL DEFAULT 'Qwen3.5-35B-A3B-GGUF', system_prompt TEXT);-- Many-to-many: documents attached to sessionsCREATE TABLE session_documents ( session_id TEXT REFERENCES sessions(id) ON DELETE CASCADE, document_id TEXT REFERENCES documents(id) ON DELETE CASCADE, attached_at TEXT, PRIMARY KEY (session_id, document_id));-- MessagesCREATE TABLE messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT REFERENCES sessions(id) ON DELETE CASCADE, role TEXT CHECK(role IN ('user', 'assistant', 'system')) NOT NULL, content TEXT NOT NULL, created_at TEXT, rag_sources TEXT, -- JSON array of source citations agent_steps TEXT, -- JSON array of agent execution steps inference_stats TEXT, -- JSON blob with timing/token stats tokens_prompt INTEGER, tokens_completion INTEGER);-- Key/value user settings (e.g. custom_model override)CREATE TABLE settings ( key TEXT PRIMARY KEY, value TEXT);
The indexing_status, file_mtime, agent_steps, and inference_stats
columns (plus the settings table) are added via migrations for databases
created before these existed. New databases include them in the initial schema.
The Agent UI server delegates to the GAIA Agent for LLM communication and tool execution:
from gaia.agents.chat.agent import ChatAgent, ChatAgentConfig# The server creates a ChatAgent instance per requestconfig = ChatAgentConfig( model_id=session.get("model", "Qwen3.5-35B-A3B-GGUF"), system_prompt=session.get("system_prompt"), allowed_paths=["/path/to/files"],)agent = ChatAgent(config)# Process a query (agent reasons, uses tools, returns response)result = agent.process_query(message)
GAIA Agent UI is also available as an npm package for quick installation:
npm install -g @amd-gaia/agent-ui
This provides the gaia-ui CLI command:
gaia-ui # Start Python backend + open browsergaia-ui --serve # Serve frontend only (Node.js static server)gaia-ui --port 8080 # Custom portgaia-ui --version # Show version
On first run, gaia-ui automatically installs the Python backend (uv, Python 3.12, amd-gaia) if not already present. On subsequent runs, it auto-updates if the version doesn’t match.