Skip to content

Commit 6ecc61a

Browse files
quzardChanzhaoyu
andauthored
feat: 添加角色设定预留API 设定页(#768)
* add systemMessage * perf: 优化代码和类型 * perf: 补全翻译和为以后做准备 --------- Co-authored-by: ChenZhaoYu <790348264@qq.com>
1 parent e02ab1f commit 6ecc61a

File tree

14 files changed

+160
-22
lines changed

14 files changed

+160
-22
lines changed

service/src/chatgpt/index.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import axios from 'axios'
99
import { sendResponse } from '../utils'
1010
import { isNotEmptyString } from '../utils/is'
1111
import type { ApiModel, ChatContext, ChatGPTUnofficialProxyAPIOptions, ModelConfig } from '../types'
12+
import type { RequestOptions } from './types'
13+
14+
dotenv.config()
1215

1316
const ErrorCodeMessage: Record<string, string> = {
1417
401: '[OpenAI] 提供错误的API密钥 | Incorrect API key provided',
@@ -19,21 +22,19 @@ const ErrorCodeMessage: Record<string, string> = {
1922
500: '[OpenAI] 服务器繁忙,请稍后再试 | Internal Server Error',
2023
}
2124

22-
dotenv.config()
23-
2425
const timeoutMs: number = !isNaN(+process.env.TIMEOUT_MS) ? +process.env.TIMEOUT_MS : 30 * 1000
2526

2627
let apiModel: ApiModel
2728

28-
if (!process.env.OPENAI_API_KEY && !process.env.OPENAI_ACCESS_TOKEN)
29+
if (!isNotEmptyString(process.env.OPENAI_API_KEY) && !isNotEmptyString(process.env.OPENAI_ACCESS_TOKEN))
2930
throw new Error('Missing OPENAI_API_KEY or OPENAI_ACCESS_TOKEN environment variable')
3031

3132
let api: ChatGPTAPI | ChatGPTUnofficialProxyAPI
3233

3334
(async () => {
3435
// More Info: https://github.com/transitive-bullshit/chatgpt-api
3536

36-
if (process.env.OPENAI_API_KEY) {
37+
if (isNotEmptyString(process.env.OPENAI_API_KEY)) {
3738
const OPENAI_API_MODEL = process.env.OPENAI_API_MODEL
3839
const model = isNotEmptyString(OPENAI_API_MODEL) ? OPENAI_API_MODEL : 'gpt-3.5-turbo'
3940

@@ -67,17 +68,19 @@ let api: ChatGPTAPI | ChatGPTUnofficialProxyAPI
6768
}
6869
})()
6970

70-
async function chatReplyProcess(
71-
message: string,
72-
lastContext?: { conversationId?: string; parentMessageId?: string },
73-
process?: (chat: ChatMessage) => void,
74-
) {
71+
async function chatReplyProcess(options: RequestOptions) {
72+
const { message, lastContext, process, systemMessage } = options
7573
try {
7674
let options: SendMessageOptions = { timeoutMs }
7775

78-
if (lastContext) {
76+
if (apiModel === 'ChatGPTAPI') {
77+
if (isNotEmptyString(systemMessage))
78+
options.systemMessage = systemMessage
79+
}
80+
81+
if (lastContext != null) {
7982
if (apiModel === 'ChatGPTAPI')
80-
options = { parentMessageId: lastContext.parentMessageId }
83+
options.parentMessageId = lastContext.parentMessageId
8184
else
8285
options = { ...lastContext }
8386
}

service/src/chatgpt/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { ChatMessage } from 'chatgpt'
2+
3+
export interface RequestOptions {
4+
message: string
5+
lastContext?: { conversationId?: string; parentMessageId?: string }
6+
process?: (chat: ChatMessage) => void
7+
systemMessage?: string
8+
}

service/src/index.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import express from 'express'
2-
import type { ChatContext, ChatMessage } from './chatgpt'
2+
import type { RequestProps } from './types'
3+
import type { ChatMessage } from './chatgpt'
34
import { chatConfig, chatReplyProcess, currentModel } from './chatgpt'
45
import { auth } from './middleware/auth'
56
import { limiter } from './middleware/limiter'
@@ -22,11 +23,16 @@ router.post('/chat-process', [auth, limiter], async (req, res) => {
2223
res.setHeader('Content-type', 'application/octet-stream')
2324

2425
try {
25-
const { prompt, options = {} } = req.body as { prompt: string; options?: ChatContext }
26+
const { prompt, options = {}, systemMessage } = req.body as RequestProps
2627
let firstChunk = true
27-
await chatReplyProcess(prompt, options, (chat: ChatMessage) => {
28-
res.write(firstChunk ? JSON.stringify(chat) : `\n${JSON.stringify(chat)}`)
29-
firstChunk = false
28+
await chatReplyProcess({
29+
message: prompt,
30+
lastContext: options,
31+
process: (chat: ChatMessage) => {
32+
res.write(firstChunk ? JSON.stringify(chat) : `\n${JSON.stringify(chat)}`)
33+
firstChunk = false
34+
},
35+
systemMessage,
3036
})
3137
}
3238
catch (error) {

service/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import type { FetchFn } from 'chatgpt'
22

3+
export interface RequestProps {
4+
prompt: string
5+
options?: ChatContext
6+
systemMessage: string
7+
}
8+
39
export interface ChatContext {
410
conversationId?: string
511
parentMessageId?: string

src/api/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
22
import { post } from '@/utils/request'
3+
import { useSettingStore } from '@/store'
34

45
export function fetchChatAPI<T = any>(
56
prompt: string,
@@ -26,9 +27,11 @@ export function fetchChatAPIProcess<T = any>(
2627
signal?: GenericAbortSignal
2728
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void },
2829
) {
30+
const settingStore = useSettingStore()
31+
2932
return post<T>({
3033
url: '/chat-process',
31-
data: { prompt: params.prompt, options: params.options },
34+
data: { prompt: params.prompt, options: params.options, systemMessage: settingStore.systemMessage },
3235
signal: params.signal,
3336
onDownloadProgress: params.onDownloadProgress,
3437
})
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<script lang="ts" setup>
2+
import { ref } from 'vue'
3+
import { NButton, NInput, useMessage } from 'naive-ui'
4+
import { useSettingStore } from '@/store'
5+
import type { SettingsState } from '@/store/modules/settings/helper'
6+
import { t } from '@/locales'
7+
8+
const settingStore = useSettingStore()
9+
10+
const ms = useMessage()
11+
12+
const systemMessage = ref(settingStore.systemMessage ?? '')
13+
14+
function updateSettings(options: Partial<SettingsState>) {
15+
settingStore.updateSetting(options)
16+
ms.success(t('common.success'))
17+
}
18+
19+
function handleReset() {
20+
settingStore.resetSetting()
21+
ms.success(t('common.success'))
22+
window.location.reload()
23+
}
24+
</script>
25+
26+
<template>
27+
<div class="p-4 space-y-5 min-h-[200px]">
28+
<div class="space-y-6">
29+
<div class="flex items-center space-x-4">
30+
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.role') }}</span>
31+
<div class="flex-1">
32+
<NInput v-model:value="systemMessage" placeholder="" />
33+
</div>
34+
<NButton size="tiny" text type="primary" @click="updateSettings({ systemMessage })">
35+
{{ $t('common.save') }}
36+
</NButton>
37+
</div>
38+
<div class="flex items-center space-x-4">
39+
<span class="flex-shrink-0 w-[100px]">&nbsp;</span>
40+
<NButton size="small" @click="handleReset">
41+
{{ $t('common.reset') }}
42+
</NButton>
43+
</div>
44+
</div>
45+
</div>
46+
</template>

src/components/common/Setting/General.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ function handleImportButtonClick(): void {
150150
{{ $t('common.save') }}
151151
</NButton>
152152
</div>
153-
154153
<div
155154
class="flex items-center space-x-4"
156155
:class="isMobile && 'items-start'"

src/components/common/Setting/index.vue

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22
import { computed, ref } from 'vue'
33
import { NModal, NTabPane, NTabs } from 'naive-ui'
44
import General from './General.vue'
5+
import Advanced from './Advanced.vue'
56
import About from './About.vue'
7+
import { useAuthStore } from '@/store'
68
import { SvgIcon } from '@/components/common'
79
8-
const props = defineProps<Props>()
9-
10-
const emit = defineEmits<Emit>()
11-
1210
interface Props {
1311
visible: boolean
1412
}
@@ -17,6 +15,14 @@ interface Emit {
1715
(e: 'update:visible', visible: boolean): void
1816
}
1917
18+
const props = defineProps<Props>()
19+
20+
const emit = defineEmits<Emit>()
21+
22+
const authStore = useAuthStore()
23+
24+
const isChatGPTAPI = computed<boolean>(() => !!authStore.isChatGPTAPI)
25+
2026
const active = ref('General')
2127
2228
const show = computed({
@@ -42,6 +48,15 @@ const show = computed({
4248
<General />
4349
</div>
4450
</NTabPane>
51+
<NTabPane v-if="isChatGPTAPI" name="Advanced" tab="Advanced">
52+
<template #tab>
53+
<SvgIcon class="text-lg" icon="ri:equalizer-line" />
54+
<span class="ml-2">{{ $t('setting.advanced') }}</span>
55+
</template>
56+
<div class="min-h-[100px]">
57+
<Advanced />
58+
</div>
59+
</NTabPane>
4560
<NTabPane name="Config" tab="Config">
4661
<template #tab>
4762
<SvgIcon class="text-lg" icon="ri:list-settings-line" />

src/locales/en-US.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ export default {
5252
setting: {
5353
setting: 'Setting',
5454
general: 'General',
55+
advanced: 'Advanced',
5556
config: 'Config',
5657
avatarLink: 'Avatar Link',
5758
name: 'Name',
5859
description: 'Description',
60+
role: 'Role',
5961
resetUserInfo: 'Reset UserInfo',
6062
chatHistory: 'ChatHistory',
6163
theme: 'Theme',

src/locales/zh-CN.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ export default {
5252
setting: {
5353
setting: '设置',
5454
general: '总览',
55+
advanced: '高级',
5556
config: '配置',
5657
avatarLink: '头像链接',
5758
name: '名称',
5859
description: '描述',
60+
role: '角色设定',
5961
resetUserInfo: '重置用户信息',
6062
chatHistory: '聊天记录',
6163
theme: '主题',

0 commit comments

Comments
 (0)