cortexgrid-infer 0.2.2__tar.gz → 0.2.4__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 (21) hide show
  1. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/.gitignore +2 -1
  2. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/PKG-INFO +13 -7
  3. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/README.md +12 -6
  4. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/providers/huggingface_complete_serve.py +33 -7
  5. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/pyproject.toml +1 -1
  6. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/LICENSE +0 -0
  7. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/__init__.py +0 -0
  8. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/compiling.py +0 -0
  9. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/completion.py +0 -0
  10. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/core.py +0 -0
  11. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/device.py +0 -0
  12. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/importing.py +0 -0
  13. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/providers/__init__.py +0 -0
  14. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/providers/anthropic.py +0 -0
  15. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/providers/anthropic_serve.py +0 -0
  16. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/providers/huggingface_complete.py +0 -0
  17. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/providers/huggingface_image.py +0 -0
  18. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/providers/huggingface_image_serve.py +0 -0
  19. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/py.typed +0 -0
  20. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/requirements.py +0 -0
  21. {cortexgrid_infer-0.2.2 → cortexgrid_infer-0.2.4}/cortexgrid_infer/utils.py +0 -0
@@ -9,4 +9,5 @@ build/
9
9
  venv/
10
10
  .env
11
11
  .claude
12
- .mypy_cache/
12
+ .mypy_cache/
13
+ uv.lock
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cortexgrid-infer
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Deploy and interact with models across providers (Anthropic, HuggingFace LLM + diffusion)
5
5
  Project-URL: Homepage, https://github.com/robodatalab/cortexgrid-infer
6
6
  Project-URL: Repository, https://github.com/robodatalab/cortexgrid-infer
@@ -178,12 +178,18 @@ Both serve apps are tuned rather than defaulted:
178
178
  whole loop. The VAE is left alone: smallest win of the three, and the one whose
179
179
  shapes move most, especially with tiling on.
180
180
  - **`HuggingFaceCompletingDeployment`** does *not* compile the module. Decode has
181
- no stable shape until the KV cache is static, so it sets
182
- `cache_implementation="static"` and a `CompileConfig(mode=Mode.GRAPHED)`, and
183
- transformers compiles `generate` itself — capturing prefill and decode
184
- separately, which it is better placed to do. Compiling the module here as well
185
- would only compile the same forward twice. The cost is VRAM: a static cache is
186
- preallocated to the length asked for, where a dynamic one grows into it.
181
+ no stable shape of its own — the sequence grows a token per forward — so it
182
+ pins one: `cache_implementation="static"` with a fixed `max_cache_len`, plus a
183
+ `CompileConfig(mode=Mode.GRAPHED)`. transformers then compiles `generate`
184
+ itself, capturing prefill and decode separately, which it is better placed to
185
+ do; compiling the module here as well would compile the same forward twice.
186
+
187
+ The fixed length is the point, and it comes from the model: the cache is
188
+ pinned to `config.max_position_embeddings`. A prompt longer than that is
189
+ truncated to it — keeping the end, since a chat template puts the turn to
190
+ answer last — with a warning naming both lengths. Serving it instead would
191
+ resize the cache and recompile the decode loop, which costs more than the
192
+ generation itself.
187
193
 
188
194
  Neither asks to be configured, for the same reason the serve apps do not ask which
189
195
  dtype to load in. Both happen on CUDA and nowhere else — inductor is weakest off
@@ -152,12 +152,18 @@ Both serve apps are tuned rather than defaulted:
152
152
  whole loop. The VAE is left alone: smallest win of the three, and the one whose
153
153
  shapes move most, especially with tiling on.
154
154
  - **`HuggingFaceCompletingDeployment`** does *not* compile the module. Decode has
155
- no stable shape until the KV cache is static, so it sets
156
- `cache_implementation="static"` and a `CompileConfig(mode=Mode.GRAPHED)`, and
157
- transformers compiles `generate` itself — capturing prefill and decode
158
- separately, which it is better placed to do. Compiling the module here as well
159
- would only compile the same forward twice. The cost is VRAM: a static cache is
160
- preallocated to the length asked for, where a dynamic one grows into it.
155
+ no stable shape of its own — the sequence grows a token per forward — so it
156
+ pins one: `cache_implementation="static"` with a fixed `max_cache_len`, plus a
157
+ `CompileConfig(mode=Mode.GRAPHED)`. transformers then compiles `generate`
158
+ itself, capturing prefill and decode separately, which it is better placed to
159
+ do; compiling the module here as well would compile the same forward twice.
160
+
161
+ The fixed length is the point, and it comes from the model: the cache is
162
+ pinned to `config.max_position_embeddings`. A prompt longer than that is
163
+ truncated to it — keeping the end, since a chat template puts the turn to
164
+ answer last — with a warning naming both lengths. Serving it instead would
165
+ resize the cache and recompile the decode loop, which costs more than the
166
+ generation itself.
161
167
 
162
168
  Neither asks to be configured, for the same reason the serve apps do not ask which
163
169
  dtype to load in. Both happen on CUDA and nowhere else — inductor is weakest off
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
+ import logging
6
7
  from collections.abc import AsyncIterator
7
8
  from threading import Thread
8
9
  from typing import Any
@@ -23,9 +24,14 @@ from cortexgrid_infer import compiling
23
24
  from cortexgrid_infer.device import detect_device
24
25
 
25
26
 
27
+ log = logging.getLogger(__name__)
28
+
26
29
  _app = FastAPI()
27
30
 
28
31
 
32
+ # Used only by a model whose config states no context length of its own.
33
+ FALLBACK_MAX_INPUT_TOKENS = 4096
34
+
29
35
  _RESERVED_BODY_KEYS = {
30
36
  "messages",
31
37
  "tools",
@@ -49,14 +55,32 @@ class HuggingFaceCompletingDeployment:
49
55
  str(path), torch_dtype=torch.float16
50
56
  )
51
57
  self._model.to(self._device) # type: ignore
52
- # Decode has no stable shape to capture until the cache is static; given one,
53
- # transformers compiles `generate` itself, so compiling the module would double up.
58
+ self._max_input_tokens = getattr(
59
+ self._model.config, "max_position_embeddings", FALLBACK_MAX_INPUT_TOKENS
60
+ )
61
+ # A static cache of fixed length is what makes decode compilable: it
62
+ # pins the shape every forward sees, so the loop is captured once
63
+ # instead of recompiled per token. transformers then compiles `generate`
64
+ # itself, which is why the module is not compiled here as well.
54
65
  self._compiled = compiling.supported(self._device)
55
66
  if self._compiled:
56
- self._model.generation_config.cache_implementation = "static"
57
- self._model.generation_config.compile_config = CompileConfig(
58
- mode=compiling.Mode.GRAPHED
59
- )
67
+ config = self._model.generation_config
68
+ config.cache_implementation = "static"
69
+ config.max_cache_len = self._max_input_tokens
70
+ config.compile_config = CompileConfig(mode=compiling.Mode.GRAPHED)
71
+
72
+ def _truncate(self, inputs: Any) -> Any:
73
+ """Cut an over-long prompt to what the model handles, keeping its end."""
74
+ length = inputs["input_ids"].shape[-1]
75
+ if length <= self._max_input_tokens:
76
+ return inputs
77
+ log.warning(
78
+ "truncating prompt from %d to %d tokens, the model's context",
79
+ length, self._max_input_tokens,
80
+ )
81
+ for key, value in inputs.items():
82
+ inputs[key] = value[..., -self._max_input_tokens :]
83
+ return inputs
60
84
 
61
85
  @_app.post("/complete")
62
86
  async def complete(self, body: dict[str, Any]) -> StreamingResponse:
@@ -75,7 +99,9 @@ class HuggingFaceCompletingDeployment:
75
99
  tokenize=False,
76
100
  add_generation_prompt=True,
77
101
  )
78
- inputs = self._tokenizer(prompt, return_tensors="pt").to(self._model.device)
102
+ inputs = self._truncate(self._tokenizer(prompt, return_tensors="pt")).to(
103
+ self._model.device
104
+ )
79
105
 
80
106
  queue: asyncio.Queue[str | None] = asyncio.Queue()
81
107
  error: list[BaseException] = []
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cortexgrid-infer"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  description = "Deploy and interact with models across providers (Anthropic, HuggingFace LLM + diffusion)"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"