renderers 0.2.1.dev8__py3-none-any.whl → 0.2.1.dev9__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.
- renderers/_version.py +2 -2
- renderers/base.py +1 -1
- renderers/client.py +159 -47
- {renderers-0.2.1.dev8.dist-info → renderers-0.2.1.dev9.dist-info}/METADATA +1 -1
- {renderers-0.2.1.dev8.dist-info → renderers-0.2.1.dev9.dist-info}/RECORD +7 -7
- {renderers-0.2.1.dev8.dist-info → renderers-0.2.1.dev9.dist-info}/WHEEL +0 -0
- {renderers-0.2.1.dev8.dist-info → renderers-0.2.1.dev9.dist-info}/licenses/LICENSE +0 -0
renderers/_version.py
CHANGED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2, 1, '
|
|
21
|
+
__version__ = version = '0.2.1.dev9'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 1, 'dev9')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
renderers/base.py
CHANGED
|
@@ -2230,7 +2230,7 @@ def build_trajectory_step(
|
|
|
2230
2230
|
"completion_mask": [True] * len(completion_ids),
|
|
2231
2231
|
"completion_logprobs": [0.0] * len(completion_ids),
|
|
2232
2232
|
"routed_experts": None,
|
|
2233
|
-
"
|
|
2233
|
+
"sampler_support": None,
|
|
2234
2234
|
}
|
|
2235
2235
|
if (
|
|
2236
2236
|
full_rendered.multi_modal_data is not None
|
renderers/client.py
CHANGED
|
@@ -169,9 +169,10 @@ def _parse_packed_completion_logprobs(
|
|
|
169
169
|
return values.astype(np.float32, copy=False)
|
|
170
170
|
|
|
171
171
|
|
|
172
|
-
def
|
|
173
|
-
choice: Mapping[str, Any], completion_ids: list[int]
|
|
174
|
-
) -> tuple[np.ndarray, list[
|
|
172
|
+
def _decode_compact_logprobs(
|
|
173
|
+
choice: Mapping[str, Any], completion_ids: list[int]
|
|
174
|
+
) -> tuple[np.ndarray, np.ndarray, list[int], int]:
|
|
175
|
+
"""Flat ids and logprobs of every compact row, the row offsets and the head size."""
|
|
175
176
|
packed = choice.get("compact_logprobs")
|
|
176
177
|
if not isinstance(packed, Mapping) or packed.get("format") != "u32-f32-le-v1":
|
|
177
178
|
raise MalformedGenerateResponseError("Unsupported compact logprob format")
|
|
@@ -198,13 +199,115 @@ def _parse_compact_logprobs(
|
|
|
198
199
|
values = np.frombuffer(raw, dtype="<f4", count=count, offset=count * 4)
|
|
199
200
|
if not np.isfinite(values).all() or (values > 1e-6).any():
|
|
200
201
|
raise MalformedGenerateResponseError("Invalid compact logprob values")
|
|
202
|
+
return ids, values, offsets, num_top
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _sampler_support(
|
|
206
|
+
ids: np.ndarray, counts: np.ndarray, logprobs: np.ndarray | None, replayed: bool
|
|
207
|
+
) -> dict[str, Any]:
|
|
208
|
+
"""The sampler's per-token support in CSR form: completion token ``i`` owns ``counts[i]`` entries of
|
|
209
|
+
the flat ``ids`` (and ``logprobs``, the sampler's logprob of each id). ``replayed`` rows are the exact
|
|
210
|
+
set the token was sampled from; other rows are a head of the sampler's distribution."""
|
|
211
|
+
return {
|
|
212
|
+
"ids": ids.astype(np.int32, copy=False),
|
|
213
|
+
"counts": counts.astype(np.int32, copy=False),
|
|
214
|
+
"logprobs": None
|
|
215
|
+
if logprobs is None
|
|
216
|
+
else logprobs.astype(np.float32, copy=False),
|
|
217
|
+
"replayed": replayed,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _pack_sampler_rows(
|
|
222
|
+
ids: list[list[int]], logprobs: list[list[float]], replayed: bool
|
|
223
|
+
) -> dict[str, Any]:
|
|
224
|
+
return _sampler_support(
|
|
225
|
+
np.fromiter((i for row in ids for i in row), dtype=np.int32),
|
|
226
|
+
np.fromiter((len(row) for row in ids), dtype=np.int32, count=len(ids)),
|
|
227
|
+
np.fromiter((p for row in logprobs for p in row), dtype=np.float32),
|
|
228
|
+
replayed,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _sampled_entries(
|
|
233
|
+
ids: np.ndarray, counts: np.ndarray, completion_ids: list[int]
|
|
234
|
+
) -> np.ndarray:
|
|
235
|
+
"""Which entry of each replayed row is the sampled token. Every row must be non-empty, hold distinct
|
|
236
|
+
ids and contain its sampled token."""
|
|
237
|
+
if (
|
|
238
|
+
len(counts) != len(completion_ids)
|
|
239
|
+
or int(counts.sum()) != len(ids)
|
|
240
|
+
or (counts <= 0).any()
|
|
241
|
+
):
|
|
242
|
+
raise MalformedGenerateResponseError("Sampling mask token count mismatch")
|
|
243
|
+
ids = ids.astype(np.int64)
|
|
244
|
+
rows = np.repeat(np.arange(len(counts)), counts)
|
|
245
|
+
order = np.lexsort((ids, rows))
|
|
246
|
+
sorted_ids, sorted_rows = ids[order], rows[order]
|
|
247
|
+
repeated = (sorted_ids[1:] == sorted_ids[:-1]) & (
|
|
248
|
+
sorted_rows[1:] == sorted_rows[:-1]
|
|
249
|
+
)
|
|
250
|
+
sampled = ids == np.asarray(completion_ids, dtype=np.int64)[rows]
|
|
251
|
+
if repeated.any() or np.count_nonzero(sampled) != len(counts):
|
|
252
|
+
raise MalformedGenerateResponseError("Invalid recorded sampling support")
|
|
253
|
+
return sampled
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _parse_replayed_support(
|
|
257
|
+
choice: Mapping[str, Any], completion_ids: list[int]
|
|
258
|
+
) -> tuple[np.ndarray, dict[str, Any]]:
|
|
259
|
+
"""A replayed request's sampled logprobs and support: the recorded sampling mask with the sampler's
|
|
260
|
+
logprob of every kept id, which must normalize over it."""
|
|
261
|
+
ids, values, offsets, _ = _decode_compact_logprobs(choice, completion_ids)
|
|
262
|
+
mask = choice.get("sampling_mask")
|
|
263
|
+
if not isinstance(mask, Mapping):
|
|
264
|
+
raise MalformedGenerateResponseError("Invalid packed sampling mask")
|
|
265
|
+
mask_ids, counts = _decode_packed_sampling_mask(mask)
|
|
266
|
+
if not np.array_equal(counts, np.diff(offsets)) or not np.array_equal(
|
|
267
|
+
mask_ids, ids
|
|
268
|
+
):
|
|
269
|
+
raise MalformedGenerateResponseError(
|
|
270
|
+
"Sampler logprobs must cover exactly the recorded sampling support"
|
|
271
|
+
)
|
|
272
|
+
if (values == VLLM_LOGPROB_SENTINEL).any():
|
|
273
|
+
raise MalformedGenerateResponseError("Missing sampling-support probabilities")
|
|
274
|
+
sampled = _sampled_entries(mask_ids, counts, completion_ids)
|
|
275
|
+
if len(counts):
|
|
276
|
+
mass = np.add.reduceat(np.exp(values.astype(np.float64)), offsets[:-1])
|
|
277
|
+
if not np.allclose(mass, 1.0, rtol=0, atol=1e-4):
|
|
278
|
+
raise MalformedGenerateResponseError(
|
|
279
|
+
"Sampler probabilities must normalize over the recorded support"
|
|
280
|
+
)
|
|
281
|
+
return values[sampled], _sampler_support(mask_ids, counts, values, replayed=True)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _parse_mask_support(
|
|
285
|
+
choice: Mapping[str, Any], completion_ids: list[int]
|
|
286
|
+
) -> dict[str, Any]:
|
|
287
|
+
"""The recorded sampling mask of a request that returned no sampler logprobs for it."""
|
|
288
|
+
mask = choice.get("sampling_mask")
|
|
289
|
+
if isinstance(mask, Mapping):
|
|
290
|
+
ids, counts = _decode_packed_sampling_mask(mask)
|
|
291
|
+
_sampled_entries(ids, counts, completion_ids)
|
|
292
|
+
return _sampler_support(ids, counts, None, replayed=True)
|
|
293
|
+
rows = _parse_sampling_mask(choice, completion_ids)
|
|
294
|
+
assert rows is not None
|
|
295
|
+
return _sampler_support(
|
|
296
|
+
np.fromiter((i for row in rows for i in row), dtype=np.int32),
|
|
297
|
+
np.fromiter((len(row) for row in rows), dtype=np.int32, count=len(rows)),
|
|
298
|
+
None,
|
|
299
|
+
replayed=True,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _parse_compact_logprobs(
|
|
304
|
+
choice: Mapping[str, Any], completion_ids: list[int], k: int | None
|
|
305
|
+
) -> tuple[np.ndarray, list[list[int]] | None, list[list[float]] | None]:
|
|
306
|
+
ids, values, offsets, num_top = _decode_compact_logprobs(choice, completion_ids)
|
|
201
307
|
sampled = []
|
|
202
308
|
top_ids: list[list[int]] = []
|
|
203
309
|
top_logprobs: list[list[float]] = []
|
|
204
|
-
|
|
205
|
-
for index, (token_id, start, end) in enumerate(
|
|
206
|
-
zip(completion_ids, offsets, offsets[1:])
|
|
207
|
-
):
|
|
310
|
+
for token_id, start, end in zip(completion_ids, offsets, offsets[1:]):
|
|
208
311
|
# FlatLogprobs can repeat the sampled ID. Match vLLM's dict order/value semantics.
|
|
209
312
|
items = list(zip(ids[start:end].tolist(), values[start:end].tolist()))
|
|
210
313
|
row = dict(items)
|
|
@@ -218,11 +321,7 @@ def _parse_compact_logprobs(
|
|
|
218
321
|
if k is not None:
|
|
219
322
|
head = dict(islice(row.items(), max(num_top, 1)))
|
|
220
323
|
selected_ids, selected_logprobs = _select_sampler_head(
|
|
221
|
-
head,
|
|
222
|
-
token_id,
|
|
223
|
-
row[token_id],
|
|
224
|
-
k,
|
|
225
|
-
supports[index] if supports is not None else None,
|
|
324
|
+
head, token_id, row[token_id], k
|
|
226
325
|
)
|
|
227
326
|
top_ids.append(selected_ids)
|
|
228
327
|
top_logprobs.append(selected_logprobs)
|
|
@@ -300,6 +399,28 @@ def _parse_completion_logprobs(
|
|
|
300
399
|
return np.asarray(completion_logprobs, dtype=np.float32)
|
|
301
400
|
|
|
302
401
|
|
|
402
|
+
def _decode_packed_sampling_mask(
|
|
403
|
+
mask: Mapping[str, Any],
|
|
404
|
+
) -> tuple[np.ndarray, np.ndarray]:
|
|
405
|
+
arrays = {}
|
|
406
|
+
for name in ("ids", "counts"):
|
|
407
|
+
packed = mask.get(name)
|
|
408
|
+
if not isinstance(packed, Mapping) or packed.get("dtype") != "int32":
|
|
409
|
+
raise MalformedGenerateResponseError("Invalid packed sampling mask")
|
|
410
|
+
try:
|
|
411
|
+
values = np.frombuffer(
|
|
412
|
+
base64.b64decode(packed["data"], validate=True), dtype=np.int32
|
|
413
|
+
)
|
|
414
|
+
except (KeyError, TypeError, ValueError, binascii.Error) as exc:
|
|
415
|
+
raise MalformedGenerateResponseError(
|
|
416
|
+
"Invalid packed sampling mask"
|
|
417
|
+
) from exc
|
|
418
|
+
if packed.get("shape") != [len(values)] or (values < 0).any():
|
|
419
|
+
raise MalformedGenerateResponseError("Invalid packed sampling mask")
|
|
420
|
+
arrays[name] = values
|
|
421
|
+
return arrays["ids"], arrays["counts"]
|
|
422
|
+
|
|
423
|
+
|
|
303
424
|
def _parse_sampling_mask(
|
|
304
425
|
choice: Mapping[str, Any], completion_ids: list[int]
|
|
305
426
|
) -> list[list[int]] | None:
|
|
@@ -307,23 +428,7 @@ def _parse_sampling_mask(
|
|
|
307
428
|
if mask is None:
|
|
308
429
|
return None
|
|
309
430
|
if isinstance(mask, Mapping):
|
|
310
|
-
|
|
311
|
-
for name in ("ids", "counts"):
|
|
312
|
-
packed = mask.get(name)
|
|
313
|
-
if not isinstance(packed, Mapping) or packed.get("dtype") != "int32":
|
|
314
|
-
raise MalformedGenerateResponseError("Invalid packed sampling mask")
|
|
315
|
-
try:
|
|
316
|
-
values = np.frombuffer(
|
|
317
|
-
base64.b64decode(packed["data"], validate=True), dtype=np.int32
|
|
318
|
-
)
|
|
319
|
-
except (KeyError, TypeError, ValueError, binascii.Error) as exc:
|
|
320
|
-
raise MalformedGenerateResponseError(
|
|
321
|
-
"Invalid packed sampling mask"
|
|
322
|
-
) from exc
|
|
323
|
-
if packed.get("shape") != [len(values)] or (values < 0).any():
|
|
324
|
-
raise MalformedGenerateResponseError("Invalid packed sampling mask")
|
|
325
|
-
arrays[name] = values
|
|
326
|
-
counts, ids = arrays["counts"], arrays["ids"]
|
|
431
|
+
ids, counts = _decode_packed_sampling_mask(mask)
|
|
327
432
|
if len(counts) != len(completion_ids) or int(counts.sum()) != len(ids):
|
|
328
433
|
raise MalformedGenerateResponseError("Sampling mask token count mismatch")
|
|
329
434
|
mask = [row.tolist() for row in np.split(ids, np.cumsum(counts)[:-1])]
|
|
@@ -500,7 +605,7 @@ async def generate(
|
|
|
500
605
|
Returns a dict with: request_id, prompt_ids, renderer_prompt_ids,
|
|
501
606
|
mm_placeholders, completion_ids, completion_logprobs, content,
|
|
502
607
|
reasoning_content, tool_calls, finish_reason, routed_experts,
|
|
503
|
-
weight_versions, multi_modal_data, prompt_attribution.
|
|
608
|
+
sampler_support, weight_versions, multi_modal_data, prompt_attribution.
|
|
504
609
|
``completion_logprobs`` is a float32 numpy array, one value per completion
|
|
505
610
|
token: vLLM samples in float32, so the packed form decodes without a copy
|
|
506
611
|
and the JSON forms narrow losslessly.
|
|
@@ -509,9 +614,13 @@ async def generate(
|
|
|
509
614
|
is vLLM's (>= 0.32) list of ``{version, start, end}`` spans over
|
|
510
615
|
completion-token indexes, or ``None`` when the engine labels none.
|
|
511
616
|
|
|
512
|
-
|
|
513
|
-
``
|
|
514
|
-
|
|
617
|
+
``sampler_support`` is the sampler's per-token support in CSR form, or ``None`` when
|
|
618
|
+
the engine returned none: ``counts[i]`` entries of the flat int32 ``ids`` (and float32
|
|
619
|
+
``logprobs``, or ``None``) per completion token. With ``replayed`` the rows are the
|
|
620
|
+
recorded sampling masks of truncated sampling, with logprobs normalized over each
|
|
621
|
+
mask when the engine returned them. Otherwise, when ``sampling_params["logprobs"]``
|
|
622
|
+
is a positive int, the rows are that many top candidates of the sampler's
|
|
623
|
+
distribution, independent of the sampled token and full-vocabulary normalized.
|
|
515
624
|
|
|
516
625
|
``prompt_attribution`` is the renderer's :class:`RenderedTokens` for
|
|
517
626
|
the prompt — either the one this call computed via
|
|
@@ -646,18 +755,27 @@ async def generate(
|
|
|
646
755
|
"Engine response must include mm_placeholders when process_multimodal=False."
|
|
647
756
|
)
|
|
648
757
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
if choice.get("compact_logprobs") is not None:
|
|
652
|
-
completion_logprobs,
|
|
653
|
-
|
|
758
|
+
sampler_support = None
|
|
759
|
+
replayed = choice.get("sampling_mask") is not None
|
|
760
|
+
if choice.get("compact_logprobs") is not None and replayed:
|
|
761
|
+
completion_logprobs, sampler_support = _parse_replayed_support(
|
|
762
|
+
choice, completion_ids
|
|
763
|
+
)
|
|
764
|
+
elif choice.get("compact_logprobs") is not None:
|
|
765
|
+
completion_logprobs, top_ids, top_logprobs = _parse_compact_logprobs(
|
|
766
|
+
choice, completion_ids, head_size
|
|
654
767
|
)
|
|
768
|
+
if top_ids is not None and top_logprobs is not None:
|
|
769
|
+
sampler_support = _pack_sampler_rows(top_ids, top_logprobs, replayed=False)
|
|
655
770
|
else:
|
|
656
771
|
completion_logprobs = _parse_completion_logprobs(choice, completion_ids)
|
|
657
772
|
if head_size is not None:
|
|
658
|
-
|
|
659
|
-
_parse_completion_top_logprobs(choice, completion_ids, head_size)
|
|
773
|
+
sampler_support = _pack_sampler_rows(
|
|
774
|
+
*_parse_completion_top_logprobs(choice, completion_ids, head_size),
|
|
775
|
+
replayed=replayed,
|
|
660
776
|
)
|
|
777
|
+
elif replayed:
|
|
778
|
+
sampler_support = _parse_mask_support(choice, completion_ids)
|
|
661
779
|
|
|
662
780
|
parsed = renderer.parse_response(
|
|
663
781
|
completion_ids,
|
|
@@ -666,10 +784,6 @@ async def generate(
|
|
|
666
784
|
)
|
|
667
785
|
|
|
668
786
|
routed_experts = choice.get("routed_experts")
|
|
669
|
-
# vLLM's native kept-set sampling masks (``--return-sampling-mask``):
|
|
670
|
-
# one list of surviving vocab ids per completion token, or (prime-rl's
|
|
671
|
-
# server) packed CSR ``{"ids", "counts"}`` base64 int32 arrays.
|
|
672
|
-
sampling_mask = choice.get("sampling_mask")
|
|
673
787
|
|
|
674
788
|
# /inference/v1/generate returns finish_reason in {"stop","length",...} —
|
|
675
789
|
# never "tool_calls" (a chat-completions concept). Promote stop→tool_calls
|
|
@@ -694,15 +808,13 @@ async def generate(
|
|
|
694
808
|
"mm_placeholders": mm_placeholders,
|
|
695
809
|
"completion_ids": list(completion_ids),
|
|
696
810
|
"completion_logprobs": completion_logprobs,
|
|
697
|
-
"
|
|
698
|
-
"completion_top_logprobs": completion_top_logprobs,
|
|
811
|
+
"sampler_support": sampler_support,
|
|
699
812
|
"content": parsed.content,
|
|
700
813
|
"reasoning_content": parsed.reasoning_content,
|
|
701
814
|
"tool_calls": parsed.tool_calls,
|
|
702
815
|
"finish_reason": finish_reason,
|
|
703
816
|
"reasoning_complete": parsed.reasoning_complete,
|
|
704
817
|
"routed_experts": routed_experts,
|
|
705
|
-
"sampling_mask": sampling_mask,
|
|
706
818
|
"weight_versions": choice.get("weight_versions"),
|
|
707
819
|
# The mm sidecar consumed on the request side, surfaced back so
|
|
708
820
|
# callers can persist it on the trajectory step for downstream
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
renderers/__init__.py,sha256=Ti81-To_6zlqryQ5aKxQeuJTtkXD89FKrMDNUu94kMQ,7575
|
|
2
|
-
renderers/_version.py,sha256=
|
|
3
|
-
renderers/base.py,sha256
|
|
4
|
-
renderers/client.py,sha256=
|
|
2
|
+
renderers/_version.py,sha256=GmUA9E_4oKM6yzYbkMFjjMl7KG0RqexjdSLxohFQSBI,533
|
|
3
|
+
renderers/base.py,sha256=FzkAxXVYEb_EoHGMcUCOI78vDmAoXdD1G0_6aykrlJ0,96106
|
|
4
|
+
renderers/client.py,sha256=fGDjUnuXrloeokwW82ldVdO4WAw0LOzw6GCzhPjdvjo,48613
|
|
5
5
|
renderers/configs.py,sha256=ZY7IchF0KDQ3WOvNv_uSbMthWfvVWgC4Sj7H89kH04U,46829
|
|
6
6
|
renderers/custom.py,sha256=TC08fa2x8dJ-EF-8do9TCrq1uSBKyh0R6hwpXKdgXwo,3613
|
|
7
7
|
renderers/deepseek_r1.py,sha256=mYpRRx4WkSg642FT0UDK-R8ha8Lk0GBSWirZG2PafJA,2660
|
|
@@ -32,7 +32,7 @@ renderers/qwen38.py,sha256=Kyl-NlxXteyEHiMJgs8ZMYYnjmtV8nenmGVhGdL5GjQ,3247
|
|
|
32
32
|
renderers/qwen3_vl.py,sha256=F66Rt26BM123C8USHE1dxAaGcFmw8f87-Hre8bzIPxE,43208
|
|
33
33
|
renderers/reasoning.py,sha256=mdPnEg99y2ialU4APdL7eubjQ4WRF75wHWmKz94N9DA,8738
|
|
34
34
|
renderers/tagged.py,sha256=91NgN61TvzWB2Y6UTEvpH-UNC0XGUrYIfcS8MskTSas,6357
|
|
35
|
-
renderers-0.2.1.
|
|
36
|
-
renderers-0.2.1.
|
|
37
|
-
renderers-0.2.1.
|
|
38
|
-
renderers-0.2.1.
|
|
35
|
+
renderers-0.2.1.dev9.dist-info/METADATA,sha256=-NunNM_YD_pC3SgdSm32dFEyoWBPz1H85LQ8EuQKu2s,22254
|
|
36
|
+
renderers-0.2.1.dev9.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
37
|
+
renderers-0.2.1.dev9.dist-info/licenses/LICENSE,sha256=u3UTCrYI-lDtgtxpbCiEyDzK_E6vS8U10W_IEDH2mdE,11287
|
|
38
|
+
renderers-0.2.1.dev9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|