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.
For per-tool-call receipts via smolagents step callbacks, see smolagents-aer1.
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
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/
See it live
Your receipt is offline-verifiable, but you can also check it on the live verifier:
- Copy the receipt JSON your code produced
- Paste it at https://zambo.dev/verify
- See the verification result with the Merkle root and step hashes
Or mint a live receipt directly: run any call at https://zambo.dev/demo and get a shareable receipt URL like https://zambo.dev/run/.
License
Apache-2.0
Metadata
Release files for aer1-smolagents 0.1.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| aer1_smolagents-0.1.7.tar.gz | 9.8 kB | Without provenance | Details for aer1_smolagents-0.1.7.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| aer1_smolagents-0.1.7-py3-none-any.whl | Python 3 | none | any | Details for aer1_smolagents-0.1.7-py3-none-any.whl |
Total release size: 18.3 kB
Release files / aer1_smolagents-0.1.7.tar.gz
| Download URL | aer1_smolagents-0.1.7.tar.gz |
|---|---|
| Size | 9.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
733f3cbc8b22485543fec80b4843c071ba75555775e00c4aff66ebd4d359b4e7
|
|
BLAKE2b-256 checksum How to use checksums |
5b50e14e4ff1cdf56572d2589be85357fc73781c17213d34ef28f1790e4c46ad
|
| 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.7-py3-none-any.whl
| Download URL | aer1_smolagents-0.1.7-py3-none-any.whl |
|---|---|
| Size | 8.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2da1e3dc984f2a6634342e936717f18f19e8cdfd428599f650bc871e47c6aaa9
|
|
BLAKE2b-256 checksum How to use checksums |
aacc2fdac85acc2e48160a9434b3e24fbda5cc0a751da39861632fcd9ed2879e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
rambo-pypi-grab/0.1.0
|
Security for aer1-smolagents 0.1.7
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.