diff --git a/web_ui/src/app/home/bots/ICreateBotField.ts b/web_ui/src/app/home/bots/ICreateBotField.ts index fea53c17..e69de29b 100644 --- a/web_ui/src/app/home/bots/ICreateBotField.ts +++ b/web_ui/src/app/home/bots/ICreateBotField.ts @@ -1,3 +0,0 @@ -export interface ICreateBotField { - -} \ No newline at end of file diff --git a/web_ui/src/app/home/bots/components/bot-form/BotForm.tsx b/web_ui/src/app/home/bots/components/bot-form/BotForm.tsx index 8f5bdf29..5c3e804d 100644 --- a/web_ui/src/app/home/bots/components/bot-form/BotForm.tsx +++ b/web_ui/src/app/home/bots/components/bot-form/BotForm.tsx @@ -1,281 +1,292 @@ -import {BotFormEntity, IBotFormEntity} from "@/app/home/bots/components/bot-form/BotFormEntity"; -import {Button, Form, Input, Select, Space} from "antd"; -import {useEffect, useState} from "react"; -import {IChooseAdapterEntity} from "@/app/home/bots/components/bot-form/ChooseAdapterEntity"; import { - DynamicFormItemConfig, - IDynamicFormItemConfig, - parseDynamicFormItemType + BotFormEntity, + IBotFormEntity +} from "@/app/home/bots/components/bot-form/BotFormEntity"; +import { Button, Form, Input, notification, Select, Space } from "antd"; +import { useEffect, useState } from "react"; +import { IChooseAdapterEntity } from "@/app/home/bots/components/bot-form/ChooseAdapterEntity"; +import { + DynamicFormItemConfig, + IDynamicFormItemConfig, + parseDynamicFormItemType } from "@/app/home/components/dynamic-form/DynamicFormItemConfig"; -import {UUID} from 'uuidjs' +import { UUID } from "uuidjs"; import DynamicFormComponent from "@/app/home/components/dynamic-form/DynamicFormComponent"; -import {httpClient} from "@/app/infra/http/HttpClient"; +import { httpClient } from "@/app/infra/http/HttpClient"; import { Bot } from "@/app/infra/api/api-types"; -import { notification } from "antd"; - export default function BotForm({ - initBotId, - onFormSubmit, - onFormCancel, + initBotId, + onFormSubmit, + onFormCancel }: { - initBotId?: string; - onFormSubmit: (value: IBotFormEntity) => void; - onFormCancel: (value: IBotFormEntity) => void; + initBotId?: string; + onFormSubmit: (value: IBotFormEntity) => void; + onFormCancel: (value: IBotFormEntity) => void; }) { - const [adapterNameToDynamicConfigMap, setAdapterNameToDynamicConfigMap] = useState(new Map()) - const [form] = Form.useForm(); - const [showDynamicForm, setShowDynamicForm] = useState(false) - const [dynamicForm] = Form.useForm(); - const [adapterNameList, setAdapterNameList] = useState([]) - const [dynamicFormConfigList, setDynamicFormConfigList] = useState([]) - const [isLoading, setIsLoading] = useState(false) + const [adapterNameToDynamicConfigMap, setAdapterNameToDynamicConfigMap] = + useState(new Map()); + const [form] = Form.useForm(); + const [showDynamicForm, setShowDynamicForm] = useState(false); + const [dynamicForm] = Form.useForm(); + const [adapterNameList, setAdapterNameList] = useState< + IChooseAdapterEntity[] + >([]); + const [dynamicFormConfigList, setDynamicFormConfigList] = useState< + IDynamicFormItemConfig[] + >([]); + const [isLoading, setIsLoading] = useState(false); - useEffect(() => { - initBotFormComponent() - if (initBotId) { - onEditMode() - } else { - onCreateMode() - } - }, []) + useEffect(() => { + initBotFormComponent(); + if (initBotId) { + onEditMode(); + } else { + onCreateMode(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - async function initBotFormComponent() { - // 拉取adapter - const rawAdapterList = await httpClient.getAdapters() - // 初始化适配器选择列表 - setAdapterNameList( - rawAdapterList.adapters.map(item => { - return { - label: item.label.zh_CN, - value: item.name - } + async function initBotFormComponent() { + // 拉取adapter + const rawAdapterList = await httpClient.getAdapters(); + // 初始化适配器选择列表 + setAdapterNameList( + rawAdapterList.adapters.map((item) => { + return { + label: item.label.zh_CN, + value: item.name + }; + }) + ); + // 初始化适配器表单map + rawAdapterList.adapters.forEach((rawAdapter) => { + adapterNameToDynamicConfigMap.set( + rawAdapter.name, + rawAdapter.spec.config.map( + (item) => + new DynamicFormItemConfig({ + default: item.default, + id: UUID.generate(), + label: item.label, + name: item.name, + required: item.required, + type: parseDynamicFormItemType(item.type) }) ) - // 初始化适配器表单map - rawAdapterList.adapters.forEach(rawAdapter => { - adapterNameToDynamicConfigMap.set( - rawAdapter.name, - rawAdapter.spec.config.map(item => - new DynamicFormItemConfig({ - default: item.default, - id: UUID.generate(), - label: item.label, - name: item.name, - required: item.required, - type: parseDynamicFormItemType(item.type) - }) - ) - ) + ); + }); + // 拉取初始化表单信息 + if (initBotId) { + getBotFieldById(initBotId).then((val) => { + form.setFieldsValue(val); + handleAdapterSelect(val.adapter); + dynamicForm.setFieldsValue(val.adapter_config); + }); + } else { + form.resetFields(); + } + setAdapterNameToDynamicConfigMap(adapterNameToDynamicConfigMap); + } + + async function onCreateMode() {} + + function onEditMode() {} + + async function getBotFieldById(botId: string): Promise { + const bot = (await httpClient.getBot(botId)).bot; + return new BotFormEntity({ + adapter: bot.adapter, + description: bot.description, + name: bot.name, + adapter_config: bot.adapter_config + }); + } + + function handleAdapterSelect(adapterName: string) { + console.log("Select adapter: ", adapterName); + if (adapterName) { + const dynamicFormConfigList = + adapterNameToDynamicConfigMap.get(adapterName); + console.log(dynamicFormConfigList); + if (dynamicFormConfigList) { + setDynamicFormConfigList(dynamicFormConfigList); + } + setShowDynamicForm(true); + } else { + setShowDynamicForm(false); + } + } + + function handleSubmitButton() { + form.submit(); + } + + function handleFormFinish() { + dynamicForm.submit(); + } + + // 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里 + function onDynamicFormSubmit(value: object) { + setIsLoading(true); + console.log("set loading", true); + if (initBotId) { + // 编辑提交 + console.log("submit edit", form.getFieldsValue(), value); + const updateBot: Bot = { + uuid: initBotId, + name: form.getFieldsValue().name, + description: form.getFieldsValue().description, + adapter: form.getFieldsValue().adapter, + adapter_config: value + }; + httpClient + .updateBot(initBotId, updateBot) + .then((res) => { + // TODO success toast + console.log("update bot success", res); + onFormSubmit(form.getFieldsValue()); + notification.success({ + message: "更新成功", + description: "机器人更新成功" + }); }) - // 拉取初始化表单信息 - if (initBotId) { - getBotFieldById(initBotId).then(val => { - form.setFieldsValue(val) - handleAdapterSelect(val.adapter) - dynamicForm.setFieldsValue(val.adapter_config) - }) - } else { - form.resetFields() - } - setAdapterNameToDynamicConfigMap(adapterNameToDynamicConfigMap) - } - - async function onCreateMode() { - - } - - function onEditMode() { - - } - - async function getBotFieldById(botId: string): Promise { - const bot = (await httpClient.getBot(botId)).bot - let botFormEntity = new BotFormEntity({ - adapter: bot.adapter, - description: bot.description, - name: bot.name, - adapter_config: bot.adapter_config + .catch(() => { + // TODO error toast + notification.error({ + message: "更新失败", + description: "机器人更新失败" + }); }) - return botFormEntity + .finally(() => { + setIsLoading(false); + form.resetFields(); + dynamicForm.resetFields(); + }); + } else { + // 创建提交 + console.log("submit create", form.getFieldsValue(), value); + const newBot: Bot = { + name: form.getFieldsValue().name, + description: form.getFieldsValue().description, + adapter: form.getFieldsValue().adapter, + adapter_config: value + }; + httpClient + .createBot(newBot) + .then((res) => { + // TODO success toast + notification.success({ + message: "创建成功", + description: "机器人创建成功" + }); + console.log(res); + onFormSubmit(form.getFieldsValue()); + }) + .catch(() => { + // TODO error toast + notification.error({ + message: "创建失败", + description: "机器人创建失败" + }); + }) + .finally(() => { + setIsLoading(false); + form.resetFields(); + dynamicForm.resetFields(); + }); } + setShowDynamicForm(false); + console.log("set loading", false); + // TODO 刷新bot列表 + // TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)? + } - function handleAdapterSelect(adapterName: string) { - console.log("Select adapter: ", adapterName) - if (adapterName) { - const dynamicFormConfigList = adapterNameToDynamicConfigMap.get(adapterName) - console.log(dynamicFormConfigList) - if (dynamicFormConfigList) { - setDynamicFormConfigList(dynamicFormConfigList) - } - setShowDynamicForm(true) - } else { - setShowDynamicForm(false) - } - } + function handleSaveButton() { + form.submit(); + } - function handleSubmitButton() { - form.submit() - } + return ( +
+
+ + label={"机器人名称"} + name={"name"} + rules={[{ required: true, message: "该项为必填项哦~" }]} + > + + - function handleFormFinish(value: IBotFormEntity) { - dynamicForm.submit() - } + + label={"描述"} + name={"description"} + rules={[{ required: true, message: "该项为必填项哦~" }]} + > + + - // 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里 - function onDynamicFormSubmit(value: object) { - setIsLoading(true) - console.log('setloading', true) - if (initBotId) { - // 编辑提交 - console.log('submit edit', form.getFieldsValue() ,value) - let updateBot: Bot = { - uuid: initBotId, - name: form.getFieldsValue().name, - description: form.getFieldsValue().description, - adapter: form.getFieldsValue().adapter, - adapter_config: value - } - httpClient.updateBot(initBotId, updateBot).then(res => { - // TODO success toast - console.log("update bot success", res) - onFormSubmit(form.getFieldsValue()) - notification.success({ - message: "更新成功", - description: "机器人更新成功" - }) - }).catch(err => { - // TODO error toast - notification.error({ - message: "更新失败", - description: "机器人更新失败" - }) - }).finally(() => { - setIsLoading(false) - form.resetFields() - dynamicForm.resetFields() - }) - } else { - // 创建提交 - console.log('submit create', form.getFieldsValue() ,value) - let newBot: Bot = { - name: form.getFieldsValue().name, - description: form.getFieldsValue().description, - adapter: form.getFieldsValue().adapter, - adapter_config: value - } - httpClient.createBot(newBot).then(res => { - // TODO success toast - notification.success({ - message: "创建成功", - description: "机器人创建成功" - }) - console.log(res) - onFormSubmit(form.getFieldsValue()) - }).catch(err => { - // TODO error toast - notification.error({ - message: "创建失败", - description: "机器人创建失败" - }) - }).finally(() => { - setIsLoading(false) - form.resetFields() - dynamicForm.resetFields() - }) - } - setShowDynamicForm(false) - console.log('setloading', false) - // TODO 刷新bot列表 - // TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)? - } - - function handleSaveButton() { - form.submit() - } - - return ( -
- - - label={"机器人名称"} - name={"name"} - rules={[{required: true, message: "该项为必填项哦~"}]} - > - - - - - label={"描述"} - name={"description"} - rules={[{required: true, message: "该项为必填项哦~"}]} - > - - - - - label={"平台/适配器选择"} - name={"adapter"} - rules={[{required: true, message: "该项为必填项哦~"}]} - > - { + handleAdapterSelect(value); + }} + options={adapterNameList} + /> + + + {showDynamicForm && ( + + )} + + {!initBotId && ( + + )} + {initBotId && ( + + )} + + +
+ ); +} diff --git a/web_ui/src/app/home/components/home-sidebar/HomeSidebar.tsx b/web_ui/src/app/home/components/home-sidebar/HomeSidebar.tsx index 0cc76182..12813b29 100644 --- a/web_ui/src/app/home/components/home-sidebar/HomeSidebar.tsx +++ b/web_ui/src/app/home/components/home-sidebar/HomeSidebar.tsx @@ -1,107 +1,100 @@ -"use client" +"use client"; -import styles from "./HomeSidebar.module.css" -import {useEffect, useState} from "react"; -import {SidebarChild, SidebarChildVO} from "@/app/home/components/home-sidebar/HomeSidebarChild"; -import {useRouter, usePathname, useSearchParams} from "next/navigation"; -import {sidebarConfigList} from "@/app/home/components/home-sidebar/sidbarConfigList"; +import styles from "./HomeSidebar.module.css"; +import { useEffect, useState } from "react"; +import { + SidebarChild, + SidebarChildVO +} from "@/app/home/components/home-sidebar/HomeSidebarChild"; +import { useRouter, usePathname } from "next/navigation"; +import { sidebarConfigList } from "@/app/home/components/home-sidebar/sidbarConfigList"; // TODO 侧边导航栏要加动画 export default function HomeSidebar({ - onSelectedChange + onSelectedChangeAction }: { - onSelectedChange: (sidebarChild: SidebarChildVO) => void + onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void; }) { - // 路由相关 - const router = useRouter() - const pathname = usePathname(); - const searchParams = useSearchParams(); - // 路由被动变化时处理 - useEffect(() => { - handleRouteChange(pathname) - }, [pathname, searchParams]); + // 路由相关 + const router = useRouter(); + const pathname = usePathname(); + // 路由被动变化时处理 + useEffect(() => { + handleRouteChange(pathname); + }, [pathname]); - const [selectedChild, setSelectedChild] = useState(sidebarConfigList[0]) + const [selectedChild, setSelectedChild] = useState( + sidebarConfigList[0] + ); - useEffect(() => { - console.log('HomeSidebar挂载完成'); - initSelect() - return () => console.log('HomeSidebar卸载'); - }, []); + useEffect(() => { + console.log("HomeSidebar挂载完成"); + initSelect(); + return () => console.log("HomeSidebar卸载"); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + function handleChildClick(child: SidebarChildVO) { + setSelectedChild(child); + handleRoute(child); + onSelectedChangeAction(child); + } + function initSelect() { + handleChildClick(sidebarConfigList[0]); + } - function handleChildClick(child: SidebarChildVO) { - setSelectedChild(child) - handleRoute(child) - onSelectedChange(child) + function handleRoute(child: SidebarChildVO) { + console.log(child); + router.push(`${child.route}`); + } + + function handleRouteChange(pathname: string) { + // TODO 这段逻辑并不好,未来router封装好后改掉 + // 判断在home下,并且路由更改的是自己的路由子组件则更新UI + const routeList = pathname.split("/"); + if ( + routeList[1] === "home" && + sidebarConfigList.find((childConfig) => childConfig.route === pathname) + ) { + console.log("find success"); + const routeSelectChild = sidebarConfigList.find( + (childConfig) => childConfig.route === pathname + ); + if (routeSelectChild) { + setSelectedChild(routeSelectChild); + } } + } - function initSelect() { - handleChildClick(sidebarConfigList[0]) - } - - function handleRoute(child: SidebarChildVO) { - console.log(child) - router.push(`${child.route}`) - } - - function handleRouteChange(pathname: string) { - // TODO 这段逻辑并不好,未来router封装好后改掉 - // 判断在home下,并且路由更改的是自己的路由子组件则更新UI - const routeList = pathname.split('/') - if ( - routeList[1] === "home" && - sidebarConfigList.find(childConfig => - childConfig.route === pathname - ) - ) { - console.log("find success") - const routeSelectChild = sidebarConfigList.find(childConfig => - childConfig.route === pathname - ) - if (routeSelectChild) { - setSelectedChild(routeSelectChild) - } - } - } - - - return ( -
- {/* LangBot、ICON区域 */} -
- {/* icon */} -
- L -
-
- Langbot -
+ return ( +
+ {/* LangBot、ICON区域 */} +
+ {/* icon */} +
L
+
Langbot
+
+ {/* 菜单列表,后期可升级成配置驱动 */} +
+ {sidebarConfigList.map((config) => { + return ( +
{ + console.log("click:", config.id); + handleChildClick(config); + }} + > +
- {/* 菜单列表,后期可升级成配置驱动 */} -
- { - sidebarConfigList.map(config => { - return ( -
{ - console.log('click:', config.id) - handleChildClick(config) - }} - > - -
- ) - }) - } - -
-
- ); -} \ No newline at end of file + ); + })} +
+
+ ); +} diff --git a/web_ui/src/app/home/components/home-sidebar/HomeSidebarChild.tsx b/web_ui/src/app/home/components/home-sidebar/HomeSidebarChild.tsx index 448d0ab8..e1089eba 100644 --- a/web_ui/src/app/home/components/home-sidebar/HomeSidebarChild.tsx +++ b/web_ui/src/app/home/components/home-sidebar/HomeSidebarChild.tsx @@ -1,41 +1,44 @@ import styles from "./HomeSidebar.module.css"; export interface ISidebarChildVO { - id: string; - icon: string; - name: string; - route: string; + id: string; + icon: string; + name: string; + route: string; } export class SidebarChildVO { - id: string; - icon: string; - name: string; - route: string; + id: string; + icon: string; + name: string; + route: string; - constructor(props: ISidebarChildVO) { - this.id = props.id; - this.icon = props.icon; - this.name = props.name; - this.route = props.route; - } + constructor(props: ISidebarChildVO) { + this.id = props.id; + this.icon = props.icon; + this.name = props.name; + this.route = props.route; + } } - - export function SidebarChild({ - icon, - name, - isSelected, + icon, + name, + isSelected }: { - icon: string; - name: string; - isSelected: boolean; + icon: string; + name: string; + isSelected: boolean; }) { - return ( -
-
-
{name}
-
- ); -} \ No newline at end of file + return ( +
+
+
+ {icon} + {name} +
+
+ ); +} diff --git a/web_ui/src/app/home/layout.tsx b/web_ui/src/app/home/layout.tsx index 57f21bac..7985dae6 100644 --- a/web_ui/src/app/home/layout.tsx +++ b/web_ui/src/app/home/layout.tsx @@ -1,36 +1,30 @@ -"use client" +"use client"; -import '@ant-design/v5-patch-for-react-19'; -import styles from "./layout.module.css" +import "@ant-design/v5-patch-for-react-19"; +import styles from "./layout.module.css"; import HomeSidebar from "@/app/home/components/home-sidebar/HomeSidebar"; import HomeTitleBar from "@/app/home/components/home-titlebar/HomeTitleBar"; -import React, {useState} from "react"; -import {SidebarChildVO} from "@/app/home/components/home-sidebar/HomeSidebarChild"; -import { useRouter } from 'next/navigation'; +import React, { useState } from "react"; +import { SidebarChildVO } from "@/app/home/components/home-sidebar/HomeSidebarChild"; export default function HomeLayout({ - children + children }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode; }>) { - const router = useRouter(); - const [title, setTitle] = useState("") - const onSelectedChange = (child: SidebarChildVO) => { - setTitle(child.name) - } + const [title, setTitle] = useState(""); + const onSelectedChange = (child: SidebarChildVO) => { + setTitle(child.name); + }; - return ( -
- -
- - {/* 主页面 */} -
- {children} -
-
-
- ) + return ( +
+ +
+ + {/* 主页面 */} +
{children}
+
+
+ ); } diff --git a/web_ui/src/app/home/models/component/llm-form/LLMForm.tsx b/web_ui/src/app/home/models/component/llm-form/LLMForm.tsx index ccb00730..4832cc6c 100644 --- a/web_ui/src/app/home/models/component/llm-form/LLMForm.tsx +++ b/web_ui/src/app/home/models/component/llm-form/LLMForm.tsx @@ -1,296 +1,287 @@ import styles from "@/app/home/models/LLMConfig.module.css"; -import {Button, Form, Input, Select, SelectProps, Space, Modal} from "antd"; -import {ICreateLLMField} from "@/app/home/models/ICreateLLMField"; -import {useEffect, useState} from "react"; -import {IChooseRequesterEntity} from "@/app/home/models/component/llm-form/ChooseAdapterEntity"; +import { Button, Form, Input, Select, SelectProps, Space, Modal } from "antd"; +import { ICreateLLMField } from "@/app/home/models/ICreateLLMField"; +import { useEffect, useState } from "react"; +import { IChooseRequesterEntity } from "@/app/home/models/component/llm-form/ChooseAdapterEntity"; import { httpClient } from "@/app/infra/http/HttpClient"; -import {LLMModel} from "@/app/infra/api/api-types"; -import {UUID} from "uuidjs"; +import { LLMModel } from "@/app/infra/api/api-types"; +import { UUID } from "uuidjs"; export default function LLMForm({ - editMode, - initLLMId, - onFormSubmit, - onFormCancel, - onLLMDeleted, + editMode, + initLLMId, + onFormSubmit, + onFormCancel, + onLLMDeleted }: { - editMode: boolean; - initLLMId?: string; - onFormSubmit: (value: ICreateLLMField) => void; - onFormCancel: (value: ICreateLLMField) => void; - onLLMDeleted: () => void; + editMode: boolean; + initLLMId?: string; + onFormSubmit: (value: ICreateLLMField) => void; + onFormCancel: (value: ICreateLLMField) => void; + onLLMDeleted: () => void; }) { - const [form] = Form.useForm(); - const extraOptions: SelectProps['options'] = [] - const [initValue, setInitValue] = useState() - const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false) - const abilityOptions: SelectProps['options'] = [ - { - label: '函数调用', - value: 'func_call', - }, - { - label: '图像识别', - value: 'vision', - }, - ]; - const [requesterNameList, setRequesterNameList] = useState([]) - - useEffect(() => { - initLLMModelFormComponent() - if (editMode && initLLMId) { - getLLMConfig(initLLMId).then(val => { - form.setFieldsValue(val) - }) - } else { - form.resetFields() - } - }, []) - - async function initLLMModelFormComponent() { - const requesterNameList = await httpClient.getProviderRequesters() - setRequesterNameList(requesterNameList.requesters.map(item => { - return { - label: item.label.zh_CN, - value: item.name - } - })) + const [form] = Form.useForm(); + const extraOptions: SelectProps["options"] = []; + const [initValue] = useState(); + const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false); + const abilityOptions: SelectProps["options"] = [ + { + label: "函数调用", + value: "func_call" + }, + { + label: "图像识别", + value: "vision" } + ]; + const [requesterNameList, setRequesterNameList] = useState< + IChooseRequesterEntity[] + >([]); - async function getLLMConfig(id: string): Promise { + useEffect(() => { + initLLMModelFormComponent(); + if (editMode && initLLMId) { + getLLMConfig(initLLMId).then((val) => { + form.setFieldsValue(val); + }); + } else { + form.resetFields(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - const llmModel = await httpClient.getProviderLLMModel(id) - - let fakeExtraArgs = [] - const extraArgs = llmModel.model.extra_args as Record - for (const key in extraArgs) { - fakeExtraArgs.push(`${key}:${extraArgs[key]}`) - } + async function initLLMModelFormComponent() { + const requesterNameList = await httpClient.getProviderRequesters(); + setRequesterNameList( + requesterNameList.requesters.map((item) => { return { - name: llmModel.model.name, - model_provider: llmModel.model.requester, - url: llmModel.model.requester_config?.base_url, - api_key: llmModel.model.api_keys[0], - abilities: llmModel.model.abilities, - extra_args: fakeExtraArgs, - } - } - - function handleFormSubmit(value: ICreateLLMField) { - if (editMode) { - // 暂不支持更改模型 - // onSaveEdit(value) - } else { - onCreateLLM(value) - } - form.resetFields() - } - - function onSaveEdit(value: ICreateLLMField) { - const requestParam: LLMModel = { - uuid: UUID.generate(), - name: value.name, - description: "", - requester: value.model_provider, - requester_config: { - "base_url": value.url, - "timeout": 120 - }, - extra_args: value.extra_args, - api_keys: [value.api_key], - abilities: value.abilities, - // created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', - // updated_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', + label: item.label.zh_CN, + value: item.name }; - httpClient.createProviderLLMModel(requestParam).then(r => console.log(r)) + }) + ); + } + + async function getLLMConfig(id: string): Promise { + const llmModel = await httpClient.getProviderLLMModel(id); + + const fakeExtraArgs = []; + const extraArgs = llmModel.model.extra_args as Record; + for (const key in extraArgs) { + fakeExtraArgs.push(`${key}:${extraArgs[key]}`); } + return { + name: llmModel.model.name, + model_provider: llmModel.model.requester, + url: llmModel.model.requester_config?.base_url, + api_key: llmModel.model.api_keys[0], + abilities: llmModel.model.abilities, + extra_args: fakeExtraArgs + }; + } - function onCreateLLM(value: ICreateLLMField) { - console.log("create llm", value) - const requestParam: LLMModel = { - uuid: UUID.generate(), - name: value.name, - description: "", - requester: value.model_provider, - requester_config: { - "base_url": value.url, - "timeout": 120 - }, - extra_args: value.extra_args, - api_keys: [value.api_key], - abilities: value.abilities, - // created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', - // updated_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', - }; - httpClient.createProviderLLMModel(requestParam).then(r => { - onFormSubmit(value) - }) + function handleFormSubmit(value: ICreateLLMField) { + if (editMode) { + // 暂不支持更改模型 + // onSaveEdit(value) + } else { + onCreateLLM(value); } + form.resetFields(); + } - function handleAbilitiesChange() { + // function onSaveEdit(value: ICreateLLMField) { + // const requestParam: LLMModel = { + // uuid: UUID.generate(), + // name: value.name, + // description: "", + // requester: value.model_provider, + // requester_config: { + // "base_url": value.url, + // "timeout": 120 + // }, + // extra_args: value.extra_args, + // api_keys: [value.api_key], + // abilities: value.abilities, + // // created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', + // // updated_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', + // }; + // httpClient.createProviderLLMModel(requestParam).then(r => console.log(r)) + // } + function onCreateLLM(value: ICreateLLMField) { + console.log("create llm", value); + const requestParam: LLMModel = { + uuid: UUID.generate(), + name: value.name, + description: "", + requester: value.model_provider, + requester_config: { + base_url: value.url, + timeout: 120 + }, + extra_args: value.extra_args, + api_keys: [value.api_key], + abilities: value.abilities + // created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', + // updated_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', + }; + httpClient.createProviderLLMModel(requestParam).then(() => { + onFormSubmit(value); + }); + } + + function handleAbilitiesChange() {} + + function deleteModel() { + if (initLLMId) { + httpClient.deleteProviderLLMModel(initLLMId).then(() => { + onLLMDeleted(); + }); } + } - function deleteModel() { - if (initLLMId) { - httpClient.deleteProviderLLMModel(initLLMId).then(res => { - onLLMDeleted() - }) - } - } - - return ( -
- setShowDeleteConfirmModal(false)} - footer={ -
- - -
- } - + return ( +
+ setShowDeleteConfirmModal(false)} + footer={ +
+ +
+ } + > + 你确定要删除这个模型吗? +
+ + + label={"模型名称"} + name={"name"} + rules={[{ required: true, message: "该项为必填项哦~" }]} + > + + + + + label={"模型供应商"} + name={"model_provider"} + rules={[{ required: true, message: "该项为必填项哦~" }]} + > + + + + + label={"API Key"} + name={"api_key"} + rules={[{ required: true, message: "该项为必填项哦~" }]} + > + + + + label={"开启能力"} name={"abilities"}> + + + + + + {!editMode && ( + + )} + {editMode && ( + + )} + - } - { - editMode && - - } - - - - -
- - ) -} \ No newline at end of file + 取消 + + + + +
+ ); +} diff --git a/web_ui/src/app/home/pipelines/components/pipeline-form/PipelineChildFormEntity.ts b/web_ui/src/app/home/pipelines/components/pipeline-form/PipelineChildFormEntity.ts index a2038c35..4f9770dd 100644 --- a/web_ui/src/app/home/pipelines/components/pipeline-form/PipelineChildFormEntity.ts +++ b/web_ui/src/app/home/pipelines/components/pipeline-form/PipelineChildFormEntity.ts @@ -1,20 +1,19 @@ -import {DynamicFormItemConfig} from "@/app/home/components/dynamic-form/DynamicFormItemConfig"; +import { DynamicFormItemConfig } from "@/app/home/components/dynamic-form/DynamicFormItemConfig"; export interface IPipelineChildFormEntity { - name: string; - label: string; - formItems: DynamicFormItemConfig[] + name: string; + label: string; + formItems: DynamicFormItemConfig[]; } export class PipelineChildFormEntity implements IPipelineChildFormEntity { - formItems: DynamicFormItemConfig[]; - label: string; - name: string; + formItems: DynamicFormItemConfig[]; + label: string; + name: string; - constructor(props: IPipelineChildFormEntity) { - this.form = props.form; - this.label = props.label; - this.name = props.name; - this.formItems = props.formItems; - } -} \ No newline at end of file + constructor(props: IPipelineChildFormEntity) { + this.label = props.label; + this.name = props.name; + this.formItems = props.formItems; + } +} diff --git a/web_ui/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx b/web_ui/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx index 5e81219f..32ca5405 100644 --- a/web_ui/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx +++ b/web_ui/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx @@ -1,633 +1,604 @@ -import {Form, Button, Switch, Select, Input, InputNumber, SelectProps} from "antd"; -import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons'; -import {useEffect, useState} from "react"; -import styles from "./pipelineFormStyle.module.css" -import {httpClient} from "@/app/infra/http/HttpClient"; -import {LLMModel, Pipeline} from "@/app/infra/api/api-types"; -import {LLMCardVO} from "@/app/home/models/component/llm-card/LLMCardVO"; -import {UUID} from "uuidjs"; +import { + Form, + Button, + Switch, + Select, + Input, + InputNumber, + SelectProps +} from "antd"; +import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons"; +import { useEffect, useState } from "react"; +import styles from "./pipelineFormStyle.module.css"; +import { httpClient } from "@/app/infra/http/HttpClient"; +import { LLMModel, Pipeline } from "@/app/infra/api/api-types"; +import { UUID } from "uuidjs"; export default function PipelineFormComponent({ - onFinish, - onCancel, + onFinish }: { - onFinish: () => void; - onCancel: () => void; + onFinish: () => void; }) { - const [nowFormIndex, setNowFormIndex] = useState(0) - const [nowAIRunner, setNowAIRunner] = useState("") - const [llmModelList, setLlmModelList] = useState([]) - // 这里不好,可以改成enum等 - const formLabelList: FormLabel[] = [ - {label: "基础", name: "basic"}, - {label: "AI能力", name: "ai"}, - {label: "触发条件", name: "trigger"}, - {label: "安全能力", name: "safety"}, - {label: "输出处理", name: "output"}, - ] - const [basicForm] = Form.useForm() - const [aiForm] = Form.useForm() - const [triggerForm] = Form.useForm() - const [safetyForm] = Form.useForm() - const [outputForm] = Form.useForm() + const [nowFormIndex, setNowFormIndex] = useState(0); + const [nowAIRunner, setNowAIRunner] = useState(""); + const [llmModelList, setLlmModelList] = useState([]); + // 这里不好,可以改成enum等 + const formLabelList: FormLabel[] = [ + { label: "基础", name: "basic" }, + { label: "AI能力", name: "ai" }, + { label: "触发条件", name: "trigger" }, + { label: "安全能力", name: "safety" }, + { label: "输出处理", name: "output" } + ]; + const [basicForm] = Form.useForm(); + const [aiForm] = Form.useForm(); + const [triggerForm] = Form.useForm(); + const [safetyForm] = Form.useForm(); + const [outputForm] = Form.useForm(); - useEffect(() => { - getLLMModelList() - }, []) + useEffect(() => { + getLLMModelList(); + }, []); - function getLLMModelList() { - httpClient.getProviderLLMModels().then((resp) => { - setLlmModelList(resp.models.map((model: LLMModel) => { - return { - value: model.uuid, - label: model.name, - } - })) - }).catch((err) => { - console.error("get LLM model list error", err) - }) + function getLLMModelList() { + httpClient + .getProviderLLMModels() + .then((resp) => { + setLlmModelList( + resp.models.map((model: LLMModel) => { + return { + value: model.uuid, + label: model.name + }; + }) + ); + }) + .catch((err) => { + console.error("get LLM model list error", err); + }); + } + + function getNowFormLabel() { + return formLabelList[nowFormIndex]; + } + + function getPreFormLabel(): undefined | FormLabel { + if (nowFormIndex !== undefined && nowFormIndex > 0) { + return formLabelList[nowFormIndex - 1]; + } else { + return undefined; } + } - function getNowFormLabel() { - return formLabelList[nowFormIndex] + function getNextFormLabel(): undefined | FormLabel { + if (nowFormIndex !== undefined && nowFormIndex < formLabelList.length - 1) { + return formLabelList[nowFormIndex + 1]; + } else { + return undefined; } + } - - function getPreFormLabel(): undefined | FormLabel { - if (nowFormIndex !== undefined && nowFormIndex > 0) { - return formLabelList[nowFormIndex - 1] - } else { - return undefined - } + function addFormLabelIndex() { + if (nowFormIndex < formLabelList.length - 1) { + setNowFormIndex(nowFormIndex + 1); } + } - function getNextFormLabel(): undefined | FormLabel { - if (nowFormIndex !== undefined && nowFormIndex < formLabelList.length - 1) { - return formLabelList[nowFormIndex + 1] - } else { - return undefined - } + function reduceFormLabelIndex() { + if (nowFormIndex > 0) { + setNowFormIndex(nowFormIndex - 1); } + } - function addFormLabelIndex() { - if (nowFormIndex < formLabelList.length - 1) { - setNowFormIndex(nowFormIndex + 1) - } - } + function handleCommit() { + Promise.all([ + basicForm.validateFields(), + aiForm.validateFields(), + triggerForm.validateFields(), + safetyForm.validateFields(), + outputForm.validateFields() + ]) + .then(() => { + const pipeline = assembleForm(); + httpClient.createPipeline(pipeline).then(() => onFinish()); + }) + .catch((e) => { + console.error(e); + }); + } - function reduceFormLabelIndex() { - if (nowFormIndex > 0) { - setNowFormIndex(nowFormIndex - 1) - } - } + // TODO 类型混乱,需要优化 + function assembleForm(): Pipeline { + console.log("basicForm:", basicForm.getFieldsValue()); + console.log("aiForm:", aiForm.getFieldsValue()); + console.log("triggerForm:", triggerForm.getFieldsValue()); + console.log("safetyForm:", safetyForm.getFieldsValue()); + console.log("outputForm:", outputForm.getFieldsValue()); + const config: object = { + ai: aiForm.getFieldsValue(), + trigger: triggerForm.getFieldsValue(), + safety: safetyForm.getFieldsValue(), + output: outputForm.getFieldsValue() + }; - function handleCommit() { - Promise.all([ - basicForm.validateFields(), - aiForm.validateFields(), - triggerForm.validateFields(), - safetyForm.validateFields(), - outputForm.validateFields(), - ]).then(() => { - const pipeline = assembleForm() - httpClient.createPipeline(pipeline).then(r => - onFinish() - ) - }).catch(e => { - console.error(e) - }) - } + return { + config, + created_at: "", + description: basicForm.getFieldsValue().description, + for_version: "", + name: basicForm.getFieldsValue().name, + stages: [], + updated_at: "", + uuid: UUID.generate() + }; + } - // TODO 类型混乱,需要优化 - function assembleForm(): Pipeline { - console.log("basicForm:", basicForm.getFieldsValue()) - console.log("aiForm:", aiForm.getFieldsValue()) - console.log("triggerForm:", triggerForm.getFieldsValue()) - console.log("safetyForm:", safetyForm.getFieldsValue()) - console.log("outputForm:", outputForm.getFieldsValue()) - const config: object = { - ai: aiForm.getFieldsValue(), - trigger: triggerForm.getFieldsValue(), - safety: safetyForm.getFieldsValue(), - output: outputForm.getFieldsValue(), - } - - return { - config, - created_at: "", - description: basicForm.getFieldsValue().description, - for_version: "", - name: basicForm.getFieldsValue().name, - stages: [], - updated_at: "", - uuid: UUID.generate(), - } - } - - - return ( -
+

{getNowFormLabel().label}

+
+ -

- {getNowFormLabel().label} -

- - - - + +
- - - -
- {/* AI能力表单 ai */} -
+ + +
+ {/* AI能力表单 ai */} +
+ {/* Runner 配置区块 */} +
运行器
+ + setNowAIRunner(value)} - /> - - - {/* 内置 Agent 配置区块 */} + - - - - - {/* TODO 这里要做转换处理 */} - - - - - + required: true } - {/* Dify 服务 API 区块 */} - { - nowAIRunner === "dify-service-api" && - <> -
配置Dify服务API
- - - - - ...\<\/think\>", value: "plain"}, - {label: "原始", value: "original"}, - {label: "移除", value: "remove"} - ]} - /> - - - } - {/* 阿里云百炼区块 */} - { - nowAIRunner === "dashscope-app-api" && - <> -
配置阿里云百炼平台 API
- - - - - - - - } -
- - {/* 触发条件表单 trigger */} -
- {/* 群响应规则块 */} -
群响应规则
- - - - - - - - - -
访问控制
- - - - - - - - - - - - - - - - - {/* 速率限制块 rate-limit */} -
速率限制
- - - - - - - - - - - - - - {/* 强制延迟区块 */} -
强制延迟
- - - - - - - - {/* 杂项区块 */} -
杂项
- - - - - - - - - - - - -
- -
- - - - + + + + ...\<\/think\>", value: "plain" }, + { label: "原始", value: "original" }, + { label: "移除", value: "remove" } + ]} + /> + + + )} + {/* 阿里云百炼区块 */} + {nowAIRunner === "dashscope-app-api" && ( + <> +
+ 配置阿里云百炼平台 API
+ + + + + + + + )} + -
- ) -} + {/* 触发条件表单 trigger */} +
+ {/* 群响应规则块 */} +
群响应规则
+ + + + + + + + + +
访问控制
+ + + -} + + + + + + + + + + + + + {/* 速率限制块 rate-limit */} +
速率限制
+ + + + + + + + + + + + + + {/* 强制延迟区块 */} +
强制延迟
+ + + + + + + + {/* 杂项区块 */} +
杂项
+ + + + + + + + + + + + +
+ +
+ + + + +
+
+ ); } interface FormLabel { - label: string, - name: string, + label: string; + name: string; } - -interface LLMSelectList { - label: string, - name: string, -} \ No newline at end of file diff --git a/web_ui/src/app/home/pipelines/page.tsx b/web_ui/src/app/home/pipelines/page.tsx index e6a8a331..fc1036f3 100644 --- a/web_ui/src/app/home/pipelines/page.tsx +++ b/web_ui/src/app/home/pipelines/page.tsx @@ -1,68 +1,78 @@ -"use client" -import {Modal} from "antd"; -import {useState, useEffect} from "react"; +"use client"; +import { Modal } from "antd"; +import { useState, useEffect } from "react"; import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import PipelineFormComponent from "./components/pipeline-form/PipelineFormComponent"; -import {httpClient} from "@/app/infra/http/HttpClient"; -import {PipelineCardVO} from "@/app/home/pipelines/components/pipeline-card/PipelineCardVO"; +import { httpClient } from "@/app/infra/http/HttpClient"; +import { PipelineCardVO } from "@/app/home/pipelines/components/pipeline-card/PipelineCardVO"; import PipelineCardComponent from "@/app/home/pipelines/components/pipeline-card/PipelineCardComponent"; export default function PluginConfigPage() { - const [modalOpen, setModalOpen] = useState(false); - const [isEditForm, setIsEditForm] = useState(false) - const [pipelineList, setPipelineList] = useState([]) + const [modalOpen, setModalOpen] = useState(false); + const [isEditForm] = useState(false); + const [pipelineList, setPipelineList] = useState([]); - useEffect(() => { - getPipelines() - }, []) + useEffect(() => { + getPipelines(); + }, []); + function getPipelines() { + httpClient + .getPipelines() + .then((value) => { + const pipelineList = value.pipelines.map((pipeline) => { + return new PipelineCardVO({ + createTime: pipeline.created_at, + description: pipeline.description, + id: pipeline.uuid, + name: pipeline.name, + version: pipeline.for_version + }); + }); + setPipelineList(pipelineList); + }) + .catch((error) => { + // TODO toast + console.log(error); + }); + } - function getPipelines() { - httpClient.getPipelines().then(value => { - const pipelineList = value.pipelines.map(pipeline => { - return new PipelineCardVO({ - createTime: pipeline.created_at, - description: pipeline.description, - id: pipeline.uuid, - name: pipeline.name, - version: pipeline.for_version - }) - }) - setPipelineList(pipelineList) - }).catch(error => { - // TODO toast - console.log(error) - }) - } + return ( +
+ setModalOpen(false)} + onCancel={() => setModalOpen(false)} + width={700} + footer={null} + > + { + getPipelines(); + setModalOpen(false); + }} + /> + - return ( + {pipelineList.length > 0 && (
- setModalOpen(false)} - onCancel={() => setModalOpen(false)} - width={700} - footer={null} - > - { - getPipelines() - setModalOpen(false) - }} - onCancel={() => {}}/> - - - { - pipelineList.length > 0 && -
- {pipelineList.map(pipeline => { - return - })} -
- } - {setModalOpen(true)}}/> + {pipelineList.map((pipeline) => { + return ( + + ); + })}
- ); + )} + { + setModalOpen(true); + }} + /> +
+ ); } diff --git a/web_ui/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx b/web_ui/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx index 8aa8b8f0..1b5cea43 100644 --- a/web_ui/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx +++ b/web_ui/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx @@ -1,104 +1,107 @@ -"use client" +"use client"; import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; -import {PluginCardVO} from "@/app/home/plugins/plugin-installed/PluginCardVO"; -import {useEffect, useState} from "react"; +import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; +import { useEffect, useState } from "react"; import PluginCardComponent from "@/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent"; import styles from "@/app/home/plugins/plugins.module.css"; -import {Modal, Input} from "antd"; -import {GithubOutlined} from "@ant-design/icons"; -import {httpClient} from "@/app/infra/http/HttpClient"; +import { Modal, Input } from "antd"; +import { GithubOutlined } from "@ant-design/icons"; +import { httpClient } from "@/app/infra/http/HttpClient"; -export default function PluginInstalledComponent () { - const [pluginList, setPluginList] = useState([]) - const [modalOpen, setModalOpen] = useState(false) - const [githubURL, setGithubURL] = useState("") +export default function PluginInstalledComponent() { + const [pluginList, setPluginList] = useState([]); + const [modalOpen, setModalOpen] = useState(false); + const [githubURL, setGithubURL] = useState(""); + useEffect(() => { + initData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - useEffect(() => { - initData() - }, []) + function initData() { + getPluginList(); + } - function initData() { - getPluginList() - } - - function getPluginList() { - httpClient.getPlugins().then((value) => { - setPluginList(value.plugins.map(plugin => { - return new PluginCardVO({ - author: plugin.author, - description: plugin.description.zh_CN, - handlerCount: 0, - name: plugin.name, - version: plugin.version, - isInitialized: plugin.status === "initialized", - }) - })) + function getPluginList() { + httpClient.getPlugins().then((value) => { + setPluginList( + value.plugins.map((plugin) => { + return new PluginCardVO({ + author: plugin.author, + description: plugin.description.zh_CN, + handlerCount: 0, + name: plugin.name, + version: plugin.version, + isInitialized: plugin.status === "initialized" + }); }) - } + ); + }); + } - function handleModalConfirm() { - installPlugin(githubURL) - setModalOpen(false) - } + function handleModalConfirm() { + installPlugin(githubURL); + setModalOpen(false); + } - function installPlugin(url: string) { - httpClient.installPluginFromGithub(url).then(res => { - // 安装后重新拉取 - getPluginList() - }).catch(err => { - console.log("error when install plugin:", err) - }) - } - return ( -
- - - 从 GitHub 安装插件 -
- } - centered - open={modalOpen} - onOk={() => handleModalConfirm()} - onCancel={() => setModalOpen(false)} - width={500} - destroyOnClose={true} - > -
-
- 目前仅支持从 GitHub 安装 -
- setGithubURL(e.target.value)} - /> -
- - { - pluginList.map((vo, index) => { - return
- -
- }) - } - { - setModalOpen(true) - }} + function installPlugin(url: string) { + httpClient + .installPluginFromGithub(url) + .then(() => { + // 安装后重新拉取 + getPluginList(); + }) + .catch((err) => { + console.log("error when install plugin:", err); + }); + } + return ( +
+ + + 从 GitHub 安装插件 +
+ } + centered + open={modalOpen} + onOk={() => handleModalConfirm()} + onCancel={() => setModalOpen(false)} + width={500} + destroyOnClose={true} + > +
+
目前仅支持从 GitHub 安装
+ setGithubURL(e.target.value)} + />
- ) + + {pluginList.map((vo, index) => { + return ( +
+ +
+ ); + })} + { + setModalOpen(true); + }} + /> +
+ ); } diff --git a/web_ui/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx b/web_ui/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx index b8a2c784..fcae4000 100644 --- a/web_ui/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx +++ b/web_ui/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx @@ -1,68 +1,65 @@ -import styles from "./pluginCard.module.css" -import {PluginCardVO} from "@/app/home/plugins/plugin-installed/PluginCardVO"; -import {GithubOutlined, LinkOutlined, ToolOutlined} from '@ant-design/icons'; -import {Switch, Tag} from 'antd' -import {useState} from "react"; -import {httpClient} from "@/app/infra/http/HttpClient"; +import styles from "./pluginCard.module.css"; +import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; +import { GithubOutlined, LinkOutlined, ToolOutlined } from "@ant-design/icons"; +import { Switch, Tag } from "antd"; +import { useState } from "react"; +import { httpClient } from "@/app/infra/http/HttpClient"; export default function PluginCardComponent({ - cardVO + cardVO }: { - cardVO: PluginCardVO + cardVO: PluginCardVO; }) { - const [initialized, setInitialized] = useState(cardVO.isInitialized) - const [switchEnable, setSwitchEnable] = useState(true) + const [initialized, setInitialized] = useState(cardVO.isInitialized); + const [switchEnable, setSwitchEnable] = useState(true); - function handleEnable() { - setSwitchEnable(false) - httpClient.togglePlugin(cardVO.author, cardVO.name, !initialized).then(result => { - setInitialized(!initialized) - }).catch(err => { - console.log("error: ", err) - }).finally(() => { - setSwitchEnable(true) - }) - } - return ( -
- {/* header */} -
- {/* left author */} -
{cardVO.author}
- {/* right icon & version */} -
- - v{cardVO.version} -
-
- {/* content */} -
-
{cardVO.name}
-
{cardVO.description}
-
- {/* footer */} -
-
-
- - 1 -
- -
- - -
+ function handleEnable() { + setSwitchEnable(false); + httpClient + .togglePlugin(cardVO.author, cardVO.name, !initialized) + .then(() => { + setInitialized(!initialized); + }) + .catch((err) => { + console.log("error: ", err); + }) + .finally(() => { + setSwitchEnable(true); + }); + } + return ( +
+ {/* header */} +
+ {/* left author */} +
{cardVO.author}
+ {/* right icon & version */} +
+ + v{cardVO.version}
- ); +
+ {/* content */} +
+
{cardVO.name}
+
{cardVO.description}
+
+ {/* footer */} +
+
+
+ + 1 +
+ +
+ + +
+
+ ); } diff --git a/web_ui/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx b/web_ui/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx index f7ba31be..d01aeb6e 100644 --- a/web_ui/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx +++ b/web_ui/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx @@ -1,79 +1,87 @@ -"use client" +"use client"; -import {useCallback, useEffect, useState} from "react"; +import { useEffect, useState } from "react"; import styles from "@/app/home/plugins/plugins.module.css"; -import {PluginMarketCardVO} from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO"; +import { PluginMarketCardVO } from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO"; import PluginMarketCardComponent from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent"; -import {Input, Pagination} from "antd"; -import {debounce} from "lodash" -import {httpClient, spaceClient} from "@/app/infra/http/HttpClient"; +import { Input, Pagination } from "antd"; +import { spaceClient } from "@/app/infra/http/HttpClient"; -export default function PluginMarketComponent () { - const [marketPluginList, setMarketPluginList] = useState([]) - const [totalCount, setTotalCount] = useState(0) - const [nowPage, setNowPage] = useState(1) - const [searchKeyword, setSearchKeyword] = useState("") +export default function PluginMarketComponent() { + const [marketPluginList, setMarketPluginList] = useState< + PluginMarketCardVO[] + >([]); + const [totalCount, setTotalCount] = useState(0); + const [nowPage, setNowPage] = useState(1); + const [searchKeyword, setSearchKeyword] = useState(""); - useEffect(() => { - initData() - }, []) + useEffect(() => { + initData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - function initData() { - getPluginList() - } + function initData() { + getPluginList(); + } - function onInputSearchKeyword(keyword: string) { - // 这里记得加防抖,暂时没加 - setSearchKeyword(keyword) - setNowPage(1) - getPluginList(1, keyword) - } + function onInputSearchKeyword(keyword: string) { + // 这里记得加防抖,暂时没加 + setSearchKeyword(keyword); + setNowPage(1); + getPluginList(1, keyword); + } + function getPluginList( + page: number = nowPage, + keyword: string = searchKeyword + ) { + spaceClient.getMarketPlugins(page, 10, keyword).then((res) => { + setMarketPluginList( + res.plugins.map( + (marketPlugin) => + new PluginMarketCardVO({ + author: marketPlugin.author, + description: marketPlugin.description, + githubURL: marketPlugin.repository, + name: marketPlugin.name, + pluginId: String(marketPlugin.ID), + starCount: marketPlugin.stars + }) + ) + ); + setTotalCount(res.total); + console.log("market plugins:", res); + }); + } - function getPluginList(page: number = nowPage, keyword: string = searchKeyword) { - spaceClient.getMarketPlugins(page, 10, keyword).then(res => { - setMarketPluginList(res.plugins.map(marketPlugin => new PluginMarketCardVO({ - author: marketPlugin.author, - description: marketPlugin.description, - githubURL: marketPlugin.repository, - name: marketPlugin.name, - pluginId: String(marketPlugin.ID), - starCount: marketPlugin.stars, - }))) - setTotalCount(res.total) - console.log("market plugins:", res) - }) - } - - return ( -
- onInputSearchKeyword(e.target.value)} - /> -
- { - marketPluginList.map((vo, index) => { - return
- -
- }) - } + return ( +
+ onInputSearchKeyword(e.target.value)} + /> +
+ {marketPluginList.map((vo, index) => { + return ( +
+
- { - setNowPage(pageNumber) - getPluginList(pageNumber) - }} - /> -
- - ) + ); + })} +
+ { + setNowPage(pageNumber); + getPluginList(pageNumber); + }} + /> +
+ ); } diff --git a/web_ui/src/app/infra/api/api-types/index.ts b/web_ui/src/app/infra/api/api-types/index.ts index 5059529d..f3113468 100644 --- a/web_ui/src/app/infra/api/api-types/index.ts +++ b/web_ui/src/app/infra/api/api-types/index.ts @@ -1,204 +1,217 @@ export interface ApiResponse { - code: number; - data: T; - msg: string; + code: number; + data: T; + msg: string; } export interface I18nText { - en_US: string; - zh_CN: string; + en_US: string; + zh_CN: string; } export interface AsyncTaskCreatedResp { - task_id: number; + task_id: number; } export interface ApiRespProviderRequesters { - requesters: Requester[]; + requesters: Requester[]; } export interface ApiRespProviderRequester { - requester: Requester; + requester: Requester; } export interface Requester { - name: string; - label: I18nText; - description: I18nText; - icon?: string; - spec: object; + name: string; + label: I18nText; + description: I18nText; + icon?: string; + spec: object; } export interface ApiRespProviderLLMModels { - models: LLMModel[]; + models: LLMModel[]; } export interface ApiRespProviderLLMModel { - model: LLMModel; + model: LLMModel; } export interface LLMModel { - name: string; - description: string; - uuid: string; - requester: string; - requester_config: object; - extra_args: object; - api_keys: string[]; - abilities: string[]; - // created_at: string; - // updated_at: string; + name: string; + description: string; + uuid: string; + requester: string; + requester_config: { + base_url: string; + timeout: number; + }; + extra_args: object; + api_keys: string[]; + abilities: string[]; + // created_at: string; + // updated_at: string; } export interface ApiRespPipelines { - pipelines: Pipeline[]; + pipelines: Pipeline[]; } export interface ApiRespPipeline { - pipeline: Pipeline; + pipeline: Pipeline; } export interface Pipeline { - uuid: string; - name: string; - description: string; - for_version: string; - config: object; - stages: string[]; - created_at: string; - updated_at: string; + uuid: string; + name: string; + description: string; + for_version: string; + config: object; + stages: string[]; + created_at: string; + updated_at: string; } export interface ApiRespPlatformAdapters { - adapters: Adapter[]; + adapters: Adapter[]; } export interface ApiRespPlatformAdapter { - adapter: Adapter; + adapter: Adapter; } export interface Adapter { - name: string; - label: I18nText; - description: I18nText; - icon?: string; - spec: object; + name: string; + label: I18nText; + description: I18nText; + icon?: string; + spec: { + config: AdapterSpecConfig[]; + }; +} + +export interface AdapterSpecConfig { + default: string | number | boolean | Array; + label: I18nText; + name: string; + required: boolean; + type: string; } export interface ApiRespPlatformBots { - bots: Bot[]; + bots: Bot[]; } export interface ApiRespPlatformBot { - bot: Bot; + bot: Bot; } export interface Bot { - uuid?: string; - name: string; - description: string; - enable?: boolean; - adapter: string; - adapter_config: object; - use_pipeline_name?: string; - use_pipeline_uuid?: string; - created_at?: string; - updated_at?: string; + uuid?: string; + name: string; + description: string; + enable?: boolean; + adapter: string; + adapter_config: object; + use_pipeline_name?: string; + use_pipeline_uuid?: string; + created_at?: string; + updated_at?: string; } // plugins export interface ApiRespPlugins { - plugins: Plugin[]; + plugins: Plugin[]; } export interface ApiRespPlugin { - plugin: Plugin; + plugin: Plugin; } export interface Plugin { - author: string; - name: string; - description: I18nText; - label: I18nText; - version: string; - enabled: boolean; - priority: number; - status: string; - tools: object[]; - event_handlers: object; - main_file: string; - pkg_path: string; - repository: string; - config_schema: object; + author: string; + name: string; + description: I18nText; + label: I18nText; + version: string; + enabled: boolean; + priority: number; + status: string; + tools: object[]; + event_handlers: object; + main_file: string; + pkg_path: string; + repository: string; + config_schema: object; } export interface ApiRespPluginConfig { - config: object; + config: object; } export interface PluginReorderElement { - author: string; - name: string; - priority: number; + author: string; + name: string; + priority: number; } // system export interface ApiRespSystemInfo { - debug: boolean; - version: string; + debug: boolean; + version: string; } export interface ApiRespAsyncTasks { - tasks: AsyncTask[]; + tasks: AsyncTask[]; } export interface ApiRespAsyncTask { - task: AsyncTask; + task: AsyncTask; } export interface AsyncTaskRuntimeInfo { - done: boolean; - exception?: string; - result?: object; - state: string; + done: boolean; + exception?: string; + result?: object; + state: string; } export interface AsyncTaskTaskContext { - current_action: string; - log: string; + current_action: string; + log: string; } export interface AsyncTask { - id: number; - kind: string; - name: string; - task_type: string; // system or user - runtime: AsyncTaskRuntimeInfo; - task_context: AsyncTaskTaskContext; + id: number; + kind: string; + name: string; + task_type: string; // system or user + runtime: AsyncTaskRuntimeInfo; + task_context: AsyncTaskTaskContext; } export interface ApiRespUserToken { - token: string; + token: string; } export interface MarketPlugin { - ID: number - CreatedAt: string // ISO 8601 格式日期 - UpdatedAt: string - DeletedAt: string | null - name: string - author: string - description: string - repository: string // GitHub 仓库路径 - artifacts_path: string - stars: number - downloads: number - status: "initialized" | "mounted" // 可根据实际状态值扩展联合类型 - synced_at: string - pushed_at: string // 最后一次代码推送时间 + ID: number; + CreatedAt: string; // ISO 8601 格式日期 + UpdatedAt: string; + DeletedAt: string | null; + name: string; + author: string; + description: string; + repository: string; // GitHub 仓库路径 + artifacts_path: string; + stars: number; + downloads: number; + status: "initialized" | "mounted"; // 可根据实际状态值扩展联合类型 + synced_at: string; + pushed_at: string; // 最后一次代码推送时间 } export interface MarketPluginResponse { - plugins: MarketPlugin[] - total: number + plugins: MarketPlugin[]; + total: number; } diff --git a/web_ui/src/app/infra/api/api-types/pipelines/GetMetaDataResponse.ts b/web_ui/src/app/infra/api/api-types/pipelines/GetMetaDataResponse.ts index 41a1b8dc..026d1a07 100644 --- a/web_ui/src/app/infra/api/api-types/pipelines/GetMetaDataResponse.ts +++ b/web_ui/src/app/infra/api/api-types/pipelines/GetMetaDataResponse.ts @@ -1,47 +1,46 @@ export interface GetMetaDataResponse { - configs: Config[] + configs: Config[]; } - interface Label { - en_US: string; - zh_CN: string; + en_US: string; + zh_CN: string; } interface Option { - label: Label; - name: string; + label: Label; + name: string; } interface ConfigItem { - default?: boolean | Array | number | string; - description?: Label; - items?: { - type?: string; - properties?: { - [key: string]: { - type: string; - default?: any; - }; - }; + default?: boolean | Array | number | string; + description?: Label; + items?: { + type?: string; + properties?: { + [key: string]: { + type: string; + default?: object | string; + }; }; - label: Label; - name: string; - options?: Option[]; - required: boolean; - scope?: string; - type: string; + }; + label: Label; + name: string; + options?: Option[]; + required: boolean; + scope?: string; + type: string; } interface Stage { - config: ConfigItem[]; - description?: Label; - label: Label; - name: string; + config: ConfigItem[]; + description?: Label; + label: Label; + name: string; } interface Config { - label: Label; - name: string; - stages: Stage[]; + label: Label; + name: string; + stages: Stage[]; } diff --git a/web_ui/src/app/login/page.tsx b/web_ui/src/app/login/page.tsx index c40274ee..f6ce8bea 100644 --- a/web_ui/src/app/login/page.tsx +++ b/web_ui/src/app/login/page.tsx @@ -1,194 +1,204 @@ -'use client'; -import { Button, Input, Form, Checkbox, Divider } from 'antd'; -import {GoogleOutlined, AppleOutlined, LockOutlined, UserOutlined, QqCircleFilled, QqOutlined} from '@ant-design/icons'; -import styles from './login.module.css'; -import {useEffect, useState} from 'react'; - - -import {httpClient} from "@/app/infra/http/HttpClient"; -import '@ant-design/v5-patch-for-react-19'; -import {useRouter} from "next/navigation"; +"use client"; +import { Button, Input, Form, Checkbox, Divider } from "antd"; +import { + GoogleOutlined, + LockOutlined, + UserOutlined, + QqOutlined +} from "@ant-design/icons"; +import styles from "./login.module.css"; +import { useEffect, useState } from "react"; +import { httpClient } from "@/app/infra/http/HttpClient"; +import "@ant-design/v5-patch-for-react-19"; +import { useRouter } from "next/navigation"; export default function Home() { - const router = useRouter(); - const [form] = Form.useForm(); - const [rememberMe, setRememberMe] = useState(false); - const [isRegisterMode, setIsRegisterMode] = useState(false); - const [isInitialized, setIsInitialized] = useState(false) + const router = useRouter(); + const [form] = Form.useForm(); + const [rememberMe, setRememberMe] = useState(false); + const [isRegisterMode, setIsRegisterMode] = useState(false); + const [isInitialized, setIsInitialized] = useState(false); - useEffect(() => { - getIsInitialized() - }, []) + useEffect(() => { + getIsInitialized(); + }, []); + // 检查是否为首次启动项目,只为首次启动的用户提供注册资格 + function getIsInitialized() { + httpClient + .checkIfInited() + .then((res) => { + setIsInitialized(res.initialized); + }) + .catch((err) => { + console.log("error at getIsInitialized: ", err); + }); + } - // 检查是否为首次启动项目,只为首次启动的用户提供注册资格 - function getIsInitialized() { - httpClient.checkIfInited().then(res => { - setIsInitialized(res.initialized) - }).catch(err => { - console.log("error at getIsInitialized: ", err) - }) + function handleFormSubmit(formField: LoginField) { + if (isRegisterMode) { + handleRegister(formField.email, formField.password); + } else { + handleLogin(formField.email, formField.password); } + } - function handleFormSubmit(formField: LoginField) { - if (isRegisterMode) { - handleRegister(formField.email, formField.password); - } else { - handleLogin(formField.email, formField.password) - } - } + function handleRegister(username: string, password: string) { + httpClient + .initUser(username, password) + .then((res) => { + console.log("init user success: ", res); + }) + .catch((err) => { + console.log("init user error: ", err); + }); + } - function handleRegister(username: string, password: string) { - httpClient.initUser(username, password).then(res => { - console.log("init user success: ", res) - }).catch(err => { - console.log("init user error: ", err) - }) - } + function handleLogin(username: string, password: string) { + httpClient + .authUser(username, password) + .then((res) => { + localStorage.setItem("token", res.token); + console.log("login success: ", res); + router.push("/home"); + }) + .catch((err) => { + console.log("login error: ", err); + }); + } - function handleLogin(username: string, password: string) { - httpClient.authUser(username, password).then(res => { - localStorage.setItem("token", res.token) - console.log("login success: ", res) - router.push("/home") - }).catch(err => { - console.log("login error: ", err) - }) - } + return ( + // 使用 Ant Design 的组件库,使用 antd 的样式 + // 仅前端样式,无交互功能。 +
+ {/* login 类是整个 container,使用 flex 左右布局 */} +
+ {/* left 为注册的表单,需要填入的内容有:邮箱,密码 */} +
+
+ {isRegisterMode && ( +

注册 LangBot 账号

+ )} + {!isRegisterMode && ( +

欢迎回到 LangBot

+ )} +
{ + handleFormSubmit(values); + }} + > + + } + /> + - return ( - // 使用 Ant Design 的组件库,使用 antd 的样式 - // 仅前端样式,无交互功能。 + + } + /> + -
- {/* login 类是整个 container,使用 flex 左右布局 */} -
- {/* left 为注册的表单,需要填入的内容有:邮箱,密码 */} -
-
- { - isRegisterMode && -

注册 LangBot 账号

- } - { - !isRegisterMode && -

欢迎回到 LangBot

- } - { - handleFormSubmit(values) - }} - > - - } - /> - +
+ setRememberMe(e.target.checked)} + > + 30天内自动登录 + + + + 忘记密码? + + {!isRegisterMode && ( + { + setIsRegisterMode(true); + event.preventDefault(); + }} + > + 去注册? + + )} + {isRegisterMode && ( + { + setIsRegisterMode(false); + event.preventDefault(); + }} + > + 去登录 + + )} + +
- - } - /> - + -
- setRememberMe(e.target.checked)} - > - 30天内自动登录 - - - 忘记密码? - { - !isRegisterMode && - { - setIsRegisterMode(true) - event.preventDefault() - }} - >去注册? - } - { - isRegisterMode && - { - setIsRegisterMode(false) - event.preventDefault() - }} - >去登录 - } - -
+ - - - - - - -
- -
-
-
- -
- -
-
-
+
+ +
+
+
+ +
+ +
- ); +
+
+ ); } interface LoginField { - email: string; - password: string; -} \ No newline at end of file + email: string; + password: string; +}