-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Add GLM-4-MoE tool calling support #5463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 23 commits
4b3aa51
0894910
730070b
4622d77
08d4c51
276559d
673c35d
604c476
83a7ef6
0f28384
e5d7cdf
a618809
a0b81b1
2384da5
14ac6a7
0b85443
c9dfa1f
e06d88c
303eac5
87e23ed
d8985b2
dff9615
c31e258
3fc2ca8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -186,11 +186,47 @@ def clone_chat_template( | |
| }, | ||
| } | ||
|
|
||
| glm4moe_schema = { | ||
| "x-regex": r"^(?:\n?<think>\n?(?:(?P<reasoning_content>.*?\S.*?)\n?|[\s]*)</think>\s*)?(?P<content>.*?)(?:\n(?=<tool_call>))?(?=(?:<tool_call>|$))(?P<tool_calls>(?:<tool_call>.+?</tool_call>\s*)+)?$", | ||
| "type": "object", | ||
| "properties": { | ||
| "role": {"const": "assistant"}, | ||
| "content": {"type": "string"}, | ||
| "reasoning_content": {"type": "string"}, | ||
| "tool_calls": { | ||
| "type": "array", | ||
| "x-regex-iterator": r"<tool_call>\s*(.+?)\s*</tool_call>", | ||
| "items": { | ||
| "type": "object", | ||
| "properties": { | ||
| "type": {"const": "function"}, | ||
| "function": { | ||
| "type": "object", | ||
| "properties": { | ||
| "name": {"type": "string", "x-regex": r"^(\S+)"}, | ||
| "arguments": { | ||
| "type": "object", | ||
| "x-regex-key-value": r"<arg_key>(?P<key>[^<]+)</arg_key>\s*\n<arg_value>(?P<value>.*?)</arg_value>", | ||
| "default": {}, | ||
| "additionalProperties": { | ||
| "x-parser": "json", | ||
| "x-parser-args": {"allow_non_json": True}, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| glm4moe_chat_template = (_CHAT_TEMPLATES_DIR / "glm4moe.jinja").read_text() | ||
|
|
||
| qwen3_chat_template = (_CHAT_TEMPLATES_DIR / "qwen3.jinja").read_text() | ||
|
|
||
| qwen3_5_chat_template_2b_and_below = (_CHAT_TEMPLATES_DIR / "qwen3_5_2b_and_below.jinja").read_text() | ||
|
|
||
|
|
||
| qwen3_5_chat_template_4b_and_above = (_CHAT_TEMPLATES_DIR / "qwen3_5_4b_and_above.jinja").read_text() | ||
|
|
||
|
|
||
|
|
@@ -223,6 +259,9 @@ def add_response_schema(tokenizer: PreTrainedTokenizer) -> PreTrainedTokenizer: | |
| {'role': 'assistant', 'content': '', 'tool_calls': [{'type': 'function', 'function': {'name': 'multiply', 'arguments': {'a': 3, 'b': 4}}}]} | ||
| ``` | ||
| """ | ||
| if tokenizer.chat_template == glm4moe_chat_template: | ||
| tokenizer.response_schema = glm4moe_schema | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing return statement in GLM-4-MoE branchHigh Severity The Reviewed by Cursor Bugbot for commit 3fc2ca8. Configure here. |
||
| return tokenizer | ||
| if tokenizer.chat_template == qwen3_chat_template: | ||
| tokenizer.response_schema = qwen3_schema | ||
| return tokenizer | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| [gMASK]<sop> | ||
| {%- if tools -%} | ||
| <|system|> | ||
| # Tools | ||
|
|
||
| You may call one or more functions to assist with the user query. | ||
|
|
||
| You are provided with function signatures within <tools></tools> XML tags: | ||
| <tools> | ||
| {% for tool in tools %} | ||
| {{ tool | tojson(ensure_ascii=False) }} | ||
| {% endfor %} | ||
| </tools> | ||
|
|
||
| For each function call, output the function name and arguments within the following XML format: | ||
| <tool_call>{function-name} | ||
| <arg_key>{arg-key-1}</arg_key> | ||
| <arg_value>{arg-value-1}</arg_value> | ||
| <arg_key>{arg-key-2}</arg_key> | ||
| <arg_value>{arg-value-2}</arg_value> | ||
| ... | ||
| </tool_call>{%- endif -%} | ||
| {%- macro visible_text(content) -%} | ||
| {%- if content is string -%} | ||
| {{- content }} | ||
| {%- elif content is iterable and content is not mapping -%} | ||
| {%- for item in content -%} | ||
| {%- if item is mapping and item.type == 'text' -%} | ||
| {{- item.text }} | ||
| {%- elif item is string -%} | ||
| {{- item }} | ||
| {%- endif -%} | ||
| {%- endfor -%} | ||
| {%- else -%} | ||
| {{- content }} | ||
| {%- endif -%} | ||
| {%- endmacro -%} | ||
| {%- set ns = namespace(last_user_index=-1) %} | ||
| {%- for m in messages %} | ||
| {%- if m.role == 'user' %} | ||
| {% set ns.last_user_index = loop.index0 -%} | ||
| {%- endif %} | ||
| {%- endfor %} | ||
| {% for m in messages %} | ||
| {%- if m.role == 'user' -%}<|user|> | ||
| {{ visible_text(m.content) }} | ||
| {{- '/nothink' if (enable_thinking is defined and not enable_thinking and not visible_text(m.content).endswith("/nothink")) else '' -}} | ||
| {%- elif m.role == 'assistant' -%} | ||
| <|assistant|> | ||
| {%- set reasoning_content = '' %} | ||
| {%- set content = visible_text(m.content) %} | ||
| {%- if m.reasoning_content is string %} | ||
| {%- set reasoning_content = m.reasoning_content %} | ||
| {%- else %} | ||
| {%- if '</think>' in content %} | ||
| {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %} | ||
| {%- set content = content.split('</think>')[-1].lstrip('\n') %} | ||
| {%- endif %} | ||
| {%- endif %} | ||
| {%- if loop.index0 > ns.last_user_index and reasoning_content -%} | ||
| {{ '\n<think>' + reasoning_content.strip() + '</think>'}} | ||
| {%- else -%} | ||
| {{ '\n<think></think>' }} | ||
| {%- endif -%} | ||
| {%- if content.strip() -%} | ||
| {{ '\n' + content.strip() }} | ||
| {%- endif -%} | ||
| {% if m.tool_calls %} | ||
| {% for tc in m.tool_calls %} | ||
| {%- if tc.function %} | ||
| {%- set tc = tc.function %} | ||
| {%- endif %} | ||
| {{ '\n<tool_call>' + tc.name }} | ||
| {% set _args = tc.arguments %} | ||
| {% for k, v in _args.items() %} | ||
| <arg_key>{{ k }}</arg_key> | ||
| <arg_value>{{ v | tojson(ensure_ascii=False) if v is not string else v }}</arg_value> | ||
| {% endfor %} | ||
| </tool_call>{% endfor %} | ||
| {% endif %} | ||
| {%- elif m.role == 'tool' -%} | ||
| {%- if m.content is string -%} | ||
| {%- if loop.first or (messages[loop.index0 - 1].role != "tool") %} | ||
| {{- '<|observation|>' }} | ||
| {%- endif %} | ||
| {{- '\n<tool_response>\n' }} | ||
| {{- m.content }} | ||
| {{- '\n</tool_response>' }} | ||
| {%- else -%} | ||
| <|observation|>{% for tr in m.content %} | ||
|
|
||
| <tool_response> | ||
| {{ tr.output if tr.output is defined else tr }} | ||
| </tool_response>{% endfor -%} | ||
| {% endif -%} | ||
| {%- elif m.role == 'system' -%} | ||
| <|system|> | ||
| {{ visible_text(m.content) }} | ||
| {%- endif -%} | ||
| {%- endfor -%} | ||
| {%- if add_generation_prompt -%} | ||
| <|assistant|>{{- '\n<think></think>' if (enable_thinking is defined and not enable_thinking) else '' -}} | ||
| {%- endif -%} |


Uh oh!
There was an error while loading. Please reload this page.