feat: fix eslint limits to build

This commit is contained in:
HYana
2025-04-28 21:35:26 +08:00
parent 32f138bff5
commit 5c74bb41c9
15 changed files with 1886 additions and 1887 deletions
@@ -1,3 +0,0 @@
export interface ICreateBotField {
}
@@ -1,5 +1,8 @@
import {BotFormEntity, IBotFormEntity} from "@/app/home/bots/components/bot-form/BotFormEntity"; import {
import {Button, Form, Input, Select, Space} from "antd"; 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 { useEffect, useState } from "react";
import { IChooseAdapterEntity } from "@/app/home/bots/components/bot-form/ChooseAdapterEntity"; import { IChooseAdapterEntity } from "@/app/home/bots/components/bot-form/ChooseAdapterEntity";
import { import {
@@ -7,56 +10,61 @@ import {
IDynamicFormItemConfig, IDynamicFormItemConfig,
parseDynamicFormItemType parseDynamicFormItemType
} from "@/app/home/components/dynamic-form/DynamicFormItemConfig"; } 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 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 { Bot } from "@/app/infra/api/api-types";
import { notification } from "antd";
export default function BotForm({ export default function BotForm({
initBotId, initBotId,
onFormSubmit, onFormSubmit,
onFormCancel, onFormCancel
}: { }: {
initBotId?: string; initBotId?: string;
onFormSubmit: (value: IBotFormEntity) => void; onFormSubmit: (value: IBotFormEntity) => void;
onFormCancel: (value: IBotFormEntity) => void; onFormCancel: (value: IBotFormEntity) => void;
}) { }) {
const [adapterNameToDynamicConfigMap, setAdapterNameToDynamicConfigMap] = useState(new Map<string, IDynamicFormItemConfig[]>()) const [adapterNameToDynamicConfigMap, setAdapterNameToDynamicConfigMap] =
useState(new Map<string, IDynamicFormItemConfig[]>());
const [form] = Form.useForm<IBotFormEntity>(); const [form] = Form.useForm<IBotFormEntity>();
const [showDynamicForm, setShowDynamicForm] = useState<boolean>(false) const [showDynamicForm, setShowDynamicForm] = useState<boolean>(false);
const [dynamicForm] = Form.useForm(); const [dynamicForm] = Form.useForm();
const [adapterNameList, setAdapterNameList] = useState<IChooseAdapterEntity[]>([]) const [adapterNameList, setAdapterNameList] = useState<
const [dynamicFormConfigList, setDynamicFormConfigList] = useState<IDynamicFormItemConfig[]>([]) IChooseAdapterEntity[]
const [isLoading, setIsLoading] = useState<boolean>(false) >([]);
const [dynamicFormConfigList, setDynamicFormConfigList] = useState<
IDynamicFormItemConfig[]
>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
initBotFormComponent() initBotFormComponent();
if (initBotId) { if (initBotId) {
onEditMode() onEditMode();
} else { } else {
onCreateMode() onCreateMode();
} }
}, []) // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function initBotFormComponent() { async function initBotFormComponent() {
// 拉取adapter // 拉取adapter
const rawAdapterList = await httpClient.getAdapters() const rawAdapterList = await httpClient.getAdapters();
// 初始化适配器选择列表 // 初始化适配器选择列表
setAdapterNameList( setAdapterNameList(
rawAdapterList.adapters.map(item => { rawAdapterList.adapters.map((item) => {
return { return {
label: item.label.zh_CN, label: item.label.zh_CN,
value: item.name value: item.name
} };
}) })
) );
// 初始化适配器表单map // 初始化适配器表单map
rawAdapterList.adapters.forEach(rawAdapter => { rawAdapterList.adapters.forEach((rawAdapter) => {
adapterNameToDynamicConfigMap.set( adapterNameToDynamicConfigMap.set(
rawAdapter.name, rawAdapter.name,
rawAdapter.spec.config.map(item => rawAdapter.spec.config.map(
(item) =>
new DynamicFormItemConfig({ new DynamicFormItemConfig({
default: item.default, default: item.default,
id: UUID.generate(), id: UUID.generate(),
@@ -66,132 +74,136 @@ export default function BotForm({
type: parseDynamicFormItemType(item.type) type: parseDynamicFormItemType(item.type)
}) })
) )
) );
}) });
// 拉取初始化表单信息 // 拉取初始化表单信息
if (initBotId) { if (initBotId) {
getBotFieldById(initBotId).then(val => { getBotFieldById(initBotId).then((val) => {
form.setFieldsValue(val) form.setFieldsValue(val);
handleAdapterSelect(val.adapter) handleAdapterSelect(val.adapter);
dynamicForm.setFieldsValue(val.adapter_config) dynamicForm.setFieldsValue(val.adapter_config);
}) });
} else { } else {
form.resetFields() form.resetFields();
} }
setAdapterNameToDynamicConfigMap(adapterNameToDynamicConfigMap) setAdapterNameToDynamicConfigMap(adapterNameToDynamicConfigMap);
} }
async function onCreateMode() { async function onCreateMode() {}
} function onEditMode() {}
function onEditMode() {
}
async function getBotFieldById(botId: string): Promise<IBotFormEntity> { async function getBotFieldById(botId: string): Promise<IBotFormEntity> {
const bot = (await httpClient.getBot(botId)).bot const bot = (await httpClient.getBot(botId)).bot;
let botFormEntity = new BotFormEntity({ return new BotFormEntity({
adapter: bot.adapter, adapter: bot.adapter,
description: bot.description, description: bot.description,
name: bot.name, name: bot.name,
adapter_config: bot.adapter_config adapter_config: bot.adapter_config
}) });
return botFormEntity
} }
function handleAdapterSelect(adapterName: string) { function handleAdapterSelect(adapterName: string) {
console.log("Select adapter: ", adapterName) console.log("Select adapter: ", adapterName);
if (adapterName) { if (adapterName) {
const dynamicFormConfigList = adapterNameToDynamicConfigMap.get(adapterName) const dynamicFormConfigList =
console.log(dynamicFormConfigList) adapterNameToDynamicConfigMap.get(adapterName);
console.log(dynamicFormConfigList);
if (dynamicFormConfigList) { if (dynamicFormConfigList) {
setDynamicFormConfigList(dynamicFormConfigList) setDynamicFormConfigList(dynamicFormConfigList);
} }
setShowDynamicForm(true) setShowDynamicForm(true);
} else { } else {
setShowDynamicForm(false) setShowDynamicForm(false);
} }
} }
function handleSubmitButton() { function handleSubmitButton() {
form.submit() form.submit();
} }
function handleFormFinish(value: IBotFormEntity) { function handleFormFinish() {
dynamicForm.submit() dynamicForm.submit();
} }
// 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里 // 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里
function onDynamicFormSubmit(value: object) { function onDynamicFormSubmit(value: object) {
setIsLoading(true) setIsLoading(true);
console.log('setloading', true) console.log("set loading", true);
if (initBotId) { if (initBotId) {
// 编辑提交 // 编辑提交
console.log('submit edit', form.getFieldsValue() ,value) console.log("submit edit", form.getFieldsValue(), value);
let updateBot: Bot = { const updateBot: Bot = {
uuid: initBotId, uuid: initBotId,
name: form.getFieldsValue().name, name: form.getFieldsValue().name,
description: form.getFieldsValue().description, description: form.getFieldsValue().description,
adapter: form.getFieldsValue().adapter, adapter: form.getFieldsValue().adapter,
adapter_config: value adapter_config: value
} };
httpClient.updateBot(initBotId, updateBot).then(res => { httpClient
.updateBot(initBotId, updateBot)
.then((res) => {
// TODO success toast // TODO success toast
console.log("update bot success", res) console.log("update bot success", res);
onFormSubmit(form.getFieldsValue()) onFormSubmit(form.getFieldsValue());
notification.success({ notification.success({
message: "更新成功", message: "更新成功",
description: "机器人更新成功" description: "机器人更新成功"
});
}) })
}).catch(err => { .catch(() => {
// TODO error toast // TODO error toast
notification.error({ notification.error({
message: "更新失败", message: "更新失败",
description: "机器人更新失败" description: "机器人更新失败"
});
}) })
}).finally(() => { .finally(() => {
setIsLoading(false) setIsLoading(false);
form.resetFields() form.resetFields();
dynamicForm.resetFields() dynamicForm.resetFields();
}) });
} else { } else {
// 创建提交 // 创建提交
console.log('submit create', form.getFieldsValue() ,value) console.log("submit create", form.getFieldsValue(), value);
let newBot: Bot = { const newBot: Bot = {
name: form.getFieldsValue().name, name: form.getFieldsValue().name,
description: form.getFieldsValue().description, description: form.getFieldsValue().description,
adapter: form.getFieldsValue().adapter, adapter: form.getFieldsValue().adapter,
adapter_config: value adapter_config: value
} };
httpClient.createBot(newBot).then(res => { httpClient
.createBot(newBot)
.then((res) => {
// TODO success toast // TODO success toast
notification.success({ notification.success({
message: "创建成功", message: "创建成功",
description: "机器人创建成功" description: "机器人创建成功"
});
console.log(res);
onFormSubmit(form.getFieldsValue());
}) })
console.log(res) .catch(() => {
onFormSubmit(form.getFieldsValue())
}).catch(err => {
// TODO error toast // TODO error toast
notification.error({ notification.error({
message: "创建失败", message: "创建失败",
description: "机器人创建失败" description: "机器人创建失败"
});
}) })
}).finally(() => { .finally(() => {
setIsLoading(false) setIsLoading(false);
form.resetFields() form.resetFields();
dynamicForm.resetFields() dynamicForm.resetFields();
}) });
} }
setShowDynamicForm(false) setShowDynamicForm(false);
console.log('setloading', false) console.log("set loading", false);
// TODO 刷新bot列表 // TODO 刷新bot列表
// TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)? // TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)?
} }
function handleSaveButton() { function handleSaveButton() {
form.submit() form.submit();
} }
return ( return (
@@ -200,7 +212,7 @@ export default function BotForm({
form={form} form={form}
labelCol={{ span: 5 }} labelCol={{ span: 5 }}
wrapperCol={{ span: 18 }} wrapperCol={{ span: 18 }}
layout='vertical' layout="vertical"
onFinish={handleFormFinish} onFinish={handleFormFinish}
disabled={isLoading} disabled={isLoading}
> >
@@ -220,9 +232,7 @@ export default function BotForm({
name={"description"} name={"description"}
rules={[{ required: true, message: "该项为必填项哦~" }]} rules={[{ required: true, message: "该项为必填项哦~" }]}
> >
<Input <Input placeholder="简单描述一下这个机器人"></Input>
placeholder="简单描述一下这个机器人"
></Input>
</Form.Item> </Form.Item>
<Form.Item<IBotFormEntity> <Form.Item<IBotFormEntity>
@@ -233,23 +243,21 @@ export default function BotForm({
<Select <Select
style={{ width: 220 }} style={{ width: 220 }}
onChange={(value) => { onChange={(value) => {
handleAdapterSelect(value) handleAdapterSelect(value);
}} }}
options={adapterNameList} options={adapterNameList}
/> />
</Form.Item> </Form.Item>
</Form> </Form>
{ {showDynamicForm && (
showDynamicForm &&
<DynamicFormComponent <DynamicFormComponent
form={dynamicForm} form={dynamicForm}
itemConfigList={dynamicFormConfigList} itemConfigList={dynamicFormConfigList}
onSubmit={onDynamicFormSubmit} onSubmit={onDynamicFormSubmit}
/> />
} )}
<Space> <Space>
{ {!initBotId && (
!initBotId &&
<Button <Button
type="primary" type="primary"
htmlType="button" htmlType="button"
@@ -258,9 +266,8 @@ export default function BotForm({
> >
</Button> </Button>
} )}
{ {initBotId && (
initBotId &&
<Button <Button
type="primary" type="primary"
htmlType="submit" htmlType="submit"
@@ -269,13 +276,17 @@ export default function BotForm({
> >
</Button> </Button>
} )}
<Button htmlType="button" onClick={() => { <Button
onFormCancel(form.getFieldsValue()) htmlType="button"
}} disabled={isLoading}> onClick={() => {
onFormCancel(form.getFieldsValue());
}}
disabled={isLoading}
>
</Button> </Button>
</Space> </Space>
</div> </div>
) );
} }
@@ -1,94 +1,89 @@
"use client" "use client";
import styles from "./HomeSidebar.module.css" import styles from "./HomeSidebar.module.css";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import {SidebarChild, SidebarChildVO} from "@/app/home/components/home-sidebar/HomeSidebarChild"; import {
import {useRouter, usePathname, useSearchParams} from "next/navigation"; SidebarChild,
SidebarChildVO
} from "@/app/home/components/home-sidebar/HomeSidebarChild";
import { useRouter, usePathname } from "next/navigation";
import { sidebarConfigList } from "@/app/home/components/home-sidebar/sidbarConfigList"; import { sidebarConfigList } from "@/app/home/components/home-sidebar/sidbarConfigList";
// TODO 侧边导航栏要加动画 // TODO 侧边导航栏要加动画
export default function HomeSidebar({ export default function HomeSidebar({
onSelectedChange onSelectedChangeAction
}: { }: {
onSelectedChange: (sidebarChild: SidebarChildVO) => void onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void;
}) { }) {
// 路由相关 // 路由相关
const router = useRouter() const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
const searchParams = useSearchParams();
// 路由被动变化时处理 // 路由被动变化时处理
useEffect(() => { useEffect(() => {
handleRouteChange(pathname) handleRouteChange(pathname);
}, [pathname, searchParams]); }, [pathname]);
const [selectedChild, setSelectedChild] = useState<SidebarChildVO>(sidebarConfigList[0]) const [selectedChild, setSelectedChild] = useState<SidebarChildVO>(
sidebarConfigList[0]
);
useEffect(() => { useEffect(() => {
console.log('HomeSidebar挂载完成'); console.log("HomeSidebar挂载完成");
initSelect() initSelect();
return () => console.log('HomeSidebar卸载'); return () => console.log("HomeSidebar卸载");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
function handleChildClick(child: SidebarChildVO) { function handleChildClick(child: SidebarChildVO) {
setSelectedChild(child) setSelectedChild(child);
handleRoute(child) handleRoute(child);
onSelectedChange(child) onSelectedChangeAction(child);
} }
function initSelect() { function initSelect() {
handleChildClick(sidebarConfigList[0]) handleChildClick(sidebarConfigList[0]);
} }
function handleRoute(child: SidebarChildVO) { function handleRoute(child: SidebarChildVO) {
console.log(child) console.log(child);
router.push(`${child.route}`) router.push(`${child.route}`);
} }
function handleRouteChange(pathname: string) { function handleRouteChange(pathname: string) {
// TODO 这段逻辑并不好,未来router封装好后改掉 // TODO 这段逻辑并不好,未来router封装好后改掉
// 判断在home下,并且路由更改的是自己的路由子组件则更新UI // 判断在home下,并且路由更改的是自己的路由子组件则更新UI
const routeList = pathname.split('/') const routeList = pathname.split("/");
if ( if (
routeList[1] === "home" && routeList[1] === "home" &&
sidebarConfigList.find(childConfig => sidebarConfigList.find((childConfig) => childConfig.route === pathname)
childConfig.route === pathname
)
) { ) {
console.log("find success") console.log("find success");
const routeSelectChild = sidebarConfigList.find(childConfig => const routeSelectChild = sidebarConfigList.find(
childConfig.route === pathname (childConfig) => childConfig.route === pathname
) );
if (routeSelectChild) { if (routeSelectChild) {
setSelectedChild(routeSelectChild) setSelectedChild(routeSelectChild);
} }
} }
} }
return ( return (
<div className={`${styles.sidebarContainer}`}> <div className={`${styles.sidebarContainer}`}>
{/* LangBot、ICON区域 */} {/* LangBot、ICON区域 */}
<div className={`${styles.langbotIconContainer}`}> <div className={`${styles.langbotIconContainer}`}>
{/* icon */} {/* icon */}
<div className={`${styles.langbotIcon}`}> <div className={`${styles.langbotIcon}`}>L</div>
L <div className={`${styles.langbotText}`}>Langbot</div>
</div>
<div className={`${styles.langbotText}`}>
Langbot
</div>
</div> </div>
{/* 菜单列表,后期可升级成配置驱动 */} {/* 菜单列表,后期可升级成配置驱动 */}
<div> <div>
{ {sidebarConfigList.map((config) => {
sidebarConfigList.map(config => {
return ( return (
<div <div
key={config.id} key={config.id}
onClick={() => { onClick={() => {
console.log('click:', config.id) console.log("click:", config.id);
handleChildClick(config) handleChildClick(config);
}} }}
> >
<SidebarChild <SidebarChild
@@ -97,10 +92,8 @@ export default function HomeSidebar({
name={config.name} name={config.name}
/> />
</div> </div>
) );
}) })}
}
</div> </div>
</div> </div>
); );
@@ -21,21 +21,24 @@ export class SidebarChildVO {
} }
} }
export function SidebarChild({ export function SidebarChild({
icon, icon,
name, name,
isSelected, isSelected
}: { }: {
icon: string; icon: string;
name: string; name: string;
isSelected: boolean; isSelected: boolean;
}) { }) {
return ( return (
<div className={`${styles.sidebarChildContainer} ${isSelected ? styles.sidebarSelected : styles.sidebarUnselected}`}> <div
className={`${styles.sidebarChildContainer} ${isSelected ? styles.sidebarSelected : styles.sidebarUnselected}`}
>
<div className={`${styles.sidebarChildIcon}`} /> <div className={`${styles.sidebarChildIcon}`} />
<div>{name}</div> <div>
{icon}
{name}
</div>
</div> </div>
); );
} }
+9 -15
View File
@@ -1,36 +1,30 @@
"use client" "use client";
import '@ant-design/v5-patch-for-react-19'; import "@ant-design/v5-patch-for-react-19";
import styles from "./layout.module.css" import styles from "./layout.module.css";
import HomeSidebar from "@/app/home/components/home-sidebar/HomeSidebar"; import HomeSidebar from "@/app/home/components/home-sidebar/HomeSidebar";
import HomeTitleBar from "@/app/home/components/home-titlebar/HomeTitleBar"; import HomeTitleBar from "@/app/home/components/home-titlebar/HomeTitleBar";
import React, { useState } from "react"; import React, { useState } from "react";
import { SidebarChildVO } from "@/app/home/components/home-sidebar/HomeSidebarChild"; import { SidebarChildVO } from "@/app/home/components/home-sidebar/HomeSidebarChild";
import { useRouter } from 'next/navigation';
export default function HomeLayout({ export default function HomeLayout({
children children
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
const router = useRouter(); const [title, setTitle] = useState<string>("");
const [title, setTitle] = useState<string>("")
const onSelectedChange = (child: SidebarChildVO) => { const onSelectedChange = (child: SidebarChildVO) => {
setTitle(child.name) setTitle(child.name);
} };
return ( return (
<div className={`${styles.homeLayoutContainer}`}> <div className={`${styles.homeLayoutContainer}`}>
<HomeSidebar <HomeSidebar onSelectedChangeAction={onSelectedChange} />
onSelectedChange={onSelectedChange}
/>
<div className={`${styles.main}`}> <div className={`${styles.main}`}>
<HomeTitleBar title={title} /> <HomeTitleBar title={title} />
{/* 主页面 */} {/* 主页面 */}
<div className={`${styles.mainContent}`}> <div className={`${styles.mainContent}`}>{children}</div>
{children}
</div> </div>
</div> </div>
</div> );
)
} }
@@ -12,7 +12,7 @@ export default function LLMForm({
initLLMId, initLLMId,
onFormSubmit, onFormSubmit,
onFormCancel, onFormCancel,
onLLMDeleted, onLLMDeleted
}: { }: {
editMode: boolean; editMode: boolean;
initLLMId?: string; initLLMId?: string;
@@ -21,50 +21,54 @@ export default function LLMForm({
onLLMDeleted: () => void; onLLMDeleted: () => void;
}) { }) {
const [form] = Form.useForm<ICreateLLMField>(); const [form] = Form.useForm<ICreateLLMField>();
const extraOptions: SelectProps['options'] = [] const extraOptions: SelectProps["options"] = [];
const [initValue, setInitValue] = useState<ICreateLLMField>() const [initValue] = useState<ICreateLLMField>();
const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false) const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false);
const abilityOptions: SelectProps['options'] = [ const abilityOptions: SelectProps["options"] = [
{ {
label: '函数调用', label: "函数调用",
value: 'func_call', value: "func_call"
}, },
{ {
label: '图像识别', label: "图像识别",
value: 'vision', value: "vision"
}, }
]; ];
const [requesterNameList, setRequesterNameList] = useState<IChooseRequesterEntity[]>([]) const [requesterNameList, setRequesterNameList] = useState<
IChooseRequesterEntity[]
>([]);
useEffect(() => { useEffect(() => {
initLLMModelFormComponent() initLLMModelFormComponent();
if (editMode && initLLMId) { if (editMode && initLLMId) {
getLLMConfig(initLLMId).then(val => { getLLMConfig(initLLMId).then((val) => {
form.setFieldsValue(val) form.setFieldsValue(val);
}) });
} else { } else {
form.resetFields() form.resetFields();
} }
}, []) // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function initLLMModelFormComponent() { async function initLLMModelFormComponent() {
const requesterNameList = await httpClient.getProviderRequesters() const requesterNameList = await httpClient.getProviderRequesters();
setRequesterNameList(requesterNameList.requesters.map(item => { setRequesterNameList(
requesterNameList.requesters.map((item) => {
return { return {
label: item.label.zh_CN, label: item.label.zh_CN,
value: item.name value: item.name
} };
})) })
);
} }
async function getLLMConfig(id: string): Promise<ICreateLLMField> { async function getLLMConfig(id: string): Promise<ICreateLLMField> {
const llmModel = await httpClient.getProviderLLMModel(id);
const llmModel = await httpClient.getProviderLLMModel(id) const fakeExtraArgs = [];
const extraArgs = llmModel.model.extra_args as Record<string, string>;
let fakeExtraArgs = []
const extraArgs = llmModel.model.extra_args as Record<string, string>
for (const key in extraArgs) { for (const key in extraArgs) {
fakeExtraArgs.push(`${key}:${extraArgs[key]}`) fakeExtraArgs.push(`${key}:${extraArgs[key]}`);
} }
return { return {
name: llmModel.model.name, name: llmModel.model.name,
@@ -72,8 +76,8 @@ export default function LLMForm({
url: llmModel.model.requester_config?.base_url, url: llmModel.model.requester_config?.base_url,
api_key: llmModel.model.api_keys[0], api_key: llmModel.model.api_keys[0],
abilities: llmModel.model.abilities, abilities: llmModel.model.abilities,
extra_args: fakeExtraArgs, extra_args: fakeExtraArgs
} };
} }
function handleFormSubmit(value: ICreateLLMField) { function handleFormSubmit(value: ICreateLLMField) {
@@ -81,61 +85,59 @@ export default function LLMForm({
// 暂不支持更改模型 // 暂不支持更改模型
// onSaveEdit(value) // onSaveEdit(value)
} else { } else {
onCreateLLM(value) onCreateLLM(value);
} }
form.resetFields() form.resetFields();
} }
function onSaveEdit(value: ICreateLLMField) { // function onSaveEdit(value: ICreateLLMField) {
const requestParam: LLMModel = { // const requestParam: LLMModel = {
uuid: UUID.generate(), // uuid: UUID.generate(),
name: value.name, // name: value.name,
description: "", // description: "",
requester: value.model_provider, // requester: value.model_provider,
requester_config: { // requester_config: {
"base_url": value.url, // "base_url": value.url,
"timeout": 120 // "timeout": 120
}, // },
extra_args: value.extra_args, // extra_args: value.extra_args,
api_keys: [value.api_key], // api_keys: [value.api_key],
abilities: value.abilities, // abilities: value.abilities,
// created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', // // created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800',
// updated_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)) // httpClient.createProviderLLMModel(requestParam).then(r => console.log(r))
} // }
function onCreateLLM(value: ICreateLLMField) { function onCreateLLM(value: ICreateLLMField) {
console.log("create llm", value) console.log("create llm", value);
const requestParam: LLMModel = { const requestParam: LLMModel = {
uuid: UUID.generate(), uuid: UUID.generate(),
name: value.name, name: value.name,
description: "", description: "",
requester: value.model_provider, requester: value.model_provider,
requester_config: { requester_config: {
"base_url": value.url, base_url: value.url,
"timeout": 120 timeout: 120
}, },
extra_args: value.extra_args, extra_args: value.extra_args,
api_keys: [value.api_key], api_keys: [value.api_key],
abilities: value.abilities, abilities: value.abilities
// created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', // created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800',
// updated_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 => { httpClient.createProviderLLMModel(requestParam).then(() => {
onFormSubmit(value) onFormSubmit(value);
}) });
} }
function handleAbilitiesChange() { function handleAbilitiesChange() {}
}
function deleteModel() { function deleteModel() {
if (initLLMId) { if (initLLMId) {
httpClient.deleteProviderLLMModel(initLLMId).then(res => { httpClient.deleteProviderLLMModel(initLLMId).then(() => {
onLLMDeleted() onLLMDeleted();
}) });
} }
} }
@@ -157,18 +159,21 @@ export default function LLMForm({
<Button <Button
danger danger
onClick={() => { onClick={() => {
deleteModel() deleteModel();
setShowDeleteConfirmModal(false) setShowDeleteConfirmModal(false);
}} }}
></Button> >
</Button>
<Button <Button
onClick={() => { onClick={() => {
setShowDeleteConfirmModal(false) setShowDeleteConfirmModal(false);
}} }}
></Button> >
</Button>
</div> </div>
} }
> >
</Modal> </Modal>
@@ -176,7 +181,7 @@ export default function LLMForm({
form={form} form={form}
labelCol={{ span: 4 }} labelCol={{ span: 4 }}
wrapperCol={{ span: 14 }} wrapperCol={{ span: 14 }}
layout='horizontal' layout="horizontal"
initialValues={{ initialValues={{
...initValue ...initValue
}} }}
@@ -202,8 +207,7 @@ export default function LLMForm({
> >
<Select <Select
style={{ width: 120 }} style={{ width: 120 }}
onChange={() => { onChange={() => {}}
}}
options={requesterNameList} options={requesterNameList}
/> />
</Form.Item> </Form.Item>
@@ -224,17 +228,10 @@ export default function LLMForm({
name={"api_key"} name={"api_key"}
rules={[{ required: true, message: "该项为必填项哦~" }]} rules={[{ required: true, message: "该项为必填项哦~" }]}
> >
<Input <Input placeholder="你的API Key" style={{ width: 500 }}></Input>
placeholder="你的API Key"
style={{width: 500}}
></Input>
</Form.Item> </Form.Item>
<Form.Item<ICreateLLMField> label={"开启能力"} name={"abilities"}>
<Form.Item<ICreateLLMField>
label={"开启能力"}
name={"abilities"}
>
<Select <Select
mode="tags" mode="tags"
style={{ width: 500 }} style={{ width: 500 }}
@@ -244,10 +241,7 @@ export default function LLMForm({
/> />
</Form.Item> </Form.Item>
<Form.Item<ICreateLLMField> <Form.Item<ICreateLLMField> label={"其他参数"} name={"extra_args"}>
label={"其他参数"}
name={"extra_args"}
>
<Select <Select
mode="tags" mode="tags"
style={{ width: 500 }} style={{ width: 500 }}
@@ -257,31 +251,29 @@ export default function LLMForm({
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item wrapperCol={{ offset: 4, span: 14 }}>
wrapperCol={{offset: 4, span: 14}}
>
<Space> <Space>
{ {!editMode && (
!editMode &&
<Button type="primary" htmlType="submit"> <Button type="primary" htmlType="submit">
</Button> </Button>
} )}
{ {editMode && (
editMode &&
<Button <Button
color="danger" color="danger"
variant="solid" variant="solid"
onClick={() => {setShowDeleteConfirmModal(true)}} onClick={() => {
setShowDeleteConfirmModal(true);
}}
disabled={false} disabled={false}
> >
</Button> </Button>
} )}
<Button <Button
htmlType="button" htmlType="button"
onClick={() => { onClick={() => {
onFormCancel(form.getFieldsValue()) onFormCancel(form.getFieldsValue());
}} }}
disabled={false} disabled={false}
> >
@@ -291,6 +283,5 @@ export default function LLMForm({
</Form.Item> </Form.Item>
</Form> </Form>
</div> </div>
);
)
} }
@@ -3,7 +3,7 @@ import {DynamicFormItemConfig} from "@/app/home/components/dynamic-form/DynamicF
export interface IPipelineChildFormEntity { export interface IPipelineChildFormEntity {
name: string; name: string;
label: string; label: string;
formItems: DynamicFormItemConfig[] formItems: DynamicFormItemConfig[];
} }
export class PipelineChildFormEntity implements IPipelineChildFormEntity { export class PipelineChildFormEntity implements IPipelineChildFormEntity {
@@ -12,7 +12,6 @@ export class PipelineChildFormEntity implements IPipelineChildFormEntity {
name: string; name: string;
constructor(props: IPipelineChildFormEntity) { constructor(props: IPipelineChildFormEntity) {
this.form = props.form;
this.label = props.label; this.label = props.label;
this.name = props.name; this.name = props.name;
this.formItems = props.formItems; this.formItems = props.formItems;
@@ -1,83 +1,92 @@
import {Form, Button, Switch, Select, Input, InputNumber, SelectProps} from "antd"; import {
import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons'; Form,
Button,
Switch,
Select,
Input,
InputNumber,
SelectProps
} from "antd";
import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import styles from "./pipelineFormStyle.module.css" import styles from "./pipelineFormStyle.module.css";
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from "@/app/infra/http/HttpClient";
import { LLMModel, Pipeline } from "@/app/infra/api/api-types"; import { LLMModel, Pipeline } from "@/app/infra/api/api-types";
import {LLMCardVO} from "@/app/home/models/component/llm-card/LLMCardVO";
import { UUID } from "uuidjs"; import { UUID } from "uuidjs";
export default function PipelineFormComponent({ export default function PipelineFormComponent({
onFinish, onFinish
onCancel,
}: { }: {
onFinish: () => void; onFinish: () => void;
onCancel: () => void;
}) { }) {
const [nowFormIndex, setNowFormIndex] = useState<number>(0) const [nowFormIndex, setNowFormIndex] = useState<number>(0);
const [nowAIRunner, setNowAIRunner] = useState("") const [nowAIRunner, setNowAIRunner] = useState("");
const [llmModelList, setLlmModelList] = useState<SelectProps['options']>([]) const [llmModelList, setLlmModelList] = useState<SelectProps["options"]>([]);
// 这里不好,可以改成enum等 // 这里不好,可以改成enum等
const formLabelList: FormLabel[] = [ const formLabelList: FormLabel[] = [
{ label: "基础", name: "basic" }, { label: "基础", name: "basic" },
{ label: "AI能力", name: "ai" }, { label: "AI能力", name: "ai" },
{ label: "触发条件", name: "trigger" }, { label: "触发条件", name: "trigger" },
{ label: "安全能力", name: "safety" }, { label: "安全能力", name: "safety" },
{label: "输出处理", name: "output"}, { label: "输出处理", name: "output" }
] ];
const [basicForm] = Form.useForm() const [basicForm] = Form.useForm();
const [aiForm] = Form.useForm() const [aiForm] = Form.useForm();
const [triggerForm] = Form.useForm() const [triggerForm] = Form.useForm();
const [safetyForm] = Form.useForm() const [safetyForm] = Form.useForm();
const [outputForm] = Form.useForm() const [outputForm] = Form.useForm();
useEffect(() => { useEffect(() => {
getLLMModelList() getLLMModelList();
}, []) }, []);
function getLLMModelList() { function getLLMModelList() {
httpClient.getProviderLLMModels().then((resp) => { httpClient
setLlmModelList(resp.models.map((model: LLMModel) => { .getProviderLLMModels()
.then((resp) => {
setLlmModelList(
resp.models.map((model: LLMModel) => {
return { return {
value: model.uuid, value: model.uuid,
label: model.name, label: model.name
} };
}))
}).catch((err) => {
console.error("get LLM model list error", err)
}) })
);
})
.catch((err) => {
console.error("get LLM model list error", err);
});
} }
function getNowFormLabel() { function getNowFormLabel() {
return formLabelList[nowFormIndex] return formLabelList[nowFormIndex];
} }
function getPreFormLabel(): undefined | FormLabel { function getPreFormLabel(): undefined | FormLabel {
if (nowFormIndex !== undefined && nowFormIndex > 0) { if (nowFormIndex !== undefined && nowFormIndex > 0) {
return formLabelList[nowFormIndex - 1] return formLabelList[nowFormIndex - 1];
} else { } else {
return undefined return undefined;
} }
} }
function getNextFormLabel(): undefined | FormLabel { function getNextFormLabel(): undefined | FormLabel {
if (nowFormIndex !== undefined && nowFormIndex < formLabelList.length - 1) { if (nowFormIndex !== undefined && nowFormIndex < formLabelList.length - 1) {
return formLabelList[nowFormIndex + 1] return formLabelList[nowFormIndex + 1];
} else { } else {
return undefined return undefined;
} }
} }
function addFormLabelIndex() { function addFormLabelIndex() {
if (nowFormIndex < formLabelList.length - 1) { if (nowFormIndex < formLabelList.length - 1) {
setNowFormIndex(nowFormIndex + 1) setNowFormIndex(nowFormIndex + 1);
} }
} }
function reduceFormLabelIndex() { function reduceFormLabelIndex() {
if (nowFormIndex > 0) { if (nowFormIndex > 0) {
setNowFormIndex(nowFormIndex - 1) setNowFormIndex(nowFormIndex - 1);
} }
} }
@@ -87,30 +96,30 @@ export default function PipelineFormComponent({
aiForm.validateFields(), aiForm.validateFields(),
triggerForm.validateFields(), triggerForm.validateFields(),
safetyForm.validateFields(), safetyForm.validateFields(),
outputForm.validateFields(), outputForm.validateFields()
]).then(() => { ])
const pipeline = assembleForm() .then(() => {
httpClient.createPipeline(pipeline).then(r => const pipeline = assembleForm();
onFinish() httpClient.createPipeline(pipeline).then(() => onFinish());
)
}).catch(e => {
console.error(e)
}) })
.catch((e) => {
console.error(e);
});
} }
// TODO 类型混乱,需要优化 // TODO 类型混乱,需要优化
function assembleForm(): Pipeline { function assembleForm(): Pipeline {
console.log("basicForm:", basicForm.getFieldsValue()) console.log("basicForm:", basicForm.getFieldsValue());
console.log("aiForm:", aiForm.getFieldsValue()) console.log("aiForm:", aiForm.getFieldsValue());
console.log("triggerForm:", triggerForm.getFieldsValue()) console.log("triggerForm:", triggerForm.getFieldsValue());
console.log("safetyForm:", safetyForm.getFieldsValue()) console.log("safetyForm:", safetyForm.getFieldsValue());
console.log("outputForm:", outputForm.getFieldsValue()) console.log("outputForm:", outputForm.getFieldsValue());
const config: object = { const config: object = {
ai: aiForm.getFieldsValue(), ai: aiForm.getFieldsValue(),
trigger: triggerForm.getFieldsValue(), trigger: triggerForm.getFieldsValue(),
safety: safetyForm.getFieldsValue(), safety: safetyForm.getFieldsValue(),
output: outputForm.getFieldsValue(), output: outputForm.getFieldsValue()
} };
return { return {
config, config,
@@ -120,28 +129,28 @@ export default function PipelineFormComponent({
name: basicForm.getFieldsValue().name, name: basicForm.getFieldsValue().name,
stages: [], stages: [],
updated_at: "", updated_at: "",
uuid: UUID.generate(), uuid: UUID.generate()
};
} }
}
return ( return (
<div <div style={{ maxHeight: "70vh", overflowY: "auto" }}>
style={{ maxHeight: '70vh', overflowY: 'auto' }} <h1>{getNowFormLabel().label}</h1>
>
<h1>
{getNowFormLabel().label}
</h1>
<Form <Form
layout={"vertical"} layout={"vertical"}
style={{ display: getNowFormLabel().name === "basic" ? 'block' : 'none' }} style={{
form={basicForm}> display: getNowFormLabel().name === "basic" ? "block" : "none"
}}
form={basicForm}
>
<Form.Item <Form.Item
label="流水线名称" label="流水线名称"
name={"name"} name={"name"}
rules={[{ rules={[
required: true, {
}]} required: true
}
]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
@@ -149,9 +158,11 @@ export default function PipelineFormComponent({
<Form.Item <Form.Item
label="流水线描述" label="流水线描述"
name={"description"} name={"description"}
rules={[{ rules={[
required: true, {
}]} required: true
}
]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
@@ -159,7 +170,7 @@ export default function PipelineFormComponent({
{/* AI能力表单 ai */} {/* AI能力表单 ai */}
<Form <Form
layout={"vertical"} layout={"vertical"}
style={{ display: getNowFormLabel().name === "ai" ? 'block' : 'none' }} style={{ display: getNowFormLabel().name === "ai" ? "block" : "none" }}
form={aiForm} form={aiForm}
> >
{/* Runner 配置区块 */} {/* Runner 配置区块 */}
@@ -175,16 +186,14 @@ export default function PipelineFormComponent({
{ label: "Dify 服务 API", value: "dify-service-api" }, { label: "Dify 服务 API", value: "dify-service-api" },
{ label: "阿里云百炼平台 API", value: "dashscope-app-api" } { label: "阿里云百炼平台 API", value: "dashscope-app-api" }
]} ]}
onChange={value => setNowAIRunner(value)} onChange={(value) => setNowAIRunner(value)}
/> />
</Form.Item> </Form.Item>
{/* 内置 Agent 配置区块 */} {/* 内置 Agent 配置区块 */}
{ {nowAIRunner === "local-agent" && (
nowAIRunner === "local-agent" &&
<> <>
<div className={`${styles.formItemSubtitle}`}>Agent</div> <div className={`${styles.formItemSubtitle}`}>Agent</div>
{/* TODO 这里要拉模型 */}
<Form.Item <Form.Item
label="模型" label="模型"
name={["local-agent", "model"]} name={["local-agent", "model"]}
@@ -200,13 +209,13 @@ export default function PipelineFormComponent({
<Form.Item <Form.Item
label="最大回合数" label="最大回合数"
name={["local-agent", "max-round"]} name={["local-agent", "max-round"]}
rules={[{ rules={[
required: true, {
}]} required: true
}
]}
> >
<InputNumber <InputNumber precision={0} />
precision={0}
/>
</Form.Item> </Form.Item>
{/* TODO 这里要做转换处理 */} {/* TODO 这里要做转换处理 */}
<Form.Item <Form.Item
@@ -220,12 +229,10 @@ export default function PipelineFormComponent({
placeholder={`示例结构:{ "role": "user", "content": "你好" } `} placeholder={`示例结构:{ "role": "user", "content": "你好" } `}
/> />
</Form.Item> </Form.Item>
</> </>
} )}
{/* Dify 服务 API 区块 */} {/* Dify 服务 API 区块 */}
{ {nowAIRunner === "dify-service-api" && (
nowAIRunner === "dify-service-api" &&
<> <>
<div className={`${styles.formItemSubtitle}`}>Dify服务API</div> <div className={`${styles.formItemSubtitle}`}>Dify服务API</div>
<Form.Item <Form.Item
@@ -233,7 +240,7 @@ export default function PipelineFormComponent({
name={["dify-service-api", "base-url"]} name={["dify-service-api", "base-url"]}
rules={[ rules={[
{ required: true }, { required: true },
{type: 'url', message: '请输入有效的URL地址'} { type: "url", message: "请输入有效的URL地址" }
]} ]}
> >
<Input /> <Input />
@@ -273,12 +280,13 @@ export default function PipelineFormComponent({
/> />
</Form.Item> </Form.Item>
</> </>
} )}
{/* 阿里云百炼区块 */} {/* 阿里云百炼区块 */}
{ {nowAIRunner === "dashscope-app-api" && (
nowAIRunner === "dashscope-app-api" &&
<> <>
<div className={`${styles.formItemSubtitle}`}> API</div> <div className={`${styles.formItemSubtitle}`}>
API
</div>
<Form.Item <Form.Item
label="应用类型" label="应用类型"
name={["dashscope-app-api", "app-type"]} name={["dashscope-app-api", "app-type"]}
@@ -301,9 +309,7 @@ export default function PipelineFormComponent({
<Form.Item <Form.Item
label="应用 ID" label="应用 ID"
name={["dashscope-app-api", "app-id"]} name={["dashscope-app-api", "app-id"]}
rules={[ rules={[{ required: true }]}
{required: true},
]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
@@ -315,13 +321,15 @@ export default function PipelineFormComponent({
<Input.TextArea rows={2} /> <Input.TextArea rows={2} />
</Form.Item> </Form.Item>
</> </>
} )}
</Form> </Form>
{/* 触发条件表单 trigger */} {/* 触发条件表单 trigger */}
<Form <Form
layout={"vertical"} layout={"vertical"}
style={{display: getNowFormLabel().name === "trigger" ? 'block' : 'none'}} style={{
display: getNowFormLabel().name === "trigger" ? "block" : "none"
}}
form={triggerForm} form={triggerForm}
> >
{/* 群响应规则块 */} {/* 群响应规则块 */}
@@ -339,9 +347,7 @@ export default function PipelineFormComponent({
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select
options={[ options={[{ value: '"type": "string"', label: '"type": "string"' }]}
{value: "\"type\": \"string\"", label: "\"type\": \"string\""},
]}
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@@ -349,21 +355,14 @@ export default function PipelineFormComponent({
name={["group-respond-rules", "regexp"]} name={["group-respond-rules", "regexp"]}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select mode="tags" options={[]} />
mode="tags"
options={[]}
/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"随机"} label={"随机"}
name={["group-respond-rules", "random"]} name={["group-respond-rules", "random"]}
rules={[{ required: false }]} rules={[{ required: false }]}
> >
<InputNumber <InputNumber max={1} min={0} step={0.05} />
max={1}
min={0}
step={0.05}
/>
</Form.Item> </Form.Item>
<div className={`${styles.formItemSubtitle}`}> 访 </div> <div className={`${styles.formItemSubtitle}`}> 访 </div>
<Form.Item <Form.Item
@@ -375,7 +374,7 @@ export default function PipelineFormComponent({
<Select <Select
options={[ options={[
{ label: "黑名单", value: "blacklist" }, { label: "黑名单", value: "blacklist" },
{label: "白名单", value: "Whitelist"}, { label: "白名单", value: "Whitelist" }
]} ]}
/> />
</Form.Item> </Form.Item>
@@ -385,10 +384,7 @@ export default function PipelineFormComponent({
name={["access-control", "blacklist"]} name={["access-control", "blacklist"]}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select mode={"tags"} options={[]} />
mode={"tags"}
options={[]}
/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@@ -396,10 +392,7 @@ export default function PipelineFormComponent({
name={["access-control", "whitelist"]} name={["access-control", "whitelist"]}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select mode={"tags"} options={[]} />
mode={"tags"}
options={[]}
/>
</Form.Item> </Form.Item>
<div className={`${styles.formItemSubtitle}`}> </div> <div className={`${styles.formItemSubtitle}`}> </div>
@@ -410,10 +403,7 @@ export default function PipelineFormComponent({
rules={[{ required: true }]} rules={[{ required: true }]}
tooltip={"消息前缀"} tooltip={"消息前缀"}
> >
<Select <Select mode={"tags"} options={[]} />
mode={"tags"}
options={[]}
/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@@ -422,17 +412,16 @@ export default function PipelineFormComponent({
rules={[{ required: true }]} rules={[{ required: true }]}
tooltip={"消息正则表达式"} tooltip={"消息正则表达式"}
> >
<Select <Select mode={"tags"} options={[]} />
mode={"tags"}
options={[]}
/>
</Form.Item> </Form.Item>
</Form> </Form>
{/* 安全控制表单 safety */} {/* 安全控制表单 safety */}
<Form <Form
layout={"vertical"} layout={"vertical"}
style={{ display: getNowFormLabel().name === "safety" ? 'block' : 'none' }} style={{
display: getNowFormLabel().name === "safety" ? "block" : "none"
}}
form={safetyForm} form={safetyForm}
> >
{/* 内容过滤块 content-filter */} {/* 内容过滤块 content-filter */}
@@ -446,7 +435,7 @@ export default function PipelineFormComponent({
options={[ options={[
{ label: "全部", value: "all" }, { label: "全部", value: "all" },
{ label: "传入消息(用户消息)", value: "income-msg" }, { label: "传入消息(用户消息)", value: "income-msg" },
{label: "传出消息(机器人消息)", value: "output-msg"}, { label: "传出消息(机器人消息)", value: "output-msg" }
]} ]}
/> />
</Form.Item> </Form.Item>
@@ -486,17 +475,18 @@ export default function PipelineFormComponent({
<Select <Select
options={[ options={[
{ label: "丢弃", value: "drop" }, { label: "丢弃", value: "drop" },
{label: "等待", value: "wait"}, { label: "等待", value: "wait" }
]} ]}
/> />
</Form.Item> </Form.Item>
</Form> </Form>
{/* 输出处理控制表单 output */} {/* 输出处理控制表单 output */}
<Form <Form
layout={"vertical"} layout={"vertical"}
style={{ display: getNowFormLabel().name === "output" ? 'block' : 'none' }} style={{
display: getNowFormLabel().name === "output" ? "block" : "none"
}}
form={outputForm} form={outputForm}
> >
{/* 长文本处理区块 */} {/* 长文本处理区块 */}
@@ -600,34 +590,15 @@ export default function PipelineFormComponent({
{getNextFormLabel()?.label || "暂无更多"} {getNextFormLabel()?.label || "暂无更多"}
</Button> </Button>
<Button <Button type="primary" onClick={handleCommit}>
type="primary"
onClick={handleCommit}
>
</Button> </Button>
</div> </div>
</div> </div>
) );
}
enum PipelineFormRoute {
}
interface FormPageLabel {
formIndex: number,
formName: string,
formLabel: string,
} }
interface FormLabel { interface FormLabel {
label: string, label: string;
name: string, name: string;
}
interface LLMSelectList {
label: string,
name: string,
} }
+32 -22
View File
@@ -1,4 +1,4 @@
"use client" "use client";
import { Modal } from "antd"; import { Modal } from "antd";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
@@ -9,30 +9,32 @@ import PipelineCardComponent from "@/app/home/pipelines/components/pipeline-card
export default function PluginConfigPage() { export default function PluginConfigPage() {
const [modalOpen, setModalOpen] = useState<boolean>(false); const [modalOpen, setModalOpen] = useState<boolean>(false);
const [isEditForm, setIsEditForm] = useState(false) const [isEditForm] = useState(false);
const [pipelineList, setPipelineList] = useState<PipelineCardVO[]>([]) const [pipelineList, setPipelineList] = useState<PipelineCardVO[]>([]);
useEffect(() => { useEffect(() => {
getPipelines() getPipelines();
}, []) }, []);
function getPipelines() { function getPipelines() {
httpClient.getPipelines().then(value => { httpClient
const pipelineList = value.pipelines.map(pipeline => { .getPipelines()
.then((value) => {
const pipelineList = value.pipelines.map((pipeline) => {
return new PipelineCardVO({ return new PipelineCardVO({
createTime: pipeline.created_at, createTime: pipeline.created_at,
description: pipeline.description, description: pipeline.description,
id: pipeline.uuid, id: pipeline.uuid,
name: pipeline.name, name: pipeline.name,
version: pipeline.for_version version: pipeline.for_version
});
});
setPipelineList(pipelineList);
}) })
}) .catch((error) => {
setPipelineList(pipelineList)
}).catch(error => {
// TODO toast // TODO toast
console.log(error) console.log(error);
}) });
} }
return ( return (
@@ -48,21 +50,29 @@ export default function PluginConfigPage() {
> >
<PipelineFormComponent <PipelineFormComponent
onFinish={() => { onFinish={() => {
getPipelines() getPipelines();
setModalOpen(false) setModalOpen(false);
}} }}
onCancel={() => {}}/> />
</Modal> </Modal>
{ {pipelineList.length > 0 && (
pipelineList.length > 0 &&
<div className={``}> <div className={``}>
{pipelineList.map(pipeline => { {pipelineList.map((pipeline) => {
return <PipelineCardComponent cardVO={pipeline}/> return (
<PipelineCardComponent key={pipeline.id} cardVO={pipeline} />
);
})} })}
</div> </div>
} )}
<CreateCardComponent width={360} height={200} plusSize={90} onClick={() => {setModalOpen(true)}}/> <CreateCardComponent
width={360}
height={200}
plusSize={90}
onClick={() => {
setModalOpen(true);
}}
/>
</div> </div>
); );
} }
@@ -1,4 +1,4 @@
"use client" "use client";
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO";
@@ -10,46 +10,51 @@ import {GithubOutlined} from "@ant-design/icons";
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from "@/app/infra/http/HttpClient";
export default function PluginInstalledComponent() { export default function PluginInstalledComponent() {
const [pluginList, setPluginList] = useState<PluginCardVO[]>([]) const [pluginList, setPluginList] = useState<PluginCardVO[]>([]);
const [modalOpen, setModalOpen] = useState(false) const [modalOpen, setModalOpen] = useState(false);
const [githubURL, setGithubURL] = useState("") const [githubURL, setGithubURL] = useState("");
useEffect(() => { useEffect(() => {
initData() initData();
}, []) // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
function initData() { function initData() {
getPluginList() getPluginList();
} }
function getPluginList() { function getPluginList() {
httpClient.getPlugins().then((value) => { httpClient.getPlugins().then((value) => {
setPluginList(value.plugins.map(plugin => { setPluginList(
value.plugins.map((plugin) => {
return new PluginCardVO({ return new PluginCardVO({
author: plugin.author, author: plugin.author,
description: plugin.description.zh_CN, description: plugin.description.zh_CN,
handlerCount: 0, handlerCount: 0,
name: plugin.name, name: plugin.name,
version: plugin.version, version: plugin.version,
isInitialized: plugin.status === "initialized", isInitialized: plugin.status === "initialized"
}) });
}))
}) })
);
});
} }
function handleModalConfirm() { function handleModalConfirm() {
installPlugin(githubURL) installPlugin(githubURL);
setModalOpen(false) setModalOpen(false);
} }
function installPlugin(url: string) { function installPlugin(url: string) {
httpClient.installPluginFromGithub(url).then(res => { httpClient
.installPluginFromGithub(url)
.then(() => {
// 安装后重新拉取 // 安装后重新拉取
getPluginList() getPluginList();
}).catch(err => {
console.log("error when install plugin:", err)
}) })
.catch((err) => {
console.log("error when install plugin:", err);
});
} }
return ( return (
<div className={`${styles.pluginListContainer}`}> <div className={`${styles.pluginListContainer}`}>
@@ -58,8 +63,8 @@ export default function PluginInstalledComponent () {
<div className={`${styles.modalTitle}`}> <div className={`${styles.modalTitle}`}>
<GithubOutlined <GithubOutlined
style={{ style={{
fontSize: '30px', fontSize: "30px",
marginRight: '20px' marginRight: "20px"
}} }}
type="setting" type="setting"
/> />
@@ -74,9 +79,7 @@ export default function PluginInstalledComponent () {
destroyOnClose={true} destroyOnClose={true}
> >
<div className={`${styles.modalBody}`}> <div className={`${styles.modalBody}`}>
<div> <div> GitHub </div>
GitHub
</div>
<Input <Input
placeholder="请输入插件的Github链接" placeholder="请输入插件的Github链接"
value={githubURL} value={githubURL}
@@ -84,21 +87,21 @@ export default function PluginInstalledComponent () {
/> />
</div> </div>
</Modal> </Modal>
{ {pluginList.map((vo, index) => {
pluginList.map((vo, index) => { return (
return <div key={index}> <div key={index}>
<PluginCardComponent cardVO={vo} /> <PluginCardComponent cardVO={vo} />
</div> </div>
}) );
} })}
<CreateCardComponent <CreateCardComponent
width={360} width={360}
height={140} height={140}
plusSize={90} plusSize={90}
onClick={() => { onClick={() => {
setModalOpen(true) setModalOpen(true);
}} }}
/> />
</div> </div>
) );
} }
@@ -1,27 +1,31 @@
import styles from "./pluginCard.module.css" import styles from "./pluginCard.module.css";
import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO";
import {GithubOutlined, LinkOutlined, ToolOutlined} from '@ant-design/icons'; import { GithubOutlined, LinkOutlined, ToolOutlined } from "@ant-design/icons";
import {Switch, Tag} from 'antd' import { Switch, Tag } from "antd";
import { useState } from "react"; import { useState } from "react";
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from "@/app/infra/http/HttpClient";
export default function PluginCardComponent({ export default function PluginCardComponent({
cardVO cardVO
}: { }: {
cardVO: PluginCardVO cardVO: PluginCardVO;
}) { }) {
const [initialized, setInitialized] = useState(cardVO.isInitialized) const [initialized, setInitialized] = useState(cardVO.isInitialized);
const [switchEnable, setSwitchEnable] = useState(true) const [switchEnable, setSwitchEnable] = useState(true);
function handleEnable() { function handleEnable() {
setSwitchEnable(false) setSwitchEnable(false);
httpClient.togglePlugin(cardVO.author, cardVO.name, !initialized).then(result => { httpClient
setInitialized(!initialized) .togglePlugin(cardVO.author, cardVO.name, !initialized)
}).catch(err => { .then(() => {
console.log("error: ", err) setInitialized(!initialized);
}).finally(() => {
setSwitchEnable(true)
}) })
.catch((err) => {
console.log("error: ", err);
})
.finally(() => {
setSwitchEnable(true);
});
} }
return ( return (
<div className={`${styles.cardContainer}`}> <div className={`${styles.cardContainer}`}>
@@ -31,10 +35,7 @@ export default function PluginCardComponent({
<div className={`${styles.fontGray}`}>{cardVO.author}</div> <div className={`${styles.fontGray}`}>{cardVO.author}</div>
{/* right icon & version */} {/* right icon & version */}
<div className={`${styles.iconVersionContainer}`}> <div className={`${styles.iconVersionContainer}`}>
<GithubOutlined <GithubOutlined style={{ fontSize: "26px" }} type="setting" />
style={{fontSize: '26px'}}
type="setting"
/>
<Tag color="#108ee9">v{cardVO.version}</Tag> <Tag color="#108ee9">v{cardVO.version}</Tag>
</div> </div>
</div> </div>
@@ -47,14 +48,10 @@ export default function PluginCardComponent({
<div className={`${styles.cardFooter}`}> <div className={`${styles.cardFooter}`}>
<div className={`${styles.linkSettingContainer}`}> <div className={`${styles.linkSettingContainer}`}>
<div className={`${styles.link}`}> <div className={`${styles.link}`}>
<LinkOutlined <LinkOutlined style={{ fontSize: "22px" }} />
style={{fontSize: '22px'}}
/>
<span>1</span> <span>1</span>
</div> </div>
<ToolOutlined <ToolOutlined style={{ fontSize: "22px" }} />
style={{fontSize: '22px'}}
/>
</div> </div>
<Switch <Switch
@@ -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 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 PluginMarketCardComponent from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent";
import { Input, Pagination } from "antd"; import { Input, Pagination } from "antd";
import {debounce} from "lodash" import { spaceClient } from "@/app/infra/http/HttpClient";
import {httpClient, spaceClient} from "@/app/infra/http/HttpClient";
export default function PluginMarketComponent() { export default function PluginMarketComponent() {
const [marketPluginList, setMarketPluginList] = useState<PluginMarketCardVO[]>([]) const [marketPluginList, setMarketPluginList] = useState<
const [totalCount, setTotalCount] = useState(0) PluginMarketCardVO[]
const [nowPage, setNowPage] = useState(1) >([]);
const [searchKeyword, setSearchKeyword] = useState("") const [totalCount, setTotalCount] = useState(0);
const [nowPage, setNowPage] = useState(1);
const [searchKeyword, setSearchKeyword] = useState("");
useEffect(() => { useEffect(() => {
initData() initData();
}, []) // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
function initData() { function initData() {
getPluginList() getPluginList();
} }
function onInputSearchKeyword(keyword: string) { function onInputSearchKeyword(keyword: string) {
// 这里记得加防抖,暂时没加 // 这里记得加防抖,暂时没加
setSearchKeyword(keyword) setSearchKeyword(keyword);
setNowPage(1) setNowPage(1);
getPluginList(1, keyword) getPluginList(1, keyword);
} }
function getPluginList(
function getPluginList(page: number = nowPage, keyword: string = searchKeyword) { page: number = nowPage,
spaceClient.getMarketPlugins(page, 10, keyword).then(res => { keyword: string = searchKeyword
setMarketPluginList(res.plugins.map(marketPlugin => new PluginMarketCardVO({ ) {
spaceClient.getMarketPlugins(page, 10, keyword).then((res) => {
setMarketPluginList(
res.plugins.map(
(marketPlugin) =>
new PluginMarketCardVO({
author: marketPlugin.author, author: marketPlugin.author,
description: marketPlugin.description, description: marketPlugin.description,
githubURL: marketPlugin.repository, githubURL: marketPlugin.repository,
name: marketPlugin.name, name: marketPlugin.name,
pluginId: String(marketPlugin.ID), pluginId: String(marketPlugin.ID),
starCount: marketPlugin.stars, starCount: marketPlugin.stars
})))
setTotalCount(res.total)
console.log("market plugins:", res)
}) })
)
);
setTotalCount(res.total);
console.log("market plugins:", res);
});
} }
return ( return (
<div className={`${styles.marketComponentBody}`}> <div className={`${styles.marketComponentBody}`}>
<Input <Input
style={{ style={{
width: '300px', width: "300px",
marginTop: '10px', marginTop: "10px"
}} }}
value={searchKeyword} value={searchKeyword}
placeholder="搜索插件" placeholder="搜索插件"
onChange={(e) => onInputSearchKeyword(e.target.value)} onChange={(e) => onInputSearchKeyword(e.target.value)}
/> />
<div className={`${styles.pluginListContainer}`}> <div className={`${styles.pluginListContainer}`}>
{ {marketPluginList.map((vo, index) => {
marketPluginList.map((vo, index) => { return (
return <div key={index}> <div key={index}>
<PluginMarketCardComponent cardVO={vo} /> <PluginMarketCardComponent cardVO={vo} />
</div> </div>
}) );
} })}
</div> </div>
<Pagination <Pagination
defaultCurrent={1} defaultCurrent={1}
total={totalCount} total={totalCount}
onChange={(pageNumber) => { onChange={(pageNumber) => {
setNowPage(pageNumber) setNowPage(pageNumber);
getPluginList(pageNumber) getPluginList(pageNumber);
}} }}
/> />
</div> </div>
);
)
} }
+31 -18
View File
@@ -42,7 +42,10 @@ export interface LLMModel {
description: string; description: string;
uuid: string; uuid: string;
requester: string; requester: string;
requester_config: object; requester_config: {
base_url: string;
timeout: number;
};
extra_args: object; extra_args: object;
api_keys: string[]; api_keys: string[];
abilities: string[]; abilities: string[];
@@ -82,7 +85,17 @@ export interface Adapter {
label: I18nText; label: I18nText;
description: I18nText; description: I18nText;
icon?: string; icon?: string;
spec: object; spec: {
config: AdapterSpecConfig[];
};
}
export interface AdapterSpecConfig {
default: string | number | boolean | Array<unknown>;
label: I18nText;
name: string;
required: boolean;
type: string;
} }
export interface ApiRespPlatformBots { export interface ApiRespPlatformBots {
@@ -182,23 +195,23 @@ export interface ApiRespUserToken {
} }
export interface MarketPlugin { export interface MarketPlugin {
ID: number ID: number;
CreatedAt: string // ISO 8601 格式日期 CreatedAt: string; // ISO 8601 格式日期
UpdatedAt: string UpdatedAt: string;
DeletedAt: string | null DeletedAt: string | null;
name: string name: string;
author: string author: string;
description: string description: string;
repository: string // GitHub 仓库路径 repository: string; // GitHub 仓库路径
artifacts_path: string artifacts_path: string;
stars: number stars: number;
downloads: number downloads: number;
status: "initialized" | "mounted" // 可根据实际状态值扩展联合类型 status: "initialized" | "mounted"; // 可根据实际状态值扩展联合类型
synced_at: string synced_at: string;
pushed_at: string // 最后一次代码推送时间 pushed_at: string; // 最后一次代码推送时间
} }
export interface MarketPluginResponse { export interface MarketPluginResponse {
plugins: MarketPlugin[] plugins: MarketPlugin[];
total: number total: number;
} }
@@ -1,8 +1,7 @@
export interface GetMetaDataResponse { export interface GetMetaDataResponse {
configs: Config[] configs: Config[];
} }
interface Label { interface Label {
en_US: string; en_US: string;
zh_CN: string; zh_CN: string;
@@ -21,7 +20,7 @@ interface ConfigItem {
properties?: { properties?: {
[key: string]: { [key: string]: {
type: string; type: string;
default?: any; default?: object | string;
}; };
}; };
}; };
+73 -63
View File
@@ -1,63 +1,73 @@
'use client'; "use client";
import { Button, Input, Form, Checkbox, Divider } from 'antd'; import { Button, Input, Form, Checkbox, Divider } from "antd";
import {GoogleOutlined, AppleOutlined, LockOutlined, UserOutlined, QqCircleFilled, QqOutlined} from '@ant-design/icons'; import {
import styles from './login.module.css'; GoogleOutlined,
import {useEffect, useState} from 'react'; 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 { httpClient } from "@/app/infra/http/HttpClient";
import '@ant-design/v5-patch-for-react-19'; import "@ant-design/v5-patch-for-react-19";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
export default function Home() { export default function Home() {
const router = useRouter(); const router = useRouter();
const [form] = Form.useForm<LoginField>(); const [form] = Form.useForm<LoginField>();
const [rememberMe, setRememberMe] = useState(false); const [rememberMe, setRememberMe] = useState(false);
const [isRegisterMode, setIsRegisterMode] = useState(false); const [isRegisterMode, setIsRegisterMode] = useState(false);
const [isInitialized, setIsInitialized] = useState(false) const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => { useEffect(() => {
getIsInitialized() getIsInitialized();
}, []) }, []);
// 检查是否为首次启动项目,只为首次启动的用户提供注册资格 // 检查是否为首次启动项目,只为首次启动的用户提供注册资格
function getIsInitialized() { function getIsInitialized() {
httpClient.checkIfInited().then(res => { httpClient
setIsInitialized(res.initialized) .checkIfInited()
}).catch(err => { .then((res) => {
console.log("error at getIsInitialized: ", err) setIsInitialized(res.initialized);
}) })
.catch((err) => {
console.log("error at getIsInitialized: ", err);
});
} }
function handleFormSubmit(formField: LoginField) { function handleFormSubmit(formField: LoginField) {
if (isRegisterMode) { if (isRegisterMode) {
handleRegister(formField.email, formField.password); handleRegister(formField.email, formField.password);
} else { } else {
handleLogin(formField.email, formField.password) handleLogin(formField.email, formField.password);
} }
} }
function handleRegister(username: string, password: string) { function handleRegister(username: string, password: string) {
httpClient.initUser(username, password).then(res => { httpClient
console.log("init user success: ", res) .initUser(username, password)
}).catch(err => { .then((res) => {
console.log("init user error: ", err) console.log("init user success: ", res);
}) })
.catch((err) => {
console.log("init user error: ", err);
});
} }
function handleLogin(username: string, password: string) { function handleLogin(username: string, password: string) {
httpClient.authUser(username, password).then(res => { httpClient
localStorage.setItem("token", res.token) .authUser(username, password)
console.log("login success: ", res) .then((res) => {
router.push("/home") localStorage.setItem("token", res.token);
}).catch(err => { console.log("login success: ", res);
console.log("login error: ", err) router.push("/home");
}) })
.catch((err) => {
console.log("login error: ", err);
});
} }
return ( return (
// 使用 Ant Design 的组件库,使用 antd 的样式 // 使用 Ant Design 的组件库,使用 antd 的样式
// 仅前端样式,无交互功能。 // 仅前端样式,无交互功能。
@@ -68,26 +78,24 @@ export default function Home() {
{/* left 为注册的表单,需要填入的内容有:邮箱,密码 */} {/* left 为注册的表单,需要填入的内容有:邮箱,密码 */}
<div className={styles.left}> <div className={styles.left}>
<div className={styles.loginForm}> <div className={styles.loginForm}>
{ {isRegisterMode && (
isRegisterMode &&
<h1 className={styles.title}> LangBot </h1> <h1 className={styles.title}> LangBot </h1>
} )}
{ {!isRegisterMode && (
!isRegisterMode &&
<h1 className={styles.title}> LangBot</h1> <h1 className={styles.title}> LangBot</h1>
} )}
<Form <Form
form={form} form={form}
layout="vertical" layout="vertical"
onFinish={(values) => { onFinish={(values) => {
handleFormSubmit(values) handleFormSubmit(values);
}} }}
> >
<Form.Item <Form.Item
name="email" name="email"
rules={[ rules={[
{ required: true, message: '请输入邮箱!' }, { required: true, message: "请输入邮箱!" },
{ type: 'email', message: '请输入有效的邮箱地址!' } { type: "email", message: "请输入有效的邮箱地址!" }
]} ]}
> >
<Input <Input
@@ -99,9 +107,7 @@ export default function Home() {
<Form.Item <Form.Item
name="password" name="password"
rules={[ rules={[{ required: true, message: "请输入密码!" }]}
{ required: true, message: '请输入密码!' }
]}
> >
<Input.Password <Input.Password
placeholder="输入密码" placeholder="输入密码"
@@ -118,29 +124,34 @@ export default function Home() {
30 30
</Checkbox> </Checkbox>
<span> <span>
<a href="#" className={`${styles.forgetPassword}`}>?</a> <a href="#" className={`${styles.forgetPassword}`}>
{ ?
!isRegisterMode && </a>
<a href="" {!isRegisterMode && (
<a
href=""
onClick={(event) => { onClick={(event) => {
setIsRegisterMode(true) setIsRegisterMode(true);
event.preventDefault() event.preventDefault();
}} }}
></a> >
}
{ </a>
isRegisterMode && )}
<a href="" {isRegisterMode && (
<a
href=""
onClick={(event) => { onClick={(event) => {
setIsRegisterMode(false) setIsRegisterMode(false);
event.preventDefault() event.preventDefault();
}} }}
></a> >
}
</a>
)}
</span> </span>
</div> </div>
<Button <Button
type="primary" type="primary"
size="large" size="large"
@@ -149,14 +160,13 @@ export default function Home() {
htmlType="submit" htmlType="submit"
disabled={isRegisterMode && isInitialized} disabled={isRegisterMode && isInitialized}
> >
{ {isRegisterMode
isRegisterMode ? ( ? isInitialized
isInitialized ? "暂不提供注册" : "注册" ? "暂不提供注册"
) : "登录" : "注册"
} : "登录"}
</Button> </Button>
<Divider className={styles.divider}></Divider> <Divider className={styles.divider}></Divider>
<div className={styles.socialLogin}> <div className={styles.socialLogin}>
@@ -169,7 +179,7 @@ export default function Home() {
使 使
</Button> </Button>
</div> </div>
<div style={{ height: '10px' }}></div> <div style={{ height: "10px" }}></div>
<div className={styles.socialLogin}> <div className={styles.socialLogin}>
<Button <Button
className={styles.socialButton} className={styles.socialButton}