agent-chat Server
May 3, 2026 · View on GitHub
Go API server powered by agent-sdk-go and Temporal. Provides REST endpoints for managing conversations and messages, with durable workflow-orchestrated AI agent execution.
Architecture
The API and Temporal worker are separate processes, both built from the same image. APP_MODE selects the role (server = HTTP API + agent client; worker = Temporal worker polling the task queue). Docker Compose starts both plus Postgres and Temporal — see docker-compose.yml (server and worker services). Run migrations once from the API before workers rely on the schema (Compose orders worker after server).
Conversations and messages live in PostgreSQL; LLM runs go through Temporal workflows via agent-sdk-go.
server/
├── agent/ # Conversation bridge for the agent SDK
├── config/ # Environment-based configuration
├── db/ # PostgreSQL connection and migrations
├── handlers/ # HTTP route handlers
├── llm/ # Azure OpenAI client (adds api-key header + api-version param)
├── stream/ # SSE broker — forwards AG-UI JSON from the agent stream
├── store/ # Data access layer
├── Dockerfile
├── agent.go # HTTP server entry when APP_MODE=server
└── worker.go # Temporal worker entry when APP_MODE=worker
Prerequisites
- Docker and Docker Compose
- An LLM API key
Run with Docker
From the repository root:
# API + dependencies (add `worker` for Temporal execution — same image, APP_MODE=worker)
# Full stack from repo root: `docker compose up -d` includes `server`, `worker`, and `ui`.
docker compose up -d postgres temporal server worker
Ports (host): The API is published at http://localhost:9090 (Compose maps 9090 → 8080 in the server container so host 8080 is left for other tools). The UI in Docker is at http://localhost:3000. The browser loads the UI, then calls the API using SERVER_API_URL baked into config.json (not through the UI container as a proxy). Temporal UI at http://localhost:8233 is optional: workflow execution visibility, tracing, and debugging — not required to use Agent Chat.
# Full stack (backend + React UI + Temporal dashboard)
docker compose up -d
View logs
docker compose logs -f server
Stop services
docker compose down
Start Agent Chat
From the repository root (where docker-compose.yml lives), after you change server code or server/Dockerfile, rebuild and recreate only the API service (Postgres and Temporal keep running):
docker compose up -d --build server
To rebuild and start the full Agent Chat stack: docker compose up -d --build.
Optional shortcuts if you use the repo Makefile: run make help, or e.g. make up, make restart-server, make logs, make secrets-scan.
Environment variables
Docker Compose: server gets Postgres connection (POSTGRES_*) and Temporal client settings (TEMPORAL_*) from docker-compose.yml. The process listens on 8080 inside the container (config.HTTPListenPort); Compose publishes it as 9090:8080 on the host. server/.env holds LLM and Agent Chat tuning (LLM_*, AGENT_*).
Local go run (no Compose): export POSTGRES_*, TEMPORAL_*, or set DATABASE_URL. Defaults assume localhost for Postgres when building the URL from POSTGRES_*.
| Variable | Description |
|---|---|
LLM_API_KEY | (required) API key for the LLM |
LLM_PROVIDER | e.g. openai (default openai) |
LLM_MODEL | Model id (default gpt-4o) |
LLM_BASE_URL | Optional custom API base |
AGENT_SYSTEM_PROMPT | How Agent Chat should behave — role, tone, and rules for replies (default: short helpful-assistant prompt) |
AGENT_NAME | Display name for the agent (default agent-chat) |
AGENT_DESCRIPTION | Short description (default set in code) |
AGENT_CONVERSATION_WINDOW_SIZE | How many recent messages feed into context (default 20) |
LOG_LEVEL | debug | info | warn | error (default info) |
Infra (when not using Compose, or to override): DATABASE_URL (optional full URL), POSTGRES_HOST, POSTGRES_PORT, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, TEMPORAL_HOST, TEMPORAL_PORT, TEMPORAL_NAMESPACE, and related Temporal settings in config.go / Compose.
Copy the example and add your LLM key:
cp server/.env.example server/.env
# then edit server/.env
API endpoints
| Endpoint | Method | Request | Response |
|---|---|---|---|
/api/conversations | GET | — | Conversation[] |
/api/conversations | POST | { title?: string } (empty title defaults to New chat) | { id, title, createdAt } |
/api/conversations/:id | PATCH | { title: string } | 204 No Content |
/api/conversations/:id | DELETE | — | 204 No Content |
/api/conversations/:id/messages | GET | — | Message[] |
/api/conversations/:id/messages | POST | { content: string } | Message (assistant reply, waits for agent to finish) |
/api/conversations/:id/messages/stream | POST | { content: string } | SSE stream of agent events (see below) |
SSE stream events
The stream endpoint returns text/event-stream frames. Each data: line is JSON whose discriminator is type (AG-UI event names from agent-sdk-go, e.g. TEXT_MESSAGE_CONTENT, RUN_FINISHED, RUN_ERROR). The bridge forwards ev.ToJSON() from the SDK where possible.
After a root RUN_FINISHED, the server appends one extension frame:
type | Purpose |
|---|---|
MESSAGE_PERSISTED | Optional persisted message row (DB id and body) when the assistant message has been written — small delay vs RUN_FINISHED is normal. |
The agent run continues in a background goroutine — closing the browser stream does not cancel the workflow. Use GET /api/conversations/:id/messages to reconcile state.
Database
PostgreSQL is provisioned automatically by Docker Compose. The server runs migrations on startup, creating the conversations and messages tables if they don't exist.
Built with
- agent-sdk-go — AI agent SDK for Go (powered by Temporal)
- chi — HTTP router
- pgx — PostgreSQL driver
- PostgreSQL — Data persistence