Skip to main content

Python client for StixDB — the agentic context database where the agent lives inside.

Project description

stixdb-sdk — Python Client for StixDB

Lightweight, ergonomic Python client for the StixDB Agentic Context Database. Covers the full REST API surface in both synchronous and asynchronous flavours.

Tip: Use stixdb-sdk for memory management, ingestion, search, and Q&A. For streaming chat or drop-in OpenAI replacement, use the standard openai client pointed at http://your-stix-server/v1 (see OpenAI Compatibility).


Installation

pip install stixdb-sdk

Or from source:

pip install -e sdk/   # from the repo root

Requires Python 3.10+ and a running StixDB server (stixdb serve --port 4020).


Quick Start

from stixdb_sdk import StixDBClient

with StixDBClient(base_url="http://localhost:4020", api_key="your-key") as client:

    # Store a memory
    client.memory.store("my_agent", content="Launch is June 1st, 2026",
                        node_type="fact", tags=["deadline"], importance=0.9)

    # Ingest a whole folder of documents
    client.memory.ingest_folder("my_agent", folder_path="./docs", recursive=True)

    # Search — ranked, filterable, no LLM needed
    results = client.search.create("launch deadline", collection="my_agent", max_results=5)
    for hit in results["results"]:
        print(f"[{hit['score']:.3f}] {hit['snippet']}")

    # Ask — LLM reasoning over the graph
    answer = client.query.ask("my_agent", question="When is the launch and who owns it?")
    print(answer["answer"])
    print(answer["reasoning_trace"])

Client Initialisation

from stixdb_sdk import StixDBClient, AsyncStixDBClient

# Sync
client = StixDBClient(
    base_url="http://localhost:4020",  # default
    api_key="your-key",               # sets Authorization: Bearer <key>
    timeout=60.0,                     # request timeout in seconds
)

# Async
client = AsyncStixDBClient(
    base_url="http://localhost:4020",
    api_key="your-key",
    timeout=60.0,
)

# Both support context managers
with StixDBClient(...) as client:
    ...

async with AsyncStixDBClient(...) as client:
    ...

Memory — client.memory

store — Add a single node

result = client.memory.store(
    collection="my_agent",
    content="Alice is the lead engineer on the payments team.",
    node_type="entity",          # fact | entity | event | concept | procedure | summary
    tier="episodic",             # working | episodic | semantic | procedural | archived
    importance=0.8,              # 0.0 – 1.0; affects retrieval ranking and decay
    source="onboarding-doc.pdf", # optional provenance label
    tags=["team", "payments"],
    metadata={"department": "engineering"},
    pinned=False,                # True = never pruned by the background agent
)
print(result["id"])  # UUID of the new node

bulk_store — Add many nodes in one call

items = [
    {"content": "Sprint 4 kicked off", "node_type": "event", "tags": ["sprint"]},
    {"content": "Payments module uses event sourcing", "node_type": "concept"},
]
result = client.memory.bulk_store("my_agent", items=items)
print(f"Stored {result['stored']} nodes")

upload — Upload a single file

Supported: .pdf, .txt, .md, .json, .yaml, .py, .js, .ts, .csv, .sql and more.

result = client.memory.upload(
    collection="my_agent",
    file_path="./roadmap.pdf",
    tags=["roadmap", "planning"],
    chunk_size=1000,     # characters per chunk
    chunk_overlap=200,   # overlap between chunks
    parser="auto",       # auto | legacy | docling
)
print(f"Ingested {result['chunks_created']} chunks from {result['filename']}")

PDF ingestion preserves page-level provenance — each chunk stores its source page number in metadata.

ingest_folder — Upload an entire directory

result = client.memory.ingest_folder(
    collection="docs",
    folder_path="./knowledge_base",
    recursive=True,
    tags=["v2-docs"],
    chunk_size=1000,
    chunk_overlap=200,
    parser="auto",
)
print(f"Processed {result['files_processed']} files, {result['chunks_created']} chunks")

list — List nodes in a collection

nodes = client.memory.list(
    collection="my_agent",
    tier="working",           # filter by tier (optional)
    node_type="fact",         # filter by type (optional)
    limit=50,
    offset=0,
)
for node in nodes:
    print(node["content"], node["tier"], node["importance"])

get — Retrieve a single node by ID

node = client.memory.get("my_agent", node_id="uuid-here")
print(node["content"], node["decay_score"], node["access_count"])

delete — Delete a single node

client.memory.delete("my_agent", node_id="uuid-here")

delete_collection — Wipe an entire collection

client.memory.delete_collection("temp_experiments")  # irreversible

Query — client.query

ask — Agentic Q&A with LLM reasoning

Runs the full 7-phase retrieval pipeline: embed → vector search → graph BFS → re-rank → truncate → LLM reason → record.

response = client.query.ask(
    collection="my_agent",
    question="Who should I contact about the payments deadline?",
    top_k=15,         # max semantic candidates
    threshold=0.25,   # minimum cosine similarity
    depth=2,          # graph BFS depth from seed nodes
)

print(response["answer"])
# → "Alice, lead engineer on the payments team. Deadline is June 1st, 2026."

print(response["confidence"])          # 0.0 – 1.0
print(response["reasoning_trace"])     # step-by-step reasoning
for source in response["sources"]:
    print(source["content"], source["tier"], source["score"])

With a custom system prompt and structured output:

response = client.query.ask(
    collection="my_agent",
    question="Summarise all deadlines as a JSON list",
    system_prompt="You are a project management assistant. Be concise.",
    output_schema={"type": "array", "items": {"type": "string"}},
)

retrieve — Raw retrieval, no LLM

Returns ranked nodes without generating an answer. Use this when you want to feed context into your own pipeline.

nodes = client.query.retrieve(
    collection="my_agent",
    query="upcoming deadlines",
    top_k=10,
    threshold=0.25,
    depth=1,
)
for node in nodes:
    print(f"[{node['score']:.3f}] {node['content']}")

Search — client.search

Product-style search across one or more collections — no LLM, just ranked results.

Basic search

results = client.search.create(
    query="project deadline",
    collection="my_agent",
    max_results=5,
)
for hit in results["results"]:
    print(f"[{hit['score']:.3f}] {hit['snippet']}")

Multi-query, cross-collection

results = client.search.create(
    query=["launch deadline", "team contacts"],   # multiple queries
    collections=["project", "hr"],                # multiple collections
    max_results=5,
)
# results["results"] is grouped by query, in order

Filtering

results = client.search.create(
    query="climate risk analysis",
    collection="research",
    max_results=10,
    # Source filtering
    source_filter=["paper.pdf", "briefing.md"],  # allowlist
    # source_filter=["-slack-export.json"],       # denylist (prefix with -)
    # Node filtering
    tag_filter=["climate", "risk"],
    node_type_filter=["fact", "summary"],
    tier_filter=["working", "episodic"],
    # Sizing
    max_chars_per_result=800,
    include_metadata=True,
)

Heat-based ranking

results = client.search.create(
    query="project status",
    collection="my_agent",
    sort_by="heat",          # relevance | heat | hybrid
    include_heatmap=True,    # adds recency, frequency, decay, temperature per result
)
for hit in results["results"]:
    print(hit["score"], hit.get("heatmap"))

OpenAI Compatibility

StixDB exposes a /v1 endpoint compatible with the OpenAI API. Use the standard openai library for streaming chat, or any tool that speaks the OpenAI protocol.

from openai import OpenAI

# Point to your StixDB server
openai_client = OpenAI(
    base_url="http://localhost:4020/v1",
    api_key="your-stix-api-key",
)

# Collection name = model name
stream = openai_client.chat.completions.create(
    model="my_agent",
    messages=[{"role": "user", "content": "What are the upcoming milestones?"}],
    stream=True,
    extra_body={
        "thinking": True,   # show agent reasoning steps in the stream
        "verbose": True,    # show retrieval progress
    },
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)

List available collections as models:

models = openai_client.models.list()
for m in models.data:
    print(m.id)

Async Usage

import asyncio
from stixdb_sdk import AsyncStixDBClient

async def main():
    async with AsyncStixDBClient(base_url="http://localhost:4020", api_key="your-key") as client:

        health = await client.health()
        print(health["status"])

        await client.memory.store("demo", content="Async is fast", node_type="fact")

        results = await client.search.create(
            ["launch deadline", "team contacts"],
            collections=["project", "hr"],
            max_results=5,
            sort_by="hybrid",
        )

        answer = await client.query.ask("project", question="When is the launch?")
        print(answer["answer"])

asyncio.run(main())

Error Handling

The SDK uses httpx internally. All non-2xx responses raise httpx.HTTPStatusError.

import httpx

try:
    client.query.ask("missing_collection", question="Hello?")
except httpx.HTTPStatusError as e:
    print(f"HTTP {e.response.status_code}: {e.response.text}")

Full API Reference

Namespace Method Description
client health() Server health check
client.memory store(collection, content, ...) Add a single node
client.memory bulk_store(collection, items) Add many nodes
client.memory upload(collection, file_path, ...) Ingest a file
client.memory ingest_folder(collection, folder_path, ...) Ingest a directory
client.memory list(collection, ...) List nodes
client.memory get(collection, node_id) Get a single node
client.memory delete(collection, node_id) Delete a node
client.memory delete_collection(collection) Wipe a collection
client.query ask(collection, question, ...) Agentic Q&A
client.query retrieve(collection, query, ...) Raw retrieval
client.search create(query, collection, ...) Search

All methods have async equivalents on AsyncStixDBClient.


Related

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stixdb_sdk-0.1.0.tar.gz (9.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stixdb_sdk-0.1.0-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

Details for the file stixdb_sdk-0.1.0.tar.gz.

File metadata

  • Download URL: stixdb_sdk-0.1.0.tar.gz
  • Upload date:
  • Size: 9.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.7

File hashes

Hashes for stixdb_sdk-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ddf4349f301df5cbcf0fd3b1b16d464c952e6912599f4f1f851e5bdf887e1b90
MD5 e6c10dc6bcbf78b0d430bbadb455e267
BLAKE2b-256 20ac98c140a27e39159977cff7d8dfa40afcbd002c7db0cfa4f10e9167c8e94e

See more details on using hashes here.

File details

Details for the file stixdb_sdk-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: stixdb_sdk-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.7

File hashes

Hashes for stixdb_sdk-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bd8ef3f0bf4c5be787eeeec42fe5be7dfa4477d724583eec5c632d9744cf80e5
MD5 278f13f679ad93967c9ea0feb698093a
BLAKE2b-256 c5c24955d2f4468346f216096c9ffeb0920164af35399e5ab6475837d0f0dc35

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page