Metadata-Version: 2.4
Name: midchain-governance
Version: 0.2.0
Summary: Selective re-verification of safety/compliance constraints across multi-step AI processes
Author-email: Subramanya Lingaraju <connect.subbu08@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Subramanya Lingaraju/ SmartRoutIQ
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/subramanya-dev/midchain-governance
Project-URL: Issues, https://github.com/subramanya-dev/midchain-governance/issues
Project-URL: Changelog, https://github.com/subramanya-dev/midchain-governance/blob/master/CHANGELOG.md
Keywords: llm,governance,guardrails,multi-agent,compliance,safety
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

# midchain-governance

[![PyPI version](https://img.shields.io/pypi/v/midchain-governance.svg)](https://pypi.org/project/midchain-governance/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

A small library for re-verifying safety/compliance constraints across a
multi-step AI process — not just at the entry point.

## The problem this addresses

Most guardrail setups check a request once, at the start. But in a
multi-step AI pipeline (agent chains, model failover, tool-calling
sequences), a violation can be introduced *after* that check — a later
step's output, not the original prompt, is what actually reaches the
user. An entry-only check can't see that.

## What this is — and isn't

**Is:** a small, tested implementation of interval-based re-verification,
with a specific guarantee: whatever output is about to be delivered is
always checked, regardless of how many steps ran or were planned.

**Isn't:** a novel idea. Re-checking constraints across multi-step AI
processes is an active area — see `docs/CITATIONS.md` for directly
related published work (compliance gating, constraint drift across
agent delegation, context-compaction-driven constraint decay). This
library doesn't propose a new mechanism class; it's a specific,
tested, reusable implementation of an established idea, plus a
simulation exploring the catch-rate/latency tradeoff between three
concrete policies.

See `docs/PAPER_DRAFT.md` for the full writeup, including honest
limitations, and `experiments/RESULTS.md` for what the simulation
actually found (and didn't).

## Why use this

Three concrete advantages over what most guardrail setups do by default:

1. **Most guardrail setups only check the user's prompt.** If an agent
   calls several tools in sequence, or a request fails over between
   models, whatever comes back from step 2 or the second model attempt
   often never gets checked — only the final output does, if that. This
   library closes that gap deliberately, by design, not as an
   afterthought.
2. **"Check everything" is expensive; "check once" is unsafe.** The
   `selective` policy gives you a middle setting that, *in simulation*
   under the assumptions in `experiments/RESULTS.md`, recovers most of
   full re-checking's catch-rate benefit at roughly half the latency
   cost — so you don't have to choose between safe and fast for every
   request. Re-measure on your own guardrails before treating those
   numbers as production forecasts.
3. **The delivery guarantee is unconditional.** Whatever actually
   reaches the end user gets checked, regardless of which step or which
   failover candidate produced it — even in a process where you don't
   know in advance how many steps will run. This is the guarantee with
   a real bug fix and regression test behind it (see CHANGELOG.md), not
   just a design intention.

## Install

```bash
pip install midchain-governance
```

For local development instead (editable install from a clone):

```bash
pip install -e .
```

Or copy `src/midchain_governance/` into your project directly. Optional: `pip install -e ".[dev]"` for pytest.

## Quick start

```python
from midchain_governance import GovernanceGate, run_sequential_pipeline

gate = GovernanceGate(interval=2, always_check_final=True)

def my_compliance_check(output: str) -> bool:
    # wire this to your real moderation/guardrail API
    return "SECRET" not in output

steps = [lambda: "step 1", lambda: "step 2", lambda: "step 3"]
result = run_sequential_pipeline(steps, check_fn=my_compliance_check, gate=gate)
```

For failover / retry logic (candidates are alternatives, not a mandatory
sequence — the first passing one wins):

```python
from midchain_governance import run_failover_chain

candidates = [lambda: call_model_a(), lambda: call_model_b()]
result = run_failover_chain(candidates, check_fn=my_compliance_check)
```

See `examples/basic_usage.py` for a runnable version of both.

## Real-time example: LLM provider failover with compliance checking

A realistic setup — try OpenAI, fail over to Anthropic if the response is
blocked or the provider errors. Uses `litellm` only as an *illustrative*
client (optional; not a package dependency) plus a moderation-style check:

```python
import litellm
from midchain_governance import GovernanceGate, run_failover_chain

def call_openai():
    resp = litellm.completion(
        model="gpt-4o",
        messages=[{"role": "user", "content": user_prompt}],
    )
    return resp.choices[0].message.content

def call_anthropic():
    resp = litellm.completion(
        model="claude-sonnet-4-6",
        messages=[{"role": "user", "content": user_prompt}],
    )
    return resp.choices[0].message.content

def real_compliance_check(output: str) -> bool:
    # Swap this for whatever you actually use: a moderation endpoint,
    # a PII/regex scanner, a policy-tuned classifier, or an
    # LLM-as-judge prompt. This example uses litellm's moderation call.
    result = litellm.moderation(input=output)
    return not result.results[0].flagged

gate = GovernanceGate(interval=2, always_check_final=True)

response = run_failover_chain(
    candidate_fns=[call_openai, call_anthropic],
    check_fn=real_compliance_check,
    gate=gate,
)
```

If OpenAI's response is flagged, this automatically tries Anthropic next
— the request doesn't abort, and whichever response actually gets
returned has been checked, guaranteed, regardless of which provider
produced it.

### Real-time example: fixed multi-step agent pipeline

For a pipeline where every request walks the same steps (e.g. fetch data
→ analyze → draft a response), each with its own agent/model call:

```python
from midchain_governance import GovernanceGate, run_sequential_pipeline

gate = GovernanceGate(interval=2, always_check_final=True)

steps = [
    lambda: fetch_agent.run(query),
    lambda: analyze_agent.run(fetched_data),
    lambda: writer_agent.run(analysis),
]

result = run_sequential_pipeline(steps, check_fn=real_compliance_check, gate=gate)
```

With `interval=2`, this checks after step 1 (entry), step 2, and always
step 3 (final) — so a violation introduced by the analyze step gets
caught before the writer agent ever sees it, not just after the final
draft is produced.

## Three checking policies

| Config | Behavior |
|---|---|
| `GovernanceGate(interval=<very large>, always_check_final=False)` | Entry-only ("once") |
| `GovernanceGate(interval=1)` | Every step ("every_hop") |
| `GovernanceGate(interval=K, always_check_final=True)` | Selective: entry, every Kth step, and always the delivered output |

## Why "selective" over "every step"

In simulation (see `experiments/`), interval-based selective checking
recovered most of full re-checking's catch-rate benefit at roughly half
the latency cost. Full numbers, methodology, and — importantly — the
assumptions those numbers depend on, are in `experiments/RESULTS.md`.
**These are simulation results, not production measurements.** Don't
cite the specific percentages as what you'll see on real traffic without
re-measuring against your own guardrail's real detection rate.

## A real bug this caught (kept as a regression test)

Early in development, the "final check" guarantee used the *planned*
number of steps to decide when to force a check. In a variable-length
process like failover — where most requests only make one attempt — that
meant an early-succeeding step could skip the check entirely. Fixed by
checking whatever's about to be delivered directly (`before_delivery=True`),
independent of how many steps were planned. See
`tests/test_gate.py::test_regression_early_success_still_gets_final_check`.

## Reference checkers (for trying the library quickly)

`midchain_governance.checkers` ships a few basic starting checkers so
you can run the examples without writing `check_fn` from scratch:

```python
from midchain_governance import KeywordChecker, RegexChecker, CompositeChecker, COMMON_PII_PATTERNS

checker = CompositeChecker([
    KeywordChecker(["forbidden", "secret"]),
    RegexChecker(COMMON_PII_PATTERNS),  # basic SSN/card-number shape matching
])

result = run_sequential_pipeline(steps, check_fn=checker, gate=gate)
```

**These are demo-grade, not production-grade.** Keyword and regex
matching can't understand meaning or context — they're here so you have
something to plug in immediately, not as a real content-moderation
solution. Replace with an actual moderation API or classifier before
using this for anything that matters.

## Honest limitations

- **This is a small, focused library, not a full guardrail system.** It
  decides *when* to check; `midchain_governance.checkers` now ships a
  few basic reference checkers (`KeywordChecker`, `RegexChecker`,
  `CompositeChecker`) so you can try the library without writing one
  from scratch — but these are keyword/regex matching, not semantic
  understanding. For anything real, wire `check_fn` to an actual
  moderation API or trained classifier instead.
- **The catch-rate/latency numbers are from simulation, not production
  traffic.** `experiments/RESULTS.md` documents this in full, including
  the assumed parameters those numbers depend on — re-measure against
  your own guardrail's real detection rate before treating them as a
  forecast.
- **Adoption is currently zero.** This is a brand-new repo with one
  contributor and no other users yet. Its usefulness in practice depends
  entirely on people actually discovering, integrating, and reporting
  back on it — which hasn't happened yet. Take the design choices here
  (like `interval=2` as a sensible default) as a reasonable starting
  point, not a battle-tested one.

## Structure

```
src/midchain_governance/   the library
tests/                     outcome-level tests (not just mechanism tests)
experiments/                the simulation, its results, and honest limitations
examples/                  runnable usage examples
docs/                      citations, limitations, full paper draft
```

## Running tests

```bash
python3 -m unittest discover tests
```

## License

MIT — see LICENSE.

## Contributing

See CONTRIBUTING.md.
