Skip to content

Commit 80cd9be

Browse files
authored
feat: model defaults and room creation (#680)
* feat: model defaults and room creation Signed-off-by: Bob Du <i@bobdu.cc> * fix: honor model defaults when creating chat Signed-off-by: Bob Du <i@bobdu.cc> * feat: add default search for models Signed-off-by: Bob Du <i@bobdu.cc> --------- Signed-off-by: Bob Du <i@bobdu.cc>
1 parent 2d1fcc5 commit 80cd9be

15 files changed

Lines changed: 203 additions & 59 deletions

File tree

service/src/routes/room.ts

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -91,44 +91,69 @@ router.post('/room-create', auth, async (req, res) => {
9191
try {
9292
const userId = req.headers.userId as string
9393
const user = await getUserById(userId)
94-
const { title, roomId } = req.body as { title: string, roomId: number }
95-
const room = await createChatRoom(userId, title, roomId, user.config?.chatModel, user.config?.maxContextCount)
94+
const { title, roomId, chatModel, modelId } = req.body as {
95+
title: string
96+
roomId: number
97+
chatModel?: string
98+
modelId?: string
99+
}
100+
const keys = (await getCacheApiKeys()).filter(d => hasAnyRole(d.userRoles, user.roles))
101+
let selectedKey = modelId ? keys.find(key => key._id.toString() === modelId) : undefined
102+
let resolvedChatModel = chatModel || user.config?.chatModel
103+
if (selectedKey) {
104+
resolvedChatModel = `${selectedKey.chatModel}|${selectedKey._id.toString()}`
105+
}
106+
let actualModelName = resolvedChatModel || ''
107+
let specifiedKeyId: string | undefined
108+
if (!selectedKey && resolvedChatModel && resolvedChatModel.includes('|')) {
109+
const parts = resolvedChatModel.split('|')
110+
actualModelName = parts[0]
111+
specifiedKeyId = parts[1]
112+
selectedKey = keys.find(key => key._id.toString() === specifiedKeyId && key.chatModel === actualModelName)
113+
}
114+
else if (selectedKey) {
115+
actualModelName = selectedKey.chatModel
116+
specifiedKeyId = selectedKey._id.toString()
117+
}
118+
const fallbackKey = !specifiedKeyId
119+
? keys.find(key => key.chatModel === actualModelName && !key.toolsEnabled && !key.imageUploadEnabled)
120+
: undefined
121+
const defaultThinkEnabled = selectedKey?.defaultThinkEnabled || fallbackKey?.defaultThinkEnabled || false
122+
const defaultSearchEnabled = selectedKey?.defaultSearchEnabled || fallbackKey?.defaultSearchEnabled || false
123+
const room = await createChatRoom(
124+
userId,
125+
title,
126+
roomId,
127+
resolvedChatModel || '',
128+
user.config?.maxContextCount,
129+
defaultThinkEnabled,
130+
defaultSearchEnabled,
131+
)
96132
// Set imageUploadEnabled based on chatModel.
97133
if (user && room.chatModel) {
98134
// Parse model name, supporting "modelName|keyId".
99-
let actualModelName = room.chatModel
100-
let specifiedKeyId: string | undefined
135+
actualModelName = room.chatModel
136+
specifiedKeyId = undefined
101137
if (room.chatModel.includes('|')) {
102138
const parts = room.chatModel.split('|')
103139
actualModelName = parts[0]
104140
specifiedKeyId = parts[1]
105141
}
106142

107-
const keys = (await getCacheApiKeys()).filter(d => hasAnyRole(d.userRoles, user.roles))
108143
let imageUploadEnabled = false
109144
let toolsEnabled = false
110-
if (specifiedKeyId) {
111-
// Use the specified keyId configuration.
112-
const specifiedKey = keys.find(key => key._id.toString() === specifiedKeyId && key.chatModel === actualModelName)
113-
if (specifiedKey) {
114-
imageUploadEnabled = specifiedKey.imageUploadEnabled || false
115-
toolsEnabled = specifiedKey.toolsEnabled || false
116-
}
117-
}
118-
else {
119-
// Fall back to the default logic when keyId is not specified.
120-
imageUploadEnabled = false
121-
toolsEnabled = false
145+
const specifiedKey = specifiedKeyId
146+
? keys.find(key => key._id.toString() === specifiedKeyId && key.chatModel === actualModelName)
147+
: selectedKey
148+
if (specifiedKey) {
149+
imageUploadEnabled = specifiedKey.imageUploadEnabled || false
150+
toolsEnabled = specifiedKey.toolsEnabled || false
122151
}
123152

124153
await updateRoomImageUploadEnabled(userId, roomId, imageUploadEnabled || false)
125154
await updateRoomToolsEnabled(userId, roomId, toolsEnabled || false)
126-
if (toolsEnabled) {
127-
await updateRoomThinkEnabled(userId, roomId, false)
128-
await updateRoomSearchEnabled(userId, roomId, false)
129-
room.thinkEnabled = false
130-
room.searchEnabled = false
131-
}
155+
room.thinkEnabled = defaultThinkEnabled
156+
room.searchEnabled = defaultSearchEnabled
132157
room.imageUploadEnabled = imageUploadEnabled || false
133158
room.toolsEnabled = toolsEnabled || false
134159
}

service/src/storage/config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ export async function getApiKeys() {
157157
if (key.chatModel == null || key.chatModel === '') {
158158
key.chatModel = config.siteConfig.chatModels.split(',')[0] || ''
159159
}
160+
if (key.defaultThinkEnabled == null) {
161+
key.defaultThinkEnabled = false
162+
}
163+
if (key.defaultSearchEnabled == null) {
164+
key.defaultSearchEnabled = false
165+
}
160166
})
161167
return result
162168
}

service/src/storage/model.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ export class KeyConfig {
327327
baseUrl?: string
328328
toolsEnabled?: boolean
329329
imageUploadEnabled?: boolean
330+
defaultThinkEnabled?: boolean
331+
defaultSearchEnabled?: boolean
330332
inputFidelity?: 'low' | 'medium' | 'high'
331333
quality?: 'low' | 'medium' | 'high'
332334
imageModel?: 'gpt-image-1' | 'gpt-image-1.5'

service/src/storage/mongo.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,15 +355,23 @@ export async function insertChatUsage(userId: ObjectId, roomId: number, chatId:
355355
return chatUsage
356356
}
357357

358-
export async function createChatRoom(userId: string, title: string, roomId: number, chatModel: string, maxContextCount: number) {
358+
export async function createChatRoom(
359+
userId: string,
360+
title: string,
361+
roomId: number,
362+
chatModel: string,
363+
maxContextCount: number,
364+
thinkEnabled = false,
365+
searchEnabled = true,
366+
) {
359367
const config = await getCacheConfig()
360368
if (!chatModel) {
361369
chatModel = config?.siteConfig?.chatModels.split(',')[0]
362370
}
363371
if (maxContextCount === undefined) {
364372
maxContextCount = 10
365373
}
366-
const room = new ChatRoom(userId, title, roomId, chatModel, true, maxContextCount, true, false, false)
374+
const room = new ChatRoom(userId, title, roomId, chatModel, true, maxContextCount, searchEnabled, thinkEnabled, false)
367375
// After room creation, set imageUploadEnabled based on chatModel.
368376
// Initialize as false here; the room-create API will set it dynamically.
369377
room.imageUploadEnabled = false

src/api/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,10 @@ export function fetchGetChatRoomsCount<T = any>(page: number, size: number, user
340340
})
341341
}
342342

343-
export function fetchCreateChatRoom<T = any>(title: string, roomId: number, chatModel?: string) {
343+
export function fetchCreateChatRoom<T = any>(title: string, roomId: number, chatModel?: string, modelId?: string) {
344344
return post<T>({
345345
url: '/room-create',
346-
data: { title, roomId, chatModel },
346+
data: { title, roomId, chatModel, modelId },
347347
})
348348
}
349349

src/components/common/Setting/Keys.vue

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const keys = ref([])
2222
function createColumns(): DataTableColumns {
2323
return [
2424
{
25-
title: 'Key',
25+
title: () => t('setting.model.table.key'),
2626
key: 'key',
2727
resizable: true,
2828
width: 120,
@@ -31,33 +31,33 @@ function createColumns(): DataTableColumns {
3131
ellipsis: true,
3232
},
3333
{
34-
title: 'Api Model',
34+
title: () => t('setting.model.table.apiModel'),
3535
key: 'keyModel',
3636
width: 150,
3737
},
3838
{
39-
title: 'Base url',
39+
title: () => t('setting.model.table.baseUrl'),
4040
key: 'baseUrl',
4141
width: 150,
4242
},
4343
{
44-
title: 'Chat Model',
44+
title: () => t('setting.model.table.chatModel'),
4545
key: 'chatModel',
4646
width: 200,
4747
render(row: any) {
4848
return row.chatModel || '-'
4949
},
5050
},
5151
{
52-
title: 'Model Alias',
52+
title: () => t('setting.model.table.alias'),
5353
key: 'modelAlias',
5454
width: 150,
5555
render(row: any) {
5656
return row.modelAlias || '-'
5757
},
5858
},
5959
{
60-
title: 'User Roles',
60+
title: () => t('setting.model.table.userRoles'),
6161
key: 'userRoles',
6262
width: 180,
6363
render(row: any) {
@@ -80,12 +80,12 @@ function createColumns(): DataTableColumns {
8080
},
8181
},
8282
{
83-
title: 'Remark',
83+
title: () => t('setting.model.table.remark'),
8484
key: 'remark',
8585
width: 150,
8686
},
8787
{
88-
title: 'Action',
88+
title: () => t('setting.model.table.action'),
8989
key: '_id',
9090
width: 220,
9191
fixed: 'right',
@@ -131,7 +131,7 @@ const pagination = reactive({
131131
pageCount: 1,
132132
itemCount: 1,
133133
prefix({ itemCount }: { itemCount: number | undefined }) {
134-
return `Total is ${itemCount}.`
134+
return t('setting.model.total', { count: itemCount ?? 0 })
135135
},
136136
showSizePicker: true,
137137
pageSizes: [100],
@@ -171,15 +171,15 @@ async function handleUpdateApiKeyStatus(id: string, status: Status) {
171171
negativeText: t('common.no'),
172172
onPositiveClick: async () => {
173173
await fetchUpdateApiKeyStatus(id, status)
174-
ms.info('OK')
174+
ms.info(t('common.success'))
175175
await handleGetKeys(pagination.page)
176176
},
177177
})
178178
}
179179
180180
async function handleUpdateKeyConfig() {
181181
if (!keyConfig.value.key) {
182-
ms.error('Api key is required')
182+
ms.error(t('setting.model.apiKeyRequired'))
183183
return
184184
}
185185
handleSaving.value = true
@@ -215,7 +215,7 @@ onMounted(async () => {
215215
<NSpace vertical :size="12">
216216
<NSpace>
217217
<NButton @click="handleNewKey()">
218-
New Key
218+
{{ t('setting.model.new') }}
219219
</NButton>
220220
</NSpace>
221221
<NDataTable
@@ -377,6 +377,26 @@ onMounted(async () => {
377377
/>
378378
</div>
379379
</div>
380+
<div class="flex items-center space-x-4">
381+
<span class="shrink-0 w-[100px]">{{ t('setting.model.defaultThinkEnabled') }}</span>
382+
<div class="flex-1">
383+
<NSwitch
384+
:round="false"
385+
:value="keyConfig.defaultThinkEnabled || false"
386+
@update:value="(val) => { keyConfig.defaultThinkEnabled = val }"
387+
/>
388+
</div>
389+
</div>
390+
<div class="flex items-center space-x-4">
391+
<span class="shrink-0 w-[100px]">{{ t('setting.model.defaultSearchEnabled') }}</span>
392+
<div class="flex-1">
393+
<NSwitch
394+
:round="false"
395+
:value="keyConfig.defaultSearchEnabled || false"
396+
@update:value="(val) => { keyConfig.defaultSearchEnabled = val }"
397+
/>
398+
</div>
399+
</div>
380400
</div>
381401
<div class="flex items-center space-x-4">
382402
<span class="shrink-0 w-[100px]">{{ t('setting.remark') }}</span>

src/components/common/Setting/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ const show = computed({
116116
<NTabPane v-if="userStore.userInfo.root" name="KeysConfig" tab="KeysConfig">
117117
<template #tab>
118118
<SvgIcon class="text-lg" icon="ri-key-2-line" />
119-
<span class="ml-2">{{ t('setting.keysConfig') }}</span>
119+
<span class="ml-2">{{ t('setting.model.management') }}</span>
120120
</template>
121121
<Key />
122122
</NTabPane>

src/components/common/Setting/model.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ export class KeyConfig {
114114
baseUrl?: string
115115
toolsEnabled?: boolean
116116
imageUploadEnabled?: boolean
117+
defaultThinkEnabled?: boolean
118+
defaultSearchEnabled?: boolean
117119
inputFidelity?: 'low' | 'medium' | 'high'
118120
quality?: 'low' | 'medium' | 'high'
119121
imageModel?: 'gpt-image-1' | 'gpt-image-1.5'
@@ -124,6 +126,8 @@ export class KeyConfig {
124126
this.userRoles = userRoles
125127
this.status = Status.Normal
126128
this.remark = remark
129+
this.defaultThinkEnabled = false
130+
this.defaultSearchEnabled = false
127131
}
128132
}
129133

src/locales/en-US.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,24 @@
216216
"searchTest": "Test Search",
217217
"accessTokenExpiredTime": "Expired Time",
218218
"userConfig": "Users",
219-
"keysConfig": "Keys Manager",
219+
"model": {
220+
"management": "Model Management",
221+
"new": "New Model",
222+
"apiKeyRequired": "API key is required",
223+
"defaultThinkEnabled": "Default Think",
224+
"defaultSearchEnabled": "Default Web Search",
225+
"total": "Total is {count}.",
226+
"table": {
227+
"key": "Key",
228+
"apiModel": "API Model",
229+
"baseUrl": "Base URL",
230+
"chatModel": "Chat Model",
231+
"alias": "Model Alias",
232+
"userRoles": "User Roles",
233+
"remark": "Remark",
234+
"action": "Actions"
235+
}
236+
},
220237
"builtInPromptConfig": "Built-in Prompts",
221238
"builtInPromptDeleteConfirm": "Are you sure to delete this built-in prompt?",
222239
"userRoles": "User Role",

src/locales/ja-JP.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,24 @@
216216
"searchTest": "検索テスト",
217217
"accessTokenExpiredTime": "有効期限",
218218
"userConfig": "ユーザー",
219-
"keysConfig": "キー管理",
219+
"model": {
220+
"management": "モデル管理",
221+
"new": "モデル追加",
222+
"apiKeyRequired": "APIキーは必須です",
223+
"defaultThinkEnabled": "既定の思考",
224+
"defaultSearchEnabled": "既定のウェブ検索",
225+
"total": "合計 {count} 件。",
226+
"table": {
227+
"key": "キー",
228+
"apiModel": "APIモデル",
229+
"baseUrl": "Base URL",
230+
"chatModel": "チャットモデル",
231+
"alias": "モデル別名",
232+
"userRoles": "ユーザーロール",
233+
"remark": "備考",
234+
"action": "操作"
235+
}
236+
},
220237
"builtInPromptConfig": "組み込みプロンプト",
221238
"builtInPromptDeleteConfirm": "この組み込みプロンプトを削除しますか?",
222239
"userRoles": "ユーザーロール",

0 commit comments

Comments
 (0)