Skip to content

Commit 3d855e0

Browse files
authored
🐋 feat: support deepseek reasoner (#34)
1 parent bfe5c5f commit 3d855e0

File tree

9 files changed

+397
-86
lines changed

9 files changed

+397
-86
lines changed

.env.template

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
VITE_SPARK_KEY=key123456:secret123456
33
VITE_SILICONFLOW_KEY=sk-xxxxxx
44
VITE_MOONSHOT_KEY=sk-xxxxxx
5+
VITE_DEEPSEEK_KEY=sk-xxxxxx

README.md

+48-19
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ __[🌈 Live Demo 在线体验](https://pdsuwwz.github.io/chatgpt-vue3-light-mvp
2525

2626
| 模型名称 | 模型标识符 | 需要 API Key | 可否本地运行 | 备注 |
2727
|----------|----------|----------|----------|----------|
28-
| (默认类型)模拟数据模型 | `standard` | × || 开发环境默认使用 |
29-
| Ollama (Llama 3) 大模型 | `ollama3` | × || 需本地安装运行 Ollama 服务 |
30-
| Spark 星火大模型 | `spark` || × | 需配置 `VITE_SPARK_KEY` |
31-
| SiliconFlow 硅基流动大模型 | `siliconflow` || × | 需配置 `VITE_SILICONFLOW_KEY` |
32-
| Kimi Moonshot 月之暗面大模型 | `moonshot` || × | 需配置 `VITE_MOONSHOT_KEY` |
28+
| (默认类型)模拟数据模型 | `standard` | × || 开发环境默认使用 |
29+
| Ollama (Llama 3) 大模型 | `ollama3` | × || 需本地安装运行 Ollama 服务 |
30+
| DeepSeek-V3 | `deepseek-v3` || × | 需配置 `VITE_DEEPSEEK_KEY` |
31+
| DeepSeek-R1 (推理模型) | `deepseek-deep` || × | 需配置 `VITE_DEEPSEEK_KEY` |
32+
| Spark 星火大模型 | `spark` || × | 需配置 `VITE_SPARK_KEY` |
33+
| SiliconFlow 硅基流动大模型 | `siliconflow` || × | 需配置 `VITE_SILICONFLOW_KEY` |
34+
| Kimi Moonshot 月之暗面大模型 | `moonshot` || × | 需配置 `VITE_MOONSHOT_KEY` |
3335

3436

3537
## 前置条件
@@ -87,14 +89,15 @@ pnpm dev
8789
VITE_SPARK_KEY=你的_星火_API_Key # 需要用冒号拼接key和secret,格式如 `key123456:secret123456`
8890
VITE_SILICONFLOW_KEY=你的_SiliconFlow_API_Key # 通常以 `sk-` 开头,如 `sk-xxxxxx`
8991
VITE_MOONSHOT_KEY=你的_Moonshot_API_Key # 通常以 `sk-` 开头,如 `sk-xxxxxx`
92+
VITE_DEEPSEEK_KEY=你的_DeepSeek_API_Key # 通常以 `sk-` 开头,如 `sk-xxxxxx`
9093
```
9194

9295

9396
## 🛠️ API 代理配置说明
9497

9598
本项目采用纯前端架构,所有后端服务均由外部或本地其他服务提供。为解决开发环境中的跨域问题,项目使用了 `Vite` 的代理功能 `server.proxy`(详见[官方文档](https://vite.dev/config/server-options.html#server-proxy))
9699

97-
以下是当前仓库的[代理配置](./vite.config.ts#L21)
100+
以下是当前仓库的[代理配置](./vite.config.ts#L23)
98101

99102
```ts
100103
server: {
@@ -117,6 +120,12 @@ server: {
117120
changeOrigin: true,
118121
ws: true,
119122
rewrite: (path) => path.replace(/^\/moonshot/, '')
123+
},
124+
'/deepseek': {
125+
target: 'https://api.deepseek.com',
126+
changeOrigin: true,
127+
ws: true,
128+
rewrite: (path) => path.replace(/^\/deepseek/, '')
120129
}
121130
}
122131
},
@@ -155,7 +164,7 @@ export const isGithubDeployed = process.env.VITE_ROUTER_MODE === 'hash'
155164

156165
默认情况下,在开发环境,`isGithubDeployed` 会被设置为 `false`, 这意味着应用将默认使用模拟数据,但也可按照需求自行切换其他大模型 API 接口。
157166

158-
当部署在演示环境时,也就是本项目在线预览地址中,则使用 `hash` 路由模式, `isGithubDeployed` 会被设置为 `true`, 这意味着应用将使用模拟数据而不是真实的大模型 API 接口
167+
当部署在演示环境时,也就是本项目在线预览地址中,则使用 `hash` 路由模式, `isGithubDeployed` 会被设置为 `true`, 这意味着真实的大模型 API 接口将被禁用
159168

160169
### 切换至真实 API
161170

@@ -180,7 +189,25 @@ export const isGithubDeployed = process.env.VITE_ROUTER_MODE === 'hash'
180189
<summary>国内在线大模型配置</summary><br>
181190

182191

183-
**1. Spark 星火认知大模型**
192+
**1. DeepSeek 深度求索大模型**
193+
- **官方开放平台**:访问 [DeepSeek 官方文档](https://api-docs.deepseek.com/zh-cn) 查看使用手册
194+
- **注册**:访问 [DeepSeek 开放平台控制台](https://platform.deepseek.com/usage) 进行注册登录
195+
- **模型 & 价格**:访问 [模型 & 价格](https://api-docs.deepseek.com/zh-cn/quick_start/pricing) 查看模型价格
196+
- **Token 购买**:访问 [账户信息 - Top up 管理](https://platform.deepseek.com/top_up) 请按需购买 API 所需 Token(一般 10 块就够了,能用好久)
197+
- **创建 API 密钥**:访问 [账户信息 - API Key 管理](https://platform.deepseek.com/api_keys) 新建 API 密钥
198+
199+
![image](https://github.com/user-attachments/assets/f3ad036f-9938-4ff5-b301-7ca645346517)
200+
201+
- **接口说明**:[首次调用 API](https://api-docs.deepseek.com/zh-cn)
202+
- **在线调试**:[官方 Chat Completions 在线调试](https://api-docs.deepseek.com/zh-cn/api/create-chat-completion)
203+
- **配置到本仓库**:将创建的 API 密钥填入 `.env` 文件中的 `VITE_DEEPSEEK_KEY` 环境变量
204+
- **DeepSeek 现已支持的大模型**:[模型 & 价格](https://api-docs.deepseek.com/zh-cn/quick_start/pricing)
205+
- **DeepSeek 现已支持的大模型-接口调用查看**:[通过接口查看](https://api-docs.deepseek.com/zh-cn/api/list-models)
206+
207+
![image](https://github.com/user-attachments/assets/8aa98691-94ac-4516-a9c4-18ac2da92c01)
208+
209+
210+
**2. Spark 星火认知大模型**
184211

185212
- **注册**:访问 [星火大模型 API](https://xinghuo.xfyun.cn/sparkapi) 进行注册并登录
186213
- **获取 API 密钥**:访问 [控制台](https://console.xfyun.cn/services/bm4) 获取 `APIKey``APISecret`
@@ -194,7 +221,7 @@ export const isGithubDeployed = process.env.VITE_ROUTER_MODE === 'hash'
194221
- **配置到本仓库**:将创建的 `APIKey``APISecret` 密钥用冒号 `:` 拼接填入到 `.env` 文件中的 `VITE_SPARK_KEY` 环境变量
195222

196223

197-
**2. SiliconFlow 大模型**
224+
**3. SiliconFlow 大模型**
198225
- **注册**:访问 [SiliconFlow 官网](https://siliconflow.cn/zh-cn/siliconcloud) 进行注册登录并创建 API 密钥
199226
- **创建 API 密钥**:访问 [账户管理 - API 密钥](https://cloud.siliconflow.cn/account/ak) 创建新 API 密钥
200227

@@ -207,7 +234,7 @@ export const isGithubDeployed = process.env.VITE_ROUTER_MODE === 'hash'
207234
![image](https://github.com/user-attachments/assets/f320f495-cb17-48ff-99c4-aaedbf87fc84)
208235

209236

210-
**3. Kimi Moonshot 月之暗面大模型**
237+
**4. Kimi Moonshot 月之暗面大模型**
211238
- **官方开放平台**:访问 [Moonshot 开放平台](https://platform.moonshot.cn/docs/intro) 查看使用手册
212239
- **注册**:访问 [Moonshot 开放平台控制台](https://platform.moonshot.cn/console) 进行注册登录
213240
- **创建 API 密钥**:访问 [账户信息 - API Key 管理](https://platform.moonshot.cn/console/api-keys) 新建 API 密钥
@@ -291,17 +318,19 @@ export const isGithubDeployed = process.env.VITE_ROUTER_MODE === 'hash'
291318

292319
| 模型名称 | 模型标识符 | 需要 API Key | 可否本地运行 | 备注 |
293320
|----------|----------|----------|----------|----------|
294-
| (默认类型)模拟数据模型 | `standard` | × || 开发环境默认使用 |
295-
| Ollama (Llama 3) 大模型 | `ollama3` | × || 需本地安装运行 Ollama 服务 |
296-
| Spark 星火大模型 | `spark` || × | 需配置 `VITE_SPARK_KEY` |
297-
| SiliconFlow 硅基流动大模型 | `siliconflow` || × | 需配置 `VITE_SILICONFLOW_KEY` |
298-
| Kimi Moonshot 月之暗面大模型 | `moonshot` || × | 需配置 `VITE_MOONSHOT_KEY` |
321+
| (默认类型)模拟数据模型 | `standard` | × || 开发环境默认使用 |
322+
| Ollama (Llama 3) 大模型 | `ollama3` | × || 需本地安装运行 Ollama 服务 |
323+
| DeepSeek-V3 | `deepseek-v3` || × | 需配置 `VITE_DEEPSEEK_KEY` |
324+
| DeepSeek-R1 (推理模型) | `deepseek-deep` || × | 需配置 `VITE_DEEPSEEK_KEY` |
325+
| Spark 星火大模型 | `spark` || × | 需配置 `VITE_SPARK_KEY` |
326+
| SiliconFlow 硅基流动大模型 | `siliconflow` || × | 需配置 `VITE_SILICONFLOW_KEY` |
327+
| Kimi Moonshot 月之暗面大模型 | `moonshot` || × | 需配置 `VITE_MOONSHOT_KEY` |
299328

300329

301330
### 🔬 主要实现
302331

303-
- **modelMappingList**: 定义了支持的每个大模型的 modelName, 响应结果的处理以及请求 API 函数,[详见代码](src/components/MarkdownPreview/models/index.ts#L85)
304-
- **transformStreamValue**: 包含了针对各种模型的响应结果转换函数,[详见代码](src/components/MarkdownPreview/models/index.ts#L85)
332+
- **modelMappingList**: 定义了支持的每个大模型的 modelName, 响应结果的处理以及请求 API 函数,[详见代码](src/components/MarkdownPreview/models/index.ts#L199)
333+
- **transformStreamValue**: 包含了针对各种模型的响应结果转换函数,[详见代码](src/components/MarkdownPreview/models/index.ts#L199)
305334
- **MarkdownPreview 组件**: 接收 `model``transformStreamFn` props 属性,根据不同模型类型处理流式响应,[详见代码](src/components/MarkdownPreview/index.vue#L9)
306335

307336
> 本项目的 `MarkdownPreview` 组件接收 `model` props 属性是为了回显不同的 `Placeholder`,如果你不需要可直接删掉该 props 参数及对应的回显逻辑
@@ -321,7 +350,7 @@ export const isGithubDeployed = process.env.VITE_ROUTER_MODE === 'hash'
321350
/>
322351
```
323352

324-
其中 `model``transformStreamFn` 的值会根据用户选择的下拉框选项自动映射到对应的模型,并实时由全局 pinia [src/store/business/index.ts](https://github.com/pdsuwwz/chatgpt-vue3-light-mvp/blob/main/src/store/business/index.ts#L18) 状态管理来管控:
353+
其中 `model``transformStreamFn` 的值会根据用户选择的下拉框选项自动映射到对应的模型,并实时由全局 pinia [src/store/business/index.ts](https://github.com/pdsuwwz/chatgpt-vue3-light-mvp/blob/main/src/store/business/index.ts#L22) 状态管理来管控:
325354

326355
```ts
327356
export const useBusinessStore = defineStore('business-store', {
@@ -341,7 +370,7 @@ export const useBusinessStore = defineStore('business-store', {
341370
})
342371
```
343372

344-
在模拟开发环境下,默认使用 `standard` 模型,同时也可以自定义修改为指定模型(尝试基于本项目二次开发的话,可以重点看下这个文件 [models/index.ts](src/components/MarkdownPreview/models/index.ts)),具体的模型类型可以根据需求进行自己二次配置:
373+
在模拟开发环境下,默认使用 `standard` 模型,同时也可以自定义修改为指定模型(尝试基于本项目二次开发的话,可以重点看下这个文件 [models/index.ts](src/components/MarkdownPreview/models/index.ts#L190)),具体的模型类型可以根据需求进行自己二次配置:
345374

346375
```ts
347376
/**

src/components/MarkdownPreview/index.vue

+82-35
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<script lang="ts" setup>
1+
<script lang="tsx" setup>
22
import { renderMarkdownText } from './plugins/markdown'
33
44
import type { CrossTransformFunction, TransformFunction } from './models'
@@ -42,6 +42,33 @@ const renderedMarkdown = computed(() => {
4242
return renderMarkdownText(displayText.value)
4343
})
4444
45+
// 接口响应是否正则排队等待
46+
const waitingForQueue = ref(false)
47+
48+
const WaitTextRender = defineComponent({
49+
render() {
50+
return (
51+
<n-empty
52+
size="large"
53+
class="font-bold [&_.n-empty\_\_icon]:flex [&_.n-empty\_\_icon]:justify-center"
54+
>
55+
{{
56+
default: () => (
57+
<div
58+
whitespace-break-spaces
59+
text-center
60+
>请求排队处理中,请耐心等待...</div>
61+
),
62+
icon: () => (
63+
<n-icon class="text-30">
64+
<div class="i-svg-spinners:clock"></div>
65+
</n-icon>
66+
)
67+
}}
68+
</n-empty>
69+
)
70+
}
71+
})
4572
4673
const abortReader = () => {
4774
if (props.reader) {
@@ -145,7 +172,14 @@ const readTextStream = async () => {
145172
readIsOver.value = true
146173
break
147174
}
148-
textBuffer.value += stream.content
175+
176+
if (stream.isWaitQueuing) {
177+
waitingForQueue.value = stream.isWaitQueuing
178+
}
179+
if (stream.content) {
180+
waitingForQueue.value = false
181+
textBuffer.value += stream.content
182+
}
149183
150184
if (typingAnimationFrame === null) {
151185
showText()
@@ -345,39 +379,47 @@ const emptyPlaceholder = computed(() => {
345379
!displayText && 'flex items-center justify-center'
346380
]"
347381
>
348-
<n-empty
349-
v-if="!displayText"
350-
size="large"
351-
class="font-bold"
352-
>
353-
<div
354-
whitespace-break-spaces
355-
text-center
356-
v-html="emptyPlaceholder"
357-
></div>
358-
<template #icon>
359-
<n-icon>
360-
<div class="i-hugeicons:ai-chat-02"></div>
361-
</n-icon>
362-
</template>
363-
</n-empty>
364-
<div
365-
v-else
366-
ref="refWrapperContent"
367-
text-16
368-
class="w-full h-full overflow-y-auto"
369-
p-24px
370-
>
371-
<div
372-
class="markdown-wrapper"
373-
v-html="renderedContent"
374-
></div>
382+
<WaitTextRender
383+
v-if="waitingForQueue && !displayText"
384+
/>
385+
<template v-else>
386+
<n-empty
387+
v-if="!displayText"
388+
size="large"
389+
class="font-bold"
390+
>
391+
<div
392+
whitespace-break-spaces
393+
text-center
394+
v-html="emptyPlaceholder"
395+
></div>
396+
<template #icon>
397+
<n-icon>
398+
<div class="i-hugeicons:ai-chat-02"></div>
399+
</n-icon>
400+
</template>
401+
</n-empty>
375402
<div
376-
v-if="readerLoading"
377-
size-24
378-
class="i-svg-spinners:pulse-3"
379-
></div>
380-
</div>
403+
v-else
404+
ref="refWrapperContent"
405+
text-16
406+
class="w-full h-full overflow-y-auto"
407+
p-24px
408+
>
409+
<div
410+
class="markdown-wrapper"
411+
v-html="renderedContent"
412+
></div>
413+
<WaitTextRender
414+
v-if="waitingForQueue"
415+
/>
416+
<div
417+
v-if="readerLoading"
418+
size-24
419+
class="i-svg-spinners:pulse-3"
420+
></div>
421+
</div>
422+
</template>
381423
</div>
382424
</div>
383425
</n-spin>
@@ -426,7 +468,12 @@ const emptyPlaceholder = computed(() => {
426468
}
427469
428470
li {
429-
line-height: 1.5;
471+
line-height: 1.7;
472+
473+
& code {
474+
--at-apply: 'bg-#e5e5e5';
475+
--at-apply: whitespace-pre m-2px px-6px py-2px rounded-5px;
476+
}
430477
}
431478
432479
ol ol {

0 commit comments

Comments
 (0)