Tork Governance Python SDK
On-device AI governance with PII detection, redaction, and local audit receipts.
Installation
pip install tork-governance
With optional framework support:
pip install tork-governance[langchain]
pip install tork-governance[fastapi]
pip install tork-governance[all]
Quick Start
from tork_governance import Tork
tork = Tork()
# Detect and redact PII
result = tork.govern("My SSN is 123-45-6789 and email is john@example.com")
print(result.output) # "My SSN is [SSN_REDACTED] and email is [EMAIL_REDACTED]"
print(result.pii.types) # ['ssn', 'email']
print(result.receipt.receipt_id) # Locally-generated receipt ID
Regional PII Detection (v1.1)
Activate country-specific and industry-specific PII patterns with the optional region and industry parameters:
from tork_governance import Tork
tork = Tork()
# UAE regional detection — Emirates ID, +971 phone, PO Box
result = tork.govern(
"Emirates ID: 784-1234-1234567-1",
region=["ae"]
)
# Multi-region + industry
result = tork.govern(
"Aadhaar: 1234 5678 9012, ICD-10: J45.20",
region=["in"],
industry="healthcare"
)
# Available regions: AU, US, GB, EU, AE, SA, NG, IN, JP, CN, KR, BR
# Available industries: healthcare, finance, legal
Supported AI Frameworks (67 Adapters)
LLM Provider SDKs
- OpenAI SDK - Direct OpenAI API governance with streaming
- Anthropic SDK - Claude API governance
- Google Gemini - Gemini API with multi-modal support
- AWS Bedrock - Bedrock with Claude, Titan, Llama support
- Azure OpenAI - Azure OpenAI Service governance
- Cohere SDK - Chat, embed, rerank, classify governance
- Mistral SDK - Mistral AI chat and embeddings governance
- Groq SDK - Groq LPU chat and audio transcription governance
- Together AI SDK - Together AI chat, completions, and embeddings governance
- Replicate SDK - Replicate model run and predictions governance
- LocalAI - Local OpenAI-compatible LLM governance
- LM Studio - LM Studio local inference governance
- GPT4All - GPT4All local LLM governance
- PrivateGPT - PrivateGPT private document AI governance
LLM Orchestration
- LangChain - Chain and agent governance
- LlamaIndex - Query engine and retriever governance
- Semantic Kernel - Microsoft SK filters and plugins
- Haystack - Pipeline and document processor governance
- LiteLLM - Unified interface for 100+ LLMs
- vLLM - High-throughput LLM serving
- Ollama - Local LLM governance
Agent Frameworks
- CrewAI - Multi-agent crew governance
- AutoGen - Microsoft AutoGen agent governance
- OpenAI Agents SDK - Function calling governance
- SuperAGI - Autonomous agent governance
- MetaGPT - Multi-agent role governance
- BabyAGI - Task-driven agent governance
- AgentGPT - Goal-oriented agent governance
Structured Output & Guardrails
- Pydantic AI - Type-safe AI with governance
- Instructor - Structured outputs governance
- DSPy - Stanford DSPy module governance
- Guidance - Microsoft Guidance governance
- LMQL - Query language governance
- Outlines - Structured generation governance
- Marvin - AI function governance
- Guardrails AI - Validator integration
- NeMo Guardrails - NVIDIA Colang integration
- Rebuff - Prompt injection detection with governance
- LLM Guard - Input/output scanning with governance
AI Development Frameworks
- Mirascope - Decorator-based LLM call governance
- Magentic - @prompt decorator governance
- txtai - Embeddings and pipeline governance
- ChatDev - Multi-agent software development governance
- CAMEL - Multi-agent role-playing governance
Visual Builders & Platforms
- Flowise - Visual workflow governance
- Langflow - Visual LangChain governance
- Dify - Low-code AI platform governance
Vector Databases
- ChromaDB - AI-native vector DB governance
- Pinecone - Managed vector DB governance
- Weaviate - Vector search governance
- Qdrant - Vector similarity governance
- Milvus - Scalable vector DB governance
LLM Observability
- LangSmith - LangChain tracing governance
- Langfuse - LLM analytics governance
- Phoenix - Arize Phoenix observability
- Helicone - LLM monitoring governance
- Weights & Biases - Experiment tracking governance
- Arize - ML observability governance
- Portkey - AI gateway governance
- PromptLayer - Prompt management governance
- Humanloop - Prompt optimization governance
Protocols
- MCP - Model Context Protocol governance
Web Frameworks
- FastAPI - Middleware and dependency injection
- Django - Middleware integration
- Flask - Extension and decorator support
- Starlette - ASGI middleware
- Tornado - RequestHandler mixin and middleware
- Pyramid - Tween and middleware governance
- Sanic - Async request/response middleware
Framework Examples
LiteLLM - Unified LLM Interface
from tork_governance.adapters.litellm import TorkLiteLLMProxy, govern_completion
# Option 1: Governed proxy client
proxy = TorkLiteLLMProxy()
response = proxy.completion(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "My SSN is 123-45-6789"}]
)
# Option 2: One-off governed completion
response = govern_completion(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "My email is john@example.com"}]
)
Ollama - Local LLM Governance
from tork_governance.adapters.ollama import TorkOllamaClient, govern_generate
# Governed Ollama client
client = TorkOllamaClient()
response = client.generate(
model="llama2",
prompt="My phone number is 555-123-4567"
)
print(response["response"]) # PII redacted
# Chat with governance
response = client.chat(
model="llama2",
messages=[{"role": "user", "content": "My SSN is 123-45-6789"}]
)
ChromaDB - Vector Database Governance
from tork_governance.adapters.chromadb import TorkChromaClient
# Governed ChromaDB client
client = TorkChromaClient()
collection = client.get_or_create_collection("my_docs")
# Documents are governed before storage
collection.add(
documents=["User John has SSN 123-45-6789"],
ids=["doc1"]
)
# Query results are governed before returning
results = collection.query(query_texts=["Find user data"])
LangChain Integration
from langchain.llms import OpenAI
from tork_governance.adapters.langchain import TorkCallbackHandler
llm = OpenAI(callbacks=[TorkCallbackHandler()])
response = llm("My credit card is 4111-1111-1111-1111")
# PII automatically redacted in prompts and responses
FastAPI Middleware
from fastapi import FastAPI
from tork_governance.adapters.fastapi import TorkFastAPIMiddleware
app = FastAPI()
app.add_middleware(TorkFastAPIMiddleware)
@app.post("/chat")
async def chat(message: str):
# Request body is automatically governed
return {"response": message}
PII Detection
Tork.govern() and Tork.scan_tool_result() run one of two on-device PII detectors,
chosen by TorkConfig.detector, the Tork(detector=...) constructor kwarg, or the
TORK_PII_DETECTOR environment variable (constructor kwarg wins, then the env var,
then the default):
detector |
Default? | Type vocabulary | Checksum validation |
|---|---|---|---|
"regional" |
Yes | 44 types across US/AU/EU/UK + universal/financial/healthcare/biometric (tork_governance.detectors.pii_patterns) |
Yes, on the types that have one (SSN, credit card, IBAN, TFN, NHS, ABN, Medicare, NINO, routing number, NPI, DEA) — a lookalike that fails its checksum is not flagged |
"basic" |
No — opt in | 10 types: ssn, credit_card, email, phone, address, ip_address, date_of_birth, passport, drivers_license, bank_account |
No |
from tork_governance import Tork
# Default: the wider, checksum-validated regional detector
tork = Tork()
result = tork.govern("IBAN: DE89370400440532013000")
print(result.pii.types) # ['iban']
# Opt back into the original 10-type detector
basic_tork = Tork(detector="basic")
# ...or: TorkConfig(detector="basic"), or `TORK_PII_DETECTOR=basic` in the environment
Wider vocabulary than the JS SDK. @torknetwork/sdk (JS) implements only the 10
basic types above — it has no regional detector. A Python govern()/scan_tool_result()
result's pii.types (or tool_result_scan receipt block finding types) may therefore
contain labels with no JS-SDK equivalent, e.g. "iban", "tfn", "nhs_uk". Don't
assume the two SDKs' outputs are directly comparable without accounting for this —
use detector="basic" (or TORK_PII_DETECTOR=basic) on the Python side to match the
JS SDK's exact 10-type vocabulary and behavior byte-for-byte.
Full list of regional types by category, generated from
PIIDetector(regions=['all']).get_supported_types():
| Category | Types |
|---|---|
| US (6) | driver_license_us, ein, itin, passport_us, phone_us, ssn |
| Australia (5) | abn, acn, medicare_au, phone_au, tfn |
| EU (5) | french_ssn, german_id, iban, phone_eu, vat_eu |
| UK (4) | nhs_uk, nino_uk, postcode_uk, sort_code_uk |
| Universal (8) | credit_card, date_of_birth, email, ip_address, ipv6_address, mac_address, phone_generic, url_with_pii |
| Financial (6) | bank_account, card_expiry, crypto_address, cvv, routing_number, swift_bic |
| Healthcare (7) | cpt_code, dea_number, health_plan_id, icd_code, mrn, npi, patient_id |
| Biometric (3) | biometric_id, face_id, fingerprint_id |
Removed regional types (0.26.1). The regional PIIType enum previously declared
five types with no backing pattern anywhere in pii_patterns.py — they were listed
in get_supported_types()'s vocabulary but could never actually be matched. Each was
resolved individually:
name,address— removed. Free-text personal names and postal addresses can't be reliably matched with a regex; that needs an NER model, which this detector doesn't have. A low-precision regex here would flag ordinary prose as PII (or miss most real addresses/names), which is worse than not claiming the type at all.ssn_no_dashes— removed. The existingssnpattern's separators are already optional (\d{3}[-\s]?\d{2}[-\s]?\d{4}), so it already matches unformatted 9-digit input under thessnlabel. A second type for the identical span wouldn't add detection coverage — worse,PIIDetector.redact()'s reversed-replacement loop assumes matches don't share a span, and two same-span matches corrupt its output (confirmed today, rarely, when a bare 9-digit number happens to pass both thessnandtfnchecksums — seetest_pii_type_parity.py). Addingssn_no_dasheswould have turned that from a rare coincidence into a routine occurrence on every unformatted SSN.phone_generic,url_with_pii— added a real pattern. Both are reliable, keyword-gated heuristics in the same style already used elsewhere in this file (bank_account,mrn,patient_id):phone_genericmatches aPhone:/Tel:/Mobile:/Cell:-labeled number outside any region-specific format, validated against E.164 digit-count bounds (7–15 digits);url_with_piimatches a URL whose query string carries a PII-indicative parameter (email=,ssn=,token=,api_key=,password=, etc).
tests/test_pii_type_parity.py enforces, with no exceptions list, that every
declared PIIType in both the basic and regional enums has a backing pattern —
this class of bug (declared-but-unmatched type) can't reoccur silently for either
detector.
Compliance Support
- GDPR - EU data protection
- HIPAA - Healthcare (18 PHI identifiers)
- PCI-DSS - Payment card data
- SOC 2 - Security controls
- CCPA/CPRA - California privacy
- FERPA - Education records
- GLBA - Financial privacy
- COPPA - Children's privacy
Receipts & Attestation
Every govern() call locally mints a receipt, entirely on-device. This happens
every time, regardless of network access, and doesn't depend on whether a server was
ever reached:
result = tork.govern("Sensitive data here")
receipt = result.receipt
print(receipt.receipt_id) # Locally-generated identifier
print(receipt.timestamp) # ISO 8601 timestamp
print(receipt.input_hash) # SHA-256 of input
print(receipt.output_hash) # SHA-256 of output
print(receipt.policy_version) # Applied policy version
If you supply an api_key (via Tork(api_key=...) or TorkConfig), govern()
additionally attempts to report the decision to tork.network as a separate server
attestation. This is optional, asynchronous, and best-effort:
- It runs on a background thread —
govern()never blocks on it. - The network call (and its one retry) can fail; nothing about the local decision above is affected either way.
- Check
result.report.attemptedand.succeededbefore treating a decision as anchored on the server — don't assume success just because reporting was attempted. - On success,
result.report.receipt_idis the server's receipt ID, a different value from the localreceipt.receipt_idabove.
report = result.report
print(report.attempted) # Whether reporting was attempted (api_key configured)
print(report.succeeded) # Whether the server actually persisted the attestation
print(report.receipt_id) # Server-side receipt ID, only set once succeeded
print(report.reason) # Why it hasn't succeeded (or its current status)
# Block until the background attempt settles, if you need the confirmed
# outcome before proceeding — most callers don't need this.
report.wait(timeout=5)
Scanning tool results
A tool result returned by an MCP server — or any external system you do not control — is untrusted input that is about to be appended to a model's context. Tork.scan_tool_result() scans it first, on-device, for PII and prompt injection:
from tork_governance import Tork
tork = Tork()
outcome = tork.scan_tool_result(
"lookup_customer",
tool_result, # whatever the server returned
"mcp://crm.internal/customers",
block_on_injection=True,
)
if outcome.blocked:
print(outcome.reason) # do not append anything
else:
append_to_context(outcome.sanitized) # PII masked in place
outcome.findings
# [ToolResultFinding(kind='pii', type='email', count=1, location='$.content[0].text'),
# ToolResultFinding(kind='injection', type='heuristic:instruction_override', count=1, location='$.content[0].text')]
There is also a standalone scan_tool_result(tool_name, payload, server_uri=None, **options) function with the same signature (minus the receipt) that returns an object with sanitized, findings, blocked, and reason, and produces no receipt.
- PII uses the same on-device detector as
govern()— same patterns, same redaction labels. Matches are masked in place; the payload structure is otherwise unchanged, and a clean payload comes back untouched (same object identity, not just equal). - Injection detection is heuristic. A conservative pattern set (
tork-injection-heuristics-v1) covering instruction-override phrases, role reassignment, and exfiltration URLs. Every injection finding is typedheuristic:<name>because that is exactly what it is: a regex match over untrusted text, with false positives and false negatives, not a verified determination. Withoutblock_on_injection, matches are reported and the result is still returned; with it,sanitizedisNoneso no masked copy can be appended by accident. - Zero network calls. The scan is pure and synchronous. The payload never leaves the machine, whether or not an
api_keyis configured. - Recorded on the receipt as counts only.
receipt.tool_result_scancarriesattested_by: 'client',capture_mode: 'edge', the tool name and server URI, counts by kind and type, the blocked flag, and the SDK version. It never carries the payload, a matched value, or a location path.
This is a client-side, client-attested control. The scan runs in your process, and the receipt says so: Tork did not execute it and cannot verify it ran at all — the same honest boundary as every other edge attestation this SDK produces. Gateway-side enforcement, where a caller cannot skip the scan, is a separate and later control. Do not read a tool_result_scan block as proof that every tool result reaching a model was scanned; read it as a record of the scans a caller chose to run and report.
Configuration
from tork_governance import Tork, TorkConfig
tork = Tork(
config=TorkConfig(
policy_version="1.0.0",
default_action="redact", # or "allow", "deny"
custom_patterns={
"employee_id": r"EMP-\d{6}"
}
)
)
Documentation
License
MIT License - see LICENSE for details.
Release files for tork-governance 0.27.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tork_governance-0.27.0.tar.gz | 308.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tork_governance-0.27.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 570.0 kB
Release files / tork_governance-0.27.0.tar.gz
| Download URL | tork_governance-0.27.0.tar.gz |
|---|---|
| Size | 308.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
de92c4fdb981b65444c5ca91a73c276127f98c8abe3dc6208d96d33f6f900ffd
|
|
BLAKE2b-256 checksum How to use checksums |
a6918f72a0008f99e9435609569a204a1c78572449362dd6294fc330e3e4cc75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.1
|
Release files / tork_governance-0.27.0-py3-none-any.whl
| Download URL | tork_governance-0.27.0-py3-none-any.whl |
|---|---|
| Size | 261.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bed3e19ddf2644f996755a940b4c61a7c8b9ebb72a42de222bf588444c85ab4e
|
|
BLAKE2b-256 checksum How to use checksums |
f60c87ab1adc154587af626b666be14877850692223f7ddc873d854b1f76df5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.1
|