diff --git a/src/api/providers/featherless.ts b/src/api/providers/featherless.ts index 2a985e2a87..ab7b5a9e18 100644 --- a/src/api/providers/featherless.ts +++ b/src/api/providers/featherless.ts @@ -31,6 +31,7 @@ export class FeatherlessHandler extends BaseOpenAiCompatibleProvider() + 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 { ): ApiStream { const stream = await this.createStream(systemPrompt, messages, metadata) + const toolCallAccumulator = new Map() + 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 { } } + 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) } diff --git a/src/api/providers/zai.ts b/src/api/providers/zai.ts index cc83945e48..bc589e1d18 100644 --- a/src/api/providers/zai.ts +++ b/src/api/providers/zai.ts @@ -64,6 +64,8 @@ export class ZAiHandler extends BaseOpenAiCompatibleProvider { 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