Skip to content

feat: add deepseek model support #1334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
- iFlytek Spark
- Tongyi Qianwen
- Baichuan
- Deepseek (via API/PPIO)
- ...

## 🔨 Build from Source
Expand Down
36 changes: 36 additions & 0 deletions src/app/bots/deepseek-api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { UserConfig } from '~services/user-config'
import { AbstractChatGPTApiBot } from '../chatgpt-api'
import { ChatMessage } from '../chatgpt-api/types'

export class DeepSeekApiBot extends AbstractChatGPTApiBot {
constructor(
private config: Pick<
UserConfig,
'deepseekApiKey' |
'deepseekApiModel'
>,
) {
super()
}

async fetchCompletionApi(messages: ChatMessage[], signal?: AbortSignal) {
const endpoint = `https://api.deepseek.com/chat/completions`
return fetch(endpoint, {
method: 'POST',
signal,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.deepseekApiKey}`,
},
body: JSON.stringify({
messages,
stream: true,
model: this.config.deepseekApiModel
}),
})
}

get name() {
return `DeepSeek API`
}
}
36 changes: 36 additions & 0 deletions src/app/bots/deepseek-ppio/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { UserConfig } from '~services/user-config'
import { AbstractChatGPTApiBot } from '../chatgpt-api'
import { ChatMessage } from '../chatgpt-api/types'

export class DeepSeekPpioBot extends AbstractChatGPTApiBot {
constructor(
private config: Pick<
UserConfig,
'deepseekPpioKey' |
'deepseekPpioModel'
>,
) {
super()
}

async fetchCompletionApi(messages: ChatMessage[], signal?: AbortSignal) {
const endpoint = `https://api.ppinfra.com/v3/openai/chat/completions`
return fetch(endpoint, {
method: 'POST',
signal,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.deepseekPpioKey}`,
},
body: JSON.stringify({
messages,
stream: true,
model: this.config.deepseekPpioModel
}),
})
}

get name() {
return `DeepSeek PPIO API`
}
}
21 changes: 21 additions & 0 deletions src/app/bots/deepseek/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { DeepSeekMode, getUserConfig } from '~/services/user-config'
import { AsyncAbstractBot } from '../abstract-bot'
import { DeepSeekApiBot } from '../deepseek-api'
import { DeepSeekPpioBot } from '../deepseek-ppio'

export class DeepSeekBot extends AsyncAbstractBot {
async initializeBot() {
const { deepseekMode, ...config } = await getUserConfig()
if (deepseekMode === DeepSeekMode.API) {
if (!config.deepseekApiKey) {
throw new Error('DeepSeek API key missing')
}
return new DeepSeekApiBot(config)
} else {
if (!config.deepseekPpioKey) {
throw new Error('DeepSeek PPIO API key missing')
}
return new DeepSeekPpioBot(config)
}
}
}
4 changes: 4 additions & 0 deletions src/app/bots/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BardBot } from './bard'
import { BingWebBot } from './bing'
import { ChatGPTBot } from './chatgpt'
import { ClaudeBot } from './claude'
import { DeepSeekBot } from './deepseek'
import { GeminiBot } from './gemini-api'
import { GrokWebBot } from './grok'
import { LMSYSBot } from './lmsys'
Expand Down Expand Up @@ -30,6 +31,7 @@ export type BotId =
| 'yi'
| 'grok'
| 'gemini'
| 'deepseek'

export function createBotInstance(botId: BotId) {
switch (botId) {
Expand Down Expand Up @@ -69,6 +71,8 @@ export function createBotInstance(botId: BotId) {
return new GrokWebBot()
case 'gemini':
return new GeminiBot()
case 'deepseek':
return new DeepSeekBot()
}
}

Expand Down
48 changes: 48 additions & 0 deletions src/app/components/Settings/DeepSeekAPISettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { DeepSeekAPIModel, UserConfig } from '~services/user-config'
import { Input } from '../Input'
import Select from '../Select'
import Blockquote from './Blockquote'

interface Props {
userConfig: UserConfig
updateConfigValue: (update: Partial<UserConfig>) => void
}

const DeepSeekAPISettings: FC<Props> = ({ userConfig, updateConfigValue }) => {
const { t } = useTranslation()
return (
<div className="flex flex-col gap-2 w-[400px]">
<div className="flex flex-col gap-1">
<p className="font-medium text-sm">API Key (
<a
href="https://api-docs.deepseek.com"
target="_blank"
rel="noreferrer"
className="underline"
>
how to create key
</a>
)</p>
<Input
placeholder="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
value={userConfig.deepseekApiKey}
onChange={(e) => updateConfigValue({ deepseekApiKey: e.currentTarget.value })}
type="password"
/>
<Blockquote className="mt-1">{t('Your keys are stored locally')}</Blockquote>
</div>
<div className="flex flex-col gap-1">
<p className="font-medium text-sm">{t('Model')}</p>
<Select
options={Object.entries(DeepSeekAPIModel).map(([k, v]) => ({ name: k, value: v }))}
value={userConfig.deepseekApiModel}
onChange={(v) => updateConfigValue({ deepseekApiModel: v })}
/>
</div>
</div>
)
}

export default DeepSeekAPISettings
48 changes: 48 additions & 0 deletions src/app/components/Settings/DeepSeekPPIOSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { DeepSeekPPIOModel, UserConfig } from '~services/user-config'
import { Input } from '../Input'
import Select from '../Select'
import Blockquote from './Blockquote'

interface Props {
userConfig: UserConfig
updateConfigValue: (update: Partial<UserConfig>) => void
}

const DeepSeekPPIOSettings: FC<Props> = ({ userConfig, updateConfigValue }) => {
const { t } = useTranslation()
return (
<div className="flex flex-col gap-2 w-[400px]">
<div className="flex flex-col gap-1">
<p className="font-medium text-sm">API Key (
<a
href="https://ppinfra.com/docs/get-started/quickstart.html"
target="_blank"
rel="noreferrer"
className="underline"
>
how to create key
</a>
)</p>
<Input
placeholder="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
value={userConfig.deepseekPpioKey}
onChange={(e) => updateConfigValue({ deepseekPpioKey: e.currentTarget.value })}
type="password"
/>
<Blockquote className="mt-1">{t('Your keys are stored locally')}</Blockquote>
</div>
<div className="flex flex-col gap-1">
<p className="font-medium text-sm">{t('Model')}</p>
<Select
options={Object.entries(DeepSeekPPIOModel).map(([k, v]) => ({ name: k, value: v }))}
value={userConfig.deepseekPpioModel}
onChange={(v) => updateConfigValue({ deepseekPpioModel: v })}
/>
</div>
</div>
)
}

export default DeepSeekPPIOSettings
5 changes: 5 additions & 0 deletions src/app/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import vicunaLogo from '~/assets/logos/vicuna.jpg'
import wizardlmLogo from '~/assets/logos/wizardlm.png'
import xunfeiLogo from '~/assets/logos/xunfei.png'
import yiLogo from '~/assets/logos/yi.svg'
import deepseekLogo from '~/assets/logos/deepseek.svg'
import { BotId } from './bots'

export const CHATBOTS: Record<BotId, { name: string; avatar: string }> = {
Expand Down Expand Up @@ -91,6 +92,10 @@ export const CHATBOTS: Record<BotId, { name: string; avatar: string }> = {
name: 'Yi-Chat',
avatar: yiLogo,
},
deepseek: {
name: 'DeepSeek',
avatar: deepseekLogo,
},
}

export const CHATGPT_HOME_URL = 'https://chat.openai.com'
Expand Down
15 changes: 15 additions & 0 deletions src/app/pages/SettingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ import ClaudeWebappSettings from '~app/components/Settings/ClaudeWebappSettings'
import EnabledBotsSettings from '~app/components/Settings/EnabledBotsSettings'
import ExportDataPanel from '~app/components/Settings/ExportDataPanel'
import PerplexityAPISettings from '~app/components/Settings/PerplexityAPISettings'
import DeepSeekAPISettings from '~app/components/Settings/DeepSeekAPISettings'
import DeepSeekPPIOSettings from '~app/components/Settings/DeepSeekPPIOSettings'
import ShortcutPanel from '~app/components/Settings/ShortcutPanel'
import { ALL_IN_ONE_PAGE_ID, CHATBOTS } from '~app/consts'
import {
BingConversationStyle,
ChatGPTMode,
ClaudeMode,
PerplexityMode,
DeepSeekMode,
UserConfig,
getUserConfig,
updateUserConfig,
Expand Down Expand Up @@ -194,6 +197,18 @@ function SettingPage() {
<PerplexityAPISettings userConfig={userConfig} updateConfigValue={updateConfigValue} />
)}
</ChatBotSettingPanel>
<ChatBotSettingPanel title="DeepSeek">
<RadioGroup
options={Object.entries(DeepSeekMode).map(([k, v]) => ({ label: `${k} ${t('Mode')}`, value: v }))}
value={userConfig.deepseekMode}
onChange={(v) => updateConfigValue({ deepseekMode: v as DeepSeekMode })}
/>
{userConfig.deepseekMode === DeepSeekMode.API ? (
<DeepSeekAPISettings userConfig={userConfig} updateConfigValue={updateConfigValue} />
) : (
<DeepSeekPPIOSettings userConfig={userConfig} updateConfigValue={updateConfigValue} />
)}
</ChatBotSettingPanel>
</div>
<ShortcutPanel />
<ExportDataPanel />
Expand Down
6 changes: 6 additions & 0 deletions src/assets/logos/deepseek.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions src/services/user-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ export enum PerplexityMode {
API = 'api',
}

export enum DeepSeekMode {
API = 'api',
PPIO = 'ppio'
}

export enum DeepSeekAPIModel {
'deepseek-chat' = 'deepseek-chat'
}

export enum DeepSeekPPIOModel {
'deepseek-v3/community' = 'deepseek/deepseek-v3/community',
'deepseek-v3' = 'deepseek/deepseek-v3'
}

const userConfigWithDefaultValue = {
openaiApiKey: '',
openaiApiHost: 'https://api.openai.com',
Expand Down Expand Up @@ -82,6 +96,11 @@ const userConfigWithDefaultValue = {
perplexityMode: PerplexityMode.Webapp,
perplexityApiKey: '',
geminiApiKey: '',
deepseekMode: DeepSeekMode.API,
deepseekApiModel: DeepSeekAPIModel['deepseek-chat'],
deepseekPpioModel: DeepSeekPPIOModel['deepseek-v3/community'],
deepseekApiKey: '',
deepseekPpioKey: ''
}

export type UserConfig = typeof userConfigWithDefaultValue
Expand Down