2626import torch
2727from transformers import AutoModelForCausalLM , AutoTokenizer
2828
29+ from renderers .configs import Qwen35RendererConfig
2930from renderers .gpt_oss import GptOssRenderer
3031from renderers .qwen35 import Qwen35Renderer
3132
5556def make_renderer (model : str , enable_thinking : bool | None ):
5657 tokenizer = AutoTokenizer .from_pretrained (model , trust_remote_code = False )
5758 if model .startswith ("Qwen/Qwen3.5-" ):
58- return Qwen35Renderer (tokenizer , enable_thinking = enable_thinking ), tokenizer
59+ config = Qwen35RendererConfig (enable_thinking = enable_thinking )
60+ return Qwen35Renderer (tokenizer , config ), tokenizer
5961 if model == "openai/gpt-oss-20b" :
6062 return GptOssRenderer (tokenizer ), tokenizer
6163 raise ValueError (f"unsupported demo model: { model } " )
@@ -65,8 +67,9 @@ def print_parsed(label: str, turn: str, parsed) -> None:
6567 print (f"\n [{ label } ] { turn } " )
6668 if parsed .reasoning_content :
6769 print (f"reasoning: { parsed .reasoning_content [:240 ]} " )
68- if parsed .tool_calls :
69- print (f"tool_calls: { json .dumps (parsed .tool_calls , ensure_ascii = False )} " )
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 } ]" )
7073 if parsed .content :
7174 print (f"content: { parsed .content } " )
7275
@@ -139,21 +142,33 @@ def main() -> None:
139142 if parsed1 .reasoning_content :
140143 assistant ["reasoning_content" ] = parsed1 .reasoning_content
141144 if parsed1 .tool_calls :
142- assistant ["tool_calls" ] = parsed1 .tool_calls
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+ ]
143159 messages .append (assistant )
144160
145161 if parsed1 .tool_calls :
146162 new_messages = []
147163 for idx , tool_call in enumerate (parsed1 .tool_calls ):
148- fn = tool_call .get ("function" ) or tool_call
149- tool_args = fn .get ("arguments" ) or {}
164+ tool_args = tool_call .arguments or {}
150165 if isinstance (tool_args , str ):
151166 tool_args = json .loads (tool_args )
152167 new_messages .append (
153168 {
154169 "role" : "tool" ,
155- "tool_call_id" : tool_call .get ( "id" , f"call_{ idx } " ) ,
156- "name" : fn . get ( " name" , "multiply" ) ,
170+ "tool_call_id" : tool_call .id or f"call_{ idx } " ,
171+ "name" : tool_call . name or "multiply" ,
157172 "content" : json .dumps (
158173 {"result" : int (tool_args ["a" ]) * int (tool_args ["b" ])}
159174 ),
@@ -165,11 +180,14 @@ def main() -> None:
165180 ]
166181
167182 # Turn 2: bridge extends prompt_ids + completion1 exactly.
168- bridged_ids = renderer .bridge_to_next_turn (
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 (
169186 prompt_ids , completion1 , new_messages , tools = TOOLS
170187 )
171- if bridged_ids is None :
188+ if bridged is None :
172189 raise RuntimeError ("bridge_to_next_turn returned None" )
190+ bridged_ids = bridged .token_ids
173191 assert bridged_ids [: len (prompt_ids ) + len (completion1 )] == (
174192 prompt_ids + completion1
175193 )
0 commit comments