Metadata-Version: 2.5
Name: matrix-verify
Version: 0.1.0
Summary: Fail-silent tracing for agent claim verification. Did your agent actually do what it said?
Project-URL: Homepage, https://matrixverify.dev
Project-URL: Documentation, https://matrixverify.dev/examples/langchain
Author: Matrix
License: MIT
Keywords: agents,langchain,observability,tracing,verification
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.3.0; extra == 'langchain'
Description-Content-Type: text/markdown

# matrix-verify

Your agent said it sent the email. Did it?

This SDK records what your agent was asked, which tools it called with which
arguments, and what it claimed afterwards. [Matrix](https://matrixverify.dev)
then checks the claim against the real system and returns **confirmed**,
**contradicted**, or **inconclusive**, with the evidence attached.

Python 3.9+. No dependencies. Fail-silent by design: it never raises into your
process, and if anything is misconfigured your agent keeps running exactly as
before. That also means a misconfiguration looks like success — see
[What fails silently](#what-fails-silently) before you ship.

## Install

```bash
pip install matrix-verify
```

Get an API key by signing up at [matrixverify.dev](https://matrixverify.dev).
It is shown once; keep it in an environment variable.

## LangChain

```python
import os
from matrix_verify import verify
from matrix_verify.langchain import verify_callback_handler

verify.init(api_key=os.environ["MATRIX_API_KEY"])

handler = verify_callback_handler(
    # Callbacks cannot discover which account acted. Without this,
    # every email claim comes back account_unverified.
    from_resolver=lambda tool: os.environ.get("AGENT_EMAIL"),
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": instruction}]},
    config={"callbacks": [handler]},
)

# Flushes the buffer. Nothing is sent without it.
verify.shutdown()
```

The handler reads the tool calls your agent already makes — nothing to wrap by
hand. It records the first human message as the instruction and the agent's
final answer as the claim.

A complete runnable agent, dependencies included:
**[matrixverify.dev/examples/python](https://matrixverify.dev/examples/python)**

## Without a framework

As a context manager:

```python
from matrix_verify import verify

verify.init(api_key=os.environ["MATRIX_API_KEY"])

verify.instruction("Email the August invoice to dana@northwind.example")

with verify.with_span(
    "gmail.send_email",
    span_type="tool",
    input={"to": to, "subject": subject, "from": os.environ["AGENT_EMAIL"]},
) as span:
    span.set_output(send_email(to=to, subject=subject))

verify.claim(f"Email sent successfully to {to}")
verify.shutdown()
```

Or as a decorator, which records the return value for you:

```python
@verify.with_span("gmail.send_email", span_type="tool")
def send_email(to, subject):
    ...
```

Name tool spans the way the verifier expects — `gmail.send_email`,
`send_email`, or `email.send` all match. A span named something else
(`dispatch_message`) is reported as a setup problem, not as your agent lying;
map it with `tool_span_name` rather than renaming your tool.

## Options

| `verify.init(...)` | Default | |
|---|---|---|
| `api_key` | — | Required. Without it `init` does nothing. |
| `endpoint` | `https://matrixverify.dev/api/traces` | Set only when self-hosting. |
| `environment` | `"production"` | `"production"`, `"test"` or `"dev"`. |
| `scheduled_delay_millis` | `500` | Batch export delay. |

## What fails silently

Every one of these leaves your agent running normally and sends nothing useful:

- **No `api_key`** — `init` is a no-op; `with_span` still runs your code and
  `claim` does nothing.
- **No `shutdown()`** — spans are batched and flushed on shutdown. The export
  thread is a daemon, so a process that exits without it loses whatever is
  still queued.
- **No `from_resolver`** (LangChain) — traces arrive, but every email claim is
  `inconclusive / account_unverified`, because the server cannot tell which
  mailbox to search.
- **No mailbox connected** — email claims come back
  `inconclusive / no_mailbox_connected`. Connect one under Settings.

To see what the SDK is doing, run with `MATRIX_DEBUG=1`. It prints the
endpoint, each export, and whether the server accepted it:

```text
[matrix-sdk] init endpoint=https://matrixverify.dev/api/traces environment=production apiKey=<len 51>
[matrix-sdk] runId=8e7780b5-1fc6-4a5b-9adb-5071f6d01335
[matrix-sdk] emitting completion marker, spanCount=4
[matrix-sdk] exporting 4 span(s) -> https://matrixverify.dev/api/traces: agent.instruction, send_email, agent.claim, sutra.trace_complete
[matrix-sdk] export OK (4 span(s) accepted)
[matrix-sdk] exporter shut down
```

## Same wire format as the TypeScript SDK

Traces from `matrix-verify` and `@matrixverify/verify` are indistinguishable to
the server: the same span names, the same `sutra.run_id` and `sutra.seq`
stamping, and the same `sutra.trace_complete` marker declaring how many spans a
run produced. That marker is what lets the server refuse to contradict a claim
on a trace that arrived incomplete — a lost span would otherwise look exactly
like a tool the agent never called.

## What gets verified today

Email claims, against Gmail. A claim that your agent sent mail with no send
tool call anywhere in the trace is contradicted from the trace alone. A claim
backed by a send call is checked against the mailbox you connect under
Settings.

## Licence

MIT.
