renderers 0.1.13.dev13__tar.gz → 0.1.13.dev15__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.
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/PKG-INFO +1 -1
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/pyproject.toml +2 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/_version.py +2 -2
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/muse_glimmer.py +44 -18
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_muse_glimmer.py +30 -2
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_tool_arg_type_preservation.py +14 -5
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/uv.lock +4 -3
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/.github/workflows/style.yml +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/.github/workflows/test.yml +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/.gitignore +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/LICENSE +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/README.md +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/docs/renderer-config.md +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/examples/README.md +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/__init__.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/base.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/client.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/configs.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/custom.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/deepseek_r1.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/deepseek_v4.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/default.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/gemma4.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/glm45.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/glm5.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/hy3.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/inkling.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/laguna_s21.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/laguna_xs2.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/llama_3.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/nemotron3.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/parsers.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/parsing.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/prime_qwen3.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/qwen3.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/qwen35.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/qwen36.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/qwen38.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/reasoning.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/renderers/tagged.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/audit_prefix_stability.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/conftest.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/fixtures/prefix_stability.json +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/parity.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/reference_continuations.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/reference_rendering.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/replay.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_bridge.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_build_helpers.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_client.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_custom.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_deepseek_r1.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_deepseek_v4.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_disabled_thinking_stability.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_gemma4.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_glm53_flash.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_glm_tool_name_validation.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_hy3.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_incremental.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_inkling.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_is_content.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_laguna_m1.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_laguna_xs21.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_llama_3.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_message_indices.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_message_tool_names.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_multimodal.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_nemotron35_vlm.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_nemotron3_ultra.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_offsetless_tokenizers.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_parity.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_parse_response.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_parsers.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_qwen38.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_reasoning_boundaries.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_roundtrip.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_tagged_rollout.py +0 -0
- {renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/tests/test_tokens_per_message.py +0 -0
|
@@ -121,6 +121,8 @@ exclude-newer = "7 days"
|
|
|
121
121
|
# fast-track so first-party releases can land same-day. Only packages that
|
|
122
122
|
# appear in `uv tree` are listed.
|
|
123
123
|
prime-pydantic-config = false
|
|
124
|
+
# CA bundle: security fixes (e.g. 2026.10.19 for the eMudhra compromise) must not wait out the cooldown
|
|
125
|
+
certifi = false
|
|
124
126
|
|
|
125
127
|
[tool.ty.environment]
|
|
126
128
|
python-version = "3.13"
|
|
@@ -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.1.13.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1, 13, '
|
|
21
|
+
__version__ = version = '0.1.13.dev15'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 13, 'dev15')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -51,7 +51,6 @@ from renderers.base import (
|
|
|
51
51
|
should_rerender_for_thinking_retention,
|
|
52
52
|
)
|
|
53
53
|
from renderers.configs import MuseGlimmerRendererConfig
|
|
54
|
-
from renderers.parsing import _build_param_type_index, _coerce_arg_value
|
|
55
54
|
|
|
56
55
|
_IMAGE_TYPES = ("image", "image_url")
|
|
57
56
|
_UNSUPPORTED_MEDIA_TYPES = ("video", "video_url", "audio", "audio_url", "input_audio")
|
|
@@ -104,6 +103,15 @@ _PARAM_RE = re.compile(
|
|
|
104
103
|
r'<atem:parameter\b[^>]*?\bname="(?P<key>[^"]+)"[^>]*?>(?P<value>.*?)</atem:parameter>',
|
|
105
104
|
re.DOTALL,
|
|
106
105
|
)
|
|
106
|
+
_STRIP_REASONING_RE = re.compile(
|
|
107
|
+
r"(?:<\|start\|>assistant\s*)?to=self<\|message\|>.*?<\|eom\|>", re.DOTALL
|
|
108
|
+
)
|
|
109
|
+
_STRIP_OPEN_REASONING_RE = re.compile(
|
|
110
|
+
r"(?:<\|start\|>assistant\s*)?to=self<\|message\|>"
|
|
111
|
+
r"(?:(?!<\|eom\|>)(?!to=[^\s<]+<\|message\|>).)*"
|
|
112
|
+
r"(?=to=[^\s<]+<\|message\|>|$)",
|
|
113
|
+
re.DOTALL,
|
|
114
|
+
)
|
|
107
115
|
|
|
108
116
|
|
|
109
117
|
def _json(value: Any) -> str:
|
|
@@ -723,23 +731,42 @@ class MuseGlimmerRenderer:
|
|
|
723
731
|
reasoning = (
|
|
724
732
|
"\n".join(p for p in reasoning_parts if p) if reasoning_parts else None
|
|
725
733
|
)
|
|
726
|
-
|
|
734
|
+
registered = {_function(tool)["name"] for tool in tools or []}
|
|
727
735
|
calls = [
|
|
728
736
|
c
|
|
729
737
|
for recipient, body, closed in tool_bodies
|
|
730
|
-
for c in self._parse_calls(
|
|
738
|
+
for c in self._parse_calls(body, registered)
|
|
731
739
|
]
|
|
740
|
+
content = "".join(content_parts)
|
|
741
|
+
if (
|
|
742
|
+
messages
|
|
743
|
+
and messages[-1][0] == "user"
|
|
744
|
+
and not messages[-1][2]
|
|
745
|
+
and not any(call.name for call in calls)
|
|
746
|
+
and content.endswith("\n")
|
|
747
|
+
and "<atem:invoke" not in content
|
|
748
|
+
and "<atem:function_calls>" not in content
|
|
749
|
+
):
|
|
750
|
+
# vLLM's non-streaming content regex ends at `$`, before one final LF.
|
|
751
|
+
content = content[:-1]
|
|
752
|
+
if tool_bodies and not any(call.name for call in calls):
|
|
753
|
+
# Stock vLLM preserves malformed tool markup as content when no
|
|
754
|
+
# complete invocation can be dispatched; it does not invent a call.
|
|
755
|
+
remainder = _STRIP_REASONING_RE.sub("", text)
|
|
756
|
+
remainder = _STRIP_OPEN_REASONING_RE.sub("", remainder)
|
|
757
|
+
if "<atem:invoke" in remainder or "<atem:function_calls>" in remainder:
|
|
758
|
+
content = remainder
|
|
759
|
+
elif not content:
|
|
760
|
+
content = "" if "to=self<|message|>" in text else text
|
|
732
761
|
return ParsedResponse(
|
|
733
|
-
content=
|
|
762
|
+
content=content,
|
|
734
763
|
reasoning_content=reasoning,
|
|
735
764
|
tool_calls=calls,
|
|
736
765
|
reasoning_complete=reasoning_complete,
|
|
737
766
|
)
|
|
738
767
|
|
|
739
768
|
@staticmethod
|
|
740
|
-
def _parse_calls(
|
|
741
|
-
recipient: str, body: str, param_types: dict[str, dict[str, dict[str, Any]]]
|
|
742
|
-
) -> list[ParsedToolCall]:
|
|
769
|
+
def _parse_calls(body: str, registered: set[str]) -> list[ParsedToolCall]:
|
|
743
770
|
"""Every ``<atem:invoke>`` in one tool-channel message."""
|
|
744
771
|
calls: list[ParsedToolCall] = []
|
|
745
772
|
last_end = 0
|
|
@@ -754,23 +781,24 @@ class MuseGlimmerRenderer:
|
|
|
754
781
|
)
|
|
755
782
|
)
|
|
756
783
|
continue
|
|
757
|
-
|
|
784
|
+
if name not in registered:
|
|
785
|
+
head, sep, tail = name.partition(".")
|
|
786
|
+
if sep and head == tail and head in registered:
|
|
787
|
+
name = head
|
|
758
788
|
arguments: dict[str, Any] = {}
|
|
759
|
-
fallback = False
|
|
760
789
|
for p in _PARAM_RE.finditer(m.group("body")):
|
|
761
|
-
value
|
|
762
|
-
|
|
763
|
-
|
|
790
|
+
value = p.group("value")
|
|
791
|
+
try:
|
|
792
|
+
value = json.loads(value)
|
|
793
|
+
except (json.JSONDecodeError, ValueError):
|
|
794
|
+
pass
|
|
764
795
|
arguments[p.group("key")] = value
|
|
765
|
-
fallback = fallback or used
|
|
766
796
|
calls.append(
|
|
767
797
|
ParsedToolCall(
|
|
768
798
|
raw=m.group(0),
|
|
769
799
|
name=name,
|
|
770
800
|
arguments=arguments,
|
|
771
|
-
status=ToolCallParseStatus.
|
|
772
|
-
if fallback
|
|
773
|
-
else ToolCallParseStatus.OK,
|
|
801
|
+
status=ToolCallParseStatus.OK,
|
|
774
802
|
)
|
|
775
803
|
)
|
|
776
804
|
rest = body[last_end:]
|
|
@@ -779,7 +807,6 @@ class MuseGlimmerRenderer:
|
|
|
779
807
|
calls.append(
|
|
780
808
|
ParsedToolCall(
|
|
781
809
|
raw=rest.strip(),
|
|
782
|
-
name=recipient,
|
|
783
810
|
status=ToolCallParseStatus.UNCLOSED_BLOCK,
|
|
784
811
|
)
|
|
785
812
|
)
|
|
@@ -787,7 +814,6 @@ class MuseGlimmerRenderer:
|
|
|
787
814
|
calls.append(
|
|
788
815
|
ParsedToolCall(
|
|
789
816
|
raw=body,
|
|
790
|
-
name=recipient,
|
|
791
817
|
status=ToolCallParseStatus.MALFORMED_STRUCTURE,
|
|
792
818
|
)
|
|
793
819
|
)
|
|
@@ -17,7 +17,6 @@ from datetime import datetime
|
|
|
17
17
|
from functools import lru_cache
|
|
18
18
|
|
|
19
19
|
import pytest
|
|
20
|
-
|
|
21
20
|
from renderers.base import ToolCallParseStatus
|
|
22
21
|
|
|
23
22
|
MODEL = "meta-models/Muse-Glimmer-30B"
|
|
@@ -365,6 +364,22 @@ _PARSE_CASES = {
|
|
|
365
364
|
("Let me think.", "The answer is 4.", []),
|
|
366
365
|
),
|
|
367
366
|
"answer-only": (" to=user<|message|>Hello there!", (None, "Hello there!", [])),
|
|
367
|
+
"answer-final-newline": (" to=user<|message|>Hello\n", (None, "Hello", [])),
|
|
368
|
+
"answer-final-two-newlines": (
|
|
369
|
+
" to=user<|message|>Hello\n\n",
|
|
370
|
+
(None, "Hello\n", []),
|
|
371
|
+
),
|
|
372
|
+
"answer-final-space-newline": (" to=user<|message|>Hello \n", (None, "Hello ", [])),
|
|
373
|
+
"closed-answer-keeps-newline": (
|
|
374
|
+
" to=user<|message|>Hello\n<|eom|>",
|
|
375
|
+
(None, "Hello\n", []),
|
|
376
|
+
),
|
|
377
|
+
"tool-and-answer-keeps-newline": (
|
|
378
|
+
" to=get_weather<|message|>"
|
|
379
|
+
+ _invoke("get_weather", ("city", "Paris"))
|
|
380
|
+
+ "<|eom|><|start|>assistant to=user<|message|>Hello\n",
|
|
381
|
+
(None, "Hello\n", [("get_weather", {"city": "Paris"})]),
|
|
382
|
+
),
|
|
368
383
|
"tool-call": (
|
|
369
384
|
" to=self<|message|>Need weather.<|eom|><|start|>assistant to=get_weather<|message|>"
|
|
370
385
|
+ _invoke(
|
|
@@ -413,6 +428,16 @@ _PARSE_CASES = {
|
|
|
413
428
|
"<|start|>assistant to=user<|message|>Done.",
|
|
414
429
|
("First.\nSecond.", "Done.", []),
|
|
415
430
|
),
|
|
431
|
+
"doubled-bare-name": (
|
|
432
|
+
" to=get_weather<|message|>"
|
|
433
|
+
+ _invoke("get_weather.get_weather", ("city", "Paris")),
|
|
434
|
+
(None, "", [("get_weather", {"city": "Paris"})]),
|
|
435
|
+
),
|
|
436
|
+
"string-schema-does-not-override-scalar-decoding": (
|
|
437
|
+
" to=get_weather<|message|>"
|
|
438
|
+
+ _invoke("get_weather", ("city", "123"), ("code", "false")),
|
|
439
|
+
(None, "", [("get_weather", {"city": 123, "code": False})]),
|
|
440
|
+
),
|
|
416
441
|
}
|
|
417
442
|
|
|
418
443
|
|
|
@@ -520,14 +545,17 @@ def test_unfinished_tool_call_is_unclosed():
|
|
|
520
545
|
)
|
|
521
546
|
)
|
|
522
547
|
assert [c.status for c in parsed.tool_calls] == [ToolCallParseStatus.UNCLOSED_BLOCK]
|
|
548
|
+
assert parsed.tool_calls[0].name is None
|
|
549
|
+
assert '<atem:invoke name="get_weather">' in parsed.content
|
|
523
550
|
|
|
524
551
|
|
|
525
552
|
def test_tool_channel_without_invoke_is_malformed():
|
|
526
553
|
_, renderer = _load()
|
|
527
554
|
parsed = renderer.parse_response(_ids(" to=get_weather<|message|>Paris please"))
|
|
528
555
|
assert [(c.name, c.status) for c in parsed.tool_calls] == [
|
|
529
|
-
(
|
|
556
|
+
(None, ToolCallParseStatus.MALFORMED_STRUCTURE)
|
|
530
557
|
]
|
|
558
|
+
assert parsed.content == " to=get_weather<|message|>Paris please"
|
|
531
559
|
|
|
532
560
|
|
|
533
561
|
def test_prefilled_reasoning_prompt_continues_reasoning():
|
|
@@ -13,6 +13,9 @@ reference parsers (e.g. ``vllm/glm45_tool_parser.py``). The hermes-JSON
|
|
|
13
13
|
(Qwen3) and section-JSON (Kimi K2) parsers sidestep the bug because
|
|
14
14
|
their wire format quotes strings; both serve as controls.
|
|
15
15
|
|
|
16
|
+
Muse Glimmer follows vLLM's schema-independent JSON decoding instead, so
|
|
17
|
+
its cases assert decoded values even when the schema declares a string.
|
|
18
|
+
|
|
16
19
|
Originally raised by Robin (Poolside) on PR #21.
|
|
17
20
|
"""
|
|
18
21
|
|
|
@@ -25,6 +28,8 @@ from typing import Any
|
|
|
25
28
|
import pytest
|
|
26
29
|
from parity import models_for
|
|
27
30
|
|
|
31
|
+
from renderers.muse_glimmer import MuseGlimmerRenderer
|
|
32
|
+
|
|
28
33
|
|
|
29
34
|
# (HuggingFace model name, renderer name). Two JSON-shaped controls
|
|
30
35
|
# (string types already preserved by the wire format) + five XML-style
|
|
@@ -110,9 +115,8 @@ def _extract_assistant_tokens(renderer, prompt, assistant_msg, *, tools=None):
|
|
|
110
115
|
|
|
111
116
|
|
|
112
117
|
@pytest.mark.parametrize("args", JSON_LOOKING_STRING_ARGS)
|
|
113
|
-
def
|
|
114
|
-
"""
|
|
115
|
-
not get re-parsed as bool/int/null/list/dict by the parser."""
|
|
118
|
+
def test_string_arg_follows_parser_contract(model, renderer_name, renderer, args):
|
|
119
|
+
"""Preserve declared strings unless the reference parser decodes JSON."""
|
|
116
120
|
msg = {
|
|
117
121
|
"role": "assistant",
|
|
118
122
|
"content": "",
|
|
@@ -128,8 +132,13 @@ def test_string_arg_preserves_type(model, renderer_name, renderer, args):
|
|
|
128
132
|
|
|
129
133
|
assert parsed.tool_calls, f"{model}: parser returned no tool_calls"
|
|
130
134
|
got = _normalize_args(parsed.tool_calls[0].arguments)
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
expected = (
|
|
136
|
+
{key: json.loads(value) for key, value in args.items()}
|
|
137
|
+
if isinstance(renderer, MuseGlimmerRenderer)
|
|
138
|
+
else args
|
|
139
|
+
)
|
|
140
|
+
assert got == expected, (
|
|
141
|
+
f"{model}: tool-arg type drift — expected {expected!r}, parser returned {got!r}"
|
|
133
142
|
)
|
|
134
143
|
|
|
135
144
|
|
|
@@ -7,6 +7,7 @@ exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for
|
|
|
7
7
|
exclude-newer-span = "P7D"
|
|
8
8
|
|
|
9
9
|
[options.exclude-newer-package]
|
|
10
|
+
certifi = false
|
|
10
11
|
prime-pydantic-config = false
|
|
11
12
|
|
|
12
13
|
[[package]]
|
|
@@ -42,11 +43,11 @@ wheels = [
|
|
|
42
43
|
|
|
43
44
|
[[package]]
|
|
44
45
|
name = "certifi"
|
|
45
|
-
version = "2026.
|
|
46
|
+
version = "2026.10.19"
|
|
46
47
|
source = { registry = "https://pypi.org/simple" }
|
|
47
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
48
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/1436c50cc46699de0230fbaacc1d1de7bfec413176099949e9d5112e88f2/certifi-2026.10.19.tar.gz", hash = "sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae", size = 137598, upload-time = "2026-10-20T02:24:16.604Z" }
|
|
48
49
|
wheels = [
|
|
49
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
50
|
+
{ url = "https://files.pythonhosted.org/packages/c4/51/29046e48f693c54e5b7412216d447a1d8362c9653148d98c02cdf65fa733/certifi-2026.10.19-py3-none-any.whl", hash = "sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b", size = 136464, upload-time = "2026-10-20T02:24:15.072Z" },
|
|
50
51
|
]
|
|
51
52
|
|
|
52
53
|
[[package]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/examples/sglang/multiturn_generate_sglang.py
RENAMED
|
File without changes
|
{renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/examples/sglang/online_multiturn_sglang.py
RENAMED
|
File without changes
|
{renderers-0.1.13.dev13 → renderers-0.1.13.dev15}/examples/tinker/multiturn_generate_tinker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|