Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

add systemMessage #768

Merged
merged 3 commits into from
Mar 22, 2023
Merged
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
25 changes: 14 additions & 11 deletions service/src/chatgpt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import axios from 'axios'
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 @@ -19,21 +22,19 @@ 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 (!process.env.OPENAI_API_KEY && !process.env.OPENAI_ACCESS_TOKEN)
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

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

if (process.env.OPENAI_API_KEY) {
if (isNotEmptyString(process.env.OPENAI_API_KEY)) {
const OPENAI_API_MODEL = process.env.OPENAI_API_MODEL
const model = isNotEmptyString(OPENAI_API_MODEL) ? OPENAI_API_MODEL : 'gpt-3.5-turbo'

Expand Down Expand Up @@ -67,17 +68,19 @@ let api: ChatGPTAPI | ChatGPTUnofficialProxyAPI
}
})()

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
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
@@ -1,5 +1,6 @@
import express from 'express'
import type { ChatContext, ChatMessage } from './chatgpt'
import type { RequestProps } from './types'
import type { ChatMessage } from './chatgpt'
import { chatConfig, chatReplyProcess, currentModel } from './chatgpt'
import { auth } from './middleware/auth'
import { limiter } from './middleware/limiter'
Expand All @@ -22,11 +23,16 @@ router.post('/chat-process', [auth, limiter], async (req, res) => {
res.setHeader('Content-type', 'application/octet-stream')

try {
const { prompt, options = {} } = req.body as { prompt: string; options?: ChatContext }
const { prompt, options = {}, systemMessage } = req.body as RequestProps
let firstChunk = true
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,
})
}
catch (error) {
Expand Down
6 changes: 6 additions & 0 deletions service/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { FetchFn } from 'chatgpt'

export interface RequestProps {
prompt: string
options?: ChatContext
systemMessage: string
}

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,5 +1,6 @@
import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
import { post } from '@/utils/request'
import { useSettingStore } from '@/store'

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

return post<T>({
url: '/chat-process',
data: { prompt: params.prompt, options: params.options },
data: { 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 @@ -150,7 +150,6 @@ function handleImportButtonClick(): void {
{{ $t('common.save') }}
</NButton>
</div>

<div
class="flex items-center space-x-4"
:class="isMobile && 'items-start'"
Expand Down
23 changes: 19 additions & 4 deletions src/components/common/Setting/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
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 { useAuthStore } from '@/store'
import { SvgIcon } from '@/components/common'

const props = defineProps<Props>()

const emit = defineEmits<Emit>()

interface Props {
visible: boolean
}
Expand All @@ -17,6 +15,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 @@ -42,6 +48,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 name="Config" tab="Config">
<template #tab>
<SvgIcon class="text-lg" icon="ri:list-settings-line" />
Expand Down
2 changes: 2 additions & 0 deletions src/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ export default {
setting: {
setting: 'Setting',
general: 'General',
advanced: 'Advanced',
config: 'Config',
avatarLink: 'Avatar Link',
name: 'Name',
description: 'Description',
role: 'Role',
resetUserInfo: 'Reset UserInfo',
chatHistory: 'ChatHistory',
theme: 'Theme',
Expand Down
2 changes: 2 additions & 0 deletions src/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ export default {
setting: {
setting: '设置',
general: '总览',
advanced: '高级',
config: '配置',
avatarLink: '头像链接',
name: '名称',
description: '描述',
role: '角色设定',
resetUserInfo: '重置用户信息',
chatHistory: '聊天记录',
theme: '主题',
Expand Down
2 changes: 2 additions & 0 deletions src/locales/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ export default {
setting: {
setting: '設定',
general: '總覽',
advanced: '高級',
config: '設定',
avatarLink: '頭貼連結',
name: '名稱',
description: '描述',
role: '角色設定',
resetUserInfo: '重設使用者資訊',
chatHistory: '紀錄',
theme: '主題',
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)
},
},
})