Metadata-Version: 2.5
Name: implicant
Version: 0.8.0
Summary: Python client SDK for the Implicant FHE inference platform
Author: Implicant
License: Apache-2.0
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: implicant-engine-client<0.2,>=0.1
Requires-Dist: numpy>=1.26
Requires-Dist: pydantic>=2.6
Requires-Dist: rich>=13.8
Requires-Dist: seal-engine<1,>=0.6
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: build>=1; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Provides-Extra: ui
Requires-Dist: fastapi>=0.115; extra == 'ui'
Requires-Dist: uvicorn>=0.30; extra == 'ui'
Provides-Extra: vision
Requires-Dist: opencv-python-headless>=4.8; extra == 'vision'
Requires-Dist: pillow>=10; extra == 'vision'
Requires-Dist: torch>=2.2; extra == 'vision'
Requires-Dist: torchvision>=0.17; extra == 'vision'
Description-Content-Type: text/markdown

# Implicant SDK

Python **client** for the Implicant FHE inference platform. Encrypts one row
locally under BGV, sends the ciphertext to the platform for homomorphic
evaluation, and decrypts the result. The server never sees plaintext; the
secret key never leaves the process.

The classifier head runs **server-side under FHE**: the platform returns the
encrypted class scores, which the client decrypts before threshold/argmax.
`W` / `b` never reach the client.

Pure Python. The platform serves a deployment profile per model that names
the engine; the SDK loads it through the `implicant-engine` contract. The
served models use `bgv-seal`, from the `seal-engine` package.
Architecture: [`docs/architecture.md`](docs/architecture.md).

## Install

```bash
pip install implicant
```

Pulls `seal-engine` (the `bgv-seal` crypto wheel) from PyPI automatically.
Needs **CPython 3.11–3.14**; `seal-engine` wheels ship for **macOS-arm64** and
**manylinux x86_64** only. On any other target — Windows, Linux aarch64, macOS x86_64 —
there is no wheel and no source fallback, so pip stops with `No matching
distribution found for seal-engine`.

### Upgrading from 0.7.x

- The console command `implicant` is now `implicant-fhe-sdk`.
- `implicant.manifest`, `implicant.keys`, `implicant.envelope` and `implicant.CircuitMeta` are
  removed.
- A key cache written by 0.7.x is deleted with a warning: the key is generated and registered again.
- The platform must serve each model's deployment profile (`/profile`).
- Requests carry `User-Agent: implicant/0.8.0`.

Details in [`CHANGELOG.md`](CHANGELOG.md).

### Development

```bash
python3.12 -m venv .venv
.venv/bin/pip install -r requirements-dev.txt   # private test packages (read access to their repo)
# then the engine's private build, as the comments of requirements-dev.txt say
.venv/bin/pip install -e ".[dev,ui]"
.venv/bin/pytest tests -q -m "not seal"
```

The private test packages are listed in `requirements-dev.txt`, not in a `dev`
extra: extras are written into the published package metadata, which is public.
These tests run on the contract's `plain` engine. The `seal` tests (real BGV, GBs of RAM)
need the engine's private build and one process each: see [`tests/README.md`](tests/README.md).
CI runs them weekly, or on a pull request labelled `seal`.

## Running an inference (CLI)

The `implicant-fhe-sdk` CLI is the client-side workflow: configure once, then run
encrypted predictions. Everything stays local except the ciphertext and the
public keys — the secret key never leaves your machine.

> **This release is demo-only:** every `predict` needs `--demo` (or
> `IMPLICANT_DEMO=1` in the environment) and runs on the bundled demo
> keypair — see [Demo mode](#demo-mode) for what that implies. Without the
> flag, `predict` exits with an explanatory message.

### 1. Point the client at the platform

```bash
implicant-fhe-sdk init --api-url https://api.implicant.example
```

Writes `~/.implicant/config.toml`. Run `implicant-fhe-sdk status` to confirm the
configured `api_url` and list any locally cached keys.

### 2. Set your API token

The bearer token is read from the environment, never stored on disk:

```bash
export IMPLICANT_API_KEY="imp_..."
```

### 3. Discover the available models

The SDK ships a hand-maintained registry of the models servable on the
platform (there is no server-side listing endpoint yet):

```bash
$ implicant-fhe-sdk models list
credit        Predicts whether a borrower will default on a loan
bankfraud     Predicts whether a bank account application is fraudulent
heartdisease  Predicts whether a patient has heart disease
cancer        Predicts whether a breast tumor is malignant or benign
diabetes      Predicts whether a patient will be readmitted within 30 days

Try one:           implicant-fhe-sdk predict --model <slug> --sample --demo
Editable example:  implicant-fhe-sdk models sample <slug> --out row.json
Then:              implicant-fhe-sdk predict --model <slug> --input row.json --demo
```

The listed slug is the `--model` value for `predict`.

### 4. Prepare the input row

One row of raw feature values, as a JSON object (or a single-row CSV). The keys
are the feature names the model expects. Each known model ships a bundled
example row — the fastest way to get a correct starting point:

```bash
implicant-fhe-sdk models sample diabetes --out row.json   # or no --out to print to stdout
```

```json
{
  "age": "[80-90)",
  "medical_specialty": "InternalMedicine",
  "discharge_disposition_id": 6.0,
  "time_in_hospital": 4.0,
  "num_lab_procedures": 56.0,
  "num_medications": 16.0,
  "number_emergency": 1.0986122886681098,
  "number_inpatient": 2.3978952727983707,
  "number_diagnoses": 9.0
}
```

Edit the values by hand to test different inputs, then feed the file to
`predict`. Keep categorical values as quoted strings (`"[80-90)"` above) —
they are matched textually against the model's fitted levels. (`--out`
refuses to overwrite an existing file unless you pass `--force`.)

### 5. Predict

```bash
implicant-fhe-sdk predict --model diabetes --input row.json --demo
```

Or skip the file entirely and run on the bundled example row:

```bash
implicant-fhe-sdk predict --model diabetes --sample --demo
```

This fetches the model's deployment profile, generates (or reloads) your keys, uploads the
**public** keys on first use, encrypts the row locally, sends the ciphertext,
then decrypts and decodes the returned class scores. Progress streams to
stderr (with a spinner on the current step in a terminal) and the decision
lands on stdout:

```
✓ Fetching model configuration
✓ Preparing encryption keys
✓ Encrypting input data
✓ Evaluating on the server (encrypted)
✓ Decrypting server response
Final decision: readmitted
```

Add `--json` for the machine-readable result instead of the decision line
(progress stays on stderr, so piping stdout to `jq` is safe):

```json
{
  "key_id": "bgv-n16384-L8-5f657f27f77c",
  "label_index": 1,
  "label": "readmitted",
  "scores": [2028]
}
```

Add `--no-persist-key` to use an ephemeral secret key for a single run (nothing
written to disk; keys are regenerated and re-uploaded next time).

### Managing keys

Outside demo mode (today the Python API; `predict` requires `--demo` in this release), the
first prediction for a `key_id` persists your secret key to
`~/.implicant/keys/<key_id>/secret_key.bin` (mode `0600`) so later runs skip
keygen. Inspect and clean up the local store:

```bash
implicant-fhe-sdk keys list            # list the stored key_ids
implicant-fhe-sdk keys rm <key_id>     # delete one stored key (secret + public)
implicant-fhe-sdk keys purge --yes     # delete all stored keys
```

If the platform answers with scores your key cannot have produced (most likely the key was
replaced there), `predict` refuses the answer (`OutputRejectedError` in Python); the message
says what to do before retrying (remove the key, or update the package for the demo key).

## Use (Python)

The same flow is available programmatically:

```python
from implicant import ImplicantClient, list_known_models, sample_row
from implicant.transport import HttpxTransport

for m in list_known_models():          # discover: (slug, description) pairs
    print(m.slug, "—", m.name)

row = sample_row("diabetes")           # fresh mutable copy of the bundled example
row["time_in_hospital"] = 2.0          # hand-tweak values for testing

model = next(m for m in list_known_models() if m.slug == "diabetes")
client = ImplicantClient(
    HttpxTransport(base_url="https://api.implicant.example", api_key="imp_..."),
    key_cache_dir="~/.implicant/keys",
)
result = client.predict("diabetes", row, class_names=model.class_names)
print(result.prediction.label)
```

## Demo mode

Demo mode skips FHE key generation by loading a pre-generated keypair shipped
inside the package, and skips the public-key upload (the platform already holds
the matching public bundle). It exists so a prediction can run immediately in a
live demo, without first-use key generation and without uploading the
multi-hundred-megabyte public key set. **In this release it is required:**
`predict` refuses to run outside demo mode.

Activate with an environment variable (applies to the whole session):

```bash
export IMPLICANT_DEMO=1
implicant-fhe-sdk predict -m cancer -i row.json
```

Or per-command — combined with `--sample`, this is a zero-setup end-to-end run:

```bash
implicant-fhe-sdk predict -m cancer --sample --demo
```

All five registry models (`credit`, `bankfraud`, `heartdisease`, `cancer`,
`diabetes`) are provisioned with bundled demo keys; a model ID without a
bundled key raises an error in demo mode.

> **Security warning — demo mode is not private.** The demo secret key is
> shipped inside the package and is therefore public: anyone with the wheel can
> decrypt anything encrypted in demo mode. **Never use demo mode for real or
> sensitive data.** Normal mode (the default) generates a secret key that never
> leaves your machine.

## Web UI (demo)

A local web interface for live demonstrations — the four-step flow (choose a
model, generate keys on your machine, watch your row get encrypted, see the
blind result) with every visual driven by the real pipeline: a live
execution-path strip, genuine per-phase timings, the exact encrypted payload
and response sizes, a plain-words inventory of what the request contains, and
an in-memory history of the session's inferences:

```bash
pip install "implicant[ui]"
export IMPLICANT_API_KEY="imp_..."
implicant-fhe-sdk ui        # starts a localhost server and opens your browser
```

All cryptography runs in the local Python process; the browser only renders.
The UI is demo-only in this release and runs on the shared demonstration key
(see the demo-mode warning above). On Windows, run the same UI via Docker:

```bash
docker build -t implicant-ui -f docker/Dockerfile .   # from the repo root
docker run --rm -p 8642:8642 \
  -e IMPLICANT_API_KEY=imp_... \
  -e IMPLICANT_API_URL=https://your-platform \
  implicant-ui
# then open the printed URL — including the #token — in your browser
```

**Interpretable AI** — a third tab where models read as short if-then
rulebooks, run in the clear on the serving machine, every fired rule spelled
out in words. *Vision*: face models over visual concepts (Female/Male from 39
CelebA attributes; under 20 / 20+ from FairFace age cues, in two cue sets) on
gallery faces, an uploaded photo, a webcam snapshot or the live view. *Tabular*:
the rulebooks of the FHE demo models on a row you type; its rule bundles are not
in the wheel (point `IMPLICANT_XAI_TABULAR_ASSETS` at them; the Docker image
does). Vision needs the optional `[vision]` extra (torch, torchvision, OpenCV,
Pillow); without it that space shows an install hint and nothing else changes:

```bash
pip install "implicant[ui,vision]"                     # macOS / Windows
# Linux: point pip at PyTorch's CPU index too (the CPU torch wheel is
# ~200 MB; PyPI's default Linux build pulls ~2 GB of CUDA):
PIP_EXTRA_INDEX_URL=https://download.pytorch.org/whl/cpu pip install "implicant[ui,vision]"
```

The Docker image installs the extra already. The camera needs a secure
page (`localhost` or HTTPS).

By default the page is protected by a per-launch URL token — the printed
`#fragment` is the credential, so keep the full URL. For a deliberately
public deployment behind a reverse proxy (e.g. a demo site), start with
`implicant-fhe-sdk ui --public-demo` (or `-e IMPLICANT_UI_PUBLIC_DEMO=1` in Docker):
no token is required, visitors cannot change the server-side API key, and
`IMPLICANT_API_KEY` must be set in the environment. Anyone who can reach
the address can then drive the demo — never expose a private instance
this way.

## License

Apache-2.0.
