Skip to content

Commit 83d415c

Browse files
AntiMorondaniel-lxs
authored andcommitted
fix: i18n
1 parent 75f93c4 commit 83d415c

File tree

36 files changed

+346
-4
lines changed

36 files changed

+346
-4
lines changed

packages/types/src/codebase-index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const codebaseIndexModelsSchema = z.object({
4848
"openai-compatible": z.record(z.string(), z.object({ dimension: z.number() })).optional(),
4949
gemini: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
5050
mistral: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
51+
doubao: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
5152
})
5253

5354
export type CodebaseIndexModels = z.infer<typeof codebaseIndexModelsSchema>

packages/types/src/provider-settings.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const providerNames = [
2424
"mistral",
2525
"moonshot",
2626
"deepseek",
27+
"doubao",
2728
"unbound",
2829
"requesty",
2930
"human-relay",
@@ -194,6 +195,11 @@ const deepSeekSchema = apiModelIdProviderModelSchema.extend({
194195
deepSeekApiKey: z.string().optional(),
195196
})
196197

198+
const doubaoSchema = apiModelIdProviderModelSchema.extend({
199+
doubaoBaseUrl: z.string().optional(),
200+
doubaoApiKey: z.string().optional(),
201+
})
202+
197203
const moonshotSchema = apiModelIdProviderModelSchema.extend({
198204
moonshotBaseUrl: z
199205
.union([z.literal("https://api.moonshot.ai/v1"), z.literal("https://api.moonshot.cn/v1")])
@@ -266,6 +272,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
266272
openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })),
267273
mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })),
268274
deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })),
275+
doubaoSchema.merge(z.object({ apiProvider: z.literal("doubao") })),
269276
moonshotSchema.merge(z.object({ apiProvider: z.literal("moonshot") })),
270277
unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })),
271278
requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })),
@@ -297,6 +304,7 @@ export const providerSettingsSchema = z.object({
297304
...openAiNativeSchema.shape,
298305
...mistralSchema.shape,
299306
...deepSeekSchema.shape,
307+
...doubaoSchema.shape,
300308
...moonshotSchema.shape,
301309
...unboundSchema.shape,
302310
...requestySchema.shape,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { ModelInfo } from "../model.js"
2+
3+
export const doubaoDefaultModelId = "doubao-seed-1-6-250615"
4+
5+
export const doubaoModels = {
6+
"doubao-seed-1-6-250615": {
7+
maxTokens: 32_768,
8+
contextWindow: 128_000,
9+
supportsImages: true,
10+
supportsPromptCache: true,
11+
inputPrice: 0.0001, // $0.0001 per million tokens (cache miss)
12+
outputPrice: 0.0004, // $0.0004 per million tokens
13+
cacheWritesPrice: 0.0001, // $0.0001 per million tokens (cache miss)
14+
cacheReadsPrice: 0.00002, // $0.00002 per million tokens (cache hit)
15+
description: `Doubao Seed 1.6 is a powerful model designed for high-performance tasks with extensive context handling.`,
16+
},
17+
"doubao-seed-1-6-thinking-250715": {
18+
maxTokens: 32_768,
19+
contextWindow: 128_000,
20+
supportsImages: true,
21+
supportsPromptCache: true,
22+
inputPrice: 0.0002, // $0.0002 per million tokens
23+
outputPrice: 0.0008, // $0.0008 per million tokens
24+
cacheWritesPrice: 0.0002, // $0.0002 per million
25+
cacheReadsPrice: 0.00004, // $0.00004 per million tokens (cache hit)
26+
description: `Doubao Seed 1.6 Thinking is optimized for reasoning tasks, providing enhanced performance in complex problem-solving scenarios.`,
27+
},
28+
"doubao-seed-1-6-flash-250715": {
29+
maxTokens: 32_768,
30+
contextWindow: 128_000,
31+
supportsImages: true,
32+
supportsPromptCache: true,
33+
inputPrice: 0.00015, // $0.00015 per million tokens
34+
outputPrice: 0.0006, // $0.0006 per million tokens
35+
cacheWritesPrice: 0.00015, // $0.00015 per million
36+
cacheReadsPrice: 0.00003, // $0.00003 per million tokens (cache hit)
37+
description: `Doubao Seed 1.6 Flash is tailored for speed and efficiency, making it ideal for applications requiring rapid responses.`,
38+
},
39+
} as const satisfies Record<string, ModelInfo>
40+
41+
export const doubaoDefaultModelInfo: ModelInfo = doubaoModels[doubaoDefaultModelId]
42+
43+
export const DOUBAO_API_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3"
44+
export const DOUBAO_API_CHAT_PATH = "/chat/completions"

packages/types/src/providers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ export * from "./unbound.js"
2020
export * from "./vertex.js"
2121
export * from "./vscode-llm.js"
2222
export * from "./xai.js"
23+
export * from "./doubao.js"

src/api/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
LiteLLMHandler,
3232
ClaudeCodeHandler,
3333
SambaNovaHandler,
34+
DoubaoHandler,
3435
} from "./providers"
3536

3637
export interface SingleCompletionHandler {
@@ -92,6 +93,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
9293
return new OpenAiNativeHandler(options)
9394
case "deepseek":
9495
return new DeepSeekHandler(options)
96+
case "doubao":
97+
return new DoubaoHandler(options)
9598
case "moonshot":
9699
return new MoonshotHandler(options)
97100
case "vscode-lm":

src/api/providers/doubao.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { OpenAiHandler } from "./openai"
2+
import type { ApiHandlerOptions } from "../../shared/api"
3+
import { DOUBAO_API_BASE_URL, doubaoDefaultModelId, doubaoModels } from "@roo-code/types"
4+
import { getModelParams } from "../transform/model-params"
5+
import { ApiStreamUsageChunk } from "../transform/stream"
6+
7+
// by a fan of Doubao, his name on Github is AntiMoron.
8+
// Core types for Doubao API
9+
interface ChatCompletionMessageParam {
10+
role: "system" | "user" | "assistant" | "developer"
11+
content:
12+
| string
13+
| Array<{
14+
type: "text" | "image_url"
15+
text?: string
16+
image_url?: { url: string }
17+
}>
18+
}
19+
20+
interface ChatCompletionParams {
21+
model: string
22+
messages: ChatCompletionMessageParam[]
23+
temperature?: number
24+
stream?: boolean
25+
stream_options?: { include_usage: boolean }
26+
max_completion_tokens?: number
27+
}
28+
29+
interface ChatCompletion {
30+
choices: Array<{
31+
message: {
32+
content: string
33+
}
34+
}>
35+
usage?: {
36+
prompt_tokens: number
37+
completion_tokens: number
38+
}
39+
}
40+
41+
interface ChatCompletionChunk {
42+
choices: Array<{
43+
delta: {
44+
content?: string
45+
}
46+
}>
47+
usage?: {
48+
prompt_tokens: number
49+
completion_tokens: number
50+
}
51+
}
52+
53+
// interface ApiHandlerOptions {
54+
// doubaoBaseUrl?: string
55+
// doubaoApiKey?: string
56+
// doubaoHeaders?: Record<string, string>
57+
// doubaoModelId?: string
58+
// doubaoStreamingEnabled?: boolean
59+
// modelTemperature?: number
60+
// includeMaxTokens?: boolean
61+
// modelMaxTokens?: number
62+
// }
63+
64+
export class DoubaoHandler extends OpenAiHandler {
65+
constructor(options: ApiHandlerOptions) {
66+
super({
67+
...options,
68+
openAiApiKey: options.doubaoApiKey ?? "not-provided",
69+
openAiModelId: options.apiModelId ?? doubaoDefaultModelId,
70+
openAiBaseUrl: options.doubaoBaseUrl ?? DOUBAO_API_BASE_URL,
71+
openAiStreamingEnabled: true,
72+
includeMaxTokens: true,
73+
})
74+
}
75+
76+
override getModel() {
77+
const id = this.options.apiModelId ?? doubaoDefaultModelId
78+
const info = doubaoModels[id as keyof typeof doubaoModels] || doubaoModels[doubaoDefaultModelId]
79+
const params = getModelParams({ format: "openai", modelId: id, model: info, settings: this.options })
80+
return { id, info, ...params }
81+
}
82+
83+
// Override to handle Doubao's usage metrics, including caching.
84+
protected override processUsageMetrics(usage: any): ApiStreamUsageChunk {
85+
return {
86+
type: "usage",
87+
inputTokens: usage?.prompt_tokens || 0,
88+
outputTokens: usage?.completion_tokens || 0,
89+
cacheWriteTokens: usage?.prompt_tokens_details?.cache_miss_tokens,
90+
cacheReadTokens: usage?.prompt_tokens_details?.cached_tokens,
91+
}
92+
}
93+
}

src/api/providers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export { AwsBedrockHandler } from "./bedrock"
44
export { ChutesHandler } from "./chutes"
55
export { ClaudeCodeHandler } from "./claude-code"
66
export { DeepSeekHandler } from "./deepseek"
7+
export { DoubaoHandler } from "./doubao"
78
export { MoonshotHandler } from "./moonshot"
89
export { FakeAIHandler } from "./fake-ai"
910
export { GeminiHandler } from "./gemini"

src/services/code-index/constants/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ export const BATCH_PROCESSING_CONCURRENCY = 10
2929

3030
/**Gemini Embedder */
3131
export const GEMINI_MAX_ITEM_TOKENS = 2048
32+
33+
/**Doubao Embedder */
34+
export const DOUBAO_MAX_ITEM_TOKENS = 2048
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { OpenAICompatibleEmbedder } from "./openai-compatible"
2+
import { IEmbedder, EmbeddingResponse, EmbedderInfo } from "../interfaces/embedder"
3+
import { t } from "../../../i18n"
4+
import { TelemetryEventName } from "@roo-code/types"
5+
import { TelemetryService } from "@roo-code/telemetry"
6+
import { DOUBAO_MAX_ITEM_TOKENS } from "../constants"
7+
8+
export class DoubaoEmbedder implements IEmbedder {
9+
private readonly openAICompatibleEmbedder: OpenAICompatibleEmbedder
10+
private static readonly Doubao_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3/"
11+
private static readonly DEFAULT_MODEL = "doubao-embedding-text-240515"
12+
private readonly modelId: string
13+
14+
constructor(apiKey: string, modelId?: string) {
15+
if (!apiKey) {
16+
throw new Error(t("embeddings:validation.apiKeyRequired"))
17+
}
18+
19+
// Use provided model or default
20+
this.modelId = modelId || DoubaoEmbedder.DEFAULT_MODEL
21+
22+
// Create an OpenAI Compatible embedder with Doubao's configuration
23+
this.openAICompatibleEmbedder = new OpenAICompatibleEmbedder(
24+
DoubaoEmbedder.Doubao_BASE_URL,
25+
apiKey,
26+
this.modelId,
27+
DOUBAO_MAX_ITEM_TOKENS,
28+
)
29+
}
30+
31+
/**
32+
* Creates embeddings for the given texts using Doubao's embedding API
33+
* @param texts Array of text strings to embed
34+
* @param model Optional model identifier (uses constructor model if not provided)
35+
* @returns Promise resolving to embedding response
36+
*/
37+
async createEmbeddings(texts: string[], model?: string): Promise<EmbeddingResponse> {
38+
try {
39+
// Use the provided model or fall back to the instance's model
40+
const modelToUse = model || this.modelId
41+
return await this.openAICompatibleEmbedder.createEmbeddings(texts, modelToUse)
42+
} catch (error) {
43+
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
44+
error: error instanceof Error ? error.message : String(error),
45+
stack: error instanceof Error ? error.stack : undefined,
46+
location: "DoubaoEmbedder:createEmbeddings",
47+
})
48+
throw error
49+
}
50+
}
51+
52+
/**
53+
* Validates the Doubao embedder configuration by delegating to the underlying OpenAI-compatible embedder
54+
* @returns Promise resolving to validation result with success status and optional error message
55+
*/
56+
async validateConfiguration(): Promise<{ valid: boolean; error?: string }> {
57+
try {
58+
// Delegate validation to the OpenAI-compatible embedder
59+
// The error messages will be specific to Doubao since we're using Doubao's base URL
60+
return await this.openAICompatibleEmbedder.validateConfiguration()
61+
} catch (error) {
62+
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
63+
error: error instanceof Error ? error.message : String(error),
64+
stack: error instanceof Error ? error.stack : undefined,
65+
location: "DoubaoEmbedder:validateConfiguration",
66+
})
67+
throw error
68+
}
69+
}
70+
71+
get embedderInfo(): EmbedderInfo {
72+
return {
73+
name: "doubao",
74+
}
75+
}
76+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export * from "./openai-compatible"
2+
export * from "./openai"
3+
export * from "./ollama"
4+
export * from "./gemini"
5+
export * from "./mistral"
6+
export * from "./doubao"

0 commit comments

Comments
 (0)