Skip to content

LiteLLM + DeepSeek-V3 multi-tool calling fails: tool call parsing error #5024

Closed

Description

@yfzhang1214

When using LiteLLM to proxy DeepSeek-V3 model with multiple tool calls, the first tool call consistently fails to parse. The observed behavior is:

  • If the first tool call succeeds, all subsequent tool calls execute normally

Environment

  • Model: DeepSeek-V3 (via LiteLLM proxy)
  • Framework: Google ADK (version: [1.22.1])
  • LiteLLM version: [1.82.1]

Error Output

raw event: model_version='deepseek-v3' content=Content(
  parts=[
    Part(
      text=""" <|tool▁calls▁begin|><|tool▁call▁begin|>function<|tool▁sep|>analysis_input
```json
{"work_dir_name":"bf01a09edc0f4c4ca376bc03d153445b"}
```<|tool▁call▁end|><|tool▁calls▁end|>"""
    ),
  ],
  role='model'
) grounding_metadata=None partial=False turn_complete=None finish_reason=<FinishReason.STOP: 'STOP'> error_code=None error_message=None interrupted=None custom_metadata=None usage_metadata=GenerateContentResponseUsageMetadata(
  cached_content_token_count=0,
  candidates_token_count=39,
  prompt_token_count=1003,
  total_token_count=1042
) live_session_resumption_update=None input_transcription=None output_transcription=None avg_logprobs=None logprobs_result=None cache_metadata=None citation_metadata=None interaction_id=None invocation_id='e-bb165836-aee3-404e-9ef0-ef499f6f997e' author='brain_agent' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}, requested_tool_confirmations={}, compaction=None, end_of_agent=None, agent_state=None, rewind_before_invocation_id=None) long_running_tool_ids=None branch=None id='4824cba0-6653-4d41-b0e6-6343bc856434' timestamp=1774580915.381178

This error doesn't seem to be triggered every time; sometimes it displays normally:

Part(
      function_call=FunctionCall(
        args={
          'work_dir_name': '84a652c678564995be15e5759404d7b3'
        },
        id='646ee4f52df44e58a70d99ab4313b0f8',
        name='analysis_input'
      )

Activity

  1. added
    models[Component] This issue is related to model support
    on Mar 27, 2026
  2. added theissue type on Mar 27, 2026
  3. yfzhang1214 commented on Mar 27, 2026

    @yfzhang1214
    Author

    run script:

    async for event in runner.run_async(
                user_id=user_id,
                session_id=session.id,
                new_message=content,
                run_config=RunConfig(save_input_blobs_as_artifacts=False),
            ):
                print("\n================ EVENT ================") 
                print("raw event:", event)
                if event.is_final_response() and event.content and event.content.parts:
                    for part in event.content.parts:
                        if getattr(part, "text", None):
                            parts.append(part.text)
                    final_usage = getattr(event, "usage_metadata", None)
                    print("prompt_tokens:", final_usage.prompt_token_count) 
                    print("completion_tokens:", final_usage.candidates_token_count) 
                    print("total_tokens:", final_usage.total_token_count)
    
  4. added 2 commits that reference this issue on May 10, 2026
    e91b1f6
    08e864e
  5. surajksharma07 commented on Jun 5, 2026

    @surajksharma07
    Collaborator

    /adk-issue-analyze

  6. adk-bot commented on Jun 5, 2026

    @adk-bot
    Collaborator

    GitHub Issue #5024 Analysis: LiteLLM + DeepSeek-V3 multi-tool calling fails: tool call parsing error

    Executive Summary

    1. What is broken? When LiteLLM proxies DeepSeek-V3, the model's tool calls are sometimes emitted as raw text wrapped in DeepSeek's proprietary special tokens (e.g., <|tool_calls_begin|>...) instead of being structured as standard tool_calls by LiteLLM. When this occurs, ADK-python's fallback JSON parser, _parse_tool_calls_from_text, fails to parse them into standard tool calls because the JSON object contains only the function arguments—with the function name situated outside the JSON block. As a result, _build_tool_call_from_json_dict rejects the payload because the "name" key is missing, causing the tool call to be silently dropped.
    2. Is there a linked PR that fixes this issue? Yes, PR fix(litellm): parse DeepSeek-V3 proprietary inline tool-call tokens #5654 - OPEN
    3. Recommendation: Should Fix (High Priority). DeepSeek-V3 is a widely used and high-performance model. Silently dropping tool calls severely impacts standard developer workflows and reliability. Merging or adapting PR fix(litellm): parse DeepSeek-V3 proprietary inline tool-call tokens #5654 will resolve this cleanly.
    Detailed Analysis

    1. Root Cause Analysis ("What is broken?")

    DeepSeek-V3 uses a custom token block to structure custom tool call emissions:

    <|tool_calls_begin|><|tool_call_begin|>function<|tool_sep|>analysis_input
    ```json
    {"work_dir_name":"bf01a09edc0f4c4ca376bc03d153445b"}
    ```<|tool_call_end|><|tool_calls_end|>
    

    When LiteLLM returns this payload in the raw content field without translating it to standard formatted tool_calls, ADK-python delegates parsing to _split_message_content_and_tool_calls, which invokes _parse_tool_calls_from_text:

    • brace_index matches the starting { of the JSON block.
    • _JSON_DECODER.raw_decode successfully extracts the JSON dictionary {"work_dir_name": "bf01a09edc0f4c4ca376bc03d153445b"}.
    • Then, _build_tool_call_from_json_dict expects the JSON dictionary to have "name" and "arguments" keys. Because the JSON block parsed consists purely of the parameters dictionary (with the function name analysis_input parsed outside the JSON brace inside the <|tool_sep|> token), candidate.get("name") evaluates to None.
    • Consequently, _build_tool_call_from_json_dict returns None and the tool call is silently ignored, bubbling up to the user as raw text content instead of executable actions.

    2. Existing Pull Requests ("Is there a linked PR that fixes this issue?")

    • Linked PR: Pull Request fix(litellm): parse DeepSeek-V3 proprietary inline tool-call tokens #5654 - fix(litellm): parse DeepSeek-V3 proprietary inline tool-call tokens (OPEN)
    • PR URL: fix(litellm): parse DeepSeek-V3 proprietary inline tool-call tokens #5654
    • Analysis:
      • The PR author (fuchun1010) implemented _parse_deepseek_tool_calls_from_text which specifically scans for <|tool_calls_begin|> and parses the custom format (handling markdown fences, single/multiple tool calls, and bare JSON).
      • It integrates it as a pre-pass step inside _parse_tool_calls_from_text.
      • The PR adds 8 robust unit-tests covering different permutations (e.g., code-fences, mixed formats, text-only) inside tests/unittests/models/test_litellm.py.
      • It is currently open, green on CI, and awaiting a review from the maintainers.

    3. Recommendation


    Summary of Work Done

  7. added 3 commits that reference this issue on Jun 10, 2026
    6a165b3
    4754c8b
    44ca518
  8. added a commit that references this issue on Jun 30, 2026
    c5b2caa
  9. added a commit that references this issue on Aug 11, 2026
    9fc98de
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

models[Component] This issue is related to model support

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions