renderers 0.1.8.dev28__tar.gz → 0.1.8.dev30__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.8.dev28 → renderers-0.1.8.dev30}/PKG-INFO +1 -1
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/_version.py +2 -2
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/nemotron3.py +2 -1
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/parsing.py +17 -11
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_tool_arg_type_preservation.py +74 -1
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/.github/workflows/style.yml +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/.github/workflows/test.yml +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/.gitignore +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/LICENSE +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/README.md +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/docs/renderer-config.md +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/examples/README.md +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/pyproject.toml +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/__init__.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/base.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/client.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/configs.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/default.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/glm45.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/glm5.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/laguna_xs2.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/parsers.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/qwen3.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/qwen35.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/qwen36.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/conftest.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_bridge.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_build_helpers.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_client.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_incremental.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_is_content.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_load_tokenizer_fastokens.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_message_indices.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_multimodal.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_parse_response.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_parsers.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_render_ids.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_renderer_config_parity.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_roundtrip.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/uv.lock +0 -0
|
@@ -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.8.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1, 8, '
|
|
21
|
+
__version__ = version = '0.1.8.dev30'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 8, 'dev30')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -430,7 +430,7 @@ class Nemotron3Renderer:
|
|
|
430
430
|
self,
|
|
431
431
|
token_ids: list[int],
|
|
432
432
|
*,
|
|
433
|
-
tools: list[ToolSpec] | None = None,
|
|
433
|
+
tools: list[ToolSpec] | None = None,
|
|
434
434
|
) -> ParsedResponse:
|
|
435
435
|
stop_ids = {self._im_end}
|
|
436
436
|
if self._endoftext is not None:
|
|
@@ -443,6 +443,7 @@ class Nemotron3Renderer:
|
|
|
443
443
|
think_end_id=self._think_end,
|
|
444
444
|
tool_call_id=self._tool_call,
|
|
445
445
|
tool_call_end_id=self._tool_call_end,
|
|
446
|
+
tools=tools,
|
|
446
447
|
)
|
|
447
448
|
|
|
448
449
|
def get_stop_token_ids(self) -> list[int]:
|
|
@@ -65,30 +65,36 @@ def _coerce_arg_value(
|
|
|
65
65
|
"""Coerce a raw ``<arg_value>`` body to its declared type.
|
|
66
66
|
|
|
67
67
|
Returns ``(value, used_json_fallback)``. The boolean is ``True`` only
|
|
68
|
-
when ``json.loads`` was attempted
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
when ``json.loads`` was attempted, raised, AND the schema doesn't
|
|
69
|
+
permit a string. Returning a string verbatim because the schema
|
|
70
|
+
permits strings is NOT a fallback.
|
|
71
71
|
|
|
72
72
|
Rule (matches vLLM / SGLang reference parsers):
|
|
73
73
|
|
|
74
74
|
- If the param's declared ``type`` is ``"string"`` (or single-element
|
|
75
75
|
``["string"]``), return ``text`` verbatim — never ``json.loads``.
|
|
76
|
-
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Union types that include ``"string"`` alongside
|
|
81
|
-
attempt ``json.loads`` first so an explicit
|
|
82
|
-
parse; the string branch
|
|
76
|
+
- Otherwise try ``json.loads``. If that fails, return raw ``text``.
|
|
77
|
+
The ``used_json_fallback`` flag is ``True`` only when the schema
|
|
78
|
+
does NOT permit a string — i.e. the fallback is truly suspect.
|
|
79
|
+
|
|
80
|
+
Union types (``anyOf``/``oneOf``) that include ``"string"`` alongside
|
|
81
|
+
other types still attempt ``json.loads`` first so an explicit
|
|
82
|
+
integer / bool can parse; the string branch wins as fallback, and
|
|
83
|
+
landing there is expected — not a malformed-JSON signal.
|
|
83
84
|
"""
|
|
85
|
+
string_is_allowed = False
|
|
84
86
|
if param_schema is not None:
|
|
85
87
|
declared = param_schema.get("type")
|
|
86
88
|
if declared == "string" or declared == ["string"]:
|
|
87
89
|
return text, False
|
|
90
|
+
for branch in param_schema.get("anyOf") or param_schema.get("oneOf") or []:
|
|
91
|
+
if isinstance(branch, dict) and branch.get("type") == "string":
|
|
92
|
+
string_is_allowed = True
|
|
93
|
+
break
|
|
88
94
|
try:
|
|
89
95
|
return json.loads(text), False
|
|
90
96
|
except (json.JSONDecodeError, ValueError):
|
|
91
|
-
return text,
|
|
97
|
+
return text, not string_is_allowed
|
|
92
98
|
|
|
93
99
|
|
|
94
100
|
def _find(ids: list[int], target: int, start: int = 0) -> int:
|
|
@@ -26,7 +26,7 @@ import pytest
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
# (HuggingFace model name, renderer name). Two JSON-shaped controls
|
|
29
|
-
# (string types already preserved by the wire format) +
|
|
29
|
+
# (string types already preserved by the wire format) + five XML-style
|
|
30
30
|
# parsers that rely on the schema to preserve them.
|
|
31
31
|
_MODELS = [
|
|
32
32
|
("Qwen/Qwen3-8B", "auto"), # hermes JSON — control
|
|
@@ -35,6 +35,7 @@ _MODELS = [
|
|
|
35
35
|
("zai-org/GLM-5", "auto"), # XML
|
|
36
36
|
("MiniMaxAI/MiniMax-M2.5", "auto"), # XML
|
|
37
37
|
("poolside/Laguna-XS.2", "auto"), # XML
|
|
38
|
+
("nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16", "auto"), # XML
|
|
38
39
|
]
|
|
39
40
|
|
|
40
41
|
|
|
@@ -137,3 +138,75 @@ def test_string_arg_preserves_type(model, renderer_name, renderer, args):
|
|
|
137
138
|
assert got == args, (
|
|
138
139
|
f"{model}: tool-arg type drift — sent {args!r}, parser returned {got!r}"
|
|
139
140
|
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# Schemas where ``string`` is one branch of a union (``anyOf`` / ``oneOf``).
|
|
144
|
+
# These are common in practice — e.g. ``form_input.value: str | bool`` in
|
|
145
|
+
# Pydantic serialises to ``{"anyOf": [{"type": "string"}, {"type": "boolean"}]}``.
|
|
146
|
+
# Without the union-aware check, the XML parser's ``json.loads`` falls back
|
|
147
|
+
# to raw text for bare strings, but flags the call ``INVALID_JSON`` because
|
|
148
|
+
# no top-level ``type`` key declared a string — silently dropping otherwise
|
|
149
|
+
# valid tool calls in the renderer client.
|
|
150
|
+
UNION_WITH_STRING_SCHEMAS = [
|
|
151
|
+
pytest.param(
|
|
152
|
+
{"anyOf": [{"type": "string"}, {"type": "boolean"}]},
|
|
153
|
+
id="anyOf-string-boolean",
|
|
154
|
+
),
|
|
155
|
+
pytest.param(
|
|
156
|
+
{"anyOf": [{"type": "string"}, {"type": "null"}]},
|
|
157
|
+
id="anyOf-string-null",
|
|
158
|
+
),
|
|
159
|
+
pytest.param(
|
|
160
|
+
{"oneOf": [{"type": "string"}, {"type": "integer"}]},
|
|
161
|
+
id="oneOf-string-integer",
|
|
162
|
+
),
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@pytest.mark.parametrize("param_schema", UNION_WITH_STRING_SCHEMAS)
|
|
167
|
+
def test_union_with_string_emits_ok_status(
|
|
168
|
+
model, renderer_name, renderer, param_schema
|
|
169
|
+
):
|
|
170
|
+
"""Union schemas containing ``string`` must yield ``status=OK`` when
|
|
171
|
+
the model emits a bare string. Pre-fix, ``_coerce_arg_value`` flagged
|
|
172
|
+
this as ``INVALID_JSON`` because the top-level ``type`` key was
|
|
173
|
+
absent (the string branch was under ``anyOf`` / ``oneOf``)."""
|
|
174
|
+
from renderers.base import ToolCallParseStatus
|
|
175
|
+
|
|
176
|
+
tools = [
|
|
177
|
+
{
|
|
178
|
+
"type": "function",
|
|
179
|
+
"function": {
|
|
180
|
+
"name": "f",
|
|
181
|
+
"description": "Test tool with one union-typed parameter.",
|
|
182
|
+
"parameters": {
|
|
183
|
+
"type": "object",
|
|
184
|
+
"properties": {"x": param_schema},
|
|
185
|
+
"required": ["x"],
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
args = {"x": "abc"}
|
|
191
|
+
msg = {
|
|
192
|
+
"role": "assistant",
|
|
193
|
+
"content": "",
|
|
194
|
+
"tool_calls": [
|
|
195
|
+
{
|
|
196
|
+
"id": "functions.f:0",
|
|
197
|
+
"function": {"name": "f", "arguments": args},
|
|
198
|
+
}
|
|
199
|
+
],
|
|
200
|
+
}
|
|
201
|
+
completion_ids = _extract_assistant_tokens(renderer, PROMPT, msg, tools=tools)
|
|
202
|
+
parsed = renderer.parse_response(completion_ids, tools=tools)
|
|
203
|
+
|
|
204
|
+
assert parsed.tool_calls, f"{model}: parser returned no tool_calls"
|
|
205
|
+
tc = parsed.tool_calls[0]
|
|
206
|
+
assert tc.status == ToolCallParseStatus.OK, (
|
|
207
|
+
f"{model}: union-with-string schema flagged {tc.status} on bare string"
|
|
208
|
+
)
|
|
209
|
+
got = _normalize_args(tc.arguments)
|
|
210
|
+
assert got == args, (
|
|
211
|
+
f"{model}: tool-arg drift — sent {args!r}, parser returned {got!r}"
|
|
212
|
+
)
|
|
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.8.dev28 → renderers-0.1.8.dev30}/examples/sglang/multiturn_generate_sglang.py
RENAMED
|
File without changes
|
|
File without changes
|
{renderers-0.1.8.dev28 → renderers-0.1.8.dev30}/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
|