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. Pick a **date-stamped model ID** from the [Ark model list](https://www.volcengine.com/docs/82379/1330310) (e.g. `doubao-seed-1-6-251015`), or deploy the model as an Ark endpoint and copy its endpoint ID (`ep-20240xxx-xxxxx`). Undated aliases such as plain `doubao-1-5-pro` are not guaranteed to resolve.
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");
});
});
31 changes: 31 additions & 0 deletions packages/openai-adapters/src/apis/Doubao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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
*/
const DEFAULT_DOUBAO_API_BASE = "https://ark.cn-beijing.volces.com/api/v3/";

export class DoubaoApi extends OpenAIApi {
constructor(config: DoubaoConfig) {
// Pass the default apiBase via config so users can still override it with
// a custom `apiBase`. Assigning `apiBase` as a subclass field initializer
// would clobber whatever `OpenAIApi`'s constructor already resolved.
super({
...config,
apiBase: config.apiBase ?? DEFAULT_DOUBAO_API_BASE,
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