{"record":{"id":"1ac5e7e61b9ea7ee","repo":"microsoft/autogen","slug":"unexpected-tool-call-type-from-llamacpp-model","errorCode":null,"errorMessage":"Unexpected tool call type from LlamaCpp model.","messagePattern":"Unexpected tool call type from LlamaCpp model\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-ext/src/autogen_ext/models/llama_cpp/_llama_cpp_completion_client.py","lineNumber":353,"sourceCode":"        self._total_usage[\"prompt_tokens\"] += response[\"usage\"][\"prompt_tokens\"]\n        self._total_usage[\"completion_tokens\"] += response[\"usage\"][\"completion_tokens\"]\n\n        # Parse the response\n        response_tool_calls: ChatCompletionTool | None = None\n        response_text: str | None = None\n        if \"choices\" in response and len(response[\"choices\"]) > 0:\n            if \"message\" in response[\"choices\"][0]:\n                response_text = response[\"choices\"][0][\"message\"][\"content\"]\n            if \"tool_calls\" in response[\"choices\"][0]:\n                response_tool_calls = response[\"choices\"][0][\"tool_calls\"]  # type: ignore\n\n        content: List[FunctionCall] | str = \"\"\n        thought: str | None = None\n        if response_tool_calls:\n            content = []\n            for tool_call in response_tool_calls:\n                if not isinstance(tool_call, dict):\n                    raise ValueError(\"Unexpected tool call type from LlamaCpp model.\")\n                content.append(\n                    FunctionCall(\n                        id=tool_call[\"id\"],\n                        arguments=tool_call[\"function\"][\"arguments\"],\n                        name=normalize_name(tool_call[\"function\"][\"name\"]),\n                    )\n                )\n            if response_text and len(response_text) > 0:\n                thought = response_text\n        else:\n            if response_text:\n                content = response_text\n\n        # Detect tool usage in the response\n        if not response_tool_calls and not response_text:\n            logger.debug(\"DEBUG: No response text found. Returning empty response.\")\n            return CreateResult(\n                content=\"\", usage=RequestUsage(prompt_tokens=0, completion_tokens=0), finish_reason=\"stop\", cached=False","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-ext/src/autogen_ext/models/llama_cpp/_llama_cpp_completion_client.py#L335-L371","documentation":"When the response contains tool_calls, each element of that list must be a dict with 'id' and 'function'->{'arguments','name'} keys. If llama-cpp-python returns tool calls as objects (or any non-dict), the per-call isinstance check raises this ValueError. Like error 804 it indicates the installed llama-cpp-python does not match the response shape this client parses.","triggerScenarios":"A model emits tool_calls and the installed llama-cpp-python serializes them as objects/namedtuples instead of dicts; a mocked Llama returning string-encoded tool calls; partial upgrade where the wheel's chat-completion schema changed.","commonSituations":"Upgrading llama-cpp-python independently of autogen-ext (or vice versa); running tool-calling tests against a hand-rolled fake that returns JSON strings; GPU/CPU wheels from different builds mixed in one environment.","solutions":["Align versions: reinstall a llama-cpp-python release that autogen-ext was tested with (check the package's dependency pins)","If the model should not emit tool calls, drop tools from the create() call or use tool_choice='none'","In tests, return tool calls as dicts: [{'id': '1', 'function': {'name': 'f', 'arguments': '{}'}}]"],"exampleFix":"# test fake fix\n# before\nfake.create_chat_completion.return_value = {\"choices\": [{\"message\": {\"tool_calls\": [\"call(foo)\"]}}]}\n# after\nfake.create_chat_completion.return_value = {\"choices\": [{\"message\": {\"tool_calls\": [{\"id\": \"1\", \"function\": {\"name\": \"foo\", \"arguments\": \"{}\"}}]}}]}","handlingStrategy":"fallback","validationCode":"if response_tool_calls and not all(isinstance(tc, dict) for tc in response_tool_calls):\n    # only possible in a patched client; re-normalize if you control the wrapper\n    response_tool_calls = [tc if isinstance(tc, dict) else tc.model_dump() for tc in response_tool_calls]","typeGuard":"def is_dict_tool_call(tc: object) -> TypeGuard[dict]:\n    return isinstance(tc, dict) and \"id\" in tc and isinstance(tc.get(\"function\"), dict)","tryCatchPattern":"try:\n    result = await client.create(messages, tools=tools)\nexcept ValueError as e:\n    if \"Unexpected tool call type\" in str(e):\n        # version mismatch: retry once without tools so text output still completes\n        result = await client.create(messages, tool_choice=\"none\")\n    else:\n        raise","preventionTips":["Keep llama-cpp-python and autogen-ext versions in lockstep (upgrade together)","Smoke-test one tool-calling round after any llama-cpp-python upgrade","Return dict-shaped tool calls from test doubles"],"tags":["llama-cpp","tool-calling","version-compatibility","response-parsing"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}