feat: Add database table for model context length configuration (#477)
* feat: add database table for model context length configuration - Add model_context table with provider/model/context_limit fields - Implement UPSERT endpoint for model context configuration - Add priority lookup: database > config.yaml > custom_providers > cache - Add frontend click-to-edit UI in ChatInput with tooltip - Add i18n support for context editing dialog (all 8 locales) - Use context_limit field consistently across frontend and backend Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: use useMessage() composable instead of window.$message in ChatInput - Remove incorrect NMessage import (not a component) - Use useMessage() composable from naive-ui - Replace window.$message?.xxx() with message.xxx() - Fixes TypeScript build errors Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,12 +4,14 @@ import { useChatStore } from '@/stores/hermes/chat'
|
||||
import { useAppStore } from '@/stores/hermes/app'
|
||||
import { useProfilesStore } from '@/stores/hermes/profiles'
|
||||
import { fetchContextLength } from '@/api/hermes/sessions'
|
||||
import { NButton, NTooltip, NSwitch } from 'naive-ui'
|
||||
import { setModelContext } from '@/api/hermes/model-context'
|
||||
import { NButton, NTooltip, NSwitch, NModal, NInputNumber, useMessage } from 'naive-ui'
|
||||
import { computed, ref, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const chatStore = useChatStore()
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
const inputText = ref('')
|
||||
const textareaRef = ref<HTMLTextAreaElement>()
|
||||
const fileInputRef = ref<HTMLInputElement>()
|
||||
@@ -45,6 +47,44 @@ const canSend = computed(() => inputText.value.trim() || attachments.value.lengt
|
||||
const contextLength = ref(200000)
|
||||
const FALLBACK_CONTEXT = 200000
|
||||
|
||||
// Context length editing
|
||||
const showContextEditModal = ref(false)
|
||||
const editingContextLimit = ref(200000)
|
||||
const isSavingContextLimit = ref(false)
|
||||
|
||||
async function handleEditContextLimit() {
|
||||
editingContextLimit.value = contextLength.value
|
||||
showContextEditModal.value = true
|
||||
}
|
||||
|
||||
async function saveContextLimit() {
|
||||
if (!editingContextLimit.value || editingContextLimit.value <= 0) {
|
||||
message.error(t('chat.contextEditInvalid'))
|
||||
return
|
||||
}
|
||||
|
||||
isSavingContextLimit.value = true
|
||||
try {
|
||||
const appStore = useAppStore()
|
||||
const provider = appStore.selectedProvider || ''
|
||||
const model = appStore.selectedModel || ''
|
||||
|
||||
if (!provider || !model) {
|
||||
message.error(t('chat.contextEditFailed'))
|
||||
return
|
||||
}
|
||||
|
||||
await setModelContext(provider, model, editingContextLimit.value)
|
||||
contextLength.value = editingContextLimit.value
|
||||
showContextEditModal.value = false
|
||||
message.success(t('chat.contextEditSuccess'))
|
||||
} catch (err: any) {
|
||||
message.error(`${t('chat.contextEditFailed')}: ${err.message || ''}`)
|
||||
} finally {
|
||||
isSavingContextLimit.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadContextLength() {
|
||||
try {
|
||||
const profile = useProfilesStore().activeProfileName || undefined
|
||||
@@ -247,7 +287,16 @@ function isImage(type: string): boolean {
|
||||
</div>
|
||||
|
||||
<span v-if="totalTokens > 0" class="context-info" :class="{ 'context-warning': usagePercent > 80 }">
|
||||
{{ formatTokens(totalTokens) }} / {{ formatTokens(contextLength) }} · {{ t('chat.contextRemaining') }} {{ formatTokens(remainingTokens) }}
|
||||
{{ formatTokens(totalTokens) }} /
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<span class="context-limit-editable" @click="handleEditContextLimit">
|
||||
{{ formatTokens(contextLength) }}
|
||||
</span>
|
||||
</template>
|
||||
<span>{{ t('chat.contextClickToEdit') }}</span>
|
||||
</NTooltip>
|
||||
· {{ t('chat.contextRemaining') }} {{ formatTokens(remainingTokens) }}
|
||||
</span>
|
||||
<div v-if="totalTokens > 0" class="context-bar">
|
||||
<div
|
||||
@@ -335,6 +384,47 @@ function isImage(type: string): boolean {
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Context Length Edit Modal -->
|
||||
<NModal
|
||||
v-model:show="showContextEditModal"
|
||||
:title="t('chat.contextEditTitle')"
|
||||
:mask-closable="true"
|
||||
preset="card"
|
||||
style="width: 400px"
|
||||
>
|
||||
<div class="context-edit-content">
|
||||
<p style="margin-bottom: 16px; color: #666;">
|
||||
{{ t('chat.contextEditDesc') }}
|
||||
</p>
|
||||
<NInputNumber
|
||||
v-model:value="editingContextLimit"
|
||||
:min="1000"
|
||||
:max="10000000"
|
||||
:step="1000"
|
||||
:show-button="false"
|
||||
:placeholder="t('chat.contextEditPlaceholder')"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #suffix>
|
||||
<span style="color: #999;">tokens</span>
|
||||
</template>
|
||||
</NInputNumber>
|
||||
<div style="margin-top: 12px; font-size: 12px; color: #999;">
|
||||
{{ t('chat.contextEditHint') }}
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div style="display: flex; justify-content: flex-end; gap: 8px;">
|
||||
<NButton @click="showContextEditModal = false" :disabled="isSavingContextLimit">
|
||||
{{ t('chat.contextEditCancel') }}
|
||||
</NButton>
|
||||
<NButton type="primary" @click="saveContextLimit" :loading="isSavingContextLimit">
|
||||
{{ t('chat.contextEditSave') }}
|
||||
</NButton>
|
||||
</div>
|
||||
</template>
|
||||
</NModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -383,6 +473,19 @@ function isImage(type: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
.context-limit-editable {
|
||||
cursor: pointer;
|
||||
border-bottom: 1px dashed transparent;
|
||||
transition: all 0.2s ease;
|
||||
padding: 0 2px;
|
||||
|
||||
&:hover {
|
||||
border-bottom-color: $text-muted;
|
||||
background: rgba(128, 128, 128, 0.1);
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.context-bar {
|
||||
width: 60px;
|
||||
height: 4px;
|
||||
|
||||
Reference in New Issue
Block a user