aer1-smolagents
AER-1 verifiable workflow receipts for HuggingFace SmolAgents.
Attach one collector to your agent and every run emits a hash-chained, offline-verifiable verifiable workflow receipt (AER-1, Section 8): what the agent did, in what order, with per-step hashes and a Merkle root over the whole run. No network calls, no behavior changes, the collector only observes.
The AER-1 framework collector family
The SmolAgents collector in the AER-1 framework collector family. Any agent running on these frameworks can emit verifiable AER-1 execution receipts: every step recorded, hash-chained, one Merkle root over the whole run.
- aer1-smolagents ��� SmolAgents agents via step callbacks
- aer1-openai-agents ��� the OpenAI Agents SDK via its TracingProcessor
- aer1-crewai ��� CrewAI crews via the event bus
- aer1-langgraph ��� LangGraph swarms via callback handler
- aer1-autogen ��� AutoGen multi-agent chats
Plus the aer1 metapackage: pip install aer1, then import aer1; aer1.instrument(). One line, zero config, auto-detects your framework.
Install
pip install aer1-smolagents
Use it (copy, paste, run ��� no API keys needed)
# pip install aer1-smolagents
from aer1_smolagents import AER1ReceiptCollector
from smolagents import ToolCallingAgent, TaskStep, ActionStep
from smolagents.models import (Model, ChatMessage, ChatMessageToolCall,
ChatMessageToolCallFunction, MessageRole)
from smolagents.tools import tool
@tool
def get_weather(location: str) -> str:
"""Get the weather for a location.
Args:
location: The city name.
"""
return f"Sunny, 22C in {location}"
class ScriptedModel(Model):
"""Deterministic stand-in: one tool call, then a final answer."""
def __init__(self):
super().__init__(model_id="demo")
self.calls = 0
def generate(self, messages, **kwargs):
self.calls += 1
if self.calls == 1:
return ChatMessage(role=MessageRole.ASSISTANT, tool_calls=[
ChatMessageToolCall(
function=ChatMessageToolCallFunction(
name="get_weather", arguments={"location": "Paris"}),
id="call_1", type="function")])
return ChatMessage(role=MessageRole.ASSISTANT, tool_calls=[
ChatMessageToolCall(
function=ChatMessageToolCallFunction(
name="final_answer",
arguments={"answer": "Sunny, 22C in Paris."}),
id="call_2", type="function")])
collector = AER1ReceiptCollector(goal="Check the Paris weather")
agent = ToolCallingAgent(
tools=[get_weather],
model=ScriptedModel(), # swap for your real model in production
step_callbacks={TaskStep: collector, ActionStep: collector}, # line 1
)
answer = agent.run("What is the weather in Paris?")
receipt = collector.finalize(final_answer=answer) # line 2
assert collector.verify(receipt) == [] # VALID
collector.save("receipt.json", workflow=receipt)
That is the whole integration: attach the collector, run, finalize. The receipt is a plain JSON object you can store, ship to an auditor, or render in a UI.
What the receipt contains
Workflow level (AER-1 Section 8, Table 2):
type,version,workflow_id,receipt_id,session_idgoal,statussteps: one record per agent step, seq 1..n in ordermerkle_root: Section 8.1 root over the ordered step receipt idsoutput_hash: SHA-256 of the final answerverify_url: where the verification procedure is documented
Step level (AER-1 Section 8, Table 3):
seq,receipt_id,tool,receipt_hash,started_at,ended_at,status
Each step receipt_hash is SHA-256 over the canonical JSON of what the
step actually did: tool name, arguments, observations, action output, and
error if any. The hash commits to the content; the receipt stays compact.
Verification
collector.verify(receipt) runs the full offline check and returns a
list of failure reasons, empty when valid:
- all Table 2 / Table 3 members present and well-formed
seqvalues exactly 1..n in order, no gaps- no two steps share a
receipt_id(MM-1) merkle_rootmatches the recomputed Section 8.1 root- strict RFC 3339 timestamps, lowercase UUIDs, 64-char hex digests
Tamper with any field and verification fails. Try it:
receipt["steps"][0]["tool"] = ""
assert collector.verify(receipt) != [] # fails, as it should
Notes
- Works with
ToolCallingAgentandCodeAgent. Planning steps are recorded astool: "plan"when the agent emits them. - Pass
goal=to the collector; SmolAgents does not forward the task text through step callbacks in current versions, so the constructor is the reliable place for it. session_iddefaults to a fresh UUID per collector; pass your own to correlate receipts across runs.verify_urldefaults to the AER-1 specification page; point it at your own verifier in production.
Spec
AER-1: Agent Execution Receipts, draft-zambo-aer1,
https://datatracker.ietf.org/doc/draft-zambo-aer1/
License
Apache-2.0
Metadata
Release files for aer1-smolagents 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aer1_smolagents-0.1.4.tar.gz | 9.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aer1_smolagents-0.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 18.0 kB
Release files / aer1_smolagents-0.1.4.tar.gz
| Download URL | aer1_smolagents-0.1.4.tar.gz |
|---|---|
| Size | 9.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b67b76576191c8ac9ba18b5fa314fddeb12ff310ee4ab9834faea1a3e9e33f07
|
|
BLAKE2b-256 checksum How to use checksums |
f10af9a882a9f9542c74a514a06e318317c74b8d6378392e23a02524fabdfb31
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
rambo-pypi-grab/0.1.0
|
Release files / aer1_smolagents-0.1.4-py3-none-any.whl
| Download URL | aer1_smolagents-0.1.4-py3-none-any.whl |
|---|---|
| Size | 8.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2c4459e90c59272b4c631e57c3b5cee5398fa8d6a847de1c3d9f991fdbae2a42
|
|
BLAKE2b-256 checksum How to use checksums |
aefab2f91f28adc3746477fc5ac12bd1414534e01e35aaa2e158d1d7ad7bc0a9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
rambo-pypi-grab/0.1.0
|