Metadata-Version: 2.4
Name: sansin-core
Version: 0.1.0
Summary: Local-first AI agent safety engine with Thompson Sampling. Score tool calls, learn from corrections, zero cloud dependency.
Author-email: Sansin <hello@sansin.ai>
License-Expression: MIT
Project-URL: Homepage, https://sansin.ai
Project-URL: Repository, https://github.com/choongylol/sansin-core
Project-URL: Documentation, https://docs.sansin.ai
Keywords: ai,agent,safety,guardrails,thompson-sampling,mcp,ollama,local
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
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: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: mcp
Requires-Dist: mcp>=1.2.0; extra == "mcp"
Dynamic: license-file

# sansin-core

Local-first AI agent safety engine. Score tool calls, learn from corrections, zero cloud dependency.

Thompson Sampling + context-aware heuristics. Ships with pre-trained priors so it works out of the box. Runs anywhere Python runs, stdlib only, no API keys.

```
pip install sansin-core
```

## Quick Start

```python
from sansin_core import SansinLocal

gate = SansinLocal()

# Score a tool call
decision = gate.check("send_email", {"recipients": 500, "irreversible": True})
print(decision.allow)        # False
print(decision.risk_score)   # 0.9
print(decision.reason)       # "deny"

# Override to teach the engine
gate.override(decision.id, "allow", "approved by admin")

# Next time, the engine remembers
decision = gate.check("send_email", {"recipients": 500})
# risk_score is now lower — it learned
```

## How It Works

Every tool call gets a risk score from 0.0 (safe) to 1.0 (dangerous).

**Heuristics** classify tools by keyword. `read_file` starts low-risk. `delete_database` starts high-risk. Context modifiers like `{"recipients": 500}` or `{"irreversible": True}` push scores higher.

**Thompson Sampling** learns from your corrections. Override a blocked call with "allow" and the engine updates its Beta distribution for that tool. After ~10 overrides per tool, learned behavior starts blending with heuristics. After ~50, the engine mostly trusts what it learned from you.

**Community priors** ship with the package (15 common tools, ~20-override quality each). New installs aren't starting from zero.

## MCP Safety Proxy

Drop-in firewall between any MCP client and upstream server. The agent never knows Sansin exists.

```
pip install sansin-core[mcp]
sansin-mcp-proxy --upstream stdio://npx -y @modelcontextprotocol/server-filesystem /tmp
```

Point your MCP client at the proxy instead of the upstream server. Every tool call is scored before forwarding. High-risk calls are blocked with an explanation.

The proxy exposes three management tools (configurable prefix, default `sansin_`):

- `sansin_override` — override a blocked decision
- `sansin_status` — view engine statistics and per-tool priors
- `sansin_decisions` — list recent decisions for auditing

```
# Custom prefix to avoid collisions
sansin-mcp-proxy --upstream stdio:///path/to/server --prefix safety_

# Fail-closed mode for regulated environments
sansin-mcp-proxy --upstream stdio:///path/to/server --fail-closed
```

## Interactive Demo

See the learning engine in action with 50 simulated decisions:

```
sansin-demo
```

Watches Thompson Sampling learn in real-time with ASCII confidence bars.

## API Reference

### `SansinLocal(db_path, fail_closed, load_community)`

| Parameter | Default | Description |
|-----------|---------|-------------|
| `db_path` | `~/.sansin/decisions.db` | SQLite database path |
| `fail_closed` | `False` | Block on internal errors instead of allowing |
| `load_community` | `True` | Load pre-trained community priors on first run |

### `gate.check(tool_name, context) -> Decision`

Score a tool call. Returns immediately.

```python
decision = gate.check("delete_file", {"path": "/etc/passwd"})
decision.allow          # bool — should the call proceed?
decision.risk_score     # float 0.0-1.0
decision.safe_probability  # float — P(safe) from Thompson Sampling
decision.certainty      # float — how confident the engine is
decision.reason         # str — "allow", "escalate", or "deny"
decision.id             # str — UUID for override reference
```

### `gate.override(decision_id, correct_action, reason) -> bool`

Teach the engine. `correct_action` must be `"allow"` or `"block"`.

### `gate.status() -> dict`

Engine statistics: decision counts, per-tool priors (alpha, beta, override count).

### `gate.export_priors(path)` / `gate.import_priors(path)`

Export/import learned priors as JSON. Share across machines or teams.

## Design Decisions

**Fail-open by default.** A safety product that breaks your agent's workflow gets uninstalled. Fail-closed mode is available for regulated environments.

**Context-aware keywords.** "write" is medium-risk by default. It only escalates to high-risk when combined with blast radius modifiers like "all", "every", "bulk", "batch", "mass" in the context.

**SQLite WAL mode.** Thread-safe, handles concurrent reads with a single writer. Good for <50 tool calls/second, which covers every local agent use case.

**Zero required dependencies.** The core engine uses only Python stdlib. MCP proxy is an optional extra (`pip install sansin-core[mcp]`).

## Requirements

- Python 3.10+
- No required dependencies (stdlib only)
- Optional: `mcp>=1.2.0` for MCP proxy

## License

MIT. See [LICENSE](LICENSE).

## Links

- [Documentation](https://docs.sansin.ai)
- [GitHub](https://github.com/choongylol/sansin-core)
- [PyPI](https://pypi.org/project/sansin-core/)
- [Sansin Cloud](https://sansin.ai)
