SDK reference
The SDK lives in owlgraph_core.sdk. It’s a thin HTTP wrapper around the platform’s retrieve endpoint — the same surface the owlgraph CLI uses, so behavior matches exactly.
Install
Section titled “Install”pip install owlgraph-corePython 3.12+. Runtime deps: httpx. No build step.
Quick reference
Section titled “Quick reference”| Symbol | Purpose |
|---|---|
connect(database_id, *, api_key=None, url=None, timeout=120.0) | Sugar for Database(...). Most callers use this. |
Database | Sync client. Holds an httpx pool; reuse the instance. |
AsyncDatabase | Async sibling. Use under asyncio. |
RetrievalResult | Frozen dataclass — what retrieve() returns. |
Passage | One supporting passage on a result. |
TrajectoryStep | One step in the inner agent’s reasoning trace. |
OWLGraphError | Base class for SDK errors. |
AuthError | 401 / 403 — API key missing, invalid, or revoked. |
RateLimitError | 429 — quota exhausted. Carries retry_after. |
connect(...)
Section titled “connect(...)”from owlgraph_core import sdk as owl
db = owl.connect( database_id="abc-123-...", api_key="sk-owl-...", # optional if OWLGRAPH_API_KEY is set url="https://api.owlgraph.ai", # default; override for staging timeout=120.0, # seconds)Returns a Database. Holds a long-lived httpx.Client — keep the instance around for your app’s lifetime, or use as a context manager:
with owl.connect(database_id="...") as db: result = db.retrieve("...")# sockets closed hereConfiguration precedence
Section titled “Configuration precedence”- Arguments to
connect()/Database(...) OWLGRAPH_API_KEY/OWLGRAPH_PLATFORM_URLenvironment variables- (Coming soon)
~/.config/owlgraph/config.toml— written byowlgraph auth login
db.retrieve(question, *, module=None, trace="summary")
Section titled “db.retrieve(question, *, module=None, trace="summary")”The core method. Runs an ontology-aware retrieval and returns a structured result.
result = db.retrieve( "Which suppliers ship to states where we're licensed?", module="aragog", # override the DB's configured RAG module for this call trace="full", # "summary" (default) or "full" (verbose trajectory))
print(result.answer)print(f"{result.loops} loops, ${result.cost_usd:.4f}")
for p in result.passages: print(p.source, p.text[:200])
for step in result.trajectory: print(step.tool, step.arguments)Arguments
Section titled “Arguments”question(str) — the user-facing question. The inner agent rewrites it as needed.module(str, optional) — overrides the database’s configured RAG module for this call. One ofnaive,owl,arag,aragog. Leave unset to use the DB default.trace(“summary” | “full”) — controls trajectory verbosity. Default"summary".
Returns: RetrievalResult
Section titled “Returns: RetrievalResult”| Field | Type | Notes |
|---|---|---|
answer | str | None | Free-text answer. None if the agent couldn’t answer in budget. |
passages | list[Passage] | Source passages cited. Each has source, text, optional chunk_index and score. |
trajectory | list[TrajectoryStep] | Inner agent’s tool calls. Each has tool, arguments, optional result_summary. |
cost_usd | float | Approximate LLM cost in USD. |
loops | int | How many outer-LLM iterations the agent took. |
module_used | str | Which RAG module actually ran (after any override). |
metadata | dict | Module-specific debug info (e.g., pruned_by_disjointness_total for ARAGOG). |
import asynciofrom owlgraph_core import sdk as owl
async def main(): async with owl.AsyncDatabase(database_id="...") as db: result = await db.aretrieve("Who invented Hawaiian pizza?") print(result.answer)
asyncio.run(main())Same arguments, same return type. Use under asyncio when you want non-blocking I/O — particularly handy when fanning out many concurrent queries.
Error handling
Section titled “Error handling”from owlgraph_core.sdk import AuthError, RateLimitError, OWLGraphError
try: result = db.retrieve("...")except AuthError as e: # 401 / 403 — refresh credentials, fail fast raiseexcept RateLimitError as e: # 429 — back off time.sleep(e.retry_after or 30)except OWLGraphError as e: # everything else — e.status_code + e.body have details raiseAll SDK errors carry status_code and a parsed body (when the server returned JSON).
Patterns
Section titled “Patterns”Inject as a tool into your agent
Section titled “Inject as a tool into your agent”The SDK doesn’t ship MCP tool definitions yet — for now, wrap retrieve in your own tool call:
def retrieve_tool(question: str) -> str: result = db.retrieve(question) return result.answer or "No answer found."Then hand retrieve_tool to your agent framework of choice (LangChain, Pydantic AI, raw Anthropic / OpenAI tool calling). The platform’s hosted MCP endpoint (POST /mcp) is the alternative when your agent already speaks MCP.
Eval loop
Section titled “Eval loop”from concurrent.futures import ThreadPoolExecutor
def score(q, expected): r = db.retrieve(q) return q, expected, r.answer, r.cost_usd
with db, ThreadPoolExecutor(max_workers=8) as pool: rows = list(pool.map(lambda qe: score(*qe), eval_set))A single Database instance is thread-safe (its httpx.Client pools connections internally). For asyncio-native concurrency, use AsyncDatabase and asyncio.gather.
Streaming chat
Section titled “Streaming chat”The SDK doesn’t expose the SSE chat endpoint yet (Phase B follow-up). For now, hit POST /api/v1/databases/<id>/chat/conversations/<cid>/messages directly — see the HTTP API reference.