feat: 优化上下文窗口配置逻辑

This commit is contained in:
digua
2026-04-29 23:53:56 +08:00
committed by digua
parent 5fc7b5670c
commit b290a85499
9 changed files with 92 additions and 8 deletions
+3 -1
View File
@@ -58,11 +58,13 @@ export function getProviderDefinitionById(id: string): ProviderDefinition | null
return getBuiltinProviderById(id) || loadCustomProviders().find((p) => p.id === id) || null
}
/** 按 providerId + modelId 查找模型定义(内置优先,再查自定义) */
/** 按 providerId + modelId 查找模型定义(内置优先,再查自定义,最后跨 provider 兜底 */
export function findModelDefinition(providerId: string, modelId: string): ModelDefinition | null {
return (
getBuiltinModelById(providerId, modelId) ||
loadCustomModels().find((m) => m.providerId === providerId && m.id === modelId) ||
BUILTIN_MODELS.find((m) => m.id === modelId) ||
loadCustomModels().find((m) => m.id === modelId) ||
null
)
}
+3 -1
View File
@@ -85,7 +85,9 @@ const contextTokens = computed(() => {
const modelContextWindow = computed(() => {
if (!activeConfig.value) return 128000
const model = llmStore.getModelById(activeConfig.value.provider, activeConfig.value.model)
const model =
llmStore.getModelById(activeConfig.value.provider, activeConfig.value.model) ||
llmStore.findModelAcrossProviders(activeConfig.value.model)
return model?.contextWindow ?? 128000
})
@@ -58,6 +58,7 @@ const {
canFetchModels,
showEditModelDialog,
editModelContextWindow,
editModelName,
selectProvider,
onConnectionModeChange,
openAddModelDialog,
@@ -225,6 +226,13 @@ function closeModal() {
}}
</span>
</p>
<p
v-if="formData.model && !selectedModelContextWindow"
class="mt-1 flex items-center gap-1 text-xs text-red-500 dark:text-red-400"
>
<UIcon name="i-heroicons-exclamation-triangle" class="h-3.5 w-3.5 shrink-0" />
{{ t('settings.aiConfig.modal.contextWindowMissing') }}
</p>
<div class="mt-2 flex items-center gap-2">
<button
@@ -305,7 +313,7 @@ function closeModal() {
</label>
<UITabs v-if="modelTabItems.length > 0" v-model="formData.model" :items="modelTabItems" size="xs" />
<p v-else class="flex items-center gap-1 text-xs text-amber-500 dark:text-amber-400">
<p v-else class="flex items-center gap-1 text-xs text-red-500 dark:text-red-400">
<UIcon name="i-heroicons-exclamation-triangle" class="h-3.5 w-3.5 shrink-0" />
{{ t('settings.aiConfig.modal.modelRequired') }}
</p>
@@ -320,6 +328,13 @@ function closeModal() {
}}
</span>
</p>
<p
v-if="formData.model && !selectedModelContextWindow"
class="mt-1 flex items-center gap-1 text-xs text-red-500 dark:text-red-400"
>
<UIcon name="i-heroicons-exclamation-triangle" class="h-3.5 w-3.5 shrink-0" />
{{ t('settings.aiConfig.modal.contextWindowMissing') }}
</p>
<div class="mt-2 flex items-center gap-2">
<button
@@ -424,7 +439,7 @@ function closeModal() {
</label>
<UITabs v-if="modelTabItems.length > 0" v-model="formData.model" :items="modelTabItems" size="xs" />
<p v-else class="flex items-center gap-1 text-xs text-amber-500 dark:text-amber-400">
<p v-else class="flex items-center gap-1 text-xs text-red-500 dark:text-red-400">
<UIcon name="i-heroicons-exclamation-triangle" class="h-3.5 w-3.5 shrink-0" />
{{ t('settings.aiConfig.modal.modelRequired') }}
</p>
@@ -439,6 +454,13 @@ function closeModal() {
}}
</span>
</p>
<p
v-if="formData.model && !selectedModelContextWindow"
class="mt-1 flex items-center gap-1 text-xs text-red-500 dark:text-red-400"
>
<UIcon name="i-heroicons-exclamation-triangle" class="h-3.5 w-3.5 shrink-0" />
{{ t('settings.aiConfig.modal.contextWindowMissing') }}
</p>
<div class="mt-2 flex items-center gap-2">
<button
@@ -687,6 +709,17 @@ function closeModal() {
</label>
<p class="text-sm text-gray-600 dark:text-gray-400">{{ formData.model }}</p>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ t('settings.aiConfig.modal.customModelDisplayName') }}
<span class="font-normal text-gray-400">{{ t('settings.aiConfig.modal.optional') }}</span>
</label>
<UInput
v-model="editModelName"
class="w-full"
:placeholder="formData.model || t('settings.aiConfig.modal.customModelDisplayNamePlaceholder')"
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ t('settings.aiConfig.modal.contextWindow') }}
@@ -157,7 +157,8 @@ export function useAIConfigForm(props: {
const selectedModelContextWindow = computed(() => {
if (!formData.value.model) return undefined
const providerId = formData.value.provider || 'openai-compatible'
const model = llmStore.getModelById(providerId, formData.value.model)
const model =
llmStore.getModelById(providerId, formData.value.model) || llmStore.findModelAcrossProviders(formData.value.model)
return model?.contextWindow
})
@@ -373,7 +374,7 @@ export function useAIConfigForm(props: {
const providerId = formData.value.provider || 'openai-compatible'
remoteModels.value = result.models.map((m) => {
if (m.contextWindow) return m
const catalogModel = llmStore.getModelById(providerId, m.id)
const catalogModel = llmStore.getModelById(providerId, m.id) || llmStore.findModelAcrossProviders(m.id)
return catalogModel?.contextWindow ? { ...m, contextWindow: catalogModel.contextWindow } : m
})
} else {
@@ -391,6 +392,24 @@ export function useAIConfigForm(props: {
if (!compatModels.value.some((m) => m.id === model.id)) {
compatModels.value.push({ id: model.id, name: model.name })
}
const providerId = formData.value.provider || 'openai-compatible'
if (model.contextWindow && !llmStore.getModelById(providerId, model.id)?.contextWindow) {
try {
await window.llmApi.addCustomModel({
id: model.id,
providerId,
name: model.name,
contextWindow: model.contextWindow,
capabilities: ['chat'],
recommendedFor: [],
description: '',
status: 'stable',
})
await llmStore.refreshConfigs()
} catch {
// already exists
}
}
} else {
const providerId = formData.value.provider || 'openai-compatible'
if (!catalogModels.value.some((m) => m.id === model.id)) {
@@ -468,6 +487,7 @@ export function useAIConfigForm(props: {
const showEditModelDialog = ref(false)
const editModelContextWindow = ref<number | undefined>(undefined)
const editModelName = ref('')
function openEditModelDialog() {
const modelId = formData.value.model
@@ -475,6 +495,7 @@ export function useAIConfigForm(props: {
const providerId = formData.value.provider || 'openai-compatible'
const model = llmStore.getModelById(providerId, modelId)
editModelContextWindow.value = model?.contextWindow ?? undefined
editModelName.value = model?.name ?? ''
showEditModelDialog.value = true
}
@@ -483,9 +504,25 @@ export function useAIConfigForm(props: {
if (!modelId) return
const providerId = formData.value.provider || 'openai-compatible'
try {
await window.llmApi.updateCustomModel(providerId, modelId, {
const updates: Record<string, unknown> = {
contextWindow: editModelContextWindow.value || undefined,
})
}
if (editModelName.value.trim()) {
updates.name = editModelName.value.trim()
}
const result = await window.llmApi.updateCustomModel(providerId, modelId, updates)
if (!result.success) {
await window.llmApi.addCustomModel({
id: modelId,
providerId,
name: editModelName.value.trim() || modelId,
contextWindow: editModelContextWindow.value || undefined,
capabilities: ['chat'],
recommendedFor: [],
description: '',
status: 'stable',
})
}
await llmStore.refreshConfigs()
} catch (error) {
console.error('编辑模型失败:', error)
@@ -768,6 +805,7 @@ export function useAIConfigForm(props: {
// 编辑模型
showEditModelDialog,
editModelContextWindow,
editModelName,
// 方法
selectProvider,
+1
View File
@@ -161,6 +161,7 @@
"contextWindow": "Context Window",
"contextWindowPlaceholder": "e.g. 128000",
"contextWindowHint": "Maximum context tokens supported by this model (used for context compression)",
"contextWindowMissing": "Context window not set, will use default. Click \"Edit Model\" to set it manually",
"modelRequired": "Add at least one model to save",
"modelCapabilities": "Model Capabilities",
"apiFormat": "API Format",
+1
View File
@@ -161,6 +161,7 @@
"contextWindow": "コンテキストウィンドウ",
"contextWindowPlaceholder": "例: 128000",
"contextWindowHint": "モデルがサポートする最大コンテキストトークン数(コンテキスト圧縮に使用)",
"contextWindowMissing": "コンテキストウィンドウが未設定です。デフォルト値が使用されます。「モデル編集」から手動で設定してください",
"modelRequired": "少なくとも 1 つのモデルを追加してから保存してください",
"modelCapabilities": "モデル能力ラベル",
"apiFormat": "API インターフェース",
+1
View File
@@ -161,6 +161,7 @@
"contextWindow": "上下文窗口",
"contextWindowPlaceholder": "如 128000",
"contextWindowHint": "模型支持的最大上下文 token 数(用于上下文压缩)",
"contextWindowMissing": "未设置上下文窗口,将使用默认值,建议点击「编辑模型」手动填写",
"modelCapabilities": "模型能力标注",
"modelRequired": "至少添加一个模型才能保存",
"apiFormat": "API 接口类型",
+1
View File
@@ -161,6 +161,7 @@
"contextWindow": "上下文視窗",
"contextWindowPlaceholder": "如 128000",
"contextWindowHint": "模型支援的最大上下文 token 數(用於上下文壓縮)",
"contextWindowMissing": "未設定上下文視窗,將使用預設值,建議點擊「編輯模型」手動填寫",
"modelRequired": "至少新增一個模型才能儲存",
"modelCapabilities": "模型能力標註",
"apiFormat": "API 介面類型",
+5
View File
@@ -67,6 +67,10 @@ export const useLLMStore = defineStore('llm', () => {
return modelCatalog.value.find((m) => m.providerId === providerId && m.id === modelId)
}
function findModelAcrossProviders(modelId: string): ModelDefinition | undefined {
return modelCatalog.value.find((m) => m.id === modelId)
}
// ============ 方法 ============
async function init() {
@@ -144,5 +148,6 @@ export const useLLMStore = defineStore('llm', () => {
getProviderById,
getModelsByProviderId,
getModelById,
findModelAcrossProviders,
}
})