> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.mima.works/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.mima.works/_mcp/server.

# Python SDK Reference

**Package**: `mima-governance` · **Version**: 0.3.x · **Python**: 3.9+

```bash
pip install mima-governance          # core
pip install "mima-governance[otel]"  # + OpenTelemetry guard
pip install "mima-governance[all]"   # + otel + langchain + llamaindex + autogen
```

---

## Client setup

```python
from mima_governance import MimaGovernance, AuthorisedBy

mima = MimaGovernance(
    api_key="mima_ext_...",          # required
    system_name="my-ai-pipeline",    # required — matches mima.attest() system_name
    workspace_id=None,               # resolved automatically from API key
    base_url="https://api.mima.works",  # override for self-hosted deployments
    agent_name=None,                 # defaults to system_name
    signing_key=None,                # 32-byte hex key for HMAC-SHA256 GRC signing
    authorised_by=AuthorisedBy(      # default principal for all attestations
        identity="alice@example.com",
        role="ml-engineer",
    ),
    on_error="warn",                 # "warn" | "raise" | "silent"
    batch_flush_interval=30.0,       # seconds
    batch_max_size=100,
)
```

### Environment variables

| Variable            | Purpose                                |
| ------------------- | -------------------------------------- |
| `MIMA_API_KEY`      | Alternative to passing `api_key=`      |
| `MIMA_WORKSPACE_ID` | Alternative to passing `workspace_id=` |
| `MIMA_BASE_URL`     | Alternative to passing `base_url=`     |

---

## Attestation

### `@mima.attest()` — decorator

Wraps any function. Records input hash, output hash, and execution time.

```python
@mima.attest(tool_name="classify_document", model_id="claude-opus-4-6")
def classify(document: str) -> str:
    return llm.complete(document)
```

**Parameters**

| Parameter        | Type           | Default        | Description                                    |
| ---------------- | -------------- | -------------- | ---------------------------------------------- |
| `tool_name`      | `str`          | required       | Name of the tool / action                      |
| `mode`           | `str`          | `"sync"`       | `"sync"` pushes immediately; `"batch"` buffers |
| `model_id`       | `str`          | `None`         | LLM model identifier                           |
| `authorised_by`  | `AuthorisedBy` | client default | Override for this call                         |
| `approval_token` | token          | `None`         | Token from `require_approval()`                |

Works with async functions:

```python
@mima.attest(tool_name="generate_summary")
async def summarise(text: str) -> str:
    return await async_llm.complete(text)
```

---

### `mima.push()` — explicit attestation

Use when you already have input/output hashes (e.g. from a pipeline step).

```python
import hashlib

input_hash  = hashlib.sha256(json.dumps(payload).encode()).hexdigest()
output_hash = hashlib.sha256(json.dumps(result).encode()).hexdigest()

result = mima.push(
    "generate_report",
    input_hash,
    output_hash,
    model_id="claude-opus-4-6",
)
```

---

### `mima.trace()` — context manager

Capture hashes manually within the `with` block.

```python
with mima.trace("run_pipeline") as t:
    t.set_input(input_docs)
    output = pipeline.run(input_docs)
    t.set_output(output)
    t.set_model_id("gpt-4o")
```

---

### `mima.batch()` — buffered bulk push

```python
with mima.batch() as b:
    for item in work_queue:
        result = process(item)
        b.add("process_item", input=item, output=result)
# Flushes on exit — single batch API call.
```

---

## Pre-approval gates

Block an AI action until a GRC manager approves it in the dashboard.

```python
from mima_governance.approvals import ApprovalDenied, ApprovalTimeout

try:
    token = mima.require_approval(
        action_type="high_risk_decision",
        context={"affected_users": 1200, "policy": "credit_v3"},
        timeout_seconds=300,
        approver="bob@example.com",
        on_timeout="raise",
    )
except ApprovalDenied:
    return {"status": "rejected"}

# Use the token to link attestation to the approval record.
@mima.attest(tool_name="credit_decision", approval_token=token)
def make_credit_decision(application):
    return model.predict(application)
```

When `token` is an `ApprovalToken`, the decorator also pushes a `human_oversight` GRC record with `oversight_status='approved'`, earning `EUAIA_ART14`.

Do not call `require_approval()` inside a web request handler — it holds the connection for up to `timeout_seconds`. Gate job submission instead.

---

## GRC evidence methods

All 11 methods push to `POST /api/workspaces/{ws}/governance/grc/evidence` and return a `GrcResult`:

```python
@dataclass
class GrcResult:
    record_id: str          # "" on failure
    record_type: str
    mapped_controls: list   # e.g. ['EUAIA_ART9', 'ISO42001_6_1']
    detail: str             # "ok" on success, error message on failure
```

---

### `mima.works_risk_assessment()`

Records an AI system risk classification under EU AI Act Art. 9.

```python
mima.works_risk_assessment(
    system_name="loan-scorer",
    risk_tier="high",
    use_case="Automated credit scoring for consumer loans",
    intended_purpose="Score loan applications and surface them for human review",
    impact_domains=["credit", "consumer_finance"],
    art5_self_assessment=True,   # certifies no Art.5 prohibited practices
    assessor="alice@example.com",
    annex_iii_category="essential_services",  # required for high-risk
    environment="production",
    system_version="v2.1.0",
    technical_doc_url="https://docs.internal/loan-scorer/annex-iv",
)
```

`art5_self_assessment=True` certifies the system does not engage in any Art. 5 prohibited practices. Never set without explicit human confirmation.

`annex_iii_category` must be one of:
`biometric_identification`, `critical_infrastructure`, `education_vocational`,
`employment_management`, `essential_services`, `law_enforcement`,
`migration_border`, `justice_democratic`, `not_annex_iii`

---

### `mima.model_evaluation()`

```python
mima.model_evaluation(
    model_id="loan-scorer-v2.1",
    dataset="holdout-q2-2026",
    accuracy=0.94,
    evaluated_by="alice@example.com",
    evaluation_type="quarterly",   # "initial" | "quarterly" | "triggered"
    bias_metrics={"demographic_parity": 0.02, "equal_opportunity": 0.01},
    robustness_score=0.91,
    passed_threshold=True,
)
```

---

### `mima.human_oversight()`

```python
mima.human_oversight(
    decision_id="loan-app-48291",
    ai_recommendation="approve",
    human_decision="reject",
    reviewer="bob@example.com",
    rationale="Application flagged for fraud indicators not in model training data",
    model_id="loan-scorer-v2.1",
)
# override=True is inferred automatically (ai_recommendation != human_decision)
```

---

### `mima.training_data_governance()`

```python
mima.training_data_governance(
    model_id="loan-scorer-v2.1",
    dataset_id="training-set-2026-q1",
    record_count=2_400_000,
    bias_checks_performed=True,
    approved_by="data-governance@example.com",
    data_sources=["internal_originations", "credit_bureau_x"],
    data_categories=["financial_history", "demographics"],
    known_limitations="Underrepresented: self-employed applicants pre-2020",
)
```

---

### `mima.incident_report()`

```python
mima.incident_report(
    title="Model output hallucination on edge case inputs",
    severity="high",   # "critical" | "high" | "medium" | "low"
    description="Model returned invalid IBAN numbers for 3 users on 2026-06-01",
    affected_systems=["payment-processor-ai"],
    detected_at="2026-06-01T14:22:00Z",
    authority_notified_at="2026-06-01T18:00:00Z",
)
```

---

### `mima.access_review()`

```python
mima.access_review(
    user="contractor@external.com",
    resource="model-training-pipeline",
    granted=False,
    reviewed_by="alice@example.com",
    review_type="periodic",
    reason="Contractor engagement ended 2026-05-31",
)
```

---

### `mima.change_event()`

```python
mima.change_event(
    type="prompt_update",
    by="alice@example.com",
    description="Updated system prompt to add output format constraints",
    environment="production",
    system="customer-support-ai",
    change_id="CHG-4821",
)
```

---

### `mima.vendor_risk()`

```python
mima.vendor_risk(
    vendor="OpenAI",
    tier="high",   # "critical" | "high" | "medium" | "low"
    last_reviewed="2026-06-01",
    findings=2,
    contacts=["security@openai.com"],
)
```

---

### `mima.policy_acknowledged()`

```python
mima.policy_acknowledged(
    policy="AI Use Policy",
    user="alice@example.com",
    version="v3.1.0",
    acknowledgment_type="initial",  # "initial" | "renewal" | "update"
    policy_url="https://internal.example.com/policies/ai-use/v3.1.0",
    channel="in-app",
)
```

---

### `mima.model_drift_event()`

```python
mima.model_drift_event(
    model_id="loan-scorer-v2.1",
    metric="f1_score",
    baseline=0.94,
    current=0.88,
    threshold=0.90,
    drift_type="performance",   # "performance" | "data" | "concept"
    detected_by="monitoring-agent",
    action_taken="Model retraining scheduled for 2026-07-01",
)
```

---

### `mima.governance_review()`

```python
mima.governance_review(
    reviewed_by="ciso@example.com",
    report_type="quarterly",
    frameworks_reviewed=["EU_AI_ACT", "ISO_42001", "SOC2"],
    overall_readiness=78,
    action_items=4,
    notes="Three open gaps in Art.9 documentation. Drift monitoring not yet wired.",
)
```

---

## Async client

```python
from mima_governance.async_client import AsyncMimaGovernance

mima = AsyncMimaGovernance(
    api_key="mima_ext_...",
    system_name="async-pipeline",
)

@mima.attest(tool_name="generate")
async def generate(prompt: str) -> str:
    return await llm.complete(prompt)

# GRC methods are also async:
await mima.works_risk_assessment(
    system_name="async-pipeline",
    risk_tier="limited",
    ...
)
```

---

## Signing GRC records

Pass a 32-byte hex key to enable HMAC-SHA256 record signing:

```python
import secrets
# Generate once and store in secrets manager:
signing_key = secrets.token_hex(32)

mima = MimaGovernance(
    api_key="mima_ext_...",
    system_name="my-system",
    signing_key=signing_key,
)
```

Every GRC push includes `client_sig` and `client_sig_algo: "hmac-sha256"`. Auditors can verify signatures using the canonical message format (see [api-reference.md](./api-reference.md#signing)).

---

## Framework integrations

### LangChain

```python
from mima_governance.integrations.langchain_callback import MimaLangChainCallback

handler = MimaLangChainCallback(mima)
chain.invoke({"question": "..."}, config={"callbacks": [handler]})
```

### LlamaIndex

```python
from mima_governance.integrations.llamaindex_handler import MimaLlamaIndexHandler

Settings.callback_manager.add_handler(MimaLlamaIndexHandler(mima))
```

### AutoGen

```python
from mima_governance.integrations.autogen_middleware import MimaAutoGenMiddleware

middleware = MimaAutoGenMiddleware(mima, agent_name="assistant")
# Pass as a message hook when building your ConversableAgent reply chain.

```

---

## Error handling

### Error modes

| `on_error`         | Behaviour                                                                  |
| ------------------ | -------------------------------------------------------------------------- |
| `"warn"` (default) | Logs to stderr with `warnings.warn`, returns empty result (`record_id=""`) |
| `"raise"`          | Raises `MimaAttestationError`                                              |
| `"silent"`         | Swallows errors entirely, returns empty result                             |

### Catching errors

```python
from mima_governance import MimaGovernance, MimaAttestationError

mima = MimaGovernance(api_key="...", system_name="...", on_error="raise")

try:
    mima.push("tool", input_hash, output_hash)
except MimaAttestationError as e:
    logger.error("Governance record failed: %s", e)
```

### Detecting failure without raising

```python
result = mima.push("tool", input_hash, output_hash)
if not result.record_id:
    logger.warning("Push failed: %s", result.detail)
```

The pattern is the same for all GRC methods — `result.record_id == ""` means failure.

### Exception hierarchy

```
MimaGovernanceError              # base class for all SDK exceptions
├── MimaAttestationError         # attestation/GRC push failures (network, auth, validation)
├── ApprovalDenied               # human rejected the approval request
├── ApprovalTimeout              # no decision within timeout_seconds
└── ApprovalCancelled            # requester cancelled the approval
```

### Approval exceptions

```python
from mima_governance.approvals import ApprovalDenied, ApprovalTimeout, ApprovalCancelled

try:
    token = mima.require_approval(
        action_type="deploy_model",
        description="Deploy fraud-detection v2.1 to production",
        timeout_seconds=300,
        on_timeout="raise",
    )
except ApprovalDenied as e:
    # e.approval_id, e.rejected_by, e.reason
    logger.error("Blocked by %s: %s", e.rejected_by, e.reason)
except ApprovalTimeout as e:
    # e.approval_id, e.timeout_seconds
    logger.warning("No response in %ds — retry later", e.timeout_seconds)
except ApprovalCancelled as e:
    # e.approval_id
    logger.info("Approval %s was cancelled", e.approval_id)
```

### ApprovalToken vs TimeoutToken

| Token type      | Returned when                           | Earns human-oversight controls                       | Single-use                       |
| --------------- | --------------------------------------- | ---------------------------------------------------- | -------------------------------- |
| `ApprovalToken` | Human clicks Approve                    | Yes (`EUAIA_ART14`, `ISO42001_9_1`)                  | Yes — `ValueError` on second use |
| `TimeoutToken`  | `on_timeout="warn"` and timeout elapses | No — produces `oversight_status='timeout_unblocked'` | No                               |

```python
token = mima.require_approval(..., on_timeout="warn")

if isinstance(token, ApprovalToken):
    # Human approved — safe to proceed, earns compliance controls
    mima.attest(approval_token=token)
elif isinstance(token, TimeoutToken):
    # Timed out — proceeding without human sign-off
    # Record is created but with degraded compliance status
    mima.attest(approval_token=token)
```

### Network and rate limits

| Scenario              | Behaviour                                                 |
| --------------------- | --------------------------------------------------------- |
| Network timeout       | 15 s hardcoded; triggers error mode                       |
| HTTP 429 (rate limit) | Retries after `Retry-After` header; limit is 2000 req/min |
| HTTP 401/403          | Invalid or expired API key; triggers error mode           |
| HTTP 5xx              | Server error; triggers error mode (no automatic retry)    |

### Workspace ID resolution

If you omit `workspace_id`, the SDK resolves it from your API key on first call. If resolution fails:

```
MimaAttestationError: workspace_id not set and could not be resolved automatically
```

Fix: pass `workspace_id` explicitly or set `MIMA_WORKSPACE_ID`.

### Signing key dependency

GRC record signing requires `pynacl`:

```bash
pip install mima-governance[otel]  # includes pynacl
# or directly:
pip install pynacl
```

If `signing_key` is set but `pynacl` is not installed, the first push raises `ImportError`.

### Batch flush on shutdown

Call `mima.shutdown()` (or `await mima.shutdown()` for the async client) before process exit. Unflushed batch records are lost if the process terminates without shutdown.

---

## Type reference

### Result types

```python
@dataclass
class AttestationResult:
    attestation_id: str    # unique ID, empty string on failure
    external_verified: bool
    trust_tier: str        # 'verified' | 'attested' | 'observed'
    detail: str

@dataclass
class GrcResult:
    record_id: str         # unique ID, empty string on failure
    record_type: str       # one of the 11 RecordType values
    mapped_controls: list[str]  # e.g. ['EUAIA_ART9', 'ISO42001_6_1']
    detail: str
```

### Configuration

```python
mima = MimaGovernance(
    api_key="mima_ext_...",       # required
    system_name="my-pipeline",     # required
    workspace_id=None,             # auto-resolved from API key
    base_url="https://api.mima.works",
    agent_name=None,               # defaults to system_name
    signing_key=None,              # 32-byte hex HMAC-SHA256 key
    authorised_by=AuthorisedBy(identity="alice@co.com", role="ml-eng"),
    on_error="warn",               # 'warn' | 'raise' | 'silent'
    batch_flush_interval=30,       # seconds
    batch_max_size=100,
)
```

### Record types

```python
RecordType = Literal[
    'ai_risk_assessment', 'governance_review', 'model_evaluation',
    'training_data_governance', 'human_oversight', 'incident_report',
    'change_event', 'access_review', 'vendor_risk', 'model_drift_event',
    'policy_acknowledged',
]
```