Metadata-Version: 2.4
Name: hugging-bay
Version: 0.1.0
Summary: Thin, typed Python client for the Hugging Bay API — verified open-model discovery, safety, and lockfiles.
Author: Hugging Bay
License: MIT
Project-URL: Homepage, https://huggingbay.xyz
Project-URL: Repository, https://github.com/barneywohl/hugging-bay
Keywords: hugging-bay,models,safety,gguf,llm,provenance
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.24

# hugging-bay

A thin, typed Python client for the [Hugging Bay](https://huggingbay.xyz) API:
verified open-model discovery, safe-to-run verdicts, provenance passports, and
offline lockfile verification.

The client is intentionally minimal — one method per endpoint, no client-side
magic. Verdicts, fits, and identities are computed by the server and returned
as parsed JSON.

## Install

```bash
pip install hugging-bay
```

Requires Python 3.9+. The only runtime dependency is `httpx`.

## Usage

```python
from hugging_bay import Client

client = Client()  # defaults to https://huggingbay.xyz
# Optional auth: Client(token="sk-...") sends Authorization: Bearer <token>.
# The token is never printed in repr()/str() and never logged.
```

### 1. Find a commercial-safe model for a rig

```python
from hugging_bay import Client

client = Client()
result = client.find_model(
    task="coding",
    gpu="rtx4090-24",
    ctx=8192,
    device_family="nvidia",
    commercial=True,
    limit=3,
)
best = result["bestPick"]
print(best["repo"], "-> safe-to-run:", best["safety"]["verdict"])
```

### 2. Verify a source URL

```python
from hugging_bay import Client, HuggingBayError

client = Client()
try:
    resolved = client.resolve("https://huggingface.co/meta-llama/Llama-3.1-8B")
    print("resolved artifact:", resolved.get("artifactId") or resolved)
except HuggingBayError as err:
    print("could not resolve:", err.status, err.error)
```

### 3. Pull plan, then verify the lock against local files

```python
from hugging_bay import Client, verify_lock

client = Client()
artifact_id = "art_123"

# The hosted download plan (signed manifest + per-file hashes).
plan = client.download_plan(artifact_id)

# ... download the files into ./models/art_123 using the plan ...

# Recompute local hashes and compare to the bay.lock document — pure stdlib,
# no network.
lock = client.lock(artifact_id)
report = verify_lock(lock, root_dir="./models/art_123")
if report["ok"]:
    print(f"verified {report['checked']} files")
else:
    for mismatch in report["mismatches"]:
        print("BAD:", mismatch["path"], mismatch["reason"])
```

### 4. Diagnose a failing run

```python
from hugging_bay import Client

client = Client()
diagnosis = client.doctor(
    log_text="llama_model_load: error loading model: CUDA out of memory",
    runtime="llama.cpp",
)
if diagnosis["topFix"]:
    print("top fix:", diagnosis["topFix"])
for alt in diagnosis.get("smallerHostedAlternatives", []):
    print("smaller option:", alt)
```

## API surface

| Method | Endpoint |
| --- | --- |
| `find_model(task, gpu, ctx, device_family, commercial, limit)` | `GET /api/agents/find-model` |
| `artifact(id)` | `GET /api/v1/artifacts/{id}` |
| `safety(id)` | `GET /api/v1/artifacts/{id}/safety` |
| `bundle(id)` | `GET /api/artifacts/{id}/bundle` |
| `passport(id)` | `GET /api/artifacts/{id}/passport` |
| `resolve(repo_or_url)` | `GET /api/resolve?repo=` |
| `download_plan(id)` | `GET /api/v1/artifacts/{id}/download-plan` |
| `lock(id)` | `GET /api/artifacts/{id}/lock` |
| `doctor(log_text, runtime)` | `POST /api/doctor` |
| `recipes()` / `recipe(slug)` | `GET /api/recipes[/{slug}]` |
| `verify_lock(lock_dict, root_dir)` | offline, stdlib only |

## Errors

Any response with status `>= 400` raises `HuggingBayError(status, body)`. The
server's `error` field is exposed as `.error`; the parsed body is on `.body`.
On a `503` with a `Retry-After` header the client waits once and retries a
single time before raising.

## License

MIT
