Skip to content

Embedding the runtime

Production is "just a model with logs." The dev machinery (eval, synth, coach, console) does not come along — you embed the lightweight sqbyl-runtime:

from sqbyl_runtime import load

agent = load("revenue-analytics.v1.json", db=env.DATABASE_URL, model="claude-opus-4-8")

@app.post("/ask")          # your API, your auth, your scaling
def ask(q: str):
    return agent.ask(q)    # → {plan, sql, rows, used_assets, usage, latency}

It inherits your app's auth, connection pooling, and observability. The model, key, and database are injected at load() — not baked into the release.

Provider selection

load() takes a provider argument (default "anthropic"). Pass provider="openai" for an OpenAI-built release, and set the matching key (ANTHROPIC_API_KEY or OPENAI_API_KEY). To route through a corporate proxy or AI gateway, pass base_url=.

sqbyl run <release> / sqbyl serve exist for non-Python callers and quick HTTP exposure, but are intentionally not hardened — don't put sqbyl serve on the open internet. The sanctioned production path is embedding the runtime in your own service.

Async & concurrency

agent.ask() is synchronous and blocking (an LLM round-trip plus DB queries), but a single loaded agent is safe to call concurrently — the DB engine pools per-thread connections, the provider client (Anthropic or OpenAI) is thread-safe, and trace writes are locked. So under a threadpool it serves concurrent requests correctly.

The one rule for an async server: run ask() off the event loop; don't call it inside an async def directly (that blocks the loop for the whole request).

# FastAPI: a sync endpoint is auto-run in a threadpool — this is the example above, correct.
@app.post("/ask")
def ask(q: str): ...

# From an async endpoint, offload explicitly:
from starlette.concurrency import run_in_threadpool

@app.post("/ask")
async def ask(q: str):
    return await run_in_threadpool(agent.ask, q)   # or asyncio.to_thread(agent.ask, q)

Bound concurrency (threadpool + DB pool size) as you would for any blocking workload. A native-async runtime (async provider client + async DB) isn't provided — the threadpool pattern is the supported path.

The release interface

A release is a single portable JSON — the agent's brain (semantics, instructions, examples, judge prompts, scorecard), never rows. It carries a schema_version; the runtime warns on model or schema mismatch at load(). A third party can read the release without sqbyl installed at all.