Skip to content
Open
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
1 change: 1 addition & 0 deletions core/llm/autodetect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const PROVIDER_HANDLES_TEMPLATING: string[] = [
"openrouter",
"clawrouter",
"deepseek",
"doubao",
"xAI",
"minimax",
"groq",
Expand Down
33 changes: 33 additions & 0 deletions core/llm/llms/Doubao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { LLMOptions } from "../../index.js";

import OpenAI from "./OpenAI.js";

/**
* Doubao (豆包) is ByteDance's large-language-model family, served through
* Volcengine Ark (火山方舟).
*
* API surface: OpenAI-compatible `/chat/completions` at
* https://ark.cn-beijing.volces.com/api/v3/, Bearer-token auth. Unlike most
* OpenAI-compatible providers, Doubao requires users to deploy a model as an
* "endpoint" and use the endpoint ID (e.g. `ep-20240xxx-xxxxx`) as the model
* identifier — though shared/public endpoints also expose model-name aliases
* such as `doubao-1-5-pro-32k` and `doubao-seed-1-6`.
*
* Docs: https://www.volcengine.com/docs/82379
*/
class Doubao extends OpenAI {
static providerName = "doubao";
static defaultOptions: Partial<LLMOptions> = {
apiBase: "https://ark.cn-beijing.volces.com/api/v3/",
// Ark model IDs are date-stamped (e.g. `doubao-seed-1-6-251015`) or are
// Ark endpoint IDs (`ep-20240xxx-xxxxx`). We intentionally do NOT set a
// default `model` here: picking a specific dated ID would go stale, and
// users must in practice verify model availability against their own
// Ark deployment. Requiring an explicit `model` forces a conscious
// decision and avoids silent 404s from Ark.
useLegacyCompletionsEndpoint: false,
};
maxStopWords: number | undefined = 4;
}

export default Doubao;
6 changes: 6 additions & 0 deletions core/llm/llms/OpenAI-compatible.vitest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Groq from "./Groq.js";
import Fireworks from "./Fireworks.js";
import Together from "./Together.js";
import Deepseek from "./Deepseek.js";
import Doubao from "./Doubao.js";
import OpenRouter from "./OpenRouter.js";
import xAI from "./xAI.js";
import Mistral from "./Mistral.js";
Expand Down Expand Up @@ -370,6 +371,11 @@ createOpenAISubclassTests(Moonshot, {
defaultApiBase: "https://api.moonshot.cn/v1/",
});

createOpenAISubclassTests(Doubao, {
providerName: "doubao",
defaultApiBase: "https://ark.cn-beijing.volces.com/api/v3/",
});

createOpenAISubclassTests(Novita, {
providerName: "novita",
defaultApiBase: "https://api.novita.ai/v3/openai/",
Expand Down
2 changes: 2 additions & 0 deletions core/llm/llms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import CometAPI from "./CometAPI";
import DeepInfra from "./DeepInfra";
import Deepseek from "./Deepseek";
import Docker from "./Docker";
import Doubao from "./Doubao";
import Fireworks from "./Fireworks";
import Flowise from "./Flowise";
import FunctionNetwork from "./FunctionNetwork";
Expand Down Expand Up @@ -108,6 +109,7 @@ export const LLMClasses = [
Cloudflare,
Deepseek,
Docker,
Doubao,
Msty,
Azure,
WatsonX,
Expand Down
111 changes: 111 additions & 0 deletions docs/customize/model-providers/more/doubao.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
title: "Doubao (豆包)"
description: "Configure ByteDance Doubao models served via Volcengine Ark in Continue, including the doubao-seed and doubao-1.5-pro model families."
---

[Doubao (豆包)](https://www.volcengine.com/product/ark) is ByteDance's large-language-model family served through Volcengine Ark (火山方舟). The service exposes an OpenAI-compatible `/chat/completions` API and is widely used in the China region for both chat and code-assistance workloads.

Continue supports Doubao as a first-class provider. Ark addresses models either by a **date-stamped model ID** (e.g. `doubao-seed-1-6-251015`, `doubao-1-5-pro-32k-250115`) or by an **endpoint ID** you provision in the Ark console (`ep-20240xxx-xxxxx`). Always verify the current model ID in the [Ark model list](https://www.volcengine.com/docs/82379/1330310) before configuring Continue — undated aliases are not guaranteed to resolve.

## Configuration

To use Doubao models:

1. Create an API key on the [Volcengine Ark console](https://console.volcengine.com/ark/).
2. Either deploy the model you want as an endpoint and copy its endpoint ID, or use a public model alias.
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Configuration instructions are internally contradictory about using model aliases versus date-stamped model IDs/endpoint IDs, which can lead to invalid setup.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/customize/model-providers/more/doubao.mdx, line 15:

<comment>Configuration instructions are internally contradictory about using model aliases versus date-stamped model IDs/endpoint IDs, which can lead to invalid setup.</comment>

<file context>
@@ -0,0 +1,111 @@
+To use Doubao models:
+
+1. Create an API key on the [Volcengine Ark console](https://console.volcengine.com/ark/).
+2. Either deploy the model you want as an endpoint and copy its endpoint ID, or use a public model alias.
+3. Add the following configuration:
+
</file context>
Fix with Cubic

3. Add the following configuration:

<Tabs>
<Tab title="YAML">
```yaml title="config.yaml"
name: My Config
version: 0.0.1
schema: v1

models:
- name: Doubao Seed 1.6
provider: doubao
model: doubao-seed-1-6-251015
apiKey: <YOUR_VOLCENGINE_ARK_API_KEY>
```
</Tab>
<Tab title="JSON (Deprecated)">
```json title="config.json"
{
"models": [
{
"title": "Doubao Seed 1.6",
"provider": "doubao",
"model": "doubao-seed-1-6-251015",
"apiKey": "<YOUR_VOLCENGINE_ARK_API_KEY>"
}
]
}
```
</Tab>
</Tabs>

## Using an endpoint ID

For custom or newly released models, deploy the model as an Ark endpoint and pass the endpoint ID as the `model`:

```yaml title="config.yaml"
models:
- name: Doubao (custom endpoint)
provider: doubao
model: ep-20240xxx-xxxxx
apiKey: <YOUR_VOLCENGINE_ARK_API_KEY>
```

Endpoint IDs bypass model-name routing on the Ark gateway and give you full control over which deployment handles the request, including quota and region.

## Configuration Options

| Option | Description | Default |
| --------- | ---------------------------------------------------- | ------------------------------------------------- |
| `apiKey` | Volcengine Ark API key | Required |
| `apiBase` | Ark API base URL | `https://ark.cn-beijing.volces.com/api/v3/` |
| `model` | Ark model ID (date-stamped) or endpoint ID | Required |

## Example

Complete configuration with tuned completion options:

<Tabs>
<Tab title="YAML">
```yaml title="config.yaml"
name: My Config
version: 0.0.1
schema: v1

models:
- name: Doubao Seed 1.6
provider: doubao
model: doubao-seed-1-6-251015
apiKey: <YOUR_VOLCENGINE_ARK_API_KEY>
defaultCompletionOptions:
temperature: 0.7
topP: 0.95
maxTokens: 2048
```
</Tab>
<Tab title="JSON (Deprecated)">
```json title="config.json"
{
"models": [
{
"title": "Doubao Seed 1.6",
"provider": "doubao",
"model": "doubao-seed-1-6-251015",
"apiKey": "<YOUR_VOLCENGINE_ARK_API_KEY>",
"completionOptions": {
"temperature": 0.7,
"topP": 0.95,
"maxTokens": 2048
}
}
]
}
```
</Tab>
</Tabs>
34 changes: 34 additions & 0 deletions packages/openai-adapters/src/apis/Doubao.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, expect, it } from "vitest";
import { constructLlmApi } from "../index.js";
import { DoubaoApi } from "./Doubao.js";

describe("Doubao (ByteDance / Volcengine Ark) adapter", () => {
it("registers under the 'doubao' provider and returns a DoubaoApi", () => {
const api = constructLlmApi({
provider: "doubao",
apiKey: "test-key",
});
expect(api).toBeInstanceOf(DoubaoApi);
});

it("points at the Ark cn-beijing base URL by default", () => {
const api = new DoubaoApi({
provider: "doubao",
apiKey: "test-key",
});
// apiBase is a public field on the adapter; sanity-check it directly so
// a future refactor can't silently swap the default to, say, api.openai.com.
expect(api.apiBase).toBe("https://ark.cn-beijing.volces.com/api/v3/");
});

it("inherits from OpenAIApi (OpenAI-compatible Ark /chat/completions)", () => {
const api = new DoubaoApi({
provider: "doubao",
apiKey: "test-key",
});
// Ark is OpenAI-compatible for /chat/completions; the adapter relies on
// the base class, so it must not accidentally drop that relationship.
expect(typeof api.chatCompletionStream).toBe("function");
expect(typeof api.chatCompletionNonStream).toBe("function");
});
});
26 changes: 26 additions & 0 deletions packages/openai-adapters/src/apis/Doubao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { DoubaoConfig } from "../types.js";
import { OpenAIApi } from "./OpenAI.js";

/**
* Doubao (豆包) served via Volcengine Ark.
*
* Uses the OpenAI-compatible `/chat/completions` endpoint. Unlike most
* OpenAI-compatible services, Doubao recommends addressing models through
* a deployed "endpoint ID" (e.g. `ep-20240xxx-xxxxx`), though shared model
* aliases such as `doubao-1-5-pro-32k` also resolve on the public tenancy.
*
* No custom FIM: Ark does not expose a public `beta/completions` or
* `[fill]`-prompt protocol today. If that changes we can override
* `fimStream` the way Moonshot and Deepseek do.
*
* Reference: https://www.volcengine.com/docs/82379
*/
export class DoubaoApi extends OpenAIApi {
apiBase: string = "https://ark.cn-beijing.volces.com/api/v3/";
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Subclass field initialization overwrites config.apiBase set by OpenAIApi, breaking custom base URL configuration.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/openai-adapters/src/apis/Doubao.ts, line 19:

<comment>Subclass field initialization overwrites `config.apiBase` set by `OpenAIApi`, breaking custom base URL configuration.</comment>

<file context>
@@ -0,0 +1,26 @@
+ * Reference: https://www.volcengine.com/docs/82379
+ */
+export class DoubaoApi extends OpenAIApi {
+  apiBase: string = "https://ark.cn-beijing.volces.com/api/v3/";
+  constructor(config: DoubaoConfig) {
+    super({
</file context>
Fix with Cubic

constructor(config: DoubaoConfig) {
super({
...config,
provider: "openai",
});
}
}
3 changes: 3 additions & 0 deletions packages/openai-adapters/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CohereApi } from "./apis/Cohere.js";
import { CometAPIApi } from "./apis/CometAPI.js";
import { ContinueProxyApi } from "./apis/ContinueProxy.js";
import { DeepSeekApi } from "./apis/DeepSeek.js";
import { DoubaoApi } from "./apis/Doubao.js";
import { GeminiApi } from "./apis/Gemini.js";
import { InceptionApi } from "./apis/Inception.js";
import { JinaApi } from "./apis/Jina.js";
Expand Down Expand Up @@ -115,6 +116,8 @@ export function constructLlmApi(config: LLMConfig): BaseLlmApi | undefined {
return new JinaApi(config);
case "deepseek":
return new DeepSeekApi(config);
case "doubao":
return new DoubaoApi(config);
case "moonshot":
return new MoonshotApi(config);
case "relace":
Expand Down
6 changes: 6 additions & 0 deletions packages/openai-adapters/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ export const DeepseekConfigSchema = OpenAIConfigSchema.extend({
});
export type DeepseekConfig = z.infer<typeof DeepseekConfigSchema>;

export const DoubaoConfigSchema = OpenAIConfigSchema.extend({
provider: z.literal("doubao"),
});
export type DoubaoConfig = z.infer<typeof DoubaoConfigSchema>;

export const MiniMaxConfigSchema = OpenAIConfigSchema.extend({
provider: z.literal("minimax"),
});
Expand Down Expand Up @@ -271,6 +276,7 @@ export const LLMConfigSchema = z.discriminatedUnion("provider", [
BedrockConfigSchema,
MoonshotConfigSchema,
DeepseekConfigSchema,
DoubaoConfigSchema,
MiniMaxConfigSchema,
CohereConfigSchema,
AzureConfigSchema,
Expand Down
Loading