Skip to content

Commit

Permalink
chore: suport custom chat model
Browse files Browse the repository at this point in the history
  • Loading branch information
Kerwin committed Nov 18, 2023
1 parent 948c3b2 commit b4f138d
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 57 deletions.
6 changes: 3 additions & 3 deletions service/src/chatgpt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import httpsProxyAgent from 'https-proxy-agent'
import fetch from 'node-fetch'
import jwt_decode from 'jwt-decode'
import dayjs from 'dayjs'
import type { AuditConfig, CHATMODEL, KeyConfig, UserInfo } from '../storage/model'
import type { AuditConfig, KeyConfig, UserInfo } from '../storage/model'
import { Status } from '../storage/model'
import type { TextAuditService } from '../utils/textAudit'
import { textAuditServices } from '../utils/textAudit'
Expand All @@ -34,7 +34,7 @@ const ErrorCodeMessage: Record<string, string> = {
let auditService: TextAuditService
const _lockedKeys: { key: string; lockedTime: number }[] = []

export async function initApi(key: KeyConfig, chatModel: CHATMODEL) {
export async function initApi(key: KeyConfig, chatModel: string) {
// More Info: https://github.com/transitive-bullshit/chatgpt-api

const config = await getCacheConfig()
Expand Down Expand Up @@ -400,7 +400,7 @@ async function randomKeyConfig(keys: KeyConfig[]): Promise<KeyConfig | null> {
return thisKey
}

async function getRandomApiKey(user: UserInfo, chatModel: CHATMODEL, accountId?: string): Promise<KeyConfig | undefined> {
async function getRandomApiKey(user: UserInfo, chatModel: string, accountId?: string): Promise<KeyConfig | undefined> {
let keys = (await getCacheApiKeys()).filter(d => hasAnyRole(d.userRoles, user.roles))
.filter(d => d.chatModels.includes(chatModel))
if (accountId)
Expand Down
24 changes: 18 additions & 6 deletions service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import type { ChatMessage } from './chatgpt'
import { abortChatProcess, chatConfig, chatReplyProcess, containsSensitiveWords, initAuditService } from './chatgpt'
import { auth, getUserId } from './middleware/auth'
import { clearApiKeyCache, clearConfigCache, getApiKeys, getCacheApiKeys, getCacheConfig, getOriginConfig } from './storage/config'
import type { AuditConfig, CHATMODEL, ChatInfo, ChatOptions, Config, KeyConfig, MailConfig, SiteConfig, UserConfig, UserInfo } from './storage/model'
import { Status, UsageResponse, UserRole, chatModelOptions } from './storage/model'
import type { AuditConfig, ChatInfo, ChatOptions, Config, KeyConfig, MailConfig, SiteConfig, UserConfig, UserInfo } from './storage/model'
import { Status, UsageResponse, UserRole } from './storage/model'
import {
clearChat,
createChatRoom,
Expand Down Expand Up @@ -136,7 +136,7 @@ router.post('/room-prompt', auth, async (req, res) => {
router.post('/room-chatmodel', auth, async (req, res) => {
try {
const userId = req.headers.userId as string
const { chatModel, roomId } = req.body as { chatModel: CHATMODEL; roomId: number }
const { chatModel, roomId } = req.body as { chatModel: string; roomId: number }
const success = await updateRoomChatModel(userId, roomId, chatModel)
if (success)
res.send({ status: 'Success', message: 'Saved successfully', data: null })
Expand Down Expand Up @@ -423,8 +423,8 @@ router.post('/chat-process', [auth, limiter], async (req, res) => {
result.data.detail = {}
result.data.detail.usage = new UsageResponse()
// 因为 token 本身不计算, 所以这里默认以 gpt 3.5 的算做一个伪统计
result.data.detail.usage.prompt_tokens = textTokens(prompt, 'gpt-3.5-turbo-0613')
result.data.detail.usage.completion_tokens = textTokens(result.data.text, 'gpt-3.5-turbo-0613')
result.data.detail.usage.prompt_tokens = textTokens(prompt, 'gpt-3.5-turbo')
result.data.detail.usage.completion_tokens = textTokens(result.data.text, 'gpt-3.5-turbo')
result.data.detail.usage.total_tokens = result.data.detail.usage.prompt_tokens + result.data.detail.usage.completion_tokens
result.data.detail.usage.estimated = true
}
Expand Down Expand Up @@ -579,6 +579,18 @@ router.post('/session', async (req, res) => {
key: string
value: string
}[] = []

const chatModelOptions = config.siteConfig.chatModels.split(',').map((model: string) => {
let label = model
if (model === 'text-davinci-002-render-sha-mobile')
label = 'gpt-3.5-mobile'
return {
label,
key: model,
value: model,
}
})

let userInfo: { name: string; description: string; avatar: string; userId: string; root: boolean; roles: UserRole[]; config: UserConfig }
if (userId != null) {
const user = await getUserById(userId)
Expand Down Expand Up @@ -740,7 +752,7 @@ router.post('/user-info', auth, async (req, res) => {

router.post('/user-chat-model', auth, async (req, res) => {
try {
const { chatModel } = req.body as { chatModel: CHATMODEL }
const { chatModel } = req.body as { chatModel: string }
const userId = req.headers.userId.toString()

const user = await getUserById(userId)
Expand Down
9 changes: 6 additions & 3 deletions service/src/storage/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ObjectId } from 'mongodb'
import * as dotenv from 'dotenv'
import type { TextAuditServiceProvider } from 'src/utils/textAudit'
import { isNotEmptyString, isTextAuditServiceProvider } from '../utils/is'
import { AuditConfig, CHATMODELS, Config, KeyConfig, MailConfig, SiteConfig, TextAudioType, UserRole } from './model'
import { AuditConfig, Config, KeyConfig, MailConfig, SiteConfig, TextAudioType, UserRole } from './model'
import { getConfig, getKeys, upsertKey } from './mongo'

dotenv.config()
Expand Down Expand Up @@ -93,6 +93,9 @@ export async function getOriginConfig() {
'',
)
}
if (!isNotEmptyString(config.siteConfig.chatModels))
config.siteConfig.chatModels = 'gpt-3.5-turbo,gpt-3.5-turbo-1106,gpt-3.5-turbo-16k,gpt-3.5-turbo-16k-0613,gpt-4,gpt-4-0613,gpt-4-32k,gpt-4-32k-0613,text-davinci-002-render-sha-mobile,text-embedding-ada-002,gpt-4-mobile,gpt-4-browsing,gpt-4-1106-preview,gpt-4-vision-preview'

return config
}

Expand Down Expand Up @@ -140,8 +143,8 @@ export function clearApiKeyCache() {

export async function getApiKeys() {
const result = await getKeys()
const config = await getCacheConfig()
if (result.keys.length <= 0) {
const config = await getCacheConfig()
if (config.apiModel === 'ChatGPTAPI')
result.keys.push(await upsertKey(new KeyConfig(config.apiKey, 'ChatGPTAPI', [], [], '')))

Expand All @@ -157,7 +160,7 @@ export async function getApiKeys() {
key.userRoles.push(UserRole.Guest)
}
if (key.chatModels == null || key.chatModels.length <= 0) {
CHATMODELS.forEach((chatModel) => {
config.siteConfig.chatModels.split(',').forEach((chatModel) => {
key.chatModels.push(chatModel)
})
}
Expand Down
32 changes: 6 additions & 26 deletions service/src/storage/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,8 @@ export class UserInfo {
}

export class UserConfig {
chatModel: CHATMODEL
}

// https://platform.openai.com/docs/models/overview
// 除此之外,gpt-4-0314、gpt-4-32k-0314、gpt-3.5-turbo-0301 模型将在 9 月 13 日被弃用。
export type CHATMODEL = 'gpt-3.5-turbo' | 'gpt-3.5-turbo-0301' | 'gpt-3.5-turbo-0613' | 'gpt-3.5-turbo-16k' | 'gpt-3.5-turbo-16k-0613' | 'gpt-4' | 'gpt-4-0314' | 'gpt-4-32k' | 'gpt-4-32k-0314' | 'gpt-4-0613' | 'gpt-4-32k-0613' | 'text-davinci-002-render-sha-mobile' | 'text-embedding-ada-002' | 'gpt-4-mobile' | 'gpt-4-browsing' | 'gpt-4-1106-preview' | 'gpt-4-vision-preview'

export const CHATMODELS: CHATMODEL[] = [
'gpt-3.5-turbo', 'gpt-3.5-turbo-0301', 'gpt-3.5-turbo-0613', 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-16k-0613', 'gpt-4', 'gpt-4-0314', 'gpt-4-32k', 'gpt-4-32k-0314', 'gpt-4-0613', 'gpt-4-32k-0613', 'text-davinci-002-render-sha-mobile', 'text-embedding-ada-002', 'gpt-4-mobile', 'gpt-4-browsing', 'gpt-4-1106-preview', 'gpt-4-vision-preview',
]

export const chatModelOptions = [
'gpt-3.5-turbo', 'gpt-3.5-turbo-0301', 'gpt-3.5-turbo-0613', 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-16k-0613', 'gpt-4', 'gpt-4-0314', 'gpt-4-32k', 'gpt-4-32k-0314', 'gpt-4-0613', 'gpt-4-32k-0613', 'text-davinci-002-render-sha-mobile', 'text-embedding-ada-002', 'gpt-4-mobile', 'gpt-4-browsing', 'gpt-4-1106-preview', 'gpt-4-vision-preview',
].map((model: string) => {
let label = model
if (model === 'text-davinci-002-render-sha-mobile')
label = 'gpt-3.5-mobile'
return {
label,
key: model,
value: model,
}
})
chatModel: string
}

export class ChatRoom {
_id: ObjectId
Expand All @@ -86,7 +65,7 @@ export class ChatRoom {
status: Status = Status.Normal
// only access token used
accountId?: string
chatModel: CHATMODEL
chatModel: string
constructor(userId: string, title: string, roomId: number) {
this.userId = userId
this.title = title
Expand Down Expand Up @@ -198,6 +177,7 @@ export class SiteConfig {
public registerReview?: boolean,
public registerMails?: string,
public siteDomain?: string,
public chatModels?: string,
) { }
}

Expand Down Expand Up @@ -233,11 +213,11 @@ export class KeyConfig {
_id: ObjectId
key: string
keyModel: APIMODEL
chatModels: CHATMODEL[]
chatModels: string[]
userRoles: UserRole[]
status: Status
remark: string
constructor(key: string, keyModel: APIMODEL, chatModels: CHATMODEL[], userRoles: UserRole[], remark: string) {
constructor(key: string, keyModel: APIMODEL, chatModels: string[], userRoles: UserRole[], remark: string) {
this.key = key
this.keyModel = keyModel
this.chatModels = chatModels
Expand Down
6 changes: 3 additions & 3 deletions service/src/storage/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as dotenv from 'dotenv'
import dayjs from 'dayjs'
import { md5 } from '../utils/security'
import { ChatInfo, ChatRoom, ChatUsage, Status, UserConfig, UserInfo, UserRole } from './model'
import type { CHATMODEL, ChatOptions, Config, KeyConfig, UsageResponse } from './model'
import type { ChatOptions, Config, KeyConfig, UsageResponse } from './model'

dotenv.config()

Expand Down Expand Up @@ -120,7 +120,7 @@ export async function updateRoomAccountId(userId: string, roomId: number, accoun
return result.modifiedCount > 0
}

export async function updateRoomChatModel(userId: string, roomId: number, chatModel: CHATMODEL) {
export async function updateRoomChatModel(userId: string, roomId: number, chatModel: string) {
const query = { userId, roomId }
const update = {
$set: {
Expand Down Expand Up @@ -217,7 +217,7 @@ export async function updateUserInfo(userId: string, user: UserInfo) {
, { $set: { name: user.name, description: user.description, avatar: user.avatar } })
}

export async function updateUserChatModel(userId: string, chatModel: CHATMODEL) {
export async function updateUserChatModel(userId: string, chatModel: string) {
return userCol.updateOne({ _id: new ObjectId(userId) }
, { $set: { 'config.chatModel': chatModel } })
}
Expand Down
4 changes: 2 additions & 2 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
import { get, post } from '@/utils/request'
import type { AuditConfig, CHATMODEL, ConfigState, KeyConfig, MailConfig, SiteConfig, Status, UserInfo, UserPassword } from '@/components/common/Setting/model'
import type { AuditConfig, ConfigState, KeyConfig, MailConfig, SiteConfig, Status, UserInfo, UserPassword } from '@/components/common/Setting/model'
import { useAuthStore, useSettingStore } from '@/store'

export function fetchChatConfig<T = any>() {
Expand Down Expand Up @@ -116,7 +116,7 @@ export function fetchUpdateUserInfo<T = any>(name: string, avatar: string, descr
})
}

export function fetchUpdateUserChatModel<T = any>(chatModel: CHATMODEL) {
export function fetchUpdateUserChatModel<T = any>(chatModel: string) {
return post<T>({
url: '/user-chat-model',
data: { chatModel },
Expand Down
3 changes: 1 addition & 2 deletions src/components/common/Setting/Keys.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts" setup>
import { h, onMounted, reactive, ref } from 'vue'
import { NButton, NDataTable, NInput, NModal, NSelect, NSpace, NSwitch, NTag, useDialog, useMessage } from 'naive-ui'
import type { CHATMODEL } from './model'
import { KeyConfig, Status, UserRole, apiModelOptions, userRoleOptions } from './model'
import { fetchGetKeys, fetchUpdateApiKeyStatus, fetchUpsertApiKey } from '@/api'
import { t } from '@/locales'
Expand Down Expand Up @@ -39,7 +38,7 @@ const columns = [
key: 'chatModels',
width: 320,
render(row: any) {
const tags = row.chatModels.map((chatModel: CHATMODEL) => {
const tags = row.chatModels.map((chatModel: string) => {
return h(
NTag,
{
Expand Down
12 changes: 12 additions & 0 deletions src/components/common/Setting/Site.vue
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ onMounted(() => {
/>
</div>
</div>
<div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.chatModels') }}</span>
<div class="flex-1">
<NInput
:value="config && config.chatModels"
placeholder="英文逗号分割 | English comma separated"
type="textarea"
:autosize="{ minRows: 1, maxRows: 4 }"
@input="(val) => { if (config) config.chatModels = val }"
/>
</div>
</div>
<div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]" />
<NButton :loading="saving" type="primary" @click="updateSiteInfo(config)">
Expand Down
11 changes: 5 additions & 6 deletions src/components/common/Setting/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@ export class ConfigState {
auditConfig?: AuditConfig
}

// https://platform.openai.com/docs/models/overview
export type CHATMODEL = 'gpt-3.5-turbo' | 'gpt-3.5-turbo-0301' | 'gpt-3.5-turbo-0613' | 'gpt-3.5-turbo-16k' | 'gpt-3.5-turbo-16k-0613' | 'gpt-4' | 'gpt-4-0314' | 'gpt-4-32k' | 'gpt-4-32k-0314' | 'gpt-4-0613' | 'gpt-4-32k-0613' | 'text-davinci-002-render-sha-mobile' | 'text-embedding-ada-002' | 'gpt-4-mobile' | 'gpt-4-browsing' | 'gpt-4-1106-preview' | 'gpt-4-vision-preview'

export class UserConfig {
chatModel?: CHATMODEL
chatModel?: string
}

// https://platform.openai.com/docs/models/overview
export class SiteConfig {
siteTitle?: string
loginEnabled?: boolean
Expand All @@ -30,6 +28,7 @@ export class SiteConfig {
registerReview?: boolean
registerMails?: string
siteDomain?: string
chatModels?: string
}

export class MailConfig {
Expand Down Expand Up @@ -88,11 +87,11 @@ export class KeyConfig {
_id?: string
key: string
keyModel: APIMODEL
chatModels: CHATMODEL[]
chatModels: string[]
userRoles: UserRole[]
status: Status
remark: string
constructor(key: string, keyModel: APIMODEL, chatModels: CHATMODEL[], userRoles: UserRole[], remark: string) {
constructor(key: string, keyModel: APIMODEL, chatModels: string[], userRoles: UserRole[], remark: string) {
this.key = key
this.keyModel = keyModel
this.chatModels = chatModels
Expand Down
4 changes: 1 addition & 3 deletions src/typings/chat.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
declare namespace Chat {
import { CHATMODEL } from "@/components/common/Setting/model"

interface Chat {
uuid?: number
dateTime: string
Expand All @@ -27,7 +25,7 @@ declare namespace Chat {
all?: boolean
prompt?: string
usingContext: boolean
chatModel?: CHATMODEL
chatModel?: string
}

interface ChatState {
Expand Down
5 changes: 2 additions & 3 deletions src/views/chat/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { t } from '@/locales'
import { debounce } from '@/utils/functions/debounce'
import IconPrompt from '@/icons/Prompt.vue'
import { UserConfig } from '@/components/common/Setting/model'
import type { CHATMODEL } from '@/components/common/Setting/model'
const Prompt = defineAsyncComponent(() => import('@/components/common/Setting/Prompt.vue'))
let controller = new AbortController()
Expand Down Expand Up @@ -49,7 +48,7 @@ const firstLoading = ref<boolean>(false)
const loading = ref<boolean>(false)
const inputRef = ref<Ref | null>(null)
const showPrompt = ref(false)
const nowSelectChatModel = ref<CHATMODEL | null>(null)
const nowSelectChatModel = ref<string | null>(null)
const currentChatModel = computed(() => nowSelectChatModel.value ?? currentChatHistory.value?.chatModel ?? userStore.userInfo.config.chatModel)
let loadingms: MessageReactive
Expand Down Expand Up @@ -581,7 +580,7 @@ const footerClass = computed(() => {
return classes
})
async function handleSyncChatModel(chatModel: CHATMODEL) {
async function handleSyncChatModel(chatModel: string) {
nowSelectChatModel.value = chatModel
if (userStore.userInfo.config == null)
userStore.userInfo.config = new UserConfig()
Expand Down

0 comments on commit b4f138d

Please sign in to comment.