feat:合并memory配置,优化webui交互和展示

This commit is contained in:
SengokuCola
2026-05-06 18:13:14 +08:00
parent 3bdc2a9f70
commit ad5b5889e2
28 changed files with 921 additions and 726 deletions

View File

@@ -1,4 +1,4 @@
import { Activity, Boxes, Database, FileSearch, FileText, Hash, Home, MessageSquare, Network, Package, ScrollText, Server, Settings, Sliders, Smile } from 'lucide-react'
import { Activity, Boxes, Database, FileSearch, FileText, Hash, Home, MessageSquare, Network, Package, ScrollText, Settings, Sliders, Smile } from 'lucide-react'
import type { MenuSection } from './types'
@@ -14,7 +14,6 @@ export const menuSections: MenuSection[] = [
title: 'sidebar.groups.botConfig',
items: [
{ icon: FileText, label: 'sidebar.menu.botMainConfig', path: '/config/bot', searchDescription: 'search.items.botConfigDesc' },
{ icon: Server, label: 'sidebar.menu.aiModelProvider', path: '/config/modelProvider', searchDescription: 'search.items.modelProviderDesc', tourId: 'sidebar-model-provider' },
{ icon: Boxes, label: 'sidebar.menu.modelManagement', path: '/config/model', searchDescription: 'search.items.modelDesc', tourId: 'sidebar-model-management' },
{ icon: ScrollText, label: 'sidebar.menu.promptManagement', path: '/config/prompts' },
],

View File

@@ -58,8 +58,8 @@ function unwrapConfigSchema(payload: unknown): ConfigSchema | null {
return null
}
function getModelConfigPath(fieldPath: string) {
return fieldPath.startsWith('api_providers') ? '/config/modelProvider' : '/config/model'
function getModelConfigPath(_fieldPath: string) {
return '/config/model'
}
function buildFieldSearchText(field: FieldSchema, fieldPath: string, sectionTitle: string, language?: string) {

View File

@@ -1,33 +1,31 @@
import type { Step, Placement } from 'react-joyride'
import type { Placement, Step } from 'react-joyride'
export const MODEL_ASSIGNMENT_TOUR_ID = 'model-assignment-tour'
// Tour 步骤定义
export const modelAssignmentTourSteps: Step[] = [
// Step 1: 全屏介绍
{
target: 'body',
content: '本引导旨在帮助你配置模型提供商和对应的模型,并为麦麦的各个组件分配合适的模型。',
content: '本引导会帮你在同一个页面完成模型厂商、模型列表和功能分配配置。',
placement: 'center' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 2: 侧边栏 - 模型提供商按钮(点击下一步会自动导航)
{
target: '[data-tour="sidebar-model-provider"]',
content: '第一步,你需要配置模型提供商。模型提供商决定了你要使用谁家的模型,无论是单一厂商(如 DeepSeek还是模型平台如 Siliconflow都可以在这里进行配置。点击"下一步"进入配置页面。',
placement: 'right' as Placement,
target: '[data-tour="providers-tab-trigger"]',
content: '第一步,进入"模型厂商设置"。这里用于配置要连接的模型服务厂商或模型平台。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
spotlightClicks: true,
hideFooter: true,
},
// Step 3: 添加提供商按钮
{
target: '[data-tour="add-provider-button"]',
content: '点击"添加提供商"按钮,开始配置你的模型提供商。',
content: '点击"添加提供商"按钮,开始配置模型厂商的连接信息。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
@@ -35,70 +33,63 @@ export const modelAssignmentTourSteps: Step[] = [
spotlightClicks: true,
hideFooter: true,
},
// Step 4: 添加提供商弹窗
{
target: '[data-tour="provider-dialog"]',
content: '在这里,你可以选择你想要配置的模型提供商,填写相关信息后保存即可。',
content: '在这里可以选择厂商模板,填写 API Key、URL 和连接参数,保存即可供模型引用。',
placement: 'left' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 5: 名称输入框
{
target: '[data-tour="provider-name-input"]',
content: '这里的名称是你为这个模型提供商起的一个名字,方便你在后续使用时识别它。',
content: '这里的名称用于在后续模型配置中识别这个厂商。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 6: API 密钥输入框
{
target: '[data-tour="provider-apikey-input"]',
content: '这里需要填写从模型提供商那里获取的 API 密钥,用于验证调用模型服务。对于不同的提供商,获取 API 密钥的方式可能有所不同,请参考对应提供商的文档。',
content: '这里填写从模型厂商获取的 API Key,用于验证调用模型服务。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 7: URL 输入框
{
target: '[data-tour="provider-url-input"]',
content: '这里需要填写模型提供商的 API 访问地址确保填写正确以便系统能够连接到模型服务。对于不同的提供商API 地址可能有所不同,请参考对应提供商的文档。',
content: '这里填写模型商的 API 访问地址。不同厂商或平台的地址可能不同。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 8: 模板选择下拉框
{
target: '[data-tour="provider-template-select"]',
content: '当然,如果你不知道如何填写这些信息,很多模型提供商在这里都提供了预设模板供你选择,选择对应的模板后,相关信息会自动填充。',
content: '如果不确定如何填写,可以从预设模板中选择常用厂商,相关信息会自动填充。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 9: 保存按钮
{
target: '[data-tour="provider-save-button"]',
content: '填写完所有信息后,点击保存按钮,模型提供商就配置完成了。',
content: '填写完成后点击保存,模型商就配置了。',
placement: 'top' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 10: 取消按钮
{
target: '[data-tour="provider-cancel-button"]',
content: '因为这次咱们什么都没有填写,所以点击取消按钮退出吧。',
content: '这次只是演示流程,点击取消关闭厂商配置窗口。',
placement: 'top' as Placement,
disableBeacon: true,
disableOverlayClose: true,
@@ -106,20 +97,19 @@ export const modelAssignmentTourSteps: Step[] = [
spotlightClicks: true,
hideFooter: true,
},
// Step 11: 侧边栏 - 模型管理与分配按钮(点击下一步会自动导航)
{
target: '[data-tour="sidebar-model-management"]',
content: '配置好模型提供商后,接下来我们需要为麦麦添加模型并分配功能。点击"下一步"进入模型管理页面。',
placement: 'right' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 12: 添加模型按钮
{
target: '[data-tour="add-model-button"]',
content: '在为麦麦的组件分配模型之前,首先需要添加你想要分配的模型,点击"添加模型"按钮开始添加。',
target: '[data-tour="models-tab-trigger"]',
content: '厂商配置完成后,切换到"添加模型",把具体要使用的模型加入列表。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: true,
hideFooter: true,
},
{
target: '[data-tour="add-model-button"]',
content: '点击"添加模型"按钮开始添加一个可分配给功能的模型。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
@@ -127,60 +117,54 @@ export const modelAssignmentTourSteps: Step[] = [
spotlightClicks: true,
hideFooter: true,
},
// Step 13: 添加模型弹窗
{
target: '[data-tour="model-dialog"]',
content: '在这里,你可以选择你之前配置好的模型提供商,然后选择对应的模型来添加。',
content: '在这里选择刚才配置好的厂商,并填写模型名称、标识符、价格和能力参数。',
placement: 'left' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 14: 模型名称输入框
{
target: '[data-tour="model-name-input"]',
content: '这里的模型名称是你为这个模型起的一个名字,方便你在后续使用时识别它。',
content: '模型名称用于在任务分配时识别这个模型。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 15: API 提供商下拉框
{
target: '[data-tour="model-provider-select"]',
content: '这里选择你之前配置好的模型提供商,这样系统才能知道你要添加哪个提供商的模型。',
content: '这里选择模型所属的厂商,系统会根据厂商配置获取或调用对应模型。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 16: 模型标识符输入框
{
target: '[data-tour="model-identifier-input"]',
content: '这里需要填写你想要添加的模型标识符不同的模型提供商可能有不同的标识符格式,请参考对应提供商的文档。',
content: '这里填写模型标识符不同厂商的模型标识符格式可能不同,请参考对应厂商文档。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 17: 保存按钮
{
target: '[data-tour="model-save-button"]',
content: '填写完所有信息后,点击保存按钮,模型就添加完成了。',
content: '填写完成后点击保存,模型就会加入可用模型列表。',
placement: 'top' as Placement,
disableBeacon: true,
disableOverlayClose: true,
hideCloseButton: false,
spotlightClicks: false,
},
// Step 18: 取消按钮
{
target: '[data-tour="model-cancel-button"]',
content: '当然,因为这次咱们什么都没有填写,所以直接点击取消按钮退出吧,等你准备好了再来添加模型。',
content: '这次只是演示流程,点击取消关闭模型配置窗口。',
placement: 'top' as Placement,
disableBeacon: true,
disableOverlayClose: true,
@@ -188,10 +172,9 @@ export const modelAssignmentTourSteps: Step[] = [
spotlightClicks: true,
hideFooter: true,
},
// Step 19: 为模型分配功能标签页
{
target: '[data-tour="tasks-tab-trigger"]',
content: '最后一步,添加好模型后,切换到"为模型分配功能"标签页,为麦麦的各个组件分配合适的模型。',
content: '最后切换到"为模型分配功能",为麦麦的各个组件选择合适的模型。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
@@ -199,10 +182,9 @@ export const modelAssignmentTourSteps: Step[] = [
spotlightClicks: true,
hideFooter: true,
},
// Step 20: 组件模型卡片的模型选择
{
target: '[data-tour="task-model-select"]',
content: '在这里,你可以为每个组件选择一个或多个合适的模型,选择完成后配置会自动保存。恭喜你完成了模型配置的学习!',
content: '在这里可以为每个组件选择一个或多个模型,选择完成后配置会自动保存。',
placement: 'bottom' as Placement,
disableBeacon: true,
disableOverlayClose: true,
@@ -212,33 +194,9 @@ export const modelAssignmentTourSteps: Step[] = [
]
// 需要用户点击才能继续的步骤索引0-based
// Step 2 (index 2): 点击添加提供商按钮
// Step 9 (index 9): 点击取消按钮关闭提供商弹窗
// Step 11 (index 11): 点击添加模型按钮
// Step 17 (index 17): 点击取消按钮关闭模型弹窗
// Step 18 (index 18): 点击标签页切换
export const CLICK_TO_CONTINUE_STEPS = new Set([2, 9, 11, 17, 18])
export const CLICK_TO_CONTINUE_STEPS = new Set([1, 2, 9, 10, 11, 17, 18])
// 步骤与路由的映射
export const STEP_ROUTE_MAP: Record<number, string> = {
0: '/config/model', // 起始页面
1: '/config/model', // 侧边栏可见
2: '/config/modelProvider', // 需要在模型提供商页面
3: '/config/modelProvider',
4: '/config/modelProvider',
5: '/config/modelProvider',
6: '/config/modelProvider',
7: '/config/modelProvider',
8: '/config/modelProvider',
9: '/config/modelProvider',
10: '/config/modelProvider',
11: '/config/model', // 需要在模型管理页面
12: '/config/model',
13: '/config/model',
14: '/config/model',
15: '/config/model',
16: '/config/model',
17: '/config/model',
18: '/config/model',
19: '/config/model',
}
// 合并后所有步骤都在模型管理与分配页面内完成
export const STEP_ROUTE_MAP: Record<number, string> = Object.fromEntries(
modelAssignmentTourSteps.map((_, index) => [index, '/config/model'])
)

View File

@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {

View File

@@ -10,7 +10,7 @@ const Checkbox = React.forwardRef<
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
"grid place-content-center peer h-4 w-4 shrink-0 cursor-pointer rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}

View File

@@ -114,7 +114,7 @@ const CommandItem = React.forwardRef<
<CommandPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
"relative flex cursor-pointer gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
className
)}
{...props}

View File

@@ -18,7 +18,7 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
"flex h-9 w-full cursor-pointer items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
{...props}
@@ -117,7 +117,7 @@ const SelectItem = React.forwardRef<
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-2 pl-2 pr-8 text-sm outline-none bg-white dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-gray-100 dark:focus:bg-gray-800 data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"relative flex w-full cursor-pointer select-none items-center rounded-sm py-2 pl-2 pr-8 text-sm outline-none bg-white dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-gray-100 dark:focus:bg-gray-800 data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}

View File

@@ -29,7 +29,7 @@ const TabsTrigger = React.forwardRef<
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
"inline-flex cursor-pointer items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
className
)}
{...props}