Metadata-Version: 2.4
Name: implicant
Version: 0.6.0
Summary: Python client SDK for the Implicant FHE inference platform
Author: Implicant
License: Apache-2.0
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: implicant-fhe<0.6,>=0.5
Requires-Dist: numpy>=1.26
Requires-Dist: pydantic>=2.6
Requires-Dist: rich>=13.8
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
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** (Variant B): the platform
returns one ciphertext with the class scores packed contiguously, which the
client decrypts (unsigned) and decodes with `decode_class_scores` before
threshold/argmax. `W` / `b` never reach the client.

Pure Python — all crypto comes from [`implicant-fhe`](https://github.com/implicant/implicant-fhe)
(`helut.client`). Contracts: [`docs/architecture.md`](docs/architecture.md)
and `platform/docs/FHE_INTERFACE_SPEC.md`.

## Install

```bash
pip install implicant
```

Pulls `implicant-fhe` (the `helut` crypto wheel) from PyPI automatically. Wheels
ship for **CPython 3.10–3.14** on **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
implicant-fhe`.

### Developers

```bash
python3.13 -m venv .venv313
.venv313/bin/pip install -e ".[dev]"
.venv313/bin/pytest tests/ -v      # 100 tests, ~3.5 min (real BGV keygen)
```

To pin an unreleased `implicant-fhe` build, stage its wheel in `./wheels/` and
add `--find-links wheels/`; the published versions resolve from PyPI without it.

## Running an inference (CLI)

The `implicant` 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.

### 1. Point the client at the platform

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

Writes `~/.implicant/config.toml`. Run `implicant 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 models list
cancer    Predicts whether a breast tumor is malignant or benign
diabetes  Predicts whether a patient has diabetes
```

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 models sample diabetes --out row.json   # or no --out to print to stdout
```

```json
{
  "preg": 6,
  "plas": 148,
  "pres": 72,
  "skin": 35,
  "insu": 0,
  "mass": 33.6,
  "pedi": 0.627,
  "age": 50
}
```

Edit the values by hand to test different inputs, then feed the file to
`predict`. (`--out` refuses to overwrite an existing file unless you pass
`--force`.)

### 5. Predict

```bash
implicant predict --model diabetes --input row.json
```

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

```bash
implicant predict --model diabetes --sample
```

This fetches the model manifest, 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: tested_negative
```

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-n32768-L4-a1b2c3d4e5f6",
  "label_index": 0,
  "label": "tested_negative",
  "scores": [-6684]
}
```

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

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 keys list            # list key_ids; flags which have a persisted SK
implicant keys rm <key_id>     # delete one stored key (secret + public)
implicant keys purge --yes     # delete all stored keys
```

## 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["age"] = 61                        # hand-tweak values for testing

client = ImplicantClient(
    HttpxTransport(base_url="https://api.implicant.example", api_key="imp_..."),
    key_cache_dir="~/.implicant/keys",
)
result = client.predict("diabetes", row, class_names=("negative", "positive"))
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 the ~250 s keygen delay.

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

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

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

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

Only the provisioned demo models (`cancer`, `diabetes`) have bundled keys; other
model IDs raise 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.

## License

Apache-2.0.
