renderers 0.1.8.dev32__tar.gz → 0.1.8.dev33__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.dev32 → renderers-0.1.8.dev33}/PKG-INFO +1 -1
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/examples/sglang/multiturn_generate_sglang.py +29 -10
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/examples/sglang/online_multiturn_sglang.py +29 -10
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/examples/tinker/multiturn_generate_tinker.py +29 -10
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/examples/transformers/multiturn_generate_transformers.py +28 -10
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/examples/vllm/multiturn_generate_vllm.py +29 -10
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/_version.py +2 -2
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/.github/workflows/style.yml +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/.github/workflows/test.yml +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/.gitignore +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/LICENSE +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/README.md +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/docs/renderer-config.md +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/examples/README.md +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/pyproject.toml +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/__init__.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/base.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/client.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/configs.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/default.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/glm45.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/glm5.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/laguna_xs2.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/nemotron3.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/parsers.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/parsing.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/qwen3.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/qwen35.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/qwen36.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/conftest.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_bridge.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_build_helpers.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_client.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_incremental.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_is_content.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_load_tokenizer_fastokens.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_message_indices.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_multimodal.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_parse_response.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_parsers.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_render_ids.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_renderer_config_parity.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_roundtrip.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/tests/test_tool_arg_type_preservation.py +0 -0
- {renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/uv.lock +0 -0
{renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/examples/sglang/multiturn_generate_sglang.py
RENAMED
|
@@ -22,6 +22,7 @@ import json
|
|
|
22
22
|
import os
|
|
23
23
|
|
|
24
24
|
import sglang as sgl
|
|
25
|
+
from renderers.configs import Qwen35RendererConfig
|
|
25
26
|
from renderers.gpt_oss import GptOssRenderer
|
|
26
27
|
from renderers.qwen35 import Qwen35Renderer
|
|
27
28
|
from transformers import AutoTokenizer
|
|
@@ -52,7 +53,9 @@ TOOLS = [
|
|
|
52
53
|
def make_renderer(model: str, enable_thinking: bool | None):
|
|
53
54
|
tokenizer = AutoTokenizer.from_pretrained(model, trust_remote_code=False)
|
|
54
55
|
if model.startswith("Qwen/Qwen3.5-"):
|
|
55
|
-
return Qwen35Renderer(
|
|
56
|
+
return Qwen35Renderer(
|
|
57
|
+
tokenizer, Qwen35RendererConfig(enable_thinking=enable_thinking)
|
|
58
|
+
)
|
|
56
59
|
if model == "openai/gpt-oss-20b":
|
|
57
60
|
return GptOssRenderer(tokenizer)
|
|
58
61
|
raise ValueError(f"unsupported demo model: {model}")
|
|
@@ -62,8 +65,9 @@ def print_parsed(label: str, turn: str, parsed) -> None:
|
|
|
62
65
|
print(f"\n[{label}] {turn}")
|
|
63
66
|
if parsed.reasoning_content:
|
|
64
67
|
print(f"reasoning: {parsed.reasoning_content[:240]}")
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
for tc in parsed.tool_calls:
|
|
69
|
+
# ``parse_response`` returns ``ParsedToolCall`` dataclasses, not dicts.
|
|
70
|
+
print(f"tool_call: {tc.name}({tc.arguments}) [{tc.status.value}]")
|
|
67
71
|
if parsed.content:
|
|
68
72
|
print(f"content: {parsed.content}")
|
|
69
73
|
|
|
@@ -141,21 +145,33 @@ def main() -> None:
|
|
|
141
145
|
if parsed1.reasoning_content:
|
|
142
146
|
assistant["reasoning_content"] = parsed1.reasoning_content
|
|
143
147
|
if parsed1.tool_calls:
|
|
144
|
-
|
|
148
|
+
# Convert the parsed dataclasses back to OpenAI-format tool_calls.
|
|
149
|
+
assistant["tool_calls"] = [
|
|
150
|
+
{
|
|
151
|
+
"id": tc.id or f"call_{idx}",
|
|
152
|
+
"type": "function",
|
|
153
|
+
"function": {
|
|
154
|
+
"name": tc.name,
|
|
155
|
+
"arguments": tc.arguments
|
|
156
|
+
if isinstance(tc.arguments, str)
|
|
157
|
+
else json.dumps(tc.arguments),
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
for idx, tc in enumerate(parsed1.tool_calls)
|
|
161
|
+
]
|
|
145
162
|
messages.append(assistant)
|
|
146
163
|
|
|
147
164
|
if parsed1.tool_calls:
|
|
148
165
|
new_messages = []
|
|
149
166
|
for idx, tool_call in enumerate(parsed1.tool_calls):
|
|
150
|
-
|
|
151
|
-
tool_args = fn.get("arguments") or {}
|
|
167
|
+
tool_args = tool_call.arguments or {}
|
|
152
168
|
if isinstance(tool_args, str):
|
|
153
169
|
tool_args = json.loads(tool_args)
|
|
154
170
|
new_messages.append(
|
|
155
171
|
{
|
|
156
172
|
"role": "tool",
|
|
157
|
-
"tool_call_id": tool_call.
|
|
158
|
-
"name":
|
|
173
|
+
"tool_call_id": tool_call.id or f"call_{idx}",
|
|
174
|
+
"name": tool_call.name or "multiply",
|
|
159
175
|
"content": json.dumps(
|
|
160
176
|
{"result": int(tool_args["a"]) * int(tool_args["b"])}
|
|
161
177
|
),
|
|
@@ -167,11 +183,14 @@ def main() -> None:
|
|
|
167
183
|
]
|
|
168
184
|
|
|
169
185
|
# Turn 2: bridge extends prompt_ids + completion1 exactly.
|
|
170
|
-
|
|
186
|
+
# ``bridge_to_next_turn`` returns a ``RenderedTokens`` (or None); the
|
|
187
|
+
# extended id stream is on ``.token_ids``.
|
|
188
|
+
bridged = renderer.bridge_to_next_turn(
|
|
171
189
|
prompt_ids, completion1, new_messages, tools=TOOLS
|
|
172
190
|
)
|
|
173
|
-
if
|
|
191
|
+
if bridged is None:
|
|
174
192
|
raise RuntimeError("bridge_to_next_turn returned None")
|
|
193
|
+
bridged_ids = bridged.token_ids
|
|
175
194
|
assert bridged_ids[: len(prompt_ids) + len(completion1)] == (
|
|
176
195
|
prompt_ids + completion1
|
|
177
196
|
)
|
|
@@ -44,6 +44,7 @@ from typing import Any
|
|
|
44
44
|
|
|
45
45
|
import httpx
|
|
46
46
|
from renderers.base import Renderer
|
|
47
|
+
from renderers.configs import Qwen35RendererConfig
|
|
47
48
|
from renderers.gpt_oss import GptOssRenderer
|
|
48
49
|
from renderers.qwen35 import Qwen35Renderer
|
|
49
50
|
from transformers import AutoTokenizer
|
|
@@ -71,7 +72,9 @@ TOOLS = [
|
|
|
71
72
|
def make_renderer(model: str, enable_thinking: bool | None) -> Renderer:
|
|
72
73
|
tokenizer = AutoTokenizer.from_pretrained(model, trust_remote_code=False)
|
|
73
74
|
if model.startswith("Qwen/Qwen3.5-"):
|
|
74
|
-
return Qwen35Renderer(
|
|
75
|
+
return Qwen35Renderer(
|
|
76
|
+
tokenizer, Qwen35RendererConfig(enable_thinking=enable_thinking)
|
|
77
|
+
)
|
|
75
78
|
if model == "openai/gpt-oss-20b":
|
|
76
79
|
return GptOssRenderer(tokenizer)
|
|
77
80
|
raise ValueError(f"unsupported demo model: {model}")
|
|
@@ -116,8 +119,9 @@ def print_parsed(label: str, turn: str, parsed) -> None:
|
|
|
116
119
|
print(f"\n[{label}] {turn}")
|
|
117
120
|
if parsed.reasoning_content:
|
|
118
121
|
print(f"reasoning: {parsed.reasoning_content[:240]}")
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
for tc in parsed.tool_calls:
|
|
123
|
+
# ``parse_response`` returns ``ParsedToolCall`` dataclasses, not dicts.
|
|
124
|
+
print(f"tool_call: {tc.name}({tc.arguments}) [{tc.status.value}]")
|
|
121
125
|
if parsed.content:
|
|
122
126
|
print(f"content: {parsed.content}")
|
|
123
127
|
|
|
@@ -164,21 +168,33 @@ async def run_one(
|
|
|
164
168
|
if parsed1.reasoning_content:
|
|
165
169
|
assistant["reasoning_content"] = parsed1.reasoning_content
|
|
166
170
|
if parsed1.tool_calls:
|
|
167
|
-
|
|
171
|
+
# Convert the parsed dataclasses back to OpenAI-format tool_calls.
|
|
172
|
+
assistant["tool_calls"] = [
|
|
173
|
+
{
|
|
174
|
+
"id": tc.id or f"call_{idx}",
|
|
175
|
+
"type": "function",
|
|
176
|
+
"function": {
|
|
177
|
+
"name": tc.name,
|
|
178
|
+
"arguments": tc.arguments
|
|
179
|
+
if isinstance(tc.arguments, str)
|
|
180
|
+
else json.dumps(tc.arguments),
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
for idx, tc in enumerate(parsed1.tool_calls)
|
|
184
|
+
]
|
|
168
185
|
messages.append(assistant)
|
|
169
186
|
|
|
170
187
|
if parsed1.tool_calls:
|
|
171
188
|
new_messages: list[dict[str, Any]] = []
|
|
172
189
|
for idx, tool_call in enumerate(parsed1.tool_calls):
|
|
173
|
-
|
|
174
|
-
tool_args = fn.get("arguments") or {}
|
|
190
|
+
tool_args = tool_call.arguments or {}
|
|
175
191
|
if isinstance(tool_args, str):
|
|
176
192
|
tool_args = json.loads(tool_args)
|
|
177
193
|
new_messages.append(
|
|
178
194
|
{
|
|
179
195
|
"role": "tool",
|
|
180
|
-
"tool_call_id": tool_call.
|
|
181
|
-
"name":
|
|
196
|
+
"tool_call_id": tool_call.id or f"call_{idx}",
|
|
197
|
+
"name": tool_call.name or "multiply",
|
|
182
198
|
"content": json.dumps(
|
|
183
199
|
{"result": int(tool_args["a"]) * int(tool_args["b"])}
|
|
184
200
|
),
|
|
@@ -190,11 +206,14 @@ async def run_one(
|
|
|
190
206
|
]
|
|
191
207
|
|
|
192
208
|
# Turn 2: bridge extends prompt_ids + completion1 exactly.
|
|
193
|
-
|
|
209
|
+
# ``bridge_to_next_turn`` returns a ``RenderedTokens`` (or None); the
|
|
210
|
+
# extended id stream is on ``.token_ids``.
|
|
211
|
+
bridged = renderer.bridge_to_next_turn(
|
|
194
212
|
prompt_ids, completion1, new_messages, tools=TOOLS
|
|
195
213
|
)
|
|
196
|
-
if
|
|
214
|
+
if bridged is None:
|
|
197
215
|
raise RuntimeError("bridge_to_next_turn returned None")
|
|
216
|
+
bridged_ids = bridged.token_ids
|
|
198
217
|
assert bridged_ids[: len(prompt_ids) + len(completion1)] == (
|
|
199
218
|
prompt_ids + completion1
|
|
200
219
|
)
|
{renderers-0.1.8.dev32 → renderers-0.1.8.dev33}/examples/tinker/multiturn_generate_tinker.py
RENAMED
|
@@ -22,6 +22,7 @@ import json
|
|
|
22
22
|
import os
|
|
23
23
|
|
|
24
24
|
import tinker
|
|
25
|
+
from renderers.configs import Qwen35RendererConfig
|
|
25
26
|
from renderers.gpt_oss import GptOssRenderer
|
|
26
27
|
from renderers.qwen35 import Qwen35Renderer
|
|
27
28
|
from tinker import types
|
|
@@ -53,7 +54,9 @@ TOOLS = [
|
|
|
53
54
|
def make_renderer(model: str, enable_thinking: bool | None):
|
|
54
55
|
tokenizer = AutoTokenizer.from_pretrained(model, trust_remote_code=False)
|
|
55
56
|
if model.startswith("Qwen/Qwen3.5-"):
|
|
56
|
-
return Qwen35Renderer(
|
|
57
|
+
return Qwen35Renderer(
|
|
58
|
+
tokenizer, Qwen35RendererConfig(enable_thinking=enable_thinking)
|
|
59
|
+
)
|
|
57
60
|
if model == "openai/gpt-oss-20b":
|
|
58
61
|
return GptOssRenderer(tokenizer)
|
|
59
62
|
raise ValueError(f"unsupported demo model: {model}")
|
|
@@ -63,8 +66,9 @@ def print_parsed(label: str, turn: str, parsed) -> None:
|
|
|
63
66
|
print(f"\n[{label}] {turn}")
|
|
64
67
|
if parsed.reasoning_content:
|
|
65
68
|
print(f"reasoning: {parsed.reasoning_content[:240]}")
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
for tc in parsed.tool_calls:
|
|
70
|
+
# ``parse_response`` returns ``ParsedToolCall`` dataclasses, not dicts.
|
|
71
|
+
print(f"tool_call: {tc.name}({tc.arguments}) [{tc.status.value}]")
|
|
68
72
|
if parsed.content:
|
|
69
73
|
print(f"content: {parsed.content}")
|
|
70
74
|
|
|
@@ -131,21 +135,33 @@ async def main() -> None:
|
|
|
131
135
|
if parsed1.reasoning_content:
|
|
132
136
|
assistant["reasoning_content"] = parsed1.reasoning_content
|
|
133
137
|
if parsed1.tool_calls:
|
|
134
|
-
|
|
138
|
+
# Convert the parsed dataclasses back to OpenAI-format tool_calls.
|
|
139
|
+
assistant["tool_calls"] = [
|
|
140
|
+
{
|
|
141
|
+
"id": tc.id or f"call_{idx}",
|
|
142
|
+
"type": "function",
|
|
143
|
+
"function": {
|
|
144
|
+
"name": tc.name,
|
|
145
|
+
"arguments": tc.arguments
|
|
146
|
+
if isinstance(tc.arguments, str)
|
|
147
|
+
else json.dumps(tc.arguments),
|
|
148
|
+
},
|
|
149
|
+
}
|
|
150
|
+
for idx, tc in enumerate(parsed1.tool_calls)
|
|
151
|
+
]
|
|
135
152
|
messages.append(assistant)
|
|
136
153
|
|
|
137
154
|
if parsed1.tool_calls:
|
|
138
155
|
new_messages = []
|
|
139
156
|
for idx, tool_call in enumerate(parsed1.tool_calls):
|
|
140
|
-
|
|
141
|
-
tool_args = fn.get("arguments") or {}
|
|
157
|
+
tool_args = tool_call.arguments or {}
|
|
142
158
|
if isinstance(tool_args, str):
|
|
143
159
|
tool_args = json.loads(tool_args)
|
|
144
160
|
new_messages.append(
|
|
145
161
|
{
|
|
146
162
|
"role": "tool",
|
|
147
|
-
"tool_call_id": tool_call.
|
|
148
|
-
"name":
|
|
163
|
+
"tool_call_id": tool_call.id or f"call_{idx}",
|
|
164
|
+
"name": tool_call.name or "multiply",
|
|
149
165
|
"content": json.dumps(
|
|
150
166
|
{"result": int(tool_args["a"]) * int(tool_args["b"])}
|
|
151
167
|
),
|
|
@@ -157,11 +173,14 @@ async def main() -> None:
|
|
|
157
173
|
]
|
|
158
174
|
|
|
159
175
|
# Turn 2: bridge extends prompt_ids + completion1 exactly.
|
|
160
|
-
|
|
176
|
+
# ``bridge_to_next_turn`` returns a ``RenderedTokens`` (or None); the
|
|
177
|
+
# extended id stream is on ``.token_ids``.
|
|
178
|
+
bridged = renderer.bridge_to_next_turn(
|
|
161
179
|
prompt_ids, completion1, new_messages, tools=TOOLS
|
|
162
180
|
)
|
|
163
|
-
if
|
|
181
|
+
if bridged is None:
|
|
164
182
|
raise RuntimeError("bridge_to_next_turn returned None")
|
|
183
|
+
bridged_ids = bridged.token_ids
|
|
165
184
|
assert bridged_ids[: len(prompt_ids) + len(completion1)] == (
|
|
166
185
|
prompt_ids + completion1
|
|
167
186
|
)
|
|
@@ -26,6 +26,7 @@ import os
|
|
|
26
26
|
import torch
|
|
27
27
|
from transformers import AutoModelForCausalLM, AutoTokenizer
|
|
28
28
|
|
|
29
|
+
from renderers.configs import Qwen35RendererConfig
|
|
29
30
|
from renderers.gpt_oss import GptOssRenderer
|
|
30
31
|
from renderers.qwen35 import Qwen35Renderer
|
|
31
32
|
|
|
@@ -55,7 +56,8 @@ TOOLS = [
|
|
|
55
56
|
def make_renderer(model: str, enable_thinking: bool | None):
|
|
56
57
|
tokenizer = AutoTokenizer.from_pretrained(model, trust_remote_code=False)
|
|
57
58
|
if model.startswith("Qwen/Qwen3.5-"):
|
|
58
|
-
|
|
59
|
+
config = Qwen35RendererConfig(enable_thinking=enable_thinking)
|
|
60
|
+
return Qwen35Renderer(tokenizer, config), tokenizer
|
|
59
61
|
if model == "openai/gpt-oss-20b":
|
|
60
62
|
return GptOssRenderer(tokenizer), tokenizer
|
|
61
63
|
raise ValueError(f"unsupported demo model: {model}")
|
|
@@ -65,8 +67,9 @@ def print_parsed(label: str, turn: str, parsed) -> None:
|
|
|
65
67
|
print(f"\n[{label}] {turn}")
|
|
66
68
|
if parsed.reasoning_content:
|
|
67
69
|
print(f"reasoning: {parsed.reasoning_content[:240]}")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
for tc in parsed.tool_calls:
|
|
71
|
+
# ``parse_response`` returns ``ParsedToolCall`` dataclasses, not dicts.
|
|
72
|
+
print(f"tool_call: {tc.name}({tc.arguments}) [{tc.status.value}]")
|
|
70
73
|
if parsed.content:
|
|
71
74
|
print(f"content: {parsed.content}")
|
|
72
75
|
|
|
@@ -139,21 +142,33 @@ def main() -> None:
|
|
|
139
142
|
if parsed1.reasoning_content:
|
|
140
143
|
assistant["reasoning_content"] = parsed1.reasoning_content
|
|
141
144
|
if parsed1.tool_calls:
|
|
142
|
-
|
|
145
|
+
# Convert the parsed dataclasses back to OpenAI-format tool_calls.
|
|
146
|
+
assistant["tool_calls"] = [
|
|
147
|
+
{
|
|
148
|
+
"id": tc.id or f"call_{idx}",
|
|
149
|
+
"type": "function",
|
|
150
|
+
"function": {
|
|
151
|
+
"name": tc.name,
|
|
152
|
+
"arguments": tc.arguments
|
|
153
|
+
if isinstance(tc.arguments, str)
|
|
154
|
+
else json.dumps(tc.arguments),
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
for idx, tc in enumerate(parsed1.tool_calls)
|
|
158
|
+
]
|
|
143
159
|
messages.append(assistant)
|
|
144
160
|
|
|
145
161
|
if parsed1.tool_calls:
|
|
146
162
|
new_messages = []
|
|
147
163
|
for idx, tool_call in enumerate(parsed1.tool_calls):
|
|
148
|
-
|
|
149
|
-
tool_args = fn.get("arguments") or {}
|
|
164
|
+
tool_args = tool_call.arguments or {}
|
|
150
165
|
if isinstance(tool_args, str):
|
|
151
166
|
tool_args = json.loads(tool_args)
|
|
152
167
|
new_messages.append(
|
|
153
168
|
{
|
|
154
169
|
"role": "tool",
|
|
155
|
-
"tool_call_id": tool_call.
|
|
156
|
-
"name":
|
|
170
|
+
"tool_call_id": tool_call.id or f"call_{idx}",
|
|
171
|
+
"name": tool_call.name or "multiply",
|
|
157
172
|
"content": json.dumps(
|
|
158
173
|
{"result": int(tool_args["a"]) * int(tool_args["b"])}
|
|
159
174
|
),
|
|
@@ -165,11 +180,14 @@ def main() -> None:
|
|
|
165
180
|
]
|
|
166
181
|
|
|
167
182
|
# Turn 2: bridge extends prompt_ids + completion1 exactly.
|
|
168
|
-
|
|
183
|
+
# ``bridge_to_next_turn`` returns a ``RenderedTokens`` (or None); the
|
|
184
|
+
# extended id stream is on ``.token_ids``.
|
|
185
|
+
bridged = renderer.bridge_to_next_turn(
|
|
169
186
|
prompt_ids, completion1, new_messages, tools=TOOLS
|
|
170
187
|
)
|
|
171
|
-
if
|
|
188
|
+
if bridged is None:
|
|
172
189
|
raise RuntimeError("bridge_to_next_turn returned None")
|
|
190
|
+
bridged_ids = bridged.token_ids
|
|
173
191
|
assert bridged_ids[: len(prompt_ids) + len(completion1)] == (
|
|
174
192
|
prompt_ids + completion1
|
|
175
193
|
)
|
|
@@ -21,6 +21,7 @@ import gc
|
|
|
21
21
|
import json
|
|
22
22
|
import os
|
|
23
23
|
|
|
24
|
+
from renderers.configs import Qwen35RendererConfig
|
|
24
25
|
from renderers.gpt_oss import GptOssRenderer
|
|
25
26
|
from renderers.qwen35 import Qwen35Renderer
|
|
26
27
|
from transformers import AutoTokenizer
|
|
@@ -52,7 +53,9 @@ TOOLS = [
|
|
|
52
53
|
def make_renderer(model: str, enable_thinking: bool | None):
|
|
53
54
|
tokenizer = AutoTokenizer.from_pretrained(model, trust_remote_code=False)
|
|
54
55
|
if model.startswith("Qwen/Qwen3.5-"):
|
|
55
|
-
return Qwen35Renderer(
|
|
56
|
+
return Qwen35Renderer(
|
|
57
|
+
tokenizer, Qwen35RendererConfig(enable_thinking=enable_thinking)
|
|
58
|
+
)
|
|
56
59
|
if model == "openai/gpt-oss-20b":
|
|
57
60
|
return GptOssRenderer(tokenizer)
|
|
58
61
|
raise ValueError(f"unsupported demo model: {model}")
|
|
@@ -62,8 +65,9 @@ def print_parsed(label: str, turn: str, parsed) -> None:
|
|
|
62
65
|
print(f"\n[{label}] {turn}")
|
|
63
66
|
if parsed.reasoning_content:
|
|
64
67
|
print(f"reasoning: {parsed.reasoning_content[:240]}")
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
for tc in parsed.tool_calls:
|
|
69
|
+
# ``parse_response`` returns ``ParsedToolCall`` dataclasses, not dicts.
|
|
70
|
+
print(f"tool_call: {tc.name}({tc.arguments}) [{tc.status.value}]")
|
|
67
71
|
if parsed.content:
|
|
68
72
|
print(f"content: {parsed.content}")
|
|
69
73
|
|
|
@@ -134,21 +138,33 @@ def main() -> None:
|
|
|
134
138
|
if parsed1.reasoning_content:
|
|
135
139
|
assistant["reasoning_content"] = parsed1.reasoning_content
|
|
136
140
|
if parsed1.tool_calls:
|
|
137
|
-
|
|
141
|
+
# Convert the parsed dataclasses back to OpenAI-format tool_calls.
|
|
142
|
+
assistant["tool_calls"] = [
|
|
143
|
+
{
|
|
144
|
+
"id": tc.id or f"call_{idx}",
|
|
145
|
+
"type": "function",
|
|
146
|
+
"function": {
|
|
147
|
+
"name": tc.name,
|
|
148
|
+
"arguments": tc.arguments
|
|
149
|
+
if isinstance(tc.arguments, str)
|
|
150
|
+
else json.dumps(tc.arguments),
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
for idx, tc in enumerate(parsed1.tool_calls)
|
|
154
|
+
]
|
|
138
155
|
messages.append(assistant)
|
|
139
156
|
|
|
140
157
|
if parsed1.tool_calls:
|
|
141
158
|
new_messages = []
|
|
142
159
|
for idx, tool_call in enumerate(parsed1.tool_calls):
|
|
143
|
-
|
|
144
|
-
tool_args = fn.get("arguments") or {}
|
|
160
|
+
tool_args = tool_call.arguments or {}
|
|
145
161
|
if isinstance(tool_args, str):
|
|
146
162
|
tool_args = json.loads(tool_args)
|
|
147
163
|
new_messages.append(
|
|
148
164
|
{
|
|
149
165
|
"role": "tool",
|
|
150
|
-
"tool_call_id": tool_call.
|
|
151
|
-
"name":
|
|
166
|
+
"tool_call_id": tool_call.id or f"call_{idx}",
|
|
167
|
+
"name": tool_call.name or "multiply",
|
|
152
168
|
"content": json.dumps(
|
|
153
169
|
{"result": int(tool_args["a"]) * int(tool_args["b"])}
|
|
154
170
|
),
|
|
@@ -160,11 +176,14 @@ def main() -> None:
|
|
|
160
176
|
]
|
|
161
177
|
|
|
162
178
|
# Turn 2: bridge extends prompt_ids + completion1 exactly.
|
|
163
|
-
|
|
179
|
+
# ``bridge_to_next_turn`` returns a ``RenderedTokens`` (or None); the
|
|
180
|
+
# extended id stream is on ``.token_ids``.
|
|
181
|
+
bridged = renderer.bridge_to_next_turn(
|
|
164
182
|
prompt_ids, completion1, new_messages, tools=TOOLS
|
|
165
183
|
)
|
|
166
|
-
if
|
|
184
|
+
if bridged is None:
|
|
167
185
|
raise RuntimeError("bridge_to_next_turn returned None")
|
|
186
|
+
bridged_ids = bridged.token_ids
|
|
168
187
|
assert bridged_ids[: len(prompt_ids) + len(completion1)] == (
|
|
169
188
|
prompt_ids + completion1
|
|
170
189
|
)
|
|
@@ -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.dev33'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 8, 'dev33')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
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
|