spring-projects / spring-projects/spring-ai

DeepSeekChatModel forces tool_choice to be specified to trigger loop tool call

Open
#4,617 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug deepseek
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 7h
Merged PRs (30d)
6

Description

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

https://github.com/spring-projects/spring-ai/blob/3fc1ed6750d9521a50abe56a537319681d785d6b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java#L209-L225

  • 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;

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java at lines 209-225, then trace the tool execution and push-back flow. Run the described DeepSeekChatModel reproduction with tool_choice and returnDirect=false. Done means the forced tool is called once and the completion returns text without looping.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
ai, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.