Metadata-Version: 2.5
Name: agent-adapter
Version: 1.1.0
Summary: One safety layer for every agent's tools: approvals, budgets, cost and traces, in any framework or none.
Project-URL: Documentation, https://agent-adapter.veloceai.in/
Project-URL: Changelog, https://agent-adapter.veloceai.in/changelog/
Author: Veloce AI
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: agents,approval,function-calling,guardrails,llm,mcp,tools
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: pydantic<3,>=2.12
Provides-Extra: a2a
Requires-Dist: a2a-sdk[http-server]<2,>=1.3; extra == 'a2a'
Provides-Extra: adk
Requires-Dist: google-adk<3,>=2.13; extra == 'adk'
Provides-Extra: ag-ui
Requires-Dist: ag-ui-protocol<2,>=1.0; extra == 'ag-ui'
Provides-Extra: ag2
Requires-Dist: ag2<2,>=1.2.2; extra == 'ag2'
Provides-Extra: agent-framework
Requires-Dist: agent-framework-core<2,>=1.23; extra == 'agent-framework'
Provides-Extra: agno
Requires-Dist: agno<4,>=3.1; extra == 'agno'
Provides-Extra: all
Requires-Dist: a2a-sdk[http-server]<2,>=1.3; extra == 'all'
Requires-Dist: a2wsgi<2,>=1.10; extra == 'all'
Requires-Dist: ag-ui-protocol<2,>=1.0; extra == 'all'
Requires-Dist: ag2<2,>=1.2.2; extra == 'all'
Requires-Dist: agent-framework-core<2,>=1.23; extra == 'all'
Requires-Dist: agno<4,>=3.1; extra == 'all'
Requires-Dist: aioboto3<16,>=13; extra == 'all'
Requires-Dist: anthropic<3,>=2.3; extra == 'all'
Requires-Dist: autogen-agentchat<0.8,>=0.7.5; extra == 'all'
Requires-Dist: autogen-core<0.8,>=0.7.5; extra == 'all'
Requires-Dist: claude-agent-sdk<0.3,>=0.2.150; extra == 'all'
Requires-Dist: google-genai<3,>=2.30; extra == 'all'
Requires-Dist: h11<1,>=0.16; extra == 'all'
Requires-Dist: haystack-ai<4,>=3.3; extra == 'all'
Requires-Dist: httpx<1,>=0.27; extra == 'all'
Requires-Dist: langchain-core<2,>=1.4; extra == 'all'
Requires-Dist: langgraph-checkpoint<5,>=4.3; extra == 'all'
Requires-Dist: langgraph<2,>=1.2.15; extra == 'all'
Requires-Dist: llama-index-core<0.15,>=0.14; extra == 'all'
Requires-Dist: mcp<3,>=2.3; extra == 'all'
Requires-Dist: openai-agents<1,>=0.24; extra == 'all'
Requires-Dist: openai<4,>=3.41; extra == 'all'
Requires-Dist: opentelemetry-api<2,>=1.39; extra == 'all'
Requires-Dist: pydantic-ai-slim<3,>=2.60; extra == 'all'
Requires-Dist: pyyaml<7,>=6; extra == 'all'
Requires-Dist: redis<9,>=8.1; extra == 'all'
Requires-Dist: smolagents<2,>=1.24; extra == 'all'
Requires-Dist: sqlalchemy[asyncio]<3,>=2.0.30; extra == 'all'
Requires-Dist: starlette<2,>=1.0; extra == 'all'
Requires-Dist: uvicorn<1,>=0.30; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: anthropic<3,>=2.3; extra == 'anthropic'
Provides-Extra: autogen
Requires-Dist: autogen-agentchat<0.8,>=0.7.5; extra == 'autogen'
Requires-Dist: autogen-core<0.8,>=0.7.5; extra == 'autogen'
Provides-Extra: camel
Requires-Dist: camel-ai<0.3,>=0.2.90; extra == 'camel'
Requires-Dist: mcp<2; extra == 'camel'
Provides-Extra: claude-agent
Requires-Dist: claude-agent-sdk<0.3,>=0.2.150; extra == 'claude-agent'
Requires-Dist: mcp<3,>=2.3; extra == 'claude-agent'
Provides-Extra: crewai
Requires-Dist: crewai<2,>=1.15; extra == 'crewai'
Provides-Extra: dbos
Requires-Dist: dbos<4,>=3.4; extra == 'dbos'
Provides-Extra: dev
Requires-Dist: aiosqlite<1,>=0.20; extra == 'dev'
Requires-Dist: fakeredis>=2.26; extra == 'dev'
Requires-Dist: hatchling>=1.27; extra == 'dev'
Requires-Dist: lupa>=2.2; extra == 'dev'
Requires-Dist: moto[dynamodb]<6,>=5; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: nox>=2024.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest-testmon<3,>=2.3; extra == 'dev'
Requires-Dist: pytest-xdist<4,>=3.8; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: sqlalchemy[asyncio]<3,>=2.0.30; extra == 'dev'
Requires-Dist: tzdata>=2024.1; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
Provides-Extra: dspy
Requires-Dist: dspy<4,>=3.4; extra == 'dspy'
Provides-Extra: dynamodb
Requires-Dist: aioboto3<16,>=13; extra == 'dynamodb'
Provides-Extra: gemini
Requires-Dist: google-genai<3,>=2.30; extra == 'gemini'
Provides-Extra: graphiti
Requires-Dist: graphiti-core<1,>=0.31; extra == 'graphiti'
Provides-Extra: haystack
Requires-Dist: haystack-ai<4,>=3.3; extra == 'haystack'
Provides-Extra: langchain
Requires-Dist: langchain-core<2,>=1.4; extra == 'langchain'
Provides-Extra: langgraph
Requires-Dist: langgraph-checkpoint<5,>=4.3; extra == 'langgraph'
Requires-Dist: langgraph<2,>=1.2.15; extra == 'langgraph'
Provides-Extra: llamaindex
Requires-Dist: llama-index-core<0.15,>=0.14; extra == 'llamaindex'
Provides-Extra: mcp
Requires-Dist: mcp<3,>=2.3; extra == 'mcp'
Provides-Extra: mem0
Requires-Dist: mem0ai<3,>=2.2; extra == 'mem0'
Provides-Extra: nemo
Requires-Dist: nemoguardrails<1,>=0.24; extra == 'nemo'
Provides-Extra: openai
Requires-Dist: openai<4,>=3.41; extra == 'openai'
Provides-Extra: openai-agents
Requires-Dist: openai-agents<1,>=0.24; extra == 'openai-agents'
Provides-Extra: openapi
Requires-Dist: httpx<1,>=0.27; extra == 'openapi'
Requires-Dist: pyyaml<7,>=6; extra == 'openapi'
Provides-Extra: otel
Requires-Dist: opentelemetry-api<2,>=1.39; extra == 'otel'
Provides-Extra: presidio
Requires-Dist: presidio-analyzer<3,>=2.2; extra == 'presidio'
Requires-Dist: presidio-anonymizer<3,>=2.2; extra == 'presidio'
Provides-Extra: pydantic-ai
Requires-Dist: pydantic-ai-slim<3,>=2.60; extra == 'pydantic-ai'
Provides-Extra: redis
Requires-Dist: redis<9,>=8.1; extra == 'redis'
Provides-Extra: semantic-kernel
Requires-Dist: semantic-kernel<2,>=1.44; extra == 'semantic-kernel'
Provides-Extra: server
Requires-Dist: h11<1,>=0.16; extra == 'server'
Requires-Dist: mcp<3,>=2.3; extra == 'server'
Requires-Dist: starlette<2,>=1.0; extra == 'server'
Requires-Dist: uvicorn<1,>=0.30; extra == 'server'
Provides-Extra: server-wsgi
Requires-Dist: a2wsgi<2,>=1.10; extra == 'server-wsgi'
Requires-Dist: h11<1,>=0.16; extra == 'server-wsgi'
Requires-Dist: mcp<3,>=2.3; extra == 'server-wsgi'
Requires-Dist: starlette<2,>=1.0; extra == 'server-wsgi'
Requires-Dist: uvicorn<1,>=0.30; extra == 'server-wsgi'
Provides-Extra: smolagents
Requires-Dist: smolagents<2,>=1.24; extra == 'smolagents'
Provides-Extra: sqlalchemy
Requires-Dist: sqlalchemy[asyncio]<3,>=2.0.30; extra == 'sqlalchemy'
Provides-Extra: strands
Requires-Dist: strands-agents<2,>=1.60; extra == 'strands'
Provides-Extra: temporal
Requires-Dist: temporalio<2,>=1.35; extra == 'temporal'
Provides-Extra: zep
Requires-Dist: zep-cloud<5,>=4.1; extra == 'zep'
Description-Content-Type: text/markdown

# Agent Adapter

**Build with any framework. Ship with one safety layer.**

Write a tool once, as a plain Python function. Every call to it, from any model or agent
framework, is approved, budgeted, priced and traced the same way.

LangChain, CrewAI or ADK help you build an agent. Agent Adapter makes every tool call that
agent makes safe, priced and traced, in any framework or in none.

It turns your functions into LLM tools, checks their schemas once at startup, and runs
every call through one executor that applies approval, budgets, guards, caching and cost
tracking. The core depends only on pydantic. Every framework is an optional extra, imported
lazily. No agent framework is needed: `run_tool_loop` with your own model is the agent loop,
and `Flow` is the graph.

## Install

Python 3.11 or later.

```bash
pip install agent-adapter                 # core (pydantic only)
pip install "agent-adapter[openai]"       # + the extras you need
```

## Quickstart

```python
import agent_adapter as aa


@aa.tool
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b


print(aa.call(add, a=1, b=2))  # 3: validated and guarded by the default executor
```

`@aa.tool` registers the function; `aa.call` runs it through the default executor. From
async code (FastAPI, ...) use `await aa.acall(add, a=1, b=2)`.

A tool with side effects asks a person first, once, and the approval survives a restart:

```python
@aa.tool(confirm=True)
def refund(order_id: str, amount: float) -> str:
    """Refund an order."""
    return f"refunded {amount} on {order_id}"


executor = aa.configure(approval=aa.DeferredApproval.sqlite("approvals.db"))
try:
    aa.call(refund, order_id="o-7", amount=500)
except aa.ApprovalPending as pending:
    approval_id = pending.approval_id  # show it to a reviewer
# ... later, in any process that opens the same file:
print(executor.resume_sync(approval_id, True, decided_by="ana").value)
```

## Why Agent Adapter

Each framework has its own tool format, its own idea of approval and no shared view of cost.
Change framework, or run two, and the rules are rewritten. Agent Adapter keeps them in one place.

| You need | Without it | With Agent Adapter |
|---|---|---|
| A person to approve risky actions | each framework's own flag, easy to bypass | a one-use approval the executor checks on every call, from any source; it survives a restart |
| Spending limits | prices added up by hand | `BudgetHook` per run, user or team, per day or month; every call priced in a ledger |
| Rules you can prove | settings spread across code | one policy file for access, approvals, guards and budgets; a signed, hash-chained audit log |
| To change framework | rewrite the tools | keep them: one module per framework, the same tools everywhere |

## Works with

| You have | Use |
|---|---|
| a script, a service, a notebook | `await executor.run(...)`; `run_sync(...)` from sync code (Flask, Django, Celery) |
| a raw model API | OpenAI, Gemini and Anthropic renderers, or the built-in `run_tool_loop` |
| an agent framework | one extra each: LangGraph, LangChain, ADK, OpenAI Agents SDK, Pydantic AI, LlamaIndex, CrewAI, Agent Framework, Agno, Strands, Haystack, Claude Agent SDK, Semantic Kernel, DSPy, smolagents, AutoGen, CAMEL-AI, AG2 |
| a durable engine | Temporal or DBOS run a `Flow` crash-safe |
| another agent, UI or language | MCP, an HTTP tool server, A2A, AG-UI and a TypeScript client |

## Features

| Area | What you get |
|---|---|
| Tools | `@registry.tool`, `registry.wrap()`, shaping, a portable-schema check, typed errors |
| Execution | `run`, `run_many`, `run_plan`, `parallel`; retry, timeout, hooks, run guards |
| Approval | one-use decisions; stores in SQLite, Redis, SQLAlchemy and DynamoDB; batch approval |
| Guardrails | `PIIGuard` (emails, phones, cards, IBANs, secrets), Presidio, NeMo, your own guard |
| Budgets and cost | per run, user or team budgets; a priced ledger; tool, response and prefix caches |
| Policy and audit | one policy file per tool, team and environment; a signed, hash-chained audit log |
| Flows and agents | route, map, join, pause, sub-flows, node cache; agents as tools and handoffs |
| Memory and RAG | per-user memory (SQLite, Mem0, Zep, Graphiti); a retriever tool with citations |
| Sandboxed code | `code_tool` over Docker or a hosted sandbox; every run needs approval |
| Evals | `run_eval` with graders, an LLM judge, reports, a pytest plugin and a cost advisor |
| Observability | an event catalogue, OpenTelemetry GenAI spans, and a local run viewer with a chat page |

## Safe by default

- A `confirm` tool runs only after an approval port approves, and each decision is used
  once. No port: denied. A framework's own "approved" signal never counts alone.
- The HTTP server refuses to start without an authenticator unless anonymous mode is chosen.
- The local viewer and chat bind 127.0.0.1 only, behind a private token link.
- Logs never carry secrets or the arguments of `confirm` tools.

## Command line

| Command | Does |
|---|---|
| `agent-adapter init fastapi my-service` | writes a starter project from a tested template |
| `agent-adapter doctor` | checks Python, extras, integrations and the pip index |
| `agent-adapter ui` | the local run viewer; with `--agent` or `--model`, a chat page |
| `agent-adapter eval app:agent cases.jsonl` | grades an agent on eval cases |
| `agent-adapter guide` | prints the guide for AI coding agents |

## Licence

Apache License 2.0. Copyright 2026 Veloce AI. Documentation:
<https://agent-adapter.veloceai.in/>
