Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const providerNames = [
"mistral",
"moonshot",
"deepseek",
"doubao",
"unbound",
"requesty",
"human-relay",
Expand Down Expand Up @@ -194,6 +195,11 @@ const deepSeekSchema = apiModelIdProviderModelSchema.extend({
deepSeekApiKey: z.string().optional(),
})

const doubaoSchema = apiModelIdProviderModelSchema.extend({
doubaoBaseUrl: z.string().optional(),
doubaoApiKey: z.string().optional(),
})

const moonshotSchema = apiModelIdProviderModelSchema.extend({
moonshotBaseUrl: z
.union([z.literal("https://api.moonshot.ai/v1"), z.literal("https://api.moonshot.cn/v1")])
Expand Down Expand Up @@ -266,6 +272,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })),
mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })),
deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })),
doubaoSchema.merge(z.object({ apiProvider: z.literal("doubao") })),
moonshotSchema.merge(z.object({ apiProvider: z.literal("moonshot") })),
unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })),
requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })),
Expand Down Expand Up @@ -297,6 +304,7 @@ export const providerSettingsSchema = z.object({
...openAiNativeSchema.shape,
...mistralSchema.shape,
...deepSeekSchema.shape,
...doubaoSchema.shape,
...moonshotSchema.shape,
...unboundSchema.shape,
...requestySchema.shape,
Expand Down
44 changes: 44 additions & 0 deletions packages/types/src/providers/doubao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { ModelInfo } from "../model.js"

export const doubaoDefaultModelId = "doubao-seed-1-6-250615"

export const doubaoModels = {
"doubao-seed-1-6-250615": {
maxTokens: 32_768,
contextWindow: 128_000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 0.0001, // $0.0001 per million tokens (cache miss)
outputPrice: 0.0004, // $0.0004 per million tokens
cacheWritesPrice: 0.0001, // $0.0001 per million tokens (cache miss)
cacheReadsPrice: 0.00002, // $0.00002 per million tokens (cache hit)
description: `Doubao Seed 1.6 is a powerful model designed for high-performance tasks with extensive context handling.`,
},
"doubao-seed-1-6-thinking-250715": {
maxTokens: 32_768,
contextWindow: 128_000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 0.0002, // $0.0002 per million tokens
outputPrice: 0.0008, // $0.0008 per million tokens
cacheWritesPrice: 0.0002, // $0.0002 per million
cacheReadsPrice: 0.00004, // $0.00004 per million tokens (cache hit)
description: `Doubao Seed 1.6 Thinking is optimized for reasoning tasks, providing enhanced performance in complex problem-solving scenarios.`,
},
"doubao-seed-1-6-flash-250715": {
maxTokens: 32_768,
contextWindow: 128_000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 0.00015, // $0.00015 per million tokens
outputPrice: 0.0006, // $0.0006 per million tokens
cacheWritesPrice: 0.00015, // $0.00015 per million
cacheReadsPrice: 0.00003, // $0.00003 per million tokens (cache hit)
description: `Doubao Seed 1.6 Flash is tailored for speed and efficiency, making it ideal for applications requiring rapid responses.`,
},
} as const satisfies Record<string, ModelInfo>

export const doubaoDefaultModelInfo: ModelInfo = doubaoModels[doubaoDefaultModelId]

export const DOUBAO_API_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3"
export const DOUBAO_API_CHAT_PATH = "/chat/completions"
1 change: 1 addition & 0 deletions packages/types/src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export * from "./unbound.js"
export * from "./vertex.js"
export * from "./vscode-llm.js"
export * from "./xai.js"
export * from "./doubao.js"
3 changes: 3 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
LiteLLMHandler,
ClaudeCodeHandler,
SambaNovaHandler,
DoubaoHandler,
} from "./providers"

export interface SingleCompletionHandler {
Expand Down Expand Up @@ -92,6 +93,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
return new OpenAiNativeHandler(options)
case "deepseek":
return new DeepSeekHandler(options)
case "doubao":
return new DoubaoHandler(options)
case "moonshot":
return new MoonshotHandler(options)
case "vscode-lm":
Expand Down
81 changes: 81 additions & 0 deletions src/api/providers/doubao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { OpenAiHandler } from "./openai"
import type { ApiHandlerOptions } from "../../shared/api"
import { DOUBAO_API_BASE_URL, doubaoDefaultModelId, doubaoModels } from "@roo-code/types"
import { getModelParams } from "../transform/model-params"
import { ApiStreamUsageChunk } from "../transform/stream"

// Core types for Doubao API
interface ChatCompletionMessageParam {
role: "system" | "user" | "assistant" | "developer"
content:
| string
| Array<{
type: "text" | "image_url"
text?: string
image_url?: { url: string }
}>
}

interface ChatCompletionParams {
model: string
messages: ChatCompletionMessageParam[]
temperature?: number
stream?: boolean
stream_options?: { include_usage: boolean }
max_completion_tokens?: number
}

interface ChatCompletion {
choices: Array<{
message: {
content: string
}
}>
usage?: {
prompt_tokens: number
completion_tokens: number
}
}

interface ChatCompletionChunk {
choices: Array<{
delta: {
content?: string
}
}>
usage?: {
prompt_tokens: number
completion_tokens: number
}
}

export class DoubaoHandler extends OpenAiHandler {
constructor(options: ApiHandlerOptions) {
super({
...options,
openAiApiKey: options.doubaoApiKey ?? "not-provided",
openAiModelId: options.apiModelId ?? doubaoDefaultModelId,
openAiBaseUrl: options.doubaoBaseUrl ?? DOUBAO_API_BASE_URL,
openAiStreamingEnabled: true,
includeMaxTokens: true,
})
}

override getModel() {
const id = this.options.apiModelId ?? doubaoDefaultModelId
const info = doubaoModels[id as keyof typeof doubaoModels] || doubaoModels[doubaoDefaultModelId]
const params = getModelParams({ format: "openai", modelId: id, model: info, settings: this.options })
return { id, info, ...params }
}

// Override to handle Doubao's usage metrics, including caching.
protected override processUsageMetrics(usage: any): ApiStreamUsageChunk {
return {
type: "usage",
inputTokens: usage?.prompt_tokens || 0,
outputTokens: usage?.completion_tokens || 0,
cacheWriteTokens: usage?.prompt_tokens_details?.cache_miss_tokens,
cacheReadTokens: usage?.prompt_tokens_details?.cached_tokens,
}
}
}
1 change: 1 addition & 0 deletions src/api/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export { AwsBedrockHandler } from "./bedrock"
export { ChutesHandler } from "./chutes"
export { ClaudeCodeHandler } from "./claude-code"
export { DeepSeekHandler } from "./deepseek"
export { DoubaoHandler } from "./doubao"
export { MoonshotHandler } from "./moonshot"
export { FakeAIHandler } from "./fake-ai"
export { GeminiHandler } from "./gemini"
Expand Down
7 changes: 7 additions & 0 deletions webview-ui/src/components/settings/ApiOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
litellmDefaultModelId,
openAiNativeDefaultModelId,
anthropicDefaultModelId,
doubaoDefaultModelId,
claudeCodeDefaultModelId,
geminiDefaultModelId,
deepSeekDefaultModelId,
Expand Down Expand Up @@ -57,6 +58,7 @@ import {
Chutes,
ClaudeCode,
DeepSeek,
Doubao,
Gemini,
Glama,
Groq,
Expand Down Expand Up @@ -292,6 +294,7 @@ const ApiOptions = ({
"openai-native": { field: "apiModelId", default: openAiNativeDefaultModelId },
gemini: { field: "apiModelId", default: geminiDefaultModelId },
deepseek: { field: "apiModelId", default: deepSeekDefaultModelId },
doubao: { field: "apiModelId", default: doubaoDefaultModelId },
moonshot: { field: "apiModelId", default: moonshotDefaultModelId },
mistral: { field: "apiModelId", default: mistralDefaultModelId },
xai: { field: "apiModelId", default: xaiDefaultModelId },
Expand Down Expand Up @@ -475,6 +478,10 @@ const ApiOptions = ({
<DeepSeek apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}

{selectedProvider === "doubao" && (
<Doubao apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}

{selectedProvider === "moonshot" && (
<Moonshot apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/components/settings/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import {
groqModels,
chutesModels,
sambaNovaModels,
doubaoModels,
} from "@roo-code/types"

export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, ModelInfo>>> = {
anthropic: anthropicModels,
"claude-code": claudeCodeModels,
bedrock: bedrockModels,
deepseek: deepSeekModels,
doubao: doubaoModels,
moonshot: moonshotModels,
gemini: geminiModels,
mistral: mistralModels,
Expand All @@ -37,6 +39,7 @@ export const PROVIDERS = [
{ value: "anthropic", label: "Anthropic" },
{ value: "claude-code", label: "Claude Code" },
{ value: "gemini", label: "Google Gemini" },
{ value: "doubao", label: "Doubao" },
{ value: "deepseek", label: "DeepSeek" },
{ value: "moonshot", label: "Moonshot" },
{ value: "openai-native", label: "OpenAI" },
Expand Down
52 changes: 52 additions & 0 deletions webview-ui/src/components/settings/providers/Doubao.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"

import type { ProviderSettings } from "@roo-code/types"

import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"

import { inputEventTransform } from "../transforms"

type DoubaoProps = {
apiConfiguration: ProviderSettings
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
}

export const Doubao = ({ apiConfiguration, setApiConfigurationField }: DoubaoProps) => {
const { t } = useAppTranslation()

const handleInputChange = useCallback(
<K extends keyof ProviderSettings, E>(
field: K,
transform: (event: E) => ProviderSettings[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)

return (
<>
<VSCodeTextField
value={apiConfiguration?.doubaoApiKey || ""}
type="password"
onInput={handleInputChange("doubaoApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.doubaoApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.doubaoApiKey && (
<VSCodeButtonLink
href="https://www.volcengine.com/experience/ark?model=doubao-1-5-thinking-vision-pro-250428"
appearance="secondary">
{t("settings:providers.getDoubaoApiKey")}
</VSCodeButtonLink>
)}
</>
)
}
1 change: 1 addition & 0 deletions webview-ui/src/components/settings/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { Bedrock } from "./Bedrock"
export { Chutes } from "./Chutes"
export { ClaudeCode } from "./ClaudeCode"
export { DeepSeek } from "./DeepSeek"
export { Doubao } from "./Doubao"
export { Gemini } from "./Gemini"
export { Glama } from "./Glama"
export { Groq } from "./Groq"
Expand Down
7 changes: 7 additions & 0 deletions webview-ui/src/components/ui/hooks/useSelectedModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import {
claudeCodeModels,
sambaNovaModels,
sambaNovaDefaultModelId,
doubaoModels,
doubaoDefaultModelId,
} from "@roo-code/types"

import type { RouterModels } from "@roo/api"
Expand Down Expand Up @@ -176,6 +178,11 @@ function getSelectedModel({
const info = deepSeekModels[id as keyof typeof deepSeekModels]
return { id, info }
}
case "doubao": {
const id = apiConfiguration.apiModelId ?? doubaoDefaultModelId
const info = doubaoModels[id as keyof typeof doubaoModels]
return { id, info }
}
case "moonshot": {
const id = apiConfiguration.apiModelId ?? moonshotDefaultModelId
const info = moonshotModels[id as keyof typeof moonshotModels]
Expand Down
2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/ca/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/de/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@
"getChutesApiKey": "Get Chutes API Key",
"deepSeekApiKey": "DeepSeek API Key",
"getDeepSeekApiKey": "Get DeepSeek API Key",
"doubaoApiKey": "Doubao API Key",
"getDoubaoApiKey": "Get Doubao API Key",
"moonshotApiKey": "Moonshot API Key",
"getMoonshotApiKey": "Get Moonshot API Key",
"moonshotBaseUrl": "Moonshot Entrypoint",
Expand Down
2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/es/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/fr/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/hi/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/id/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading