{"record":{"id":"498ac4a7be5545a7","repo":"hiyouga/LlamaFactory","slug":"glm-4-does-not-support-parallel-functions","errorCode":null,"errorMessage":"GLM-4 does not support parallel functions.","messagePattern":"GLM-4 does not support parallel functions\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/llamafactory/data/tool_utils.py","lineNumber":392,"sourceCode":"    r\"\"\"GLM-4 tool using template.\"\"\"\n\n    @override\n    @staticmethod\n    def tool_formatter(tools: list[dict[str, Any]]) -> str:\n        tool_text = \"\"\n        for tool in tools:\n            tool = tool.get(\"function\", \"\") if tool.get(\"type\") == \"function\" else tool\n            tool_text += \"\\n\\n## {name}\\n\\n{body}\\n在调用上述函数时，请使用 Json 格式表示调用的参数。\".format(\n                name=tool[\"name\"], body=json.dumps(tool, indent=4, ensure_ascii=False)\n            )\n\n        return GLM4_TOOL_PROMPT.format(tool_text=tool_text)\n\n    @override\n    @staticmethod\n    def function_formatter(functions: list[\"FunctionCall\"]) -> str:\n        if len(functions) > 1:\n            raise ValueError(\"GLM-4 does not support parallel functions.\")\n\n        return f\"{functions[0].name}\\n{functions[0].arguments}\"\n\n    @override\n    @staticmethod\n    def tool_extractor(content: str) -> Union[str, list[\"FunctionCall\"]]:\n        if \"\\n\" not in content:\n            return content\n\n        tool_name, tool_input = content.split(\"\\n\", maxsplit=1)\n        try:\n            arguments = json.loads(tool_input.strip())\n        except json.JSONDecodeError:\n            return content\n\n        return [FunctionCall(tool_name, json.dumps(arguments, ensure_ascii=False))]\n\n","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/data/tool_utils.py#L374-L410","documentation":"GLM-4's function formatter renders exactly one function call per assistant message (its chat format has no separator for multiple parallel calls). If the parsed FunctionCall list contains more than one entry, GLM4ToolUtils.function_formatter raises ValueError before the text is serialized into training data.","triggerScenarios":"Using tool_format: glm4 with a dataset whose assistant messages contain multiple tool calls in one turn (e.g. [{'name': 'f1', ...}, {'name': 'f2', ...}] after JSON parsing); data converted from OpenAI parallel-function-call format; model outputs with several calls pasted as training targets.","commonSituations":"Tool-calling datasets generated from GPT-4 traces that use parallel calls; switching tool_format from qwen (supports multiple calls) to glm4 without cleaning data.","solutions":["Split multi-call assistant turns into separate turns, or keep only the first call when preparing data for GLM-4.","Switch tool_format to one that supports parallel calls (e.g. qwen) if your model/format allows it.","Pre-scan the dataset for len(functions) > 1 and clean those samples (see validationCode)."],"exampleFix":"// before\n{\"from\": \"gpt\", \"value\": \"[{\\\"name\\\": \\\"search\\\", ...}, {\\\"name\\\": \\\"weather\\\", ...}]\"}\n\n// after\n{\"from\": \"gpt\", \"value\": \"[{\\\"name\\\": \\\"search\\\", ...}]\"}","handlingStrategy":"validation","validationCode":"import json\n\nfor sample in dataset:\n    for turn in sample[\"conversations\"]:\n        if turn[\"from\"] == \"gpt\":\n            try:\n                calls = json.loads(turn[\"value\"])\n            except (json.JSONDecodeError, TypeError):\n                continue\n            if isinstance(calls, list) and len(calls) > 1:\n                raise ValueError(f\"parallel tool calls unsupported by glm4: {sample}\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use glm4 tool_format only on single-call-per-turn data; split or filter parallel calls first.","Prefer qwen tool_format for datasets with parallel function calls."],"tags":["tools","glm4","data"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}