cortexgrid-infer 0.3.7__tar.gz → 0.3.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/PKG-INFO +48 -48
  2. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/README.md +45 -46
  3. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/__init__.py +7 -17
  4. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/core.py +72 -60
  5. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/importers/base.py +0 -4
  6. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/registry.py +5 -13
  7. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/anthropic.py +41 -53
  8. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/base.py +2 -14
  9. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/gemini/text2image.py +32 -28
  10. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/gemini/text2text.py +45 -57
  11. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/image2mesh.py +25 -29
  12. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/streamed_completion.py +27 -40
  13. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/text2image.py +35 -36
  14. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/text2text.py +47 -73
  15. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/text_rewriter.py +19 -20
  16. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/utils.py +13 -24
  17. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/pyproject.toml +3 -2
  18. cortexgrid_infer-0.3.7/cortexgrid_infer/protocols/__init__.py +0 -14
  19. cortexgrid_infer-0.3.7/cortexgrid_infer/protocols/completion.py +0 -92
  20. cortexgrid_infer-0.3.7/cortexgrid_infer/protocols/imaging.py +0 -74
  21. cortexgrid_infer-0.3.7/cortexgrid_infer/protocols/meshing.py +0 -72
  22. cortexgrid_infer-0.3.7/cortexgrid_infer/protocols/rewriting.py +0 -44
  23. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/.gitignore +0 -0
  24. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/LICENSE +0 -0
  25. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/compiling.py +0 -0
  26. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/device.py +0 -0
  27. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/importers/__init__.py +0 -0
  28. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/importers/huggingface.py +0 -0
  29. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/py.typed +0 -0
  30. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/__init__.py +0 -0
  31. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/gemini/__init__.py +0 -0
  32. {cortexgrid_infer-0.3.7 → cortexgrid_infer-0.3.9}/cortexgrid_infer/serve_apps/gemini/base.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cortexgrid-infer
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: Serve apps per task (text-to-text, text-to-image, image-to-mesh) and importers per source (HuggingFace) for running models on cortexgrid
5
5
  Project-URL: Homepage, https://github.com/robodatalab/cortexgrid-infer
6
6
  Project-URL: Repository, https://github.com/robodatalab/cortexgrid-infer
@@ -9,7 +9,7 @@ License-File: LICENSE
9
9
  Requires-Python: <3.12,>=3.11
10
10
  Requires-Dist: accelerate>=0.33
11
11
  Requires-Dist: anthropic>=0.40.0
12
- Requires-Dist: cortexgrid>=0.3.12
12
+ Requires-Dist: cortexgrid>=0.3.18
13
13
  Requires-Dist: diffusers>=0.38
14
14
  Requires-Dist: fastapi>=0.133
15
15
  Requires-Dist: google-genai>=1.0
@@ -23,6 +23,7 @@ Requires-Dist: sentencepiece>=0.2
23
23
  Requires-Dist: torch>=2.0.0
24
24
  Requires-Dist: transformers>=4.47.0
25
25
  Provides-Extra: dev
26
+ Requires-Dist: parameterized>=0.9.0; extra == 'dev'
26
27
  Requires-Dist: pytest>=8.0; extra == 'dev'
27
28
  Description-Content-Type: text/markdown
28
29
 
@@ -50,23 +51,24 @@ Upgrading from 0.2.x? See [MIGRATION.md](https://github.com/robodatalab/cortexgr
50
51
  A model is two things, and the library keeps them apart:
51
52
 
52
53
  - **What runs it** — a serve app in `cortexgrid_infer.serve_apps`, named for the task
53
- it runs, not for where the model came from. It loads the weights, answers the task's
54
- routes (defined in `cortexgrid_infer.protocols`), and names the client that speaks
55
- them. One serve app runs every model of its task, whichever source staged it.
54
+ it runs, not for where the model came from. It loads the weights and serves the
55
+ task's endpoints, declared by the task's type (`CompletingModel`, say); cortexgrid
56
+ generates the client that calls them. One serve app runs every model of its task,
57
+ whichever source staged it.
56
58
  - **Where its weights come from** — an importer in `cortexgrid_infer.importers`, one
57
59
  per source. It names the model in the registry, downloads the weights, and sizes
58
60
  them from the source's metadata. It is handed the serve app, so one importer takes
59
61
  any model its source holds.
60
62
 
61
- | Serve app | Task | Runs | Client |
63
+ | Serve app | Task | Runs | Task type |
62
64
  |---|---|---|---|
63
- | `Text2Text` | text to text | any causal LM in the transformers layout, via `AutoModelForCausalLM` | `ServedCompletingModel` (`complete`) |
64
- | `Text2Image` | text (and image) to image | any diffusers pipeline | `ServedGeneratingModel` (`generate`) |
65
- | `Image2Mesh` — a base: subclass it with the model's `load` and `make_mesh` | image to mesh | the model the subclass loads, behind the task's `POST /mesh` | `ServedMeshingModel` (`mesh`: one picture in, a `GeneratedMesh` out) |
66
- | `TextRewriter` | text to text, rewritten | any encoder-decoder LM (T5, BART, Marian, ...) in the transformers layout, via `AutoModelForSeq2SeqLM` | `ServedRewritingModel` (`rewrite`: one text in, one text out) |
67
- | `AnthropicText2Text` | text to text | the Anthropic API, forwarded from the cluster | `ServedCompletingModel` (`complete`) |
68
- | `GeminiText2Text` | text to text | the Gemini API, forwarded from the cluster | `ServedCompletingModel` (`complete`) |
69
- | `GeminiText2Image` | text to image | the Gemini API, forwarded from the cluster | `ServedGeneratingModel` (`generate`) |
65
+ | `Text2Text` | text to text | any causal LM in the transformers layout, via `AutoModelForCausalLM` | `CompletingModel` (`complete`), plus its own `loglikelihoods` and `last_hidden_states` |
66
+ | `Text2Image` | text (and image) to image | any diffusers pipeline | `GeneratingModel` (`generate`) |
67
+ | `Image2Mesh` — a base: subclass it with the model's `load` and `make_mesh` | image to mesh | the model the subclass loads, behind the task's `mesh` endpoint | `MeshingModel` (`mesh`: one picture in, a `GeneratedMesh` out) |
68
+ | `TextRewriter` | text to text, rewritten | any encoder-decoder LM (T5, BART, Marian, ...) in the transformers layout, via `AutoModelForSeq2SeqLM` | `RewritingModel` (`rewrite`: one text in, one text out) |
69
+ | `AnthropicText2Text` | text to text | the Anthropic API, forwarded from the cluster | `CompletingModel` (`complete`) |
70
+ | `GeminiText2Text` | text to text | the Gemini API, forwarded from the cluster | `CompletingModel` (`complete`) |
71
+ | `GeminiText2Image` | text to image | the Gemini API, forwarded from the cluster | `GeneratingModel` (`generate`) |
70
72
 
71
73
  | Importer | Source |
72
74
  |---|---|
@@ -85,11 +87,13 @@ need:
85
87
  | `.serve_app` | the class cortexgrid bundles and runs on the cluster |
86
88
  | `.requirements()` | what one replica needs to be placed and to run |
87
89
  | `.config()` | settings the serve app reads at construction, if it needs any |
88
- | `.client(url)` | a client that speaks the deployed app's routes |
89
90
  | `.source` | *(importers only)* a callable that downloads the weights and returns the directory |
90
91
 
91
92
  One with weights goes through `cortexgrid.import_model`, one without through
92
- `cortexgrid.register_model`.
93
+ `cortexgrid.register_model`. Once deployed, the `cortexgrid.Deployment` hands back
94
+ the client that calls the app's endpoints, typed as the serve app and so as its task
95
+ type: `deployment.client()` once the app serves, or `deployment.client_async()` at
96
+ once, with `await model.is_ready()` to ask.
93
97
 
94
98
  ## Quick start
95
99
 
@@ -117,13 +121,11 @@ cortexgrid.remote(
117
121
  import_weights, imp, imp.requirements(), num_gpus=0, num_cpus=2
118
122
  ).result()
119
123
 
120
- # 2. Deploy — blocks until the Ray Serve app is running.
121
- deployment = cortexgrid.deploy_model(
122
- imp.family, imp.suffix, cortexgrid.IMPORTED, wait=True, timeout=1800
123
- )
124
+ # 2. Deploy.
125
+ deployment = cortexgrid.deploy_model(imp.family, imp.suffix, cortexgrid.IMPORTED, timeout=1800)
124
126
 
125
- # 3. Inference.
126
- model = imp.client(deployment.url)
127
+ # 3. Inference — the client blocks until the Ray Serve app is running.
128
+ model: mg.GeneratingModel = deployment.client()
127
129
 
128
130
  async def run():
129
131
  result = await mg.generate(model, "a red bicycle on a beach at sunrise")
@@ -278,11 +280,13 @@ it is the floor; only quantizing the weights or running fewer forwards moves it.
278
280
 
279
281
  ## Inference
280
282
 
281
- `complete` streams `CompletionChunk`s (`.content`, `.thinking`, `.tool_calls`, `.finish_reason`):
283
+ `complete` streams `CompletionChunk`s (`.content`, `.thinking`, `.tool_calls`, `.finish_reason`).
284
+ `tools` are Python functions or tool specs; a function goes to the model as its spec,
285
+ and each `ToolCall` comes back as data (`.id`, `.name`, `.arguments`) for the caller
286
+ to run:
282
287
 
283
288
  ```python
284
- imp = mg.HuggingFaceImporter("Qwen/Qwen2.5-7B-Instruct", mg.Text2Text)
285
- model = imp.client(deployment.url)
289
+ model: mg.CompletingModel = deployment.client()
286
290
  messages = [{"role": "user", "content": "Explain RAG in one sentence."}]
287
291
 
288
292
  async for chunk in mg.complete(model, messages, max_new_tokens=512, temperature=0.7):
@@ -323,9 +327,9 @@ a re-deploy rather than a re-registration.
323
327
 
324
328
  A replica asks for no hardware at all, so it is placed on any node, CPU-only
325
329
  included. From there it deploys and streams exactly like a cluster-served model:
326
- `entry.client(deployment.url)` returns the same `ServedCompletingModel`. Anthropic
330
+ `deployment.client()` is a `CompletingModel` like `Text2Text`'s. Anthropic
327
331
  reports tool calls as structured blocks rather than as generated text, so the
328
- serve app re-encodes them into the text form the client parses.
332
+ serve app turns them into `ToolCall`s as they come.
329
333
 
330
334
  ### Gemini
331
335
 
@@ -346,17 +350,16 @@ cortexgrid.register_model(
346
350
 
347
351
  Everything said above for Anthropic holds: `config` carries the model name and
348
352
  the name of the secret (`api_key_secret="..."` to use another), both editable on
349
- the model card; a replica needs no hardware; and `entry.client(deployment.url)`
350
- is a `ServedCompletingModel`, with Gemini's function calls re-encoded into the
351
- text form the client parses.
353
+ the model card; a replica needs no hardware; and `deployment.client()`
354
+ is a `CompletingModel`, with Gemini's function calls turned into `ToolCall`s.
352
355
 
353
- `GeminiText2Image` answers the same `/generate` route as `Text2Image`, so its
354
- client is a `ServedGeneratingModel`:
356
+ `GeminiText2Image` is a `GeneratingModel` like `Text2Image`:
355
357
 
356
358
  ```python
357
359
  entry = mg.Hosted("gemini-2.5-flash-image", mg.GeminiText2Image)
358
360
  ...
359
- picture = await mg.generate(entry.client(deployment.url), "a fox in the snow", size=2048)
361
+ model: mg.GeneratingModel = deployment.client()
362
+ picture = await mg.generate(model, "a fox in the snow", size=2048)
360
363
  ```
361
364
 
362
365
  It takes a prompt only, and makes a square picture. `size` picks Gemini's size
@@ -368,40 +371,37 @@ diffusion settings with no Gemini counterpart, and come back as `None`.
368
371
  | | |
369
372
  |---|---|
370
373
  | `HuggingFaceImporter(hf_id, serve_app, token=None, ignore_patterns=None)` | Importer for a model on the HuggingFace Hub, run by `serve_app`. |
371
- | `Importer` | Base of the importers: identity, scratch directory, `source`, and `requirements()` / `client(url)` taken from the serve app. A new source implements `download(local_dir)` and `weights()`. |
374
+ | `Importer` | Base of the importers: identity, scratch directory, `source`, and `requirements()` taken from the serve app. A new source implements `download(local_dir)` and `weights()`. |
372
375
  | `Hosted(model_id, serve_app, **settings)` | Entry for a model hosted elsewhere; no weights. `settings` are the serve app's `config` arguments. |
373
- | `ModelEntry` | Base of both: `model_id`, `family`, `suffix`, `serve_app`, `requirements()`, `config()`, `client(url)`. |
376
+ | `ModelEntry` | Base of both: `model_id`, `family`, `suffix`, `serve_app`, `requirements()`, `config()`. |
374
377
  | `Text2Text`, `Text2Image`, `TextRewriter`, `Image2Mesh` | Serve apps that run weights (`LocalModel`s). |
375
378
  | `AnthropicText2Text` | Serve app that forwards to the Anthropic API (a `HostedModel`). |
376
379
  | `GeminiText2Text`, `GeminiText2Image` | Serve apps that forward to the Gemini API (`HostedModel`s sharing the `GeminiModel` base). |
377
- | `LocalModel` | Base of the serve apps that run weights: `ignore_patterns()`, `bytes_per_param`, `min_vram_gb`, `requirements(weights)`, `config()` (the model card's defaults, `compile: "false"` among them), `client(url, name)`. |
378
- | `HostedModel` | Base of the serve apps that forward: `config(model_id, **settings)`, `requirements()`, `client(url, name)`. |
380
+ | `LocalModel` | Base of the serve apps that run weights: `ignore_patterns()`, `bytes_per_param`, `min_vram_gb`, `requirements(weights)`, `config()` (the model card's defaults, `compile: "false"` among them). |
381
+ | `HostedModel` | Base of the serve apps that forward: `config(model_id, **settings)`, `requirements()`. |
379
382
  | `Weights(params=None, file_bytes=0)` | What an importer reads about the weights, handed to the serve app to size a replica. |
380
- | `complete(model, messages, tools=None, max_new_tokens=2048, temperature=0.7, **kw)` | Async stream of `CompletionChunk` for a `CompletingModel`. |
381
- | `generate(model, prompt, *, image=None, **kw) -> GeneratedImage` | One image from a `GeneratingModel`. |
382
- | `ServedCompletingModel(url, model_id)` | Client for any deployed completion app. |
383
- | `ServedGeneratingModel(url, model_id)` | Client for any deployed text-to-image app. |
384
- | `ServedMeshingModel(url, model_id)` | Client for any deployed image-to-mesh app. |
383
+ | `complete(model, messages, tools=None, max_new_tokens=None, temperature=0.7)` | Async stream of `CompletionChunk` for a `CompletingModel`. |
384
+ | `generate(model, prompt, *, image=None, steps=None, guidance=None, size=1024, seed=None) -> GeneratedImage` | One image from a `GeneratingModel`. |
385
385
  | `split_model_id(model_id) -> (family, suffix)` | The registry identity an importer derives. |
386
386
  | `detect_device()` | The torch device a serve app should load onto. |
387
387
  | `compiling.Mode.GRAPHED` / `compiling.Mode.FUSED` | The two modes a serve app chooses between. See [Compilation](#compilation). |
388
388
  | `compiling.supported(device)` / `compile_module(module, device, mode)` / `compile_pipeline(pipe, device, mode)` | Whether to compile here, and what a serve app calls to do it. |
389
389
  | `ModelDeployFailed` | Re-exported from cortexgrid; subclasses `RuntimeError`. |
390
390
 
391
- Types: `DeployedModel`, `CompletingModel`, `GeneratingModel`, `MeshingModel`,
391
+ Types: `CompletingModel`, `GeneratingModel`, `MeshingModel`, `RewritingModel` (each
392
+ the abstract base of its task's serve apps, declaring the task's endpoints),
392
393
  `CompletionChunk`, `GeneratedImage`, `GeneratedMesh`, `ToolCall`, `Message`, `Tool`,
393
- `ToolSpec`.
394
+ `ToolSpec`, `Tensor`.
394
395
 
395
396
  Extending it:
396
397
 
397
398
  - **A new source** is one importer: subclass `Importer` with `download(local_dir)`
398
399
  and `weights()`, and every serve app whose weight format it delivers runs its
399
400
  models unchanged.
400
- - **A new hosted API** is one serve app: subclass `HostedModel`, and register it
401
- with `Hosted`. A text-to-text one only has to stream text from `POST /complete`,
402
- inlining tool calls as `<tool_call>{"name": ..., "arguments": {...}}</tool_call>`
403
- (there is an `encode_tool_call` for upstreams that report them structurally) —
404
- then `ServedCompletingModel` is its client, unchanged.
401
+ - **A new hosted API** is one serve app: subclass `HostedModel` and the task's type,
402
+ and register it with `Hosted`. A text-to-text one subclasses `CompletingModel` and
403
+ streams `CompletionChunk`s from its `complete` endpoint, with
404
+ `StreamedCompletionWriter` turning the upstream's text and tool calls into them.
405
405
  - **A model no task's serve app can load** gets its own serve app — an `Image2Mesh`
406
406
  subclass, say, with `load` and `make_mesh`, and `compile` if compiling it pays —
407
407
  and, if its source is unusual too, its own importer.
@@ -22,23 +22,24 @@ Upgrading from 0.2.x? See [MIGRATION.md](https://github.com/robodatalab/cortexgr
22
22
  A model is two things, and the library keeps them apart:
23
23
 
24
24
  - **What runs it** — a serve app in `cortexgrid_infer.serve_apps`, named for the task
25
- it runs, not for where the model came from. It loads the weights, answers the task's
26
- routes (defined in `cortexgrid_infer.protocols`), and names the client that speaks
27
- them. One serve app runs every model of its task, whichever source staged it.
25
+ it runs, not for where the model came from. It loads the weights and serves the
26
+ task's endpoints, declared by the task's type (`CompletingModel`, say); cortexgrid
27
+ generates the client that calls them. One serve app runs every model of its task,
28
+ whichever source staged it.
28
29
  - **Where its weights come from** — an importer in `cortexgrid_infer.importers`, one
29
30
  per source. It names the model in the registry, downloads the weights, and sizes
30
31
  them from the source's metadata. It is handed the serve app, so one importer takes
31
32
  any model its source holds.
32
33
 
33
- | Serve app | Task | Runs | Client |
34
+ | Serve app | Task | Runs | Task type |
34
35
  |---|---|---|---|
35
- | `Text2Text` | text to text | any causal LM in the transformers layout, via `AutoModelForCausalLM` | `ServedCompletingModel` (`complete`) |
36
- | `Text2Image` | text (and image) to image | any diffusers pipeline | `ServedGeneratingModel` (`generate`) |
37
- | `Image2Mesh` — a base: subclass it with the model's `load` and `make_mesh` | image to mesh | the model the subclass loads, behind the task's `POST /mesh` | `ServedMeshingModel` (`mesh`: one picture in, a `GeneratedMesh` out) |
38
- | `TextRewriter` | text to text, rewritten | any encoder-decoder LM (T5, BART, Marian, ...) in the transformers layout, via `AutoModelForSeq2SeqLM` | `ServedRewritingModel` (`rewrite`: one text in, one text out) |
39
- | `AnthropicText2Text` | text to text | the Anthropic API, forwarded from the cluster | `ServedCompletingModel` (`complete`) |
40
- | `GeminiText2Text` | text to text | the Gemini API, forwarded from the cluster | `ServedCompletingModel` (`complete`) |
41
- | `GeminiText2Image` | text to image | the Gemini API, forwarded from the cluster | `ServedGeneratingModel` (`generate`) |
36
+ | `Text2Text` | text to text | any causal LM in the transformers layout, via `AutoModelForCausalLM` | `CompletingModel` (`complete`), plus its own `loglikelihoods` and `last_hidden_states` |
37
+ | `Text2Image` | text (and image) to image | any diffusers pipeline | `GeneratingModel` (`generate`) |
38
+ | `Image2Mesh` — a base: subclass it with the model's `load` and `make_mesh` | image to mesh | the model the subclass loads, behind the task's `mesh` endpoint | `MeshingModel` (`mesh`: one picture in, a `GeneratedMesh` out) |
39
+ | `TextRewriter` | text to text, rewritten | any encoder-decoder LM (T5, BART, Marian, ...) in the transformers layout, via `AutoModelForSeq2SeqLM` | `RewritingModel` (`rewrite`: one text in, one text out) |
40
+ | `AnthropicText2Text` | text to text | the Anthropic API, forwarded from the cluster | `CompletingModel` (`complete`) |
41
+ | `GeminiText2Text` | text to text | the Gemini API, forwarded from the cluster | `CompletingModel` (`complete`) |
42
+ | `GeminiText2Image` | text to image | the Gemini API, forwarded from the cluster | `GeneratingModel` (`generate`) |
42
43
 
43
44
  | Importer | Source |
44
45
  |---|---|
@@ -57,11 +58,13 @@ need:
57
58
  | `.serve_app` | the class cortexgrid bundles and runs on the cluster |
58
59
  | `.requirements()` | what one replica needs to be placed and to run |
59
60
  | `.config()` | settings the serve app reads at construction, if it needs any |
60
- | `.client(url)` | a client that speaks the deployed app's routes |
61
61
  | `.source` | *(importers only)* a callable that downloads the weights and returns the directory |
62
62
 
63
63
  One with weights goes through `cortexgrid.import_model`, one without through
64
- `cortexgrid.register_model`.
64
+ `cortexgrid.register_model`. Once deployed, the `cortexgrid.Deployment` hands back
65
+ the client that calls the app's endpoints, typed as the serve app and so as its task
66
+ type: `deployment.client()` once the app serves, or `deployment.client_async()` at
67
+ once, with `await model.is_ready()` to ask.
65
68
 
66
69
  ## Quick start
67
70
 
@@ -89,13 +92,11 @@ cortexgrid.remote(
89
92
  import_weights, imp, imp.requirements(), num_gpus=0, num_cpus=2
90
93
  ).result()
91
94
 
92
- # 2. Deploy — blocks until the Ray Serve app is running.
93
- deployment = cortexgrid.deploy_model(
94
- imp.family, imp.suffix, cortexgrid.IMPORTED, wait=True, timeout=1800
95
- )
95
+ # 2. Deploy.
96
+ deployment = cortexgrid.deploy_model(imp.family, imp.suffix, cortexgrid.IMPORTED, timeout=1800)
96
97
 
97
- # 3. Inference.
98
- model = imp.client(deployment.url)
98
+ # 3. Inference — the client blocks until the Ray Serve app is running.
99
+ model: mg.GeneratingModel = deployment.client()
99
100
 
100
101
  async def run():
101
102
  result = await mg.generate(model, "a red bicycle on a beach at sunrise")
@@ -250,11 +251,13 @@ it is the floor; only quantizing the weights or running fewer forwards moves it.
250
251
 
251
252
  ## Inference
252
253
 
253
- `complete` streams `CompletionChunk`s (`.content`, `.thinking`, `.tool_calls`, `.finish_reason`):
254
+ `complete` streams `CompletionChunk`s (`.content`, `.thinking`, `.tool_calls`, `.finish_reason`).
255
+ `tools` are Python functions or tool specs; a function goes to the model as its spec,
256
+ and each `ToolCall` comes back as data (`.id`, `.name`, `.arguments`) for the caller
257
+ to run:
254
258
 
255
259
  ```python
256
- imp = mg.HuggingFaceImporter("Qwen/Qwen2.5-7B-Instruct", mg.Text2Text)
257
- model = imp.client(deployment.url)
260
+ model: mg.CompletingModel = deployment.client()
258
261
  messages = [{"role": "user", "content": "Explain RAG in one sentence."}]
259
262
 
260
263
  async for chunk in mg.complete(model, messages, max_new_tokens=512, temperature=0.7):
@@ -295,9 +298,9 @@ a re-deploy rather than a re-registration.
295
298
 
296
299
  A replica asks for no hardware at all, so it is placed on any node, CPU-only
297
300
  included. From there it deploys and streams exactly like a cluster-served model:
298
- `entry.client(deployment.url)` returns the same `ServedCompletingModel`. Anthropic
301
+ `deployment.client()` is a `CompletingModel` like `Text2Text`'s. Anthropic
299
302
  reports tool calls as structured blocks rather than as generated text, so the
300
- serve app re-encodes them into the text form the client parses.
303
+ serve app turns them into `ToolCall`s as they come.
301
304
 
302
305
  ### Gemini
303
306
 
@@ -318,17 +321,16 @@ cortexgrid.register_model(
318
321
 
319
322
  Everything said above for Anthropic holds: `config` carries the model name and
320
323
  the name of the secret (`api_key_secret="..."` to use another), both editable on
321
- the model card; a replica needs no hardware; and `entry.client(deployment.url)`
322
- is a `ServedCompletingModel`, with Gemini's function calls re-encoded into the
323
- text form the client parses.
324
+ the model card; a replica needs no hardware; and `deployment.client()`
325
+ is a `CompletingModel`, with Gemini's function calls turned into `ToolCall`s.
324
326
 
325
- `GeminiText2Image` answers the same `/generate` route as `Text2Image`, so its
326
- client is a `ServedGeneratingModel`:
327
+ `GeminiText2Image` is a `GeneratingModel` like `Text2Image`:
327
328
 
328
329
  ```python
329
330
  entry = mg.Hosted("gemini-2.5-flash-image", mg.GeminiText2Image)
330
331
  ...
331
- picture = await mg.generate(entry.client(deployment.url), "a fox in the snow", size=2048)
332
+ model: mg.GeneratingModel = deployment.client()
333
+ picture = await mg.generate(model, "a fox in the snow", size=2048)
332
334
  ```
333
335
 
334
336
  It takes a prompt only, and makes a square picture. `size` picks Gemini's size
@@ -340,40 +342,37 @@ diffusion settings with no Gemini counterpart, and come back as `None`.
340
342
  | | |
341
343
  |---|---|
342
344
  | `HuggingFaceImporter(hf_id, serve_app, token=None, ignore_patterns=None)` | Importer for a model on the HuggingFace Hub, run by `serve_app`. |
343
- | `Importer` | Base of the importers: identity, scratch directory, `source`, and `requirements()` / `client(url)` taken from the serve app. A new source implements `download(local_dir)` and `weights()`. |
345
+ | `Importer` | Base of the importers: identity, scratch directory, `source`, and `requirements()` taken from the serve app. A new source implements `download(local_dir)` and `weights()`. |
344
346
  | `Hosted(model_id, serve_app, **settings)` | Entry for a model hosted elsewhere; no weights. `settings` are the serve app's `config` arguments. |
345
- | `ModelEntry` | Base of both: `model_id`, `family`, `suffix`, `serve_app`, `requirements()`, `config()`, `client(url)`. |
347
+ | `ModelEntry` | Base of both: `model_id`, `family`, `suffix`, `serve_app`, `requirements()`, `config()`. |
346
348
  | `Text2Text`, `Text2Image`, `TextRewriter`, `Image2Mesh` | Serve apps that run weights (`LocalModel`s). |
347
349
  | `AnthropicText2Text` | Serve app that forwards to the Anthropic API (a `HostedModel`). |
348
350
  | `GeminiText2Text`, `GeminiText2Image` | Serve apps that forward to the Gemini API (`HostedModel`s sharing the `GeminiModel` base). |
349
- | `LocalModel` | Base of the serve apps that run weights: `ignore_patterns()`, `bytes_per_param`, `min_vram_gb`, `requirements(weights)`, `config()` (the model card's defaults, `compile: "false"` among them), `client(url, name)`. |
350
- | `HostedModel` | Base of the serve apps that forward: `config(model_id, **settings)`, `requirements()`, `client(url, name)`. |
351
+ | `LocalModel` | Base of the serve apps that run weights: `ignore_patterns()`, `bytes_per_param`, `min_vram_gb`, `requirements(weights)`, `config()` (the model card's defaults, `compile: "false"` among them). |
352
+ | `HostedModel` | Base of the serve apps that forward: `config(model_id, **settings)`, `requirements()`. |
351
353
  | `Weights(params=None, file_bytes=0)` | What an importer reads about the weights, handed to the serve app to size a replica. |
352
- | `complete(model, messages, tools=None, max_new_tokens=2048, temperature=0.7, **kw)` | Async stream of `CompletionChunk` for a `CompletingModel`. |
353
- | `generate(model, prompt, *, image=None, **kw) -> GeneratedImage` | One image from a `GeneratingModel`. |
354
- | `ServedCompletingModel(url, model_id)` | Client for any deployed completion app. |
355
- | `ServedGeneratingModel(url, model_id)` | Client for any deployed text-to-image app. |
356
- | `ServedMeshingModel(url, model_id)` | Client for any deployed image-to-mesh app. |
354
+ | `complete(model, messages, tools=None, max_new_tokens=None, temperature=0.7)` | Async stream of `CompletionChunk` for a `CompletingModel`. |
355
+ | `generate(model, prompt, *, image=None, steps=None, guidance=None, size=1024, seed=None) -> GeneratedImage` | One image from a `GeneratingModel`. |
357
356
  | `split_model_id(model_id) -> (family, suffix)` | The registry identity an importer derives. |
358
357
  | `detect_device()` | The torch device a serve app should load onto. |
359
358
  | `compiling.Mode.GRAPHED` / `compiling.Mode.FUSED` | The two modes a serve app chooses between. See [Compilation](#compilation). |
360
359
  | `compiling.supported(device)` / `compile_module(module, device, mode)` / `compile_pipeline(pipe, device, mode)` | Whether to compile here, and what a serve app calls to do it. |
361
360
  | `ModelDeployFailed` | Re-exported from cortexgrid; subclasses `RuntimeError`. |
362
361
 
363
- Types: `DeployedModel`, `CompletingModel`, `GeneratingModel`, `MeshingModel`,
362
+ Types: `CompletingModel`, `GeneratingModel`, `MeshingModel`, `RewritingModel` (each
363
+ the abstract base of its task's serve apps, declaring the task's endpoints),
364
364
  `CompletionChunk`, `GeneratedImage`, `GeneratedMesh`, `ToolCall`, `Message`, `Tool`,
365
- `ToolSpec`.
365
+ `ToolSpec`, `Tensor`.
366
366
 
367
367
  Extending it:
368
368
 
369
369
  - **A new source** is one importer: subclass `Importer` with `download(local_dir)`
370
370
  and `weights()`, and every serve app whose weight format it delivers runs its
371
371
  models unchanged.
372
- - **A new hosted API** is one serve app: subclass `HostedModel`, and register it
373
- with `Hosted`. A text-to-text one only has to stream text from `POST /complete`,
374
- inlining tool calls as `<tool_call>{"name": ..., "arguments": {...}}</tool_call>`
375
- (there is an `encode_tool_call` for upstreams that report them structurally) —
376
- then `ServedCompletingModel` is its client, unchanged.
372
+ - **A new hosted API** is one serve app: subclass `HostedModel` and the task's type,
373
+ and register it with `Hosted`. A text-to-text one subclasses `CompletingModel` and
374
+ streams `CompletionChunk`s from its `complete` endpoint, with
375
+ `StreamedCompletionWriter` turning the upstream's text and tool calls into them.
377
376
  - **A model no task's serve app can load** gets its own serve app — an `Image2Mesh`
378
377
  subclass, say, with `load` and `make_mesh`, and `compile` if compiling it pays —
379
378
  and, if its source is unusual too, its own importer.
@@ -7,9 +7,9 @@ nothing else, split along the two things a model is:
7
7
 
8
8
  - where its weights come from - an importer per source (`importers`), which
9
9
  names, fetches and sizes them;
10
- - what runs it - a serve app per task (`serve_apps`), which loads the weights,
11
- answers the task's routes (`protocols`), and names the client that speaks
12
- them.
10
+ - what runs it - a serve app per task (`serve_apps`), which loads the weights
11
+ and serves the task's endpoints, declared by the task's type in `core`
12
+ (`CompletingModel`, say); cortexgrid generates the client that calls them.
13
13
 
14
14
  imp = cortexgrid_infer.HuggingFaceImporter("Qwen/Qwen2.5-0.5B-Instruct", cortexgrid_infer.Text2Text)
15
15
  with imp:
@@ -18,8 +18,8 @@ nothing else, split along the two things a model is:
18
18
  family=imp.family, suffix=imp.suffix,
19
19
  requirements=imp.requirements(),
20
20
  )
21
- deployment = cortexgrid.deploy_model(imp.family, imp.suffix, cortexgrid.IMPORTED, wait=True)
22
- model = imp.client(deployment.url)
21
+ deployment = cortexgrid.deploy_model(imp.family, imp.suffix, cortexgrid.IMPORTED)
22
+ model: cortexgrid_infer.CompletingModel = deployment.client()
23
23
 
24
24
  A model hosted elsewhere has no weights and so no importer: its serve app
25
25
  forwards to it, and a `Hosted` entry registers it.
@@ -31,7 +31,6 @@ from cortexgrid import ModelDeployFailed
31
31
 
32
32
  from cortexgrid_infer.core import (
33
33
  CompletionChunk,
34
- DeployedModel,
35
34
  CompletingModel,
36
35
  GeneratedImage,
37
36
  GeneratedMesh,
@@ -39,6 +38,7 @@ from cortexgrid_infer.core import (
39
38
  MeshingModel,
40
39
  Message,
41
40
  RewritingModel,
41
+ Tensor,
42
42
  Tool,
43
43
  ToolCall,
44
44
  ToolSpec,
@@ -47,12 +47,6 @@ from cortexgrid_infer.core import (
47
47
  mesh,
48
48
  rewrite,
49
49
  )
50
- from cortexgrid_infer.protocols import (
51
- ServedCompletingModel,
52
- ServedGeneratingModel,
53
- ServedMeshingModel,
54
- ServedRewritingModel,
55
- )
56
50
  from cortexgrid_infer.device import detect_device
57
51
  from cortexgrid_infer.serve_apps import (
58
52
  AnthropicText2Text,
@@ -71,7 +65,6 @@ from cortexgrid_infer.importers import HuggingFaceImporter, Importer
71
65
 
72
66
  __all__ = [
73
67
  "CompletionChunk",
74
- "DeployedModel",
75
68
  "CompletingModel",
76
69
  "GeneratedImage",
77
70
  "GeneratedMesh",
@@ -79,6 +72,7 @@ __all__ = [
79
72
  "MeshingModel",
80
73
  "RewritingModel",
81
74
  "Message",
75
+ "Tensor",
82
76
  "Tool",
83
77
  "ToolCall",
84
78
  "ToolSpec",
@@ -88,10 +82,6 @@ __all__ = [
88
82
  "rewrite",
89
83
  "ModelDeployFailed",
90
84
  "detect_device",
91
- "ServedCompletingModel",
92
- "ServedGeneratingModel",
93
- "ServedMeshingModel",
94
- "ServedRewritingModel",
95
85
  "LocalModel",
96
86
  "HostedModel",
97
87
  "Weights",