Skip to content

feat: add configurable max context count per room #622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 6, 2025
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
2 changes: 1 addition & 1 deletion service/src/chatgpt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async function chatReplyProcess(options: RequestOptions) {
const searchEnabled = options.room.searchEnabled
const key = await getRandomApiKey(options.user, model)
const userId = options.user._id.toString()
const maxContextCount = options.user.advanced.maxContextCount ?? 20
const maxContextCount = options.room.maxContextCount ?? 10
const messageId = options.messageId
if (key == null || key === undefined)
throw new Error('没有对应的apikeys配置。请再试一次 | No available apikeys configuration. Please try again.')
Expand Down
34 changes: 29 additions & 5 deletions service/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AnnounceConfig, AuditConfig, Config, GiftCard, KeyConfig, MailConfig, SiteConfig, UserConfig, UserInfo } from './storage/model'
import type { AnnounceConfig, AuditConfig, Config, GiftCard, KeyConfig, MailConfig, SiteConfig, UserInfo } from './storage/model'
import type { AuthJwtPayload } from './types'
import * as path from 'node:path'
import * as process from 'node:process'
Expand All @@ -16,7 +16,7 @@ import { router as promptRouter } from './routes/prompt'
import { router as roomRouter } from './routes/room'
import { router as uploadRouter } from './routes/upload'
import { clearApiKeyCache, clearConfigCache, getApiKeys, getCacheApiKeys, getCacheConfig, getOriginConfig } from './storage/config'
import { AdvancedConfig, Status, UserRole } from './storage/model'
import { AdvancedConfig, Status, UserConfig, UserRole } from './storage/model'
import {
createUser,
disableUser2FA,
Expand All @@ -35,6 +35,7 @@ import {
updateUserAmount,
updateUserChatModel,
updateUserInfo,
updateUserMaxContextCount,
updateUserPassword,
updateUserPasswordWithVerifyOld,
updateUserStatus,
Expand Down Expand Up @@ -183,6 +184,16 @@ router.post('/session', async (req, res) => {
return
}

if (!user?.config) {
user.config = new UserConfig()
}
if (!user.config?.chatModel) {
user.config.chatModel = config?.siteConfig?.chatModels.split(',')[0]
}
if (user.config?.maxContextCount === undefined) {
user.config.maxContextCount = 10
}

userInfo = {
name: user.name,
description: user.description,
Expand Down Expand Up @@ -451,6 +462,22 @@ router.post('/user-chat-model', auth, async (req, res) => {
}
})

router.post('/user-max-context-count', auth, async (req, res) => {
try {
const { maxContextCount } = req.body as { maxContextCount: number }
const userId = req.headers.userId.toString()

const user = await getUserById(userId)
if (user == null || user.status !== Status.Normal)
throw new Error('用户不存在 | User does not exist.')
await updateUserMaxContextCount(userId, maxContextCount)
res.send({ status: 'Success', message: '更新成功 | Update successfully' })
}
catch (error) {
res.send({ status: 'Fail', message: error.message, data: null })
}
})

router.get('/users', rootAuth, async (req, res) => {
try {
const page = +req.query.page
Expand Down Expand Up @@ -811,7 +838,6 @@ router.post('/setting-advanced', auth, async (req, res) => {
systemMessage: string
temperature: number
top_p: number
maxContextCount: number
sync: boolean
}
if (config.sync) {
Expand All @@ -824,7 +850,6 @@ router.post('/setting-advanced', auth, async (req, res) => {
config.systemMessage,
config.temperature,
config.top_p,
config.maxContextCount,
)
await updateConfig(thisConfig)
clearConfigCache()
Expand All @@ -834,7 +859,6 @@ router.post('/setting-advanced', auth, async (req, res) => {
config.systemMessage,
config.temperature,
config.top_p,
config.maxContextCount,
))
res.send({ status: 'Success', message: '操作成功 | Successfully' })
}
Expand Down
24 changes: 22 additions & 2 deletions service/src/routes/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {
existsChatRoom,
getChatRooms,
getChatRoomsCount,
getUserById,
renameChatRoom,
updateRoomChatModel,
updateRoomMaxContextCount,
updateRoomPrompt,
updateRoomSearchEnabled,
updateRoomThinkEnabled,
Expand All @@ -28,6 +30,7 @@ router.get('/chatrooms', auth, async (req, res) => {
isEdit: false,
prompt: r.prompt,
usingContext: r.usingContext === undefined ? true : r.usingContext,
maxContextCount: r.maxContextCount === undefined ? 10 : r.maxContextCount,
chatModel: r.chatModel,
searchEnabled: !!r.searchEnabled,
thinkEnabled: !!r.thinkEnabled,
Expand Down Expand Up @@ -81,8 +84,9 @@ router.get('/chatrooms-count', auth, async (req, res) => {
router.post('/room-create', auth, async (req, res) => {
try {
const userId = req.headers.userId as string
const { title, roomId, chatModel } = req.body as { title: string, roomId: number, chatModel: string }
const room = await createChatRoom(userId, title, roomId, chatModel)
const user = await getUserById(userId)
const { title, roomId } = req.body as { title: string, roomId: number }
const room = await createChatRoom(userId, title, roomId, user.config?.chatModel, user.config?.maxContextCount)
res.send({ status: 'Success', message: null, data: room })
}
catch (error) {
Expand Down Expand Up @@ -139,6 +143,22 @@ router.post('/room-chatmodel', auth, async (req, res) => {
}
})

router.post('/room-max-context-count', auth, async (req, res) => {
try {
const userId = req.headers.userId as string
const { maxContextCount, roomId } = req.body as { maxContextCount: number, roomId: number }
const success = await updateRoomMaxContextCount(userId, roomId, maxContextCount)
if (success)
res.send({ status: 'Success', message: 'Saved successfully', data: null })
else
res.send({ status: 'Fail', message: 'Saved Failed', data: null })
}
catch (error) {
console.error(error)
res.send({ status: 'Fail', message: 'Update error', data: null })
}
})

router.post('/room-search-enabled', auth, async (req, res) => {
try {
const userId = req.headers.userId as string
Expand Down
1 change: 0 additions & 1 deletion service/src/storage/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ export async function getOriginConfig() {
'You are a large language model. Follow the user\'s instructions carefully. Respond using markdown (latex start with $).',
0.8,
1,
20,
)
}

Expand Down
8 changes: 5 additions & 3 deletions service/src/storage/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class UserInfo {

export class UserConfig {
chatModel: string
maxContextCount: number
}

export class ChatRoom {
Expand All @@ -80,16 +81,18 @@ export class ChatRoom {
title: string
prompt: string
usingContext: boolean
maxContextCount: number
status: Status = Status.Normal
chatModel: string
searchEnabled: boolean
thinkEnabled: boolean
constructor(userId: string, title: string, roomId: number, chatModel: string, searchEnabled: boolean, thinkEnabled: boolean) {
constructor(userId: string, title: string, roomId: number, chatModel: string, usingContext: boolean, maxContextCount: number, searchEnabled: boolean, thinkEnabled: boolean) {
this.userId = userId
this.title = title
this.prompt = undefined
this.roomId = roomId
this.usingContext = true
this.usingContext = usingContext
this.maxContextCount = maxContextCount
this.chatModel = chatModel
this.searchEnabled = searchEnabled
this.thinkEnabled = thinkEnabled
Expand Down Expand Up @@ -263,7 +266,6 @@ export class AdvancedConfig {
public systemMessage: string,
public temperature: number,
public top_p: number,
public maxContextCount: number,
) { }
}

Expand Down
27 changes: 25 additions & 2 deletions service/src/storage/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,15 @@ export async function insertChatUsage(userId: ObjectId, roomId: number, chatId:
return chatUsage
}

export async function createChatRoom(userId: string, title: string, roomId: number, chatModel: string) {
const room = new ChatRoom(userId, title, roomId, chatModel, true, false)
export async function createChatRoom(userId: string, title: string, roomId: number, chatModel: string, maxContextCount: number) {
const config = await getCacheConfig()
if (!chatModel) {
chatModel = config?.siteConfig?.chatModels.split(',')[0]
}
if (maxContextCount === undefined) {
maxContextCount = 10
}
const room = new ChatRoom(userId, title, roomId, chatModel, true, maxContextCount, true, false)
await roomCol.insertOne(room)
return room
}
Expand Down Expand Up @@ -220,6 +227,17 @@ export async function updateRoomThinkEnabled(userId: string, roomId: number, thi
return result.modifiedCount > 0
}

export async function updateRoomMaxContextCount(userId: string, roomId: number, maxContextCount: number) {
const query = { userId, roomId }
const update = {
$set: {
maxContextCount,
},
}
const result = await roomCol.updateOne(query, update)
return result.modifiedCount > 0
}

export async function getChatRooms(userId: string) {
const cursor = roomCol.find({ userId, status: { $ne: Status.Deleted } })
const rooms = []
Expand Down Expand Up @@ -422,6 +440,7 @@ export async function createUser(email: string, password: string, roles?: UserRo
// Use the first item from the globally available chatModel configuration as the default model for new users
userInfo.config = new UserConfig()
userInfo.config.chatModel = config?.siteConfig?.chatModels.split(',')[0]
userInfo.config.maxContextCount = 10

await userCol.insertOne(userInfo)
return userInfo
Expand All @@ -440,6 +459,10 @@ export async function updateUserChatModel(userId: string, chatModel: string) {
await userCol.updateOne({ _id: new ObjectId(userId) }, { $set: { 'config.chatModel': chatModel } })
}

export async function updateUserMaxContextCount(userId: string, maxContextCount: number) {
await userCol.updateOne({ _id: new ObjectId(userId) }, { $set: { 'config.maxContextCount': maxContextCount } })
}

export async function updateUserAdvancedConfig(userId: string, config: AdvancedConfig) {
await userCol.updateOne({ _id: new ObjectId(userId) }, { $set: { advanced: config } })
}
Expand Down
14 changes: 14 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ export function fetchUpdateUserChatModel<T = any>(chatModel: string) {
})
}

export function fetchUpdateUserMaxContextCount<T = any>(maxContextCount: number) {
return post<T>({
url: '/user-max-context-count',
data: { maxContextCount },
})
}

export function fetchGetUsers<T = any>(page: number, size: number) {
return get<T>({
url: '/users',
Expand Down Expand Up @@ -258,6 +265,13 @@ export function fetchUpdateChatRoomChatModel<T = any>(chatModel: string, roomId:
})
}

export function fetchUpdateChatRoomMaxContextCount<T = any>(maxContextCount: number, roomId: number) {
return post<T>({
url: '/room-max-context-count',
data: { maxContextCount, roomId },
})
}

export function fetchUpdateChatRoomUsingContext<T = any>(using: boolean, roomId: number) {
return post<T>({
url: '/room-context',
Expand Down
7 changes: 0 additions & 7 deletions src/components/common/Setting/Advanced.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,6 @@ function handleReset() {
</div>
<span>{{ userStore.userInfo.advanced.top_p }}</span>
</div>
<div class="flex items-center space-x-4">
<span class="shrink-0 w-[120px]">{{ $t('setting.maxContextCount') }} </span>
<div class="flex-1">
<NSlider v-model:value="userStore.userInfo.advanced.maxContextCount" :max="100" :min="0" :step="1" />
</div>
<span>{{ userStore.userInfo.advanced.maxContextCount }}</span>
</div>
<div class="flex items-center space-x-4">
<span class="shrink-0 w-[120px]">&nbsp;</span>
<NButton type="primary" @click="updateSettings(false)">
Expand Down
29 changes: 28 additions & 1 deletion src/components/common/Setting/General.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import type { Language, Theme } from '@/store/modules/app/helper'
import type { UserInfo } from '@/store/modules/user/helper'
import { decode_redeemcard, fetchClearAllChat, fetchUpdateUserChatModel } from '@/api'
import { decode_redeemcard, fetchClearAllChat, fetchUpdateUserChatModel, fetchUpdateUserMaxContextCount } from '@/api'
import { SvgIcon } from '@/components/common'
import { UserConfig } from '@/components/common/Setting/model'
import { useBasicLayout } from '@/hooks/useBasicLayout'
Expand Down Expand Up @@ -104,6 +104,19 @@ async function updateUserChatModel(chatModel: string) {
await fetchUpdateUserChatModel(chatModel)
}

async function updateUserMaxContextCount(maxContextCount: number) {
if (!userStore.userInfo.config)
userStore.userInfo.config = new UserConfig()
userStore.userInfo.config.maxContextCount = maxContextCount
userStore.recordState()
}

async function syncUserMaxContextCount() {
if (userStore.userInfo.config.maxContextCount) {
await fetchUpdateUserMaxContextCount(userStore.userInfo.config.maxContextCount)
}
}

function exportData(): void {
const date = getCurrentDate()
const data: string = localStorage.getItem('chatStorage') || '{}'
Expand Down Expand Up @@ -206,6 +219,20 @@ function handleImportButtonClick(): void {
/>
</div>
</div>
<div class="flex items-center space-x-4">
<span class="shrink-0 w-[100px]">{{ $t('setting.maxContextCount') }}</span>
<div class="w-[300px]">
<NSlider
:value="userInfo.config.maxContextCount"
:max="40"
:min="0"
:step="1"
style="width: 300px"
:on-dragend="syncUserMaxContextCount"
@update:value="(val) => updateUserMaxContextCount(val)"
/>
</div>
</div>
<div
class="flex items-center space-x-4"
:class="isMobile && 'items-start'"
Expand Down
1 change: 1 addition & 0 deletions src/components/common/Setting/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export class ConfigState {

export class UserConfig {
chatModel?: string
maxContextCount?: number
}

// https://platform.openai.com/docs/models/overview
Expand Down
3 changes: 2 additions & 1 deletion src/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default {
thinkHelp: 'After enabling deep thinking, the model will use more computational resources and take longer time to simulate more complex thinking chains for logical reasoning.\nSuitable for complex tasks or high-requirement scenarios, such as mathematical derivations and project planning.\nDaily simple queries do not need to be enabled.',
showOnContext: 'Include context',
showOffContext: 'Not include context',
maxContextCount: 'Maximum number of messages included in the context window',
searchEnabled: 'Search enabled',
searchDisabled: 'Search disabled',
thinkEnabled: 'Think enabled',
Expand Down Expand Up @@ -214,7 +215,7 @@ export default {
info2FAStep3Tip1: 'Note: How to turn off two-step verification?',
info2FAStep3Tip2: '1. After logging in, use the two-step verification on the Two-Step Verification page to disable it.',
info2FAStep3Tip3: '2. Contact the administrator to disable two-step verification.',
maxContextCount: 'Number of context messages included in the conversation',
maxContextCount: 'Maximum number of messages included in the context window for default new sessions',
fastDelMsg: 'Fast Delete Message',
},
store: {
Expand Down
3 changes: 2 additions & 1 deletion src/locales/ko-KR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default {
thinkHelp: '깊은 사고를 활성화한 후 모델은 더 많은 계산 리소스를 사용하고 더 오랜 시간을 소비하여 더 복잡한 사고 체인을 시뮬레이션하여 논리적 추론을 수행합니다.\n복잡한 작업이나 높은 요구 사항 시나리오에 적합합니다. 예: 수학 문제 유도, 프로젝트 계획.\n일상적인 간단한 조회는 활성화할 필요가 없습니다.',
showOnContext: '컨텍스트 포함됨',
showOffContext: '컨텍스트 미포함',
maxContextCount: '컨텍스트 창에 포함된 최대 메시지 수',
searchEnabled: '검색 활성화됨',
searchDisabled: '검색 비활성화됨',
thinkEnabled: '사고 활성화됨',
Expand Down Expand Up @@ -200,7 +201,7 @@ export default {
info2FAStep3Tip1: 'Note: How to turn off two-step verification?',
info2FAStep3Tip2: '1. After logging in, use the two-step verification on the Two-Step Verification page to disable it.',
info2FAStep3Tip3: '2. Contact the administrator to disable two-step verification.',
maxContextCount: '대화에 포함된 컨텍스트 메시지 수량',
maxContextCount: '기본 새 세션의 컨텍스트 창에 포함된 최대 메시지 ',
fastDelMsg: '빠르게 메시지 삭제',
},
store: {
Expand Down
Loading