Skip to content

Commit be55474

Browse files
committed
feat: support deepseek models
1 parent 9ff933c commit be55474

3 files changed

Lines changed: 184 additions & 6 deletions

File tree

src/models.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export type ConfigModel = {
9797

9898
function buildLookupMap(modelsDevData: ModelsDevData) {
9999
const byFullId = new Map<string, ModelDevEntry>();
100-
const allowedProviders = new Set(["openai", "anthropic"]);
100+
const allowedProviders = new Set(["openai", "anthropic", "deepseek"]);
101101

102102
for (const [provider, providerData] of Object.entries(modelsDevData)) {
103103
if (!allowedProviders.has(provider) || !providerData?.models) continue;
@@ -156,8 +156,8 @@ export async function fetchHubModels(): Promise<HubResponse> {
156156
return res.json() as Promise<HubResponse>;
157157
}
158158

159-
function checkIsAnthropic(provider: string): boolean {
160-
return provider === "anthropic";
159+
function usesAnthropicApi(provider: string): boolean {
160+
return provider === "anthropic" || provider === "deepseek";
161161
}
162162

163163
export function buildConfigModels(
@@ -171,7 +171,7 @@ export function buildConfigModels(
171171
for (const [provider, providerData] of Object.entries(hubData.providers)) {
172172
if (!providerData?.models) continue;
173173

174-
const anthropic = checkIsAnthropic(provider);
174+
const anthropic = usesAnthropicApi(provider);
175175

176176
for (const [modelId, hubModel] of Object.entries(providerData.models)) {
177177
const entry = resolveEntry(provider, modelId, byFullId);

test/index.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,43 @@ const MODELS_DEV_DATA = {
6565
},
6666
},
6767
},
68+
deepseek: {
69+
models: {
70+
"deepseek-v4-pro": {
71+
id: "deepseek-v4-pro",
72+
name: "DeepSeek V4 Pro",
73+
limit: { context: 128000, output: 32000 },
74+
attachment: true,
75+
reasoning: true,
76+
temperature: true,
77+
tool_call: true,
78+
modalities: {
79+
input: ["text"],
80+
output: ["text"],
81+
},
82+
cost: { input: 1.5, output: 6, cache_read: 0.15, cache_write: 1.5 },
83+
},
84+
"deepseek-v4-flash": {
85+
id: "deepseek-v4-flash",
86+
name: "DeepSeek V4 Flash",
87+
limit: { context: 128000, output: 32000 },
88+
attachment: true,
89+
reasoning: true,
90+
temperature: true,
91+
tool_call: true,
92+
modalities: {
93+
input: ["text"],
94+
output: ["text"],
95+
},
96+
cost: {
97+
input: 0.15,
98+
output: 0.6,
99+
cache_read: 0.015,
100+
cache_write: 0.15,
101+
},
102+
},
103+
},
104+
},
68105
};
69106

70107
function hubResponse() {
@@ -80,6 +117,12 @@ function hubResponse() {
80117
"claude-sonnet-4-20250514": { display_name: "Claude Sonnet 4" },
81118
},
82119
},
120+
deepseek: {
121+
models: {
122+
"deepseek-v4-pro": { display_name: "DeepSeek V4 Pro" },
123+
"deepseek-v4-flash": { display_name: "DeepSeek V4 Flash" },
124+
},
125+
},
83126
},
84127
};
85128
}
@@ -169,6 +212,48 @@ describe("config hook", () => {
169212
"interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
170213
},
171214
});
215+
216+
expect(models["deepseek-v4-pro"]).toEqual({
217+
id: "deepseek-v4-pro",
218+
name: "DeepSeek V4 Pro",
219+
provider: {
220+
api: "https://hub.coreinfra.ai/claude/api/v1",
221+
npm: "@ai-sdk/anthropic",
222+
},
223+
attachment: true,
224+
reasoning: true,
225+
temperature: true,
226+
tool_call: true,
227+
modalities: { input: ["text"], output: ["text"] },
228+
cost: { input: 1.5, output: 6, cache_read: 0.15, cache_write: 1.5 },
229+
limit: { context: 128000, output: 32000 },
230+
interleaved: { field: "reasoning_content" },
231+
headers: {
232+
"anthropic-beta":
233+
"interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
234+
},
235+
});
236+
237+
expect(models["deepseek-v4-flash"]).toEqual({
238+
id: "deepseek-v4-flash",
239+
name: "DeepSeek V4 Flash",
240+
provider: {
241+
api: "https://hub.coreinfra.ai/claude/api/v1",
242+
npm: "@ai-sdk/anthropic",
243+
},
244+
attachment: true,
245+
reasoning: true,
246+
temperature: true,
247+
tool_call: true,
248+
modalities: { input: ["text"], output: ["text"] },
249+
cost: { input: 0.15, output: 0.6, cache_read: 0.015, cache_write: 0.15 },
250+
limit: { context: 128000, output: 32000 },
251+
interleaved: { field: "reasoning_content" },
252+
headers: {
253+
"anthropic-beta":
254+
"interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
255+
},
256+
});
172257
});
173258

174259
it("uses defaults when models.dev has no matching model", async () => {

test/models.test.ts

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,43 @@ const MODELS_DEV_FIXTURE = {
5252
},
5353
},
5454
},
55+
deepseek: {
56+
models: {
57+
"deepseek-v4-pro": {
58+
id: "deepseek-v4-pro",
59+
name: "DeepSeek V4 Pro",
60+
limit: { context: 128000, output: 32000 },
61+
attachment: true,
62+
reasoning: true,
63+
temperature: true,
64+
tool_call: true,
65+
modalities: {
66+
input: ["text"],
67+
output: ["text"],
68+
},
69+
cost: { input: 1.5, output: 6, cache_read: 0.15, cache_write: 1.5 },
70+
},
71+
"deepseek-v4-flash": {
72+
id: "deepseek-v4-flash",
73+
name: "DeepSeek V4 Flash",
74+
limit: { context: 128000, output: 32000 },
75+
attachment: true,
76+
reasoning: true,
77+
temperature: true,
78+
tool_call: true,
79+
modalities: {
80+
input: ["text"],
81+
output: ["text"],
82+
},
83+
cost: {
84+
input: 0.15,
85+
output: 0.6,
86+
cache_read: 0.015,
87+
cache_write: 0.15,
88+
},
89+
},
90+
},
91+
},
5592
};
5693

5794
const HUB_FIXTURE = {
@@ -66,6 +103,12 @@ const HUB_FIXTURE = {
66103
"claude-sonnet-4-20250514": { display_name: "Claude Sonnet 4" },
67104
},
68105
},
106+
deepseek: {
107+
models: {
108+
"deepseek-v4-pro": { display_name: "DeepSeek V4 Pro" },
109+
"deepseek-v4-flash": { display_name: "DeepSeek V4 Flash" },
110+
},
111+
},
69112
},
70113
};
71114

@@ -77,7 +120,7 @@ describe("buildConfigModels", () => {
77120
);
78121

79122
expect(warnings).toEqual([]);
80-
expect(Object.keys(models)).toHaveLength(2);
123+
expect(Object.keys(models)).toHaveLength(4);
81124

82125
const gpt = models["gpt-5.4-nano"];
83126
expect(gpt).toEqual({
@@ -125,6 +168,56 @@ describe("buildConfigModels", () => {
125168
"interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
126169
},
127170
});
171+
172+
const dsPro = models["deepseek-v4-pro"];
173+
expect(dsPro).toEqual({
174+
id: "deepseek-v4-pro",
175+
name: "DeepSeek V4 Pro",
176+
provider: {
177+
api: "https://hub.coreinfra.ai/claude/api/v1",
178+
npm: "@ai-sdk/anthropic",
179+
},
180+
attachment: true,
181+
reasoning: true,
182+
temperature: true,
183+
tool_call: true,
184+
modalities: {
185+
input: ["text"],
186+
output: ["text"],
187+
},
188+
cost: { input: 1.5, output: 6, cache_read: 0.15, cache_write: 1.5 },
189+
limit: { context: 128000, output: 32000 },
190+
interleaved: { field: "reasoning_content" },
191+
headers: {
192+
"anthropic-beta":
193+
"interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
194+
},
195+
});
196+
197+
const dsFlash = models["deepseek-v4-flash"];
198+
expect(dsFlash).toEqual({
199+
id: "deepseek-v4-flash",
200+
name: "DeepSeek V4 Flash",
201+
provider: {
202+
api: "https://hub.coreinfra.ai/claude/api/v1",
203+
npm: "@ai-sdk/anthropic",
204+
},
205+
attachment: true,
206+
reasoning: true,
207+
temperature: true,
208+
tool_call: true,
209+
modalities: {
210+
input: ["text"],
211+
output: ["text"],
212+
},
213+
cost: { input: 0.15, output: 0.6, cache_read: 0.015, cache_write: 0.15 },
214+
limit: { context: 128000, output: 32000 },
215+
interleaved: { field: "reasoning_content" },
216+
headers: {
217+
"anthropic-beta":
218+
"interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
219+
},
220+
});
128221
});
129222

130223
it("uses defaults and emits warning when model not in models.dev", () => {
@@ -197,7 +290,7 @@ describe("buildConfigModels", () => {
197290
expect(models["shared-id"].limit.context).toBe(100000);
198291
});
199292

200-
it("does not resolve from non-openai/anthropic providers in models.dev", () => {
293+
it("does not resolve from non-allowed providers in models.dev", () => {
201294
const modelsDevData = {
202295
"provider-a": {
203296
models: {

0 commit comments

Comments
 (0)