Catalog PyPI
Back to packageRELEASE DETAILS

0.1.1

PyPI

agentanvil

Contract-based testing framework for LLM agents — hybrid metrics (objective + LLM-as-judge + human), multi-agent and A2A protocol support, deterministic record/replay envelope.

a2aagentsevaluationllmmulti-agentobservabilitytesting
Published29 Apr 2026
Direct dependencies19
Transitive dependenciesNot calculated

README

Version 0.1.1 documentation

AgentAnvil

Testing and evaluation framework for LLM agents.

Receives an agent (any Python code, any framework), runs it under a formalised contract, subjects it to test scenarios, evaluates its behaviour with hybrid metrics (objective + LLM-as-judge + human annotation with active sampling), and produces reproducible results with a deterministic replay envelope.

AgentAnvil is portable across the LLM ecosystem. It accepts two LLM backends:

  • AgentLoom (recommended) — cchinchilla-dev/agentloom. Production-grade runtime with resilience (circuit breaker, rate limiter, fallback), observability (OpenTelemetry, Prometheus), cost control, and built-in record/replay for deterministic experiments.
  • Direct providers — OpenAI, Anthropic, Google via DirectBackend (thin httpx adapter). No AgentLoom required. Use this to integrate AgentAnvil with LangChain / LangGraph / CrewAI / AutoGen agents without adopting additional dependencies.

Install

# Minimal — works with direct LLM providers (no AgentLoom required)
pip install agentanvil

# Recommended — with AgentLoom as backend for resilience, observability, record/replay
pip install "agentanvil[agentloom]"

# With Docker runner
pip install "agentanvil[docker]"

# All extras
pip install "agentanvil[all]"

Quick start

1. Define a contract:

# contract.yaml
name: my-agent
version: "1.0"
policies:
  - id: no_pii_leak
    description: Never reveal customer PII
    severity: critical
    check: llm
tasks:
  - id: answer_question
    description: Answer user questions accurately
    oracle: hybrid
constraints:
  max_latency_ms: 3000
  max_cost_per_interaction_usd: 0.05

2. Validate it:

agentanvil validate contract.yaml

3. Run an evaluation (direct backend, no AgentLoom):

export OPENAI_API_KEY=sk-...
agentanvil run path/to/agent/ \
  --contract contract.yaml \
  --backend direct \
  --provider openai \
  --threshold 0.85

4. Run an evaluation (AgentLoom backend with record for replay):

agentanvil run path/to/agent/ \
  --contract contract.yaml \
  --backend agentloom \
  --record recordings/run1.json \
  --budget 10.0

5. Replay offline (CI, reproducible, no API cost):

agentanvil run path/to/agent/ \
  --contract contract.yaml \
  --replay recordings/run1.json

6. As a library:

from agentanvil import AgentAnvil, AgentContract
from agentanvil.backends import DirectBackend  # or AgentLoomBackend

contract = AgentContract.from_yaml("contract.yaml")
ashut = AgentAnvil(contract=contract, backend=DirectBackend(provider="openai"))
result = ashut.run(agent_path="./my_agent/")
print(result.score, result.passed)

Architecture

AgentAnvil
├── Contracts   — formal model of expected agent behaviour (YAML + Python + static analysis)
├── Analyzer    — static analysis of agent code (AST, no LLM required)
├── Generator   — auto-generate test scenarios from contract + agent profile
├── Runner      — execute scenarios: SubprocessRunner, DockerRunner (K8sRunner planned for 0.4.0)
├── Evaluator   — hybrid metrics: objective + LLM-judge + human (active sampling)
├── Reporter    — HTML, JSON, and Markdown reports + CI quality gate
└── Record/Replay envelope — deterministic replay of LLM responses for reproducibility

Ecosystem

agentanvil
├── pydantic, httpx, pyyaml, typer, rich, anyio
└── [extra: agentloom]
    └── agentloom[observability]
        └── agentloom.contracts   (submodule — OTel span names, metric names, trace schemas)

The agentloom.contracts submodule is the typed contract between AgentLoom (emitter of OTel traces and Prometheus metrics) and AgentAnvil (consumer). It is installed automatically with agentanvil[agentloom] and can also be installed alone via pip install agentloom[contracts] for third-party consumers that want the schema without the runtime.

When using DirectBackend, AgentAnvil emits and consumes OpenTelemetry GenAI semantic conventions directly — no coupling to AgentLoom.

What AgentAnvil is not

  • Not a replacement for DSPy / TextGrad / ProTeGi. Automatic prompt optimisation is out of scope.
  • Not a replacement for LangSmith / LangFuse / Phoenix. It is a testing layer that consumes observability, not a production observability platform.
  • Not a replacement for Docker or Kubernetes — it uses them.

Status

Early alpha. See the roadmap for the 0.2.0 MVP plan.

License

MIT

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 32

Native requirements
19 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
anyio
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
anyio>=4.0runtime
httpx
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
httpx>=0.25runtime
pydantic
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pydantic<3.0,>=2.0runtime
pyyaml
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pyyaml>=6.0runtime
rich
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
rich>=13.0runtime
typer
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
typer>=0.9runtime
agentloom
Environment
{
  "extras": "",
  "marker": "extra == \"agentloom\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
agentloom>=0.4.0; extra == "agentloom"runtime
agentloom
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
agentloom>=0.4.0; extra == "all"runtime
cyclonedx-python-lib
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
cyclonedx-python-lib>=7.0; extra == "all"runtime
detect-secrets
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
detect-secrets>=1.5; extra == "all"runtime
docker
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
docker>=7.0; extra == "all"runtime
krippendorff
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
krippendorff>=0.6; extra == "all"runtime
networkx
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
networkx>=3.0; extra == "all"runtime
plotly
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
plotly>=5.0; extra == "all"runtime
presidio-analyzer
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
presidio-analyzer>=2.2; extra == "all"runtime
pyvis
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pyvis>=0.3; extra == "all"runtime
sarif-om
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
sarif-om>=1.0; extra == "all"runtime
scipy
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
scipy>=1.12; extra == "all"runtime
sigstore
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
sigstore>=3.0; extra == "all"runtime
statsmodels
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
statsmodels>=0.14; extra == "all"runtime
sarif-om
Environment
{
  "extras": "",
  "marker": "extra == \"cicd\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
sarif-om>=1.0; extra == "cicd"runtime
docker
Environment
{
  "extras": "",
  "marker": "extra == \"docker\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
docker>=7.0; extra == "docker"runtime
cyclonedx-python-lib
Environment
{
  "extras": "",
  "marker": "extra == \"replication\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
cyclonedx-python-lib>=7.0; extra == "replication"runtime
sigstore
Environment
{
  "extras": "",
  "marker": "extra == \"replication\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
sigstore>=3.0; extra == "replication"runtime
detect-secrets
Environment
{
  "extras": "",
  "marker": "extra == \"security\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
detect-secrets>=1.5; extra == "security"runtime
presidio-analyzer
Environment
{
  "extras": "",
  "marker": "extra == \"security\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
presidio-analyzer>=2.2; extra == "security"runtime
krippendorff
Environment
{
  "extras": "",
  "marker": "extra == \"stats\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
krippendorff>=0.6; extra == "stats"runtime
scipy
Environment
{
  "extras": "",
  "marker": "extra == \"stats\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
scipy>=1.12; extra == "stats"runtime
statsmodels
Environment
{
  "extras": "",
  "marker": "extra == \"stats\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
statsmodels>=0.14; extra == "stats"runtime
networkx
Environment
{
  "extras": "",
  "marker": "extra == \"viz\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
networkx>=3.0; extra == "viz"runtime
plotly
Environment
{
  "extras": "",
  "marker": "extra == \"viz\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
plotly>=5.0; extra == "viz"runtime
pyvis
Environment
{
  "extras": "",
  "marker": "extra == \"viz\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pyvis>=0.3; extra == "viz"runtime

Distribution files

1 collected
agentanvil-0.1.1-py3-none-any.whl

bdist_wheel · 32.45 KB · Python py3

Source file

Repository

PENDING
cchinchilla-dev/agentanvil

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 21:27 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:2ea77b53d2f19cd5c07f38798b688df13d369866ce99e099a7c4c60cfb9a9d14

Other collected versions

View all collected versions →