mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
feat: add native tool calling support to OpenAI-compatible providers
- Updated FeatherlessHandler to support tools in DeepSeek-R1 path - Updated ZAiHandler to pass tools and tool_choice in createStream - Updated GroqHandler to handle tool calls in createMessage - All providers now utilize the native tool calling support from BaseOpenAiCompatibleProvider
This commit is contained in:
parent
18c4d1ac41
commit
7a6ee82554
3 changed files with 80 additions and 3 deletions
|
|
@ -31,6 +31,7 @@ export class FeatherlessHandler extends BaseOpenAiCompatibleProvider<Featherless
|
|||
private getCompletionParams(
|
||||
systemPrompt: string,
|
||||
messages: Anthropic.Messages.MessageParam[],
|
||||
metadata?: any,
|
||||
): OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming {
|
||||
const {
|
||||
id: model,
|
||||
|
|
@ -46,15 +47,21 @@ export class FeatherlessHandler extends BaseOpenAiCompatibleProvider<Featherless
|
|||
messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
...(metadata?.tools && { tools: this.convertToolsForOpenAI(metadata.tools) }),
|
||||
...(metadata?.tool_choice && { tool_choice: metadata.tool_choice }),
|
||||
}
|
||||
}
|
||||
|
||||
override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
|
||||
override async *createMessage(
|
||||
systemPrompt: string,
|
||||
messages: Anthropic.Messages.MessageParam[],
|
||||
metadata?: any,
|
||||
): ApiStream {
|
||||
const model = this.getModel()
|
||||
|
||||
if (model.id.includes("DeepSeek-R1")) {
|
||||
const stream = await this.client.chat.completions.create({
|
||||
...this.getCompletionParams(systemPrompt, messages),
|
||||
...this.getCompletionParams(systemPrompt, messages, metadata),
|
||||
messages: convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]),
|
||||
})
|
||||
|
||||
|
|
@ -67,8 +74,11 @@ export class FeatherlessHandler extends BaseOpenAiCompatibleProvider<Featherless
|
|||
}) as const,
|
||||
)
|
||||
|
||||
const toolCallAccumulator = new Map<number, { id: string; name: string; arguments: string }>()
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
const finishReason = chunk.choices[0]?.finish_reason
|
||||
|
||||
if (delta?.content) {
|
||||
for (const processedChunk of matcher.update(delta.content)) {
|
||||
|
|
@ -76,6 +86,37 @@ export class FeatherlessHandler extends BaseOpenAiCompatibleProvider<Featherless
|
|||
}
|
||||
}
|
||||
|
||||
if (delta?.tool_calls) {
|
||||
for (const toolCall of delta.tool_calls) {
|
||||
const index = toolCall.index
|
||||
const existing = toolCallAccumulator.get(index)
|
||||
|
||||
if (existing) {
|
||||
if (toolCall.function?.arguments) {
|
||||
existing.arguments += toolCall.function.arguments
|
||||
}
|
||||
} else {
|
||||
toolCallAccumulator.set(index, {
|
||||
id: toolCall.id || "",
|
||||
name: toolCall.function?.name || "",
|
||||
arguments: toolCall.function?.arguments || "",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (finishReason === "tool_calls") {
|
||||
for (const toolCall of toolCallAccumulator.values()) {
|
||||
yield {
|
||||
type: "tool_call",
|
||||
id: toolCall.id,
|
||||
name: toolCall.name,
|
||||
arguments: toolCall.arguments,
|
||||
}
|
||||
}
|
||||
toolCallAccumulator.clear()
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
yield {
|
||||
type: "usage",
|
||||
|
|
@ -90,7 +131,7 @@ export class FeatherlessHandler extends BaseOpenAiCompatibleProvider<Featherless
|
|||
yield processedChunk
|
||||
}
|
||||
} else {
|
||||
yield* super.createMessage(systemPrompt, messages)
|
||||
yield* super.createMessage(systemPrompt, messages, metadata)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,11 @@ export class GroqHandler extends BaseOpenAiCompatibleProvider<GroqModelId> {
|
|||
): ApiStream {
|
||||
const stream = await this.createStream(systemPrompt, messages, metadata)
|
||||
|
||||
const toolCallAccumulator = new Map<number, { id: string; name: string; arguments: string }>()
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
const finishReason = chunk.choices[0]?.finish_reason
|
||||
|
||||
if (delta?.content) {
|
||||
yield {
|
||||
|
|
@ -47,6 +50,37 @@ export class GroqHandler extends BaseOpenAiCompatibleProvider<GroqModelId> {
|
|||
}
|
||||
}
|
||||
|
||||
if (delta?.tool_calls) {
|
||||
for (const toolCall of delta.tool_calls) {
|
||||
const index = toolCall.index
|
||||
const existing = toolCallAccumulator.get(index)
|
||||
|
||||
if (existing) {
|
||||
if (toolCall.function?.arguments) {
|
||||
existing.arguments += toolCall.function.arguments
|
||||
}
|
||||
} else {
|
||||
toolCallAccumulator.set(index, {
|
||||
id: toolCall.id || "",
|
||||
name: toolCall.function?.name || "",
|
||||
arguments: toolCall.function?.arguments || "",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (finishReason === "tool_calls") {
|
||||
for (const toolCall of toolCallAccumulator.values()) {
|
||||
yield {
|
||||
type: "tool_call",
|
||||
id: toolCall.id,
|
||||
name: toolCall.name,
|
||||
arguments: toolCall.arguments,
|
||||
}
|
||||
}
|
||||
toolCallAccumulator.clear()
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
yield* this.yieldUsage(chunk.usage as GroqUsage)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ export class ZAiHandler extends BaseOpenAiCompatibleProvider<string> {
|
|||
messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
...(metadata?.tools && { tools: this.convertToolsForOpenAI(metadata.tools) }),
|
||||
...(metadata?.tool_choice && { tool_choice: metadata.tool_choice }),
|
||||
}
|
||||
|
||||
// Add thinking parameter if reasoning is enabled and model supports it
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue