Run The LGOS Server¶
lgos serve runs a graph registry as a complete application: JSON logging,
OpenTelemetry, Langfuse, CORS, interrupt persistence and expiry, and background
Responses. Your project supplies only the graphs.
To add authentication, custom routes, or middleware, extend the application or use the library API in your own FastAPI application.
Registry Factory¶
Point the command at a function, in module:attribute form, that receives the
server's ServerResources and returns a GraphRegistry:
from langgraph_openai_serve import GraphConfig, GraphFeature, GraphRegistry
from langgraph_openai_serve.server import ServerResources
from my_app.graphs import build_approval_graph, chat_graph
def create_registry(resources: ServerResources) -> GraphRegistry:
return GraphRegistry(
graphs={
"chat": GraphConfig(graph=chat_graph, description="Answer questions."),
"approval": GraphConfig(
graph=build_approval_graph(resources.checkpointer),
description="Pause for approval.",
features={GraphFeature.INTERRUPTS},
),
},
run_coordinator=resources.run_coordinator,
)
ServerResources carries the checkpointer and store to compile graphs with,
and the run_coordinator that leases interrupt runs. Compile only
interrupt-enabled graphs with the checkpointer. When graphs own clients that
must be closed, make the factory an asynccontextmanager that yields the
registry; the server keeps it open for the process lifetime.
Like Uvicorn, the command imports from the working directory first, so an
installed package and a local module both work, and LGOS_REGISTRY can name the
factory instead of the argument, for example once in a container image.
HTTP Server¶
lgos serve is Uvicorn's command with the
registry in place of the application. Every Uvicorn option applies through its
flag or its UVICORN_* variable, and so do WEB_CONCURRENCY and
FORWARDED_ALLOW_IPS. lgos serve --help lists them.
lgos serve my_app.registry:create_registry --port 8080 --reload
UVICORN_HOST=0.0.0.0 UVICORN_WORKERS=4 lgos serve my_app.registry:create_registry
LGOS changes two defaults: it writes structured logs,
which --log-config replaces, and turns off access logs, which --access-log
enables. --env-file loads LGOS_* settings, but as with UVICORN_*
variables, name the registry on the command line or in the process environment.
Each Uvicorn worker is a separate process, like a replica. Workers need a shared
LGOS_POSTGRES_URI for interrupts and LGOS_BACKGROUND=hatchet for background
Responses, and each opens its own LGOS_POSTGRES_POOL_SIZE connections.
lgos serve refuses to start more than one worker with
LGOS_BACKGROUND=memory.
Persistence And Background Work¶
Without LGOS_POSTGRES_URI, the checkpointer, store, and run coordinator live
in process memory: paused runs end with the process and only one process can
serve them. Set LGOS_POSTGRES_URI for durable interrupts across restarts and
replicas. The server applies LangGraph's checkpoint and store migrations at
startup under an advisory lock, and deletes paused runs older than
LGOS_INTERRUPT_TTL_MINUTES.
LGOS_BACKGROUND selects the background engine:
Background requests are rejected. This is the default.
Runs execute in the server process and are lost when it stops. Use it for development and tests.
The API submits runs to Hatchet, and a separate worker executes them:
The worker runs the same factory, so both processes serve one catalog.
Interrupt runs that move between them need a shared LGOS_POSTGRES_URI.
For container health checks, set Hatchet's
HATCHET_CLIENT_WORKER_HEALTHCHECK_ENABLED=true: the worker then serves
/health on port 8001,
which returns 200 once it is connected to Hatchet.
Logs And Telemetry¶
Both commands write JSON records to stdout, or readable lines when stdout is a
terminal. Records from LGOS, Uvicorn, Hatchet, and the registry's top-level
package appear at INFO; other loggers at WARNING. Every record handled
during a request carries the LGOS request fields,
including records from graph nodes.
Launch either command with opentelemetry-instrument to export traces,
metrics, and logs through the standard OTEL_* variables:
Set LGOS_ENABLE_LANGFUSE=True and the LANGFUSE_* credentials to trace graph
runs in Langfuse.
Settings¶
The server reads these from the process environment, alongside the
package settings, UVICORN_*,
HATCHET_CLIENT_*, LANGFUSE_*, and OTEL_*. An empty value counts as unset.
In .env files that Just or uv run --env-file load, single-quote JSON lists,
as in LGOS_CORS_ORIGINS='["https://app.example.com"]'; those loaders strip
unquoted double quotes.
| Variable | Default | Purpose |
|---|---|---|
LGOS_REGISTRY |
unset | Registry factory used when the command names none |
LGOS_CORS_ORIGINS |
[] |
JSON list of allowed browser origins |
LGOS_POSTGRES_URI |
unset | PostgreSQL for checkpoints, store, and run leases |
LGOS_POSTGRES_POOL_SIZE |
5 |
Connections per process; one serves checkpoints and each running interrupt holds one of the rest |
LGOS_INTERRUPT_TTL_MINUTES |
43200 |
Age at which paused runs are deleted |
LGOS_INTERRUPT_SWEEP_INTERVAL_MINUTES |
5 |
Interval between expiry sweeps; 0 disables them |
LGOS_BACKGROUND |
none |
none, memory, or hatchet |
LGOS_HATCHET_WORKER_SLOTS |
4 |
Concurrent worker runs; with PostgreSQL, lgos worker needs it below LGOS_POSTGRES_POOL_SIZE |
The command has no built-in authentication. Put a gateway in front of it, or extend the application.
Extend The Application¶
create_app returns the FastAPI application that lgos serve runs. To add
routes, middleware such as the API key middleware, or
other FastAPI features, extend it in your own module:
from langgraph_openai_serve.server import create_app
from my_app.auth import APIKeyMiddleware
from my_app.registry import create_registry
from my_app.routes import router
app = create_app(create_registry)
app.add_middleware(APIKeyMiddleware)
app.include_router(router)
Serve the module with Uvicorn, or any other ASGI server:
Uvicorn then applies its own logging configuration. To keep JSON records with
the LGOS request fields, pass a --log-config that follows
production logging.
Test The Application¶
create_app builds the same application in tests. Pass explicit settings so
variables from a developer's .env cannot reach the test:
from langgraph_openai_serve.server import ServerSettings, create_app
app = create_app(
create_registry,
settings=ServerSettings(POSTGRES_URI=None, BACKGROUND="memory"),
)
async with app.router.lifespan_context(app):
... # Call app through httpx2.ASGITransport.
The starter template generates a project that uses this layout.