Skip to content

Commit

Permalink
feat: 添加角色设定预留API 设定页(Chanzhaoyu#768)
Browse files Browse the repository at this point in the history
* add systemMessage

* perf: 优化代码和类型

* perf: 补全翻译和为以后做准备

---------

Co-authored-by: ChenZhaoYu <790348264@qq.com>
  • Loading branch information
2 people authored and pzcn committed Mar 22, 2023
1 parent df7dcde commit 7c463fd
Show file tree
Hide file tree
Showing 14 changed files with 170 additions and 16 deletions.
26 changes: 18 additions & 8 deletions service/src/chatgpt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { getCacheConfig, getOriginConfig } from '../storage/config'
import { sendResponse } from '../utils'
import { isNotEmptyString } from '../utils/is'
import type { ApiModel, ChatContext, ChatGPTUnofficialProxyAPIOptions, ModelConfig } from '../types'
import type { RequestOptions } from './types'

dotenv.config()

const ErrorCodeMessage: Record<string, string> = {
401: '[OpenAI] 提供错误的API密钥 | Incorrect API key provided',
Expand All @@ -20,10 +23,13 @@ const ErrorCodeMessage: Record<string, string> = {
500: '[OpenAI] 服务器繁忙,请稍后再试 | Internal Server Error',
}

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

let apiModel: ApiModel

if (!isNotEmptyString(process.env.OPENAI_API_KEY) && !isNotEmptyString(process.env.OPENAI_ACCESS_TOKEN))
throw new Error('Missing OPENAI_API_KEY or OPENAI_ACCESS_TOKEN environment variable')

let api: ChatGPTAPI | ChatGPTUnofficialProxyAPI

export async function initApi() {
Expand Down Expand Up @@ -78,18 +84,22 @@ export async function initApi() {
}
}

async function chatReplyProcess(
message: string,
lastContext?: { conversationId?: string; parentMessageId?: string },
process?: (chat: ChatMessage) => void,
) {


async function chatReplyProcess(options: RequestOptions) {
const { message, lastContext, process, systemMessage } = options
const timeoutMs = (await getCacheConfig()).timeoutMs
try {
let options: SendMessageOptions = { timeoutMs }

if (lastContext) {
if (apiModel === 'ChatGPTAPI') {
if (isNotEmptyString(systemMessage))
options.systemMessage = systemMessage
}

if (lastContext != null) {
if (apiModel === 'ChatGPTAPI')
options = { parentMessageId: lastContext.parentMessageId }
options.parentMessageId = lastContext.parentMessageId
else
options = { ...lastContext }
}
Expand Down
8 changes: 8 additions & 0 deletions service/src/chatgpt/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { ChatMessage } from 'chatgpt'

export interface RequestOptions {
message: string
lastContext?: { conversationId?: string; parentMessageId?: string }
process?: (chat: ChatMessage) => void
systemMessage?: string
}
16 changes: 11 additions & 5 deletions service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import jwt from 'jsonwebtoken'
import { ObjectId } from 'mongodb'
import type { ChatContext, ChatMessage } from './chatgpt'
import { chatConfig, chatReplyProcess, currentModel, initApi } from './chatgpt'
import type { RequestProps } from './types'
import { auth } from './middleware/auth'
import { clearConfigCache, getCacheConfig, getOriginConfig } from './storage/config'
import type { ChatOptions, Config, MailConfig, SiteConfig, UserInfo } from './storage/model'
Expand Down Expand Up @@ -155,15 +156,20 @@ router.post('/chat-process', [auth, limiter], async (req, res) => {
res.setHeader('Content-type', 'application/octet-stream')

try {
const { roomId, uuid, regenerate, prompt, options = {} } = req.body as
{ roomId: number; uuid: number; regenerate: boolean; prompt: string; options?: ChatContext }
const { roomId, uuid, regenerate, prompt, options = {}, systemMessage } = req.body as RequestProps
const message = regenerate
? await getChat(roomId, uuid)
: await insertChat(uuid, prompt, roomId, options as ChatOptions)
let firstChunk = true
const result = await chatReplyProcess(prompt, options, (chat: ChatMessage) => {
res.write(firstChunk ? JSON.stringify(chat) : `\n${JSON.stringify(chat)}`)
firstChunk = false
await chatReplyProcess({
message: prompt,
lastContext: options,
process: (chat: ChatMessage) => {
res.write(firstChunk ? JSON.stringify(chat) : `\n${JSON.stringify(chat)}`)
firstChunk = false
},
systemMessage,

})
if (result.status === 'Success')
await updateChat(message._id, result.data.text, result.data.id)
Expand Down
9 changes: 9 additions & 0 deletions service/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import type { FetchFn } from 'chatgpt'

export interface RequestProps {
prompt: string
options?: ChatContext
systemMessage: string
roomId: number
uuid: number
regenerate: boolean
}

export interface ChatContext {
conversationId?: string
parentMessageId?: string
Expand Down
5 changes: 4 additions & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
import { get, post } from '@/utils/request'
import type { ConfigState, MailConfig, SiteConfig } from '@/components/common/Setting/model'
import { useSettingStore } from '@/store'

export function fetchChatAPI<T = any>(
prompt: string,
Expand Down Expand Up @@ -30,9 +31,11 @@ export function fetchChatAPIProcess<T = any>(
signal?: GenericAbortSignal
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void },
) {
const settingStore = useSettingStore()

return post<T>({
url: '/chat-process',
data: { roomId: params.roomId, uuid: params.uuid, regenerate: params.regenerate || false, prompt: params.prompt, options: params.options },
data: { roomId: params.roomId, uuid: params.uuid, regenerate: params.regenerate || false, prompt: params.prompt, options: params.options, systemMessage: settingStore.systemMessage },
signal: params.signal,
onDownloadProgress: params.onDownloadProgress,
})
Expand Down
46 changes: 46 additions & 0 deletions src/components/common/Setting/Advanced.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { NButton, NInput, useMessage } from 'naive-ui'
import { useSettingStore } from '@/store'
import type { SettingsState } from '@/store/modules/settings/helper'
import { t } from '@/locales'
const settingStore = useSettingStore()
const ms = useMessage()
const systemMessage = ref(settingStore.systemMessage ?? '')
function updateSettings(options: Partial<SettingsState>) {
settingStore.updateSetting(options)
ms.success(t('common.success'))
}
function handleReset() {
settingStore.resetSetting()
ms.success(t('common.success'))
window.location.reload()
}
</script>

<template>
<div class="p-4 space-y-5 min-h-[200px]">
<div class="space-y-6">
<div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.role') }}</span>
<div class="flex-1">
<NInput v-model:value="systemMessage" placeholder="" />
</div>
<NButton size="tiny" text type="primary" @click="updateSettings({ systemMessage })">
{{ $t('common.save') }}
</NButton>
</div>
<div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">&nbsp;</span>
<NButton size="small" @click="handleReset">
{{ $t('common.reset') }}
</NButton>
</div>
</div>
</div>
</template>
1 change: 0 additions & 1 deletion src/components/common/Setting/General.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ function handleImportButtonClick(): void {
<NInput v-model:value="avatar" placeholder="" />
</div>
</div>

<div
class="flex items-center space-x-4"
:class="isMobile && 'items-start'"
Expand Down
20 changes: 19 additions & 1 deletion src/components/common/Setting/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import { computed, ref } from 'vue'
import { NModal, NTabPane, NTabs } from 'naive-ui'
import General from './General.vue'
import Advanced from './Advanced.vue'
import About from './About.vue'
import Site from './Site.vue'
import Mail from './Mail.vue'
import { SvgIcon } from '@/components/common'
import { useUserStore } from '@/store'
import { useUserStore, useAuthStore } from '@/store'
const props = defineProps<Props>()
Expand All @@ -22,6 +23,14 @@ interface Emit {
(e: 'update:visible', visible: boolean): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emit>()
const authStore = useAuthStore()
const isChatGPTAPI = computed<boolean>(() => !!authStore.isChatGPTAPI)
const active = ref('General')
const show = computed({
Expand All @@ -47,6 +56,15 @@ const show = computed({
<General />
</div>
</NTabPane>
<NTabPane v-if="isChatGPTAPI" name="Advanced" tab="Advanced">
<template #tab>
<SvgIcon class="text-lg" icon="ri:equalizer-line" />
<span class="ml-2">{{ $t('setting.advanced') }}</span>
</template>
<div class="min-h-[100px]">
<Advanced />
</div>
</NTabPane>
<NTabPane v-if="userStore.userInfo.root" name="Config" tab="Config">
<template #tab>
<SvgIcon class="text-lg" icon="ri:list-settings-line" />
Expand Down
3 changes: 3 additions & 0 deletions src/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ export default {
name: 'Name',
description: 'Description',
saveUserInfo: 'Save User Info',
advanced: 'Advanced',
role: 'Role',
resetUserInfo: 'Reset UserInfo',
chatHistory: 'ChatHistory',
theme: 'Theme',
language: 'Language',
Expand Down
3 changes: 3 additions & 0 deletions src/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ export default {
name: '名称',
description: '描述',
saveUserInfo: '保存用户信息',
advanced: '高级',
role: '角色设定',
resetUserInfo: '重置用户信息',
chatHistory: '聊天记录',
theme: '主题',
language: '语言',
Expand Down
3 changes: 3 additions & 0 deletions src/locales/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ export default {
name: '名稱',
description: '描述',
saveUserInfo: '保存用户資訊',
advanced: '高級',
role: '角色設定',
resetUserInfo: '重設使用者資訊',
chatHistory: '紀錄',
theme: '主題',
language: '語言',
Expand Down
1 change: 1 addition & 0 deletions src/store/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './app'
export * from './chat'
export * from './user'
export * from './prompt'
export * from './settings'
export * from './auth'
23 changes: 23 additions & 0 deletions src/store/modules/settings/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ss } from '@/utils/storage'

const LOCAL_NAME = 'settingsStorage'

export interface SettingsState {
systemMessage: string
}

export function defaultSetting(): SettingsState {
const currentDate = new Date().toISOString().split('T')[0]
return {
systemMessage: `You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.\nKnowledge cutoff: 2021-09-01\nCurrent date: ${currentDate}`,
}
}

export function getLocalState(): SettingsState {
const localSetting: SettingsState | undefined = ss.get(LOCAL_NAME)
return { ...defaultSetting(), ...localSetting }
}

export function setLocalState(setting: SettingsState): void {
ss.set(LOCAL_NAME, setting)
}
22 changes: 22 additions & 0 deletions src/store/modules/settings/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defineStore } from 'pinia'
import type { SettingsState } from './helper'
import { defaultSetting, getLocalState, setLocalState } from './helper'

export const useSettingStore = defineStore('setting-store', {
state: (): SettingsState => getLocalState(),
actions: {
updateSetting(settings: Partial<SettingsState>) {
this.$state = { ...this.$state, ...settings }
this.recordState()
},

resetSetting() {
this.$state = defaultSetting()
this.recordState()
},

recordState() {
setLocalState(this.$state)
},
},
})

0 comments on commit 7c463fd

Please sign in to comment.