Skip to main content
The official Python SDK for QWED.

Installation

Quick start

As of v5.0.0, the status field may return INCONCLUSIVE, BLOCKED, or UNKNOWN in addition to VERIFIED and ERROR. Natural-language math queries return INCONCLUSIVE when the inner engine succeeds, because the LLM translation step is non-deterministic. See the trust boundary documentation for details.

How is_verified is decided

Changed in v8.0.0 (GHSA-jppc-7r58-7pjh)
VerificationResult.is_verified follows the server’s admission decision. The SDK applies the same rule as the server (is_admitted() in qwed_sdk.models):
  • If the response carries an admission field, only ADMIT admits. BLOCKED is never verified.
  • If the route returns no admission, status must be VERIFIED and is_valid must be true.
A bare is_valid: true or is_verified: true in the response never admits on its own. The server flattens engine fields into the response, so a result that a gate blocked can still carry the engine’s is_valid: true. The server’s decision is also available as result.admission.
Before v8.0.0, the SDK set is_verified from a bare is_valid in the response. A result the server had marked BLOCKED or UNVERIFIABLE could read as verified. Upgrade to v8.0.0 or later.

Async client

Methods

verify(query)

Auto-detect and verify any claim.

verify_math(expression)

Verify mathematical expressions.

verify_logic(query)

Verify logical constraints (QWED-Logic DSL).

verify_code(code, language)

Check code for security vulnerabilities with the server’s CodeVerifier.
verify_code always sends the code to POST /verify/code, and is_verified follows the server’s admission. If the client can’t connect (httpx.ConnectError) or the request times out (httpx.TimeoutException), it returns status="UNVERIFIABLE" and is_verified=False. It never answers from the local CodeGuard blocklist.

verify_sql(query, schema_ddl, dialect)

Validate SQL queries against a schema with the server’s SQLVerifier.
verify_sql always sends the query to POST /verify/sql, and is_verified follows the server’s admission. If the server can’t be reached or the request times out, it returns status="UNVERIFIABLE" and is_verified=False. It never answers from the local SQLGuard.
Before v8.0.0, verify_code and verify_sql answered from the local CodeGuard and SQLGuard when the full qwed package was installed, and reported a pass as VERIFIED without calling the server. Those blocklists missed reflective calls, stacked SQL statements, and dangerous SQL functions. If your code relied on offline verify_code or verify_sql, it now receives UNVERIFIABLE when the server is unreachable. See GHSA-4rj3-8wqj-cqwc and GHSA-45jv-2j44-4c2q.
To check code without a server, use QWEDLocal.verify_code. It runs CodeVerifier in process and returns its admission in evidence["admission"]. Without the full qwed package, it returns UNVERIFIABLE.

verify_fact(claim, context)

New in v4.0.0
Verify factual claims against a provided context. Includes client-side PII pre-check.
verify_fact fails closed when the QWED service can’t complete the check. If the client can’t connect (httpx.ConnectError) or the request times out (httpx.TimeoutException), it returns status="UNVERIFIABLE" and is_verified=False. It doesn’t raise. The local PII pre-check still runs first, but it is not a fact check, so passing it never produces VERIFIED.
Earlier SDK versions returned status="VERIFIED" and is_verified=True when the service was unreachable, after running only the local PII scan. If your code relied on that offline pass, it now receives UNVERIFIABLE.

verify_stats(query, file_path)

New in v4.0.0
Verify statistical claims against CSV data.

verify_consensus(query, mode, min_confidence)

New in v4.0.0
Multi-engine consensus verification.
verify_consensus takes status from the server’s response. is_verified is True only when the server returns meets_requirement exactly True, which it sets only for a VERIFIED result that meets min_confidence. A truthy value that is not True admits nothing.

verify_image(image_path, claim)

New in v4.0.0
Verify claims about image content.

verify_batch(items)

Verify multiple items at once. The response is a BatchResult whose items are per-item records — for math items the verdict is a DiagnosticResult nested under each item’s result, so check result.status and result.proof_ref per item rather than relying on an aggregate.
For math items, supply equality claims (e.g., "x + x = 2*x") to receive proof results. Bare expressions without an = return status: "UNVERIFIABLE" with the simplified form in developer_fields.simplified — they are never reported as verified. The legacy is_valid flag is preserved inside developer_fields.is_valid for backward compatibility.

Verification Context

New in v7.1.0
The SDK exposes Verification Context, the standardized JSON record of a verification, as client methods, re-exported types, and a to_verification_context() method on the verifier classes.

Client methods

All three methods are available on both QWEDClient and QWEDAsyncClient and call the corresponding Verification Context endpoints.

create_verification_context_from_diagnostic(diagnostic, query, verifier)

Create a schema-valid Verification Context document from a DiagnosticResult dict. Optional keyword arguments: verifier_version and attestation_token. A VERIFIED diagnostic without a valid attestation token fails closed to BLOCKED (fail-closed). The admission is DENY unless the token also signs ADMIT, as the attestation of an answer the server admitted does. The server returns a v1.1 document, signed in its attestation field.

validate_verification_context(document, require_attestation=True)

Validate a document against the JSON Schema of its spec_version. By default, the client first verifies the document’s attestation against the server’s keys (/.well-known/jwks.json), then asks the server, which checks it too.

resolve_verification_context(document, require_attestation=True)

Resolve the document’s proof_ref evidence commitment, after the same attestation check. A proof_ref alone is a commitment anyone can compute; the attestation shows the server issued the document.
When the attestation check fails, both methods return the failure without asking the server: resolve_verification_context() returns the same errors with resolved in place of valid. If the client can’t fetch the server’s keys, it raises KeysUnavailableError instead of reporting the document invalid. Pass require_attestation=False to check the schema or proof_ref alone, for example for a v1.0 document you built locally.

verify_document_attestation(document)

Verify a document’s attestation against the server’s keys and return its claims, or raise AttestationError. To verify offline, or against keys you pinned, use the module function with the JWKS you hold:
The signature authenticates the document. It adds nothing to the verdict or the admission: a signed DENY document is still DENY.

Re-exported types and helpers

qwed_sdk re-exports the Verification Context v1.0 model, so you can build, validate, and resolve documents locally without an API round trip:
  • compute_document_proof_ref(document) derives the canonical sha256:<64-hex> commitment for a document.
  • resolve_document_proof_ref(document) returns True only when the document is VERIFIED, schema-valid, and the stored proof_ref matches the re-derived commitment. Everything else returns False (fail-closed).

to_verification_context() on verifiers

Every verification engine that defines to_verification_context() maps its DiagnosticResult to a Verification Context document: the Code, Consensus, DSL Logic, Fact, Graph Fact, Image, Logic, Reasoning, Schema, SQL, Stats, and Symbolic verifier classes, plus the BatchFactVerifier and MultiVLMVerifier helpers. Math and the secure code executor do not define the method — pass their DiagnosticResult to create_verification_context_from_diagnostic() instead:
The mapping preserves the fail-closed invariants: VERIFIED documents carry a sha256 proof_ref, while UNVERIFIABLE and BLOCKED documents carry proof_ref: null with admission DENY.

Verification cache

qwed_sdk.cache.VerificationCache is the persistent SQLite-backed cache for verification results. It reduces LLM cost on repeated queries. Cache entries are context-bound. A hit requires an exact match of both the normalized query and the trust-bound CacheContext. A mismatch on any context dimension is a deterministic miss. The cache never falls back to a query-only match. This prevents cross-provider, cross-model, cross-policy, and cross-tenant replay of VERIFIED results.

CacheContext

Every get() and set() call requires a CacheContext. All fields participate in the cache key.

Usage

Miss conditions

cache.get(query, context) returns None when any of the following holds:
  • No entry exists for this (query, context) pair.
  • The stored entry has expired (TTL, default 24 hours).
  • The stored context fingerprint does not match context (replay guard).
  • The entry uses the legacy v1 schema. The cache never returns legacy rows.

Constructor options

  • cache_dir — Directory for the SQLite database. Defaults to ~/.qwed/cache.
  • ttl — Time-to-live in seconds. Defaults to 24 hours.
get() and set() require a CacheContext argument as of the May 17, 2026 release. Calls that omit the context raise TypeError. See the changelog entry for migration details.

CLI

See the CLI reference for details on the qwed context command group.

Verification cache

qwed_sdk.cache provides a context-bound SQLite cache for verification results. A cache hit requires an exact match of both the normalized query and the trust-bound CacheContext. Any mismatch on a context dimension yields a deterministic miss — the cache never crosses trust boundaries. The cache also ignores legacy entries that lack a context fingerprint.

CacheContext

Trust-bound context dimensions required for every cache operation. All fields participate in the cache key; omitting or changing any field causes a deterministic cache miss.
str
required
Provider or API endpoint identifier (for example, "openai", "claude").
str
required
Model or deployment name (for example, "gpt-4o").
str
required
Verifier policy version string (for example, "v1").
Optional[str]
default:"None"
Tenant or session scope identifier. Use to prevent cross-tenant replay.
Optional[str]
default:"None"
Environment or config fingerprint for additional binding.

VerificationCache(cache_dir=None, ttl=86400)

Optional[str]
default:"~/.qwed/cache"
Directory for the SQLite cache database.
int
default:"86400"
Time-to-live in seconds (default: 24 hours). get() treats entries older than ttl as a miss and deletes them on access.
The cache caps total entries at MAX_ENTRIES = 1000. When set() grows the table past the cap, set() evicts the least-recently-accessed entries.

get(query, context)

Return the cached result for (query, context), or None on miss. get() returns None when:
  • No entry exists for the (query, context) pair.
  • The entry has expired (older than ttl).
  • The stored context fingerprint does not match context (defence-in-depth replay guard).
str
required
The verification query string. Normalized (lowercased, whitespace-collapsed) before hashing.
CacheContext
required
Trust-bound context that must match exactly. Omitting this argument raises TypeError.
Optional[Dict[str, Any]]
The cached result dict, or None on any miss.

set(query, result, context)

Store a verification result bound to (query, context). The composite primary key (key, context_fingerprint) keeps identical queries under different contexts as independent entries.
str
required
The verification query string.
Dict[str, Any]
required
Verification result to cache. set() serializes this dict as JSON.
CacheContext
required
Trust-bound context to bind this entry to.

clear()

Remove all cached entries and reset stats.

get_stats()

Return a CacheStats dataclass with hits, misses, total_entries, cache_size_bytes, and a hit_rate property. Print a formatted summary of cache statistics to stdout. Uses ANSI colors when colorama is installed.
Breaking change (Issue #187). get() and set() now require a CacheContext argument. Calls like cache.get("2+2") or cache.set("2+2", result) raise TypeError. The on-disk schema is cache_v2 with composite PRIMARY KEY (key, context_fingerprint). The cache never returns entries from the legacy v1 cache table.

Guards

The SDK includes security guards for protecting AI agent pipelines:
See the SDK Guards reference for complete documentation.

Environment variables