cilow — Python
The Python client for Cilow, the verifiable context layer for AI agents. Point it at a
running cilow-serve and call four core verbs —
ingest, context_pack, forget, explain — the same four the MCP core profile advertises.
The headline feature is calibrated deferral: every read tells you when the engine
doesn't know instead of fabricating an answer.
Install
cd sdk/python && pip install -e .
Python 3.9+. Zero required dependencies — the client uses only the standard library
(urllib + json). httpx is an optional extra (pip install 'cilow[httpx]') for users
who want pooling/async; it is not needed for the default client.
Point it at a server
cargo build --profile fast --features serve --bin cilow-serve
CILOW_DB=/tmp/cilow.log CILOW_TOKENS="demo:1:1" ./target/fast/cilow-serve
# listening on http://0.0.0.0:8080 · bearer token "demo"
The token maps to a tenant scope that the server stamps onto every request — a client can
never read another tenant's memory, and a client-supplied scope is ignored over HTTP.
Five lines
from cilow import CilowClient
cilow = CilowClient(base_url="http://localhost:8080", token="demo")
cilow.ingest(facts=[{"subject": "Maya", "predicate": "employer", "object": "Acme"}])
r = cilow.context_pack("where does Maya work", anchor="Maya", attribute="employer")
print(r.reason if r.abstained else r.pack) # the injectible pack, citations on r.citations
print(cilow.explain(r.trace_id).report.grounded) # why it surfaced, vs the tenant threshold
The four core verbs
| Verb | Signature | Returns | What it does |
|---|---|---|---|
ingest |
ingest(*, facts= | text= | url= | content_base64=, mime=, subject_hint=, metadata=, source=, raw_context=, branch=, scope=, source_id=, observed_at=) |
IngestResponse |
The one write door. Never rejects: known kinds extract, unknown kinds are retained and recallable. A new value for an exclusive attribute supersedes the old one (history stays queryable). |
context_pack |
context_pack(query, *, scope=, now=, as_of=, anchor=, attribute=) |
ContextPackResponse |
The product read: a ready-to-inject pack, structured citations / claims with is_current, and the faithfulness fields abstained / reason_code / grounding / pack_sufficient / as_of / trace_id. No free-form synthesis. |
forget |
forget(entity, *, attribute=, value=, erase=, scope=, at=) |
ForgetResponse |
Withdraw, not erase history: recall stops returning it; as-of-T reads before at still see it. erase=True is the destructive right-to-be-forgotten path. |
explain |
explain(trace_id, *, scope=) |
ExplainResponse |
Why a pack/recall surfaced what it surfaced: the cited claims (labels resolved, inclusion probability each), best_nonconformity vs the tenant threshold q_hat, and report.grounded. found=False + error is in-band. |
Plus health() (liveness probe, no auth). All responses are frozen dataclasses.
ingest takes exactly one content form. source_id (the idempotency key the wire requires)
defaults to a content hash — ingest_source_id(...) — so re-sending the same payload is a
no-op; the key actually sent is echoed on .source_id. .kind tells you which server path ran
("facts" → the remember receipt on .remember; "content" → the raw-durable receipt on
.content with blob_id / detected / reader_error).
anchor (an entity surface form like "Maya") + attribute (a predicate like "employer")
take the exact-key fast path. Omit them for free-text semantic reads — same response shape.
Full profile / legacy aliases
The engine's full tool profile (CILOW_MCP_PROFILE=full) also advertises the older verbs, and
the client keeps them as aliases with their original receipts:
| Alias | Signature | Returns |
|---|---|---|
remember |
remember(facts, *, scope=, source=, source_id=, observed_at=, raw_context=, branch=) |
RememberResponse |
recall |
recall(query, *, scope=, as_of=, now=, anchor=, attribute=, branch=) |
RecallResponse |
answer |
answer(query, *, scope=, now=, anchor=, attribute=, branch=) |
AnswerResponse |
timeline |
timeline(entity, *, scope=, at=, t_from=, t_to=) |
list[TimelineEntry] |
track_entity |
track_entity(name, *, scope=, at=) |
TrackEntityResponse |
Also available: remember_text, ingest_image / ingest_audio / ingest_pdf /
ingest_structured, save / ingest_any, feedback, consolidate, fork / branch_list,
blob_get / blob_meta / blob_list, and the client.memories.* id-based REST surface.
Time is in microseconds
Every time field (observed_at, as_of, now, at, t_from, t_to, and the valid_from
/ valid_until on timeline entries) is epoch microseconds (µs), matching the engine. They
default to "now". Use the exported now_micros() helper:
from cilow import now_micros
t = now_micros()
cilow.remember(facts, observed_at=t)
past = cilow.recall("...", as_of=t - 86_400_000_000, now=t) # a day ago
The deferral contract
context_pack (and the recall / answer aliases) never raise for a deferral — they return
a normal response with a boolean flag. When abstained is True, the pack / claims / citations
are empty and reason_code says why: below_threshold / no_candidates are a calibrated
abstain (memory genuinely lacks grounded context — do not fabricate past it), while
provider_unavailable is an infrastructure abstain (retry when the provider recovers). Branch
with r.is_calibrated_abstain / r.is_infrastructure_abstain rather than matching strings.
On answer / context_pack an abstain also names which layer stopped it — r.terminal_layer
(structural | gate | provider | reader | verifier; r.stopped_at is the same thing gated on
abstained). Only provider is retryable. Both surfaces also carry r.evidence: the claim, span,
and verbatim lanes collapsed into one list ordered by fused cross-lane rank (Evidence.origins
says which lanes agreed; Evidence.structure is present when a typed claim backs the unit). It is
present on abstains too — the raw lanes have no standing at the gate, but you may still read them.
r = cilow.context_pack("what is Maya's salary", anchor="Maya", attribute="salary")
if r.abstained:
# r.pack is empty; r.reason explains the configured gate decision.
return f"I don't have that in memory ({r.reason})."
return r.pack
Every read carries a trace_id. Hand it to explain for the audit trail — the cited claims,
the tenant's conformal threshold q_hat, the best candidate's best_nonconformity, and the
margin (q̂ − n, ≥0 grounded, <0 a gap) — or to feedback so memory learns from the outcome.
This is the difference between "I don't know" and a confident wrong answer.
Errors
CilowError is raised for genuine failures — auth (401 / JSON-RPC -32001), bad params,
transport errors, malformed responses. A deferral is not an error.
from cilow import CilowError
try:
cilow.recall("...")
except CilowError as e:
if e.is_unauthorized: # bad/expired token
...
Develop
pip install -e .
python3 -m pytest -q tests # mocks the HTTP layer (no server needed)
python examples/quickstart.py # runs against a live server
Metadata
Release files for cilow 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| cilow-0.1.0.tar.gz | 53.6 kB | Without provenance | Details for cilow-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| cilow-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for cilow-0.1.0-py3-none-any.whl |
Total release size: 96.7 kB
Release files / cilow-0.1.0.tar.gz
| Download URL | cilow-0.1.0.tar.gz |
|---|---|
| Size | 53.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ceda263601355e655f224d59deaf0f944ea36f08593feb8d4a7ffd761f83a29
|
|
BLAKE2b-256 checksum How to use checksums |
070ef597c06367db600be6f82482643fe555386c46af776af0b27d646f448a35
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.5
|
Release files / cilow-0.1.0-py3-none-any.whl
| Download URL | cilow-0.1.0-py3-none-any.whl |
|---|---|
| Size | 43.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ef9015fa19586dd681a9f6c64da5812631089ce589f66cd5d1cca444bacb0dd9
|
|
BLAKE2b-256 checksum How to use checksums |
820b449570319ba32147d82b37c0d4d08a0ed355be634672a8215ddf472582c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.5
|
Security for cilow 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.