import { useState, useEffect, useCallback, useRef } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { ScrollArea } from '@/components/ui/scroll-area' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover' import { Switch } from '@/components/ui/switch' import { Slider } from '@/components/ui/slider' import { Badge } from '@/components/ui/badge' import { Plus, Pencil, Trash2, Save, Search, Info, Power, Check, ChevronsUpDown, RefreshCw, Loader2, GraduationCap, Share2, AlertTriangle, Settings, Lock, Unlock } from 'lucide-react' import { getModelConfig, updateModelConfig } from '@/lib/config-api' import { useToast } from '@/hooks/use-toast' import { Alert, AlertDescription } from '@/components/ui/alert' import { HelpTooltip } from '@/components/ui/help-tooltip' import { RestartOverlay } from '@/components/restart-overlay' import { RestartProvider, useRestart } from '@/lib/restart-context' import { ExtraParamsDialog } from '@/components/ui/extra-params-dialog' import { SharePackDialog } from '@/components/share-pack-dialog' // 导入模块化的类型定义和组件 import type { ModelInfo, ProviderConfig, ModelTaskConfig, TaskConfig } from './model/types' import { Pagination, ModelTable, ModelCardList } from './model/components' import { useModelTour, useModelFetcher, useModelAutoSave } from './model/hooks' // 导入动态表单和 Hook 系统 import { DynamicConfigForm } from '@/components/dynamic-form' import { fieldHooks } from '@/lib/field-hooks' // 主导出组件:包装 RestartProvider export function ModelConfigPage() { return ( ) } // 内部实现组件 function ModelConfigPageContent() { const [models, setModels] = useState([]) const [providers, setProviders] = useState([]) const [providerConfigs, setProviderConfigs] = useState([]) const [taskConfig, setTaskConfig] = useState(null) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [autoSaving, setAutoSaving] = useState(false) const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false) const [editDialogOpen, setEditDialogOpen] = useState(false) const [editingModel, setEditingModel] = useState(null) const [editingIndex, setEditingIndex] = useState(null) const [extraParamsDialogOpen, setExtraParamsDialogOpen] = useState(false) const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) const [deletingIndex, setDeletingIndex] = useState(null) const [searchQuery, setSearchQuery] = useState('') const [selectedModels, setSelectedModels] = useState>(new Set()) const [batchDeleteDialogOpen, setBatchDeleteDialogOpen] = useState(false) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(20) const [jumpToPage, setJumpToPage] = useState('') const [advancedTemperatureMode, setAdvancedTemperatureMode] = useState(false) // 模型 Combobox 状态 const [modelComboboxOpen, setModelComboboxOpen] = useState(false) // 嵌入模型警告相关状态 const [embeddingWarningOpen, setEmbeddingWarningOpen] = useState(false) const previousEmbeddingModelsRef = useRef([]) const pendingEmbeddingUpdateRef = useRef<{ field: keyof TaskConfig; value: string[] | number } | null>(null) // 任务配置问题检查状态 const [invalidModelRefs, setInvalidModelRefs] = useState<{ taskName: string; invalidModels: string[] }[]>([]) const [emptyTasks, setEmptyTasks] = useState([]) // 表单验证错误状态 const [formErrors, setFormErrors] = useState<{ name?: string api_provider?: string model_identifier?: string }>({}) const { toast } = useToast() const { triggerRestart, isRestarting } = useRestart() // Tour 引导 (使用 hook 封装的逻辑) const { startTour: handleStartTour, isRunning: tourIsRunning } = useModelTour({ onCloseEditDialog: () => setEditDialogOpen(false), }) // 自动保存 (使用 hook 封装的逻辑) const { clearTimers: clearAutoSaveTimers, initialLoadRef } = useModelAutoSave({ models, taskConfig, onSavingChange: setAutoSaving, onUnsavedChange: setHasUnsavedChanges, }) // 检查任务配置问题 const checkTaskConfigIssues = useCallback((taskConf: ModelTaskConfig | null, modelList: ModelInfo[]) => { if (!taskConf) return const modelNameSet = new Set(modelList.map(m => m.name)) const invalidRefs: { taskName: string; invalidModels: string[] }[] = [] const emptyTaskList: string[] = [] const taskNames: Array<{ key: keyof ModelTaskConfig; label: string }> = [ { key: 'utils', label: '工具模型' }, { key: 'tool_use', label: '工具调用模型' }, { key: 'replyer', label: '回复模型' }, { key: 'planner', label: '规划器模型' }, { key: 'vlm', label: '视觉模型' }, { key: 'voice', label: '语音模型' }, { key: 'embedding', label: '嵌入模型' }, { key: 'lpmm_entity_extract', label: 'LPMM实体抽取' }, { key: 'lpmm_rdf_build', label: 'LPMM关系构建' }, ] for (const { key, label } of taskNames) { const task = taskConf[key] if (!task) continue // 检查是否有模型 if (!task.model_list || task.model_list.length === 0) { emptyTaskList.push(label) continue } // 检查是否引用了不存在的模型 const invalid = task.model_list.filter(modelName => !modelNameSet.has(modelName)) if (invalid.length > 0) { invalidRefs.push({ taskName: label, invalidModels: invalid }) } } setInvalidModelRefs(invalidRefs) setEmptyTasks(emptyTaskList) }, []) // 加载配置 const loadConfig = useCallback(async () => { try { setLoading(true) const result = await getModelConfig() if (!result.success) { toast({ title: '加载失败', description: result.error, variant: 'destructive', }) setLoading(false) return } const config = result.data const modelList = (config.models as ModelInfo[]) || [] setModels(modelList) const providerList = (config.api_providers as ProviderConfig[]) || [] setProviders(providerList.map((p) => p.name)) setProviderConfigs(providerList) const taskConf = (config.model_task_config as ModelTaskConfig) || null setTaskConfig(taskConf) // 检查任务配置问题 checkTaskConfigIssues(taskConf, modelList) // 初始化上一次的 embedding 模型列表 const embeddingModels = taskConf?.embedding?.model_list || [] previousEmbeddingModelsRef.current = [...embeddingModels] setHasUnsavedChanges(false) initialLoadRef.current = false } catch (error) { console.error('加载配置失败:', error) } finally { setLoading(false) } }, [initialLoadRef, checkTaskConfigIssues]) // 初始加载 useEffect(() => { loadConfig() }, [loadConfig]) // 获取指定提供商的配置 const getProviderConfig = useCallback((providerName: string): ProviderConfig | undefined => { return providerConfigs.find(p => p.name === providerName) }, [providerConfigs]) // 模型列表获取 (使用 hook 封装的逻辑) const { availableModels, fetchingModels, modelFetchError, matchedTemplate, fetchModelsForProvider, clearModels, } = useModelFetcher({ getProviderConfig }) // 当选择的提供商变化时,获取模型列表 useEffect(() => { if (editDialogOpen && editingModel?.api_provider) { fetchModelsForProvider(editingModel.api_provider) } }, [editDialogOpen, editingModel?.api_provider, fetchModelsForProvider]) // 重启麦麦 const handleRestart = async () => { await triggerRestart() } // 一键删除所有无效模型引用 const handleRemoveInvalidRefs = useCallback(() => { if (!taskConfig) return const modelNameSet = new Set(models.map(m => m.name)) const newTaskConfig = { ...taskConfig } // 遍历所有任务,过滤掉无效的模型引用 const taskKeys = Object.keys(newTaskConfig) as Array for (const key of taskKeys) { const task = newTaskConfig[key] if (task && task.model_list) { task.model_list = task.model_list.filter(modelName => modelNameSet.has(modelName)) } } setTaskConfig(newTaskConfig) setInvalidModelRefs([]) toast({ title: '清理完成', description: '已删除所有无效的模型引用', }) }, [taskConfig, models, toast]) // 清理模型中的 null 值(TOML 不支持 null) const cleanModelForSave = (model: ModelInfo): ModelInfo => { const cleaned: ModelInfo = { model_identifier: model.model_identifier, name: model.name, api_provider: model.api_provider, price_in: model.price_in ?? 0, price_out: model.price_out ?? 0, force_stream_mode: model.force_stream_mode ?? false, extra_params: model.extra_params ?? {}, } // 只有在有值时才添加可选字段 if (model.temperature != null) { cleaned.temperature = model.temperature } if (model.max_tokens != null) { cleaned.max_tokens = model.max_tokens } return cleaned } // 保存并重启 const handleSaveAndRestart = async () => { try { setSaving(true) clearAutoSaveTimers() const resultGet = await getModelConfig() if (!resultGet.success) { toast({ title: '保存失败', description: resultGet.error, variant: 'destructive', }) setSaving(false) return } const config = resultGet.data // 清理每个模型中的 null 值 config.models = models.map(cleanModelForSave) config.model_task_config = taskConfig const resultUpdate = await updateModelConfig(config) if (!resultUpdate.success) { toast({ title: '保存失败', description: resultUpdate.error, variant: 'destructive', }) setSaving(false) return } setHasUnsavedChanges(false) toast({ title: '保存成功', description: '正在重启麦麦...', }) await handleRestart() } catch (error) { console.error('保存配置失败:', error) toast({ title: '保存失败', description: (error as Error).message, variant: 'destructive', }) setSaving(false) } } // 保存配置(手动保存) const saveConfig = async () => { try { setSaving(true) // 先取消自动保存定时器 clearAutoSaveTimers() const resultGet = await getModelConfig() if (!resultGet.success) { toast({ title: '保存失败', description: resultGet.error, variant: 'destructive', }) setSaving(false) return } const config = resultGet.data // 清理每个模型中的 null 值 config.models = models.map(cleanModelForSave) config.model_task_config = taskConfig const resultUpdate = await updateModelConfig(config) if (!resultUpdate.success) { toast({ title: '保存失败', description: resultUpdate.error, variant: 'destructive', }) setSaving(false) return } setHasUnsavedChanges(false) toast({ title: '保存成功', description: '模型配置已保存', }) await loadConfig() // 重新加载以更新模型名称列表 } catch (error) { console.error('保存配置失败:', error) toast({ title: '保存失败', description: (error as Error).message, variant: 'destructive', }) } finally { setSaving(false) } } // 打开编辑对话框 const openEditDialog = (model: ModelInfo | null, index: number | null) => { // 清除表单验证错误 setFormErrors({}) setEditingModel( model || { model_identifier: '', name: '', api_provider: providers[0] || '', price_in: 0, price_out: 0, temperature: null, max_tokens: null, force_stream_mode: false, extra_params: {}, } ) setEditingIndex(index) setEditDialogOpen(true) } // 保存编辑 const handleSaveEdit = () => { if (!editingModel) return // 验证必填项 const errors: { name?: string; api_provider?: string; model_identifier?: string } = {} if (!editingModel.name?.trim()) { errors.name = '请输入模型名称' } else { // 检查名称是否与现有模型重复 const isDuplicate = models.some((m, index) => { // 编辑时排除自身 if (editingIndex !== null && index === editingIndex) { return false } return m.name.trim().toLowerCase() === editingModel.name.trim().toLowerCase() }) if (isDuplicate) { errors.name = '模型名称已存在,请使用其他名称' } } if (!editingModel.api_provider?.trim()) { errors.api_provider = '请选择 API 提供商' } if (!editingModel.model_identifier?.trim()) { errors.model_identifier = '请输入模型标识符' } if (Object.keys(errors).length > 0) { setFormErrors(errors) return } // 清除错误状态 setFormErrors({}) // 填充空值的默认值,并移除 null 值的可选字段(TOML 不支持 null) const modelToSave: ModelInfo = { model_identifier: editingModel.model_identifier, name: editingModel.name, api_provider: editingModel.api_provider, price_in: editingModel.price_in ?? 0, price_out: editingModel.price_out ?? 0, force_stream_mode: editingModel.force_stream_mode ?? false, extra_params: editingModel.extra_params ?? {}, } // 只有在有值时才添加可选字段 if (editingModel.temperature != null) { modelToSave.temperature = editingModel.temperature } if (editingModel.max_tokens != null) { modelToSave.max_tokens = editingModel.max_tokens } let newModels: ModelInfo[] let oldModelName: string | null = null if (editingIndex !== null) { // 记录旧的模型名称,用于更新任务配置 oldModelName = models[editingIndex].name newModels = [...models] newModels[editingIndex] = modelToSave } else { newModels = [...models, modelToSave] } setModels(newModels) // 如果模型名称发生变化,更新任务配置中对该模型的引用 if (oldModelName && oldModelName !== modelToSave.name && taskConfig) { const updateModelList = (list: string[]): string[] => { return list.map(name => name === oldModelName ? modelToSave.name : name) } setTaskConfig({ ...taskConfig, utils: { ...taskConfig.utils, model_list: updateModelList(taskConfig.utils?.model_list || []) }, tool_use: { ...taskConfig.tool_use, model_list: updateModelList(taskConfig.tool_use?.model_list || []) }, replyer: { ...taskConfig.replyer, model_list: updateModelList(taskConfig.replyer?.model_list || []) }, planner: { ...taskConfig.planner, model_list: updateModelList(taskConfig.planner?.model_list || []) }, vlm: { ...taskConfig.vlm, model_list: updateModelList(taskConfig.vlm?.model_list || []) }, voice: { ...taskConfig.voice, model_list: updateModelList(taskConfig.voice?.model_list || []) }, embedding: { ...taskConfig.embedding, model_list: updateModelList(taskConfig.embedding?.model_list || []) }, lpmm_entity_extract: { ...taskConfig.lpmm_entity_extract, model_list: updateModelList(taskConfig.lpmm_entity_extract?.model_list || []) }, lpmm_rdf_build: { ...taskConfig.lpmm_rdf_build, model_list: updateModelList(taskConfig.lpmm_rdf_build?.model_list || []) }, }) } setEditDialogOpen(false) setEditingModel(null) setEditingIndex(null) // 提示用户配置将自动保存 toast({ title: editingIndex !== null ? '模型已更新' : '模型已添加', description: '配置将在 2 秒后自动保存,或点击右上角"保存配置"按钮立即保存', }) } // 处理编辑对话框关闭 const handleEditDialogClose = (open: boolean) => { if (!open && editingModel) { // 关闭时填充默认值 const updatedModel = { ...editingModel, price_in: editingModel.price_in ?? 0, price_out: editingModel.price_out ?? 0, } setEditingModel(updatedModel) } setEditDialogOpen(open) } // 打开删除确认对话框 const openDeleteDialog = (index: number) => { setDeletingIndex(index) setDeleteDialogOpen(true) } // 确认删除模型 const handleConfirmDelete = () => { if (deletingIndex !== null) { const newModels = models.filter((_, i) => i !== deletingIndex) setModels(newModels) // 重新检查任务配置问题 checkTaskConfigIssues(taskConfig, newModels) toast({ title: '删除成功', description: '配置将在 2 秒后自动保存,或点击右上角"保存配置"按钮立即保存', }) } setDeleteDialogOpen(false) setDeletingIndex(null) } // 切换单个模型选择 const toggleModelSelection = (index: number) => { const newSelected = new Set(selectedModels) if (newSelected.has(index)) { newSelected.delete(index) } else { newSelected.add(index) } setSelectedModels(newSelected) } // 全选/取消全选 const toggleSelectAll = () => { if (selectedModels.size === filteredModels.length) { setSelectedModels(new Set()) } else { const allIndices = filteredModels.map((_, idx) => models.findIndex(m => m === filteredModels[idx]) ) setSelectedModels(new Set(allIndices)) } } // 打开批量删除确认对话框 const openBatchDeleteDialog = () => { if (selectedModels.size === 0) { toast({ title: '提示', description: '请先选择要删除的模型', variant: 'default', }) return } setBatchDeleteDialogOpen(true) } // 确认批量删除 const handleConfirmBatchDelete = () => { const deletedCount = selectedModels.size const newModels = models.filter((_, index) => !selectedModels.has(index)) setModels(newModels) // 重新检查任务配置问题 checkTaskConfigIssues(taskConfig, newModels) setSelectedModels(new Set()) setBatchDeleteDialogOpen(false) toast({ title: '批量删除成功', description: `已删除 ${deletedCount} 个模型,配置将在 2 秒后自动保存`, }) } // 确认更新嵌入模型 const handleConfirmEmbeddingChange = () => { if (!taskConfig || !pendingEmbeddingUpdateRef.current) return const { field, value } = pendingEmbeddingUpdateRef.current const newTaskConfig = { ...taskConfig, embedding: { ...taskConfig.embedding, [field]: value, }, } setTaskConfig(newTaskConfig) // 重新检查任务配置问题 checkTaskConfigIssues(newTaskConfig, models) // 更新 ref if (field === 'model_list' && Array.isArray(value)) { previousEmbeddingModelsRef.current = [...(value as string[])] } // 清理 pendingEmbeddingUpdateRef.current = null setEmbeddingWarningOpen(false) toast({ title: '嵌入模型已更新', description: '建议重新生成知识库向量以确保最佳匹配精度', }) } // 取消更新嵌入模型 const handleCancelEmbeddingChange = () => { pendingEmbeddingUpdateRef.current = null setEmbeddingWarningOpen(false) } // 过滤模型列表 const filteredModels = models.filter((model) => { if (!searchQuery) return true const query = searchQuery.toLowerCase() return ( model.name.toLowerCase().includes(query) || model.model_identifier.toLowerCase().includes(query) || model.api_provider.toLowerCase().includes(query) ) }) // 分页逻辑 const totalPages = Math.ceil(filteredModels.length / pageSize) const paginatedModels = filteredModels.slice( (page - 1) * pageSize, page * pageSize ) // 页码跳转 const handleJumpToPage = () => { const targetPage = parseInt(jumpToPage) if (targetPage >= 1 && targetPage <= totalPages) { setPage(targetPage) setJumpToPage('') } } // 检查模型是否被任务使用 const isModelUsed = (modelName: string): boolean => { if (!taskConfig) return false const allTaskLists = [ taskConfig.utils?.model_list || [], taskConfig.tool_use?.model_list || [], taskConfig.replyer?.model_list || [], taskConfig.planner?.model_list || [], taskConfig.vlm?.model_list || [], taskConfig.voice?.model_list || [], taskConfig.embedding?.model_list || [], taskConfig.lpmm_entity_extract?.model_list || [], taskConfig.lpmm_rdf_build?.model_list || [], ] return allTaskLists.some(list => list.includes(modelName)) } if (loading) { return (

加载中...

) } return (
{/* 页面标题 */}

模型管理与分配

添加模型并为模型分配功能

分享配置 } /> 确认重启麦麦?

{hasUnsavedChanges ? '当前有未保存的配置更改。点击确认将先保存配置,然后重启麦麦使新配置生效。重启过程中麦麦将暂时离线。' : '即将重启麦麦主程序。重启过程中麦麦将暂时离线,配置将在重启后生效。' }

取消 {hasUnsavedChanges ? '保存并重启' : '确认重启'}
{/* 重启提示 */} 配置更新后需要重启麦麦才能生效。你可以点击右上角的"保存并重启"按钮一键完成保存和重启。 {/* 无效模型引用警告 */} {invalidModelRefs.length > 0 && (
检测到无效的模型引用
{invalidModelRefs.map(({ taskName, invalidModels }) => (
{taskName} 引用了不存在的模型: {invalidModels.join(', ')}
))}
)} {/* 空任务警告 */} {emptyTasks.length > 0 && ( 以下任务未配置模型
{emptyTasks.join('、')} 还未分配模型,这些功能将无法正常工作。
)} {/* 新手引导入口 - 仅在桌面端显示,移动端隐藏 */} 新手引导:不知道如何配置模型?点击这里开始学习如何为麦麦的组件分配模型。 {/* 标签页 */} 添加模型 为模型分配功能 {/* 模型配置标签页 */}

配置可用的模型列表

{selectedModels.size > 0 && ( )}
{/* 搜索框 */}
setSearchQuery(e.target.value)} className="pl-9" />
{searchQuery && (

找到 {filteredModels.length} 个结果

)}
{/* 模型列表 - 移动端卡片视图 */} {/* 模型列表 - 桌面端表格视图 */} {/* 分页 - 使用模块化组件 */} setSelectedModels(new Set())} />
{/* 模型任务配置标签页 */}

为不同的任务配置使用的模型和参数

{taskConfig && ( { if (field === 'taskConfig') { setTaskConfig(value as ModelTaskConfig) setHasUnsavedChanges(true) } }} hooks={fieldHooks} /> )}
{/* 编辑模型对话框 */} {editingIndex !== null ? '编辑模型' : '添加模型'} 配置模型的基本信息和参数
{ setEditingModel((prev) => prev ? { ...prev, name: e.target.value } : null ) if (formErrors.name) { setFormErrors((prev) => ({ ...prev, name: undefined })) } }} placeholder="例如: qwen3-30b" className={formErrors.name ? 'border-destructive focus-visible:ring-destructive' : ''} /> {formErrors.name ? (

{formErrors.name}

) : (

用于在任务配置中引用此模型

)}
{formErrors.api_provider && (

{formErrors.api_provider}

)}
{matchedTemplate?.modelFetcher && (
{matchedTemplate.display_name}
)}
{/* 模型标识符 Combobox */} {matchedTemplate?.modelFetcher ? ( {modelFetchError ? (

{modelFetchError}

{!modelFetchError.includes('API Key') && ( )}
) : ( '未找到匹配的模型' )}
{availableModels.map((model) => ( { setEditingModel((prev) => prev ? { ...prev, model_identifier: model.id } : null ) setModelComboboxOpen(false) }} >
{model.id} {model.name !== model.id && ( {model.name} )}
))}
{ setModelComboboxOpen(false) // 聚焦到手动输入框(如果需要的话可以实现) }} > 手动输入模型标识符...
) : ( { setEditingModel((prev) => prev ? { ...prev, model_identifier: e.target.value } : null ) if (formErrors.model_identifier) { setFormErrors((prev) => ({ ...prev, model_identifier: undefined })) } }} placeholder="Qwen/Qwen3-30B-A3B-Instruct-2507" className={formErrors.model_identifier ? 'border-destructive focus-visible:ring-destructive' : ''} /> )} {/* 表单验证错误提示 */} {formErrors.model_identifier && (

{formErrors.model_identifier}

)} {/* 模型获取错误提示 */} {modelFetchError && matchedTemplate?.modelFetcher && !formErrors.model_identifier && ( {modelFetchError} )} {/* 手动输入区域 - 当使用 Combobox 时也显示一个可编辑的输入框 */} {matchedTemplate?.modelFetcher && ( { setEditingModel((prev) => prev ? { ...prev, model_identifier: e.target.value } : null ) if (formErrors.model_identifier) { setFormErrors((prev) => ({ ...prev, model_identifier: undefined })) } }} placeholder="或手动输入模型标识符" className={`mt-2 ${formErrors.model_identifier ? 'border-destructive focus-visible:ring-destructive' : ''}`} /> )} {!formErrors.model_identifier && (

{modelFetchError ? '请手动输入模型标识符,或前往"模型提供商配置"检查 API Key' : matchedTemplate?.modelFetcher ? `已识别为 ${matchedTemplate.display_name},支持自动获取模型列表` : 'API 提供商提供的模型 ID'}

)}
{ const val = e.target.value === '' ? null : parseFloat(e.target.value) setEditingModel((prev) => prev ? { ...prev, price_in: val } : null ) }} placeholder="默认: 0" />
{ const val = e.target.value === '' ? null : parseFloat(e.target.value) setEditingModel((prev) => prev ? { ...prev, price_out: val } : null ) }} placeholder="默认: 0" />
{/* 模型级别温度 */}

什么是温度(Temperature)?

温度控制模型输出的随机性和创造性:

  • 低温度(0.1-0.3):更确定、更保守的输出,适合事实性任务
  • 中温度(0.5-0.7):平衡创造性与可控性
  • 高温度(0.8-1.0):更有创意、更多样化的输出
  • 极高温度(1.0-2.0):极度随机,可能产生不可预测的结果
} side="right" maxWidth="400px" />

启用后将覆盖「为模型分配功能」中的任务温度配置

{ if (checked) { setEditingModel((prev) => prev ? { ...prev, temperature: 0.5 } : null) } else { setEditingModel((prev) => prev ? { ...prev, temperature: null } : null) } }} />
{editingModel?.temperature != null && (
{ const value = parseFloat(e.target.value) if (!isNaN(value) && value >= 0 && value <= 2) { setEditingModel((prev) => prev ? { ...prev, temperature: value } : null) } }} onBlur={(e) => { const value = parseFloat(e.target.value) if (isNaN(value) || value < 0) { setEditingModel((prev) => prev ? { ...prev, temperature: 0 } : null) } else if (value > 2) { setEditingModel((prev) => prev ? { ...prev, temperature: 2 } : null) } }} step={0.01} min={0} max={2} className="w-20 h-8 text-sm text-right tabular-nums" />
0 setEditingModel((prev) => prev ? { ...prev, temperature: values[0] } : null ) } min={0} max={advancedTemperatureMode ? 2 : 1} step={advancedTemperatureMode ? 0.05 : 0.1} className="flex-1" /> {advancedTemperatureMode ? '2' : '1'}
{advancedTemperatureMode && ( 高级模式:温度 > 1 会产生更随机、更不可预测的输出,请谨慎使用 )}

{advancedTemperatureMode ? "较低(0.1-0.5)产生确定输出,中等(0.5-1.0)平衡创造性,较高(1.0-2.0)产生极度随机输出" : "较低的温度(0.1-0.3)产生更确定的输出,较高的温度(0.7-1.0)产生更多样化的输出" }

)}
{/* 模型级别最大 Token */}

什么是最大 Token?

控制模型单次回复的最大长度。1 token ≈ 0.75 个英文单词或 0.5 个中文字符。

  • 较小值(512-1024):简短回复,节省成本
  • 中等值(2048-4096):正常对话长度
  • 较大值(8192+):长文本生成,成本较高
} side="right" maxWidth="400px" />

启用后将覆盖「为模型分配功能」中的任务最大 Token 配置

{ if (checked) { // 启用时设置默认值 2048 setEditingModel((prev) => prev ? { ...prev, max_tokens: 2048 } : null) } else { // 禁用时清除 setEditingModel((prev) => prev ? { ...prev, max_tokens: null } : null) } }} />
{editingModel?.max_tokens != null && (
{ const val = parseInt(e.target.value) if (!isNaN(val) && val >= 1) { setEditingModel((prev) => prev ? { ...prev, max_tokens: val } : null) } }} className="w-28 h-8 text-sm" />

限制模型单次输出的最大 token 数量,不同模型支持的上限不同

)}
setEditingModel((prev) => prev ? { ...prev, force_stream_mode: checked } : null ) } />
{/* 额外参数 */}
{Object.keys(editingModel?.extra_params || {}).length > 0 && (
{Object.keys(editingModel?.extra_params || {}) .slice(0, 3) .map((key) => ( {key} ))} {Object.keys(editingModel?.extra_params || {}).length > 3 && ( ... )}
)}
{/* 删除确认对话框 */} 确认删除 确定要删除模型 "{deletingIndex !== null ? models[deletingIndex]?.name : ''}" 吗? 此操作无法撤销。 取消 删除 {/* 批量删除确认对话框 */} 确认批量删除 确定要删除选中的 {selectedModels.size} 个模型吗? 此操作无法撤销。 取消 批量删除 {/* 嵌入模型更换警告对话框 */} 更换嵌入模型警告

注意:更换嵌入模型可能会影响知识库的匹配精度!

  • 不同的嵌入模型会产生不同的向量表示
  • 这可能导致现有知识库的检索结果不准确
  • 建议更换嵌入模型后重新生成所有知识库的向量

确定要更换嵌入模型吗?

取消 确认更换
{/* 额外参数编辑弹窗 */} setEditingModel((prev) => prev ? { ...prev, extra_params: params } : null ) } /> {/* 重启遮罩层 */}
) }