Skip to content

DeepSeekChatModel forces tool_choice to be specified to trigger loop tool call #4617

@shenweijiekdel

Description

@shenweijiekdel

Please do a quick search on GitHub issues first, there might be already a duplicate issue for the one you are about to create.
If the bug is trivial, just go ahead and create the issue. Otherwise, please take a few moments and fill in the following sections:

Bug description
When I use DeepSeekChatModel to force a ToolCall call using tool_choice, if the tool definition has returnDirect=false, the completion of the push back will trigger ToolCall again, causing an infinite loop.

Environment
Java Version: 17
Spring AI Version: 1.0.3

Steps to reproduce

  1. Define a tool function and specify returnDirect=false.
  2. Use the DeepSeekChatModel ChatClient to specify ToolChoice as this tool and perform completion.

Expected behavior
Call the function only once and output the text normally

My Suggestion

  • Before

if (this.toolExecutionEligibilityPredicate.isToolExecutionRequired(prompt.getOptions(), response)) {
var toolExecutionResult = this.toolCallingManager.executeToolCalls(prompt, response);
if (toolExecutionResult.returnDirect()) {
// Return tool execution result directly to the client.
return ChatResponse.builder()
.from(response)
.generations(ToolExecutionResult.buildGenerations(toolExecutionResult))
.build();
}
else {
// Send the tool execution result back to the model.
return this.internalCall(new Prompt(toolExecutionResult.conversationHistory(), prompt.getOptions()),
response);
}
}
return response;

  • After

Reset tool_choice to "auto" when push back to model

		if (this.toolExecutionEligibilityPredicate.isToolExecutionRequired(prompt.getOptions(), response)) {
			var toolExecutionResult = this.toolCallingManager.executeToolCalls(prompt, response);
			if (toolExecutionResult.returnDirect()) {
				// Return tool execution result directly to the client.
				return ChatResponse.builder()
					.from(response)
					.generations(ToolExecutionResult.buildGenerations(toolExecutionResult))
					.build();
			}
			else {
							if (prompt.getOptions() instanceof DeepSeekChatOptions) {
								DeepSeekChatOptions options = (DeepSeekChatOptions)prompt.getOptions();
								options.setToolChoice(ChatCompletionRequest.ToolChoiceBuilder.AUTO);
							}

				// Send the tool execution result back to the model.
				return this.internalCall(new Prompt(toolExecutionResult.conversationHistory(), prompt.getOptions()),
						response);
			}
		}

		return response;

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdeepseek

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions