Skip to content

Integrations

attest grades a Trajectory, so it works with any framework once you convert the agent's history into one.

LangGraph

from attest import Attest, from_langgraph_messages

result = graph.invoke({"messages": [("user", question)]})
run = from_langgraph_messages(result["messages"], task=question)

report = Attest().evaluate(run)

The adapter pairs each tool call with its ToolMessage output and takes the last AI message as the final answer. It works on any list of LangChain messages, so LangChain agents that return their message history work too.

Haystack

from attest import Attest, from_haystack_messages
from haystack.dataclasses import ChatMessage

result = agent.run(messages=[ChatMessage.from_user(question)])
run = from_haystack_messages(result["messages"], allowed_tools=["search"])

report = Attest().evaluate(run)

The adapter pairs each tool call with the ToolCallResult that answers it and takes the last assistant message as the final answer. It accepts ChatMessage objects or their to_dict() form, so saved runs work too. A tool result marked as an error keeps its text as the output.

Anything else

Build the run from your own logs:

from attest import Step, ToolCall, Trajectory

run = Trajectory(
    task=question,
    allowed_tools=["search", "fetch"],
    steps=[
        Step(tool_call=ToolCall(name="search", arguments={"q": question}, output=search_text)),
        Step(tool_call=ToolCall(name="fetch", arguments={"url": url}, output=page_text)),
    ],
    final_answer=answer,
)

Record each tool's output exactly as the agent received it. attest can only check claims against the evidence the run contains. examples/quickstart.py is a complete, runnable example.

In CI

Run attest over a JSONL file of recorded runs, then gate on the pass rate's confidence interval rather than on a single run, so one noisy run can't fail the build:

set -e
attest run evals/runs.jsonl > report.json   # exits non-zero on a missing key, bad input or budget stop
jq -e '.ok and .data.verdicts.block == 0' report.json
passed=$(jq '.data.verdicts.pass' report.json)
graded=$(jq '.data.evaluated' report.json)
attest stats "$passed" "$graded" | jq -e '.data.low >= 0.8'

jq -e exits non-zero when its result is false, so each line fails the step if its check fails. The pass rate counts only runs whose verdict is pass; runs that need review don't count as passing. Keep report.json as a build artifact: every finding, with its evidence, is in it.

AI code review pipelines

See Code review mode.