Skip to content

EngineManager

EngineManager starts and stops the engine subprocess that evaluates your checks. Pytest normally manages it for you; use it directly when building a custom integration. Use AttestClient for concurrent requests rather than adding another reader to the engine’s stdout.

import asyncio
from attest.engine_manager import EngineManager
async def main():
async with EngineManager() as engine:
response = await engine.send_request("evaluate_batch", {
"trace": {"trace_id": "greeting", "output": {"message": "Hello"}, "steps": []},
"assertions": [{"assertion_id": "greeting-check", "type": "content",
"spec": {"target": "output.message", "check": "contains", "value": "hello"}}],
})
print(response["results"][0]["status"])
asyncio.run(main())
EngineManager(engine_path: str | None = None, log_level: str = "warn")

engine_path selects an explicit binary; otherwise discovery runs. log_level is passed to the engine (debug, info, warn, or error).

resolve_engine(explicit_path: str | None = None, *, allow_download: bool = True) -> str returns the resolved path. The first binary found must report exactly ENGINE_VERSION, 1.1.0 for this SDK. A mismatch raises EngineVersionMismatchError; discovery does not skip it.

PrioritySource
1Explicit path; a missing file raises FileNotFoundError
2ATTEST_ENGINE_PATH; a missing file raises FileNotFoundError
3Executable attest-engine on PATH (attest-engine.exe on Windows); empty PATH entries skipped
4Version-specific ~/.attest/bin/<ENGINE_VERSION>/ cache
5Automatic release download, unless disabled

ATTEST_ENGINE_NO_DOWNLOAD=1 (also true or yes) disables download. Neither a monorepo/local bin/ directory nor the legacy unversioned cache is searched.

Published SDK packages carry engine-checksums.json. Downloads must match that package-shipped SHA256; the release’s downloaded checksum file is not trusted. The binary is atomically installed into the version-specific cache. Downloads require HTTPS (including redirects), allow at most five redirects, and have a 10-second connect / 120-second total deadline. Source/editable installs without the checksum table cannot auto-download: build the engine and set ATTEST_ENGINE_PATH.

Supported release assets:

OSArchitectureAsset
macOSARM64attest-engine-darwin-arm64
macOSAMD64attest-engine-darwin-amd64
LinuxARM64attest-engine-linux-arm64
LinuxAMD64attest-engine-linux-amd64
WindowsARM64attest-engine-windows-arm64.exe
WindowsAMD64attest-engine-windows-amd64.exe

All methods below are async unless described as a property or synchronous method.

APIBehavior
start(env_overrides: Mapping[str, str] | None = None) -> InitializeResultSpawn, send initialize, check version/capabilities; overrides affect the child environment only. A failed initialization stops the process before propagating the error.
restart(env_overrides: Mapping[str, str] | None = None) -> InitializeResultKill the old process and start a fresh engine; used for timeout recovery
stop() -> NoneRelease an attached client’s stdout reader, request shutdown, and stop the process; kill if termination exceeds five seconds
send_request(method: str, params: dict[str, Any]) -> AnySend a request and return the result with the matching request ID
is_running: boolProperty: subprocess is alive
spawn_env: Mapping[str, str]Property: environment used for the current engine; do not log it because it may contain credentials
attach_client(client: AttestClient) -> NoneSynchronous: associate the single client that owns stdout

An async context manager calls start() on entry and stop() on exit. Calling a non-initialize request before startup raises RuntimeError. Once a client is attached, send_request() delegates to that client’s correlated request loop; attaching a second different client raises RuntimeError.

Without an attached client, the manager uses the startup/shutdown handshake reader, which drops responses whose IDs do not match. Do not issue concurrent direct handshake requests. AttestClient supplies request correlation, timeout-triggered restart, and conservative model-budget accounting for normal operation.

FieldTypeMeaning in 1.1.0
engine_versionstr"1.1.0"
protocol_versionint1
capabilitieslist[str]Accepted features and configured-provider markers
missinglist[str]Requested but unavailable capabilities
compatibleboolRequired capabilities are present
encodingstr"json"
max_concurrent_requestsintAdvertised limit 1
max_trace_size_bytesint10485760 (10 MiB)
max_steps_per_traceint10000

embedding / llm_judge capabilities mean the assertion types are accepted, not that providers are configured; inspect embedding_configured / judge_configured. Incompatible required capabilities raise RuntimeError. See JSON-RPC Protocol for wire details.

The session-scoped attest_engine fixture handles engine lifecycle; the attest fixture provides evaluate, inspect, async variants, and enforce.

OptionDefaultPurpose
--attest-engine PATHDiscoverySelect binary
--attest-log-level LEVELwarnEngine logging
--attest-tier NAllSelect tests tagged at or below the tier
--attest-budget USDUnlimitedCumulative session-cost limit
--attest-cost-reportOffSession cost summary
--attest-strictOffFail rather than skip not-evaluated tests (also ATTEST_STRICT)
--attest-report PATHOffReport-v2 JSON, including every test outcome
--attest-diagnosticsOffExpanded assertion diagnostics
--attest-trials N1Fixed-N trials for synchronous tests using attest
--attest-min-pass-rate RATE1.0Minimum evaluated-trial pass rate
--attest-egress blockATTEST_EGRESS, else allowBest-effort guard against provider connections from test code

ATTEST_ENGINE_TIMEOUT is the SDK response timeout in seconds (default 30). On an ordinary client timeout the engine restarts and pending requests fail. ATTEST_SIMULATION=1 still starts the engine and evaluates deterministic assertions, including trace trees and effects; Layers 5–6 return not_evaluated: simulation. It does not prevent the agent itself from calling providers.

from attest import __version__, ENGINE_VERSION
assert __version__ == ENGINE_VERSION == "1.1.0"

See Configuration for all provider, budget, cache and egress settings.