FORMA — AI agent compliance SDK. Zero-config tracking, EU AI Act/DPDP/RBI compliance, cryptographic audit trails.
Project description
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.
Install
pip install forma-sdk
Import name is trustlayer:
import trustlayer as tl
Quickstart — runtime enforcement (zero-touch)
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:
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).
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)
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)
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 |
Manual step control (advanced)
Optional. Auto-capture already records every LLM call. Use this only to add non-LLM steps (DB queries, HTTP calls, custom logic) to the timeline — the one case where
with tl.using()+tracker.tool_call()is the right tool.
import openai
import trustlayer as tl
tracker = tl.init(api_key="tl_live_YOUR_KEY", auto_capture=False)
def my_agent(task: str) -> str:
with tl.using("my-agent"):
with tracker.llm_call(label="generate", model="gpt-4o", prompt=task) as step:
resp = openai.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": task}],
)
step.prompt_tokens = resp.usage.prompt_tokens
step.completion_tokens = resp.usage.completion_tokens
return resp.choices[0].message.content
# For tools: with tracker.tool_call("send_email", {"to": addr}): ...
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
# 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; False = manual llm_call/tool_call. |
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) / agents={...}
Bind an existing function to an agent with no code edits. register re-points the function's name in its defining module to a governed wrapper (and also works as a decorator). agents={...} on tl.init() does the same for a whole map. Both accept the same per-agent keyword arguments as tl.using() below (unknown keyword → TypeError) and support sync + async def.
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): ...
tl.using() parameters (all 15) — advanced manual scoping
tl.using()is the per-agent scoping primitive thatregister/agents=are built on. Use it directly only to scope an arbitrary block or to instrument manualtracker.tool_call()/tracker.llm_call()steps. It works as a context manager, a sync decorator, or an async decorator.
| Parameter | Description |
|---|---|
name |
Agent name (positional). |
enforce |
Policy packs (per-scope override). |
compliance |
Frameworks for scoring/reports (per-scope override). |
risk_level |
Risk level (per-scope override). |
human_sponsor |
Accountable human (per-scope override). |
purpose |
Plain-English purpose (per-scope override). |
authorized_actions |
Allow-list of tool names (per-scope override). |
max_cost_usd |
Per-run cost cap (per-scope override). |
require_approval_when |
Approval predicate (per-scope override). |
approval_message / approval_title |
Approvals inbox text (per-scope override). |
approval_timeout |
Seconds to wait (per-scope override). |
approval_poll_interval |
Poll interval seconds (per-scope override). |
approval_via |
Approval channel (per-scope override). |
version |
Agent version label. |
kill_switch,gate,auto_capture,ambient_runs,drift_threshold, andagent_namearetl.init()only — passing them totl.using()raisesTypeError.
Node.js / TypeScript
npm install forma-sdk
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 provideinit+gatefirst; enforcement parity is on the roadmap.
Dashboard & docs
- Dashboard: forma.2bd.net
- Developer guide: forma.2bd.net/developer-guide
- Generate a tailored snippet: forma.2bd.net/snippet-generator
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file forma_sdk-2.1.0.tar.gz.
File metadata
- Download URL: forma_sdk-2.1.0.tar.gz
- Upload date:
- Size: 58.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfbb7fbe438dbfcab5686f3340f8c1709e2528eba710dde984294a6927d538cc
|
|
| MD5 |
99fb44e4f80885b10fda2071b22e6553
|
|
| BLAKE2b-256 |
9caa8a6d6bdc307c454d2e3a4b50ac092467f23bfc91f18858910b1d9eb20212
|
File details
Details for the file forma_sdk-2.1.0-py3-none-any.whl.
File metadata
- Download URL: forma_sdk-2.1.0-py3-none-any.whl
- Upload date:
- Size: 58.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6ed54924e736229e27b7f03fa3dd43e0eaa00c81eb29679100391034a18a491
|
|
| MD5 |
e4e78fe6319668c2423fbe3eab529c7c
|
|
| BLAKE2b-256 |
88cc6ce1d08c75e753be7fe6685d89714f97ef16efb9a3f4a37b4919d80b53f8
|