snowllm 0.1.0__py3-none-any.whl

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 (49) hide show
  1. snowllm/__init__.py +8 -0
  2. snowllm/_capi.py +246 -0
  3. snowllm/_version.py +4 -0
  4. snowllm/async_engine.py +170 -0
  5. snowllm/block_manager.py +68 -0
  6. snowllm/cli.py +207 -0
  7. snowllm/engine.py +353 -0
  8. snowllm/forward_context.py +103 -0
  9. snowllm/generation.py +233 -0
  10. snowllm/geometry.py +140 -0
  11. snowllm/layers.py +160 -0
  12. snowllm/loader.py +138 -0
  13. snowllm/model.py +347 -0
  14. snowllm/models/__init__.py +24 -0
  15. snowllm/models/qwen3_5/__init__.py +16 -0
  16. snowllm/models/qwen3_5/qwen3_5.py +89 -0
  17. snowllm/models/qwen3_5/qwen3_5_mtp.py +41 -0
  18. snowllm/models/qwen3_5/qwen3_5_weights.py +229 -0
  19. snowllm/multimodal.py +59 -0
  20. snowllm/ops/__init__.py +113 -0
  21. snowllm/ops/_common.py +79 -0
  22. snowllm/ops/attention_full.py +229 -0
  23. snowllm/ops/attention_linear.py +74 -0
  24. snowllm/ops/cache.py +49 -0
  25. snowllm/ops/embedding.py +34 -0
  26. snowllm/ops/gemm.py +25 -0
  27. snowllm/ops/moe.py +106 -0
  28. snowllm/ops/mtp.py +37 -0
  29. snowllm/ops/norm.py +48 -0
  30. snowllm/ops/rope.py +22 -0
  31. snowllm/ops/sampling.py +40 -0
  32. snowllm/ops/vision.py +63 -0
  33. snowllm/protocol.py +52 -0
  34. snowllm/reader.py +167 -0
  35. snowllm/request.py +73 -0
  36. snowllm/responses.py +305 -0
  37. snowllm/sampler.py +86 -0
  38. snowllm/server.py +197 -0
  39. snowllm/spec_decode.py +162 -0
  40. snowllm/state.py +58 -0
  41. snowllm/tool_parser.py +103 -0
  42. snowllm/trace.py +24 -0
  43. snowllm/vision.py +315 -0
  44. snowllm-0.1.0.dist-info/METADATA +123 -0
  45. snowllm-0.1.0.dist-info/RECORD +49 -0
  46. snowllm-0.1.0.dist-info/WHEEL +5 -0
  47. snowllm-0.1.0.dist-info/entry_points.txt +2 -0
  48. snowllm-0.1.0.dist-info/licenses/LICENSE +202 -0
  49. snowllm-0.1.0.dist-info/top_level.txt +1 -0
snowllm/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the SnowLLM project
3
+
4
+ from ._capi import ABI_VERSION, SnowLLMError, synchronize
5
+ from .geometry import ModelGeometry
6
+ from ._version import __version__
7
+
8
+ __all__ = ["ABI_VERSION", "ModelGeometry", "SnowLLMError", "__version__", "synchronize"]
snowllm/_capi.py ADDED
@@ -0,0 +1,246 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the SnowLLM project
3
+
4
+ import ctypes
5
+ import importlib.util
6
+ import os
7
+ from ctypes import POINTER, c_char_p, c_float, c_int, c_int64, c_void_p
8
+
9
+ __all__ = ["SnowLLMError", "build_geometry", "lib", "synchronize",
10
+ "assert_single_hip_runtime"]
11
+
12
+
13
+ class SnowLLMError(RuntimeError):
14
+ pass
15
+
16
+
17
+ class BuildGeometry(ctypes.Structure):
18
+ _fields_ = [
19
+ (n, c_int64)
20
+ for n in (
21
+ "hidden", "num_heads", "num_kv_heads", "head_size", "block_size", "vocab_size",
22
+ "qkv_proj_n", "qkv_q_head_stride", "qkv_off_scale", "qkv_off_k", "qkv_off_v",
23
+ "lin_num_k_heads", "lin_num_v_heads", "lin_head_k", "lin_head_v", "lin_key_dim",
24
+ "lin_value_dim", "lin_conv_dim", "lin_conv_k", "lin_conv_state", "lin_in_proj_n",
25
+ "lin_in_proj_n_pad",
26
+ "lin_in_proj_ba_n_pad", "lin_off_qkv", "lin_off_z", "lin_off_b", "lin_off_a",
27
+ "moe_num_experts", "moe_shared_expert", "moe_num_slabs", "moe_topk", "moe_topk_all",
28
+ "moe_inter", "sampling_max_k", "lm_head_decode_max_m",
29
+ )
30
+ ]
31
+
32
+
33
+ def build_geometry() -> "BuildGeometry":
34
+ g = BuildGeometry()
35
+ lib.snowllm_model_config(ctypes.byref(g))
36
+ return g
37
+
38
+
39
+ def _find_lib() -> str:
40
+ if env := os.environ.get("SNOWLLM_LIB"):
41
+ return env
42
+ try:
43
+ import snowllm_kernels
44
+ except ModuleNotFoundError as e:
45
+ if e.name != "snowllm_kernels":
46
+ raise
47
+ raise SnowLLMError(
48
+ "the snowllm-kernels package is not installed, so there are no kernels to call. "
49
+ "`pip install snowllm-kernels` (it carries libsnowllm.so and needs no compiler), or "
50
+ "point SNOWLLM_LIB at a libsnowllm.so you built yourself."
51
+ ) from e
52
+ lib = str(snowllm_kernels.LIB)
53
+ if not os.path.exists(lib):
54
+ raise SnowLLMError(
55
+ f"snowllm-kernels resolved to {os.path.dirname(snowllm_kernels.__file__)} but there is "
56
+ f"no libsnowllm.so at {lib}. If that is a source checkout, build it "
57
+ f"(`cmake --build build-release`); if it is an install, reinstall the wheel."
58
+ )
59
+ return lib
60
+
61
+
62
+ def _preload_hip_runtime() -> None:
63
+ spec = importlib.util.find_spec("_rocm_sdk_core")
64
+ if spec is None or not spec.submodule_search_locations:
65
+ return
66
+ sdk = os.path.join(spec.submodule_search_locations[0], "lib", "libamdhip64.so.7")
67
+ if os.path.exists(sdk):
68
+ try:
69
+ ctypes.CDLL(sdk, mode=ctypes.RTLD_GLOBAL)
70
+ except OSError:
71
+ pass
72
+
73
+
74
+ _preload_hip_runtime()
75
+ _lib_path = _find_lib()
76
+ lib = ctypes.CDLL(_lib_path)
77
+
78
+ ABI_VERSION = 7
79
+
80
+
81
+ def _check_abi() -> None:
82
+ try:
83
+ lib.snowllm_abi_version.restype = c_int
84
+ got = lib.snowllm_abi_version()
85
+ except AttributeError:
86
+ got = 0
87
+ if got != ABI_VERSION:
88
+ raise SnowLLMError(
89
+ f"ABI mismatch: this snowllm expects {ABI_VERSION}, "
90
+ f"{_lib_path} provides {got or 'no version at all'}. Rebuild the one that is stale."
91
+ )
92
+
93
+
94
+ _check_abi()
95
+
96
+ P, S = c_void_p, c_void_p
97
+ _SIGS = [
98
+ ("snowllm_model_config", None, [POINTER(BuildGeometry)]),
99
+ ("snowllm_shuffle_bytes", c_int64, [c_int64]),
100
+ ("snowllm_prefill_row_quantum", c_int64, []),
101
+ ("snowllm_sampling_max_k", c_int64, []),
102
+ ("snowllm_error_string", c_char_p, [c_int]),
103
+ ("snowllm_synchronize", c_int, [S]),
104
+
105
+ ("snowllm_qkv_proj_shuffle_w", c_int, [P, P, S]),
106
+ ("snowllm_qkv_proj_scratch_bytes", c_int64, [c_int64]),
107
+ ("snowllm_qkv_proj_shuffle_w_fp8", c_int, [P, P, S]),
108
+ ("snowllm_attn_out_scale_oproj_shuffle_w", c_int, [P, P, S]),
109
+ ("snowllm_attn_out_scale_oproj_scratch_bytes", c_int64, [c_int64]),
110
+ ("snowllm_attn_out_scale_oproj_shuffle_w_fp8", c_int, [P, P, S]),
111
+ ("snowllm_linear_in_proj_shuffle_w", c_int, [P, P, S]),
112
+ ("snowllm_linear_out_proj_shuffle_w", c_int, [P, P, S]),
113
+ ("snowllm_linear_out_proj_shuffle_w_fp8", c_int, [P, P, S]),
114
+ ("snowllm_linear_in_proj_qz_shuffle_w_fp8", c_int, [P, P, S]),
115
+ ("snowllm_linear_in_proj_ba_shuffle_w", c_int, [P, P, S]),
116
+ ("snowllm_fused_linear_attn_workspace_bytes", c_int64, [c_int64]),
117
+ ("snowllm_mtp_fc_shuffle_w", c_int, [P, P, S]),
118
+ ("snowllm_mtp_fc_scratch_bytes", c_int64, [c_int64]),
119
+ ("snowllm_mtp_pre_fc", c_int, [P, P, P, P, P, c_int64, c_float, S]),
120
+ ("snowllm_mtp_fc", c_int, [P, P, P, P, c_int64, c_int, S]),
121
+ ("snowllm_moe_shuffle_gate_up", c_int, [P, P, P, c_int64, S]),
122
+ ("snowllm_moe_shuffle_down", c_int, [P, P, c_int64, S]),
123
+ ("snowllm_moe_shuffle_gate_up_fused", c_int, [P, P, c_int64, S]),
124
+ ("snowllm_proj_scale_shuffle_fp8", c_int, [P, P, c_int64, c_int64, S]),
125
+ ("snowllm_moe_scale_shuffle_gate_up_fp8", c_int, [P, P, P, c_int64, S]),
126
+ ("snowllm_moe_scale_shuffle_down_fp8", c_int, [P, P, c_int64, S]),
127
+ ("snowllm_moe_shuffle_gate_up_fp8", c_int, [P, P, P, c_int64, S]),
128
+ ("snowllm_moe_shuffle_down_fp8", c_int, [P, P, c_int64, S]),
129
+ ("snowllm_moe_shuffle_router_bytes", c_int64, []),
130
+ ("snowllm_moe_shuffle_router", c_int, [P, P, S]),
131
+ ("snowllm_moe_workspace_bytes", c_int64, [c_int64]),
132
+ ("snowllm_lm_head_shuffle_weight", c_int, [P, P, S]),
133
+ ("snowllm_lm_head_rows_for", c_int64, [c_int64]),
134
+ ("snowllm_lm_head_pad_bytes", c_int64, [c_int64]),
135
+ ("snowllm_lm_head_scratch_bytes", c_int64, [c_int64]),
136
+ ("snowllm_lm_head", c_int, [P, P, P, c_int64, P, P, S]),
137
+ ("snowllm_paged_prefill_max_tokens", c_int64, []),
138
+ ("snowllm_paged_decode_workspace_size", c_int64, [c_int64, c_int64]),
139
+ ("snowllm_kv_pool_bytes", None, [c_int64, c_int, P, P]),
140
+ ("snowllm_kv_scale_bytes", None, [c_int64, P, P]),
141
+ ("snowllm_resolve_slots", c_int, [P, c_int64, P, P, P, c_int64, S]),
142
+ ("snowllm_kv_blocks_for", c_int64, [c_int64]),
143
+ ("snowllm_linear_state_slots", None, [c_int64, c_int64, c_int64, c_int64, P]),
144
+ ("snowllm_prefill_q_blocks", c_int64, [P, c_int64]),
145
+ ("snowllm_prefill_q_block_map", None, [P, c_int64, P]),
146
+ ("snowllm_moe_variant_force", None, [c_int]),
147
+ ("snowllm_moe_variant_name", c_char_p, [c_int]),
148
+ ("snowllm_paged_window_blocks", c_int64, [c_int64, c_int64]),
149
+ ("snowllm_paged_window_view", c_int, [P, c_int64, P, c_int64, c_int64, c_int64, P,
150
+ c_int64, P, S]),
151
+ ("snowllm_paged_decode_max_q_tokens", c_int64, []),
152
+ ("snowllm_paged_decode_num_slots", c_int64, [c_int64]),
153
+ ("snowllm_paged_decode_plan_elems", c_int64, [c_int64, c_int64]),
154
+
155
+ ("snowllm_gather_embedding", c_int, [P, P, P, c_int64, S]),
156
+ ("snowllm_rmsnorm", c_int, [P, P, P, c_int64, c_float, S]),
157
+ ("snowllm_rmsnorm_residual", c_int, [P, P, P, P, c_int64, c_float, S]),
158
+ ("snowllm_rmsnorm_shuffled", c_int, [P, P, P, c_int64, c_float, S]),
159
+ ("snowllm_rmsnorm_residual_shuffled", c_int, [P, P, P, P, c_int64, c_float, S]),
160
+ ("snowllm_qkv_proj", c_int, [P, P, P, P, c_int64, c_int, S]),
161
+ ("snowllm_qkv_proj_fp8", c_int, [P, P, P, P, P, c_int64, c_int, S]),
162
+ ("snowllm_qk_norm", c_int, [P, c_int64, P, P, P, P, c_int64, c_float, S]),
163
+ ("snowllm_qk_norm_rope", c_int, [P, c_int64, P, P, P, P, P, P, c_int64, c_float, S]),
164
+ ("snowllm_attn_out_scale_oproj", c_int, [P, P, c_int64, P, P, P, c_int64, c_int, S]),
165
+ ("snowllm_attn_out_scale_oproj_fp8", c_int, [P, P, c_int64, P, P, P, P, c_int64, c_int, S]),
166
+ ("snowllm_rope_cos_sin", c_int, [P, P, P, P, c_int64, S]),
167
+ ("snowllm_rope_cos_sin_mrope", c_int, [P, P, P, P, c_int64, S]),
168
+ ("snowllm_rope_apply_q", c_int, [P, P, P, c_int64, S]),
169
+ ("snowllm_rope_apply_k", c_int, [P, P, P, c_int64, S]),
170
+ ("snowllm_reshape_and_cache", c_int, [P, P, c_int64, c_int64, P, P, P, c_int64, S]),
171
+ ("snowllm_reshape_and_cache_int8", c_int,
172
+ [P, P, c_int64, c_int64, P, P, P, P, P, c_int64, S]),
173
+ ("snowllm_paged_attn_prefill_int8", c_int,
174
+ [P, P, P, P, P, P, P, P, P, c_int64, c_int64, c_int64, c_float, S, P]),
175
+ ("snowllm_paged_attn_decode_int8", c_int,
176
+ [P, P, P, P, P, P, P, P, P, c_int64, c_int64, c_int64, P, c_float, c_int64, S]),
177
+ ("snowllm_paged_attn_prefill", c_int,
178
+ [P, P, P, P, P, P, P, c_int64, c_int64, c_int64, c_float, S, P]),
179
+ ("snowllm_paged_attn_decode", c_int,
180
+ [P, P, P, P, P, P, P, c_int64, c_int64, c_int64, P, c_float, c_int64, S]),
181
+ ("snowllm_paged_attn_decode_plan", c_int, [P, P, c_int64, c_int64, c_int, S]),
182
+ ("snowllm_fused_linear_attn", c_int,
183
+ [P, P, P, P, P, P, P,
184
+ P, P, P, P,
185
+ P, P, P, P,
186
+ P, P, P, P,
187
+ c_int64, c_int64, c_int, S]),
188
+ ("snowllm_fused_moe", c_int, [P, P, P, P, P, P, c_int64, S]),
189
+ ("snowllm_fused_moe_fp8", c_int, [P, P, P, P, P, P, P, P, c_int64, S]),
190
+ ("snowllm_lm_head_gemm_decode", c_int, [P, P, P, c_int64, S]),
191
+ ("snowllm_lm_head_gemm_prefill", c_int, [P, P, P, P, c_int64, S]),
192
+ ("snowllm_sampling_softmax", c_int, [P, P, c_int64, c_int64, P, S]),
193
+ ("snowllm_sampling_topk_topp", c_int, [P, P, P, c_int64, c_int64, c_int64, P, P, S]),
194
+ ("snowllm_sampling_multinomial", c_int, [P, P, P, P, c_int64, c_int64, S]),
195
+ ("snowllm_sampling_argmax", c_int, [P, P, c_int64, c_int64, S]),
196
+
197
+ ("snowllm_gemm_bf16_shuffle_b", c_int, [P, P, c_int64, c_int64, S]),
198
+ ("snowllm_vision_layernorm", c_int, [P, P, P, P, c_int64, c_int64, c_float, S]),
199
+ ("snowllm_vision_gemm_scratch_bytes", c_int64, [c_int64, c_int64]),
200
+ ("snowllm_vision_gemm_bias_act", c_int,
201
+ [P, P, P, P, P, P, c_int64, c_int64, c_int64, c_int, S]),
202
+ ("snowllm_vision_rope_qk", c_int, [P, P, P, c_int64, c_int64, c_int64, c_int64, S]),
203
+ ("snowllm_vision_attn", c_int, [P, P, P, P, c_int64, c_int64, c_int64, c_int64, S]),
204
+ ("snowllm_vision_supported_head_dims", c_int64, [P, c_int64]),
205
+ ("snowllm_vision_supported_ln_hidden", c_int64, [P, c_int64]),
206
+
207
+ ("snowllm_file_reader_create", P, [c_int, c_int64, c_int]),
208
+ ("snowllm_file_reader_destroy", None, [P]),
209
+ ("snowllm_file_reader_read", c_int, [P, c_char_p, c_int64, c_int64, P, S]),
210
+ ]
211
+ _missing = []
212
+ for _name, _res, _args in _SIGS:
213
+ try:
214
+ _fn = getattr(lib, _name)
215
+ except AttributeError:
216
+ _missing.append(_name)
217
+ continue
218
+ _fn.restype = _res
219
+ _fn.argtypes = _args
220
+ if _missing:
221
+ raise SnowLLMError(f"{_lib_path} is missing {len(_missing)} entry points this binding needs:\n "
222
+ + "\n ".join(_missing))
223
+
224
+
225
+ def check(status: int, what: str) -> None:
226
+ if status != 0:
227
+ msg = lib.snowllm_error_string(status).decode()
228
+ raise SnowLLMError(f"{what}: hip status {status}: {msg}")
229
+
230
+
231
+ def synchronize(stream: int = 0) -> None:
232
+ check(lib.snowllm_synchronize(stream), "synchronize")
233
+
234
+
235
+ def assert_single_hip_runtime() -> None:
236
+ paths = set()
237
+ with open("/proc/self/maps") as f:
238
+ for line in f:
239
+ i = line.find("libamdhip64.so")
240
+ if i != -1:
241
+ paths.add(os.path.realpath(line.rsplit(" ", 1)[-1].strip()))
242
+ if len(paths) > 1:
243
+ raise SnowLLMError(
244
+ "two HIP runtimes are loaded in this process -- torch's device pointers are not valid "
245
+ "in snowllm's kernels:\n " + "\n ".join(sorted(paths))
246
+ )
snowllm/_version.py ADDED
@@ -0,0 +1,4 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the SnowLLM project
3
+
4
+ __version__ = "0.1.0"
@@ -0,0 +1,170 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the SnowLLM project
3
+
4
+ import asyncio
5
+ import pathlib
6
+ import time
7
+ import traceback
8
+ from collections.abc import AsyncIterator
9
+ from concurrent.futures import ThreadPoolExecutor
10
+
11
+ from .engine import Engine, Request, SamplingParams
12
+
13
+
14
+ class AsyncEngine:
15
+ def __init__(self, engine: Engine, profile_dir: str | None = None,
16
+ stats_interval: float = 0.0):
17
+ self.engine = engine
18
+ self.stats_interval = stats_interval
19
+ self._last_stats = (0.0, None)
20
+ self.profile_dir = pathlib.Path(profile_dir).expanduser() if profile_dir else None
21
+ if self.profile_dir:
22
+ self.profile_dir.mkdir(parents=True, exist_ok=True)
23
+ self._profiler = None
24
+ self._worker = ThreadPoolExecutor(max_workers=1, thread_name_prefix="snowllm-engine")
25
+ self._live: list[Request] = []
26
+ self._queues: dict[int, asyncio.Queue] = {}
27
+ self._sent: dict[int, int] = {}
28
+ self._wake = asyncio.Event()
29
+ self._task: asyncio.Task | None = None
30
+
31
+ def start(self) -> None:
32
+ self._task = asyncio.create_task(self._run())
33
+
34
+ async def start_profile(self) -> None:
35
+ if self.profile_dir is None:
36
+ raise RuntimeError("profiling is off; start the server with --profile-dir")
37
+ if self._profiler is not None:
38
+ raise RuntimeError("a profile is already running")
39
+ loop = asyncio.get_running_loop()
40
+ await loop.run_in_executor(self._worker, self._profiler_start)
41
+
42
+ async def stop_profile(self) -> str:
43
+ if self._profiler is None:
44
+ raise RuntimeError("no profile is running")
45
+ loop = asyncio.get_running_loop()
46
+ return await loop.run_in_executor(self._worker, self._profiler_stop)
47
+
48
+ def _profiler_start(self) -> None:
49
+ import torch
50
+
51
+ self._profiler = torch.profiler.profile(
52
+ activities=[torch.profiler.ProfilerActivity.CPU,
53
+ torch.profiler.ProfilerActivity.CUDA],
54
+ with_stack=True,
55
+ on_trace_ready=torch.profiler.tensorboard_trace_handler(str(self.profile_dir)),
56
+ )
57
+ self._profiler.start()
58
+
59
+ def _profiler_stop(self) -> str:
60
+ p, self._profiler = self._profiler, None
61
+ p.stop()
62
+ return str(self.profile_dir)
63
+
64
+ async def close(self) -> None:
65
+ if self._task:
66
+ self._task.cancel()
67
+ try:
68
+ await self._task
69
+ except asyncio.CancelledError:
70
+ pass
71
+ self._worker.shutdown(wait=True)
72
+
73
+ def stats(self):
74
+ return self.engine.stats()
75
+
76
+ def submit(self, prompt: list[int], params: SamplingParams, rope_factor: float = 1.0,
77
+ **mm) -> Request:
78
+ r = self.engine.add(prompt, params, rope_factor=rope_factor, **mm)
79
+ self._live.append(r)
80
+ self._queues[id(r)] = asyncio.Queue()
81
+ self._sent[id(r)] = 0
82
+ self._wake.set()
83
+ return r
84
+
85
+ async def stream(self, r: Request) -> AsyncIterator[int]:
86
+ q = self._queues[id(r)]
87
+ try:
88
+ while True:
89
+ tok = await q.get()
90
+ if tok is None:
91
+ return
92
+ yield tok
93
+ finally:
94
+ if not r.done:
95
+ self.abort(r)
96
+ self._queues.pop(id(r), None)
97
+
98
+ def abort(self, r: Request, reason: str = "abort") -> None:
99
+ r.done = True
100
+ r.finish_reason = r.finish_reason or reason
101
+ if r in self.engine.waiting:
102
+ self.engine.waiting.remove(r)
103
+ self._queues[id(r)].put_nowait(None)
104
+ self._close(r)
105
+
106
+ async def _run(self) -> None:
107
+ loop = asyncio.get_running_loop()
108
+ while True:
109
+ if not self.engine.waiting and not self.engine.running:
110
+ await self._wake.wait()
111
+ self._wake.clear()
112
+ continue
113
+ try:
114
+ await loop.run_in_executor(self._worker, self.engine.step)
115
+ except asyncio.CancelledError:
116
+ raise
117
+ except Exception:
118
+ traceback.print_exc()
119
+ self._fail_all()
120
+ continue
121
+ self._dispatch()
122
+ self._log_stats()
123
+
124
+ def _log_stats(self) -> None:
125
+ if not self.stats_interval or self.engine.acct is None:
126
+ return
127
+ now = time.monotonic()
128
+ was_at, was = self._last_stats
129
+ if was is not None and now - was_at < self.stats_interval:
130
+ return
131
+ cur = self.engine.acct.snapshot()
132
+ self._last_stats = (now, cur)
133
+ if was is None:
134
+ return
135
+ dp_t = cur["prefill_tokens"] - was["prefill_tokens"]
136
+ dp_s = cur["prefill_seconds"] - was["prefill_seconds"]
137
+ dd_t = cur["decode_tokens"] - was["decode_tokens"]
138
+ dd_s = cur["decode_seconds"] - was["decode_seconds"]
139
+ dd_r = cur["decode_rows"] - was["decode_rows"]
140
+ st = self.engine.stats()
141
+ kv = 1.0 - st.free_kv_blocks / max(self.engine.blocks.total, 1)
142
+ print(f"[snowllm {time.strftime('%H:%M:%S')}] "
143
+ f"prefill {dp_t / dp_s if dp_s else 0.0:7.1f} tok/s | "
144
+ f"decode {dd_t / dd_s if dd_s else 0.0:7.1f} tok/s | "
145
+ f"AL {dd_t / dd_r if dd_r else 0.0:.3f} | "
146
+ f"running {st.running:3d} waiting {st.waiting:4d} | KV {kv * 100:4.1f}%", flush=True)
147
+
148
+ def _fail_all(self) -> None:
149
+ for r in self._live:
150
+ r.done, r.finish_reason = True, "error"
151
+ self._dispatch()
152
+
153
+ def _dispatch(self) -> None:
154
+ for r in list(self._live):
155
+ q = self._queues.get(id(r))
156
+ if q is None:
157
+ self._close(r)
158
+ continue
159
+ n = self._sent[id(r)]
160
+ for tok in r.out[n:]:
161
+ q.put_nowait(tok)
162
+ self._sent[id(r)] = len(r.out)
163
+ if r.done:
164
+ q.put_nowait(None)
165
+ self._close(r)
166
+
167
+ def _close(self, r: Request) -> None:
168
+ if r in self._live:
169
+ self._live.remove(r)
170
+ self._sent.pop(id(r), None)
@@ -0,0 +1,68 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the SnowLLM project
3
+
4
+ import torch
5
+
6
+ from . import ops
7
+ from .request import Request
8
+
9
+
10
+ def pad_table(rows: list[list[int]]) -> torch.Tensor:
11
+ w = max((len(x) for x in rows), default=1) or 1
12
+ t = torch.zeros(len(rows), w, dtype=torch.int32)
13
+ for i, x in enumerate(rows):
14
+ t[i, : len(x)] = torch.tensor(x, dtype=torch.int32)
15
+ return t.to("cuda")
16
+
17
+
18
+ def slot_mapping(bt: torch.Tensor, rows: list[tuple[int, int]]) -> torch.Tensor:
19
+ seq = torch.tensor([b for b, _ in rows], dtype=torch.int32, device="cuda")
20
+ pos = torch.tensor([p for _, p in rows], dtype=torch.int32, device="cuda")
21
+ return ops.resolve_slots(bt, seq, pos)
22
+
23
+
24
+ def block_tables(batch: list[Request]) -> torch.Tensor:
25
+ return pad_table([r.blocks for r in batch])
26
+
27
+
28
+ class BlockAllocator:
29
+ def __init__(self, num_blocks: int):
30
+ self.free = list(range(num_blocks))
31
+ self.total = num_blocks
32
+
33
+ def alloc(self, n: int) -> list[int] | None:
34
+ if n > len(self.free):
35
+ return None
36
+ return [self.free.pop() for _ in range(n)]
37
+
38
+ def release(self, blocks: list[int]) -> None:
39
+ self.free.extend(blocks)
40
+
41
+ def grow(self, r: Request, n: int = 1) -> bool:
42
+ need = ops.kv_blocks_for(r.num_cached + n)
43
+ while need > len(r.blocks):
44
+ more = self.alloc(1)
45
+ if more is None:
46
+ return False
47
+ r.blocks += more
48
+ return True
49
+
50
+
51
+ class StateSlots:
52
+ def __init__(self, max_num_seqs: int, T: int, dummy: int):
53
+ self.T = T
54
+ self.free = list(range(max_num_seqs))
55
+ self._dummy = dummy
56
+
57
+ def take(self, r: Request) -> None:
58
+ r.slot = self.free.pop()
59
+ r.state_head = ops.linear_state_slots(r.slot, -1, 1, self.T)[0]
60
+
61
+ def give_back(self, r: Request) -> None:
62
+ self.free.append(r.slot)
63
+
64
+ def name(self, r: Request, rows: int) -> list[int]:
65
+ return ops.linear_state_slots(r.slot, r.state_head, rows, self.T)
66
+
67
+ def dummy_indices(self, n: int) -> torch.Tensor:
68
+ return torch.full((n,), self._dummy, dtype=torch.int32, device="cuda")