Skip to content

add tool call support for deepseek-chat、deepseek-reasoner(Only for deepsee… #6236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

xiaohuanxiong3
Copy link

…k official API )

Description

[ add tool call support for deepseek official api . ]

Checklist

  • [] I've read the contributing guide
  • [] The relevant docs, if any, have been updated or created
  • [] The relevant tests, if any, have been updated or created

Screenshots

[ For visual changes, include screenshots. Screen recordings are particularly helpful, and appreciated! ]

Tests

[ What tests were added or updated to ensure the changes work as expected? ]

@xiaohuanxiong3 xiaohuanxiong3 requested a review from a team as a code owner June 21, 2025 05:22
@xiaohuanxiong3 xiaohuanxiong3 requested review from Patrick-Erichsen and removed request for a team June 21, 2025 05:22
Copy link

netlify bot commented Jun 21, 2025

👷 Deploy request for continuedev pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 38342fa

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jun 21, 2025
Copy link

github-actions bot commented Jun 21, 2025

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Copy link

recurseml bot commented Jun 21, 2025

✨ No issues found! Your code is sparkling clean! ✨

@xiaohuanxiong3
Copy link
Author

I have read the CLA Document and I hereby sign the CLA

Copy link
Collaborator

@Patrick-Erichsen Patrick-Erichsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for opening the PR here @xiaohuanxiong3 ! Left a comments on some changes I wasn't sure about.

Comment on lines 295 to 296
// deepseek: deepseekTemplateMessages,
deepseek: null,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of this line of code is to prevent the program from entering the following code branch in BaseLLM.*streamChat

if (this.templateMessages) {
  for await (const chunk of this._streamComplte(
    prompt,
    signal,
    completionOptions,
  )) {
    completion += chunk;
    interaction?.logItem({
      kind: "chunk",
      chunk: chunk,
    });
    yield { role: "assistant", content: chunk };
  }
}

After the program enters this branch, the tool call information will never be returned, and because the deepseek model will stop responding after the tool call ends, the chat will suddenly end in the user interface.

Maybe I should modify the autodetectTemplateType() function in autodetect.ts instead

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I should modify the autodetectTemplateType() function in autodetect.ts instead
I think that seems like the right implementation 👍

Comment on lines 26 to 27
protected useOpenAIAdapterFor: (LlmApiRequestType | "*")[] = [];

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this is my question, I didn't expect 'openaiAdapter' to implement a similar logic to change-3, so change is not needed here

Comment on lines 64 to 167
options: CompletionOptions,
): AsyncGenerator<ChatMessage, any, any> {
const body = this._convertArgs(options, messages);

const response = await this.fetch(this._getEndpoint("chat/completions"), {
method: "POST",
headers: this._getHeaders(),
body: JSON.stringify({
...body,
...this.extraBodyProperties(),
}),
signal,
});

// Handle non-streaming response
if (body.stream === false) {
if (response.status === 499) {
return; // Aborted by user
}
const data = await response.json();
yield data.choices[0].message;
return;
}

let message: AssistantChatMessage | ThinkingChatMessage | undefined;
let myArguments: string | undefined;
let lastMessageRole: "assistant" | "thinking" | undefined;

function fromChatCompletionChunk(chunk: any): ChatMessage | undefined {
const delta = chunk.choices?.[0]?.delta;

if (delta?.content) {
lastMessageRole = "assistant";
return {
role: "assistant",
content: delta.content,
};
} else if (delta?.reasoning_content) {
lastMessageRole = "thinking";
return {
role: "thinking",
content: delta.reasoning_content,
};
} else if (delta?.tool_calls) {
if (!message) {
message = {
role: "assistant",
content: "",
toolCalls: delta?.tool_calls.map((tool_call: any) => ({
id: tool_call.id,
type: tool_call.type,
function: {
name: tool_call.function?.name,
arguments: tool_call.function?.arguments,
},
})),
};
myArguments = "";
return message;
} else {
// @ts-ignore
myArguments += delta?.tool_calls[0].function.arguments;
}
return undefined;
}

if (chunk.choices?.[0]?.finish_reason === "tool_calls") {
if (message) {
message = {
role: message.role,
content: message.content,
toolCalls: [
{
id: message.toolCalls?.[0].id,
type: message.toolCalls?.[0].type,
function: {
name: message.toolCalls?.[0].function?.name,
arguments: myArguments,
},
},
],
};
const tempMessage = message;
message = undefined;
return tempMessage;
} else {
return undefined;
}
} else {
return undefined;
}
}

for await (const value of streamSse(response)) {
const chunk = fromChatCompletionChunk(value);
if (chunk) {
yield chunk;
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we need this when the model is already OpenAI compatible? I believe deepseek-reasoner was working fine without this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologies, this was my mistake. I didn't realize that openaiAdapter already implemented the similar logic, so this change here is also unnecessary. However, because the fromChatCompletionChunk function in openaiTypeConverters.ts doesn't handle the potential ThinkingChatMessage logic, you're right that deepseek-reasoner can work fine without the above changes, but the thinking process of deepseek-reasoner won't be displayed in the chat interface.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ChatCompletionChunk returned by the openaiAdapter.chatCompletionStream function does not have the reasoning_content field corresponding to the thought process returned by deepseek-reasoner. Therefore, if it is not necessary to display the thought process of deepseek-reasoner in the user interface, change-2 and change-3 are unnecessary. If it is necessary to display the thought process of deepseek-reasoner, I looked at the return type ChatCompletionChunk of the *chatCompletionStream function in @continuedev/openai-adapters and it does not have the reasoning_content field, so besides adding change-2 and change-3, I don't have any other solutions for the time being.

@dosubot dosubot bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Jun 25, 2025
@Patrick-Erichsen
Copy link
Collaborator

@xiaohuanxiong3 seems like you rolled back most of the changes, lmk if/when it's ready for another review 👍

@xiaohuanxiong3
Copy link
Author

it's ready

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size:S This PR changes 10-29 lines, ignoring generated files.
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.

2 participants