Skip to content

Run The Demo API

This tutorial uses the self-contained project under demo/api. It serves several LangGraph graphs through the OpenAI-compatible /v1 interface.

Prerequisites

  • Python 3.11 or newer
  • uv
  • Bash and Just 1.58.0 or newer
  • PostgreSQL (the included Compose service requires Docker)
  • A running demo gateway only if you call the LLM-backed graphs; it forwards their model calls upstream
  • A Hatchet deployment and client token only for background Responses

Start without an upstream model

Several deterministic graphs do not require provider credentials. Use the graph matrix to choose one and see its other dependencies.

Start PostgreSQL And The API

Prepare the demo
cp demo/.env.example demo/.env
just demo/up lgos-db --wait

Overlay the parent LGOS checkout without changing the demo lockfile:

just demo/api --editable

Run the published API container and its PostgreSQL dependency:

just demo/up lgos-demo-api
Demo environment settings

The server reads LGOS_POSTGRES_URI from the demo environment. Use .env.example for the supplied connection settings.

LLM-backed graphs call DEMO_API_OPENAI_CHAT_COMPLETIONS_MODEL through Chat Completions or DEMO_API_OPENAI_RESPONSES_MODEL through Responses, via the selected gateway using OPENAI_GATEWAY_BASE_URL and DEMO_GRAPH_GATEWAY_API_KEY. Local API, worker, and notebook recipes set the root to DEMO_GATEWAY_HOST_URL; Compose uses the gateway's service URL. The lgos-rag graph also reads DEMO_API_OPENAI_EMBEDDING_MODEL and answers from the docs of the installed LGOS release, or of the latest release for an unreleased checkout. The server-tool graph reads DEMO_API_WEB_SEARCH_BACKEND and DEMO_API_WEB_SEARCH_URL to choose its web-search execution backend. These settings and dependencies belong to the API project and are not installed with the library.

The direct lgos-demo-api base URL is http://localhost:3004/v1. Compose also runs the independent lgos-api-coding-agent service. Both APIs publish their graphs under lgos/ in the gateway; select lgos/coding-agent for the coding agent. The separate lgos-files-api project and image serve the central S3-backed Files API on port 3006. It is not mounted into either graph API; see its run guide and settings.

Inspect registered graphs:

curl http://localhost:3004/v1/models

Each demo graph publishes its API-owned description and feature names in the lightweight lgos list extension.

The complete model and requirement matrix is in Example Graphs.

Call A Graph

Call a registered graph
from openai import OpenAI

client = OpenAI(base_url="http://localhost:3004/v1", api_key="DUMMY")

response = client.responses.create(
    model="custom-input-output-context",
    input="Show me the custom adapter.",
    store=False,
    safety_identifier="demo-user",
)

print(response.output_text)

Try the citation graph:

response = client.responses.create(
    model="citation-events",
    input="Show me a cited answer.",
    store=False,
)

print(response.output_text)
print(response.output[0].content[0].annotations)

See Events And Citations for this graph's output and Citation ownership for the normative transport boundary.

Ask the RAG graph about LGOS. It answers from the published documentation of the installed release, with real-time token streaming:

stream = client.responses.create(
    model="lgos-rag",
    input="How does LGOS streaming work?",
    store=False,
    stream=True,
)

for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)

See LGOS RAG for its plan, retrieve, and answer nodes, client settings, and process-local index lifetime.

Start with the dependency-free MCP-shaped example:

response = client.responses.create(
    model="mcp-mock",
    input="What is the weather in Istanbul?",
    store=False,
)

print(response.output_text)

mcp-mock uses a stand-in client and deterministic fake model, so it teaches async tool discovery and the agent tool loop without requiring an MCP server, gateway, database, or provider credential. See Core Graph Patterns.

The real mcp-postgres graph expects its OpenAI client to discover and execute tools through the selected gateway's MCP endpoint. Use the maintained Chainlit or Open WebUI client for the complete native tool loop; see PostgreSQL Through Native MCP. deep-research uses the same client-owned loop for any tools authorized by the gateway; mcp-postgres remains the narrower database-focused example.

Try the deterministic status-event showcase:

stream = client.responses.create(
    model="status-events",
    input="Prepare the media workflow.",
    store=False,
    stream=True,
    safety_identifier="demo-user",
)

phases = {}
for event in stream:
    if event.type == "response.output_item.added" and event.item.type == "message":
        phases[event.output_index] = event.item.phase
    elif event.type == "response.output_text.done":
        print(f"{phases[event.output_index]}: {event.text}")

See Events And Citations for the status and citation flows and their client behavior.

Try the deterministic response-outcome showcase:

refusal = client.responses.create(
    model="response-outcomes",
    input="refusal",
    store=False,
)
print(refusal.status, refusal.output[0].content[0].refusal)

stream = client.responses.create(
    model="response-outcomes",
    input="incomplete",
    store=False,
    stream=True,
)
for event in stream:
    if event.type == "response.incomplete":
        print(event.response.incomplete_details.reason)

See Core Graph Patterns for when a refusal differs from an incomplete response and which terminal events clients must handle.

For background Responses, set LGOS_BACKGROUND=hatchet, start the independent just demo/background-worker process, or run the complete UI path with the background Compose profile and just demo/compose. deep-research, background-mock, background-interrupt, and nested-subgraphs support it. Chainlit and Open WebUI expose polling through either bundled gateway. See Background Mock for basic execution and Background Interrupt for approval and resumption.

Background Python Client

The background graph examples share this direct API client and polling helper. Run this setup first with the API and background worker running, then choose a create/poll/cancel example or the review/resume example.

import time

from openai import OpenAI

client = OpenAI(base_url="http://localhost:3004/v1", api_key="DUMMY")


def poll(response):
    deadline = time.monotonic() + 120
    while response.status in {"queued", "in_progress"}:
        if time.monotonic() >= deadline:
            raise TimeoutError(f"Still running: {response.id}")
        time.sleep(1)
        response = client.responses.retrieve(response.id)
    if response.status != "completed":
        raise RuntimeError(f"{response.status}: {response.error}")
    return response

poll returns a completed Response or raises with its terminal status and error. The timeout stops local polling; it does not cancel the background work. The examples close the client with with client:. Rerun the setup before running another example.

For gateway URLs, model naming, and credentials, use the OpenAI proxy guide.

Try A Demo Client

The demo includes optional Chainlit and Open WebUI clients. The Compose stack routes both through the LiteLLM or Bifrost gateway selected by OPENAI_GATEWAY_TYPE, never directly to an API or Files container. See Demo Architecture for the shared request and ownership flows, then use each client guide for its adapter-specific behavior.

Next Steps