> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qwedai.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Consensus engine

> QWED's Consensus Engine orchestrates multiple verification engines in parallel for high-confidence results with circuit breaker patterns and weighted consensus.

The Consensus Engine orchestrates multiple verification engines for high-confidence results with fault tolerance.

<Warning>
  **Changed in v4.0.4:** Python code verification within consensus now runs exclusively through the secure Docker executor (`SecureCodeExecutor`). If the Docker sandbox is unavailable, consensus returns a `blocked_secure_execution` status and the API responds with HTTP 503.
</Warning>

## Features

* **Async parallel execution** — Run engines concurrently
* **Circuit breaker** — Auto-disable failing engines
* **Engine health monitoring** — Track reliability
* **Weighted consensus** — Consider engine reliability
* **Secure code execution** — Python engine runs through the Docker sandbox only

## Usage

```python theme={null}
from qwed_sdk import QWEDClient

client = QWEDClient(api_key="qwed_...")

result = client.verify_with_consensus(
    query="2 + 2 = 4",
    mode="maximum"  # single, high, maximum
)

print(result.final_answer)     # 4
print(result.confidence)       # 0.999
print(result.engines_used)     # 3
print(result.agreement_status) # "unanimous"
```

## Verification modes

| Mode | Engines | Speed | Confidence | Docker required |
| - | - | - | - | - |
| `single` | 1 (SymPy) | ⚡ Fast | Good | No |
| `high` | 2 (SymPy + Python) | Medium | High | Yes |
| `maximum` | 3+ (All applicable) | Slower | Maximum | Yes |

<Note>
  The `high` and `maximum` modes include the Python engine, which requires the secure Docker sandbox. If Docker is unavailable, these modes return `blocked_secure_execution` instead of falling back to in-process execution.
</Note>

<Note>
  The Python engine in `high` and `maximum` modes requires Docker. If the secure Docker sandbox is unavailable, consensus verification returns HTTP 503 instead of falling back to in-process code execution.
</Note>

## Async execution

```python theme={null}
import asyncio

async def verify_many():
    results = await asyncio.gather(
        client.verify_async("2+2=4"),
        client.verify_async("3*3=9"),
        client.verify_async("sqrt(16)=4")
    )
    return results
```

## Circuit breaker

When an engine fails repeatedly, it's automatically disabled:

```python theme={null}
# Check engine health
health = client.get_engine_health()

print(health)
# {
#   "SymPy": {"state": "healthy", "failures": 0},
#   "Python": {"state": "healthy", "failures": 0},
#   "Z3": {"state": "degraded", "failures": 2}
# }

# Reset circuit breakers
client.reset_circuit_breakers()
```

## Execution deadlines and partial results

Consensus verification runs under a single 30-second aggregate deadline that covers all engines. No engine call can hold a request open indefinitely, and `/verify/consensus` never hangs or returns HTTP 500 because one engine stalled.

How the deadline applies:

* **One aggregate budget, not per-engine stacking.** All selected engines share the same 30-second wall clock. Sequential per-engine waits cannot accumulate beyond it.
* **Timed-out engines become explicit `BLOCKED` results.** An engine that misses the deadline is recorded in `verification_chain` as a `BLOCKED` `EngineResult` with the timeout in its `error` field (for example, `"Engine timed out after 30s"`). The failure is also recorded with the circuit breaker.
* **Circuit-open engines are recorded too.** An engine skipped because its circuit breaker is open appears as a `BLOCKED` result rather than being silently dropped.
* **Partial results still aggregate.** Engines that finished within the deadline contribute normally. The consensus verdict is computed from the results that arrived, with the fail-closed [status propagation](#status-propagation) rules applied — a `BLOCKED` engine result caps the consensus at `UNVERIFIABLE`.
* **Off the event loop.** Engine execution runs in a dedicated per-call executor, so a slow consensus request degrades only itself, not other API traffic.

```python theme={null}
result = client.verify_with_consensus(query="2 + 2 = 4", mode="maximum")

for engine in result.verification_chain:
    if engine.status == "BLOCKED" and "timed out" in (engine.error or ""):
        print(f"{engine.engine_name} missed the 30s deadline")
```

<Note>
  A consensus response that includes timed-out engines is a valid partial result, not an error. Check `verification_chain` to see which engines contributed and treat the aggregate `status` as authoritative.
</Note>

## Async failure handling

When an unexpected error occurs during async engine aggregation, the Consensus Engine records the failure as a dedicated `EngineResult` entry rather than silently dropping it. This ensures that:

* The failure is visible in the `results` array returned to the caller
* Aggregate confidence scores account for the failed engine
* The error is logged via `logger.exception` for post-mortem debugging

The recorded result uses `engine_name: "consensus_orchestrator"`, `method: "async_aggregation"`, `success: false`, and includes the error message in the `error` field.

***

## Secure execution gates

Before any Python code runs in the Docker sandbox, two validation layers apply. Both fail closed: a rejected input returns a security error instead of executing.

### Single-expression gate on translated math

When consensus translates a natural-language math query, the translated expression is interpolated into the generated verification code. The translator therefore enforces that the output is exactly one Python expression:

* The expression must parse with `ast.parse(..., mode="eval")`. Multi-statement input, semicolon chains, assignments, imports, and multi-line newline smuggling all fail by construction.
* Expressions longer than 500 characters are rejected before parsing, so deeply nested adversarial input never reaches the parser.
* The existing character-set and denylist checks remain as defense in depth.

Bracketed multi-line expressions (a single expression wrapped in parentheses across lines) still pass. Anything that is not one expression does not.

### Module-indirection blocklist on executed code

The pre-execution AST check on sandboxed Python code inspects every dotted segment, not just the first:

* **Imports** of OS, process, and reflection modules (`os`, `sys`, `subprocess`, `socket`, `posix`, `nt`, `importlib`, `ctypes`, `builtins`, and network clients) are blocked wherever the module name appears in the import path, including `from ... import os as safe` aliasing.
* **Attribute chains** are blocked when any segment names a dangerous module, so package-internal re-exports (reaching the OS module through pandas or numpy internals) are caught even though the chain is rooted at an innocuous alias.
* **OS-primitive calls** (`system`, `popen`, `import_module`, the `exec*`/`spawn*` families, `fork`) are blocked as bare names and as attribute targets, which also stops `importlib.import_module("os")`-style indirection.

Legitimate pandas and numpy verification code at the public API surface (`np.linalg.norm`, `pd.Timestamp.now`) is unaffected.

***

## Engine selection

The consensus engine automatically selects which engines to run based on the verification mode:

| Mode | Engines selected |
| - | - |
| `single` | SymPy |
| `high` | SymPy, Python |
| `maximum` | SymPy, Python, Z3 (+ Stats for statistical queries) |

<Note>The fact engine is excluded from automatic engine selection. Fact verification requires external context, and including it in consensus without that context would create self-referential verification loops. If fact verification is invoked during consensus, it returns an error indicating that external context is required.</Note>

***

## Consensus calculation

The engine uses weighted voting:

| Engine | Reliability Weight |
| - | - |
| SymPy | 1.00 |
| Z3 | 0.995 |
| Python | 0.99 |
| Stats | 0.98 |

Final confidence = weighted average of agreeing engines.

### Agreement statuses

| Status | Meaning |
| - | - |
| `unanimous` | All engines returned the same answer |
| `majority` | More than half of the total weight agrees |
| `split` | No clear majority among engines |
| `no_results` | No engines returned a result |
| `blocked_secure_execution` | The Docker sandbox is unavailable and a required engine cannot run (returns HTTP 503) |

## Status propagation

Consensus is a status-preserving aggregator, not a proof engine. Per the [3-tier engine classification](/engines/overview), consensus never invents a `VERIFIED` status from disagreement:

* All engines return `BLOCKED` → the consensus result is `BLOCKED`.
* Any single engine returns `BLOCKED` (but not all) → the consensus result is capped at `UNVERIFIABLE` (fail-closed — a partial block prevents `VERIFIED`).
* Any engine returns `UNVERIFIABLE` → the consensus result is `UNVERIFIABLE`.
* Only unanimous `VERIFIED` engine outcomes can produce a `VERIFIED` consensus.
* Math-query translation failures fail closed — the engine raises rather than fabricating a numeric answer from a regex or decimal guess.

Individual engine outcomes are exposed via the `status` field on each `EngineResult` in the aggregated result.

### Sub-engine status contract

Not every sub-engine that runs under consensus can emit `VERIFIED`. The table below makes the fail-closed contract explicit — these engines never contribute a `VERIFIED` vote, even on a successful computation, so their output stays advisory:

| Sub-engine | Success outcome | Rationale |
| - | - | - |
| Stats (mean, median, variance) | `UNVERIFIABLE` — computed value stays on the result as advisory | Calculating a statistic is not verifying a claim. Only comparison against a claimed value with proof of correctness can produce `VERIFIED`. |
| Python code execution | `UNVERIFIABLE` — captured output stays on the result as advisory | Runtime success only disproves by crashing; executing an expression is not proving its result matches the query's semantics. |

This mirrors the existing [Fact engine](/engines/fact) heuristic contract: a TF-IDF `SUPPORTED` verdict is capped at `UNVERIFIABLE` because heuristic support is not a proof. Because a `VERIFIED` consensus requires **every** contributing engine to have emitted `VERIFIED`, a query answered only by Stats or code execution can never reach `VERIFIED` — it stops at `UNVERIFIABLE` with the computed value carried through as advisory data.

## ConsensusResult

Async and sync consensus verification both return a `ConsensusResult` dataclass that carries the aggregated verdict along with per-engine detail:

| Field | Type | Description |
| - | - | - |
| `final_answer` | `Any` | Best-supported answer across engines |
| `confidence` | `float` | Weighted confidence, `0.0`–`1.0` |
| `agreement_status` | `str` | `unanimous`, `majority`, `split`, `no_results`, `all_failed`, or `blocked_secure_execution` |
| `engines_used` | `int` | Number of engines that returned a result |
| `verification_chain` | `List[EngineResult]` | Per-engine outcomes, including each engine's `status` |
| `total_latency_ms` | `float` | End-to-end latency for the aggregation |
| `parallel_execution` | `bool` | True when engines ran concurrently |
| `status` | `Optional[DiagnosticStatus]` | Trust-boundary status — `VERIFIED`, `UNVERIFIABLE`, or `BLOCKED` (enum, not a bare string) |
| `proof_ref` | `Optional[str]` | Populated only on `VERIFIED` consensus — the sha256 hash of the canonical evidence |
| `verified_evidence` | `Optional[Dict[str, Any]]` | Populated only on `VERIFIED` consensus — the exact evidence bound to `proof_ref` (agreement status, contributing engines, and the winning chain) |

<Info>
  `status` is now a [`DiagnosticStatus`](/advanced/diagnostics) enum rather than a bare string. Code that compared `result.status == "VERIFIED"` should use `result.status is DiagnosticStatus.VERIFIED` or check `result.status.value` for the string form.
</Info>

### `.to_diagnostic_result()`

`ConsensusResult` converts cleanly to a [`DiagnosticResult`](/advanced/diagnostics) using the stored `verified_evidence`, so downstream attestation hashes bind to the exact evidence used to reach agreement:

```python theme={null}
result = client.verify_with_consensus(query="2 + 2 = 4", mode="high")

dr = result.to_diagnostic_result()

dr.is_verified          # True only if consensus was VERIFIED
dr.proof_ref            # sha256:... bound to result.verified_evidence
dr.developer_fields     # {'agreement_status': 'unanimous', 'confidence': 0.99, 'engines_used': 2}
```

The conversion is fail-closed: a `VERIFIED` `ConsensusResult` that is missing `verified_evidence` is downgraded to `UNVERIFIABLE` rather than emitting a weaker proof. This is why the `/verify/consensus` endpoint's `meets_requirement` flag is true only when consensus is `VERIFIED` at the trust boundary **and** confidence meets the requested threshold — confidence alone is never sufficient.


## Related topics

- [API endpoints](/api/endpoints.md)
- [Rate limits](/api/rate-limits.md)
- [QWED Protocol: deterministic verification for LLMs](/whitepaper.md)
- [Changelog Archive](/changelog-archive.md)
- [Changelog](/changelog.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.