Skip to content

Commit 404b3e8

Browse files
authored
🤖 feat: add DeepSeek provider support (#992)
Adds support for DeepSeek as an AI provider. ## Changes - Add `importDeepSeek` function and add `deepseek` to `PROVIDER_REGISTRY` in providers.ts - Add `deepseek` to `PROVIDER_DISPLAY_NAMES` - Add DeepSeek SVG logo icon from UXWing (royalty-free) - Add DeepSeek icon to `ProviderIcon.tsx` - Implement DeepSeek provider handler in `aiService.ts` - Install `@ai-sdk/deepseek` package ## Usage Users can now configure DeepSeek in their `providers.jsonc`: ```jsonc { "deepseek": { "apiKey": "YOUR_DEEPSEEK_API_KEY" } } ``` And use DeepSeek models like: - `deepseek:deepseek-chat` - `deepseek:deepseek-reasoner` Closes #973 --- _Generated with `mux`_
1 parent d2f335a commit 404b3e8

File tree

7 files changed

+179
-120
lines changed

7 files changed

+179
-120
lines changed

bun.lock

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"lockfileVersion": 1,
3-
"configVersion": 0,
43
"workspaces": {
54
"": {
65
"name": "mux",
76
"dependencies": {
87
"@ai-sdk/amazon-bedrock": "^3.0.61",
98
"@ai-sdk/anthropic": "^2.0.47",
9+
"@ai-sdk/deepseek": "^1.0.31",
1010
"@ai-sdk/google": "^2.0.43",
1111
"@ai-sdk/mcp": "^0.0.11",
1212
"@ai-sdk/openai": "^2.0.72",
@@ -172,6 +172,8 @@
172172

173173
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.53", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ih7NV+OFSNWZCF+tYYD7ovvvM+gv7TRKQblpVohg2ipIwC9Y0TirzocJVREzZa/v9luxUwFbsPji++DUDWWxsg=="],
174174

175+
"@ai-sdk/deepseek": ["@ai-sdk/deepseek@1.0.31", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Il7WJp8bA3CmlreYSl1YzCucGTn2e5P81IANYIIEeLtWrbK0Y9CLoOCROj8xKYyUSMKlINyGZX2uP79cKewtSg=="],
176+
175177
"@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-sDQcW+6ck2m0pTIHW6BPHD7S125WD3qNkx/B8sEzJp/hurocmJ5Cni0ybExg6sQMGo+fr/GWOwpHF1cmCdg5rQ=="],
176178

177179
"@ai-sdk/google": ["@ai-sdk/google@2.0.44", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-c5dck36FjqiVoeeMJQLTEmUheoURcGTU/nBT6iJu8/nZiKFT/y8pD85KMDRB7RerRYaaQOtslR2d6/5PditiRw=="],

docs/models.mdx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Models
3-
description: Configure AI providers including Anthropic, OpenAI, Google, xAI, and more
3+
description: Configure AI providers including Anthropic, OpenAI, Google, xAI, DeepSeek, and more
44
---
55

66
See also:
@@ -121,6 +121,26 @@ Frontier reasoning models from xAI with built-in search orchestration:
121121

122122
Mux enables Grok's live search by default using `mode: "auto"` with citations. Add [`searchParameters`](https://docs.x.ai/docs/resources/search) to `providers.jsonc` if you want to customize the defaults (e.g., regional focus, time filters, or disabling search entirely per workspace).
123123

124+
#### DeepSeek (Cloud)
125+
126+
Access DeepSeek's reasoning and chat models:
127+
128+
- `deepseek:deepseek-chat` — Fast, cost-effective chat model
129+
- `deepseek:deepseek-reasoner` — Advanced reasoning model with extended thinking
130+
131+
**Setup:**
132+
133+
1. Get your API key from [platform.deepseek.com](https://platform.deepseek.com/)
134+
2. Add to `~/.mux/providers.jsonc`:
135+
136+
```jsonc
137+
{
138+
"deepseek": {
139+
"apiKey": "sk-...",
140+
},
141+
}
142+
```
143+
124144
#### OpenRouter (Cloud)
125145

126146
Access 300+ models from multiple providers through a single API:
@@ -322,6 +342,10 @@ All providers are configured in `~/.mux/providers.jsonc`. Example configurations
322342
"xai": {
323343
"apiKey": "sk-xai-...",
324344
},
345+
// Required for DeepSeek models
346+
"deepseek": {
347+
"apiKey": "sk-...",
348+
},
325349
// Required for OpenRouter models
326350
"openrouter": {
327351
"apiKey": "sk-or-v1-...",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"dependencies": {
4848
"@ai-sdk/amazon-bedrock": "^3.0.61",
4949
"@ai-sdk/anthropic": "^2.0.47",
50+
"@ai-sdk/deepseek": "^1.0.31",
5051
"@ai-sdk/google": "^2.0.43",
5152
"@ai-sdk/mcp": "^0.0.11",
5253
"@ai-sdk/openai": "^2.0.72",
Lines changed: 4 additions & 0 deletions
Loading

src/browser/components/ProviderIcon.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,30 @@ import AnthropicIcon from "@/browser/assets/icons/anthropic.svg?react";
33
import OpenAIIcon from "@/browser/assets/icons/openai.svg?react";
44
import GoogleIcon from "@/browser/assets/icons/google.svg?react";
55
import XAIIcon from "@/browser/assets/icons/xai.svg?react";
6+
import DeepSeekIcon from "@/browser/assets/icons/deepseek.svg?react";
67
import AWSIcon from "@/browser/assets/icons/aws.svg?react";
78
import { GatewayIcon } from "@/browser/components/icons/GatewayIcon";
8-
import { PROVIDER_DISPLAY_NAMES, type ProviderName } from "@/common/constants/providers";
9+
import {
10+
PROVIDER_DEFINITIONS,
11+
PROVIDER_DISPLAY_NAMES,
12+
type ProviderName,
13+
} from "@/common/constants/providers";
914
import { cn } from "@/common/lib/utils";
1015

16+
/**
17+
* Provider icons mapped by provider name.
18+
* When adding a new provider, add its icon import above and entry here.
19+
*/
1120
const PROVIDER_ICONS: Partial<Record<ProviderName, React.FC>> = {
1221
anthropic: AnthropicIcon,
1322
openai: OpenAIIcon,
1423
google: GoogleIcon,
1524
xai: XAIIcon,
25+
deepseek: DeepSeekIcon,
1626
bedrock: AWSIcon,
1727
"mux-gateway": GatewayIcon,
1828
};
1929

20-
// Icons that use stroke instead of fill for their styling
21-
const STROKE_BASED_ICONS = new Set<string>(["mux-gateway"]);
22-
2330
export interface ProviderIconProps {
2431
provider: string;
2532
className?: string;
@@ -30,10 +37,13 @@ export interface ProviderIconProps {
3037
* Icons are sized to 1em by default to match surrounding text.
3138
*/
3239
export function ProviderIcon(props: ProviderIconProps) {
33-
const IconComponent = PROVIDER_ICONS[props.provider as keyof typeof PROVIDER_ICONS];
40+
const providerName = props.provider as ProviderName;
41+
const IconComponent = PROVIDER_ICONS[providerName];
3442
if (!IconComponent) return null;
3543

36-
const isStrokeBased = STROKE_BASED_ICONS.has(props.provider);
44+
// Check if this provider uses stroke-based icon styling (from PROVIDER_DEFINITIONS)
45+
const def = PROVIDER_DEFINITIONS[providerName] as { strokeBasedIcon?: boolean } | undefined;
46+
const isStrokeBased = def?.strokeBasedIcon ?? false;
3747

3848
return (
3949
<span

src/common/constants/providers.ts

Lines changed: 91 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,112 @@
11
/**
2-
* Typed import helpers for provider packages
2+
* Provider Definitions - Single source of truth for all provider metadata
33
*
4-
* These functions provide type-safe dynamic imports for provider packages.
5-
* TypeScript can infer the correct module type from literal string imports,
6-
* giving consuming code full type safety for provider constructors.
7-
*/
8-
9-
/**
10-
* Dynamically import the Anthropic provider package
11-
*/
12-
export function importAnthropic() {
13-
return import("@ai-sdk/anthropic");
14-
}
15-
16-
/**
17-
* Dynamically import the OpenAI provider package
18-
*/
19-
export function importOpenAI() {
20-
return import("@ai-sdk/openai");
21-
}
22-
23-
/**
24-
* Dynamically import the Ollama provider package
25-
*/
26-
export function importOllama() {
27-
return import("ollama-ai-provider-v2");
28-
}
29-
30-
/**
31-
* Dynamically import the Google provider package
32-
*/
33-
export function importGoogle() {
34-
return import("@ai-sdk/google");
35-
}
36-
37-
/**
38-
* Dynamically import the OpenRouter provider package
39-
*/
40-
export function importOpenRouter() {
41-
return import("@openrouter/ai-sdk-provider");
42-
}
43-
44-
/**
45-
* Dynamically import the xAI provider package
46-
*/
47-
export function importXAI() {
48-
return import("@ai-sdk/xai");
49-
}
50-
51-
/**
52-
* Dynamically import the Amazon Bedrock provider package
4+
* When adding a new provider:
5+
* 1. Add entry to PROVIDER_DEFINITIONS below
6+
* 2. Add SVG icon + import in src/browser/components/ProviderIcon.tsx
7+
* 3. If provider needs custom logic, add handler in aiService.ts
8+
* (simple providers using standard pattern are handled automatically)
9+
*
10+
* Simple providers (requiresApiKey + standard factory pattern) need NO aiService.ts changes.
5311
*/
54-
export function importBedrock() {
55-
return import("@ai-sdk/amazon-bedrock");
56-
}
5712

58-
/**
59-
* Dynamically import the Gateway provider from the AI SDK
60-
*/
61-
export function importMuxGateway() {
62-
return import("ai");
13+
interface ProviderDefinition {
14+
/** Display name for UI (proper casing) */
15+
displayName: string;
16+
/** Dynamic import function for lazy loading */
17+
import: () => Promise<unknown>;
18+
/** Name of the factory function exported by the package */
19+
factoryName: string;
20+
/** Whether provider requires an API key (false for local services like Ollama) */
21+
requiresApiKey: boolean;
22+
/** Whether this provider uses stroke-based icon styling instead of fill */
23+
strokeBasedIcon?: boolean;
6324
}
6425

65-
/**
66-
* Centralized provider registry mapping provider names to their import functions
67-
*
68-
* This is the single source of truth for supported providers. By mapping to import
69-
* functions rather than package strings, we eliminate duplication while maintaining
70-
* perfect type safety.
71-
*
72-
* When adding a new provider:
73-
* 1. Create an importXxx() function above
74-
* 2. Add entry mapping provider name to the import function
75-
* 3. Implement provider handling in aiService.ts createModel()
76-
* 4. Runtime check will fail if provider in registry but no handler
77-
*/
78-
export const PROVIDER_REGISTRY = {
79-
anthropic: importAnthropic,
80-
openai: importOpenAI,
81-
google: importGoogle,
82-
xai: importXAI,
83-
ollama: importOllama,
84-
openrouter: importOpenRouter,
85-
bedrock: importBedrock,
86-
"mux-gateway": importMuxGateway,
87-
} as const;
26+
// Order determines display order in UI (Settings, model selectors, etc.)
27+
export const PROVIDER_DEFINITIONS = {
28+
"mux-gateway": {
29+
displayName: "Mux Gateway",
30+
import: () => import("ai"),
31+
factoryName: "createGateway",
32+
requiresApiKey: true, // Uses couponCode
33+
strokeBasedIcon: true,
34+
},
35+
anthropic: {
36+
displayName: "Anthropic",
37+
import: () => import("@ai-sdk/anthropic"),
38+
factoryName: "createAnthropic",
39+
requiresApiKey: true,
40+
},
41+
openai: {
42+
displayName: "OpenAI",
43+
import: () => import("@ai-sdk/openai"),
44+
factoryName: "createOpenAI",
45+
requiresApiKey: true,
46+
},
47+
google: {
48+
displayName: "Google",
49+
import: () => import("@ai-sdk/google"),
50+
factoryName: "createGoogleGenerativeAI",
51+
requiresApiKey: true,
52+
},
53+
xai: {
54+
displayName: "xAI",
55+
import: () => import("@ai-sdk/xai"),
56+
factoryName: "createXai",
57+
requiresApiKey: true,
58+
},
59+
deepseek: {
60+
displayName: "DeepSeek",
61+
import: () => import("@ai-sdk/deepseek"),
62+
factoryName: "createDeepSeek",
63+
requiresApiKey: true,
64+
},
65+
openrouter: {
66+
displayName: "OpenRouter",
67+
import: () => import("@openrouter/ai-sdk-provider"),
68+
factoryName: "createOpenRouter",
69+
requiresApiKey: true,
70+
},
71+
bedrock: {
72+
displayName: "Amazon Bedrock",
73+
import: () => import("@ai-sdk/amazon-bedrock"),
74+
factoryName: "createAmazonBedrock",
75+
requiresApiKey: false, // Uses AWS credential chain
76+
},
77+
ollama: {
78+
displayName: "Ollama",
79+
import: () => import("ollama-ai-provider-v2"),
80+
factoryName: "createOllama",
81+
requiresApiKey: false, // Local service
82+
},
83+
} as const satisfies Record<string, ProviderDefinition>;
8884

8985
/**
9086
* Union type of all supported provider names
9187
*/
92-
export type ProviderName = keyof typeof PROVIDER_REGISTRY;
88+
export type ProviderName = keyof typeof PROVIDER_DEFINITIONS;
9389

9490
/**
9591
* Array of all supported provider names (for UI lists, iteration, etc.)
9692
*/
97-
export const SUPPORTED_PROVIDERS = Object.keys(PROVIDER_REGISTRY) as ProviderName[];
93+
export const SUPPORTED_PROVIDERS = Object.keys(PROVIDER_DEFINITIONS) as ProviderName[];
9894

9995
/**
10096
* Display names for providers (proper casing for UI)
97+
* Derived from PROVIDER_DEFINITIONS - do not edit directly
98+
*/
99+
export const PROVIDER_DISPLAY_NAMES: Record<ProviderName, string> = Object.fromEntries(
100+
Object.entries(PROVIDER_DEFINITIONS).map(([key, def]) => [key, def.displayName])
101+
) as Record<ProviderName, string>;
102+
103+
/**
104+
* Legacy registry for backward compatibility with aiService.ts
105+
* Maps provider names to their import functions
101106
*/
102-
export const PROVIDER_DISPLAY_NAMES: Record<ProviderName, string> = {
103-
anthropic: "Anthropic",
104-
openai: "OpenAI",
105-
google: "Google",
106-
xai: "xAI",
107-
ollama: "Ollama",
108-
openrouter: "OpenRouter",
109-
bedrock: "Amazon Bedrock",
110-
"mux-gateway": "Mux Gateway",
111-
};
107+
export const PROVIDER_REGISTRY = Object.fromEntries(
108+
Object.entries(PROVIDER_DEFINITIONS).map(([key, def]) => [key, def.import])
109+
) as { [K in ProviderName]: (typeof PROVIDER_DEFINITIONS)[K]["import"] };
112110

113111
/**
114112
* Type guard to check if a string is a valid provider name

0 commit comments

Comments
 (0)