A runtime that makes LLM agents production-safe by default.
Project description
Rampart
A runtime that makes LLM agents production-safe by default.
Built for teams who need more than a prototype framework when shipping agents to production.
The problem
Prototypes work in demos. Production is different.
- A process crash at step 7 of 12 loses everything — you restart from zero.
- A prompt change silently breaks tool selection; nobody notices until a user report.
- A runaway loop burns $800 in API credits before anyone wakes up.
- The Replit incident (July 2025) — an agent deleted 1,200+ production records despite an explicit code-and-action freeze — wasn't an edge case. It was the predictable result of a framework with no permission enforcement model.
Every team shipping agents today independently assembles Temporal + LangGraph + Langfuse + unittest.mock, paying the integration tax on every project. Rampart ships all of it in the box.
Quickstart
pip install rampart
pip install "rampart[sqlite]" # SQLite checkpointing
from dataclasses import dataclass, field
from rampart import graph, node, tool, AgentState, RunConfig
from rampart.checkpointers import SqliteCheckpointer
@dataclass
class ResearchState(AgentState):
query: str = ""
results: list[str] = field(default_factory=list)
summary: str = ""
@tool(name="web_search")
async def web_search(query: str) -> list[str]:
... # your implementation
@node(retries=3, retry_on=(TimeoutError,), timeout_seconds=30)
async def search_node(state: ResearchState, tools) -> ResearchState:
results = await tools.web_search(query=state.query)
return state.update(results=results)
@node(timeout_seconds=60)
async def summarize_node(state: ResearchState) -> ResearchState:
return state.update(summary=f"Found {len(state.results)} results for: {state.query}")
@graph(name="research", version="1.0.0")
async def research_pipeline(state: ResearchState) -> ResearchState:
state = await search_node(state)
state = await summarize_node(state)
return state
result = await research_pipeline.run(
input=ResearchState(query="quantum computing"),
config=RunConfig(
thread_id="session-001",
checkpointer=SqliteCheckpointer("./agent.db"),
),
)
print(result.state.summary)
print(f"Cost: ${result.trace.total_cost_usd:.4f}")
Features
Durable execution — crash anywhere, resume from the last step
State is checkpointed after every node. A process restart, OOM kill, or rolling deployment resumes from the last committed step without re-executing completed work.
result = await research_pipeline.resume(
thread_id="session-001",
config=RunConfig(checkpointer=SqliteCheckpointer("./agent.db")),
)
LangGraph checkpoints within a process. Rampart checkpoints survive restarts.
Testing without live APIs
mock_tools() and cassette record/replay are built into the framework. Your CI pipeline makes no network calls and produces identical results on every run.
# Record once against live APIs
async with cassette.record("tests/fixtures/research.json"):
await research_pipeline.run(input=ResearchState(query="quantum computing"))
# Replay forever — zero cost, zero flakiness
async with cassette.replay("tests/fixtures/research.json"):
result = await research_pipeline.run(input=ResearchState(query="quantum computing"))
assert result.state.summary != ""
# Mock specific tools for unit tests
async with research_pipeline.mock_tools({
"web_search": MockTool.returns([{"text": "result 1"}]),
"send_email": MockTool.noop(),
}) as ctx:
result = await research_pipeline.run(input=state)
assert ctx.calls["web_search"].count == 1
assert ctx.calls["send_email"].count == 0
Eval as a deployment gate
EvalSuite runs in CI and blocks promotion when assertions fail. Plug it into your pipeline the same way you'd block on a failing test.
suite = EvalSuite(
name="research-pipeline-v2",
graph=research_pipeline,
cases=[
EvalCase(
id="basic-query",
input=ResearchState(query="quantum computing"),
cassette="tests/fixtures/research.json",
assertions=[
ToolCallAssertion(tool_name="web_search", called=True, min_times=1),
SchemaAssertion(
predicate=lambda s: len(s.summary) > 100,
description="summary must be substantive",
),
],
),
],
pass_rate_gate=1.0,
)
results = await suite.run()
results.assert_gates() # raises EvalGateFailure if any case fails
Permission scopes
Every graph declares what it's allowed to do. Violations are blocked and logged before execution.
@graph(
name="data-analyst",
permissions=PermissionScope(
tools=["query_db", "write_report"],
network=NetworkPermission(
allowed_domains=["api.internal.company.com"],
deny_all_others=True,
),
),
)
async def analyst_pipeline(state): ...
Enforcement runs at three layers: the tool call boundary, the Python HTTP transport (httpx, requests, and urllib are patched at import time), and an optional sandboxed execution context. An agent cannot escape by calling httpx.get() directly.
Budget envelopes
Budgets are hard runtime constraints checked before each node, not alerts you configure after the first incident.
result = await research_pipeline.run(
input=state,
config=RunConfig(thread_id="session-xyz"),
budget=Budget(
max_llm_cost_usd=0.50,
max_tool_calls=20,
max_wall_time_seconds=120,
on_exceeded="hard_stop",
),
)
# result.status == "budget_exceeded" — never silently exceeded
How it compares
| Rampart | LangGraph | CrewAI | AutoGen | |
|---|---|---|---|---|
| Crash recovery (survives restart) | ✅ | ⚠️ in-process only | ❌ | ❌ |
| Testing without live APIs | ✅ built-in | ⚠️ roll your own | ⚠️ roll your own | ⚠️ roll your own |
| CI eval gate | ✅ built-in | ❌ | ❌ | ❌ |
| Permission enforcement | ✅ enforced | ❌ | ❌ | ❌ |
| HTTP transport interception | ✅ | ❌ | ❌ | ❌ |
| Budget hard limits | ✅ | ❌ | ❌ | ❌ |
| Multi-agent composition | ✅ | ✅ | ✅ | ✅ |
LangGraph is the closest comparison. Rampart has the same decorator ergonomics and adds durable multi-process recovery, built-in testing primitives, eval gates, and permission enforcement. CrewAI and AutoGen prioritize role-based multi-agent patterns and leave infrastructure concerns to you.
Multi-agent composition
from rampart import chain, parallel, supervisor
# Sequential
pipeline = chain(fetch_graph, analyze_graph, report_graph)
# Fan-out then join
pipeline = parallel(search_graph, db_graph).join(synthesize_graph)
# Router + specialists
pipeline = supervisor(
router=classifier_graph,
specialists={"billing": billing_graph, "support": support_graph},
max_handoffs=5,
)
result = await pipeline.run(input=state, config=config)
Checkpoint backends
| Backend | Install | Use case |
|---|---|---|
MemoryCheckpointer |
built-in | Tests, local dev |
SqliteCheckpointer |
pip install "rampart[sqlite]" |
Single-process, local |
PostgresCheckpointer |
pip install "rampart[postgres]" |
Production, multi-process |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rampart-0.2.0.tar.gz.
File metadata
- Download URL: rampart-0.2.0.tar.gz
- Upload date:
- Size: 57.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b6508b656648a45c50209b28c1390f342ebc806e8ad45798fe1de0a2b9351f0
|
|
| MD5 |
d4db41aeb1b3b0f509fefa14078ff3a0
|
|
| BLAKE2b-256 |
b6ab0aa346ac21a22d8a250359723d276a12349e58ccdcfc13f06155ed65d022
|
Provenance
The following attestation bundles were made for rampart-0.2.0.tar.gz:
Publisher:
publish.yml on npow/rampart
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rampart-0.2.0.tar.gz -
Subject digest:
9b6508b656648a45c50209b28c1390f342ebc806e8ad45798fe1de0a2b9351f0 - Sigstore transparency entry: 1060842962
- Sigstore integration time:
-
Permalink:
npow/rampart@edebbd68e7d33233db8cd550fe4db194d2c6d1e8 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/npow
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@edebbd68e7d33233db8cd550fe4db194d2c6d1e8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rampart-0.2.0-py3-none-any.whl.
File metadata
- Download URL: rampart-0.2.0-py3-none-any.whl
- Upload date:
- Size: 48.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
12c9a6e7c0d5819c30ed59667e974965227af7c1495c788ffabad434c8cad610
|
|
| MD5 |
a9cbe3e4a84963bb2661f483347ee2a5
|
|
| BLAKE2b-256 |
8344792bb31831053f677d1fc3fa3c8335a566454b06566ca2fb3a8395855ad0
|
Provenance
The following attestation bundles were made for rampart-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on npow/rampart
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rampart-0.2.0-py3-none-any.whl -
Subject digest:
12c9a6e7c0d5819c30ed59667e974965227af7c1495c788ffabad434c8cad610 - Sigstore transparency entry: 1060843157
- Sigstore integration time:
-
Permalink:
npow/rampart@edebbd68e7d33233db8cd550fe4db194d2c6d1e8 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/npow
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@edebbd68e7d33233db8cd550fe4db194d2c6d1e8 -
Trigger Event:
push
-
Statement type: