Skip to main content

Chat with an LLM

A multi-turn conversation composed from AI Providers, Agents, Sessions and Webhooks, in sync and background modes.

Prerequisites​

export SOAT_BASE_URL=http://localhost:5047

Step 1 — Log in as admin​

Admin bypasses policy evaluation. See Users.

ADMIN_TOKEN=$(soat login-user --username admin --password Admin1234! | jq -r '.token')
export SOAT_TOKEN=$ADMIN_TOKEN

Step 2 — Create a project​

Every resource lives inside a project.

PROJECT_ID=$(soat create-project --name "LLM Chat Demo" | jq -r '.id')
echo "PROJECT_ID: $PROJECT_ID"
# PROJECT_ID: proj_vh9qHLINTdsrAqwK

Step 3 — Create a local AI provider​

An Ollama AI provider uses the server's OLLAMA_BASE_URL; no secret is required. Other providers: Connect Third-Party LLMs.

AI_PROVIDER_ID=$(soat create-ai-provider \
--project-id "$PROJECT_ID" \
--name "Local Ollama" \
--provider "ollama" \
--default-model "qwen2.5:0.5b" | jq -r '.id')
echo "AI_PROVIDER_ID: $AI_PROVIDER_ID"
# AI_PROVIDER_ID: aip_8BTcGUvXnehCCQKs

Step 4 — Create an agent​

An agent binds a provider to a system prompt (instructions).

AGENT_ID=$(soat create-agent \
--project-id "$PROJECT_ID" \
--ai-provider-id "$AI_PROVIDER_ID" \
--name "Local Assistant" \
--instructions "You are a concise assistant running on a local Ollama model. Keep answers short (max 20 words), clear, and practical." \
| jq -r '.id')
echo "AGENT_ID: $AGENT_ID"
# AGENT_ID: agent_KO5nAMmsSOVBWLlN

Step 5 — Create a session​

A session is one conversation thread on an agent. With auto_generate: true every user message gets a reply automatically.

SESSION_RESP=$(soat create-session \
--agent-id "$AGENT_ID" \
--name "My first chat" \
--auto-generate true)
SESSION_ID=$(printf '%s' "$SESSION_RESP" | jq -r '.id')
CONV_ID=$(printf '%s' "$SESSION_RESP" | jq -r '.conversation_id')
echo "SESSION_ID: $SESSION_ID"
# SESSION_ID: sess_N0oEzsx3ayvgKwy3

Step 6 — Send messages and receive replies​

With auto_generate, add-session-message generates immediately and returns the reply inline, message being the stored assistant message; the model sees all previous messages. See Sessions.

7a — First message​

soat add-session-message \
--session-id "$SESSION_ID" \
--message "What is the capital of France?"

Example output:

{
"status": "completed",
"content": "The capital of France is Paris.",
"message": {
"role": "assistant",
"document_id": "doc_Qm4RtX8vLp2nZs7K",
"actor_id": null,
"agent_id": "agent_KO5nAMmsSOVBWLlN",
"position": 1,
"content": "The capital of France is Paris.",
"metadata": null,
"idempotency_key": null
},
"generation_id": "gen_mznGfHSV4YAGiBXy",
"trace_id": "trace_8rcvif0n29WE37NL",
"model": "qwen2.5:0.5b"
}

7b — Queue a follow-up message for async generation​

Disable auto_generate and add a follow-up message; Step 10 generates the reply asynchronously.

soat update-session \
--session-id "$SESSION_ID" \
--auto-generate false

soat add-session-message \
--session-id "$SESSION_ID" \
--message "In one short sentence, what is the population of Paris?"

Step 7 — View the conversation history​

Messages are persisted on the underlying Conversation; the session is a scoped view of it.

soat list-conversation-messages \
--conversation-id "$CONV_ID" | jq '.data[] | {role, content}'

Example output:

{ "role": "user", "content": "What is the capital of France?" }
{ "role": "assistant", "content": "The capital of France is Paris." }
{ "role": "user", "content": "What is the population of that city?" }
{ "role": "assistant", "content": "The population of Paris … approximately 2.1 million …" }

Step 8 - Start a local webhook listener​

Start the listener before creating the webhook; it prints each matching delivery. In automated tests SOAT_WEBHOOK_BASE_URL is injected so the server container can reach it. See CLI Commands for soat listen flags and Webhooks for delivery and signing.

WEBHOOK_BASE_URL=${SOAT_WEBHOOK_BASE_URL:-http://localhost:8787}
soat listen --port 8787 --path /webhook --filter sessions.generation_started,generations.* --json > session-webhooks.log 2>&1 &
LISTENER_PID=$!
sleep 2

Optional: pass --secret <webhook-secret> to validate X-Soat-Signature.


Step 9 - Create a session webhook subscription​

Subscribe to session generation events. Event types, retries and HMAC signing: Webhooks.

WEBHOOK_ID=$(soat create-webhook \
--project-id "$PROJECT_ID" \
--name "session-events" \
--url "$WEBHOOK_BASE_URL/webhook" \
--events '["sessions.generation_started","generations.*"]' | jq -r '.id')
echo "WEBHOOK_ID: $WEBHOOK_ID"

Step 10 - Trigger async generation​

Background execution is the default: the call returns 202 Accepted immediately (wait=true returns the reply inline). See Sessions — Background Generation for status codes and polling.

soat update-session \
--session-id "$SESSION_ID" \
--auto-generate false

soat add-session-message \
--session-id "$SESSION_ID" \
--message "Give me 1 concise fact about Sao Paulo."

soat generate-session-response \
--session-id "$SESSION_ID"

Expected immediate response (accepted):

{
"status": "accepted",
"session_id": "sess_..."
}

The soat listen terminal logs:

  • sessions.generation_started
  • generations.completed

Step 11 - Verify delivery and final assistant message​

Wait for the delivery, then fetch session messages again. Delivery records are queryable via Webhooks; status is queued, completed, or failed.

# → retry 30
soat list-webhook-deliveries --webhook-id "$WEBHOOK_ID" \
| jq -e '[.data[] | select(.status == "completed")] | length > 0'

soat list-webhook-deliveries \
--webhook-id "$WEBHOOK_ID" | jq '.data[] | {event_type, status, status_code}'

cat session-webhooks.log

soat list-conversation-messages \
--conversation-id "$CONV_ID" | jq '.data[] | {role, content}'

kill "$LISTENER_PID"
wait "$LISTENER_PID" 2>/dev/null || true

What's next​

  • Manual generation: a session without auto_generate plus soat generate-session-response --session-id … --wait true (omit --wait true for background).
  • Session tags: replace-session-tags / merge-session-tags attach filterable metadata.
  • Tools: attach builtin or HTTP tools to the agent — Agents module.