Metadata-Version: 2.4
Name: auditant
Version: 0.3.0
Summary: Compliance system-of-record for AI agents — tamper-evident capture with auditant.init() (LangChain/LangGraph, CrewAI, OpenAI, Anthropic, OpenAI Agents SDK), OTLP, and real-time policy checks
License: Apache-2.0
Project-URL: Homepage, https://auditant.co
Project-URL: Documentation, https://dev.auditant.co/docs
Keywords: ai-agents,audit-trail,tamper-evident,compliance,ai-governance,opentelemetry,eu-ai-act
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Classifier: Topic :: System :: Logging
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Provides-Extra: gateway
Requires-Dist: litellm>=1.96; extra == "gateway"
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.25; extra == "otel"
Requires-Dist: opentelemetry-sdk>=1.25; extra == "otel"
Provides-Extra: openai-agents
Requires-Dist: openai-agents>=0.1; extra == "openai-agents"
Provides-Extra: openinference
Requires-Dist: opentelemetry-api>=1.25; extra == "openinference"
Requires-Dist: opentelemetry-sdk>=1.25; extra == "openinference"
Requires-Dist: openinference-instrumentation-langchain>=0.1; extra == "openinference"
Requires-Dist: openinference-instrumentation-crewai>=0.1; extra == "openinference"
Requires-Dist: openinference-instrumentation-openai>=0.1; extra == "openinference"
Requires-Dist: openinference-instrumentation-anthropic>=0.1; extra == "openinference"
Provides-Extra: langchain
Requires-Dist: opentelemetry-api>=1.25; extra == "langchain"
Requires-Dist: opentelemetry-sdk>=1.25; extra == "langchain"
Requires-Dist: openinference-instrumentation-langchain>=0.1; extra == "langchain"
Provides-Extra: crewai
Requires-Dist: opentelemetry-api>=1.25; extra == "crewai"
Requires-Dist: opentelemetry-sdk>=1.25; extra == "crewai"
Requires-Dist: openinference-instrumentation-crewai>=0.1; extra == "crewai"
Dynamic: license-file

# auditant

Compliance system-of-record for AI agents: every action your agents take,
hash-chained, policy-checked, externally countersigned — and verifiable
offline by someone who doesn't trust you.

## Two lines

```python
import auditant
auditant.init(api_key="ak_…", log_id="t_…/prod", agent_id="underwriter")
```

With OpenTelemetry present, `init()` registers a span processor and
activates whichever of four OpenInference instrumentors are installed —
LangChain (which is what sees LangGraph), CrewAI, OpenAI, Anthropic — so
LLM calls and tool executions become chained audit events automatically:
hashes, never payloads; token counts reported as observed-not-priced, never
as $0. When the OpenAI Agents SDK is importable, `init()` registers on its
trace-processor bus as well. Install the extras for what you run:

```sh
pip install "auditant[openinference]"    # all four instrumentors
pip install "auditant[langchain]"        # or one at a time: [crewai], [openai-agents], [gateway]
```

`handle.activated` lists what actually turned on. An instrumentor installed
for a framework that is not present counts as *not* activated — the SDK
never reports coverage of code it cannot see.

Without OpenTelemetry, `init()` still gives you a session on the chain
(lifecycle + heartbeats, so a quiet agent is distinguishable from a dead
emitter), the manual `record()` API, and the synchronous policy check:

```python
handle = auditant.init(api_key="ak_…", log_id="t_…/prod", agent_id="underwriter")
verdict = handle.decide({"action": "wire_transfer", "amount": 50_000,
                         "input": {"to": "acct 7"}})   # hashed into the record, never sent
# {'effect': 'pending_approval', ...} → a human approves in the dashboard
# or from Slack, and the same call then returns 'allow'.
# async hosts: await handle.adecide({...})
```

Every `decide()` chains a `policy_decision` event before anything happens —
that is what the approval queue is built from. Evidence writes never block
your agent (queued, batched, spilled to disk on outage), and the policy
service failing means a *recorded* enforcement gap, never a silent allow.

## Block → approve → resume, in your framework

Capture is automatic; enforcement is one more line, because something has
to stand in front of the tool. Both shims ask `decide()` before a guarded
tool runs, chain the answer, and express a hold in the framework's own
pause:

```python
# LangGraph — interrupt() + a checkpointer
from auditant.langgraph import guard
tools = guard(handle, [wire_transfer, lookup_balance], amount="amount")
out = graph.invoke(state, config)                   # held ⇒ out["__interrupt__"]
# …a named human approves in the dashboard or from Slack…
out = graph.invoke(Command(resume=True), config)    # re-asks, walks through

# OpenAI Agents SDK — needs_approval
from auditant.openai_agents import guard, resolve
agent = Agent(name="underwriter", tools=guard(handle, [wire_transfer], amount="amount"))
result = await Runner.run(agent, "wire $50,000 to acct 7")
while result.interruptions:
    answer = await resolve(handle, result)          # answered from the chain
    if not answer.ready: await asyncio.sleep(30); continue
    result = await Runner.run(agent, answer.state)
```

The resume value is never the approval: what lets the action through is
the human's answer on the chain, matched by session and action server-side.
A refusal reaches the model in the policy's words rather than as a crash,
and is chained as `blocked`. Monitor-mode rules and `enforce=False` record
everything and stop nothing.

## The examiner's side

Every exported bundle is self-verifying: it embeds a dependency-free
verifier (`node verify.mjs bundle.json`) that recomputes the chain and
validates every countersignature — no account, no network, nothing from us.

Docs: https://dev.auditant.co/docs
