Tool-call parsing: Qwen3 native <tool_call> XML fallback in _parse (parser-agnostic, zero overhead when server converts properly)

This commit is contained in:
sora 2026-08-26 06:29:49 +00:00
parent 7114564301
commit 3d16e9647a

View File

@ -108,9 +108,25 @@ def _key_for(api_base: str) -> str:
def _parse_text_tool_calls(text: str) -> list:
"""Extract [{"name":..,"arguments":{..}}] JSON from a text-protocol reply."""
"""Extract tool calls from a text reply. Handles both shapes:
- JSON array: [{"name":..,"arguments":{..}}]
- Qwen3 native XML: <tool_call>{"name":..,"arguments":{..}}</tool_call>
(for vLLM builds whose qwen3_xml parser doesn't convert to tool_calls)
"""
import re as _re
out = []
for m in _re.finditer(r'<tool_call>\s*(\{.*?\})\s*</tool_call>', text, _re.S):
try:
obj = json.loads(m.group(1))
if isinstance(obj, dict) and obj.get('name'):
out.append({'id': '', 'type': 'function',
'function': {'name': obj['name'],
'arguments': json.dumps(obj.get('arguments', {}))}})
except (ValueError, TypeError):
continue
if out:
return out
candidates = _re.findall(r'\[[\s\S]*?\]', text) or []
for cand in candidates:
try:
@ -196,7 +212,7 @@ class OpenAICompatible(ModelAdapter):
msg = choice.get('message') or {}
calls = []
raw_calls = list(msg.get('tool_calls') or [])
if not raw_calls and self.extra.get('tools_mode') == 'text':
if not raw_calls:
raw_calls = _parse_text_tool_calls(msg.get('content') or '')
for c in raw_calls:
fn = c.get('function') or {}