Streaming

Receive an answer as server-sent events, and parse the final metadata line correctly.

Add ?stream=true to POST /v1/agents/{agent_id}/chat (or to the follow-up route) to receive the answer as text/event-stream instead of one JSON body.

What streaming does — and does not — change

The agent still works out the whole answer first: reading the schema, writing and running SQL. Streaming then sends the finished text one sentence at a time, so a chat interface can start drawing words immediately instead of waiting for the full body. It does not make the first byte arrive sooner than a non-streamed answer, so use it for presentation, not for latency.

Wire format

The stream is a series of blank-line-separated lines:

data: {"chunk": "There are **2,400 customers**.", "session_id": "184b72b3-625c-4c10-93ec-621f2e154e00"}

full_metadata: {"response": "There are **2,400 customers**.", "session_id": "184b72b3-…", "chat_id": "1008c506-…", "follow_up": […], "is_answered": true, "debug_info": null, "chart_output": null, …}
  • data: lines carry chunk — the next piece of the answer text — and the session_id.
  • The last line starts with full_metadata: and carries the complete JSON answer, exactly as the non-streaming response would return it (response, chat_id, debug_info, chart_output, …).
  • If the request fails after the stream has started, the HTTP status is still 200 and the last line is full_metadata: {"error": "…", "code": "…"}.

Reading the stream

Python
import json
import os
import requests

API_ROOT = "https://futuresmart.ai/database-agent/api"
AGENT_ID = os.environ["DATABASE_AGENT_ID"]

with requests.post(
    f"{API_ROOT}/v1/agents/{AGENT_ID}/chat",
    params={"stream": "true"},
    headers={"X-API-Key": os.environ["DATABASE_AGENT_API_KEY"]},
    json={"query": "How many customers are there?"},
    stream=True,
    timeout=180,
) as r:
    r.raise_for_status()
    final = None
    for line in r.iter_lines(decode_unicode=True):
        if line.startswith("data: "):
            print(json.loads(line[6:])["chunk"], end=" ", flush=True)
        elif line.startswith("full_metadata: "):
            final = json.loads(line[len("full_metadata: "):])

print()
if final is None or "error" in final:
    raise SystemExit(f"Stream failed: {final}")
print("session:", final["session_id"], "answered:", final["is_answered"])

-N turns off cURL's output buffering so each line prints as it arrives.

Errors before the stream starts

Authentication, permission, validation and quota errors (401, 403, 404, 422, 429) are returned as normal JSON responses with that status code before any event is sent — check the status first, as the samples do. See Errors and limits.