mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-12 23:01:21 +00:00
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: cte <cestreich@gmail.com>
103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
import { DEEP_SEEK_DEFAULT_TEMPERATURE, type FeatherlessModelId, featherlessDefaultModelId, featherlessModels } from "@roo-code/types"
|
|
import { Anthropic } from "@anthropic-ai/sdk"
|
|
import OpenAI from "openai"
|
|
|
|
import type { ApiHandlerOptions } from "../../shared/api"
|
|
import { XmlMatcher } from "../../utils/xml-matcher"
|
|
import { convertToR1Format } from "../transform/r1-format"
|
|
import { convertToOpenAiMessages } from "../transform/openai-format"
|
|
import { ApiStream } from "../transform/stream"
|
|
|
|
import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
|
|
|
|
export class FeatherlessHandler extends BaseOpenAiCompatibleProvider<FeatherlessModelId> {
|
|
constructor(options: ApiHandlerOptions) {
|
|
super({
|
|
...options,
|
|
providerName: "Featherless",
|
|
baseURL: "https://api.featherless.ai/v1",
|
|
apiKey: options.featherlessApiKey,
|
|
defaultProviderModelId: featherlessDefaultModelId,
|
|
providerModels: featherlessModels,
|
|
defaultTemperature: 0.5,
|
|
})
|
|
}
|
|
|
|
private getCompletionParams(
|
|
systemPrompt: string,
|
|
messages: Anthropic.Messages.MessageParam[],
|
|
): OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming {
|
|
const {
|
|
id: model,
|
|
info: { maxTokens: max_tokens },
|
|
} = this.getModel()
|
|
|
|
const temperature = this.options.modelTemperature ?? this.getModel().info.temperature
|
|
|
|
return {
|
|
model,
|
|
max_tokens,
|
|
temperature,
|
|
messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
|
|
stream: true,
|
|
stream_options: { include_usage: true },
|
|
}
|
|
}
|
|
|
|
override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
|
|
const model = this.getModel()
|
|
|
|
if (model.id.includes("DeepSeek-R1")) {
|
|
const stream = await this.client.chat.completions.create({
|
|
...this.getCompletionParams(systemPrompt, messages),
|
|
messages: convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]),
|
|
})
|
|
|
|
const matcher = new XmlMatcher(
|
|
"think",
|
|
(chunk) =>
|
|
({
|
|
type: chunk.matched ? "reasoning" : "text",
|
|
text: chunk.data,
|
|
}) as const,
|
|
)
|
|
|
|
for await (const chunk of stream) {
|
|
const delta = chunk.choices[0]?.delta
|
|
|
|
if (delta?.content) {
|
|
for (const processedChunk of matcher.update(delta.content)) {
|
|
yield processedChunk
|
|
}
|
|
}
|
|
|
|
if (chunk.usage) {
|
|
yield {
|
|
type: "usage",
|
|
inputTokens: chunk.usage.prompt_tokens || 0,
|
|
outputTokens: chunk.usage.completion_tokens || 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process any remaining content
|
|
for (const processedChunk of matcher.final()) {
|
|
yield processedChunk
|
|
}
|
|
} else {
|
|
yield* super.createMessage(systemPrompt, messages)
|
|
}
|
|
}
|
|
|
|
override getModel() {
|
|
const model = super.getModel()
|
|
const isDeepSeekR1 = model.id.includes("DeepSeek-R1")
|
|
return {
|
|
...model,
|
|
info: {
|
|
...model.info,
|
|
temperature: isDeepSeekR1 ? DEEP_SEEK_DEFAULT_TEMPERATURE : this.defaultTemperature,
|
|
},
|
|
}
|
|
}
|
|
}
|