+ {/* LangBot、ICON区域 */}
+
+ {/* icon */}
+
+ L
+
+
+ Langbot
+
+
+ {/* 菜单列表,后期可升级成配置驱动 */}
+
+ {
+ 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
new file mode 100644
index 00000000..448d0ab8
--- /dev/null
+++ b/web_ui/src/app/home/components/home-sidebar/HomeSidebarChild.tsx
@@ -0,0 +1,41 @@
+import styles from "./HomeSidebar.module.css";
+
+export interface ISidebarChildVO {
+ id: string;
+ icon: string;
+ name: string;
+ route: string;
+}
+
+export class SidebarChildVO {
+ 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;
+ }
+}
+
+
+
+export function SidebarChild({
+ icon,
+ name,
+ isSelected,
+}: {
+ icon: string;
+ name: string;
+ isSelected: boolean;
+}) {
+ return (
+
+
+ );
+}
\ No newline at end of file
diff --git a/web_ui/src/app/home/components/home-sidebar/sidbarConfigList.ts b/web_ui/src/app/home/components/home-sidebar/sidbarConfigList.ts
new file mode 100644
index 00000000..3c96e4f6
--- /dev/null
+++ b/web_ui/src/app/home/components/home-sidebar/sidbarConfigList.ts
@@ -0,0 +1,28 @@
+import {SidebarChildVO} from "@/app/home/components/home-sidebar/HomeSidebarChild";
+
+export const sidebarConfigList = [
+ new SidebarChildVO({
+ id: "llm-config",
+ name: "大模型配置",
+ icon: "",
+ route: "/home/llm-config",
+ }),
+ new SidebarChildVO({
+ id: "platform-config",
+ name: "机器人配置",
+ icon: "",
+ route: "/home/bot-config",
+ }),
+ new SidebarChildVO({
+ id: "plugin-config",
+ name: "插件配置",
+ icon: "",
+ route: "/home/plugin-config",
+ }),
+ new SidebarChildVO({
+ id: "pipeline-config",
+ name: "流水线配置",
+ icon: "",
+ route: "/home/pipeline-config",
+ })
+]
diff --git a/web_ui/src/app/home/components/home-titlebar/HomeTitleBar.tsx b/web_ui/src/app/home/components/home-titlebar/HomeTitleBar.tsx
new file mode 100644
index 00000000..7011055d
--- /dev/null
+++ b/web_ui/src/app/home/components/home-titlebar/HomeTitleBar.tsx
@@ -0,0 +1,16 @@
+import styles from "./HomeTittleBar.module.css"
+
+
+export default function HomeTitleBar({
+ title,
+}: {
+ title: string
+}) {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/web_ui/src/app/home/components/home-titlebar/HomeTittleBar.module.css b/web_ui/src/app/home/components/home-titlebar/HomeTittleBar.module.css
new file mode 100644
index 00000000..844b045e
--- /dev/null
+++ b/web_ui/src/app/home/components/home-titlebar/HomeTittleBar.module.css
@@ -0,0 +1,15 @@
+.titleBarContainer {
+ width: 100%;
+ height: 70px;
+ background-color: #FFF;
+ font-size: 20px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.titleText {
+ margin-left: 10px;
+ font-size: 20px;
+ font-weight: bold;
+}
\ No newline at end of file
diff --git a/web_ui/src/app/home/layout.module.css b/web_ui/src/app/home/layout.module.css
new file mode 100644
index 00000000..bcbdc800
--- /dev/null
+++ b/web_ui/src/app/home/layout.module.css
@@ -0,0 +1,18 @@
+.homeLayoutContainer {
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ flex-direction: row;
+}
+
+.main {
+ width: 100%;
+ height: 100%;
+ background-color: #FAFBFB;
+}
+
+.mainContent {
+ width: calc(100% - 40px);
+ height: calc(100% - 110px);
+ margin: 20px;
+}
\ No newline at end of file
diff --git a/web_ui/src/app/home/layout.tsx b/web_ui/src/app/home/layout.tsx
new file mode 100644
index 00000000..57f21bac
--- /dev/null
+++ b/web_ui/src/app/home/layout.tsx
@@ -0,0 +1,36 @@
+"use client"
+
+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';
+
+export default function HomeLayout({
+ children
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ const router = useRouter();
+ const [title, setTitle] = useState
("")
+ const onSelectedChange = (child: SidebarChildVO) => {
+ setTitle(child.name)
+ }
+
+ return (
+
+
+
+
+ {/* 主页面 */}
+
+ {children}
+
+
+
+ )
+}
diff --git a/web_ui/src/app/home/llm-config/ICreateLLMField.ts b/web_ui/src/app/home/llm-config/ICreateLLMField.ts
new file mode 100644
index 00000000..2c484f97
--- /dev/null
+++ b/web_ui/src/app/home/llm-config/ICreateLLMField.ts
@@ -0,0 +1,8 @@
+export interface ICreateLLMField {
+ name: string;
+ model_provider: string;
+ url: string;
+ api_key: string;
+ abilities: string[];
+ extra_args: string[];
+}
\ No newline at end of file
diff --git a/web_ui/src/app/home/llm-config/LLMConfig.module.css b/web_ui/src/app/home/llm-config/LLMConfig.module.css
new file mode 100644
index 00000000..7dfb3115
--- /dev/null
+++ b/web_ui/src/app/home/llm-config/LLMConfig.module.css
@@ -0,0 +1,90 @@
+.configPageContainer {
+ width: 100%;
+ height: 100%;
+}
+
+.modalContainer {
+ width: 100%;
+ /*height: calc(100vh - 200px);*/
+ margin-top: 20px;
+}
+
+.modelListContainer {
+ align-self: flex-start;
+ justify-self: flex-start;
+ width: calc(100% - 60px);
+ margin: auto;
+ display: grid;
+ grid-template-rows: repeat(auto-fill, minmax(220px, 1fr));
+ grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
+ gap: 15px;
+ justify-items: center;
+ align-items: center;
+}
+
+.emptyContainer {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.cardContainer {
+ width: 360px;
+ height: 200px;
+ background-color: #FFF;
+ border-radius: 9px;
+ box-shadow: rgba(0, 0, 0, 0.4) 0 1px 1px -1px;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: space-evenly;
+}
+
+.iconBasicInfoContainer {
+ width: 300px;
+ height: 100px;
+ margin-left: 20px;
+ display: flex;
+ flex-direction: row;
+}
+
+.icon {
+ width: 90px;
+ height: 90px;
+ border-radius: 5px;
+ font-size: 40px;
+ line-height: 90px;
+ text-align: center;
+ color: #ffffff;
+ background: rgba(96, 149, 209, 0.31);
+ border: 1px solid rgba(96, 149, 209, 0.31);
+}
+
+.basicInfoContainer {
+ width: 200px;
+ height: 90px;
+ padding-left: 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: space-between;
+}
+
+.basicInfoText {
+
+}
+
+.bigText {
+ font-size: 20px;
+}
+
+.urlAndUpdateText {
+ margin-left: 20px;
+}
+
+
+
+
diff --git a/web_ui/src/app/home/llm-config/component/llm-card/LLMCard.tsx b/web_ui/src/app/home/llm-config/component/llm-card/LLMCard.tsx
new file mode 100644
index 00000000..a8ab5f4b
--- /dev/null
+++ b/web_ui/src/app/home/llm-config/component/llm-card/LLMCard.tsx
@@ -0,0 +1,39 @@
+import styles from "../../LLMConfig.module.css"
+import {LLMCardVO} from "@/app/home/llm-config/component/llm-card/LLMCardVO";
+
+export default function LLMCard({
+ cardVO
+}: {
+ cardVO: LLMCardVO
+}) {
+ return (
+
+ {/* icon和基本信息 */}
+
+ {/* icon */}
+
+ ICO
+
+ {/* bot基本信息 */}
+
+
+ {cardVO.name}
+
+
+ 使用模型:{cardVO.model}
+
+
+ 厂商:{cardVO.company}
+
+
+
+ {/* URL和创建时间 */}
+
+ URL:{cardVO.URL}
+
+
+ 更新时间:{cardVO.updateTime}
+
+
+ );
+}
\ No newline at end of file
diff --git a/web_ui/src/app/home/llm-config/component/llm-card/LLMCardVO.ts b/web_ui/src/app/home/llm-config/component/llm-card/LLMCardVO.ts
new file mode 100644
index 00000000..0a9ba785
--- /dev/null
+++ b/web_ui/src/app/home/llm-config/component/llm-card/LLMCardVO.ts
@@ -0,0 +1,27 @@
+export interface ILLMCardVO {
+ id: string;
+ name: string;
+ model: string;
+ company: string;
+ URL: string;
+ updateTime: string;
+}
+
+export class LLMCardVO implements ILLMCardVO {
+ id: string;
+ name: string;
+ model: string;
+ company: string;
+ URL: string;
+ updateTime: string;
+
+ constructor(props: ILLMCardVO) {
+ this.id = props.id;
+ this.name = props.name;
+ this.model = props.model;
+ this.company = props.company;
+ this.URL = props.URL;
+ this.updateTime = props.updateTime;
+ }
+
+}
\ No newline at end of file
diff --git a/web_ui/src/app/home/llm-config/component/llm-form/LLMForm.tsx b/web_ui/src/app/home/llm-config/component/llm-form/LLMForm.tsx
new file mode 100644
index 00000000..433bf44a
--- /dev/null
+++ b/web_ui/src/app/home/llm-config/component/llm-form/LLMForm.tsx
@@ -0,0 +1,177 @@
+import styles from "@/app/home/llm-config/LLMConfig.module.css";
+import {Button, Form, Input, Select, SelectProps, Space} from "antd";
+import {ICreateLLMField} from "@/app/home/llm-config/ICreateLLMField";
+import {useEffect, useState} from "react";
+
+export default function LLMForm({
+ editMode,
+ initLLMId,
+ onFormSubmit,
+ onFormCancel,
+}: {
+ editMode: boolean;
+ initLLMId?: string;
+ onFormSubmit: (value: ICreateLLMField) => void;
+ onFormCancel: (value: ICreateLLMField) => void;
+}) {
+ const [form] = Form.useForm();
+ const extraOptions: SelectProps['options'] = []
+ const [initValue, setInitValue] = useState()
+ const abilityOptions: SelectProps['options'] = [
+ {
+ label: '函数调用',
+ value: 'func_call',
+ },
+ {
+ label: '图像识别',
+ value: 'vision',
+ },
+ ];
+ useEffect(() => {
+ if (editMode && initLLMId) {
+ getLLMConfig(initLLMId).then(val => {
+ form.setFieldsValue(val)
+ })
+ } else {
+ form.resetFields()
+ }
+ })
+
+ async function getLLMConfig(id: string): Promise {
+ return {
+ name: id,
+ model_provider: "OpenAI",
+ url: "www.aaa.com",
+ api_key: "",
+ abilities: [],
+ extra_args: [],
+ }
+ }
+
+ function handleFormSubmit(value: ICreateLLMField) {
+ if (editMode) {
+ onSaveEdit(value)
+ } else {
+ onCreateLLM(value)
+ }
+ onFormSubmit(value)
+ form.resetFields()
+ }
+
+ function onSaveEdit(value: ICreateLLMField) {
+ console.log("edit save", value)
+ }
+
+ function onCreateLLM(value: ICreateLLMField) {
+ console.log("create llm", value)
+ }
+
+ function handleAbilitiesChange() {
+
+ }
+ return (
+
+
+ label={"模型名称"}
+ name={"name"}
+ rules={[{required: true, message: "该项为必填项哦~"}]}
+ >
+
+
+
+
+ label={"模型供应商"}
+ name={"model_provider"}
+ rules={[{required: true, message: "该项为必填项哦~"}]}
+ >
+
+
+
+ label={"请求URL"}
+ name={"url"}
+ rules={[{required: true, message: "该项为必填项哦~"}]}
+ >
+
+
+
+
+ label={"开启能力"}
+ name={"abilities"}
+ >
+
+
+
+
+ label={"其他参数"}
+ name={"extra_args"}
+ >
+
+
+
+
+
+ {
+ !editMode &&
+
+ }
+ {
+ editMode &&
+
+ }
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/web_ui/src/app/home/llm-config/page.tsx b/web_ui/src/app/home/llm-config/page.tsx
new file mode 100644
index 00000000..56d8b25a
--- /dev/null
+++ b/web_ui/src/app/home/llm-config/page.tsx
@@ -0,0 +1,126 @@
+"use client"
+
+import {useState} from "react";
+import {LLMCardVO} from "@/app/home/llm-config/component/llm-card/LLMCardVO";
+import styles from "./LLMConfig.module.css"
+import EmptyAndCreateComponent from "@/app/home/components/empty-and-create-component/EmptyAndCreateComponent";
+import {Modal} from "antd";
+import LLMCard from "@/app/home/llm-config/component/llm-card/LLMCard";
+import LLMForm from "@/app/home/llm-config/component/llm-form/LLMForm";
+import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
+
+export default function LLMConfigPage() {
+ const [cardList, setCardList] = useState([
+ new LLMCardVO({
+ id: "1",
+ name: "测试模型",
+ model: "GPT-4o",
+ URL: "www.openai.com",
+ company: "OpenAI",
+ updateTime: "2025.1.2"
+ }),
+ new LLMCardVO({
+ id: "2",
+ name: "测试模型",
+ model: "GPT-4o",
+ URL: "www.openai.com",
+ company: "OpenAI",
+ updateTime: "2025.1.2"
+ }),
+ new LLMCardVO({
+ id: "3",
+ name: "测试模型",
+ model: "GPT-4o",
+ URL: "www.openai.com",
+ company: "OpenAI",
+ updateTime: "2025.1.2"
+ }),
+ new LLMCardVO({
+ id: "4",
+ name: "测试模型",
+ model: "GPT-4o",
+ URL: "www.openai.com",
+ company: "OpenAI",
+ updateTime: "2025.1.2"
+ }),
+ new LLMCardVO({
+ id: "5",
+ name: "测试模型",
+ model: "GPT-4o",
+ URL: "www.openai.com",
+ company: "OpenAI",
+ updateTime: "2025.1.2"
+ }),
+ ])
+ const [modalOpen, setModalOpen] = useState(false);
+ const [isEditForm, setIsEditForm] = useState(false)
+ const [nowSelectedLLM, setNowSelectedLLM] = useState(null)
+
+ function selectLLM(cardVO: LLMCardVO) {
+ setIsEditForm(true)
+ setNowSelectedLLM(cardVO)
+ console.log("set now vo", cardVO)
+ setModalOpen(true)
+ }
+ function handleCreateModelClick() {
+ setIsEditForm(false)
+ setNowSelectedLLM(null)
+ setModalOpen(true);
+ }
+
+ return (
+
+
setModalOpen(false)}
+ onCancel={() => setModalOpen(false)}
+ width={700}
+ footer={null}
+ >
+ {
+ setModalOpen(false);
+ }}
+ onFormCancel={() => {
+ setModalOpen(false);
+ }}
+ />
+
+ {
+ cardList.length > 0 &&
+
+ {cardList.map(cardVO => {
+ return
{selectLLM(cardVO)}}>
+
+
+ })}
+
+
+ }
+
+ {
+ cardList.length === 0 &&
+
+ {
+ handleCreateModelClick()
+ }}
+ />
+
+ }
+
+ )
+}
diff --git a/web_ui/src/app/home/mock-api/index.ts b/web_ui/src/app/home/mock-api/index.ts
new file mode 100644
index 00000000..3c4e40c9
--- /dev/null
+++ b/web_ui/src/app/home/mock-api/index.ts
@@ -0,0 +1,1374 @@
+import {GetMetaDataResponse} from "@/app/infra/api/api-types/pipelines/GetMetaDataResponse";
+import {ApiResponse} from "@/app/infra/api/api-types";
+
+export async function fetchAdapterList() {
+ return [
+ {
+ "description": {
+ "en_US": "Discord Adapter",
+ "zh_CN": "Discord 适配器"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "Discord",
+ "zh_CN": "Discord"
+ },
+ "name": "discord",
+ "spec": {
+ "config": [
+ {
+ "default": "",
+ "label": {
+ "en_US": "Client ID",
+ "zh_CN": "客户端ID"
+ },
+ "name": "client_id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Token",
+ "zh_CN": "令牌"
+ },
+ "name": "token",
+ "required": true,
+ "type": "string"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "QQ Official API (Webhook)",
+ "zh_CN": "QQ 官方 API (Webhook)"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "QQ Official API",
+ "zh_CN": "QQ 官方 API"
+ },
+ "name": "qqofficial",
+ "spec": {
+ "config": [
+ {
+ "default": "",
+ "label": {
+ "en_US": "App ID",
+ "zh_CN": "应用ID"
+ },
+ "name": "appid",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Secret",
+ "zh_CN": "密钥"
+ },
+ "name": "secret",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": 2284,
+ "label": {
+ "en_US": "Port",
+ "zh_CN": "监听端口"
+ },
+ "name": "port",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Token",
+ "zh_CN": "令牌"
+ },
+ "name": "token",
+ "required": true,
+ "type": "string"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "Telegram Adapter",
+ "zh_CN": "电报适配器"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "Telegram",
+ "zh_CN": "电报"
+ },
+ "name": "telegram",
+ "spec": {
+ "config": [
+ {
+ "default": "",
+ "label": {
+ "en_US": "Token",
+ "zh_CN": "令牌"
+ },
+ "name": "token",
+ "required": true,
+ "type": "string"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "WeCom Adapter",
+ "zh_CN": "企业微信适配器"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "WeCom",
+ "zh_CN": "企业微信"
+ },
+ "name": "wecom",
+ "spec": {
+ "config": [
+ {
+ "default": "0.0.0.0",
+ "label": {
+ "en_US": "Host",
+ "zh_CN": "监听主机"
+ },
+ "name": "host",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": 2290,
+ "label": {
+ "en_US": "Port",
+ "zh_CN": "监听端口"
+ },
+ "name": "port",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Corpid",
+ "zh_CN": "企业ID"
+ },
+ "name": "corpid",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Secret",
+ "zh_CN": "密钥"
+ },
+ "name": "secret",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Token",
+ "zh_CN": "令牌"
+ },
+ "name": "token",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "EncodingAESKey",
+ "zh_CN": "消息加解密密钥"
+ },
+ "name": "EncodingAESKey",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Contacts Secret",
+ "zh_CN": "通讯录密钥"
+ },
+ "name": "contacts_secret",
+ "required": true,
+ "type": "string"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "GeWeChat Adapter",
+ "zh_CN": "GeWeChat 适配器"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "GeWeChat",
+ "zh_CN": "GeWeChat(个人微信)"
+ },
+ "name": "gewechat",
+ "spec": {
+ "config": [
+ {
+ "default": "",
+ "label": {
+ "en_US": "GeWeChat URL",
+ "zh_CN": "GeWeChat URL"
+ },
+ "name": "gewechat_url",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "GeWeChat file download URL",
+ "zh_CN": "GeWeChat 文件下载URL"
+ },
+ "name": "gewechat_file_url",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": 2286,
+ "label": {
+ "en_US": "Port",
+ "zh_CN": "端口"
+ },
+ "name": "port",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Callback URL",
+ "zh_CN": "回调URL"
+ },
+ "name": "callback_url",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "App ID",
+ "zh_CN": "应用ID"
+ },
+ "name": "app_id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Token",
+ "zh_CN": "令牌"
+ },
+ "name": "token",
+ "required": true,
+ "type": "string"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "OneBot v11 Adapter",
+ "zh_CN": "OneBot v11 适配器"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "OneBot v11 Adapter",
+ "zh_CN": "OneBot v11 适配器"
+ },
+ "name": "aiocqhttp",
+ "spec": {
+ "config": [
+ {
+ "default": "0.0.0.0",
+ "label": {
+ "en_US": "Host",
+ "zh_CN": "主机"
+ },
+ "name": "host",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": 2280,
+ "label": {
+ "en_US": "Port",
+ "zh_CN": "端口"
+ },
+ "name": "port",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Access Token",
+ "zh_CN": "访问令牌"
+ },
+ "name": "access-token",
+ "required": false,
+ "type": "string"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "Official Account Adapter",
+ "zh_CN": "微信公众号适配器"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "Official Account",
+ "zh_CN": "微信公众号"
+ },
+ "name": "officialaccount",
+ "spec": {
+ "config": [
+ {
+ "default": "",
+ "label": {
+ "en_US": "Token",
+ "zh_CN": "令牌"
+ },
+ "name": "token",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "EncodingAESKey",
+ "zh_CN": "消息加解密密钥"
+ },
+ "name": "EncodingAESKey",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "App ID",
+ "zh_CN": "应用ID"
+ },
+ "name": "AppID",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "App Secret",
+ "zh_CN": "应用密钥"
+ },
+ "name": "AppSecret",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "0.0.0.0",
+ "label": {
+ "en_US": "Host",
+ "zh_CN": "监听主机"
+ },
+ "name": "host",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": 2287,
+ "label": {
+ "en_US": "Port",
+ "zh_CN": "监听端口"
+ },
+ "name": "port",
+ "required": true,
+ "type": "integer"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "Nakuru Adapter",
+ "zh_CN": "Nakuru 适配器(go-cqhttp)"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "Nakuru",
+ "zh_CN": "Nakuru"
+ },
+ "name": "nakuru",
+ "spec": {
+ "config": [
+ {
+ "default": "127.0.0.1",
+ "label": {
+ "en_US": "Host",
+ "zh_CN": "主机"
+ },
+ "name": "host",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": 5700,
+ "label": {
+ "en_US": "HTTP Port",
+ "zh_CN": "HTTP端口"
+ },
+ "name": "http_port",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": 8080,
+ "label": {
+ "en_US": "WebSocket Port",
+ "zh_CN": "WebSocket端口"
+ },
+ "name": "ws_port",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Token",
+ "zh_CN": "令牌"
+ },
+ "name": "token",
+ "required": true,
+ "type": "string"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "QQ Official API (WebSocket)",
+ "zh_CN": "QQ 官方 API (WebSocket)"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "QQBotPy",
+ "zh_CN": "QQBotPy"
+ },
+ "name": "qq-botpy",
+ "spec": {
+ "config": [
+ {
+ "default": "",
+ "label": {
+ "en_US": "App ID",
+ "zh_CN": "应用ID"
+ },
+ "name": "appid",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Secret",
+ "zh_CN": "密钥"
+ },
+ "name": "secret",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": [],
+ "label": {
+ "en_US": "Intents",
+ "zh_CN": "权限"
+ },
+ "name": "intents",
+ "required": true,
+ "type": "array[string]"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "DingTalk Adapter",
+ "zh_CN": "钉钉适配器"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "DingTalk",
+ "zh_CN": "钉钉"
+ },
+ "name": "dingtalk",
+ "spec": {
+ "config": [
+ {
+ "default": "",
+ "label": {
+ "en_US": "Client ID",
+ "zh_CN": "客户端ID"
+ },
+ "name": "client_id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Client Secret",
+ "zh_CN": "客户端密钥"
+ },
+ "name": "client_secret",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Robot Code",
+ "zh_CN": "机器人代码"
+ },
+ "name": "robot_code",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Robot Name",
+ "zh_CN": "机器人名称"
+ },
+ "name": "robot_name",
+ "required": true,
+ "type": "string"
+ }
+ ]
+ }
+ },
+ {
+ "description": {
+ "en_US": "Lark Adapter",
+ "zh_CN": "飞书适配器"
+ },
+ "icon": "",
+ "label": {
+ "en_US": "Lark",
+ "zh_CN": "飞书"
+ },
+ "name": "lark",
+ "spec": {
+ "config": [
+ {
+ "default": "",
+ "label": {
+ "en_US": "App ID",
+ "zh_CN": "应用ID"
+ },
+ "name": "app_id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "App Secret",
+ "zh_CN": "应用密钥"
+ },
+ "name": "app_secret",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Bot Name",
+ "zh_CN": "机器人名称"
+ },
+ "name": "bot_name",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": false,
+ "label": {
+ "en_US": "Enable Webhook Mode",
+ "zh_CN": "启用Webhook模式"
+ },
+ "name": "enable-webhook",
+ "required": true,
+ "type": "boolean"
+ },
+ {
+ "default": 2285,
+ "label": {
+ "en_US": "Webhook Port",
+ "zh_CN": "Webhook端口"
+ },
+ "name": "port",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Encrypt Key",
+ "zh_CN": "加密密钥"
+ },
+ "name": "encrypt-key",
+ "required": true,
+ "type": "string"
+ }
+ ]
+ }
+ }
+ ]
+}
+
+export async function fetchPipelineMetaData(): Promise> {
+ return {
+ "code": 0,
+ "data": {
+ "configs": [
+ {
+ "label": {
+ "en_US": "Trigger",
+ "zh_CN": "触发条件"
+ },
+ "name": "trigger",
+ "stages": [
+ {
+ "config": [
+ {
+ "default": false,
+ "description": {
+ "en_US": "Whether to trigger when the message mentions the bot",
+ "zh_CN": "是否在消息@机器人时触发"
+ },
+ "label": {
+ "en_US": "At",
+ "zh_CN": "@"
+ },
+ "name": "at",
+ "required": true,
+ "type": "boolean"
+ },
+ {
+ "default": [],
+ "description": {
+ "en_US": "The prefix of the message",
+ "zh_CN": "消息前缀"
+ },
+ "items": {
+ "type": "string"
+ },
+ "label": {
+ "en_US": "Prefix",
+ "zh_CN": "前缀"
+ },
+ "name": "prefix",
+ "required": true,
+ "type": "array"
+ },
+ {
+ "default": [],
+ "description": {
+ "en_US": "The regexp of the message",
+ "zh_CN": "消息正则表达式"
+ },
+ "items": {
+ "type": "string"
+ },
+ "label": {
+ "en_US": "Regexp",
+ "zh_CN": "正则表达式"
+ },
+ "name": "regexp",
+ "required": true,
+ "type": "array"
+ },
+ {
+ "default": 0,
+ "description": {
+ "en_US": "The probability of the random response, range from 0.0 to 1.0",
+ "zh_CN": "随机响应概率,范围为 0.0-1.0"
+ },
+ "label": {
+ "en_US": "Random",
+ "zh_CN": "随机"
+ },
+ "name": "random",
+ "required": false,
+ "type": "float"
+ }
+ ],
+ "description": {
+ "en_US": "The group respond rule of the pipeline",
+ "zh_CN": "群响应规则"
+ },
+ "label": {
+ "en_US": "Group Respond Rule",
+ "zh_CN": "群响应规则"
+ },
+ "name": "group-respond-rules"
+ },
+ {
+ "config": [
+ {
+ "default": "blacklist",
+ "description": {
+ "en_US": "The mode of the access control",
+ "zh_CN": "访问控制模式"
+ },
+ "label": {
+ "en_US": "Mode",
+ "zh_CN": "模式"
+ },
+ "name": "mode",
+ "options": [
+ {
+ "label": {
+ "en_US": "Blacklist",
+ "zh_CN": "黑名单"
+ },
+ "name": "blacklist"
+ },
+ {
+ "label": {
+ "en_US": "Whitelist",
+ "zh_CN": "白名单"
+ },
+ "name": "whitelist"
+ }
+ ],
+ "required": true,
+ "type": "select"
+ },
+ {
+ "default": [],
+ "items": {
+ "type": "string"
+ },
+ "label": {
+ "en_US": "Blacklist",
+ "zh_CN": "黑名单"
+ },
+ "name": "blacklist",
+ "required": true,
+ "type": "array"
+ },
+ {
+ "default": [],
+ "items": {
+ "type": "string"
+ },
+ "label": {
+ "en_US": "Whitelist",
+ "zh_CN": "白名单"
+ },
+ "name": "whitelist",
+ "required": true,
+ "type": "array"
+ }
+ ],
+ "label": {
+ "en_US": "Access Control",
+ "zh_CN": "访问控制"
+ },
+ "name": "access-control"
+ },
+ {
+ "config": [
+ {
+ "default": [],
+ "description": {
+ "en_US": "The prefix of the message",
+ "zh_CN": "消息前缀"
+ },
+ "items": {
+ "type": "string"
+ },
+ "label": {
+ "en_US": "Prefix",
+ "zh_CN": "前缀"
+ },
+ "name": "prefix",
+ "required": true,
+ "type": "array"
+ },
+ {
+ "default": [],
+ "description": {
+ "en_US": "The regexp of the message",
+ "zh_CN": "消息正则表达式"
+ },
+ "items": {
+ "type": "string"
+ },
+ "label": {
+ "en_US": "Regexp",
+ "zh_CN": "正则表达式"
+ },
+ "name": "regexp",
+ "required": true,
+ "type": "array"
+ }
+ ],
+ "label": {
+ "en_US": "Ignore Rules",
+ "zh_CN": "消息忽略规则"
+ },
+ "name": "ignore-rules"
+ }
+ ]
+ },
+ {
+ "label": {
+ "en_US": "Safety Control",
+ "zh_CN": "安全控制"
+ },
+ "name": "safety",
+ "stages": [
+ {
+ "config": [
+ {
+ "default": "all",
+ "label": {
+ "en_US": "Scope",
+ "zh_CN": "检查范围"
+ },
+ "name": "scope",
+ "options": [
+ {
+ "label": {
+ "en_US": "All",
+ "zh_CN": "全部"
+ },
+ "name": "all"
+ },
+ {
+ "label": {
+ "en_US": "Income Message",
+ "zh_CN": "传入消息(用户消息)"
+ },
+ "name": "income-msg"
+ },
+ {
+ "label": {
+ "en_US": "Output Message",
+ "zh_CN": "传出消息(机器人消息)"
+ },
+ "name": "output-msg"
+ }
+ ],
+ "required": true,
+ "type": "select"
+ },
+ {
+ "default": false,
+ "label": {
+ "en_US": "Check Sensitive Words",
+ "zh_CN": "检查敏感词"
+ },
+ "name": "check-sensitive-words",
+ "required": true,
+ "type": "boolean"
+ }
+ ],
+ "label": {
+ "en_US": "Content Filter",
+ "zh_CN": "内容过滤"
+ },
+ "name": "content-filter"
+ },
+ {
+ "config": [
+ {
+ "default": 60,
+ "label": {
+ "en_US": "Window Length",
+ "zh_CN": "窗口长度(秒)"
+ },
+ "name": "window-length",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": 60,
+ "label": {
+ "en_US": "Limitation",
+ "zh_CN": "限制次数"
+ },
+ "name": "limitation",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": "drop",
+ "label": {
+ "en_US": "Strategy",
+ "zh_CN": "策略"
+ },
+ "name": "strategy",
+ "options": [
+ {
+ "label": {
+ "en_US": "Drop",
+ "zh_CN": "丢弃"
+ },
+ "name": "drop"
+ },
+ {
+ "label": {
+ "en_US": "Wait",
+ "zh_CN": "等待"
+ },
+ "name": "wait"
+ }
+ ],
+ "required": true,
+ "type": "select"
+ }
+ ],
+ "label": {
+ "en_US": "Rate Limit",
+ "zh_CN": "速率限制"
+ },
+ "name": "rate-limit"
+ }
+ ]
+ },
+ {
+ "label": {
+ "en_US": "AI Feature",
+ "zh_CN": "AI 能力"
+ },
+ "name": "ai",
+ "stages": [
+ {
+ "config": [
+ {
+ "default": "local-agent",
+ "label": {
+ "en_US": "Runner",
+ "zh_CN": "运行器"
+ },
+ "name": "runner",
+ "options": [
+ {
+ "label": {
+ "en_US": "Embedded Agent",
+ "zh_CN": "内置 Agent"
+ },
+ "name": "local-agent"
+ },
+ {
+ "label": {
+ "en_US": "Dify Service API",
+ "zh_CN": "Dify 服务 API"
+ },
+ "name": "dify-service-api"
+ },
+ {
+ "label": {
+ "en_US": "Aliyun Dashscope App API",
+ "zh_CN": "阿里云百炼平台 API"
+ },
+ "name": "dashscope-app-api"
+ }
+ ],
+ "required": true,
+ "type": "select"
+ }
+ ],
+ "label": {
+ "en_US": "Runner",
+ "zh_CN": "运行方式"
+ },
+ "name": "runner"
+ },
+ {
+ "config": [
+ {
+ "label": {
+ "en_US": "Model",
+ "zh_CN": "模型"
+ },
+ "name": "model",
+ "required": true,
+ "scope": "/provider/models/llm",
+ "type": "select"
+ },
+ {
+ "default": 10,
+ "label": {
+ "en_US": "Max Round",
+ "zh_CN": "最大回合数"
+ },
+ "name": "max-round",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "items": {
+ "properties": {
+ "content": {
+ "type": "string"
+ },
+ "role": {
+ "default": "user",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "label": {
+ "en_US": "Prompt",
+ "zh_CN": "提示词"
+ },
+ "name": "prompt",
+ "required": true,
+ "type": "array"
+ }
+ ],
+ "description": {
+ "en_US": "Configure the embedded agent of the pipeline",
+ "zh_CN": "配置内置 Agent"
+ },
+ "label": {
+ "en_US": "Embedded Agent",
+ "zh_CN": "内置 Agent"
+ },
+ "name": "local-agent"
+ },
+ {
+ "config": [
+ {
+ "label": {
+ "en_US": "Base URL",
+ "zh_CN": "基础 URL"
+ },
+ "name": "base-url",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "chat",
+ "label": {
+ "en_US": "App Type",
+ "zh_CN": "应用类型"
+ },
+ "name": "app-type",
+ "options": [
+ {
+ "label": {
+ "en_US": "Chat",
+ "zh_CN": "聊天(包括Chatflow)"
+ },
+ "name": "chat"
+ },
+ {
+ "label": {
+ "en_US": "Agent",
+ "zh_CN": "Agent"
+ },
+ "name": "agent"
+ },
+ {
+ "label": {
+ "en_US": "Workflow",
+ "zh_CN": "工作流"
+ },
+ "name": "workflow"
+ }
+ ],
+ "required": true,
+ "type": "select"
+ },
+ {
+ "label": {
+ "en_US": "API Key",
+ "zh_CN": "API 密钥"
+ },
+ "name": "api-key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "plain",
+ "label": {
+ "en_US": "CoT Convert",
+ "zh_CN": "思维链转换策略"
+ },
+ "name": "thinking-convert",
+ "options": [
+ {
+ "label": {
+ "en_US": "Convert to ...",
+ "zh_CN": "转换成 ..."
+ },
+ "name": "plain"
+ },
+ {
+ "label": {
+ "en_US": "Original",
+ "zh_CN": "原始"
+ },
+ "name": "original"
+ },
+ {
+ "label": {
+ "en_US": "Remove",
+ "zh_CN": "移除"
+ },
+ "name": "remove"
+ }
+ ],
+ "required": true,
+ "type": "select"
+ }
+ ],
+ "description": {
+ "en_US": "Configure the Dify service API of the pipeline",
+ "zh_CN": "配置 Dify 服务 API"
+ },
+ "label": {
+ "en_US": "Dify Service API",
+ "zh_CN": "Dify 服务 API"
+ },
+ "name": "dify-service-api"
+ },
+ {
+ "config": [
+ {
+ "default": "agent",
+ "label": {
+ "en_US": "App Type",
+ "zh_CN": "应用类型"
+ },
+ "name": "app-type",
+ "options": [
+ {
+ "label": {
+ "en_US": "Agent",
+ "zh_CN": "Agent"
+ },
+ "name": "agent"
+ },
+ {
+ "label": {
+ "en_US": "Workflow",
+ "zh_CN": "工作流"
+ },
+ "name": "workflow"
+ }
+ ],
+ "required": true,
+ "type": "select"
+ },
+ {
+ "label": {
+ "en_US": "API Key",
+ "zh_CN": "API 密钥"
+ },
+ "name": "api-key",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "label": {
+ "en_US": "App ID",
+ "zh_CN": "应用 ID"
+ },
+ "name": "app-id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "default": "参考资料来自:",
+ "label": {
+ "en_US": "References Quote",
+ "zh_CN": "引用文本"
+ },
+ "name": "references_quote",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "description": {
+ "en_US": "Configure the Aliyun Dashscope App API of the pipeline",
+ "zh_CN": "配置阿里云百炼平台 API"
+ },
+ "label": {
+ "en_US": "Aliyun Dashscope App API",
+ "zh_CN": "阿里云百炼平台 API"
+ },
+ "name": "dashscope-app-api"
+ }
+ ]
+ },
+ {
+ "label": {
+ "en_US": "Output Processing",
+ "zh_CN": "输出处理"
+ },
+ "name": "output",
+ "stages": [
+ {
+ "config": [
+ {
+ "default": 1000,
+ "label": {
+ "en_US": "Threshold",
+ "zh_CN": "阈值"
+ },
+ "name": "threshold",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": "forward",
+ "label": {
+ "en_US": "Strategy",
+ "zh_CN": "策略"
+ },
+ "name": "strategy",
+ "options": [
+ {
+ "label": {
+ "en_US": "Forward Message Component",
+ "zh_CN": "转发消息组件"
+ },
+ "name": "forward"
+ },
+ {
+ "label": {
+ "en_US": "Convert to Image",
+ "zh_CN": "转换为图片"
+ },
+ "name": "image"
+ }
+ ],
+ "required": true,
+ "type": "select"
+ },
+ {
+ "default": "",
+ "label": {
+ "en_US": "Font Path",
+ "zh_CN": "字体路径"
+ },
+ "name": "font-path",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "label": {
+ "en_US": "Long Text Processing",
+ "zh_CN": "长文本处理"
+ },
+ "name": "long-text-processing"
+ },
+ {
+ "config": [
+ {
+ "default": 0,
+ "label": {
+ "en_US": "Min",
+ "zh_CN": "最小秒数"
+ },
+ "name": "min",
+ "required": true,
+ "type": "integer"
+ },
+ {
+ "default": 0,
+ "label": {
+ "en_US": "Max",
+ "zh_CN": "最大秒数"
+ },
+ "name": "max",
+ "required": true,
+ "type": "integer"
+ }
+ ],
+ "label": {
+ "en_US": "Force Delay",
+ "zh_CN": "强制延迟"
+ },
+ "name": "force-delay"
+ },
+ {
+ "config": [
+ {
+ "default": true,
+ "label": {
+ "en_US": "Hide Exception",
+ "zh_CN": "不输出异常信息给用户"
+ },
+ "name": "hide-exception",
+ "required": true,
+ "type": "boolean"
+ },
+ {
+ "default": true,
+ "label": {
+ "en_US": "At Sender",
+ "zh_CN": "在回复中@发送者"
+ },
+ "name": "at-sender",
+ "required": true,
+ "type": "boolean"
+ },
+ {
+ "default": false,
+ "label": {
+ "en_US": "Quote Origin",
+ "zh_CN": "引用原文"
+ },
+ "name": "quote-origin",
+ "required": true,
+ "type": "boolean"
+ },
+ {
+ "default": true,
+ "label": {
+ "en_US": "Track Function Calls",
+ "zh_CN": "跟踪函数调用"
+ },
+ "name": "track-function-calls",
+ "required": true,
+ "type": "boolean"
+ }
+ ],
+ "label": {
+ "en_US": "Misc",
+ "zh_CN": "杂项"
+ },
+ "name": "misc"
+ }
+ ]
+ }
+ ]
+ },
+ "msg": "ok"
+ }
+}
+
diff --git a/web_ui/src/app/home/page.tsx b/web_ui/src/app/home/page.tsx
new file mode 100644
index 00000000..f9fbf66f
--- /dev/null
+++ b/web_ui/src/app/home/page.tsx
@@ -0,0 +1,6 @@
+export default function Home() {
+ return (
+
+
+ );
+}
diff --git a/web_ui/src/app/home/pipeline-config/components/pipeline-form/PipelineChildFormEntity.ts b/web_ui/src/app/home/pipeline-config/components/pipeline-form/PipelineChildFormEntity.ts
new file mode 100644
index 00000000..a2038c35
--- /dev/null
+++ b/web_ui/src/app/home/pipeline-config/components/pipeline-form/PipelineChildFormEntity.ts
@@ -0,0 +1,20 @@
+import {DynamicFormItemConfig} from "@/app/home/components/dynamic-form/DynamicFormItemConfig";
+
+export interface IPipelineChildFormEntity {
+ name: string;
+ label: string;
+ formItems: DynamicFormItemConfig[]
+}
+
+export class PipelineChildFormEntity implements IPipelineChildFormEntity {
+ 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
diff --git a/web_ui/src/app/home/pipeline-config/components/pipeline-form/PipelineFormComponent.tsx b/web_ui/src/app/home/pipeline-config/components/pipeline-form/PipelineFormComponent.tsx
new file mode 100644
index 00000000..9a7ae574
--- /dev/null
+++ b/web_ui/src/app/home/pipeline-config/components/pipeline-form/PipelineFormComponent.tsx
@@ -0,0 +1,512 @@
+import {Form, Button, Switch, Select, Input, InputNumber} from "antd";
+import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons';
+import {useState} from "react";
+import styles from "./pipelineFormStyle.module.css"
+
+export default function PipelineFormComponent({
+ onFinish,
+ onCancel,
+}: {
+ onFinish: () => void;
+ onCancel: () => void;
+}) {
+ const [nowFormIndex, setNowFormIndex] = useState(0)
+ // 这里不好,可以改成enum等
+ const formLabelList: FormLabel[] = [
+ {label: "AI能力", name: "ai"},
+ {label: "触发条件", name: "trigger"},
+ {label: "安全能力", name: "safety"},
+ {label: "输出处理", name: "output"},
+ ]
+
+ function getNowFormLabel() {
+ return formLabelList[nowFormIndex]
+ }
+
+
+ function getPreFormLabel(): undefined | FormLabel {
+ if (nowFormIndex !== undefined && nowFormIndex > 0) {
+ return formLabelList[nowFormIndex - 1]
+ } else {
+ return undefined
+ }
+ }
+
+ function getNextFormLabel(): undefined | FormLabel {
+ if (nowFormIndex !== undefined && nowFormIndex < formLabelList.length - 1) {
+ return formLabelList[nowFormIndex + 1]
+ } else {
+ return undefined
+ }
+ }
+
+ function addFormLabelIndex() {
+ if (nowFormIndex < formLabelList.length - 1) {
+ setNowFormIndex(nowFormIndex + 1)
+ }
+ }
+
+ function reduceFormLabelIndex() {
+ if (nowFormIndex > 0) {
+ setNowFormIndex(nowFormIndex - 1)
+ }
+ }
+
+ return (
+
+
+ {getNowFormLabel().label}
+
+ {/* AI能力表单 ai */}
+
+
+
+
+ {/* 内置 Agent 配置区块 */}
+
配置内置Agent
+ {/* TODO 这里要拉模型 */}
+
+
+
+
+
+
+ {/* TODO 这里要做转换处理 */}
+
+
+
+
+ {/* Dify 服务 API 区块 */}
+
配置Dify服务API
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 阿里云百炼区块 */}
+
配置阿里云百炼平台 API
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 触发条件表单 trigger */}
+
+ 群响应规则
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 访问控制
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 安全控制表单 safety */}
+
+
+
+
+
+
+
+
+ {/* 速率限制块 rate-limit */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 输出处理控制表单 output */}
+
+
+
+
+
+
+
+
+
+
+
+ {/* 强制延迟区块 */}
+
+
+
+
+
+
+
+
+ {/* 杂项区块 */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ onClick={addFormLabelIndex}
+ disabled={!getNextFormLabel()}
+ iconPosition={"end"}
+ >
+ {getNextFormLabel()?.label || "暂无更多"}
+
+
+
+
+ )
+}
+
+enum PipelineFormRoute {
+
+}
+
+interface FormPageLabel {
+ formIndex: number,
+ formName: string,
+ formLabel: string,
+}
+
+interface FormLabel {
+ label: string,
+ name: string,
+}
diff --git a/web_ui/src/app/home/pipeline-config/components/pipeline-form/pipelineFormStyle.module.css b/web_ui/src/app/home/pipeline-config/components/pipeline-form/pipelineFormStyle.module.css
new file mode 100644
index 00000000..b011fa39
--- /dev/null
+++ b/web_ui/src/app/home/pipeline-config/components/pipeline-form/pipelineFormStyle.module.css
@@ -0,0 +1,12 @@
+.formItemSubtitle {
+ font-size: 18px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.changeFormButtonGroupContainer {
+ width: 250px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
diff --git a/web_ui/src/app/home/pipeline-config/page.tsx b/web_ui/src/app/home/pipeline-config/page.tsx
new file mode 100644
index 00000000..f9363e2e
--- /dev/null
+++ b/web_ui/src/app/home/pipeline-config/page.tsx
@@ -0,0 +1,30 @@
+"use client"
+import {Modal} from "antd";
+import {useState} from "react";
+import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
+import PipelineFormComponent from "./components/pipeline-form/PipelineFormComponent";
+
+
+export default function PluginConfigPage() {
+ const [modalOpen, setModalOpen] = useState(false);
+ const [isEditForm, setIsEditForm] = useState(false)
+
+
+ return (
+
+
setModalOpen(false)}
+ onCancel={() => setModalOpen(false)}
+ width={700}
+ footer={null}
+ >
+ {}} onCancel={() => {}}/>
+
+
+
{setModalOpen(true)}}/>
+
+ );
+}
diff --git a/web_ui/src/app/home/pipeline-config/pipelineConfig.module.css b/web_ui/src/app/home/pipeline-config/pipelineConfig.module.css
new file mode 100644
index 00000000..66240516
--- /dev/null
+++ b/web_ui/src/app/home/pipeline-config/pipelineConfig.module.css
@@ -0,0 +1,4 @@
+.formItemSubTitle {
+ font-size: 30px;
+ font-weight: bold;
+}
diff --git a/web_ui/src/app/home/plugin-config/page.tsx b/web_ui/src/app/home/plugin-config/page.tsx
new file mode 100644
index 00000000..8bc22362
--- /dev/null
+++ b/web_ui/src/app/home/plugin-config/page.tsx
@@ -0,0 +1,7 @@
+export default function pluginConfigPage() {
+ return (
+
+
PluginConfigPage
+
+ );
+}
diff --git a/web_ui/src/app/infra/api/api-types/index.ts b/web_ui/src/app/infra/api/api-types/index.ts
new file mode 100644
index 00000000..ce30d80b
--- /dev/null
+++ b/web_ui/src/app/infra/api/api-types/index.ts
@@ -0,0 +1,11 @@
+export interface ApiResponse {
+ code: number;
+ data: T;
+ msg: string;
+}
+
+export interface I18nText {
+ en_US: string;
+ zh_CN: string;
+}
+
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
new file mode 100644
index 00000000..41a1b8dc
--- /dev/null
+++ b/web_ui/src/app/infra/api/api-types/pipelines/GetMetaDataResponse.ts
@@ -0,0 +1,47 @@
+export interface GetMetaDataResponse {
+ configs: Config[]
+}
+
+
+interface Label {
+ en_US: string;
+ zh_CN: string;
+}
+
+interface Option {
+ label: Label;
+ name: string;
+}
+
+interface ConfigItem {
+ default?: boolean | Array | number | string;
+ description?: Label;
+ items?: {
+ type?: string;
+ properties?: {
+ [key: string]: {
+ type: string;
+ default?: any;
+ };
+ };
+ };
+ label: Label;
+ name: string;
+ options?: Option[];
+ required: boolean;
+ scope?: string;
+ type: string;
+}
+
+interface Stage {
+ config: ConfigItem[];
+ description?: Label;
+ label: Label;
+ name: string;
+}
+
+interface Config {
+ label: Label;
+ name: string;
+ stages: Stage[];
+}
diff --git a/web_ui/src/app/infra/basic-component/create-card-component/CreateCardComponent.tsx b/web_ui/src/app/infra/basic-component/create-card-component/CreateCardComponent.tsx
new file mode 100644
index 00000000..3e33d712
--- /dev/null
+++ b/web_ui/src/app/infra/basic-component/create-card-component/CreateCardComponent.tsx
@@ -0,0 +1,27 @@
+import styles from "./createCartComponent.module.css";
+
+export default function CreateCardComponent({
+ width,
+ height,
+ plusSize,
+ onClick,
+}: {
+ width: number;
+ height: number;
+ plusSize: number;
+ onClick: () => void
+}) {
+ return (
+
+ +
+
+ )
+}
\ No newline at end of file
diff --git a/web_ui/src/app/infra/basic-component/create-card-component/createCartComponent.module.css b/web_ui/src/app/infra/basic-component/create-card-component/createCartComponent.module.css
new file mode 100644
index 00000000..8d751a01
--- /dev/null
+++ b/web_ui/src/app/infra/basic-component/create-card-component/createCartComponent.module.css
@@ -0,0 +1,16 @@
+.cardContainer {
+ width: 360px;
+ height: 200px;
+ background-color: #FFF;
+ border-radius: 9px;
+ box-shadow: rgba(0, 0, 0, 0.4) 0 1px 1px -1px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-evenly;
+}
+
+.createCardContainer {
+ font-size: 90px;
+ color: #acacac;
+}
\ No newline at end of file
diff --git a/web_ui/src/app/infra/basic-types/I18N.ts b/web_ui/src/app/infra/basic-types/I18N.ts
new file mode 100644
index 00000000..54111e6c
--- /dev/null
+++ b/web_ui/src/app/infra/basic-types/I18N.ts
@@ -0,0 +1,4 @@
+export interface I18NText {
+ en_US: string;
+ zh_CN: string;
+}
\ No newline at end of file
diff --git a/web_ui/src/app/layout.tsx b/web_ui/src/app/layout.tsx
new file mode 100644
index 00000000..4d25b364
--- /dev/null
+++ b/web_ui/src/app/layout.tsx
@@ -0,0 +1,22 @@
+import "./global.css"
+import type { Metadata } from "next";
+
+
+export const metadata: Metadata = {
+ title: "Create Next App",
+ description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/web_ui/src/app/login/layout.tsx b/web_ui/src/app/login/layout.tsx
new file mode 100644
index 00000000..e868ff78
--- /dev/null
+++ b/web_ui/src/app/login/layout.tsx
@@ -0,0 +1,13 @@
+import React from "react";
+
+export default function LoginLayout({
+ children
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/web_ui/src/app/login/page.tsx b/web_ui/src/app/login/page.tsx
new file mode 100644
index 00000000..ec5499d9
--- /dev/null
+++ b/web_ui/src/app/login/page.tsx
@@ -0,0 +1,7 @@
+export default function Home() {
+ return (
+
+
loginpage
+
+ );
+}
diff --git a/web_ui/src/app/not-found.tsx b/web_ui/src/app/not-found.tsx
new file mode 100644
index 00000000..9a023999
--- /dev/null
+++ b/web_ui/src/app/not-found.tsx
@@ -0,0 +1,8 @@
+export default function NotFound() {
+ return (
+
+ {/* TODO: @qidongrui 这里404页面有时间要更新*/}
+
Langbot没有找到该页面喔~
+
+ );
+}
diff --git a/web_ui/src/app/page.tsx b/web_ui/src/app/page.tsx
new file mode 100644
index 00000000..e839b271
--- /dev/null
+++ b/web_ui/src/app/page.tsx
@@ -0,0 +1,7 @@
+export default function Home() {
+ return (
+
+
+
+ );
+}
diff --git a/web_ui/tsconfig.json b/web_ui/tsconfig.json
new file mode 100644
index 00000000..c1334095
--- /dev/null
+++ b/web_ui/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}