cortexgrid-infer
One API to run inference across providers — hosted APIs (Anthropic) and models you deploy yourself on the cortexgrid cluster (HuggingFace LLMs and diffusion pipelines). You address every model by a single model id string; the gateway routes it to the right provider by prefix.
| Prefix | Provider | Weights live on the cluster? |
|---|---|---|
Anthropic/ |
Anthropic hosted API (e.g. Anthropic/claude-sonnet-5) |
no — hosted |
hf: |
HuggingFace causal-LM, served via cortexgrid (e.g. hf:Qwen/Qwen2.5-7B-Instruct) |
yes |
hf-image: |
HuggingFace diffusion pipeline, served via cortexgrid (e.g. hf-image:black-forest-labs/FLUX.2-klein-base-4B) |
yes |
The model lifecycle
Hosted models (Anthropic/) have nothing to stage — call deploy_model and go.
Cluster models (hf:, hf-image:) move through three separate, independently
observable stages, because staging tens of GB of weights should not block a deploy:
upload_model ──▶ [ registry: uploading ─▶ ready ] ──▶ deploy_model ──▶ [ serving: deploying ─▶ running ] ──▶ inference
│ │
└── weights ingested HF ─▶ cluster node ─▶ registry (never through your machine) undeploy_model / delete_model
- Upload stages the weights into the cortexgrid registry. The download from
HuggingFace and the upload to the registry both run on the cluster (submitted
as a
cortexgrid.remotejob), so large weights never round-trip through your machine. Only the model files are stored: HuggingFace's download bookkeeping (.cache/huggingface/inside the download folder) is dropped first. Returns a job id; poll until the model isready. The weights are imported once and shared by every run (cortexgrid.import_model), so callupload_modelon every run: once the model isreadyit uploads no weights, but re-bundles the serve code if it changed and tags the run with the model it used, then returnsNone. - Deploy blocks until the model is ready to serve and returns a client. It
requires the model to be registry-
ready(raises otherwise), reuses a running Ray Serve app, waits on one still coming up, and replaces a failed one. See Deploying. - Query reports one combined phase across both lifecycles — registry while the weights upload, then serving once an app exists.
- Teardown:
model.undeploy()frees the GPU but keeps the weights (re-deploy is cheap).delete_model(id)removes everything — it undeploys, then deletes the weights and serve bundle from the registry.
Quick start
Every session starts a cortexgrid run. The model itself is imported once and shared across runs; each run that uploads it is tagged with it.
import asyncio, time
import cortexgrid
import cortexgrid_infer as mg
cortexgrid.init(experiment="img-gen") # one call per process; starts an MLflow run
mid = "hf-image:black-forest-labs/FLUX.2-klein-base-4B"
# 1. Upload — ingest weights into the registry, on the cluster.
mg.upload_model(mid) # job id, or None if already imported
# 2. Query — wait until the weights are registered and deployable. status is None
# for a brief window after upload_model, before the ingest job registers the version.
status = mg.deployment_status(mid)
while status is None or status.phase != "ready":
time.sleep(10)
status = mg.deployment_status(mid)
# 3. Deploy — blocks until the Ray Serve app is running; get a client back.
# Raises mg.ModelDeployFailed if the deploy fails, TimeoutError after 30 min.
model = mg.deploy_model(mid, timeout=1800)
# 4. Inference.
async def run():
result = await mg.generate(model, "a red bicycle on a beach at sunrise")
open("out.png", "wb").write(result.image) # PNG bytes; also .width/.height/.params
asyncio.run(run())
# 5. Teardown.
model.undeploy() # free the GPU, keep the weights (fast re-deploy later)
mg.delete_model(mid) # OR remove everything: undeploy + delete weights & bundle
Text completion (hf: and Anthropic/)
complete streams CompletionChunks (.content, .tool_calls, .finish_reason):
model = mg.deploy_model("hf:Qwen/Qwen2.5-7B-Instruct") # after upload → ready
messages = [{"role": "user", "content": "Explain RAG in one sentence."}]
async def run():
async for chunk in mg.complete(model, messages, max_new_tokens=512, temperature=0.7):
if chunk.has_text:
print(chunk.content, end="", flush=True)
Hosted models skip staging entirely — upload_model / deployment_status /
delete_model are no-ops that return None, so just deploy and call:
model = mg.deploy_model("Anthropic/claude-sonnet-5")
async for chunk in mg.complete(model, messages):
...
Deploying
deploy_model is the one call that makes a cluster model servable. It reads the
model's current Ray Serve app and does what that state needs:
| Serving phase | deploy_model |
|---|---|
running |
returns a client straight away |
not_started, deploying, unhealthy |
waits for running; it never re-sends the spec, which would restart a build in progress |
no app yet, failed, deleting |
deploys afresh; cortexgrid removes a failed app first, so the retry really restarts it |
It raises ModelDeployFailed when the app fails to deploy, with Ray's message
(including the replica's traceback), and TimeoutError when the model is not
running within timeout seconds. timeout=None (the default) waits indefinitely,
so a model waiting for a free GPU blocks forever. ModelDeployFailed is
re-exported from cortexgrid and subclasses RuntimeError.
try:
model = mg.deploy_model(mid, timeout=3600)
except mg.ModelDeployFailed as e:
log.error("deploying %s failed: %s", mid, e)
raise
A failed app is left in place, so its phase and message stay visible (e.g. in the
cortexgrid UI) until the next deploy_model replaces it. There is no need to tear
it down yourself. Retrying only helps with transient failures; a serve-app that
fails deterministically fails again until it is fixed and re-uploaded.
To follow a deploy without blocking - say, a status endpoint polled while another
thread deploys - poll deployment_status(id) instead (see
Status phases). Hosted models (Anthropic/) have nothing to
deploy: deploy_model returns immediately and ignores timeout.
API
All exported from cortexgrid_infer.*. Each generic entry point routes by model-id
prefix to the provider that registered for it.
| Function | Purpose |
|---|---|
upload_model(id) -> str | None |
Import the weights into the registry; call it on every run. Returns the job id of the cluster import, or None if the model is already imported (its serve code is re-bundled if changed and the run is tagged), another process is importing it, or there is nothing to stage (hosted models). |
deployment_status(id) -> status | None |
Combined phase: the registry lifecycle (uploading/ready/upload_failed/broken) while staging, then the serving lifecycle (deploying/running/failed/…) once a Serve app exists. None for hosted models. |
deploy_model(id, timeout=None) -> DeployedModel |
Return a client once the model is ready to serve (must be registry-ready). Blocks; see Deploying. Raises ModelDeployFailed when the deploy fails, TimeoutError past timeout seconds (None waits indefinitely). |
complete(model, messages, tools=None, max_new_tokens=2048, temperature=0.7, **kw) |
Async stream of CompletionChunk for a CompletingModel (hf:, Anthropic/). |
generate(model, prompt, *, image=None, **kw) -> GeneratedImage |
One image from a GeneratingModel (hf-image:). Pass image= bytes for img2img. |
model.undeploy() |
Free the GPU (tear down the Serve app); weights stay registered. |
delete_model(id) |
Full cleanup: undeploy if running, then delete the weights + serve bundle from the registry. Idempotent; no-op for hosted models. |
Extending to a new provider means registering four prefix handlers:
register_provider (deploy), register_uploader (upload), register_status_provider
(status), register_deleter (cleanup). A deploy factory is called as
factory(model_id, timeout) and returns a model that is ready to serve, or None
to pass the id to the next provider. A cluster-backed provider gets the behaviour in
Deploying from
cortexgrid_infer.providers.serving.ensure_serving(family, suffix, run_name, timeout),
which returns the Serve app's URL once it is running.
Status phases
deployment_status(id).phase surfaces cortexgrid's lifecycle phases directly:
- Registry (while uploading):
uploading→ready;upload_failed/brokenon error. - Serving (once deployed):
not_started→deploying→running;unhealthy/failed/deleting.
See the cortexgrid model-serving docs for the full state machine and error table.
Notes
- HuggingFace auth. For gated/private repos, set
HF_TOKENin your environment; it is read at upload time and passed to the cluster ingest job. - Image weight pruning. The
hf-image:upload skips example images, docs, and a repo's consolidated single-file checkpoint by default (only the component weights the pipeline loads are staged). Extend per-model withHF_IMAGE_SNAPSHOT_IGNORE(comma-separated globs). - Shared across runs. A model's identity is
(family, suffix, cortexgrid.IMPORTED), derived from the model id alone, so every run uploads, deploys, and deletes the same copy.delete_modeltherefore removes it for all runs. Upload still needs an active cortexgrid run, which it tags with the model.
Metadata
Release files for cortexgrid-infer 0.1.14
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cortexgrid_infer-0.1.14.tar.gz | 21.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cortexgrid_infer-0.1.14-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.6 kB
Release files / cortexgrid_infer-0.1.14.tar.gz
| Download URL | cortexgrid_infer-0.1.14.tar.gz |
|---|---|
| Size | 21.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
07ec1dc273a074ed89bcb77e6c3ca40fa3243c195e0961ed1d2ba695bbc0ac36
|
|
BLAKE2b-256 checksum How to use checksums |
4ef9d0c13a7d052213558e2739ffbab5ed758117a54c24b9eb3b0c96c4d30937
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency logRelease files / cortexgrid_infer-0.1.14-py3-none-any.whl
| Download URL | cortexgrid_infer-0.1.14-py3-none-any.whl |
|---|---|
| Size | 29.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
39a98a290b9e8b5ad99f6c44f53fc3ea69b019d9279add08d3d56529bbd24f37
|
|
BLAKE2b-256 checksum How to use checksums |
d804cb5fc84fd15cb1b6838f2ab1d622c74da6274c3f97768e8d0e92213fcaf7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency log