Quesen Python SDK
Official typed Python client for Quesen — the deterministic A2A risk-evaluation API.
Status: v0.3.0 · tracks Quesen engine v1.10.0 (+ TSC v2 agent firewall) · backward compatible with v1.0.0+ deployments.
Developer portal: senueren.co.za/quesen — canonical docs, API reference, and integration guides. This SDK is a thin HTTP client; the hosted engine is served at https://web-production-aa5ba.up.railway.app.
Install
pip install quesen-sdk
Python 3.9+, single runtime dependency (httpx).
30-second usage
from quesen_sdk import QuesenClient
client = QuesenClient(
base_url="https://<your-quesen-endpoint>",
api_key="sk_live_abc", # optional if the deployment is in open mode
)
result = client.validate(
domain_age_days=1,
engagement_ratio=0.95,
scam_keyword_count=4,
)
print(result.decision) # 'SKIP'
print(result.risk_score) # 1.0
print(result.conflict_triggers) # ['New domain (<=30d) + unusually high engagement (>=0.50)', ...]
print(result.request_id) # UUID — pass to client.report() later
print(result.input_snapshot_hash) # 64-char SHA-256 hex — self-contained replay primitive (v1.10+)
print(result.commit_sha) # 40-char git SHA of the engine ruleset that produced the verdict (v1.10+)
Agent Firewall (TSC v2) — one call before any high-risk action
TSC v2 turns Quesen into a deterministic agent firewall: describe what your
autonomous agent is about to do and get a PASS / REVIEW / BLOCK / SKIP
verdict plus a tamper-evident audit receipt — before the action crosses a trust
boundary. Secret/credential egress to an untrusted destination is deterministically
blocked; suspected prompt injection is sent to review; unauthorized privilege
grants are refused.
Requires an engine running with
QUESEN_TSC_V2_ENABLED=true(routePOST /tsc/validate).
from quesen_sdk import QuesenClient
from quesen_sdk.tsc import TscContext, TscBlocked
client = QuesenClient(base_url="https://<your-quesen-endpoint>", api_key="sk_live_abc")
# Your agent is about to POST data somewhere. Ask Quesen first.
decision = client.validate_tsc(
TscContext.data_egress(
data_classes=["secret"], # what's leaving
to="https://paste.evil.example", # where it's going
destination_trust="unverified",
framework="langchain",
)
)
print(decision.decision) # 'BLOCK'
print(decision.reason_codes) # ['EGRESS_SECRET_UNTRUSTED']
print(decision.tags) # ['exfiltration']
print(decision.commit_sha, decision.input_snapshot_hash) # audit receipt
# Fail-closed gate: raise unless the engine explicitly returned PASS.
try:
decision.require_pass()
run_the_tool() # only reached on PASS
except TscBlocked as e:
log_and_stop(e.decision) # BLOCK / REVIEW / SKIP never runs the tool
Scenario builders cover the common catastrophic actions:
| Builder | Agent is about to… |
|---|---|
TscContext.data_egress(...) |
send data OUT (exfiltration / PII / secret leakage) |
TscContext.tool_call(...) |
invoke a tool/capability (privilege + injection checks) |
TscContext.payment(...) |
move money / perform a financial action |
You can also pass a plain dict (or anything with .to_dict()) to
client.validate_tsc(...) for full control of the Typed Security Context schema.
Runnable demos: examples/agent_firewall.py (pure SDK)
and examples/langchain_firewall.py (LangChain tool wrapper).
Async is symmetric — await AsyncQuesenClient(...).validate_tsc(ctx).
Receipt provenance (v1.10, tracked in SDK v0.2.0)
Every ValidateResult and SimulateResult.baseline / .simulated now carries
two additional fields that make the verdict self-contained-replayable:
input_snapshot_hash· lowercase 64-char SHA-256 hex over canonical-JSON of the received request payload, withclient_request_idexcluded from the hash material. Hash the same payload client-side and prove the engine evaluated the exact input you sent.commit_sha· 40-char lowercase git SHA ofShxnque/quesenHEAD live at build time, or the sentinel"unknown"when running detached HEAD or a locally-built artifact. Pins the exact ruleset that produced the verdict.
Client-side hash reconstruction
import hashlib, json
def input_snapshot_hash(payload: dict) -> str:
to_hash = {k: v for k, v in payload.items()
if v is not None and k != "client_request_id"}
canonical = json.dumps(to_hash, sort_keys=True, separators=(",", ":"),
ensure_ascii=False, allow_nan=False).encode("utf-8")
return hashlib.sha256(canonical).hexdigest()
assert result.input_snapshot_hash == input_snapshot_hash({
"domain_age_days": 1,
"engagement_ratio": 0.95,
"scam_keyword_count": 4,
})
Replay recipe
git clone https://github.com/Shxnque/quesen && cd quesen
git checkout $COMMIT_SHA
pytest tests -q # asserts engine state at decision time
# Re-issue the request; verify input_snapshot_hash matches.
Backward compatibility. Against a pre-v1.10 engine the two fields default to the empty string. Callers who upgrade the SDK against an older engine continue to work unchanged; callers who upgrade the engine start seeing non-empty values automatically.
Async usage
import asyncio
from quesen_sdk import AsyncQuesenClient
async def main() -> None:
async with AsyncQuesenClient(base_url="https://q.example.com", api_key="sk_live_abc") as q:
decision = await q.validate(domain_age_days=200, engagement_ratio=0.3)
if decision.decision == "SKIP":
return # don't act
# ... execute the action ...
await q.report(request_id=decision.request_id, outcome="OK", realized_pnl=0.42)
asyncio.run(main())
What the client gives you
- Typed request + response models (dataclass-like,
__slots__, IDE-friendly attrs). - Automatic retries with exponential back-off on 5xx / network errors.
request_idpropagation — the UUID emitted by/validateis what you pass to/report.X-Request-IDheader — set once, echoed everywhere, useful for tracing across your stack./simulatehelper for the free counterfactual sales asset.- Receipt provenance surfaced as typed fields —
input_snapshot_hash+commit_shaare first-class attributes onValidateResult(v0.2.0+). - Fail-closed policy — timeouts / network errors surface as
QuesenTimeoutandQuesenTransportErrorso the caller can decide (recommendation: treat asSKIP). - Zero heavy dependencies — just
httpx.
API surface
Sync client: QuesenClient(base_url, api_key=None, timeout=5.0, retries=2, retry_backoff=0.2, request_id_header="X-Request-ID", user_agent="quesen-sdk-py/0.2.0")
| Method | Wraps | Purpose |
|---|---|---|
client.health() |
GET /health |
Liveness. |
client.version() |
GET /version |
Engine + weights + thresholds. |
client.validate(...) |
POST /validate |
Deterministic decision. Response carries input_snapshot_hash + commit_sha against v1.10+ engines. |
client.simulate(...) |
POST /simulate |
Counterfactual with weights_override / thresholds_override. |
client.report(...) |
POST /report |
Post-decision outcome feedback. v1.1.0 optional fields supported. |
Async client: AsyncQuesenClient(...) mirrors the sync surface with async def methods.
Error hierarchy
QuesenError
├── QuesenAuthError # 401 — invalid or missing X-API-Key
├── QuesenRateLimitError # 429 — per-key quota exceeded, Retry-After surfaced
├── QuesenValidationError # 422 — pydantic-side reject
├── QuesenServerError # 5xx after retries exhausted
├── QuesenTimeout # transport timeout
└── QuesenTransportError # generic transport failure
Environment variables
| Var | Meaning |
|---|---|
QUESEN_BASE_URL |
Optional default base URL if not passed to the client constructor. |
QUESEN_API_KEY |
Optional default API key if not passed to the client constructor. |
Doctrine compliance
This SDK preserves Quesen doctrine end-to-end:
- Determinism. The SDK does not add ML, prompts, randomness, or state. Same input in → same input out.
- Ecosystem neutrality. No chain lock-in, no framework lock-in, no LLM lock-in.
httpxonly. - Fail-closed. Timeouts and network errors surface as exceptions. Callers should treat them as
SKIP. - Request-ID propagation. Every call sets
X-Request-IDso your/reportcalls are correlatable to the original/validate. - Receipt provenance forwarded.
input_snapshot_hash+commit_shaare exposed as typed fields (v0.2.0+), enabling client-side replay-verification and ruleset-pin discipline.
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file quesen_sdk-0.4.0.tar.gz.
File metadata
- Download URL: quesen_sdk-0.4.0.tar.gz
- Upload date:
- Size: 23.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb6a09eb67b0f0e33c00b4ffae4a64152bc44e56029266871fa277016931ff4e
|
|
| MD5 |
9dad171df1a499f3d28e99ddcb761b9f
|
|
| BLAKE2b-256 |
ab75867fed3456d9dcf59ced6b1b42b10d78b537e9c25c944614c6689733e03d
|
File details
Details for the file quesen_sdk-0.4.0-py3-none-any.whl.
File metadata
- Download URL: quesen_sdk-0.4.0-py3-none-any.whl
- Upload date:
- Size: 18.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f6fd4993245f04bf1b5c96ef55e1c064c87782e7ab3a886ab334cc9b13a1310
|
|
| MD5 |
f35e2ad65c7a81865d8ce31c3820937a
|
|
| BLAKE2b-256 |
fa44db27a051d793478ad7144ff94d61d649f12a5a59de517d85259852063127
|