Metadata-Version: 2.4
Name: forma-sdk
Version: 2.2.0
Summary: FORMA — AI agent compliance SDK. Zero-config tracking, EU AI Act/DPDP/RBI compliance, cryptographic audit trails.
Home-page: https://github.com/amit5115/forma-sdk
Author: FORMA
Author-email: sdk@forma.ai
Keywords: ai agents compliance eu-ai-act rbi dpdp audit cryptography llm openai
Classifier: Development Status :: 4 - Beta
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 :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24.0
Requires-Dist: pydantic>=2.0
Provides-Extra: cli
Requires-Dist: click>=8.0; extra == "cli"
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == "openai"
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.20; extra == "anthropic"
Provides-Extra: litellm
Requires-Dist: litellm>=1.0; extra == "litellm"
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.1; extra == "langchain"
Provides-Extra: all
Requires-Dist: click>=8.0; extra == "all"
Requires-Dist: openai>=1.0; extra == "all"
Requires-Dist: anthropic>=0.20; extra == "all"
Requires-Dist: litellm>=1.0; extra == "all"
Requires-Dist: langchain-core>=0.1; extra == "all"
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license-file
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# FORMA SDK

**Make non-compliance impossible — in one line of code.**

FORMA doesn't just record what your AI did; it **blocks non-compliant decisions before they execute**. Add `enforce=[...]` and PII leaks, prompt injections, and policy violations are stopped at runtime — then every decision is cryptographically signed and mapped to RBI, DPDP, EU AI Act, and ISO 42001.

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)

---

## Install

```bash
pip install forma-sdk
```

Import name is `trustlayer`:

```python
import trustlayer as tl
```

## Quickstart — runtime enforcement (zero-touch)

```python
import trustlayer as tl

# One call. Enforcement is process-wide on every LLM/tool call — no decorators,
# no context managers, no edits inside your business logic.
tl.init(
    api_key="tl_live_YOUR_KEY",   # from forma.2bd.net → Settings
    human_sponsor="ops@company.com",
    enforce=["rbi_ml_risk", "dpdp"],   # ← blocks violations BEFORE they run
)

# Your existing code is unchanged.
result = run_underwriting_model(application)
```

Now, before any LLM or tool call executes:

- An Aadhaar / PAN / card number in the prompt → **blocked** (DPDP)
- "Ignore previous instructions…" / jailbreaks → **blocked**
- "Auto-approve without review" → **blocked** (RBI human-review rule)

A blocked action raises `ComplianceViolation`, and every decision (allow / warn / block) lands in your signed audit trail. The gate runs **locally in ~1 ms** — no network hop in your request path.

## Multiple agents — `tl.register()` / `agents=` (still zero edits)

Map your **existing functions** to agents without touching their bodies. Each gets its own discrete signed run plus its own `enforce` / `risk_level` / `compliance` / approval config:

```python
import trustlayer as tl
from my_app import approve_loan, validate_kyc, detect_fraud

tl.init(
    api_key="tl_live_YOUR_KEY",
    agents={
        "kyc-validator":  validate_kyc,                                        # bare callable
        "loan-approval":  {"fn": approve_loan, "enforce": ["rbi_ml_risk", "dpdp"],
                           "risk_level": "HIGH"},                              # per-agent config
        "fraud-detector": {"fn": detect_fraud, "enforce": ["dpdp"]},
    },
)

# Equivalent if you register after init:
tl.register("loan-approval", approve_loan, enforce=["rbi_ml_risk"], risk_level="HIGH")

# Call them exactly as before — each is governed and attributed independently.
result = approve_loan(application)
```

`register` / `agents=` work for **sync and `async def`** functions and stay correct under 20+ concurrent agents (scope is tracked with `contextvars`, so threads and asyncio tasks never cross-attribute). They must run before the target is *called* (top of your entrypoint).

```python
from trustlayer import ComplianceViolation

try:
    result = approve_loan(application)
except ComplianceViolation as e:
    print(e.reason)   # "PII detected in LLM prompt: Aadhaar number. ..."
```

## Zero-config tracking (no enforcement)

```python
import trustlayer as tl
tl.init(api_key="tl_live_YOUR_KEY")

# Every OpenAI / Anthropic / LangChain / LiteLLM call is now auto-captured —
# tokens, cost, latency, anomaly score — with no other code changes.
```

## Human approvals (EU AI Act Article 14 / RBI human review)

```python
import trustlayer as tl

tl.init(
    api_key="tl_live_YOUR_KEY",
    require_approval_when=lambda ctx: ctx.get("amount", 0) > 1_000_000,
    approval_message="Loan above ₹10L requires human review.",
)

# Your existing function runs unchanged — high-stakes calls pause for sign-off.
result = approve_loan(application)
```

The decision pauses in your FORMA Approvals inbox until a human approves or rejects. Fail-closed: a timeout or unreachable API is treated as a denial — a skipped review never silently passes.

---

## Policy packs

| Pack | Region | Enforces |
|------|--------|----------|
| `dpdp` | India | Aadhaar / PAN / card / phone blocked from prompts & tool args; consent-bypass blocked |
| `rbi_ml_risk` | India Banking | auto-approval-without-review blocked; fund-disbursal routed to approval |
| `eu_ai_act` | EU | human-oversight bypass (Art. 14) & logging suppression (Art. 12) blocked; PII protection |
| `iso42001` | Global | concealing AI involvement blocked |

---

## Kill switch

Pass `kill_switch=True` to `tl.init()`. When you trigger a kill from the dashboard, the next LLM/tool call raises `KillSwitchTriggered` in under ~100 ms.

## CI/CD compliance gate

```bash
# Blocks the deploy (exit 1) if any agent is below the threshold
trustlayer gate --pre-deploy --fail-below 80
```

---

## `tl.init()` parameters (all 24)

| Parameter | Description |
|-----------|-------------|
| `api_key` | FORMA API key (or `FORMA_API_KEY` env). |
| `api_url` | Backend URL override. |
| `agent_name` | Display name for ambient auto-captured runs. |
| `human_sponsor` | Accountable human on the audit record. |
| `auto_capture` (`True`) | Auto-patch OpenAI/Anthropic/LiteLLM/LangChain so every LLM call is captured with no code edits. |
| `kill_switch` (`False`) | Start global process-level kill-switch watcher. |
| `gate` (`True`) | Pre-check every auto-captured call through the compliance gate. |
| `compliance` | Frameworks for scoring/reports (`EU_AI_ACT`, `DPDP`, `RBI_MRM`, `ISO_42001`). |
| `ambient_runs` (`True`) | Create real signed runs for auto-captured calls. |
| `enforce` | Policy packs blocked locally pre-execution (`dpdp`, `rbi_ml_risk`, `eu_ai_act`, `iso42001`). |
| `agents` | Zero-edit multi-agent map. `{"name": fn}` or `{"name": {"fn": fn, "enforce": [...], "risk_level": "HIGH", ...}}`. |
| `purpose` | Plain-English purpose stored in the identity passport. |
| `risk_level` (`"MEDIUM"`) | `LOW` \| `MEDIUM` \| `HIGH` \| `CRITICAL`. |
| `authorized_actions` | Allow-list of tool names; gate blocks others. |
| `drift_threshold` (`0.15`) | Behavioural drift threshold for the passport. |
| `max_cost_usd` | Hard per-run cost cap; over-budget runs marked failed. |
| `require_approval_when` | Predicate `ctx→bool`; `True` pauses for human approval. |
| `approval_message` / `approval_title` | Text in the Approvals inbox. |
| `approval_timeout` (`3600`) | Seconds to wait for a decision. |
| `approval_poll_interval` (`5`) | Seconds between inbox polls. |
| `approval_via` (`"forma"`) | Approval channel. |
| `timeout` (`10`) | HTTP timeout seconds. |
| `max_retries` (`2`) | Transport retry attempts. |
| `fail_closed` (`False`) | Block when the gate is unreachable (default fails open). |

## `tl.register(name, fn, **cfg)` — the second entry point

FORMA's public API is **exactly two calls**: `tl.init()` and `tl.register()`.

`tl.register()` binds one existing function to a governed agent with no code edits. It re-points the function's name in its defining module to a governed wrapper (and also works as a decorator `@tl.register(...)`). `agents={...}` on `tl.init()` does the same for a whole map at once. Both support sync + `async def`.

```python
tl.register("loan-approval", approve_loan, enforce=["rbi_ml_risk"], risk_level="HIGH")
# decorator form:
@tl.register("loan-approval", enforce=["rbi_ml_risk"])
def approve_loan(application): ...
```

### When to use `tl.init(agents={...})` vs `tl.register()`

| Use… | When |
|------|------|
| `tl.init(agents={...})` | All agent functions are importable at the single `init()` call (top of your entrypoint). Simplest — one block governs everything. |
| `tl.register(name, fn, ...)` | A function is imported/defined **after** `init()`, you want the decorator form `@tl.register(...)`, or you need conditional / dynamic registration. |

### `tl.register()` parameters (full parity with init's per-agent config)

`tl.register()` accepts the **same governance arguments** as a `tl.init(agents={...})` per-agent config dict (unknown keyword → `TypeError`):

| Parameter | Description |
|-----------|-------------|
| `name` | Agent name (positional). |
| `fn` | The function to govern (positional; omit for the `@tl.register(...)` decorator form). |
| `enforce` | Policy packs blocked locally pre-execution (`dpdp`, `rbi_ml_risk`, `eu_ai_act`, `iso42001`). |
| `compliance` | Frameworks for scoring/reports (`EU_AI_ACT`, `DPDP`, `RBI_MRM`, `ISO_42001`). |
| `risk_level` (`"MEDIUM"`) | `LOW` \| `MEDIUM` \| `HIGH` \| `CRITICAL`. |
| `human_sponsor` | Accountable human on the audit record. |
| `purpose` | Plain-English purpose stored in the agent's identity passport. |
| `authorized_actions` | Allow-list of tool names; the gate blocks others. |
| `max_cost_usd` | Hard per-run cost cap; over-budget runs marked failed. |
| `kill_switch` (`False`) | Start a per-agent kill-switch watcher; a kill halts this agent's next call. |
| `drift_threshold` (`0.15`) | Behavioural drift threshold stored on the agent's passport. |
| `require_approval_when` | Predicate `ctx→bool`; `True` pauses for human approval. |
| `approval_message` / `approval_title` | Text shown in the Approvals inbox. |
| `approval_timeout` (`3600`) | Seconds to wait for a decision. |
| `approval_poll_interval` (`5`) | Seconds between inbox polls. |
| `approval_via` (`"forma"`) | Approval channel. |
| `version` | Agent version label. |

---

## Node.js / TypeScript

```bash
npm install forma-sdk
```

```ts
import * as forma from "forma-sdk";

forma.init({ apiKey: "tl_live_YOUR_KEY", humanSponsor: "ops@company.com" });

const { decision } = await forma.gate("agent-id", {
    actionType: "llm_call",
    prompt: userPrompt,
});
```

> **Note:** The Node SDK is **coming soon** (not yet published to npm). Runtime enforcement (`enforce`) and approvals are **Python-only** today. The Node package will provide `init` + `gate` first; enforcement parity is on the roadmap.

---

## Dashboard & docs

- Dashboard: **[forma.2bd.net](https://forma.2bd.net)**
- Developer guide: [forma.2bd.net/developer-guide](https://forma.2bd.net/developer-guide)
- Generate a tailored snippet: [forma.2bd.net/snippet-generator](https://forma.2bd.net/snippet-generator)

## License

MIT — see [LICENSE](LICENSE).
