Metadata-Version: 2.4
Name: agentvet-py
Version: 0.1.0
Summary: Validate LLM-generated tool args before execution. Wraps tool functions with arg validation, raises ToolArgError with LLM-friendly retry hint. Python port of @mukundakatta/agentvet.
Project-URL: Homepage, https://github.com/MukundaKatta/agentvet-py
Project-URL: Issues, https://github.com/MukundaKatta/agentvet-py/issues
Project-URL: Source, https://github.com/MukundaKatta/agentvet-py
Project-URL: JS sibling, https://github.com/MukundaKatta/agentvet
Author-email: Mukunda Katta <mukunda.vjcs6@gmail.com>
License: MIT
License-File: LICENSE
Keywords: agents,ai,llm,pydantic,schema,tool-use,validation
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Provides-Extra: pydantic
Requires-Dist: pydantic>=2; extra == 'pydantic'
Description-Content-Type: text/markdown

# agentvet-py

[![PyPI](https://img.shields.io/pypi/v/agentvet-py.svg)](https://pypi.org/project/agentvet-py/)
[![Python](https://img.shields.io/pypi/pyversions/agentvet-py.svg)](https://pypi.org/project/agentvet-py/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

**Validate LLM-generated tool args before execution.** Wraps tool functions with arg validation; raises `ToolArgError` with an LLM-friendly retry hint. Zero runtime dependencies.

Python port of [@mukundakatta/agentvet](https://github.com/MukundaKatta/agentvet).

## Install

```bash
pip install agentvet-py
# pydantic adapter is optional:
pip install "agentvet-py[pydantic]"
```

## Usage

```python
from agentvet import vet, adapters, ToolArgError

def send_email_impl(args):
    return "sent to " + args["to"]

schema = adapters.shape({"to": "str", "subject": "str", "body": "str?"})
send_email = vet(name="send_email", schema=schema, fn=send_email_impl)

# Happy path:
send_email({"to": "alice@example.com", "subject": "hello"})  # 'sent to alice@example.com'

# Bad args -> raises ToolArgError BEFORE the tool runs:
try:
    send_email({"subject": "hello"})  # missing 'to'
except ToolArgError as err:
    feedback = err.to_llm_feedback()
    # Send `feedback` back to the LLM as a tool_result with is_error=True.
```

## With pydantic

```python
from pydantic import BaseModel, EmailStr
from agentvet import vet, adapters

class SendEmail(BaseModel):
    to: EmailStr
    subject: str
    body: str | None = None

send_email = vet(
    name="send_email",
    schema=adapters.pydantic(SendEmail),
    fn=lambda args: send_impl(**args),
)
```

## Async tools

`vet()` preserves the sync/async nature of `fn`:

```python
async def fetch_impl(args):
    return await api.get(args["url"])

fetch = vet(name="fetch", schema=adapters.shape({"url": "str"}), fn=fetch_impl)
result = await fetch({"url": "https://example.com"})
```

## API

### `vet(*, name, schema, fn, on_error=None) -> wrapped_fn`

Wraps a tool function. Validates args BEFORE calling the tool; raises `ToolArgError` on failure (or invokes `on_error(err, args)` if provided -- return a non-`None` value to substitute as the tool's return).

### `validate(name, schema, args) -> ValidationResult`

One-shot validation. Returns `ValidationResult(ok=bool, value=..., error=ToolArgError | None)`.

### `adapters.shape(spec)`

Tiny built-in shape checker. Spec format: `{"field": "str"|"int"|"float"|"bool"|"list"|"dict", ...}`. Suffix with `?` for optional. Accepts JS sibling synonyms (`"string"`, `"number"`, `"array"`, `"object"`) too.

### `adapters.fn(predicate, error_builder?)`

Predicate adapter. `predicate(args) -> bool`. `error_builder` may be a string or a callable returning a string.

### `adapters.pydantic(model_cls)`

Wraps a pydantic v2 `BaseModel`. Requires `pip install agentvet-py[pydantic]`.

### `adapters.zod(schema)`

Compatibility shim for `safeParse`-style validators (rare in Python; included for parity).

### `ToolArgError`

Carries `tool`, `validation_error`, `args`. `.to_llm_feedback()` returns the retry message you send back to the LLM.

## API differences from the JS sibling

* `vet()` uses keyword-only args (`vet(name=..., schema=..., fn=..., on_error=...)`).
* Validators return Python dicts: `{"valid": True, "value": ...}` / `{"valid": False, "error": str}`.
* `adapters.pydantic` replaces `adapters.zod` as the natural Python adapter.
* Type names in `shape()` use Python conventions (`"str"`, `"int"`, `"list"`, `"dict"`); JS synonyms (`"string"`, `"number"`, `"array"`, `"object"`) are accepted.
* `ToolArgError.to_llm_feedback()` is `snake_case` (mirrors JS `toLLMFeedback()`).

See the JS sibling's [README](https://github.com/MukundaKatta/agentvet) for the full design notes.
