Metadata-Version: 2.4
Name: pyguardops
Version: 0.0.1
Summary: Runtime safety proxy for multi-agent AI systems
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: python-dotenv
Provides-Extra: telemetry
Requires-Dist: langfuse>=3.0; extra == "telemetry"
Requires-Dist: mlflow; extra == "telemetry"
Requires-Dist: opentelemetry-api; extra == "telemetry"
Provides-Extra: embeddings
Requires-Dist: sentence-transformers; extra == "embeddings"
Provides-Extra: llm
Requires-Dist: openai>=1.0; extra == "llm"
Provides-Extra: pydantic
Requires-Dist: pydantic>=2.0; extra == "pydantic"
Provides-Extra: all
Requires-Dist: langfuse>=3.0; extra == "all"
Requires-Dist: mlflow; extra == "all"
Requires-Dist: opentelemetry-api; extra == "all"
Requires-Dist: sentence-transformers; extra == "all"
Requires-Dist: openai>=1.0; extra == "all"
Requires-Dist: pydantic>=2.0; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"

# pyguardops

Runtime safety proxy for multi-agent AI systems. Intercepts agent outputs in real-time — before bad values (or bad decisions) cascade downstream — and auto-corrects or halts the pipeline based on rules you define.

## What it does

- **DATA_OVERRIDE** — detects a breach, corrects the unsafe field, pipeline continues
- **SHORT_CIRCUIT** — raises `GuardOpsRefusalIntercept`, halts the pipeline immediately
- **Telemetry (optional)** — every breach can be logged to Langfuse (live spans) and MLflow (retraining artifacts)

## Install

```bash
pip install pyguardops==0.0.1
```

The command-line tool and the Python import both remain `guardops`:

```bash
guardops init
```

```python
from guardops import guard_runtime
```

## Quickstart

```bash
guardops init
```

This creates two files in your current directory:
- `guard_manifest.json` — your rules
- `custom_guards.py` — starter custom check/recovery functions

Edit `guard_manifest.json` to describe your real rules, then decorate your agent function:

```python
from guardops import guard_runtime

@guard_runtime(node_name="PricingAgent")   # must match a key in guard_manifest.json
async def pricing_agent(payload: dict) -> dict:
    payload["price"] = compute_price(payload)
    return payload   # GuardOps checks the return value before it goes further
```

`@guard_runtime` works on both `async def` and plain `def` functions — no separate decorator needed for sync code.

Validate your manifest any time:
```bash
guardops validate --manifest guard_manifest.json
```

## Manifest basics

Three built-in condition types need no custom Python at all:

```json
{
  "PricingAgent": [
    {
      "metric_key": "price",
      "condition_type": "UNDER_FLOOR",
      "boundary_limit": 5.00,
      "fallback_value": 5.00,
      "strategy": "DATA_OVERRIDE",
      "breach_tag": "PRICE_UNDER_FLOOR"
    }
  ]
}
```

`UNDER_FLOOR`, `OVER_CEILING`, and `REGEX_MISMATCH` cover most simple checks. For anything more — comparing fields to each other, semantic similarity, LLM-as-judge — use `CUSTOM_CHECK`, which points at a function in `custom_guards.py`:

```json
{
  "condition_type": "CUSTOM_CHECK",
  "boundary_limit": "custom_guards.check_my_condition",
  "fallback_value": "custom_guards.recover_my_value",
  "breach_tag": "MY_CUSTOM_BREACH",
  "parameters": { "threshold": 0.85 }
}
```

Custom functions receive `(value, rule_config)`. `rule_config` always includes `metric_key`, `condition_type`, `boundary_limit`, `fallback_value`, `strategy`, `breach_tag`, `parameters` (whatever you defined above — free-form), and `payload` (the full agent output for this call, so you can read sibling fields alongside `value`).

## Telemetry — fully optional, off by default

GuardOps runs completely standalone with zero telemetry setup. Turning it on requires **two things together**:

**1. Install the extra:**
```bash
pip install "pyguardops[telemetry]"
```

**2. Set these in your app's environment** (via your own `.env`, loaded by your own app *before* your first `@guard_runtime` call — GuardOps does not load `.env` files itself):

```dotenv
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
MLFLOW_TRACKING_URI=http://localhost:5000
```

Both steps are required. Installing the extra without setting the variables leaves telemetry off. Setting the variables without installing the extra also leaves it off (the import fails gracefully). Neither half alone does anything — and that's intentional, so a bare `pip install pyguardops` never pulls in `mlflow`/`langfuse` for anyone who doesn't want them.

If you already run Langfuse and/or MLflow elsewhere in your stack, point these variables at your existing setup — GuardOps logs into your existing project/experiment, it doesn't spin up a separate instance.

## Optional extras

```bash
pip install "pyguardops[embeddings]"   # semantic similarity in custom guards (sentence-transformers)
pip install "pyguardops[llm]"          # LLM-as-judge custom guards (openai)
pip install "pyguardops[pydantic]"     # Pydantic BaseModel payload support
pip install "pyguardops[all]"          # everything above
```

Payloads can be plain `dict` or Pydantic `BaseModel` instances — GuardOps detects and round-trips both automatically.

## Development

```bash
git clone <this-repo>
cd pyguardops
pip install -e ".[dev]"
pytest tests/ -v
```

## License

MIT
