Metadata-Version: 2.4
Name: serva
Version: 0.5.0
Summary: Official Python client for the Serva encode/decode API
Author: Servamind
License: Proprietary - All Rights Reserved
Project-URL: Homepage, https://serva.servamind.com
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Requires-Dist: numpy>=1.24
Requires-Dist: huggingface_hub>=0.20
Requires-Dist: torch>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == "dev"
Dynamic: license-file

# serva

Python client for the Serva encode/decode API. Encode files and data into the
`.serva` format, decode them back, read a file's public index without its
password, push datasets to the Hugging Face Hub, and load them into PyTorch.

## Install

```bash
pip install serva
```

Grab an API key from [serva.servamind.com](https://serva.servamind.com) and pass
it to the client or set `SERVA_API_KEY`. Encoding and decoding need one; reading
a file's public index does not.

## Encode and decode

```python
from serva import Serva

client = Serva(api_key="sk_live_...")   # or set SERVA_API_KEY

result = client.encode("photo.raw", password="my-secret")
print(result.output_path, result.savings_percent)

client.decode("photo.serva", password="my-secret", output="photo.raw")
```

You decode with the same password you encoded with.

`encode` also takes the three settings
any of them unset and the service default is used.

```python
client.encode(
    "photo.raw",
    password="my-secret",
    hv_dim=512,          
    hv_seed=7,          
)
```

## Encode data

Rows of scalars are encoded to a `.serva` like any file. The rows go in the
locked part their hypervectors become the file's public index, one per row, so
you read them back.
```python
result = client.encode_data(
    [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]],
    hv_dim=8192,
    output="rows.serva",
)
print(result.rows, result.dimension, result.output_path)

client.hypervectors("rows.serva")   # one hypervector per row
```


## Reading the public index

A `.serva` keeps its header and hypervectors unencrypted at the front, so these
read straight off your disk. No request, no password, and they work offline.

```python
info = client.info("photo.serva")
print(info.n_rows, info.dimension, info.encrypted)

client.hypervectors("photo.serva")      # every row's hypervector
client.get_hv("photo.serva", 0)         # one row
```

Files encoded before the index was stored uncompressed can only be read by the
service; `hypervectors` and `get_hv` say so rather than returning something
wrong.

## Hugging Face

Push encoded datasets to the Hub and pull them back. Set `HF_TOKEN` or pass
`hf_token=...`; nothing extra to install.

```python
result = client.encode("photo.raw", password="my-secret")
client.hub.push(result, repo_id="your-name/my-dataset")

path = client.hub.pull("your-name/my-dataset", "photo.serva")
client.decode(path, password="my-secret", output="photo.raw")
```

## PyTorch

`ServaDataset` reads a folder of `.serva` files and returns each file's raw bytes
as a tensor, the model trains on the encoded bytes. Point it at a
directory, like `torchvision`'s `ImageFolder`.

```python
from serva.torch import ServaDataset
from torch.utils.data import DataLoader

ds = ServaDataset("data/train")
loader = DataLoader(ds, batch_size=32, shuffle=True)

for batch in loader:
    ...   # your model, your training step
```

The folder layout decides what you get:

- **Class subfolders** (`ants/*.serva`, `bees/*.serva`) → `(tensor, label)`.
- **A flat folder** → just the tensor, for text or other unlabeled data.

Use `label_fn` when labels aren't in folder names (a CSV lookup, a regression
target):

```python
ds = ServaDataset("data/train", label_fn=lambda path: scores[path.stem])
```

By default files are padded to a fixed length so batches stack. To keep every
byte and avoid padding side effects, use `length=None` with `pad_collate` it
pads each batch only to its longest file and returns a mask marking real bytes,
so the model ignores the padding:

```python
from serva.torch import ServaDataset, pad_collate

ds = ServaDataset("data/train", length=None)
loader = DataLoader(ds, batch_size=32, collate_fn=pad_collate)

for bytes, mask, labels in loader:
    ...   # pass mask to your model so padded positions are ignored
```

`ServaDataset` only turns `.serva` files into tensors. Reshaping is plain PyTorch 
that's what `transform` and the DataLoader's `collate_fn` are for.

## Configuration

| Setting | How | Default |
|---|---|---|
| API key | `Serva(api_key=...)` or `SERVA_API_KEY` | — (required to encode or decode) |
| Base URL | `Serva(base_url=...)` | production API |
| Hugging Face token | `Serva(hf_token=...)` or `HF_TOKEN` | — |

## Progress

Both calls draw a bar when stderr is a terminal, one line per phase.

```
  upload        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% 0:00:32
  encode/decode ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% 0:00:16
  download      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% 0:00:01
```

The upload and download lines track bytes moved. The middle line is named after
whichever call you made and tracks the work on the server, from the time it
reports as still to go.

## Errors

Everything raises a subclass of `ServaError`, connection failures and timeouts
included, so a single `except ServaError` covers all of them.

| Error | Raised when |
|---|---|
| `AuthError` | the API key is missing, invalid, or expired |
| `PasswordError` | the password does not decrypt this file |
| `PaymentRequiredError` | a payment method is needed to continue |
| `AccessDeniedError` | the account may not touch this resource |
| `NotFoundError` | the task, job, or file is gone from the server |
| `ConflictError` | the request clashes with work already done |
| `ValidationError` | the input or the request was rejected before any work started |
| `FileTooLargeError` | the file is over the service maximum |
| `RateLimitError` | too many requests in too short a window |
| `ServiceError` | the service failed while handling the request |
| `NetworkError` | the service could not be reached at all |
| `RequestTimeoutError` | the service did not answer in time |
| `HubError` | a Hugging Face operation failed |

Each one carries the message the service wrote, plus `status_code` and `code`,
so you can branch on the cause without matching on message text.

```python
try:
    client.decode("photo.serva", password="wrong")
except ServaError as exc:
    print(exc)          # Password is incorrect or file is corrupted
    print(exc.code)     # incorrect_password
```

## Versioning

Semantic versioning. Read the installed version from `serva.__version__`.

