Skip to main content

hugging-bay

Promise: Find open model files with recorded SHA-256 and licence labels; verify downloaded bytes locally.
Install: python -m pip install hugging-bay
MCP config: {"mcpServers":{"hugging-bay":{"url":"https://huggingbay.xyz/api/mcp"}}} (Cursor; public reads need no account).
First call: Client().resolve("Qwen/Qwen3-0.6B")
Ask: Missing a model, file, tool or feature? Ask with request_feature (MCP) or POST https://huggingbay.xyz/api/v1/requests. No account. You get an id and can check its status. What shipped: https://huggingbay.xyz/changelog
Changelog · API docs
Keywords: gguf, local-llm, huggingface-alternative, mcp

Usage

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.

0. Get an API key (self-serve, optional)

Public discovery — search, resolve, download-plan, and hosted downloads — needs no token. You only authenticate for account workflows (saves, collections, watchlists). Minting a reader token is self-serve, with no signup and no prior auth:

from hugging_bay import Client

issued = Client().create_reader_token(display_name="my app", purpose="serving models")
token = issued["apiKey"]["token"]      # shown once — store it securely
client = Client(token=token)
print(client.me()["role"])             # validate the token

The full walkthrough (curl, JS/TS, MCP) is the one-screen quickstart at https://huggingbay.xyz/quickstart. A 401/403 from any authorized endpoint returns next.href = /quickstart#get-a-key, so there is no dead-end.

1. Find a commercial-safe model for a rig

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

from hugging_bay import Client, HuggingBayError

client = Client()
try:
    resolved = client.resolve("Qwen/Qwen3-0.6B")
    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

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)

# Stream one hosted file, verify its exact plan hash, and record the completed
# pull. This stops the Hugging Bay redirect, follows the short-lived signed GCS
# URL without forwarding the API bearer, and only returns after attestation.
file = next(row for row in plan["files"] if row["path"] == "model.gguf")
# Case-only filename twins are refused by default. Opt in to suffix names
# allocated from the sorted full plan; generated suffixes skip every planned
# basename. The destination you pass supplies the selected file's base name,
# and existing unrelated names may advance its suffix. `pathMapping` reports
# the actual local path.
result = client.download_file(
    artifact_id,
    file["path"],
    "./models/art_123/" + file["path"],
    # sha256 and size_bytes default from the download plan when omitted:
    on_case_collision="suffix",
)
print(result.summary())  # compact one-liner (no nested completion JSON)
print(result.get("pathMapping"))

# 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"])

3b. One-shot local prompt (no background server)

After you have a GGUF on disk, run a single completion without starting llama-server, ollama serve, or similar long-lived processes:

from hugging_bay import llama_cpp_cli_prompt, llama_cpp_python_prompt

# llama.cpp CLI (binary must be on PATH):
text = llama_cpp_cli_prompt("./models/model.gguf", "Say hi in one sentence.", n_predict=32)

# Or llama-cpp-python in-process (pip install llama-cpp-python):
text = llama_cpp_python_prompt("./models/model.gguf", "Say hi in one sentence.", n_predict=32)

4. Diagnose a failing run

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)

Discovery → execution with Bay Run

BayRunClient runs the mirrored weights live on Bay Run (run.huggingbay.xyz, a separate origin). Take the next_call a catalog answer returns and run it — the client mints the anonymous $0 demo key for you and tolerates the edge's plain-text 429 "Rate exceeded.":

from hugging_bay import Client, BayRunClient

runnable = Client().find_runnable(task="embeddings", limit=1)
# hugging-bay.find-runnable.v1: the handoff lives at the top-level `next_call`
# (or under `recommendation.execution`); there is no `candidates` list.
next_call = runnable.get("next_call") or runnable["recommendation"]["execution"]

with BayRunClient() as run:            # mints a $0 demo key on first call
    out = run.run_next_call(next_call, input="text to embed")
    print(out)

# Or call the OpenAI-compatible surface directly:
with BayRunClient() as run:
    run.embeddings("BAAI/bge-small-en-v1.5", "hello world")
    run.rerank("BAAI/bge-reranker-base", query="q", documents=["a", "b"])
    run.run_pin("pin_abc123", input="...")

API surface

The new typed hosted methods require BayRunClient(enable_hosted_api=True). They are off by default. Existing execution methods keep their behavior. Use pins(), pin(id), mint_pin(request), instant_models(), instant_classify(request), instant_embed(request), instant_rerank(request), instant_chat(request), and coprocessor(request). Request and response types are in hugging_bay.bayrun_types.

provenance_keys() reads published Ed25519 keys. verify_receipt(receipt) checks a receipt locally with those keys. Unknown signers fail closed. Pin keys can differ from published provenance keys, so Pin verification may need an explicit trusted key document. Never use a receipt's embedded key as trust. The standalone hugging_bay.verify_receipt(receipt, keys) works offline. Verification authenticates signed claims. It does not prove model quality or the unsigned decision. For raw-result binding, set omit_raw_result=False when calling and pass result=... to verification.

With hosted methods enabled, the client retries 429 and 503 lane_cold once unless the server marks the error retriable: false. It accepts Retry-After seconds or HTTP dates and preserves the same body and idempotency key. A cold lane without a delay gets a one second retry. Delays above max_retry_after_seconds (default 120) raise BayRunError with the full .retry_after delay. Set honor_retry_after=False to let your application schedule retries. When the hosted flag is off, existing methods retain their 429/503 retry policy and 120 second cap.

See the hosted API guide and five examples.

Method Endpoint
create_reader_token(display_name, purpose) POST /api/account/reader-token (self-serve, no auth)
me() GET /api/me
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/v1/artifacts/{id}/bundle
passport(id) GET /api/v1/artifacts/{id}/passport
resolve(repo_or_url) GET /api/v1/resolve?repo=
download_plan(id, snapshot=None, tool=None) GET /api/v1/artifacts/{id}/download-plan
download_to_cache(id, path, ...) / snapshot_download(id, ...) shared cache: lease + parallel range download + SHA-256 + snapshot link
pin_snapshot() / snapshot() GET /api/v1/snapshot
download_file(id, path, destination, sha256=None, size_bytes=None, on_case_collision="error") GCS redirect + streamed SHA-256 verification + completion attestation; plan hash/size used when omitted; refuses case-folded path collisions by default, or returns deterministic .caseN destinations in pathMapping with on_case_collision="suffix"
lock(id) GET /api/v1/artifacts/{id}/lock
doctor(log_text, runtime) POST /api/doctor
find_runnable(task, limit, commercial, **filters) GET /api/agents/find-runnable
recipes() / recipe(slug) GET /api/recipes[/{slug}]
verify_lock(lock_dict, root_dir) offline, stdlib only
BayRunClient.free_key() POST /v1/keys/free (Bay Run)
BayRunClient.models() GET /v1/models (Bay Run)
BayRunClient.run_pin(pin_id, ...) POST /v1/run/{pin_id} (Bay Run)
BayRunClient.embeddings / rerank / classify / chat(...) OpenAI-compatible (Bay Run)
BayRunClient.run_next_call(next_call, input) executes a catalog next_call

Swarm mode (0.4.9)

For many agents on one machine (or many machines behind one IP):

from hugging_bay import Client, AsyncClient, RetryPolicy

# Backoff on 429/502/503/504 using Retry-After, RateLimit / RateLimit-Reset /
# X-RateLimit-Reset and the error body's retry_after_s, plus up to 25% jitter
# so a swarm does not return in one wave. HB_SWARM=1 turns this on too.
hb = Client(swarm=True)

# Pin one snapshot and hand the id to every agent: same question, same answer.
snap = hb.pin_snapshot()               # e.g. "s1-k3x9"
agent = Client(swarm=True, snapshot_id=snap)

# One verified copy per machine. Parallel range requests, resumable, SHA-256
# checked before the file appears; other processes asking for the same file
# wait on a cross-process lock and then reuse it.
folder = agent.snapshot_download("qwen/qwen3-8b-gguf", allow_patterns=["*Q4_K_M*.gguf", "*.json"])
path = agent.download_to_cache("qwen/qwen3-8b-gguf", "config.json")
print(agent.last_cache_report)         # downloaded / shared / cached, bytes, attestation

# Offline: HB_OFFLINE=1 (or Client(offline=True)) never touches the network and
# answers from the cache; anything missing raises OfflineModeError.

# Async twin of the catalog client (defaults to RetryPolicy.swarm()).
async def fetch():
    async with AsyncClient(snapshot_id=snap) as ahb:
        plans = await ahb.download_plans(["a", "b"], wait=10)
        return await ahb.snapshot_download("qwen/qwen3-8b-gguf")

Cache layout (Hugging Face style), root HB_CACHE_DIR, HB_HOME/hub or ~/.cache/hugging-bay/hub (%LOCALAPPDATA%\hugging-bay\hub on Windows):

blobs/<sha256>                         verified bytes
snapshots/<artifact>/<revision>/<path> -> ../../../blobs/<sha256>
refs/<artifact>/main                   last revision fetched (and refs/<artifact>/<snapshotId>)
manifests/<artifact>/<revision>.json   the files that revision holds
.locks/<sha256>.lock                   cross-process lock (never deleted)

The revision defaults to c-<digest of the plan's path/sha256/size list>, so identical plans share one folder; pass revision= to name it yourself. Snapshot entries are relative symlinks, falling back to hard links and then copies where symlinks are not allowed (Windows without Developer Mode). Keep the cache on a local disk: NFS/SMB may not honour the locks.

URLs come from one download-lease call for every missing file (128 per call), renewed when a signed URL expires; servers without the lease route fall back to the per-file download redirect, whose completion is attested after the SHA-256 check. When the API is unreachable and the requested snapshot is already cached, the cached copy is returned with a RuntimeWarning. A plan with case-only filename twins is refused with CaseCollisionError; fetch those with download_file(..., on_case_collision="suffix").

Errors

Every Hugging Bay error is the same typed envelope on every surface: {error, code, status, message, requestId, cause?, next}. Any response with status >= 400 raises HuggingBayError, which surfaces those fields directly — .error/.code (machine code), .message, .status, .request_id, .next (the {action, href} recovery step), and the raw .body. On a 503 with a Retry-After header the client waits once and retries a single time; pass swarm=True (or retry=RetryPolicy(...)) for full jittered backoff. A pinned read whose snapshot has aged out raises SnapshotExpiredError (status 410).

resolve() follows status-based error handling: a well-formed reference with no catalog match raises status 404; malformed/unsupported input raises 400 (invalid_query). Bay Run errors raise BayRunError, which is tolerant of the edge's non-JSON bodies and exposes .retry_after.

License

MIT

Public response note

Hugging Bay responses include a download note in note.summary. Current clients should read note, downloadable, and downloadStatus. Older clients that still need the previous response shape can opt in to it for one release; see the API reference.

Downloadable doesn't mean tested: this record says whether the exact file can be downloaded now, not that the model is complete or that it runs.

Release dependency

The request-and-status sentence above is approved copy held on the c29 AEO-72/AEO-73 deployment gate. Verify the deployed request_feature, get_request, and public changelog/status path before package publication or directory copy uses it.

Generated task client and resume

client.api exposes OpenAPI-generated task methods and typed dictionaries. client.search, client.artifact, client.find_runnable, client.deployment_plan and client.download_plan use these generated wire types. Deployment plans use GET /api/agents/deployment-plan?artifact=id; download plans keep their existing route.

Pass resume=True to client.download_file to retain interrupted bytes beside the destination. Retry the same call to continue. The SDK hashes the prefix again, validates the exact storage range, and checks the full SHA-256 before completion attestation and publication. A 200 response restarts the transfer. A crashed process can leave a lock; remove it only when no download is using it.

Run python examples/read_only.py for live GET-only reads. It never downloads model bytes or attests completion. See the SDK workflow for generation, drift checks and local packaging commands.

Metadata

Release files for hugging-bay 0.4.9

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hugging-bay 0.4.9
File Size Uploaded Provenance
hugging_bay-0.4.9.tar.gz 108.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for hugging-bay 0.4.9
File Interpreter ABI Platform
hugging_bay-0.4.9-py3-none-any.whl Python 3 none any Details

Total release size: 184.1 kB

Release files / hugging_bay-0.4.9.tar.gz

Download URL hugging_bay-0.4.9.tar.gz
Size 108.7 kB
Tags Source
SHA-256 checksum
How to use checksums
6d4cd0176b9f211fd280266e4655e8cf43cc77b3877204e1265bb24bbd9243b6
BLAKE2b-256 checksum
How to use checksums
d42d49fd5263b37db5c97b10353b7bb185816fd5eff75643d2b52c470da1f5ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release files / hugging_bay-0.4.9-py3-none-any.whl

Download URL hugging_bay-0.4.9-py3-none-any.whl
Size 75.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
33d59d160f3b1c06a5e3748f8f3c5c08cea326ba9a0b6064d09c57ff47a085f8
BLAKE2b-256 checksum
How to use checksums
b61f61ebbe2fa72218652e7640376d02c4fada301c27e6dbc225846c7414f579
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Security for hugging-bay 0.4.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page