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_*.

VariableDescription
LLM_API_KEY(required) API key for the LLM
LLM_PROVIDERe.g. openai (default openai)
LLM_MODELModel id (default gpt-4o)
LLM_BASE_URLOptional custom API base
AGENT_SYSTEM_PROMPTHow Agent Chat should behave — role, tone, and rules for replies (default: short helpful-assistant prompt)
AGENT_NAMEDisplay name for the agent (default agent-chat)
AGENT_DESCRIPTIONShort description (default set in code)
AGENT_CONVERSATION_WINDOW_SIZEHow many recent messages feed into context (default 20)
LOG_LEVELdebug | 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

EndpointMethodRequestResponse
/api/conversationsGETConversation[]
/api/conversationsPOST{ title?: string } (empty title defaults to New chat){ id, title, createdAt }
/api/conversations/:idPATCH{ title: string }204 No Content
/api/conversations/:idDELETE204 No Content
/api/conversations/:id/messagesGETMessage[]
/api/conversations/:id/messagesPOST{ content: string }Message (assistant reply, waits for agent to finish)
/api/conversations/:id/messages/streamPOST{ 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:

typePurpose
MESSAGE_PERSISTEDOptional 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

← Back to repository README