chores: 删除前端agent相关内容及无用语言文件
- 删除了多个与流程功能相关的组件和文件 - 移除了 API 服务中与流程相关的无用代码 - 更新了导航钩子和头部组件,移除了与流程相关的路由和图标 - 删除了多个语言翻译文件
This commit is contained in:
parent
aee7779237
commit
86303e358e
|
@ -155,13 +155,7 @@ export const usePreviewChat = (idKey: string) => {
|
||||||
|
|
||||||
const open = useCallback(
|
const open = useCallback(
|
||||||
(t: string) => {
|
(t: string) => {
|
||||||
window.open(
|
window.open(getUrlWithToken(t, SharedFrom.Chat), '_blank');
|
||||||
getUrlWithToken(
|
|
||||||
t,
|
|
||||||
idKey === 'canvasId' ? SharedFrom.Agent : SharedFrom.Chat,
|
|
||||||
),
|
|
||||||
'_blank',
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
[idKey],
|
[idKey],
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import i18n from '@/locales/config';
|
|
||||||
import { BeginId } from '@/pages/flow/constant';
|
|
||||||
import { DecoratorNode, LexicalNode, NodeKey } from 'lexical';
|
import { DecoratorNode, LexicalNode, NodeKey } from 'lexical';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
const prefix = BeginId + '@';
|
|
||||||
|
|
||||||
export class VariableNode extends DecoratorNode<ReactNode> {
|
export class VariableNode extends DecoratorNode<ReactNode> {
|
||||||
__value: string;
|
__value: string;
|
||||||
|
@ -37,13 +34,6 @@ export class VariableNode extends DecoratorNode<ReactNode> {
|
||||||
let content: ReactNode = (
|
let content: ReactNode = (
|
||||||
<span className="text-blue-600">{this.__label}</span>
|
<span className="text-blue-600">{this.__label}</span>
|
||||||
);
|
);
|
||||||
if (this.__value.startsWith(prefix)) {
|
|
||||||
content = (
|
|
||||||
<div>
|
|
||||||
<span>{i18n.t(`flow.begin`)}</span> / {content}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-200 dark:bg-gray-400 text-primary inline-flex items-center rounded-md px-2 py-0">
|
<div className="bg-gray-200 dark:bg-gray-400 text-primary inline-flex items-center rounded-md px-2 py-0">
|
||||||
{content}
|
{content}
|
||||||
|
|
|
@ -29,8 +29,6 @@ import {
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import * as ReactDOM from 'react-dom';
|
import * as ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import { FlowFormContext } from '@/pages/flow/context';
|
|
||||||
import { useBuildComponentIdSelectOptions } from '@/pages/flow/hooks/use-get-begin-query';
|
|
||||||
import { $createVariableNode } from './variable-node';
|
import { $createVariableNode } from './variable-node';
|
||||||
|
|
||||||
import { ProgrammaticTag } from './constant';
|
import { ProgrammaticTag } from './constant';
|
||||||
|
|
|
@ -20,7 +20,6 @@ export const variableEnabledFieldMap = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum SharedFrom {
|
export enum SharedFrom {
|
||||||
Agent = 'agent',
|
|
||||||
Chat = 'chat',
|
Chat = 'chat',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,308 +0,0 @@
|
||||||
import { ResponseType } from '@/interfaces/database/base';
|
|
||||||
import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow';
|
|
||||||
import { IDebugSingleRequestBody } from '@/interfaces/request/flow';
|
|
||||||
import i18n from '@/locales/config';
|
|
||||||
import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks';
|
|
||||||
import { BeginId } from '@/pages/flow/constant';
|
|
||||||
import flowService from '@/services/flow-service';
|
|
||||||
import { buildMessageListWithUuid } from '@/utils/chat';
|
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
||||||
import { message } from 'antd';
|
|
||||||
import { set } from 'lodash';
|
|
||||||
import get from 'lodash/get';
|
|
||||||
import { useParams } from 'umi';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
export const EmptyDsl = {
|
|
||||||
graph: {
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
id: BeginId,
|
|
||||||
type: 'beginNode',
|
|
||||||
position: {
|
|
||||||
x: 50,
|
|
||||||
y: 200,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
label: 'Begin',
|
|
||||||
name: 'begin',
|
|
||||||
},
|
|
||||||
sourcePosition: 'left',
|
|
||||||
targetPosition: 'right',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
edges: [],
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
begin: {
|
|
||||||
obj: {
|
|
||||||
component_name: 'Begin',
|
|
||||||
params: {},
|
|
||||||
},
|
|
||||||
downstream: ['Answer:China'], // other edge target is downstream, edge source is current node id
|
|
||||||
upstream: [], // edge source is upstream, edge target is current node id
|
|
||||||
},
|
|
||||||
},
|
|
||||||
messages: [],
|
|
||||||
reference: [],
|
|
||||||
history: [],
|
|
||||||
path: [],
|
|
||||||
answer: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFetchFlowTemplates = (): ResponseType<IFlowTemplate[]> => {
|
|
||||||
const { data } = useQuery({
|
|
||||||
queryKey: ['fetchFlowTemplates'],
|
|
||||||
initialData: [],
|
|
||||||
queryFn: async () => {
|
|
||||||
const { data } = await flowService.listTemplates();
|
|
||||||
if (Array.isArray(data?.data)) {
|
|
||||||
data.data.unshift({
|
|
||||||
id: uuid(),
|
|
||||||
title: 'Blank',
|
|
||||||
description: 'Create your agent from scratch',
|
|
||||||
dsl: EmptyDsl,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFetchFlowList = (): { data: IFlow[]; loading: boolean } => {
|
|
||||||
const { data, isFetching: loading } = useQuery({
|
|
||||||
queryKey: ['fetchFlowList'],
|
|
||||||
initialData: [],
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async () => {
|
|
||||||
const { data } = await flowService.listCanvas();
|
|
||||||
|
|
||||||
return data?.data ?? [];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFetchFlow = (): {
|
|
||||||
data: IFlow;
|
|
||||||
loading: boolean;
|
|
||||||
refetch: () => void;
|
|
||||||
} => {
|
|
||||||
const { id } = useParams();
|
|
||||||
const { sharedId } = useGetSharedChatSearchParams();
|
|
||||||
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
isFetching: loading,
|
|
||||||
refetch,
|
|
||||||
} = useQuery({
|
|
||||||
queryKey: ['flowDetail'],
|
|
||||||
initialData: {} as IFlow,
|
|
||||||
refetchOnReconnect: false,
|
|
||||||
refetchOnMount: false,
|
|
||||||
refetchOnWindowFocus: false,
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async () => {
|
|
||||||
const { data } = await flowService.getCanvas({}, sharedId || id);
|
|
||||||
|
|
||||||
const messageList = buildMessageListWithUuid(
|
|
||||||
get(data, 'data.dsl.messages', []),
|
|
||||||
);
|
|
||||||
set(data, 'data.dsl.messages', messageList);
|
|
||||||
|
|
||||||
return data?.data ?? {};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading, refetch };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFetchFlowSSE = (): {
|
|
||||||
data: IFlow;
|
|
||||||
loading: boolean;
|
|
||||||
refetch: () => void;
|
|
||||||
} => {
|
|
||||||
const { sharedId } = useGetSharedChatSearchParams();
|
|
||||||
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
isFetching: loading,
|
|
||||||
refetch,
|
|
||||||
} = useQuery({
|
|
||||||
queryKey: ['flowDetailSSE'],
|
|
||||||
initialData: {} as IFlow,
|
|
||||||
refetchOnReconnect: false,
|
|
||||||
refetchOnMount: false,
|
|
||||||
refetchOnWindowFocus: false,
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async () => {
|
|
||||||
if (!sharedId) return {};
|
|
||||||
const { data } = await flowService.getCanvasSSE({}, sharedId);
|
|
||||||
|
|
||||||
const messageList = buildMessageListWithUuid(
|
|
||||||
get(data, 'data.dsl.messages', []),
|
|
||||||
);
|
|
||||||
set(data, 'data.dsl.messages', messageList);
|
|
||||||
|
|
||||||
return data?.data ?? {};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading, refetch };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useSetFlow = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
isPending: loading,
|
|
||||||
mutateAsync,
|
|
||||||
} = useMutation({
|
|
||||||
mutationKey: ['setFlow'],
|
|
||||||
mutationFn: async (params: {
|
|
||||||
id?: string;
|
|
||||||
title?: string;
|
|
||||||
dsl?: DSL;
|
|
||||||
avatar?: string;
|
|
||||||
}) => {
|
|
||||||
const { data = {} } = await flowService.setCanvas(params);
|
|
||||||
if (data.code === 0) {
|
|
||||||
message.success(
|
|
||||||
i18n.t(`message.${params?.id ? 'modified' : 'created'}`),
|
|
||||||
);
|
|
||||||
queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] });
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading, setFlow: mutateAsync };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDeleteFlow = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
isPending: loading,
|
|
||||||
mutateAsync,
|
|
||||||
} = useMutation({
|
|
||||||
mutationKey: ['deleteFlow'],
|
|
||||||
mutationFn: async (canvasIds: string[]) => {
|
|
||||||
const { data } = await flowService.removeCanvas({ canvasIds });
|
|
||||||
if (data.code === 0) {
|
|
||||||
queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] });
|
|
||||||
}
|
|
||||||
return data?.data ?? [];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading, deleteFlow: mutateAsync };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRunFlow = () => {
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
isPending: loading,
|
|
||||||
mutateAsync,
|
|
||||||
} = useMutation({
|
|
||||||
mutationKey: ['runFlow'],
|
|
||||||
mutationFn: async (params: { id: string; dsl: DSL }) => {
|
|
||||||
const { data } = await flowService.runCanvas(params);
|
|
||||||
if (data.code === 0) {
|
|
||||||
message.success(i18n.t(`message.modified`));
|
|
||||||
}
|
|
||||||
return data?.data ?? {};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading, runFlow: mutateAsync };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useResetFlow = () => {
|
|
||||||
const { id } = useParams();
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
isPending: loading,
|
|
||||||
mutateAsync,
|
|
||||||
} = useMutation({
|
|
||||||
mutationKey: ['resetFlow'],
|
|
||||||
mutationFn: async () => {
|
|
||||||
const { data } = await flowService.resetCanvas({ id });
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading, resetFlow: mutateAsync };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useTestDbConnect = () => {
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
isPending: loading,
|
|
||||||
mutateAsync,
|
|
||||||
} = useMutation({
|
|
||||||
mutationKey: ['testDbConnect'],
|
|
||||||
mutationFn: async (params: any) => {
|
|
||||||
const ret = await flowService.testDbConnect(params);
|
|
||||||
if (ret?.data?.code === 0) {
|
|
||||||
message.success(ret?.data?.data);
|
|
||||||
} else {
|
|
||||||
message.error(ret?.data?.data);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading, testDbConnect: mutateAsync };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFetchInputElements = (componentId?: string) => {
|
|
||||||
const { id } = useParams();
|
|
||||||
|
|
||||||
const { data, isPending: loading } = useQuery({
|
|
||||||
queryKey: ['fetchInputElements', id, componentId],
|
|
||||||
initialData: [],
|
|
||||||
enabled: !!id && !!componentId,
|
|
||||||
retryOnMount: false,
|
|
||||||
refetchOnWindowFocus: false,
|
|
||||||
refetchOnReconnect: false,
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async () => {
|
|
||||||
try {
|
|
||||||
const { data } = await flowService.getInputElements({
|
|
||||||
id,
|
|
||||||
component_id: componentId,
|
|
||||||
});
|
|
||||||
return data?.data ?? [];
|
|
||||||
} catch (error) {
|
|
||||||
console.log('🚀 ~ queryFn: ~ error:', error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDebugSingle = () => {
|
|
||||||
const { id } = useParams();
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
isPending: loading,
|
|
||||||
mutateAsync,
|
|
||||||
} = useMutation({
|
|
||||||
mutationKey: ['debugSingle'],
|
|
||||||
mutationFn: async (params: IDebugSingleRequestBody) => {
|
|
||||||
const ret = await flowService.debugSingle({ id, ...params });
|
|
||||||
if (ret?.data?.code !== 0) {
|
|
||||||
message.error(ret?.data?.message);
|
|
||||||
}
|
|
||||||
return ret?.data?.data;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, loading, debugSingle: mutateAsync };
|
|
||||||
};
|
|
|
@ -38,21 +38,6 @@ export const useNavigatePage = () => {
|
||||||
navigate(Routes.Chat);
|
navigate(Routes.Chat);
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
const navigateToAgentList = useCallback(() => {
|
|
||||||
navigate(Routes.Agents);
|
|
||||||
}, [navigate]);
|
|
||||||
|
|
||||||
const navigateToAgent = useCallback(
|
|
||||||
(id: string) => () => {
|
|
||||||
navigate(`${Routes.Agent}/${id}`);
|
|
||||||
},
|
|
||||||
[navigate],
|
|
||||||
);
|
|
||||||
|
|
||||||
const navigateToAgentTemplates = useCallback(() => {
|
|
||||||
navigate(Routes.AgentTemplates);
|
|
||||||
}, [navigate]);
|
|
||||||
|
|
||||||
const navigateToSearchList = useCallback(() => {
|
const navigateToSearchList = useCallback(() => {
|
||||||
navigate(Routes.Searches);
|
navigate(Routes.Searches);
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
@ -104,9 +89,7 @@ export const useNavigatePage = () => {
|
||||||
navigateToChunkParsedResult,
|
navigateToChunkParsedResult,
|
||||||
getQueryString,
|
getQueryString,
|
||||||
navigateToChunk,
|
navigateToChunk,
|
||||||
navigateToAgentList,
|
|
||||||
navigateToAgent,
|
|
||||||
navigateToAgentTemplates,
|
|
||||||
navigateToSearchList,
|
navigateToSearchList,
|
||||||
navigateToSearch,
|
navigateToSearch,
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { cn } from '@/lib/utils';
|
||||||
import { Routes } from '@/routes';
|
import { Routes } from '@/routes';
|
||||||
import {
|
import {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
Cpu,
|
|
||||||
File,
|
File,
|
||||||
Github,
|
Github,
|
||||||
House,
|
House,
|
||||||
|
@ -33,7 +32,6 @@ export function Header() {
|
||||||
{ path: Routes.Datasets, name: t('knowledgeBase'), icon: Library },
|
{ path: Routes.Datasets, name: t('knowledgeBase'), icon: Library },
|
||||||
{ path: Routes.Chats, name: t('chat'), icon: MessageSquareText },
|
{ path: Routes.Chats, name: t('chat'), icon: MessageSquareText },
|
||||||
{ path: Routes.Searches, name: t('search'), icon: Search },
|
{ path: Routes.Searches, name: t('search'), icon: Search },
|
||||||
{ path: Routes.Agents, name: t('flow'), icon: Cpu },
|
|
||||||
{ path: Routes.Files, name: t('fileManager'), icon: File },
|
{ path: Routes.Files, name: t('fileManager'), icon: File },
|
||||||
],
|
],
|
||||||
[t],
|
[t],
|
||||||
|
|
|
@ -3,14 +3,8 @@ import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
import { initReactI18next } from 'react-i18next';
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
|
||||||
import { LanguageAbbreviation } from '@/constants/common';
|
import { LanguageAbbreviation } from '@/constants/common';
|
||||||
import translation_de from './de';
|
|
||||||
import translation_en from './en';
|
import translation_en from './en';
|
||||||
import translation_es from './es';
|
|
||||||
import translation_id from './id';
|
|
||||||
import translation_ja from './ja';
|
|
||||||
import translation_pt_br from './pt-br';
|
|
||||||
import { createTranslationTable, flattenObject } from './until';
|
import { createTranslationTable, flattenObject } from './until';
|
||||||
import translation_vi from './vi';
|
|
||||||
import translation_zh from './zh';
|
import translation_zh from './zh';
|
||||||
import translation_zh_traditional from './zh-traditional';
|
import translation_zh_traditional from './zh-traditional';
|
||||||
|
|
||||||
|
@ -18,42 +12,16 @@ const resources = {
|
||||||
[LanguageAbbreviation.En]: translation_en,
|
[LanguageAbbreviation.En]: translation_en,
|
||||||
[LanguageAbbreviation.Zh]: translation_zh,
|
[LanguageAbbreviation.Zh]: translation_zh,
|
||||||
[LanguageAbbreviation.ZhTraditional]: translation_zh_traditional,
|
[LanguageAbbreviation.ZhTraditional]: translation_zh_traditional,
|
||||||
[LanguageAbbreviation.Id]: translation_id,
|
|
||||||
[LanguageAbbreviation.Ja]: translation_ja,
|
|
||||||
[LanguageAbbreviation.Es]: translation_es,
|
|
||||||
[LanguageAbbreviation.Vi]: translation_vi,
|
|
||||||
[LanguageAbbreviation.PtBr]: translation_pt_br,
|
|
||||||
[LanguageAbbreviation.De]: translation_de,
|
|
||||||
};
|
};
|
||||||
const enFlattened = flattenObject(translation_en);
|
const enFlattened = flattenObject(translation_en);
|
||||||
const viFlattened = flattenObject(translation_vi);
|
|
||||||
const esFlattened = flattenObject(translation_es);
|
|
||||||
const zhFlattened = flattenObject(translation_zh);
|
const zhFlattened = flattenObject(translation_zh);
|
||||||
const jaFlattened = flattenObject(translation_ja);
|
|
||||||
const pt_brFlattened = flattenObject(translation_pt_br);
|
|
||||||
const zh_traditionalFlattened = flattenObject(translation_zh_traditional);
|
const zh_traditionalFlattened = flattenObject(translation_zh_traditional);
|
||||||
const deFlattened = flattenObject(translation_de);
|
|
||||||
export const translationTable = createTranslationTable(
|
export const translationTable = createTranslationTable(
|
||||||
[
|
[enFlattened, zhFlattened, zh_traditionalFlattened],
|
||||||
enFlattened,
|
['English', 'zh', 'zh-TRADITIONAL'],
|
||||||
viFlattened,
|
|
||||||
esFlattened,
|
|
||||||
zhFlattened,
|
|
||||||
zh_traditionalFlattened,
|
|
||||||
jaFlattened,
|
|
||||||
pt_brFlattened,
|
|
||||||
deFlattened,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'English',
|
|
||||||
'Vietnamese',
|
|
||||||
'Spanish',
|
|
||||||
'zh',
|
|
||||||
'zh-TRADITIONAL',
|
|
||||||
'ja',
|
|
||||||
'pt-BR',
|
|
||||||
'Deutsch',
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
i18n
|
i18n
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
import { SideDown } from '@/assets/icon/Icon';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
|
||||||
import {
|
|
||||||
Collapsible,
|
|
||||||
CollapsibleContent,
|
|
||||||
CollapsibleTrigger,
|
|
||||||
} from '@/components/ui/collapsible';
|
|
||||||
import {
|
|
||||||
Sidebar,
|
|
||||||
SidebarContent,
|
|
||||||
SidebarGroup,
|
|
||||||
SidebarGroupContent,
|
|
||||||
SidebarGroupLabel,
|
|
||||||
SidebarHeader,
|
|
||||||
SidebarMenu,
|
|
||||||
} from '@/components/ui/sidebar';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
AgentOperatorList,
|
|
||||||
Operator,
|
|
||||||
componentMenuList,
|
|
||||||
operatorMap,
|
|
||||||
} from './constant';
|
|
||||||
import OperatorIcon from './operator-icon';
|
|
||||||
|
|
||||||
type OperatorItem = {
|
|
||||||
name: Operator;
|
|
||||||
};
|
|
||||||
|
|
||||||
function OperatorCard({ name }: OperatorItem) {
|
|
||||||
return (
|
|
||||||
<Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard">
|
|
||||||
<CardContent className="p-2 flex items-center gap-2">
|
|
||||||
<OperatorIcon
|
|
||||||
name={name}
|
|
||||||
color={operatorMap[name].color}
|
|
||||||
></OperatorIcon>
|
|
||||||
{name}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
type OperatorCollapsibleProps = { operatorList: OperatorItem[]; title: string };
|
|
||||||
|
|
||||||
function OperatorCollapsible({
|
|
||||||
operatorList,
|
|
||||||
title,
|
|
||||||
}: OperatorCollapsibleProps) {
|
|
||||||
return (
|
|
||||||
<Collapsible defaultOpen className="group/collapsible">
|
|
||||||
<SidebarGroup>
|
|
||||||
<SidebarGroupLabel asChild className="mb-1">
|
|
||||||
<CollapsibleTrigger>
|
|
||||||
<span className="font-bold text-base">{title}</span>
|
|
||||||
<SideDown className="ml-auto" />
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
</SidebarGroupLabel>
|
|
||||||
<CollapsibleContent className="px-2">
|
|
||||||
<SidebarGroupContent>
|
|
||||||
<SidebarMenu className="gap-2">
|
|
||||||
{operatorList.map((item) => (
|
|
||||||
<OperatorCard key={item.name} name={item.name}></OperatorCard>
|
|
||||||
))}
|
|
||||||
</SidebarMenu>
|
|
||||||
</SidebarGroupContent>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</SidebarGroup>
|
|
||||||
</Collapsible>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AgentSidebar() {
|
|
||||||
const agentOperatorList = useMemo(() => {
|
|
||||||
return componentMenuList.filter((x) =>
|
|
||||||
AgentOperatorList.some((y) => y === x.name),
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const thirdOperatorList = useMemo(() => {
|
|
||||||
return componentMenuList.filter(
|
|
||||||
(x) => !AgentOperatorList.some((y) => y === x.name),
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Sidebar variant={'floating'} className="top-16">
|
|
||||||
<SidebarHeader>
|
|
||||||
<p className="font-bold text-2xl">All nodes</p>
|
|
||||||
</SidebarHeader>
|
|
||||||
<SidebarContent>
|
|
||||||
<OperatorCollapsible
|
|
||||||
title="Agent operator"
|
|
||||||
operatorList={agentOperatorList}
|
|
||||||
></OperatorCollapsible>
|
|
||||||
<OperatorCollapsible
|
|
||||||
title="Third-party tools"
|
|
||||||
operatorList={thirdOperatorList}
|
|
||||||
></OperatorCollapsible>
|
|
||||||
</SidebarContent>
|
|
||||||
</Sidebar>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
.contextMenu {
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
border-style: solid;
|
|
||||||
box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%);
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10;
|
|
||||||
button {
|
|
||||||
border: none;
|
|
||||||
display: block;
|
|
||||||
padding: 0.5em;
|
|
||||||
text-align: left;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
import { NodeMouseHandler, useReactFlow } from '@xyflow/react';
|
|
||||||
import { useCallback, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
export interface INodeContextMenu {
|
|
||||||
id: string;
|
|
||||||
top: number;
|
|
||||||
left: number;
|
|
||||||
right?: number;
|
|
||||||
bottom?: number;
|
|
||||||
[key: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NodeContextMenu({
|
|
||||||
id,
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
bottom,
|
|
||||||
...props
|
|
||||||
}: INodeContextMenu) {
|
|
||||||
const { getNode, setNodes, addNodes, setEdges } = useReactFlow();
|
|
||||||
|
|
||||||
const duplicateNode = useCallback(() => {
|
|
||||||
const node = getNode(id);
|
|
||||||
const position = {
|
|
||||||
x: node?.position?.x || 0 + 50,
|
|
||||||
y: node?.position?.y || 0 + 50,
|
|
||||||
};
|
|
||||||
|
|
||||||
addNodes({
|
|
||||||
...(node || {}),
|
|
||||||
data: node?.data,
|
|
||||||
selected: false,
|
|
||||||
dragging: false,
|
|
||||||
id: `${node?.id}-copy`,
|
|
||||||
position,
|
|
||||||
});
|
|
||||||
}, [id, getNode, addNodes]);
|
|
||||||
|
|
||||||
const deleteNode = useCallback(() => {
|
|
||||||
setNodes((nodes) => nodes.filter((node) => node.id !== id));
|
|
||||||
setEdges((edges) => edges.filter((edge) => edge.source !== id));
|
|
||||||
}, [id, setNodes, setEdges]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{ top, left, right, bottom }}
|
|
||||||
className={styles.contextMenu}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<p style={{ margin: '0.5em' }}>
|
|
||||||
<small>node: {id}</small>
|
|
||||||
</p>
|
|
||||||
<button onClick={duplicateNode} type={'button'}>
|
|
||||||
duplicate
|
|
||||||
</button>
|
|
||||||
<button onClick={deleteNode} type={'button'}>
|
|
||||||
delete
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @deprecated
|
|
||||||
*/
|
|
||||||
export const useHandleNodeContextMenu = (sideWidth: number) => {
|
|
||||||
const [menu, setMenu] = useState<INodeContextMenu>({} as INodeContextMenu);
|
|
||||||
const ref = useRef<any>(null);
|
|
||||||
|
|
||||||
const onNodeContextMenu: NodeMouseHandler = useCallback(
|
|
||||||
(event, node) => {
|
|
||||||
// Prevent native context menu from showing
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
// Calculate position of the context menu. We want to make sure it
|
|
||||||
// doesn't get positioned off-screen.
|
|
||||||
const pane = ref.current?.getBoundingClientRect();
|
|
||||||
// setMenu({
|
|
||||||
// id: node.id,
|
|
||||||
// top: event.clientY < pane.height - 200 ? event.clientY : 0,
|
|
||||||
// left: event.clientX < pane.width - 200 ? event.clientX : 0,
|
|
||||||
// right: event.clientX >= pane.width - 200 ? pane.width - event.clientX : 0,
|
|
||||||
// bottom:
|
|
||||||
// event.clientY >= pane.height - 200 ? pane.height - event.clientY : 0,
|
|
||||||
// });
|
|
||||||
|
|
||||||
setMenu({
|
|
||||||
id: node.id,
|
|
||||||
top: event.clientY - 144,
|
|
||||||
left: event.clientX - sideWidth,
|
|
||||||
// top: event.clientY < pane.height - 200 ? event.clientY - 72 : 0,
|
|
||||||
// left: event.clientX < pane.width - 200 ? event.clientX : 0,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[sideWidth],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Close the context menu if it's open whenever the window is clicked.
|
|
||||||
const onPaneClick = useCallback(
|
|
||||||
() => setMenu({} as INodeContextMenu),
|
|
||||||
[setMenu],
|
|
||||||
);
|
|
||||||
|
|
||||||
return { onNodeContextMenu, menu, onPaneClick, ref };
|
|
||||||
};
|
|
|
@ -1,31 +0,0 @@
|
||||||
.edgeButton {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
background: #eee;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 50%;
|
|
||||||
font-size: 10px;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edgeButton:hover {
|
|
||||||
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.edgeButtonDark {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
background: #0e0c0c;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 50%;
|
|
||||||
font-size: 10px;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edgeButtonDark:hover {
|
|
||||||
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
import {
|
|
||||||
BaseEdge,
|
|
||||||
EdgeLabelRenderer,
|
|
||||||
EdgeProps,
|
|
||||||
getBezierPath,
|
|
||||||
} from '@xyflow/react';
|
|
||||||
import useGraphStore from '../../store';
|
|
||||||
|
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { useFetchFlow } from '@/hooks/flow-hooks';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
export function ButtonEdge({
|
|
||||||
id,
|
|
||||||
sourceX,
|
|
||||||
sourceY,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
sourcePosition,
|
|
||||||
targetPosition,
|
|
||||||
source,
|
|
||||||
target,
|
|
||||||
style = {},
|
|
||||||
markerEnd,
|
|
||||||
selected,
|
|
||||||
}: EdgeProps) {
|
|
||||||
const deleteEdgeById = useGraphStore((state) => state.deleteEdgeById);
|
|
||||||
const [edgePath, labelX, labelY] = getBezierPath({
|
|
||||||
sourceX,
|
|
||||||
sourceY,
|
|
||||||
sourcePosition,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
targetPosition,
|
|
||||||
});
|
|
||||||
const { theme } = useTheme();
|
|
||||||
const selectedStyle = useMemo(() => {
|
|
||||||
return selected ? { strokeWidth: 2, stroke: '#1677ff' } : {};
|
|
||||||
}, [selected]);
|
|
||||||
|
|
||||||
const onEdgeClick = () => {
|
|
||||||
deleteEdgeById(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
// highlight the nodes that the workflow passes through
|
|
||||||
const { data: flowDetail } = useFetchFlow();
|
|
||||||
|
|
||||||
const graphPath = useMemo(() => {
|
|
||||||
// TODO: this will be called multiple times
|
|
||||||
const path = flowDetail?.dsl?.path ?? [];
|
|
||||||
// The second to last
|
|
||||||
const previousGraphPath: string[] = path.at(-2) ?? [];
|
|
||||||
let graphPath: string[] = path.at(-1) ?? [];
|
|
||||||
// The last of the second to last article
|
|
||||||
const previousLatestElement = previousGraphPath.at(-1);
|
|
||||||
if (previousGraphPath.length > 0 && previousLatestElement) {
|
|
||||||
graphPath = [previousLatestElement, ...graphPath];
|
|
||||||
}
|
|
||||||
return graphPath;
|
|
||||||
}, [flowDetail.dsl?.path]);
|
|
||||||
|
|
||||||
const highlightStyle = useMemo(() => {
|
|
||||||
const idx = graphPath.findIndex((x) => x === source);
|
|
||||||
if (idx !== -1) {
|
|
||||||
// The set of elements following source
|
|
||||||
const slicedGraphPath = graphPath.slice(idx + 1);
|
|
||||||
if (slicedGraphPath.some((x) => x === target)) {
|
|
||||||
return { strokeWidth: 2, stroke: 'red' };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}, [source, target, graphPath]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<BaseEdge
|
|
||||||
path={edgePath}
|
|
||||||
markerEnd={markerEnd}
|
|
||||||
style={{ ...style, ...selectedStyle, ...highlightStyle }}
|
|
||||||
/>
|
|
||||||
<EdgeLabelRenderer>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
|
||||||
fontSize: 12,
|
|
||||||
// everything inside EdgeLabelRenderer has no pointer events by default
|
|
||||||
// if you have an interactive element, set pointer-events: all
|
|
||||||
pointerEvents: 'all',
|
|
||||||
zIndex: 1001, // https://github.com/xyflow/xyflow/discussions/3498
|
|
||||||
}}
|
|
||||||
className="nodrag nopan"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className={
|
|
||||||
theme === 'dark' ? styles.edgeButtonDark : styles.edgeButton
|
|
||||||
}
|
|
||||||
type="button"
|
|
||||||
onClick={onEdgeClick}
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</EdgeLabelRenderer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
.canvasWrapper {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
:global(.react-flow__node-group) {
|
|
||||||
.commonNode();
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
import {
|
|
||||||
Background,
|
|
||||||
ConnectionMode,
|
|
||||||
NodeTypes,
|
|
||||||
ReactFlow,
|
|
||||||
} from '@xyflow/react';
|
|
||||||
import '@xyflow/react/dist/style.css';
|
|
||||||
// import ChatDrawer from '../chat/drawer';
|
|
||||||
import FormSheet from '../form-sheet/next';
|
|
||||||
import {
|
|
||||||
useHandleDrop,
|
|
||||||
useSelectCanvasData,
|
|
||||||
useValidateConnection,
|
|
||||||
useWatchNodeFormDataChange,
|
|
||||||
} from '../hooks';
|
|
||||||
import { useBeforeDelete } from '../hooks/use-before-delete';
|
|
||||||
import { useShowDrawer } from '../hooks/use-show-drawer';
|
|
||||||
// import RunDrawer from '../run-drawer';
|
|
||||||
import { ButtonEdge } from './edge';
|
|
||||||
import styles from './index.less';
|
|
||||||
import { RagNode } from './node';
|
|
||||||
import { BeginNode } from './node/begin-node';
|
|
||||||
import { CategorizeNode } from './node/categorize-node';
|
|
||||||
import { EmailNode } from './node/email-node';
|
|
||||||
import { GenerateNode } from './node/generate-node';
|
|
||||||
import { InvokeNode } from './node/invoke-node';
|
|
||||||
import { IterationNode, IterationStartNode } from './node/iteration-node';
|
|
||||||
import { KeywordNode } from './node/keyword-node';
|
|
||||||
import { LogicNode } from './node/logic-node';
|
|
||||||
import { MessageNode } from './node/message-node';
|
|
||||||
import NoteNode from './node/note-node';
|
|
||||||
import { RelevantNode } from './node/relevant-node';
|
|
||||||
import { RetrievalNode } from './node/retrieval-node';
|
|
||||||
import { RewriteNode } from './node/rewrite-node';
|
|
||||||
import { SwitchNode } from './node/switch-node';
|
|
||||||
import { TemplateNode } from './node/template-node';
|
|
||||||
|
|
||||||
const nodeTypes: NodeTypes = {
|
|
||||||
ragNode: RagNode,
|
|
||||||
categorizeNode: CategorizeNode,
|
|
||||||
beginNode: BeginNode,
|
|
||||||
relevantNode: RelevantNode,
|
|
||||||
logicNode: LogicNode,
|
|
||||||
noteNode: NoteNode,
|
|
||||||
switchNode: SwitchNode,
|
|
||||||
generateNode: GenerateNode,
|
|
||||||
retrievalNode: RetrievalNode,
|
|
||||||
messageNode: MessageNode,
|
|
||||||
rewriteNode: RewriteNode,
|
|
||||||
keywordNode: KeywordNode,
|
|
||||||
invokeNode: InvokeNode,
|
|
||||||
templateNode: TemplateNode,
|
|
||||||
emailNode: EmailNode,
|
|
||||||
group: IterationNode,
|
|
||||||
iterationStartNode: IterationStartNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
const edgeTypes = {
|
|
||||||
buttonEdge: ButtonEdge,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
drawerVisible: boolean;
|
|
||||||
hideDrawer(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|
||||||
const {
|
|
||||||
nodes,
|
|
||||||
edges,
|
|
||||||
onConnect,
|
|
||||||
onEdgesChange,
|
|
||||||
onNodesChange,
|
|
||||||
onSelectionChange,
|
|
||||||
} = useSelectCanvasData();
|
|
||||||
const isValidConnection = useValidateConnection();
|
|
||||||
|
|
||||||
const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop();
|
|
||||||
|
|
||||||
const {
|
|
||||||
onNodeClick,
|
|
||||||
onPaneClick,
|
|
||||||
clickedNode,
|
|
||||||
formDrawerVisible,
|
|
||||||
hideFormDrawer,
|
|
||||||
singleDebugDrawerVisible,
|
|
||||||
hideSingleDebugDrawer,
|
|
||||||
showSingleDebugDrawer,
|
|
||||||
chatVisible,
|
|
||||||
runVisible,
|
|
||||||
hideRunOrChatDrawer,
|
|
||||||
showChatModal,
|
|
||||||
} = useShowDrawer({
|
|
||||||
drawerVisible,
|
|
||||||
hideDrawer,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { handleBeforeDelete } = useBeforeDelete();
|
|
||||||
|
|
||||||
useWatchNodeFormDataChange();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.canvasWrapper}>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
style={{ position: 'absolute', top: 10, left: 0 }}
|
|
||||||
>
|
|
||||||
<defs>
|
|
||||||
<marker
|
|
||||||
fill="rgb(157 149 225)"
|
|
||||||
id="logo"
|
|
||||||
viewBox="0 0 40 40"
|
|
||||||
refX="8"
|
|
||||||
refY="5"
|
|
||||||
markerUnits="strokeWidth"
|
|
||||||
markerWidth="20"
|
|
||||||
markerHeight="20"
|
|
||||||
orient="auto-start-reverse"
|
|
||||||
>
|
|
||||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
<ReactFlow
|
|
||||||
connectionMode={ConnectionMode.Loose}
|
|
||||||
nodes={nodes}
|
|
||||||
onNodesChange={onNodesChange}
|
|
||||||
edges={edges}
|
|
||||||
onEdgesChange={onEdgesChange}
|
|
||||||
fitView
|
|
||||||
onConnect={onConnect}
|
|
||||||
nodeTypes={nodeTypes}
|
|
||||||
edgeTypes={edgeTypes}
|
|
||||||
onDrop={onDrop}
|
|
||||||
onDragOver={onDragOver}
|
|
||||||
onNodeClick={onNodeClick}
|
|
||||||
onPaneClick={onPaneClick}
|
|
||||||
onInit={setReactFlowInstance}
|
|
||||||
onSelectionChange={onSelectionChange}
|
|
||||||
nodeOrigin={[0.5, 0]}
|
|
||||||
isValidConnection={isValidConnection}
|
|
||||||
defaultEdgeOptions={{
|
|
||||||
type: 'buttonEdge',
|
|
||||||
markerEnd: 'logo',
|
|
||||||
style: {
|
|
||||||
strokeWidth: 2,
|
|
||||||
stroke: 'rgb(202 197 245)',
|
|
||||||
},
|
|
||||||
zIndex: 1001, // https://github.com/xyflow/xyflow/discussions/3498
|
|
||||||
}}
|
|
||||||
deleteKeyCode={['Delete', 'Backspace']}
|
|
||||||
onBeforeDelete={handleBeforeDelete}
|
|
||||||
>
|
|
||||||
<Background />
|
|
||||||
</ReactFlow>
|
|
||||||
{formDrawerVisible && (
|
|
||||||
<FormSheet
|
|
||||||
node={clickedNode}
|
|
||||||
visible={formDrawerVisible}
|
|
||||||
hideModal={hideFormDrawer}
|
|
||||||
singleDebugDrawerVisible={singleDebugDrawerVisible}
|
|
||||||
hideSingleDebugDrawer={hideSingleDebugDrawer}
|
|
||||||
showSingleDebugDrawer={showSingleDebugDrawer}
|
|
||||||
></FormSheet>
|
|
||||||
)}
|
|
||||||
{/* {chatVisible && (
|
|
||||||
<ChatDrawer
|
|
||||||
visible={chatVisible}
|
|
||||||
hideModal={hideRunOrChatDrawer}
|
|
||||||
></ChatDrawer>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{runVisible && (
|
|
||||||
<RunDrawer
|
|
||||||
hideModal={hideRunOrChatDrawer}
|
|
||||||
showModal={showChatModal}
|
|
||||||
></RunDrawer>
|
|
||||||
)} */}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FlowCanvas;
|
|
|
@ -1,72 +0,0 @@
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { IBeginNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import get from 'lodash/get';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import {
|
|
||||||
BeginQueryType,
|
|
||||||
BeginQueryTypeIconMap,
|
|
||||||
Operator,
|
|
||||||
operatorMap,
|
|
||||||
} from '../../constant';
|
|
||||||
import { BeginQuery } from '../../interface';
|
|
||||||
import OperatorIcon from '../../operator-icon';
|
|
||||||
import { RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
// TODO: do not allow other nodes to connect to this node
|
|
||||||
export function BeginNode({ selected, data }: NodeProps<IBeginNode>) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const query: BeginQuery[] = get(data, 'form.query', []);
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.ragNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
|
|
||||||
<Flex align="center" justify={'center'} gap={10}>
|
|
||||||
<OperatorIcon
|
|
||||||
name={data.label as Operator}
|
|
||||||
fontSize={24}
|
|
||||||
color={operatorMap[data.label as Operator].color}
|
|
||||||
></OperatorIcon>
|
|
||||||
<div className="truncate text-center font-semibold text-sm">
|
|
||||||
{t(`flow.begin`)}
|
|
||||||
</div>
|
|
||||||
</Flex>
|
|
||||||
<Flex gap={8} vertical className={styles.generateParameters}>
|
|
||||||
{query.map((x, idx) => {
|
|
||||||
const Icon = BeginQueryTypeIconMap[x.type as BeginQueryType];
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
key={idx}
|
|
||||||
align="center"
|
|
||||||
gap={6}
|
|
||||||
className={styles.conditionBlock}
|
|
||||||
>
|
|
||||||
<Icon className="size-4" />
|
|
||||||
<label htmlFor="">{x.key}</label>
|
|
||||||
<span className={styles.parameterValue}>{x.name}</span>
|
|
||||||
<span className="flex-1">{x.optional ? 'Yes' : 'No'}</span>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from '@/components/ui/card';
|
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { Label } from '@/components/ui/label';
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@/components/ui/select';
|
|
||||||
|
|
||||||
export function CardWithForm() {
|
|
||||||
return (
|
|
||||||
<Card className="w-[350px]">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Create project</CardTitle>
|
|
||||||
<CardDescription>Deploy your new project in one-click.</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<form>
|
|
||||||
<div className="grid w-full items-center gap-4">
|
|
||||||
<div className="flex flex-col space-y-1.5">
|
|
||||||
<Label htmlFor="name">Name</Label>
|
|
||||||
<Input id="name" placeholder="Name of your project" />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col space-y-1.5">
|
|
||||||
<Label htmlFor="framework">Framework</Label>
|
|
||||||
<Select>
|
|
||||||
<SelectTrigger id="framework">
|
|
||||||
<SelectValue placeholder="Select" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent position="popper">
|
|
||||||
<SelectItem value="next">Next.js</SelectItem>
|
|
||||||
<SelectItem value="sveltekit">SvelteKit</SelectItem>
|
|
||||||
<SelectItem value="astro">Astro</SelectItem>
|
|
||||||
<SelectItem value="nuxt">Nuxt.js</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className="flex justify-between">
|
|
||||||
<Button variant="outline">Cancel</Button>
|
|
||||||
<Button>Deploy</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
import { Handle, Position } from '@xyflow/react';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const DEFAULT_HANDLE_STYLE = {
|
|
||||||
width: 6,
|
|
||||||
height: 6,
|
|
||||||
bottom: -5,
|
|
||||||
fontSize: 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface IProps extends React.PropsWithChildren {
|
|
||||||
top: number;
|
|
||||||
right: number;
|
|
||||||
id: string;
|
|
||||||
idx?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CategorizeHandle = ({ top, right, id, children }: IProps) => {
|
|
||||||
return (
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
id={id}
|
|
||||||
isConnectable
|
|
||||||
style={{
|
|
||||||
...DEFAULT_HANDLE_STYLE,
|
|
||||||
top: `${top}%`,
|
|
||||||
right: `${right}%`,
|
|
||||||
background: 'red',
|
|
||||||
color: 'black',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className={styles.categorizeAnchorPointText}>{children || id}</span>
|
|
||||||
</Handle>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CategorizeHandle;
|
|
|
@ -1,68 +0,0 @@
|
||||||
import LLMLabel from '@/components/llm-select/llm-label';
|
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { ICategorizeNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { RightHandleStyle } from './handle-icon';
|
|
||||||
import { useBuildCategorizeHandlePositions } from './hooks';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function CategorizeNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<ICategorizeNode>) {
|
|
||||||
const { positions } = useBuildCategorizeHandlePositions({ data, id });
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
type="target"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable
|
|
||||||
className={styles.handle}
|
|
||||||
id={'a'}
|
|
||||||
></Handle>
|
|
||||||
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={styles.nodeHeader}
|
|
||||||
></NodeHeader>
|
|
||||||
|
|
||||||
<Flex vertical gap={8}>
|
|
||||||
<div className={styles.nodeText}>
|
|
||||||
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
|
||||||
</div>
|
|
||||||
{positions.map((position, idx) => {
|
|
||||||
return (
|
|
||||||
<div key={idx}>
|
|
||||||
<div className={styles.nodeText}>{position.text}</div>
|
|
||||||
<Handle
|
|
||||||
key={position.text}
|
|
||||||
id={position.text}
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable
|
|
||||||
className={styles.handle}
|
|
||||||
style={{ ...RightHandleStyle, top: position.top }}
|
|
||||||
></Handle>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
import OperateDropdown from '@/components/operate-dropdown';
|
|
||||||
import { CopyOutlined } from '@ant-design/icons';
|
|
||||||
import { Flex, MenuProps } from 'antd';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { Operator } from '../../constant';
|
|
||||||
import { useDuplicateNode } from '../../hooks';
|
|
||||||
import useGraphStore from '../../store';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
id: string;
|
|
||||||
iconFontColor?: string;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NodeDropdown = ({ id, iconFontColor, label }: IProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const deleteNodeById = useGraphStore((store) => store.deleteNodeById);
|
|
||||||
const deleteIterationNodeById = useGraphStore(
|
|
||||||
(store) => store.deleteIterationNodeById,
|
|
||||||
);
|
|
||||||
|
|
||||||
const deleteNode = useCallback(() => {
|
|
||||||
if (label === Operator.Iteration) {
|
|
||||||
deleteIterationNodeById(id);
|
|
||||||
} else {
|
|
||||||
deleteNodeById(id);
|
|
||||||
}
|
|
||||||
}, [label, deleteIterationNodeById, id, deleteNodeById]);
|
|
||||||
|
|
||||||
const duplicateNode = useDuplicateNode();
|
|
||||||
|
|
||||||
const items: MenuProps['items'] = [
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
onClick: () => duplicateNode(id, label),
|
|
||||||
label: (
|
|
||||||
<Flex justify={'space-between'}>
|
|
||||||
{t('common.copy')}
|
|
||||||
<CopyOutlined />
|
|
||||||
</Flex>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<OperateDropdown
|
|
||||||
iconFontSize={22}
|
|
||||||
height={14}
|
|
||||||
deleteItem={deleteNode}
|
|
||||||
items={items}
|
|
||||||
needsDeletionValidation={false}
|
|
||||||
iconFontColor={iconFontColor}
|
|
||||||
></OperateDropdown>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NodeDropdown;
|
|
|
@ -1,78 +0,0 @@
|
||||||
import { IEmailNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function EmailNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IEmailNode>) {
|
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(styles.ragNode, {
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
id="b"
|
|
||||||
></Handle>
|
|
||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
|
||||||
|
|
||||||
<Flex vertical gap={8} className={styles.emailNodeContainer}>
|
|
||||||
<div
|
|
||||||
className={styles.emailConfig}
|
|
||||||
onClick={() => setShowDetails(!showDetails)}
|
|
||||||
>
|
|
||||||
<div className={styles.configItem}>
|
|
||||||
<span className={styles.configLabel}>SMTP:</span>
|
|
||||||
<span className={styles.configValue}>{data.form?.smtp_server}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.configItem}>
|
|
||||||
<span className={styles.configLabel}>Port:</span>
|
|
||||||
<span className={styles.configValue}>{data.form?.smtp_port}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.configItem}>
|
|
||||||
<span className={styles.configLabel}>From:</span>
|
|
||||||
<span className={styles.configValue}>{data.form?.email}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.expandIcon}>{showDetails ? '▼' : '▶'}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showDetails && (
|
|
||||||
<div className={styles.jsonExample}>
|
|
||||||
<div className={styles.jsonTitle}>Expected Input JSON:</div>
|
|
||||||
<pre className={styles.jsonContent}>
|
|
||||||
{`{
|
|
||||||
"to_email": "...",
|
|
||||||
"cc_email": "...",
|
|
||||||
"subject": "...",
|
|
||||||
"content": "..."
|
|
||||||
}`}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
import LLMLabel from '@/components/llm-select/llm-label';
|
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { IGenerateNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function GenerateNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IGenerateNode>) {
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
id="b"
|
|
||||||
></Handle>
|
|
||||||
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={styles.nodeHeader}
|
|
||||||
></NodeHeader>
|
|
||||||
|
|
||||||
<div className={styles.nodeText}>
|
|
||||||
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { CSSProperties } from 'react';
|
|
||||||
|
|
||||||
export const HandleIcon = () => {
|
|
||||||
return (
|
|
||||||
<PlusOutlined
|
|
||||||
style={{ fontSize: 6, color: 'white', position: 'absolute', zIndex: 10 }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RightHandleStyle: CSSProperties = {
|
|
||||||
right: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const LeftHandleStyle: CSSProperties = {
|
|
||||||
left: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HandleIcon;
|
|
|
@ -1,104 +0,0 @@
|
||||||
import { useUpdateNodeInternals } from '@xyflow/react';
|
|
||||||
import get from 'lodash/get';
|
|
||||||
import { useEffect, useMemo } from 'react';
|
|
||||||
import { SwitchElseTo } from '../../constant';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ICategorizeItemResult,
|
|
||||||
ISwitchCondition,
|
|
||||||
RAGFlowNodeType,
|
|
||||||
} from '@/interfaces/database/flow';
|
|
||||||
import { generateSwitchHandleText } from '../../utils';
|
|
||||||
|
|
||||||
export const useBuildCategorizeHandlePositions = ({
|
|
||||||
data,
|
|
||||||
id,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
data: RAGFlowNodeType['data'];
|
|
||||||
}) => {
|
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
|
||||||
|
|
||||||
const categoryData: ICategorizeItemResult = useMemo(() => {
|
|
||||||
return get(data, `form.category_description`, {});
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
const positions = useMemo(() => {
|
|
||||||
const list: Array<{
|
|
||||||
text: string;
|
|
||||||
top: number;
|
|
||||||
idx: number;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
Object.keys(categoryData)
|
|
||||||
.sort((a, b) => categoryData[a].index - categoryData[b].index)
|
|
||||||
.forEach((x, idx) => {
|
|
||||||
list.push({
|
|
||||||
text: x,
|
|
||||||
idx,
|
|
||||||
top: idx === 0 ? 98 + 20 : list[idx - 1].top + 8 + 26,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}, [categoryData]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateNodeInternals(id);
|
|
||||||
}, [id, updateNodeInternals, categoryData]);
|
|
||||||
|
|
||||||
return { positions };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useBuildSwitchHandlePositions = ({
|
|
||||||
data,
|
|
||||||
id,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
data: RAGFlowNodeType['data'];
|
|
||||||
}) => {
|
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
|
||||||
|
|
||||||
const conditions: ISwitchCondition[] = useMemo(() => {
|
|
||||||
return get(data, 'form.conditions', []);
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
const positions = useMemo(() => {
|
|
||||||
const list: Array<{
|
|
||||||
text: string;
|
|
||||||
top: number;
|
|
||||||
idx: number;
|
|
||||||
condition?: ISwitchCondition;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
[...conditions, ''].forEach((x, idx) => {
|
|
||||||
let top = idx === 0 ? 58 + 20 : list[idx - 1].top + 32; // case number (Case 1) height + flex gap
|
|
||||||
if (idx - 1 >= 0) {
|
|
||||||
const previousItems = conditions[idx - 1]?.items ?? [];
|
|
||||||
if (previousItems.length > 0) {
|
|
||||||
top += 12; // ConditionBlock padding
|
|
||||||
top += previousItems.length * 22; // condition variable height
|
|
||||||
top += (previousItems.length - 1) * 25; // operator height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list.push({
|
|
||||||
text:
|
|
||||||
idx < conditions.length
|
|
||||||
? generateSwitchHandleText(idx)
|
|
||||||
: SwitchElseTo,
|
|
||||||
idx,
|
|
||||||
top,
|
|
||||||
condition: typeof x === 'string' ? undefined : x,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}, [conditions]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateNodeInternals(id);
|
|
||||||
}, [id, updateNodeInternals, conditions]);
|
|
||||||
|
|
||||||
return { positions };
|
|
||||||
};
|
|
|
@ -1,285 +0,0 @@
|
||||||
.dark {
|
|
||||||
background: rgb(63, 63, 63) !important;
|
|
||||||
}
|
|
||||||
.ragNode {
|
|
||||||
.commonNode();
|
|
||||||
.nodeName {
|
|
||||||
font-size: 10px;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
color: #777;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.description {
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.categorizeAnchorPointText {
|
|
||||||
position: absolute;
|
|
||||||
top: -4px;
|
|
||||||
left: 8px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@lightBackgroundColor: rgba(150, 150, 150, 0.1);
|
|
||||||
@darkBackgroundColor: rgba(150, 150, 150, 0.2);
|
|
||||||
|
|
||||||
.selectedNode {
|
|
||||||
border: 1.5px solid rgb(59, 118, 244);
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedIterationNode {
|
|
||||||
border-bottom: 1.5px solid rgb(59, 118, 244);
|
|
||||||
border-left: 1.5px solid rgb(59, 118, 244);
|
|
||||||
border-right: 1.5px solid rgb(59, 118, 244);
|
|
||||||
}
|
|
||||||
|
|
||||||
.iterationHeader {
|
|
||||||
.commonNodeShadow();
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedHeader {
|
|
||||||
border-top: 1.9px solid rgb(59, 118, 244);
|
|
||||||
border-left: 1.9px solid rgb(59, 118, 244);
|
|
||||||
border-right: 1.9px solid rgb(59, 118, 244);
|
|
||||||
}
|
|
||||||
|
|
||||||
.handle {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
background: rgb(59, 88, 253);
|
|
||||||
border: 1px solid white;
|
|
||||||
z-index: 1;
|
|
||||||
background-image: url('@/assets/svg/plus.svg');
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsonView {
|
|
||||||
word-wrap: break-word;
|
|
||||||
overflow: auto;
|
|
||||||
max-width: 300px;
|
|
||||||
max-height: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logicNode {
|
|
||||||
.commonNode();
|
|
||||||
|
|
||||||
.nodeName {
|
|
||||||
font-size: 10px;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
color: #777;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.categorizeAnchorPointText {
|
|
||||||
position: absolute;
|
|
||||||
top: -4px;
|
|
||||||
left: 8px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.relevantSourceLabel {
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.noteNode {
|
|
||||||
.commonNode();
|
|
||||||
min-width: 140px;
|
|
||||||
width: auto;
|
|
||||||
height: 100%;
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 10px;
|
|
||||||
min-height: 128px;
|
|
||||||
.noteTitle {
|
|
||||||
background-color: #edfcff;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 6px 6px 4px;
|
|
||||||
border-top-left-radius: 10px;
|
|
||||||
border-top-right-radius: 10px;
|
|
||||||
}
|
|
||||||
.noteTitleDark {
|
|
||||||
background-color: #edfcff;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 6px 6px 4px;
|
|
||||||
border-top-left-radius: 10px;
|
|
||||||
border-top-right-radius: 10px;
|
|
||||||
}
|
|
||||||
.noteForm {
|
|
||||||
margin-top: 4px;
|
|
||||||
height: calc(100% - 50px);
|
|
||||||
}
|
|
||||||
.noteName {
|
|
||||||
padding: 0px 4px;
|
|
||||||
}
|
|
||||||
.noteTextarea {
|
|
||||||
resize: none;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
height: 100%;
|
|
||||||
&:focus {
|
|
||||||
border: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.iterationNode {
|
|
||||||
.commonNodeShadow();
|
|
||||||
border-bottom-left-radius: 10px;
|
|
||||||
border-bottom-right-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nodeText {
|
|
||||||
padding-inline: 0.4em;
|
|
||||||
padding-block: 0.2em 0.1em;
|
|
||||||
background: @lightBackgroundColor;
|
|
||||||
border-radius: 3px;
|
|
||||||
min-height: 22px;
|
|
||||||
.textEllipsis();
|
|
||||||
}
|
|
||||||
|
|
||||||
.nodeHeader {
|
|
||||||
padding-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zeroDivider {
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conditionBlock {
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 6px;
|
|
||||||
background: @lightBackgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conditionLine {
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 0 4px;
|
|
||||||
background: @darkBackgroundColor;
|
|
||||||
.textEllipsis();
|
|
||||||
}
|
|
||||||
|
|
||||||
.conditionKey {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conditionOperator {
|
|
||||||
padding: 0 2px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.relevantLabel {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.knowledgeNodeName {
|
|
||||||
.textEllipsis();
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageNodeContainer {
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.generateParameters {
|
|
||||||
padding-top: 8px;
|
|
||||||
label {
|
|
||||||
flex: 2;
|
|
||||||
.textEllipsis();
|
|
||||||
}
|
|
||||||
.parameterValue {
|
|
||||||
flex: 3;
|
|
||||||
.conditionLine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.emailNodeContainer {
|
|
||||||
padding: 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
|
|
||||||
.emailConfig {
|
|
||||||
background: rgba(0, 0, 0, 0.02);
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 8px;
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.04);
|
|
||||||
}
|
|
||||||
|
|
||||||
.configItem {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.configLabel {
|
|
||||||
color: #666;
|
|
||||||
width: 45px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.configValue {
|
|
||||||
color: #333;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.expandIcon {
|
|
||||||
position: absolute;
|
|
||||||
right: 8px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
color: #666;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsonExample {
|
|
||||||
background: #f5f5f5;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 8px;
|
|
||||||
margin-top: 4px;
|
|
||||||
animation: slideDown 0.2s ease-out;
|
|
||||||
|
|
||||||
.jsonTitle {
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsonContent {
|
|
||||||
margin: 0;
|
|
||||||
color: #333;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideDown {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-10px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { IRagNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function RagNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IRagNode>) {
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.ragNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
id="b"
|
|
||||||
style={RightHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { IInvokeNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function InvokeNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IInvokeNode>) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { theme } = useTheme();
|
|
||||||
const url = get(data, 'form.url');
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.ragNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
id="b"
|
|
||||||
style={RightHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={styles.nodeHeader}
|
|
||||||
></NodeHeader>
|
|
||||||
<Flex vertical>
|
|
||||||
<div>{t('flow.url')}</div>
|
|
||||||
<div className={styles.nodeText}>{url}</div>
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import {
|
|
||||||
IIterationNode,
|
|
||||||
IIterationStartNode,
|
|
||||||
} from '@/interfaces/database/flow';
|
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
import { Handle, NodeProps, NodeResizeControl, Position } from '@xyflow/react';
|
|
||||||
import { ListRestart } from 'lucide-react';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
function ResizeIcon() {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
strokeWidth="2"
|
|
||||||
stroke="#5025f9"
|
|
||||||
fill="none"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: 5,
|
|
||||||
bottom: 5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<polyline points="16 20 20 20 20 16" />
|
|
||||||
<line x1="14" y1="14" x2="20" y2="20" />
|
|
||||||
<polyline points="8 4 4 4 4 8" />
|
|
||||||
<line x1="4" y1="4" x2="10" y2="10" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const controlStyle = {
|
|
||||||
background: 'transparent',
|
|
||||||
border: 'none',
|
|
||||||
cursor: 'nwse-resize',
|
|
||||||
};
|
|
||||||
|
|
||||||
export function IterationNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IIterationNode>) {
|
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={cn(
|
|
||||||
'w-full h-full bg-zinc-200 opacity-70',
|
|
||||||
styles.iterationNode,
|
|
||||||
{
|
|
||||||
['bg-gray-800']: theme === 'dark',
|
|
||||||
[styles.selectedIterationNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<NodeResizeControl style={controlStyle} minWidth={100} minHeight={50}>
|
|
||||||
<ResizeIcon />
|
|
||||||
</NodeResizeControl>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
id="b"
|
|
||||||
style={RightHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
wrapperClassName={cn(
|
|
||||||
'p-2 bg-white rounded-t-[10px] absolute w-full top-[-60px] left-[-0.3px]',
|
|
||||||
styles.iterationHeader,
|
|
||||||
{
|
|
||||||
[`${styles.dark} text-white`]: theme === 'dark',
|
|
||||||
[styles.selectedHeader]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
></NodeHeader>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function IterationStartNode({
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IIterationStartNode>) {
|
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={cn('bg-white p-2 rounded-xl', {
|
|
||||||
[styles.dark]: theme === 'dark',
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
isConnectableEnd={false}
|
|
||||||
></Handle>
|
|
||||||
<div>
|
|
||||||
<ListRestart className="size-7" />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
import LLMLabel from '@/components/llm-select/llm-label';
|
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { IKeywordNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function KeywordNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IKeywordNode>) {
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
id="b"
|
|
||||||
></Handle>
|
|
||||||
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={styles.nodeHeader}
|
|
||||||
></NodeHeader>
|
|
||||||
|
|
||||||
<div className={styles.nodeText}>
|
|
||||||
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { ILogicNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function LogicNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<ILogicNode>) {
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
id="b"
|
|
||||||
></Handle>
|
|
||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { IMessageNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function MessageNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IMessageNode>) {
|
|
||||||
const messages: string[] = get(data, 'form.messages', []);
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
id="b"
|
|
||||||
></Handle>
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={classNames({
|
|
||||||
[styles.nodeHeader]: messages.length > 0,
|
|
||||||
})}
|
|
||||||
></NodeHeader>
|
|
||||||
|
|
||||||
<Flex vertical gap={8} className={styles.messageNodeContainer}>
|
|
||||||
{messages.map((message, idx) => {
|
|
||||||
return (
|
|
||||||
<div className={styles.nodeText} key={idx}>
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Flex } from 'antd';
|
|
||||||
import { Play } from 'lucide-react';
|
|
||||||
import { Operator, operatorMap } from '../../constant';
|
|
||||||
import OperatorIcon from '../../operator-icon';
|
|
||||||
import { needsSingleStepDebugging } from '../../utils';
|
|
||||||
import NodeDropdown from './dropdown';
|
|
||||||
import { NextNodePopover } from './popover';
|
|
||||||
|
|
||||||
import { RunTooltip } from '../../flow-tooltip';
|
|
||||||
interface IProps {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
name: string;
|
|
||||||
gap?: number;
|
|
||||||
className?: string;
|
|
||||||
wrapperClassName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ExcludedRunStateOperators = [Operator.Answer];
|
|
||||||
|
|
||||||
export function RunStatus({ id, name, label }: IProps) {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
return (
|
|
||||||
<section className="flex justify-end items-center pb-1 gap-2 text-blue-600">
|
|
||||||
{needsSingleStepDebugging(label) && (
|
|
||||||
<RunTooltip>
|
|
||||||
<Play className="size-3 cursor-pointer" data-play />
|
|
||||||
</RunTooltip> // data-play is used to trigger single step debugging
|
|
||||||
)}
|
|
||||||
<NextNodePopover nodeId={id} name={name}>
|
|
||||||
<span className="cursor-pointer text-[10px]">
|
|
||||||
{t('operationResults')}
|
|
||||||
</span>
|
|
||||||
</NextNodePopover>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const NodeHeader = ({
|
|
||||||
label,
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
gap = 4,
|
|
||||||
className,
|
|
||||||
wrapperClassName,
|
|
||||||
}: IProps) => {
|
|
||||||
return (
|
|
||||||
<section className={wrapperClassName}>
|
|
||||||
{!ExcludedRunStateOperators.includes(label as Operator) && (
|
|
||||||
<RunStatus id={id} name={name} label={label}></RunStatus>
|
|
||||||
)}
|
|
||||||
<Flex
|
|
||||||
flex={1}
|
|
||||||
align="center"
|
|
||||||
justify={'space-between'}
|
|
||||||
gap={gap}
|
|
||||||
className={className}
|
|
||||||
>
|
|
||||||
<OperatorIcon
|
|
||||||
name={label as Operator}
|
|
||||||
color={operatorMap[label as Operator]?.color}
|
|
||||||
></OperatorIcon>
|
|
||||||
<span className="truncate text-center font-semibold text-sm">
|
|
||||||
{name}
|
|
||||||
</span>
|
|
||||||
<NodeDropdown id={id} label={label}></NodeDropdown>
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NodeHeader;
|
|
|
@ -1,92 +0,0 @@
|
||||||
import { NodeProps, NodeResizeControl } from '@xyflow/react';
|
|
||||||
import { Flex, Form, Input } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import NodeDropdown from './dropdown';
|
|
||||||
|
|
||||||
import SvgIcon from '@/components/svg-icon';
|
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { INoteNode } from '@/interfaces/database/flow';
|
|
||||||
import { memo, useEffect } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import {
|
|
||||||
useHandleFormValuesChange,
|
|
||||||
useHandleNodeNameChange,
|
|
||||||
} from '../../hooks';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const { TextArea } = Input;
|
|
||||||
|
|
||||||
const controlStyle = {
|
|
||||||
background: 'transparent',
|
|
||||||
border: 'none',
|
|
||||||
};
|
|
||||||
|
|
||||||
function NoteNode({ data, id }: NodeProps<INoteNode>) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
const { handleValuesChange } = useHandleFormValuesChange(id);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
form.setFieldsValue(data?.form);
|
|
||||||
}, [form, data?.form]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<NodeResizeControl style={controlStyle} minWidth={190} minHeight={128}>
|
|
||||||
<SvgIcon
|
|
||||||
name="resize"
|
|
||||||
width={12}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: 5,
|
|
||||||
bottom: 5,
|
|
||||||
cursor: 'nwse-resize',
|
|
||||||
}}
|
|
||||||
></SvgIcon>
|
|
||||||
</NodeResizeControl>
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.noteNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
justify={'space-between'}
|
|
||||||
className={classNames('note-drag-handle')}
|
|
||||||
align="center"
|
|
||||||
gap={6}
|
|
||||||
>
|
|
||||||
<SvgIcon name="note" width={14}></SvgIcon>
|
|
||||||
<Input
|
|
||||||
value={name ?? t('flow.note')}
|
|
||||||
onBlur={handleNameBlur}
|
|
||||||
onChange={handleNameChange}
|
|
||||||
className={styles.noteName}
|
|
||||||
></Input>
|
|
||||||
<NodeDropdown id={id} label={data.label}></NodeDropdown>
|
|
||||||
</Flex>
|
|
||||||
<Form
|
|
||||||
onValuesChange={handleValuesChange}
|
|
||||||
form={form}
|
|
||||||
className={styles.noteForm}
|
|
||||||
>
|
|
||||||
<Form.Item name="text" noStyle>
|
|
||||||
<TextArea
|
|
||||||
rows={3}
|
|
||||||
placeholder={t('flow.notePlaceholder')}
|
|
||||||
className={styles.noteTextarea}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</section>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(NoteNode);
|
|
|
@ -1,121 +0,0 @@
|
||||||
import { useFetchFlow } from '@/hooks/flow-hooks';
|
|
||||||
import get from 'lodash/get';
|
|
||||||
import React, { MouseEventHandler, useCallback, useMemo } from 'react';
|
|
||||||
import JsonView from 'react18-json-view';
|
|
||||||
import 'react18-json-view/src/style.css';
|
|
||||||
import { useReplaceIdWithText } from '../../hooks';
|
|
||||||
|
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from '@/components/ui/popover';
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from '@/components/ui/table';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query';
|
|
||||||
|
|
||||||
interface IProps extends React.PropsWithChildren {
|
|
||||||
nodeId: string;
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NextNodePopover({ children, nodeId, name }: IProps) {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const { data } = useFetchFlow();
|
|
||||||
const { theme } = useTheme();
|
|
||||||
const component = useMemo(() => {
|
|
||||||
return get(data, ['dsl', 'components', nodeId], {});
|
|
||||||
}, [nodeId, data]);
|
|
||||||
|
|
||||||
const inputs: Array<{ component_id: string; content: string }> = get(
|
|
||||||
component,
|
|
||||||
['obj', 'inputs'],
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
const output = get(component, ['obj', 'output'], {});
|
|
||||||
const { replacedOutput } = useReplaceIdWithText(output);
|
|
||||||
const stopPropagation: MouseEventHandler = useCallback((e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getLabel = useGetComponentLabelByValue(nodeId);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger onClick={stopPropagation} asChild>
|
|
||||||
{children}
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent
|
|
||||||
align={'start'}
|
|
||||||
side={'right'}
|
|
||||||
sideOffset={20}
|
|
||||||
onClick={stopPropagation}
|
|
||||||
className="w-[400px]"
|
|
||||||
>
|
|
||||||
<div className="mb-3 font-semibold text-[16px]">
|
|
||||||
{name} {t('operationResults')}
|
|
||||||
</div>
|
|
||||||
<div className="flex w-full gap-4 flex-col">
|
|
||||||
<div className="flex flex-col space-y-1.5">
|
|
||||||
<span className="font-semibold text-[14px]">{t('input')}</span>
|
|
||||||
<div
|
|
||||||
style={
|
|
||||||
theme === 'dark'
|
|
||||||
? {
|
|
||||||
backgroundColor: 'rgba(150, 150, 150, 0.2)',
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
className={`bg-gray-100 p-1 rounded`}
|
|
||||||
>
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead>{t('componentId')}</TableHead>
|
|
||||||
<TableHead className="w-[60px]">{t('content')}</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{inputs.map((x, idx) => (
|
|
||||||
<TableRow key={idx}>
|
|
||||||
<TableCell>{getLabel(x.component_id)}</TableCell>
|
|
||||||
<TableCell className="truncate">{x.content}</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col space-y-1.5">
|
|
||||||
<span className="font-semibold text-[14px]">{t('output')}</span>
|
|
||||||
<div
|
|
||||||
style={
|
|
||||||
theme === 'dark'
|
|
||||||
? {
|
|
||||||
backgroundColor: 'rgba(150, 150, 150, 0.2)',
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
className="bg-gray-100 p-1 rounded"
|
|
||||||
>
|
|
||||||
<JsonView
|
|
||||||
src={replacedOutput}
|
|
||||||
displaySize={30}
|
|
||||||
className="w-full max-h-[300px] break-words overflow-auto"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { RightHandleStyle } from './handle-icon';
|
|
||||||
|
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { IRelevantNode } from '@/interfaces/database/flow';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { useReplaceIdWithName } from '../../hooks';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function RelevantNode({ id, data, selected }: NodeProps<IRelevantNode>) {
|
|
||||||
const yes = get(data, 'form.yes');
|
|
||||||
const no = get(data, 'form.no');
|
|
||||||
const replaceIdWithName = useReplaceIdWithName();
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
type="target"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable
|
|
||||||
className={styles.handle}
|
|
||||||
id={'a'}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable
|
|
||||||
className={styles.handle}
|
|
||||||
id={'yes'}
|
|
||||||
style={{ ...RightHandleStyle, top: 57 + 20 }}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable
|
|
||||||
className={styles.handle}
|
|
||||||
id={'no'}
|
|
||||||
style={{ ...RightHandleStyle, top: 115 + 20 }}
|
|
||||||
></Handle>
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={styles.nodeHeader}
|
|
||||||
></NodeHeader>
|
|
||||||
|
|
||||||
<Flex vertical gap={10}>
|
|
||||||
<Flex vertical>
|
|
||||||
<div className={styles.relevantLabel}>Yes</div>
|
|
||||||
<div className={styles.nodeText}>{replaceIdWithName(yes)}</div>
|
|
||||||
</Flex>
|
|
||||||
<Flex vertical>
|
|
||||||
<div className={styles.relevantLabel}>No</div>
|
|
||||||
<div className={styles.nodeText}>{replaceIdWithName(no)}</div>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
|
||||||
import { IRetrievalNode } from '@/interfaces/database/flow';
|
|
||||||
import { UserOutlined } from '@ant-design/icons';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { Avatar, Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function RetrievalNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IRetrievalNode>) {
|
|
||||||
const knowledgeBaseIds: string[] = get(data, 'form.kb_ids', []);
|
|
||||||
const { theme } = useTheme();
|
|
||||||
const { list: knowledgeList } = useFetchKnowledgeList(true);
|
|
||||||
const knowledgeBases = useMemo(() => {
|
|
||||||
return knowledgeBaseIds.map((x) => {
|
|
||||||
const item = knowledgeList.find((y) => x === y.id);
|
|
||||||
return {
|
|
||||||
name: item?.name,
|
|
||||||
avatar: item?.avatar,
|
|
||||||
id: x,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [knowledgeList, knowledgeBaseIds]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
id="b"
|
|
||||||
></Handle>
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={classNames({
|
|
||||||
[styles.nodeHeader]: knowledgeBaseIds.length > 0,
|
|
||||||
})}
|
|
||||||
></NodeHeader>
|
|
||||||
<Flex vertical gap={8}>
|
|
||||||
{knowledgeBases.map((knowledge) => {
|
|
||||||
return (
|
|
||||||
<div className={styles.nodeText} key={knowledge.id}>
|
|
||||||
<Flex align={'center'} gap={6}>
|
|
||||||
<Avatar
|
|
||||||
size={26}
|
|
||||||
icon={<UserOutlined />}
|
|
||||||
src={knowledge.avatar}
|
|
||||||
/>
|
|
||||||
<Flex className={styles.knowledgeNodeName} flex={1}>
|
|
||||||
{knowledge.name}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
import LLMLabel from '@/components/llm-select/llm-label';
|
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { IRewriteNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
export function RewriteNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IRewriteNode>) {
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
id="b"
|
|
||||||
></Handle>
|
|
||||||
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={styles.nodeHeader}
|
|
||||||
></NodeHeader>
|
|
||||||
|
|
||||||
<div className={styles.nodeText}>
|
|
||||||
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { ISwitchCondition, ISwitchNode } from '@/interfaces/database/flow';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { Divider, Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query';
|
|
||||||
import { RightHandleStyle } from './handle-icon';
|
|
||||||
import { useBuildSwitchHandlePositions } from './hooks';
|
|
||||||
import styles from './index.less';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
const getConditionKey = (idx: number, length: number) => {
|
|
||||||
if (idx === 0 && length !== 1) {
|
|
||||||
return 'If';
|
|
||||||
} else if (idx === length - 1) {
|
|
||||||
return 'Else';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'ElseIf';
|
|
||||||
};
|
|
||||||
|
|
||||||
const ConditionBlock = ({
|
|
||||||
condition,
|
|
||||||
nodeId,
|
|
||||||
}: {
|
|
||||||
condition: ISwitchCondition;
|
|
||||||
nodeId: string;
|
|
||||||
}) => {
|
|
||||||
const items = condition?.items ?? [];
|
|
||||||
const getLabel = useGetComponentLabelByValue(nodeId);
|
|
||||||
return (
|
|
||||||
<Flex vertical className={styles.conditionBlock}>
|
|
||||||
{items.map((x, idx) => (
|
|
||||||
<div key={idx}>
|
|
||||||
<Flex>
|
|
||||||
<div
|
|
||||||
className={classNames(styles.conditionLine, styles.conditionKey)}
|
|
||||||
>
|
|
||||||
{getLabel(x?.cpn_id)}
|
|
||||||
</div>
|
|
||||||
<span className={styles.conditionOperator}>{x?.operator}</span>
|
|
||||||
<Flex flex={1} className={styles.conditionLine}>
|
|
||||||
{x?.value}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{idx + 1 < items.length && (
|
|
||||||
<Divider orientationMargin="0" className={styles.zeroDivider}>
|
|
||||||
{condition?.logical_operator}
|
|
||||||
</Divider>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function SwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) {
|
|
||||||
const { positions } = useBuildSwitchHandlePositions({ data, id });
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
type="target"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable
|
|
||||||
className={styles.handle}
|
|
||||||
id={'a'}
|
|
||||||
></Handle>
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={styles.nodeHeader}
|
|
||||||
></NodeHeader>
|
|
||||||
<Flex vertical gap={10}>
|
|
||||||
{positions.map((position, idx) => {
|
|
||||||
return (
|
|
||||||
<div key={idx}>
|
|
||||||
<Flex vertical>
|
|
||||||
<Flex justify={'space-between'}>
|
|
||||||
<span>{idx < positions.length - 1 && position.text}</span>
|
|
||||||
<span>{getConditionKey(idx, positions.length)}</span>
|
|
||||||
</Flex>
|
|
||||||
{position.condition && (
|
|
||||||
<ConditionBlock
|
|
||||||
nodeId={id}
|
|
||||||
condition={position.condition}
|
|
||||||
></ConditionBlock>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
<Handle
|
|
||||||
key={position.text}
|
|
||||||
id={position.text}
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable
|
|
||||||
className={styles.handle}
|
|
||||||
style={{ ...RightHandleStyle, top: position.top }}
|
|
||||||
></Handle>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { Handle, NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query';
|
|
||||||
import { IGenerateParameter } from '../../interface';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
|
|
||||||
import { ITemplateNode } from '@/interfaces/database/flow';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
export function TemplateNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<ITemplateNode>) {
|
|
||||||
const parameters: IGenerateParameter[] = get(data, 'form.parameters', []);
|
|
||||||
const getLabel = useGetComponentLabelByValue(id);
|
|
||||||
const { theme } = useTheme();
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className={classNames(
|
|
||||||
styles.logicNode,
|
|
||||||
theme === 'dark' ? styles.dark : '',
|
|
||||||
|
|
||||||
{
|
|
||||||
[styles.selectedNode]: selected,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
></Handle>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
className={styles.handle}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
id="b"
|
|
||||||
></Handle>
|
|
||||||
|
|
||||||
<NodeHeader
|
|
||||||
id={id}
|
|
||||||
name={data.name}
|
|
||||||
label={data.label}
|
|
||||||
className={styles.nodeHeader}
|
|
||||||
></NodeHeader>
|
|
||||||
|
|
||||||
<Flex gap={8} vertical className={styles.generateParameters}>
|
|
||||||
{parameters.map((x) => (
|
|
||||||
<Flex
|
|
||||||
key={x.id}
|
|
||||||
align="center"
|
|
||||||
gap={6}
|
|
||||||
className={styles.conditionBlock}
|
|
||||||
>
|
|
||||||
<label htmlFor="">{x.key}</label>
|
|
||||||
<span className={styles.parameterValue}>
|
|
||||||
{getLabel(x.component_id)}
|
|
||||||
</span>
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +0,0 @@
|
||||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
|
||||||
import { createContext } from 'react';
|
|
||||||
|
|
||||||
export const FlowFormContext = createContext<RAGFlowNodeType | undefined>(
|
|
||||||
undefined,
|
|
||||||
);
|
|
|
@ -1,5 +0,0 @@
|
||||||
.formWrapper {
|
|
||||||
:global(.ant-form-item-label) {
|
|
||||||
font-weight: 600 !important;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,238 +0,0 @@
|
||||||
import { Authorization } from '@/constants/authorization';
|
|
||||||
import { useSetModalState } from '@/hooks/common-hooks';
|
|
||||||
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
|
||||||
import { useHandleSubmittable } from '@/hooks/login-hooks';
|
|
||||||
import api from '@/utils/api';
|
|
||||||
import { getAuthorization } from '@/utils/authorization-util';
|
|
||||||
import { UploadOutlined } from '@ant-design/icons';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Form,
|
|
||||||
FormItemProps,
|
|
||||||
Input,
|
|
||||||
InputNumber,
|
|
||||||
Select,
|
|
||||||
Switch,
|
|
||||||
Upload,
|
|
||||||
} from 'antd';
|
|
||||||
import { UploadChangeParam, UploadFile } from 'antd/es/upload';
|
|
||||||
import { pick } from 'lodash';
|
|
||||||
import { Link } from 'lucide-react';
|
|
||||||
import React, { useCallback, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { BeginQueryType } from '../constant';
|
|
||||||
import { BeginQuery } from '../interface';
|
|
||||||
import { PopoverForm } from './popover-form';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
parameters: BeginQuery[];
|
|
||||||
ok(parameters: any[]): void;
|
|
||||||
isNext?: boolean;
|
|
||||||
loading?: boolean;
|
|
||||||
submitButtonDisabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DebugContent = ({
|
|
||||||
parameters,
|
|
||||||
ok,
|
|
||||||
isNext = true,
|
|
||||||
loading = false,
|
|
||||||
submitButtonDisabled = false,
|
|
||||||
}: IProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const {
|
|
||||||
visible,
|
|
||||||
hideModal: hidePopover,
|
|
||||||
switchVisible,
|
|
||||||
showModal: showPopover,
|
|
||||||
} = useSetModalState();
|
|
||||||
const { setRecord, currentRecord } = useSetSelectedRecord<number>();
|
|
||||||
const { submittable } = useHandleSubmittable(form);
|
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
|
||||||
|
|
||||||
const handleShowPopover = useCallback(
|
|
||||||
(idx: number) => () => {
|
|
||||||
setRecord(idx);
|
|
||||||
showPopover();
|
|
||||||
},
|
|
||||||
[setRecord, showPopover],
|
|
||||||
);
|
|
||||||
|
|
||||||
const normFile = (e: any) => {
|
|
||||||
if (Array.isArray(e)) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
return e?.fileList;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChange = useCallback(
|
|
||||||
(optional: boolean) =>
|
|
||||||
({ fileList }: UploadChangeParam<UploadFile>) => {
|
|
||||||
if (!optional) {
|
|
||||||
setIsUploading(fileList.some((x) => x.status === 'uploading'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderWidget = useCallback(
|
|
||||||
(q: BeginQuery, idx: number) => {
|
|
||||||
const props: FormItemProps & { key: number } = {
|
|
||||||
key: idx,
|
|
||||||
label: q.name ?? q.key,
|
|
||||||
name: idx,
|
|
||||||
};
|
|
||||||
if (q.optional === false) {
|
|
||||||
props.rules = [{ required: true }];
|
|
||||||
}
|
|
||||||
|
|
||||||
const urlList: { url: string; result: string }[] =
|
|
||||||
form.getFieldValue(idx) || [];
|
|
||||||
|
|
||||||
const BeginQueryTypeMap = {
|
|
||||||
[BeginQueryType.Line]: (
|
|
||||||
<Form.Item {...props}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
),
|
|
||||||
[BeginQueryType.Paragraph]: (
|
|
||||||
<Form.Item {...props}>
|
|
||||||
<Input.TextArea rows={1}></Input.TextArea>
|
|
||||||
</Form.Item>
|
|
||||||
),
|
|
||||||
[BeginQueryType.Options]: (
|
|
||||||
<Form.Item {...props}>
|
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
options={q.options?.map((x) => ({ label: x, value: x })) ?? []}
|
|
||||||
></Select>
|
|
||||||
</Form.Item>
|
|
||||||
),
|
|
||||||
[BeginQueryType.File]: (
|
|
||||||
<React.Fragment key={idx}>
|
|
||||||
<Form.Item label={q.name ?? q.key} required={!q.optional}>
|
|
||||||
<div className="relative">
|
|
||||||
<Form.Item
|
|
||||||
{...props}
|
|
||||||
valuePropName="fileList"
|
|
||||||
getValueFromEvent={normFile}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Upload
|
|
||||||
name="file"
|
|
||||||
action={api.parse}
|
|
||||||
multiple
|
|
||||||
headers={{ [Authorization]: getAuthorization() }}
|
|
||||||
onChange={onChange(q.optional)}
|
|
||||||
>
|
|
||||||
<Button icon={<UploadOutlined />}>
|
|
||||||
{t('common.upload')}
|
|
||||||
</Button>
|
|
||||||
</Upload>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
{...pick(props, ['key', 'label', 'rules'])}
|
|
||||||
required={!q.optional}
|
|
||||||
className={urlList.length > 0 ? 'mb-1' : ''}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<PopoverForm visible={visible} switchVisible={switchVisible}>
|
|
||||||
<Button
|
|
||||||
onClick={handleShowPopover(idx)}
|
|
||||||
className="absolute left-1/2 top-0"
|
|
||||||
icon={<Link className="size-3" />}
|
|
||||||
>
|
|
||||||
{t('flow.pasteFileLink')}
|
|
||||||
</Button>
|
|
||||||
</PopoverForm>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name={idx} noStyle {...pick(props, ['rules'])} />
|
|
||||||
</React.Fragment>
|
|
||||||
),
|
|
||||||
[BeginQueryType.Integer]: (
|
|
||||||
<Form.Item {...props}>
|
|
||||||
<InputNumber></InputNumber>
|
|
||||||
</Form.Item>
|
|
||||||
),
|
|
||||||
[BeginQueryType.Boolean]: (
|
|
||||||
<Form.Item valuePropName={'checked'} {...props}>
|
|
||||||
<Switch></Switch>
|
|
||||||
</Form.Item>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
BeginQueryTypeMap[q.type as BeginQueryType] ??
|
|
||||||
BeginQueryTypeMap[BeginQueryType.Paragraph]
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[form, handleShowPopover, onChange, switchVisible, t, visible],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onOk = useCallback(async () => {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
const nextValues = Object.entries(values).map(([key, value]) => {
|
|
||||||
const item = parameters[Number(key)];
|
|
||||||
let nextValue = value;
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
nextValue = ``;
|
|
||||||
|
|
||||||
value.forEach((x) => {
|
|
||||||
nextValue +=
|
|
||||||
x?.originFileObj instanceof File
|
|
||||||
? `${x.name}\n${x.response?.data}\n----\n`
|
|
||||||
: `${x.url}\n${x.result}\n----\n`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return { ...item, value: nextValue };
|
|
||||||
});
|
|
||||||
|
|
||||||
ok(nextValues);
|
|
||||||
}, [form, ok, parameters]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<section className={styles.formWrapper}>
|
|
||||||
<Form.Provider
|
|
||||||
onFormFinish={(name, { values, forms }) => {
|
|
||||||
if (name === 'urlForm') {
|
|
||||||
const { basicForm } = forms;
|
|
||||||
const urlInfo = basicForm.getFieldValue(currentRecord) || [];
|
|
||||||
basicForm.setFieldsValue({
|
|
||||||
[currentRecord]: [...urlInfo, { ...values, name: values.url }],
|
|
||||||
});
|
|
||||||
hidePopover();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Form
|
|
||||||
name="basicForm"
|
|
||||||
autoComplete="off"
|
|
||||||
layout={'vertical'}
|
|
||||||
form={form}
|
|
||||||
>
|
|
||||||
{parameters.map((x, idx) => {
|
|
||||||
return renderWidget(x, idx);
|
|
||||||
})}
|
|
||||||
</Form>
|
|
||||||
</Form.Provider>
|
|
||||||
</section>
|
|
||||||
<Button
|
|
||||||
type={'primary'}
|
|
||||||
block
|
|
||||||
onClick={onOk}
|
|
||||||
loading={loading}
|
|
||||||
disabled={!submittable || isUploading || submitButtonDisabled}
|
|
||||||
>
|
|
||||||
{t(isNext ? 'common.next' : 'flow.run')}
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DebugContent;
|
|
|
@ -1,74 +0,0 @@
|
||||||
import { useParseDocument } from '@/hooks/document-hooks';
|
|
||||||
import { useResetFormOnCloseModal } from '@/hooks/logic-hooks';
|
|
||||||
import { IModalProps } from '@/interfaces/common';
|
|
||||||
import { Button, Form, Input, Popover } from 'antd';
|
|
||||||
import { PropsWithChildren } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
const reg =
|
|
||||||
/^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/;
|
|
||||||
|
|
||||||
export const PopoverForm = ({
|
|
||||||
children,
|
|
||||||
visible,
|
|
||||||
switchVisible,
|
|
||||||
}: PropsWithChildren<IModalProps<any>>) => {
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const { parseDocument, loading } = useParseDocument();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
useResetFormOnCloseModal({
|
|
||||||
form,
|
|
||||||
visible,
|
|
||||||
});
|
|
||||||
|
|
||||||
const onOk = async () => {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
const val = values.url;
|
|
||||||
|
|
||||||
if (reg.test(val)) {
|
|
||||||
const ret = await parseDocument(val);
|
|
||||||
if (ret?.data?.code === 0) {
|
|
||||||
form.setFieldValue('result', ret?.data?.data);
|
|
||||||
form.submit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const content = (
|
|
||||||
<Form form={form} name="urlForm">
|
|
||||||
<Form.Item
|
|
||||||
name="url"
|
|
||||||
rules={[{ required: true, type: 'url' }]}
|
|
||||||
className="m-0"
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
onPressEnter={(e) => e.preventDefault()}
|
|
||||||
placeholder={t('flow.pasteFileLink')}
|
|
||||||
suffix={
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={onOk}
|
|
||||||
size={'small'}
|
|
||||||
loading={loading}
|
|
||||||
>
|
|
||||||
{t('common.submit')}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name={'result'} noStyle />
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover
|
|
||||||
content={content}
|
|
||||||
open={visible}
|
|
||||||
trigger={'click'}
|
|
||||||
onOpenChange={switchVisible}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,19 +0,0 @@
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from '@/components/ui/tooltip';
|
|
||||||
import { PropsWithChildren } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
export const RunTooltip = ({ children }: PropsWithChildren) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger>{children}</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>{t('flow.testRun')}</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,77 +0,0 @@
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import { Operator, RestrictedUpstreamMap } from './constant';
|
|
||||||
import useGraphStore from './store';
|
|
||||||
|
|
||||||
export const useBuildFormSelectOptions = (
|
|
||||||
operatorName: Operator,
|
|
||||||
selfId?: string, // exclude the current node
|
|
||||||
) => {
|
|
||||||
const nodes = useGraphStore((state) => state.nodes);
|
|
||||||
|
|
||||||
const buildCategorizeToOptions = useCallback(
|
|
||||||
(toList: string[]) => {
|
|
||||||
const excludedNodes: Operator[] = [
|
|
||||||
Operator.Note,
|
|
||||||
...(RestrictedUpstreamMap[operatorName] ?? []),
|
|
||||||
];
|
|
||||||
return nodes
|
|
||||||
.filter(
|
|
||||||
(x) =>
|
|
||||||
excludedNodes.every((y) => y !== x.data.label) &&
|
|
||||||
x.id !== selfId &&
|
|
||||||
!toList.some((y) => y === x.id), // filter out selected values in other to fields from the current drop-down box options
|
|
||||||
)
|
|
||||||
.map((x) => ({ label: x.data.name, value: x.id }));
|
|
||||||
},
|
|
||||||
[nodes, operatorName, selfId],
|
|
||||||
);
|
|
||||||
|
|
||||||
return buildCategorizeToOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* dumped
|
|
||||||
* @param nodeId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const useHandleFormSelectChange = (nodeId?: string) => {
|
|
||||||
const { addEdge, deleteEdgeBySourceAndSourceHandle } = useGraphStore(
|
|
||||||
(state) => state,
|
|
||||||
);
|
|
||||||
const handleSelectChange = useCallback(
|
|
||||||
(name?: string) => (value?: string) => {
|
|
||||||
if (nodeId && name) {
|
|
||||||
if (value) {
|
|
||||||
addEdge({
|
|
||||||
source: nodeId,
|
|
||||||
target: value,
|
|
||||||
sourceHandle: name,
|
|
||||||
targetHandle: null,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// clear selected value
|
|
||||||
deleteEdgeBySourceAndSourceHandle({
|
|
||||||
source: nodeId,
|
|
||||||
sourceHandle: name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[addEdge, nodeId, deleteEdgeBySourceAndSourceHandle],
|
|
||||||
);
|
|
||||||
|
|
||||||
return { handleSelectChange };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useBuildSortOptions = () => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return ['data', 'relevance'].map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(x),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
return options;
|
|
||||||
};
|
|
|
@ -1,21 +0,0 @@
|
||||||
.title {
|
|
||||||
flex-basis: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.formWrapper {
|
|
||||||
:global(.ant-form-item-label) {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.operatorDescription {
|
|
||||||
font-size: 14px;
|
|
||||||
padding-top: 16px;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.formDrawer {
|
|
||||||
:global(.ant-drawer-content-wrapper) {
|
|
||||||
transform: translateX(0) !important;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,208 +0,0 @@
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { IModalProps } from '@/interfaces/common';
|
|
||||||
import { Flex, Form, Input } from 'antd';
|
|
||||||
import { get, isPlainObject, lowerFirst } from 'lodash';
|
|
||||||
import { Play } from 'lucide-react';
|
|
||||||
import { useEffect, useRef } from 'react';
|
|
||||||
import { BeginId, Operator, operatorMap } from '../constant';
|
|
||||||
import AkShareForm from '../form/akshare-form';
|
|
||||||
import AnswerForm from '../form/answer-form';
|
|
||||||
import ArXivForm from '../form/arxiv-form';
|
|
||||||
import BaiduFanyiForm from '../form/baidu-fanyi-form';
|
|
||||||
import BaiduForm from '../form/baidu-form';
|
|
||||||
import BeginForm from '../form/begin-form';
|
|
||||||
import BingForm from '../form/bing-form';
|
|
||||||
import CategorizeForm from '../form/categorize-form';
|
|
||||||
import CrawlerForm from '../form/crawler-form';
|
|
||||||
import DeepLForm from '../form/deepl-form';
|
|
||||||
import DuckDuckGoForm from '../form/duckduckgo-form';
|
|
||||||
import EmailForm from '../form/email-form';
|
|
||||||
import ExeSQLForm from '../form/exesql-form';
|
|
||||||
import GenerateForm from '../form/generate-form';
|
|
||||||
import GithubForm from '../form/github-form';
|
|
||||||
import GoogleForm from '../form/google-form';
|
|
||||||
import GoogleScholarForm from '../form/google-scholar-form';
|
|
||||||
import InvokeForm from '../form/invoke-form';
|
|
||||||
import Jin10Form from '../form/jin10-form';
|
|
||||||
import KeywordExtractForm from '../form/keyword-extract-form';
|
|
||||||
import MessageForm from '../form/message-form';
|
|
||||||
import PubMedForm from '../form/pubmed-form';
|
|
||||||
import QWeatherForm from '../form/qweather-form';
|
|
||||||
import RelevantForm from '../form/relevant-form';
|
|
||||||
import RetrievalForm from '../form/retrieval-form/next';
|
|
||||||
import RewriteQuestionForm from '../form/rewrite-question-form';
|
|
||||||
import SwitchForm from '../form/switch-form';
|
|
||||||
import TemplateForm from '../form/template-form';
|
|
||||||
import TuShareForm from '../form/tushare-form';
|
|
||||||
import WenCaiForm from '../form/wencai-form';
|
|
||||||
import WikipediaForm from '../form/wikipedia-form';
|
|
||||||
import YahooFinanceForm from '../form/yahoo-finance-form';
|
|
||||||
import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks';
|
|
||||||
import OperatorIcon from '../operator-icon';
|
|
||||||
import {
|
|
||||||
buildCategorizeListFromObject,
|
|
||||||
needsSingleStepDebugging,
|
|
||||||
} from '../utils';
|
|
||||||
|
|
||||||
import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet';
|
|
||||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
|
||||||
import { FlowFormContext } from '../context';
|
|
||||||
import { RunTooltip } from '../flow-tooltip';
|
|
||||||
import IterationForm from '../form/iteration-from';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
node?: RAGFlowNodeType;
|
|
||||||
singleDebugDrawerVisible: IModalProps<any>['visible'];
|
|
||||||
hideSingleDebugDrawer: IModalProps<any>['hideModal'];
|
|
||||||
showSingleDebugDrawer: IModalProps<any>['showModal'];
|
|
||||||
}
|
|
||||||
|
|
||||||
const FormMap = {
|
|
||||||
[Operator.Begin]: BeginForm,
|
|
||||||
[Operator.Retrieval]: RetrievalForm,
|
|
||||||
[Operator.Generate]: GenerateForm,
|
|
||||||
[Operator.Answer]: AnswerForm,
|
|
||||||
[Operator.Categorize]: CategorizeForm,
|
|
||||||
[Operator.Message]: MessageForm,
|
|
||||||
[Operator.Relevant]: RelevantForm,
|
|
||||||
[Operator.RewriteQuestion]: RewriteQuestionForm,
|
|
||||||
[Operator.Baidu]: BaiduForm,
|
|
||||||
[Operator.DuckDuckGo]: DuckDuckGoForm,
|
|
||||||
[Operator.KeywordExtract]: KeywordExtractForm,
|
|
||||||
[Operator.Wikipedia]: WikipediaForm,
|
|
||||||
[Operator.PubMed]: PubMedForm,
|
|
||||||
[Operator.ArXiv]: ArXivForm,
|
|
||||||
[Operator.Google]: GoogleForm,
|
|
||||||
[Operator.Bing]: BingForm,
|
|
||||||
[Operator.GoogleScholar]: GoogleScholarForm,
|
|
||||||
[Operator.DeepL]: DeepLForm,
|
|
||||||
[Operator.GitHub]: GithubForm,
|
|
||||||
[Operator.BaiduFanyi]: BaiduFanyiForm,
|
|
||||||
[Operator.QWeather]: QWeatherForm,
|
|
||||||
[Operator.ExeSQL]: ExeSQLForm,
|
|
||||||
[Operator.Switch]: SwitchForm,
|
|
||||||
[Operator.WenCai]: WenCaiForm,
|
|
||||||
[Operator.AkShare]: AkShareForm,
|
|
||||||
[Operator.YahooFinance]: YahooFinanceForm,
|
|
||||||
[Operator.Jin10]: Jin10Form,
|
|
||||||
[Operator.TuShare]: TuShareForm,
|
|
||||||
[Operator.Crawler]: CrawlerForm,
|
|
||||||
[Operator.Invoke]: InvokeForm,
|
|
||||||
[Operator.Concentrator]: () => <></>,
|
|
||||||
[Operator.Note]: () => <></>,
|
|
||||||
[Operator.Template]: TemplateForm,
|
|
||||||
[Operator.Email]: EmailForm,
|
|
||||||
[Operator.Iteration]: IterationForm,
|
|
||||||
[Operator.IterationStart]: () => <></>,
|
|
||||||
};
|
|
||||||
|
|
||||||
const EmptyContent = () => <div></div>;
|
|
||||||
|
|
||||||
const FormDrawer = ({
|
|
||||||
visible,
|
|
||||||
hideModal,
|
|
||||||
node,
|
|
||||||
singleDebugDrawerVisible,
|
|
||||||
hideSingleDebugDrawer,
|
|
||||||
showSingleDebugDrawer,
|
|
||||||
}: IModalProps<any> & IProps) => {
|
|
||||||
const operatorName: Operator = node?.data.label as Operator;
|
|
||||||
const OperatorForm = FormMap[operatorName] ?? EmptyContent;
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
|
|
||||||
id: node?.id,
|
|
||||||
data: node?.data,
|
|
||||||
});
|
|
||||||
const previousId = useRef<string | undefined>(node?.id);
|
|
||||||
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const { handleValuesChange } = useHandleFormValuesChange(node?.id);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
if (node?.id !== previousId.current) {
|
|
||||||
form.resetFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operatorName === Operator.Categorize) {
|
|
||||||
const items = buildCategorizeListFromObject(
|
|
||||||
get(node, 'data.form.category_description', {}),
|
|
||||||
);
|
|
||||||
const formData = node?.data?.form;
|
|
||||||
if (isPlainObject(formData)) {
|
|
||||||
form.setFieldsValue({ ...formData, items });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
form.setFieldsValue(node?.data?.form);
|
|
||||||
}
|
|
||||||
previousId.current = node?.id;
|
|
||||||
}
|
|
||||||
}, [visible, form, node?.data?.form, node?.id, node, operatorName]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Sheet onOpenChange={hideModal} open={visible}>
|
|
||||||
<SheetContent>
|
|
||||||
<SheetHeader>
|
|
||||||
<Flex vertical>
|
|
||||||
<Flex gap={'middle'} align="center">
|
|
||||||
<OperatorIcon
|
|
||||||
name={operatorName}
|
|
||||||
color={operatorMap[operatorName]?.color}
|
|
||||||
></OperatorIcon>
|
|
||||||
<Flex align="center" gap={'small'} flex={1}>
|
|
||||||
<label htmlFor="" className={styles.title}>
|
|
||||||
{t('title')}
|
|
||||||
</label>
|
|
||||||
{node?.id === BeginId ? (
|
|
||||||
<span>{t(BeginId)}</span>
|
|
||||||
) : (
|
|
||||||
<Input
|
|
||||||
value={name}
|
|
||||||
onBlur={handleNameBlur}
|
|
||||||
onChange={handleNameChange}
|
|
||||||
></Input>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{needsSingleStepDebugging(operatorName) && (
|
|
||||||
<RunTooltip>
|
|
||||||
<Play
|
|
||||||
className="size-5 cursor-pointer"
|
|
||||||
onClick={showSingleDebugDrawer}
|
|
||||||
/>
|
|
||||||
</RunTooltip>
|
|
||||||
)}
|
|
||||||
{/* <CloseOutlined onClick={hideModal} /> */}
|
|
||||||
</Flex>
|
|
||||||
<span className={styles.operatorDescription}>
|
|
||||||
{t(`${lowerFirst(operatorName)}Description`)}
|
|
||||||
</span>
|
|
||||||
</Flex>
|
|
||||||
</SheetHeader>
|
|
||||||
<section className={styles.formWrapper}>
|
|
||||||
{visible && (
|
|
||||||
<FlowFormContext.Provider value={node}>
|
|
||||||
<OperatorForm
|
|
||||||
onValuesChange={handleValuesChange}
|
|
||||||
form={form}
|
|
||||||
node={node}
|
|
||||||
></OperatorForm>
|
|
||||||
</FlowFormContext.Provider>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
</SheetContent>
|
|
||||||
{/* {singleDebugDrawerVisible && (
|
|
||||||
<SingleDebugDrawer
|
|
||||||
visible={singleDebugDrawerVisible}
|
|
||||||
hideModal={hideSingleDebugDrawer}
|
|
||||||
componentId={node?.id}
|
|
||||||
></SingleDebugDrawer>
|
|
||||||
)} */}
|
|
||||||
</Sheet>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FormDrawer;
|
|
|
@ -1,158 +0,0 @@
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import {
|
|
||||||
Sheet,
|
|
||||||
SheetContent,
|
|
||||||
SheetHeader,
|
|
||||||
SheetTitle,
|
|
||||||
} from '@/components/ui/sheet';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { IModalProps } from '@/interfaces/common';
|
|
||||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
|
||||||
import { get, isPlainObject, lowerFirst } from 'lodash';
|
|
||||||
import { Play, X } from 'lucide-react';
|
|
||||||
import { useEffect, useRef } from 'react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
import { BeginId, Operator, operatorMap } from '../constant';
|
|
||||||
import { FlowFormContext } from '../context';
|
|
||||||
import { RunTooltip } from '../flow-tooltip';
|
|
||||||
import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks';
|
|
||||||
import OperatorIcon from '../operator-icon';
|
|
||||||
import {
|
|
||||||
buildCategorizeListFromObject,
|
|
||||||
needsSingleStepDebugging,
|
|
||||||
} from '../utils';
|
|
||||||
import SingleDebugDrawer from './single-debug-drawer';
|
|
||||||
import { useFormConfigMap } from './use-form-config-map';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
node?: RAGFlowNodeType;
|
|
||||||
singleDebugDrawerVisible: IModalProps<any>['visible'];
|
|
||||||
hideSingleDebugDrawer: IModalProps<any>['hideModal'];
|
|
||||||
showSingleDebugDrawer: IModalProps<any>['showModal'];
|
|
||||||
}
|
|
||||||
|
|
||||||
const EmptyContent = () => <div></div>;
|
|
||||||
|
|
||||||
const FormSheet = ({
|
|
||||||
visible,
|
|
||||||
hideModal,
|
|
||||||
node,
|
|
||||||
singleDebugDrawerVisible,
|
|
||||||
hideSingleDebugDrawer,
|
|
||||||
showSingleDebugDrawer,
|
|
||||||
}: IModalProps<any> & IProps) => {
|
|
||||||
const operatorName: Operator = node?.data.label as Operator;
|
|
||||||
|
|
||||||
const FormConfigMap = useFormConfigMap();
|
|
||||||
|
|
||||||
const currentFormMap = FormConfigMap[operatorName];
|
|
||||||
|
|
||||||
const OperatorForm = currentFormMap.component ?? EmptyContent;
|
|
||||||
|
|
||||||
const form = useForm({
|
|
||||||
defaultValues: currentFormMap.defaultValues,
|
|
||||||
resolver: zodResolver(currentFormMap.schema),
|
|
||||||
});
|
|
||||||
|
|
||||||
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
|
|
||||||
id: node?.id,
|
|
||||||
data: node?.data,
|
|
||||||
});
|
|
||||||
|
|
||||||
const previousId = useRef<string | undefined>(node?.id);
|
|
||||||
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const { handleValuesChange } = useHandleFormValuesChange(
|
|
||||||
operatorName,
|
|
||||||
node?.id,
|
|
||||||
form,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible && !form.formState.isDirty) {
|
|
||||||
if (node?.id !== previousId.current) {
|
|
||||||
form.reset();
|
|
||||||
form.clearErrors();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operatorName === Operator.Categorize) {
|
|
||||||
const items = buildCategorizeListFromObject(
|
|
||||||
get(node, 'data.form.category_description', {}),
|
|
||||||
);
|
|
||||||
const formData = node?.data?.form;
|
|
||||||
if (isPlainObject(formData)) {
|
|
||||||
// form.setFieldsValue({ ...formData, items });
|
|
||||||
form.reset({ ...formData, items });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// form.setFieldsValue(node?.data?.form);
|
|
||||||
form.reset(node?.data?.form);
|
|
||||||
}
|
|
||||||
previousId.current = node?.id;
|
|
||||||
}
|
|
||||||
}, [visible, form, node?.data?.form, node?.id, node, operatorName]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Sheet open={visible} modal={false}>
|
|
||||||
<SheetTitle className="hidden"></SheetTitle>
|
|
||||||
<SheetContent className={cn('bg-white top-20 p-0')} closeIcon={false}>
|
|
||||||
<SheetHeader>
|
|
||||||
<section className="flex-col border-b py-2 px-5">
|
|
||||||
<div className="flex items-center gap-2 pb-3">
|
|
||||||
<OperatorIcon
|
|
||||||
name={operatorName}
|
|
||||||
color={operatorMap[operatorName]?.color}
|
|
||||||
></OperatorIcon>
|
|
||||||
<div className="flex items-center gap-1 flex-1">
|
|
||||||
<label htmlFor="">{t('title')}</label>
|
|
||||||
{node?.id === BeginId ? (
|
|
||||||
<span>{t(BeginId)}</span>
|
|
||||||
) : (
|
|
||||||
<Input
|
|
||||||
value={name}
|
|
||||||
onBlur={handleNameBlur}
|
|
||||||
onChange={handleNameChange}
|
|
||||||
></Input>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{needsSingleStepDebugging(operatorName) && (
|
|
||||||
<RunTooltip>
|
|
||||||
<Play
|
|
||||||
className="size-5 cursor-pointer"
|
|
||||||
onClick={showSingleDebugDrawer}
|
|
||||||
/>
|
|
||||||
</RunTooltip>
|
|
||||||
)}
|
|
||||||
<X onClick={hideModal} />
|
|
||||||
</div>
|
|
||||||
<span>{t(`${lowerFirst(operatorName)}Description`)}</span>
|
|
||||||
</section>
|
|
||||||
</SheetHeader>
|
|
||||||
<section className="pt-4">
|
|
||||||
{visible && (
|
|
||||||
<FlowFormContext.Provider value={node}>
|
|
||||||
<OperatorForm
|
|
||||||
onValuesChange={handleValuesChange}
|
|
||||||
form={form}
|
|
||||||
node={node}
|
|
||||||
></OperatorForm>
|
|
||||||
</FlowFormContext.Provider>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
</SheetContent>
|
|
||||||
{singleDebugDrawerVisible && (
|
|
||||||
<SingleDebugDrawer
|
|
||||||
visible={singleDebugDrawerVisible}
|
|
||||||
hideModal={hideSingleDebugDrawer}
|
|
||||||
componentId={node?.id}
|
|
||||||
></SingleDebugDrawer>
|
|
||||||
)}
|
|
||||||
</Sheet>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FormSheet;
|
|
|
@ -1,81 +0,0 @@
|
||||||
import CopyToClipboard from '@/components/copy-to-clipboard';
|
|
||||||
import { useDebugSingle, useFetchInputElements } from '@/hooks/flow-hooks';
|
|
||||||
import { IModalProps } from '@/interfaces/common';
|
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
|
||||||
import { Drawer } from 'antd';
|
|
||||||
import { isEmpty } from 'lodash';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import JsonView from 'react18-json-view';
|
|
||||||
import 'react18-json-view/src/style.css';
|
|
||||||
import DebugContent from '../../debug-content';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
componentId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SingleDebugDrawer = ({
|
|
||||||
componentId,
|
|
||||||
visible,
|
|
||||||
hideModal,
|
|
||||||
}: IModalProps<any> & IProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { data: list } = useFetchInputElements(componentId);
|
|
||||||
const { debugSingle, data, loading } = useDebugSingle();
|
|
||||||
|
|
||||||
const onOk = useCallback(
|
|
||||||
(nextValues: any[]) => {
|
|
||||||
if (componentId) {
|
|
||||||
debugSingle({ component_id: componentId, params: nextValues });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[componentId, debugSingle],
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = JSON.stringify(data, null, 2);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Drawer
|
|
||||||
title={
|
|
||||||
<div className="flex justify-between">
|
|
||||||
{t('flow.testRun')}
|
|
||||||
<CloseOutlined onClick={hideModal} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
width={'100%'}
|
|
||||||
onClose={hideModal}
|
|
||||||
open={visible}
|
|
||||||
getContainer={false}
|
|
||||||
mask={false}
|
|
||||||
placement={'bottom'}
|
|
||||||
height={'95%'}
|
|
||||||
closeIcon={null}
|
|
||||||
>
|
|
||||||
<section className="overflow-y-auto">
|
|
||||||
<DebugContent
|
|
||||||
parameters={list}
|
|
||||||
ok={onOk}
|
|
||||||
isNext={false}
|
|
||||||
loading={loading}
|
|
||||||
submitButtonDisabled={list.length === 0}
|
|
||||||
></DebugContent>
|
|
||||||
{!isEmpty(data) ? (
|
|
||||||
<div className="mt-4 rounded-md bg-slate-200 border border-neutral-200">
|
|
||||||
<div className="flex justify-between p-2">
|
|
||||||
<span>JSON</span>
|
|
||||||
<CopyToClipboard text={content}></CopyToClipboard>
|
|
||||||
</div>
|
|
||||||
<JsonView
|
|
||||||
src={data}
|
|
||||||
displaySize
|
|
||||||
collapseStringsAfterLength={100000000000}
|
|
||||||
className="w-full h-[800px] break-words overflow-auto p-2 bg-slate-100"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</section>
|
|
||||||
</Drawer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SingleDebugDrawer;
|
|
|
@ -1,307 +0,0 @@
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { Operator } from '../constant';
|
|
||||||
import AkShareForm from '../form/akshare-form';
|
|
||||||
import AnswerForm from '../form/answer-form';
|
|
||||||
import ArXivForm from '../form/arxiv-form';
|
|
||||||
import BaiduFanyiForm from '../form/baidu-fanyi-form';
|
|
||||||
import BaiduForm from '../form/baidu-form';
|
|
||||||
import BeginForm from '../form/begin-form';
|
|
||||||
import BingForm from '../form/bing-form';
|
|
||||||
import CategorizeForm from '../form/categorize-form';
|
|
||||||
import CrawlerForm from '../form/crawler-form';
|
|
||||||
import DeepLForm from '../form/deepl-form';
|
|
||||||
import DuckDuckGoForm from '../form/duckduckgo-form';
|
|
||||||
import EmailForm from '../form/email-form';
|
|
||||||
import ExeSQLForm from '../form/exesql-form';
|
|
||||||
import GenerateForm from '../form/generate-form';
|
|
||||||
import GithubForm from '../form/github-form';
|
|
||||||
import GoogleForm from '../form/google-form';
|
|
||||||
import GoogleScholarForm from '../form/google-scholar-form';
|
|
||||||
import InvokeForm from '../form/invoke-form';
|
|
||||||
import IterationForm from '../form/iteration-from';
|
|
||||||
import Jin10Form from '../form/jin10-form';
|
|
||||||
import KeywordExtractForm from '../form/keyword-extract-form';
|
|
||||||
import MessageForm from '../form/message-form';
|
|
||||||
import PubMedForm from '../form/pubmed-form';
|
|
||||||
import QWeatherForm from '../form/qweather-form';
|
|
||||||
import RelevantForm from '../form/relevant-form';
|
|
||||||
import RetrievalForm from '../form/retrieval-form/next';
|
|
||||||
import RewriteQuestionForm from '../form/rewrite-question-form';
|
|
||||||
import SwitchForm from '../form/switch-form';
|
|
||||||
import TemplateForm from '../form/template-form';
|
|
||||||
import TuShareForm from '../form/tushare-form';
|
|
||||||
import WenCaiForm from '../form/wencai-form';
|
|
||||||
import WikipediaForm from '../form/wikipedia-form';
|
|
||||||
import YahooFinanceForm from '../form/yahoo-finance-form';
|
|
||||||
|
|
||||||
export function useFormConfigMap() {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const FormConfigMap = {
|
|
||||||
[Operator.Begin]: {
|
|
||||||
component: BeginForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({
|
|
||||||
name: z
|
|
||||||
.string()
|
|
||||||
.min(1, {
|
|
||||||
message: t('common.namePlaceholder'),
|
|
||||||
})
|
|
||||||
.trim(),
|
|
||||||
age: z
|
|
||||||
.string()
|
|
||||||
.min(1, {
|
|
||||||
message: t('common.namePlaceholder'),
|
|
||||||
})
|
|
||||||
.trim(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.Retrieval]: {
|
|
||||||
component: RetrievalForm,
|
|
||||||
defaultValues: { query: [] },
|
|
||||||
schema: z.object({
|
|
||||||
name: z
|
|
||||||
.string()
|
|
||||||
.min(1, {
|
|
||||||
message: t('common.namePlaceholder'),
|
|
||||||
})
|
|
||||||
.trim(),
|
|
||||||
age: z
|
|
||||||
.string()
|
|
||||||
.min(1, {
|
|
||||||
message: t('common.namePlaceholder'),
|
|
||||||
})
|
|
||||||
.trim(),
|
|
||||||
query: z.array(
|
|
||||||
z.object({
|
|
||||||
type: z.string(),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.Generate]: {
|
|
||||||
component: GenerateForm,
|
|
||||||
defaultValues: {
|
|
||||||
cite: true,
|
|
||||||
prompt: t('flow.promptText'),
|
|
||||||
},
|
|
||||||
schema: z.object({
|
|
||||||
prompt: z.string().min(1, {
|
|
||||||
message: t('flow.promptMessage'),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.Answer]: {
|
|
||||||
component: AnswerForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Categorize]: {
|
|
||||||
component: CategorizeForm,
|
|
||||||
defaultValues: { message_history_window_size: 1 },
|
|
||||||
schema: z.object({
|
|
||||||
message_history_window_size: z.number(),
|
|
||||||
items: z.array(
|
|
||||||
z.object({
|
|
||||||
name: z.string().min(1, t('flow.nameMessage')).trim(),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.Message]: {
|
|
||||||
component: MessageForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Relevant]: {
|
|
||||||
component: RelevantForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.RewriteQuestion]: {
|
|
||||||
component: RewriteQuestionForm,
|
|
||||||
defaultValues: {
|
|
||||||
message_history_window_size: 6,
|
|
||||||
},
|
|
||||||
schema: z.object({
|
|
||||||
llm_id: z.string(),
|
|
||||||
message_history_window_size: z.number(),
|
|
||||||
language: z.string(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.Baidu]: {
|
|
||||||
component: BaiduForm,
|
|
||||||
defaultValues: { top_n: 10 },
|
|
||||||
schema: z.object({ top_n: z.number() }),
|
|
||||||
},
|
|
||||||
[Operator.DuckDuckGo]: {
|
|
||||||
component: DuckDuckGoForm,
|
|
||||||
defaultValues: {
|
|
||||||
top_n: 10,
|
|
||||||
channel: 'text',
|
|
||||||
},
|
|
||||||
schema: z.object({
|
|
||||||
top_n: z.number(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.KeywordExtract]: {
|
|
||||||
component: KeywordExtractForm,
|
|
||||||
defaultValues: { top_n: 3 },
|
|
||||||
schema: z.object({
|
|
||||||
llm_id: z.string(),
|
|
||||||
top_n: z.number(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.Wikipedia]: {
|
|
||||||
component: WikipediaForm,
|
|
||||||
defaultValues: {
|
|
||||||
top_n: 10,
|
|
||||||
},
|
|
||||||
schema: z.object({
|
|
||||||
language: z.string(),
|
|
||||||
top_n: z.number(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.PubMed]: {
|
|
||||||
component: PubMedForm,
|
|
||||||
defaultValues: { top_n: 10 },
|
|
||||||
schema: z.object({
|
|
||||||
top_n: z.number(),
|
|
||||||
email: z.string(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.ArXiv]: {
|
|
||||||
component: ArXivForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Google]: {
|
|
||||||
component: GoogleForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Bing]: {
|
|
||||||
component: BingForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.GoogleScholar]: {
|
|
||||||
component: GoogleScholarForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.DeepL]: {
|
|
||||||
component: DeepLForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.GitHub]: {
|
|
||||||
component: GithubForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.BaiduFanyi]: {
|
|
||||||
component: BaiduFanyiForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.QWeather]: {
|
|
||||||
component: QWeatherForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({
|
|
||||||
web_apikey: z.string(),
|
|
||||||
lang: z.string(),
|
|
||||||
type: z.string(),
|
|
||||||
user_type: z.string(),
|
|
||||||
time_period: z.string().optional(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.ExeSQL]: {
|
|
||||||
component: ExeSQLForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Switch]: {
|
|
||||||
component: SwitchForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.WenCai]: {
|
|
||||||
component: WenCaiForm,
|
|
||||||
defaultValues: {
|
|
||||||
top_n: 20,
|
|
||||||
},
|
|
||||||
schema: z.object({
|
|
||||||
top_n: z.number(),
|
|
||||||
query_type: z.string(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.AkShare]: {
|
|
||||||
component: AkShareForm,
|
|
||||||
defaultValues: {
|
|
||||||
top_n: 10,
|
|
||||||
},
|
|
||||||
schema: z.object({
|
|
||||||
top_n: z.number(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
[Operator.YahooFinance]: {
|
|
||||||
component: YahooFinanceForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Jin10]: {
|
|
||||||
component: Jin10Form,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.TuShare]: {
|
|
||||||
component: TuShareForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Crawler]: {
|
|
||||||
component: CrawlerForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Invoke]: {
|
|
||||||
component: InvokeForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Concentrator]: {
|
|
||||||
component: () => <></>,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Note]: {
|
|
||||||
component: () => <></>,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Template]: {
|
|
||||||
component: TemplateForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Email]: {
|
|
||||||
component: EmailForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.Iteration]: {
|
|
||||||
component: IterationForm,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
[Operator.IterationStart]: {
|
|
||||||
component: () => <></>,
|
|
||||||
defaultValues: {},
|
|
||||||
schema: z.object({}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return FormConfigMap;
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
import { TopNFormField } from '@/components/top-n-item';
|
|
||||||
import { Form } from '@/components/ui/form';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
|
||||||
|
|
||||||
const AkShareForm = ({ form, node }: INextOperatorForm) => {
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<TopNFormField max={99}></TopNFormField>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AkShareForm;
|
|
|
@ -1,5 +0,0 @@
|
||||||
const AnswerForm = () => {
|
|
||||||
return <div></div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AnswerForm;
|
|
|
@ -1,36 +0,0 @@
|
||||||
import TopNItem from '@/components/top-n-item';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Form, Select } from 'antd';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const ArXivForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return ['submittedDate', 'lastUpdatedDate', 'relevance'].map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(x),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
|
|
||||||
<TopNItem initialValue={10}></TopNItem>
|
|
||||||
<Form.Item label={t('sortBy')} name={'sort_by'}>
|
|
||||||
<Select options={options}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ArXivForm;
|
|
|
@ -1,71 +0,0 @@
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Form, Input, Select } from 'antd';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
BaiduFanyiDomainOptions,
|
|
||||||
BaiduFanyiSourceLangOptions,
|
|
||||||
} from '../../constant';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const BaiduFanyiForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return ['translate', 'fieldtranslate'].map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`baiduSecretKeyOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const baiduFanyiOptions = useMemo(() => {
|
|
||||||
return BaiduFanyiDomainOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`baiduDomainOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const baiduFanyiSourceLangOptions = useMemo(() => {
|
|
||||||
return BaiduFanyiSourceLangOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`baiduSourceLangOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<Form.Item label={t('appid')} name={'appid'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('secretKey')} name={'secret_key'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('transType')} name={'trans_type'}>
|
|
||||||
<Select options={options}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item noStyle dependencies={['model_type']}>
|
|
||||||
{({ getFieldValue }) =>
|
|
||||||
getFieldValue('trans_type') === 'fieldtranslate' && (
|
|
||||||
<Form.Item label={t('domain')} name={'domain'}>
|
|
||||||
<Select options={baiduFanyiOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('sourceLang')} name={'source_lang'}>
|
|
||||||
<Select options={baiduFanyiSourceLangOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('targetLang')} name={'target_lang'}>
|
|
||||||
<Select options={baiduFanyiSourceLangOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BaiduFanyiForm;
|
|
|
@ -1,22 +0,0 @@
|
||||||
import { TopNFormField } from '@/components/top-n-item';
|
|
||||||
import { Form } from '@/components/ui/form';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
|
||||||
|
|
||||||
const BaiduForm = ({ form, node }: INextOperatorForm) => {
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<TopNFormField></TopNFormField>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BaiduForm;
|
|
|
@ -1,68 +0,0 @@
|
||||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Form, Input } from 'antd';
|
|
||||||
|
|
||||||
const BeginDynamicOptions = () => {
|
|
||||||
return (
|
|
||||||
<Form.List
|
|
||||||
name="options"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
validator: async (_, names) => {
|
|
||||||
if (!names || names.length < 1) {
|
|
||||||
return Promise.reject(new Error('At least 1 option'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{(fields, { add, remove }, { errors }) => (
|
|
||||||
<>
|
|
||||||
{fields.map((field, index) => (
|
|
||||||
<Form.Item
|
|
||||||
label={index === 0 ? 'Options' : ''}
|
|
||||||
required={false}
|
|
||||||
key={field.key}
|
|
||||||
>
|
|
||||||
<Form.Item
|
|
||||||
{...field}
|
|
||||||
validateTrigger={['onChange', 'onBlur']}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
whitespace: true,
|
|
||||||
message: 'Please input option or delete this field.',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
placeholder="option"
|
|
||||||
style={{ width: '90%', marginRight: 16 }}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
{fields.length > 1 ? (
|
|
||||||
<MinusCircleOutlined
|
|
||||||
className="dynamic-delete-button"
|
|
||||||
onClick={() => remove(field.name)}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Form.Item>
|
|
||||||
))}
|
|
||||||
<Form.Item>
|
|
||||||
<Button
|
|
||||||
type="dashed"
|
|
||||||
onClick={() => add()}
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
block
|
|
||||||
>
|
|
||||||
Add option
|
|
||||||
</Button>
|
|
||||||
<Form.ErrorList errors={errors} />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Form.List>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BeginDynamicOptions;
|
|
|
@ -1,50 +0,0 @@
|
||||||
import { useSetModalState } from '@/hooks/common-hooks';
|
|
||||||
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
|
||||||
import { BeginQuery, IOperatorForm } from '../../interface';
|
|
||||||
|
|
||||||
export const useEditQueryRecord = ({ form, onValuesChange }: IOperatorForm) => {
|
|
||||||
const { setRecord, currentRecord } = useSetSelectedRecord<BeginQuery>();
|
|
||||||
const { visible, hideModal, showModal } = useSetModalState();
|
|
||||||
const [index, setIndex] = useState(-1);
|
|
||||||
|
|
||||||
const otherThanCurrentQuery = useMemo(() => {
|
|
||||||
const query: BeginQuery[] = form?.getFieldValue('query') || [];
|
|
||||||
return query.filter((item, idx) => idx !== index);
|
|
||||||
}, [form, index]);
|
|
||||||
|
|
||||||
const handleEditRecord = useCallback(
|
|
||||||
(record: BeginQuery) => {
|
|
||||||
const query: BeginQuery[] = form?.getFieldValue('query') || [];
|
|
||||||
|
|
||||||
const nextQuery: BeginQuery[] =
|
|
||||||
index > -1 ? query.toSpliced(index, 1, record) : [...query, record];
|
|
||||||
|
|
||||||
onValuesChange?.(
|
|
||||||
{ query: nextQuery },
|
|
||||||
{ query: nextQuery, prologue: form?.getFieldValue('prologue') },
|
|
||||||
);
|
|
||||||
hideModal();
|
|
||||||
},
|
|
||||||
[form, hideModal, index, onValuesChange],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleShowModal = useCallback(
|
|
||||||
(idx?: number, record?: BeginQuery) => {
|
|
||||||
setIndex(idx ?? -1);
|
|
||||||
setRecord(record ?? ({} as BeginQuery));
|
|
||||||
showModal();
|
|
||||||
},
|
|
||||||
[setRecord, showModal],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
ok: handleEditRecord,
|
|
||||||
currentRecord,
|
|
||||||
setRecord,
|
|
||||||
visible,
|
|
||||||
hideModal,
|
|
||||||
showModal: handleShowModal,
|
|
||||||
otherThanCurrentQuery,
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,24 +0,0 @@
|
||||||
.dynamicInputVariable {
|
|
||||||
background-color: #ebe9e950;
|
|
||||||
:global(.ant-collapse-content) {
|
|
||||||
background-color: #f6f6f657;
|
|
||||||
}
|
|
||||||
:global(.ant-collapse-content-box) {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
margin-bottom: 20px;
|
|
||||||
.title {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addButton {
|
|
||||||
color: rgb(22, 119, 255);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.addButton {
|
|
||||||
color: rgb(22, 119, 255);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Form, Input } from 'antd';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { BeginQuery, IOperatorForm } from '../../interface';
|
|
||||||
import { useEditQueryRecord } from './hooks';
|
|
||||||
import { ModalForm } from './paramater-modal';
|
|
||||||
import QueryTable from './query-table';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
type FieldType = {
|
|
||||||
prologue?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const BeginForm = ({ onValuesChange, form }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const {
|
|
||||||
ok,
|
|
||||||
currentRecord,
|
|
||||||
visible,
|
|
||||||
hideModal,
|
|
||||||
showModal,
|
|
||||||
otherThanCurrentQuery,
|
|
||||||
} = useEditQueryRecord({
|
|
||||||
form,
|
|
||||||
onValuesChange,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleDeleteRecord = useCallback(
|
|
||||||
(idx: number) => {
|
|
||||||
const query = form?.getFieldValue('query') || [];
|
|
||||||
const nextQuery = query.filter(
|
|
||||||
(item: BeginQuery, index: number) => index !== idx,
|
|
||||||
);
|
|
||||||
onValuesChange?.(
|
|
||||||
{ query: nextQuery },
|
|
||||||
{ query: nextQuery, prologue: form?.getFieldValue('prologue') },
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[form, onValuesChange],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form.Provider
|
|
||||||
onFormFinish={(name, { values }) => {
|
|
||||||
if (name === 'queryForm') {
|
|
||||||
ok(values as BeginQuery);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Form
|
|
||||||
name="basicForm"
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
name={'prologue'}
|
|
||||||
label={t('chat.setAnOpener')}
|
|
||||||
tooltip={t('chat.setAnOpenerTip')}
|
|
||||||
initialValue={t('chat.setAnOpenerInitial')}
|
|
||||||
>
|
|
||||||
<Input.TextArea autoSize={{ minRows: 5 }} />
|
|
||||||
</Form.Item>
|
|
||||||
{/* Create a hidden field to make Form instance record this */}
|
|
||||||
<Form.Item name="query" noStyle />
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
shouldUpdate={(prevValues, curValues) =>
|
|
||||||
prevValues.query !== curValues.query
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const query: BeginQuery[] = getFieldValue('query') || [];
|
|
||||||
return (
|
|
||||||
<QueryTable
|
|
||||||
data={query}
|
|
||||||
showModal={showModal}
|
|
||||||
deleteRecord={handleDeleteRecord}
|
|
||||||
></QueryTable>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
htmlType="button"
|
|
||||||
style={{ margin: '0 8px' }}
|
|
||||||
onClick={() => showModal()}
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
block
|
|
||||||
className={styles.addButton}
|
|
||||||
>
|
|
||||||
{t('flow.addItem')}
|
|
||||||
</Button>
|
|
||||||
{visible && (
|
|
||||||
<ModalForm
|
|
||||||
visible={visible}
|
|
||||||
hideModal={hideModal}
|
|
||||||
initialValue={currentRecord}
|
|
||||||
onOk={ok}
|
|
||||||
otherThanCurrentQuery={otherThanCurrentQuery}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
</Form.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BeginForm;
|
|
|
@ -1,124 +0,0 @@
|
||||||
import { useResetFormOnCloseModal } from '@/hooks/logic-hooks';
|
|
||||||
import { IModalProps } from '@/interfaces/common';
|
|
||||||
import { Form, Input, Modal, Select, Switch } from 'antd';
|
|
||||||
import { DefaultOptionType } from 'antd/es/select';
|
|
||||||
import { useEffect, useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { BeginQueryType, BeginQueryTypeIconMap } from '../../constant';
|
|
||||||
import { BeginQuery } from '../../interface';
|
|
||||||
import BeginDynamicOptions from './begin-dynamic-options';
|
|
||||||
|
|
||||||
export const ModalForm = ({
|
|
||||||
visible,
|
|
||||||
initialValue,
|
|
||||||
hideModal,
|
|
||||||
otherThanCurrentQuery,
|
|
||||||
}: IModalProps<BeginQuery> & {
|
|
||||||
initialValue: BeginQuery;
|
|
||||||
otherThanCurrentQuery: BeginQuery[];
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return Object.values(BeginQueryType).reduce<DefaultOptionType[]>(
|
|
||||||
(pre, cur) => {
|
|
||||||
const Icon = BeginQueryTypeIconMap[cur];
|
|
||||||
|
|
||||||
return [
|
|
||||||
...pre,
|
|
||||||
{
|
|
||||||
label: (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Icon
|
|
||||||
className={`size-${cur === BeginQueryType.Options ? 4 : 5}`}
|
|
||||||
></Icon>
|
|
||||||
{cur}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
value: cur,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useResetFormOnCloseModal({
|
|
||||||
form,
|
|
||||||
visible: visible,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
form.setFieldsValue(initialValue);
|
|
||||||
}, [form, initialValue]);
|
|
||||||
|
|
||||||
const onOk = () => {
|
|
||||||
form.submit();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={t('flow.variableSettings')}
|
|
||||||
open={visible}
|
|
||||||
onOk={onOk}
|
|
||||||
onCancel={hideModal}
|
|
||||||
centered
|
|
||||||
>
|
|
||||||
<Form form={form} layout="vertical" name="queryForm" autoComplete="false">
|
|
||||||
<Form.Item
|
|
||||||
name="type"
|
|
||||||
label="Type"
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
initialValue={BeginQueryType.Line}
|
|
||||||
>
|
|
||||||
<Select options={options} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="key"
|
|
||||||
label="Key"
|
|
||||||
rules={[
|
|
||||||
{ required: true },
|
|
||||||
() => ({
|
|
||||||
validator(_, value) {
|
|
||||||
if (
|
|
||||||
!value ||
|
|
||||||
!otherThanCurrentQuery.some((x) => x.key === value)
|
|
||||||
) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
return Promise.reject(new Error('The key cannot be repeated!'));
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="name" label="Name" rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="optional"
|
|
||||||
label={'Optional'}
|
|
||||||
valuePropName="checked"
|
|
||||||
initialValue={false}
|
|
||||||
>
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
shouldUpdate={(prevValues, curValues) =>
|
|
||||||
prevValues.type !== curValues.type
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const type: BeginQueryType = getFieldValue('type');
|
|
||||||
return (
|
|
||||||
type === BeginQueryType.Options && (
|
|
||||||
<BeginDynamicOptions></BeginDynamicOptions>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,92 +0,0 @@
|
||||||
import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
|
||||||
import type { TableProps } from 'antd';
|
|
||||||
import { Collapse, Space, Table, Tooltip } from 'antd';
|
|
||||||
import { BeginQuery } from '../../interface';
|
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
data: BeginQuery[];
|
|
||||||
deleteRecord(index: number): void;
|
|
||||||
showModal(index: number, record: BeginQuery): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QueryTable = ({ data, deleteRecord, showModal }: IProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const columns: TableProps<BeginQuery>['columns'] = [
|
|
||||||
{
|
|
||||||
title: 'Key',
|
|
||||||
dataIndex: 'key',
|
|
||||||
key: 'key',
|
|
||||||
ellipsis: {
|
|
||||||
showTitle: false,
|
|
||||||
},
|
|
||||||
render: (key) => (
|
|
||||||
<Tooltip placement="topLeft" title={key}>
|
|
||||||
{key}
|
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('flow.name'),
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
ellipsis: {
|
|
||||||
showTitle: false,
|
|
||||||
},
|
|
||||||
render: (name) => (
|
|
||||||
<Tooltip placement="topLeft" title={name}>
|
|
||||||
{name}
|
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('flow.type'),
|
|
||||||
dataIndex: 'type',
|
|
||||||
key: 'type',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('flow.optional'),
|
|
||||||
dataIndex: 'optional',
|
|
||||||
key: 'optional',
|
|
||||||
render: (optional) => (optional ? 'Yes' : 'No'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('common.action'),
|
|
||||||
key: 'action',
|
|
||||||
render: (_, record, idx) => (
|
|
||||||
<Space>
|
|
||||||
<EditOutlined onClick={() => showModal(idx, record)} />
|
|
||||||
<DeleteOutlined
|
|
||||||
className="cursor-pointer"
|
|
||||||
onClick={() => deleteRecord(idx)}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Collapse
|
|
||||||
defaultActiveKey={['1']}
|
|
||||||
className={styles.dynamicInputVariable}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
label: <span className={styles.title}>{t('flow.input')}</span>,
|
|
||||||
children: (
|
|
||||||
<Table<BeginQuery>
|
|
||||||
columns={columns}
|
|
||||||
dataSource={data}
|
|
||||||
pagination={false}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QueryTable;
|
|
|
@ -1,42 +0,0 @@
|
||||||
import TopNItem from '@/components/top-n-item';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Form, Input, Select } from 'antd';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { BingCountryOptions, BingLanguageOptions } from '../../constant';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const BingForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return ['Webpages', 'News'].map((x) => ({ label: x, value: x }));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<TopNItem initialValue={10}></TopNItem>
|
|
||||||
<Form.Item label={t('channel')} name={'channel'}>
|
|
||||||
<Select options={options}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('apiKey')} name={'api_key'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('country')} name={'country'}>
|
|
||||||
<Select options={BingCountryOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('language')} name={'language'}>
|
|
||||||
<Select options={BingLanguageOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BingForm;
|
|
|
@ -1,265 +0,0 @@
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
|
||||||
Collapsible,
|
|
||||||
CollapsibleContent,
|
|
||||||
CollapsibleTrigger,
|
|
||||||
} from '@/components/ui/collapsible';
|
|
||||||
import {
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { RAGFlowSelect } from '@/components/ui/select';
|
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { useUpdateNodeInternals } from '@xyflow/react';
|
|
||||||
import humanId from 'human-id';
|
|
||||||
import trim from 'lodash/trim';
|
|
||||||
import { ChevronsUpDown, X } from 'lucide-react';
|
|
||||||
import {
|
|
||||||
ChangeEventHandler,
|
|
||||||
FocusEventHandler,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form';
|
|
||||||
import { Operator } from '../../constant';
|
|
||||||
import { useBuildFormSelectOptions } from '../../form-hooks';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
nodeId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface INameInputProps {
|
|
||||||
value?: string;
|
|
||||||
onChange?: (value: string) => void;
|
|
||||||
otherNames?: string[];
|
|
||||||
validate(error?: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getOtherFieldValues = (
|
|
||||||
form: UseFormReturn,
|
|
||||||
formListName: string = 'items',
|
|
||||||
index: number,
|
|
||||||
latestField: string,
|
|
||||||
) =>
|
|
||||||
(form.getValues(formListName) ?? [])
|
|
||||||
.map((x: any) => x[latestField])
|
|
||||||
.filter(
|
|
||||||
(x: string) =>
|
|
||||||
x !== form.getValues(`${formListName}.${index}.${latestField}`),
|
|
||||||
);
|
|
||||||
|
|
||||||
const NameInput = ({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
otherNames,
|
|
||||||
validate,
|
|
||||||
}: INameInputProps) => {
|
|
||||||
const [name, setName] = useState<string | undefined>();
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const handleNameChange: ChangeEventHandler<HTMLInputElement> = useCallback(
|
|
||||||
(e) => {
|
|
||||||
const val = e.target.value;
|
|
||||||
setName(val);
|
|
||||||
const trimmedVal = trim(val);
|
|
||||||
// trigger validation
|
|
||||||
if (otherNames?.some((x) => x === trimmedVal)) {
|
|
||||||
validate(t('nameRepeatedMsg'));
|
|
||||||
} else if (trimmedVal === '') {
|
|
||||||
validate(t('nameRequiredMsg'));
|
|
||||||
} else {
|
|
||||||
validate('');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[otherNames, validate, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleNameBlur: FocusEventHandler<HTMLInputElement> = useCallback(
|
|
||||||
(e) => {
|
|
||||||
const val = e.target.value;
|
|
||||||
if (otherNames?.every((x) => x !== val) && trim(val) !== '') {
|
|
||||||
onChange?.(val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[onChange, otherNames],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setName(value);
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Input
|
|
||||||
value={name}
|
|
||||||
onChange={handleNameChange}
|
|
||||||
onBlur={handleNameBlur}
|
|
||||||
></Input>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const FormSet = ({ nodeId, index }: IProps & { index: number }) => {
|
|
||||||
const form = useFormContext();
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
const buildCategorizeToOptions = useBuildFormSelectOptions(
|
|
||||||
Operator.Categorize,
|
|
||||||
nodeId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const buildFieldName = useCallback(
|
|
||||||
(name: string) => {
|
|
||||||
return `items.${index}.${name}`;
|
|
||||||
},
|
|
||||||
[index],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={buildFieldName('name')}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('categoryName')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<NameInput
|
|
||||||
{...field}
|
|
||||||
otherNames={getOtherFieldValues(form, 'items', index, 'name')}
|
|
||||||
validate={(error?: string) => {
|
|
||||||
const fieldName = buildFieldName('name');
|
|
||||||
if (error) {
|
|
||||||
form.setError(fieldName, { message: error });
|
|
||||||
} else {
|
|
||||||
form.clearErrors(fieldName);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
></NameInput>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={buildFieldName('description')}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('description')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea {...field} rows={3} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={buildFieldName('examples')}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('examples')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea {...field} rows={3} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={buildFieldName('to')}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('nextStep')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect
|
|
||||||
{...field}
|
|
||||||
allowClear
|
|
||||||
options={buildCategorizeToOptions(
|
|
||||||
getOtherFieldValues(form, 'items', index, 'to'),
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="index"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="hidden">
|
|
||||||
<FormLabel>{t('examples')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const DynamicCategorize = ({ nodeId }: IProps) => {
|
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
|
||||||
const form = useFormContext();
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
const { fields, remove, append } = useFieldArray({
|
|
||||||
name: 'items',
|
|
||||||
control: form.control,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleAdd = () => {
|
|
||||||
append({
|
|
||||||
name: humanId(),
|
|
||||||
});
|
|
||||||
if (nodeId) updateNodeInternals(nodeId);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col gap-4 ">
|
|
||||||
{fields.map((field, index) => (
|
|
||||||
<Collapsible key={field.id}>
|
|
||||||
<div className="flex items-center justify-between space-x-4">
|
|
||||||
<h4 className="font-bold">
|
|
||||||
{form.getValues(`items.${index}.name`)}
|
|
||||||
</h4>
|
|
||||||
<CollapsibleTrigger asChild>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="w-9 p-0"
|
|
||||||
onClick={() => remove(index)}
|
|
||||||
>
|
|
||||||
<X className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
<Button variant="ghost" size="sm" className="w-9 p-0">
|
|
||||||
<ChevronsUpDown className="h-4 w-4" />
|
|
||||||
<span className="sr-only">Toggle</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
</div>
|
|
||||||
<CollapsibleContent>
|
|
||||||
<FormSet nodeId={nodeId} index={index}></FormSet>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</Collapsible>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<Button type={'button'} onClick={handleAdd}>
|
|
||||||
<PlusOutlined />
|
|
||||||
{t('addCategory')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DynamicCategorize;
|
|
|
@ -1,45 +0,0 @@
|
||||||
import {
|
|
||||||
ICategorizeItem,
|
|
||||||
ICategorizeItemResult,
|
|
||||||
} from '@/interfaces/database/flow';
|
|
||||||
import omit from 'lodash/omit';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the list in the following form into an object
|
|
||||||
* {
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"name": "Categorize 1",
|
|
||||||
"description": "111",
|
|
||||||
"examples": "ddd",
|
|
||||||
"to": "Retrieval:LazyEelsStick"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const buildCategorizeObjectFromList = (list: Array<ICategorizeItem>) => {
|
|
||||||
return list.reduce<ICategorizeItemResult>((pre, cur) => {
|
|
||||||
if (cur?.name) {
|
|
||||||
pre[cur.name] = omit(cur, 'name');
|
|
||||||
}
|
|
||||||
return pre;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useHandleFormValuesChange = ({
|
|
||||||
onValuesChange,
|
|
||||||
}: IOperatorForm) => {
|
|
||||||
const handleValuesChange = useCallback(
|
|
||||||
(changedValues: any, values: any) => {
|
|
||||||
onValuesChange?.(changedValues, {
|
|
||||||
...omit(values, 'items'),
|
|
||||||
category_description: buildCategorizeObjectFromList(values.items),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[onValuesChange],
|
|
||||||
);
|
|
||||||
|
|
||||||
return { handleValuesChange };
|
|
||||||
};
|
|
|
@ -1,13 +0,0 @@
|
||||||
@lightBackgroundColor: rgba(150, 150, 150, 0.07);
|
|
||||||
@darkBackgroundColor: rgba(150, 150, 150, 0.12);
|
|
||||||
|
|
||||||
.caseCard {
|
|
||||||
:global(.ant-collapse-content) {
|
|
||||||
background-color: @darkBackgroundColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.addButton {
|
|
||||||
color: rgb(22, 119, 255);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { LargeModelFormField } from '@/components/large-model-form-field';
|
|
||||||
import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item';
|
|
||||||
import { Form } from '@/components/ui/form';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
|
||||||
import DynamicCategorize from './dynamic-categorize';
|
|
||||||
|
|
||||||
const CategorizeForm = ({ form, node }: INextOperatorForm) => {
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6 p-5 overflow-auto max-h-[76vh]"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<LargeModelFormField></LargeModelFormField>
|
|
||||||
<MessageHistoryWindowSizeFormField></MessageHistoryWindowSizeFormField>
|
|
||||||
<DynamicCategorize nodeId={node?.id}></DynamicCategorize>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CategorizeForm;
|
|
|
@ -1,130 +0,0 @@
|
||||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
|
||||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Collapse, Flex, Form, Input, Select } from 'antd';
|
|
||||||
import { PropsWithChildren, useCallback } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
node?: RAGFlowNodeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum VariableType {
|
|
||||||
Reference = 'reference',
|
|
||||||
Input = 'input',
|
|
||||||
}
|
|
||||||
|
|
||||||
const getVariableName = (type: string) =>
|
|
||||||
type === VariableType.Reference ? 'component_id' : 'value';
|
|
||||||
|
|
||||||
const DynamicVariableForm = ({ node }: IProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const valueOptions = useBuildComponentIdSelectOptions(
|
|
||||||
node?.id,
|
|
||||||
node?.parentId,
|
|
||||||
);
|
|
||||||
const form = Form.useFormInstance();
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{ value: VariableType.Reference, label: t('flow.reference') },
|
|
||||||
{ value: VariableType.Input, label: t('flow.text') },
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleTypeChange = useCallback(
|
|
||||||
(name: number) => () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
form.setFieldValue(['query', name, 'component_id'], undefined);
|
|
||||||
form.setFieldValue(['query', name, 'value'], undefined);
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
[form],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form.List name="query">
|
|
||||||
{(fields, { add, remove }) => (
|
|
||||||
<>
|
|
||||||
{fields.map(({ key, name, ...restField }) => (
|
|
||||||
<Flex key={key} gap={10} align={'baseline'}>
|
|
||||||
<Form.Item
|
|
||||||
{...restField}
|
|
||||||
name={[name, 'type']}
|
|
||||||
className={styles.variableType}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
options={options}
|
|
||||||
onChange={handleTypeChange(name)}
|
|
||||||
></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item noStyle dependencies={[name, 'type']}>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const type = getFieldValue(['query', name, 'type']);
|
|
||||||
return (
|
|
||||||
<Form.Item
|
|
||||||
{...restField}
|
|
||||||
name={[name, getVariableName(type)]}
|
|
||||||
className={styles.variableValue}
|
|
||||||
>
|
|
||||||
{type === VariableType.Reference ? (
|
|
||||||
<Select
|
|
||||||
placeholder={t('common.pleaseSelect')}
|
|
||||||
options={valueOptions}
|
|
||||||
></Select>
|
|
||||||
) : (
|
|
||||||
<Input placeholder={t('common.pleaseInput')} />
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
<Form.Item>
|
|
||||||
<Button
|
|
||||||
type="dashed"
|
|
||||||
onClick={() => add({ type: VariableType.Reference })}
|
|
||||||
block
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
className={styles.addButton}
|
|
||||||
>
|
|
||||||
{t('flow.addVariable')}
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Form.List>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function FormCollapse({
|
|
||||||
children,
|
|
||||||
title,
|
|
||||||
}: PropsWithChildren<{ title: string }>) {
|
|
||||||
return (
|
|
||||||
<Collapse
|
|
||||||
className={styles.dynamicInputVariable}
|
|
||||||
defaultActiveKey={['1']}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
label: <span className={styles.title}>{title}</span>,
|
|
||||||
children,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DynamicInputVariable = ({ node }: IProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<FormCollapse title={t('flow.input')}>
|
|
||||||
<DynamicVariableForm node={node}></DynamicVariableForm>
|
|
||||||
</FormCollapse>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DynamicInputVariable;
|
|
|
@ -1,22 +0,0 @@
|
||||||
.dynamicInputVariable {
|
|
||||||
background-color: #ebe9e950;
|
|
||||||
:global(.ant-collapse-content) {
|
|
||||||
background-color: #f6f6f657;
|
|
||||||
}
|
|
||||||
margin-bottom: 20px;
|
|
||||||
.title {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.variableType {
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
.variableValue {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addButton {
|
|
||||||
color: rgb(22, 119, 255);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,138 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { SideDown } from '@/assets/icon/Icon';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
|
||||||
Collapsible,
|
|
||||||
CollapsibleContent,
|
|
||||||
CollapsibleTrigger,
|
|
||||||
} from '@/components/ui/collapsible';
|
|
||||||
import {
|
|
||||||
FormControl,
|
|
||||||
FormDescription,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { RAGFlowSelect } from '@/components/ui/select';
|
|
||||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
|
||||||
import { Plus, Trash2 } from 'lucide-react';
|
|
||||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
node?: RAGFlowNodeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum VariableType {
|
|
||||||
Reference = 'reference',
|
|
||||||
Input = 'input',
|
|
||||||
}
|
|
||||||
|
|
||||||
const getVariableName = (type: string) =>
|
|
||||||
type === VariableType.Reference ? 'component_id' : 'value';
|
|
||||||
|
|
||||||
export function DynamicVariableForm({ node }: IProps) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const form = useFormContext();
|
|
||||||
const { fields, remove, append } = useFieldArray({
|
|
||||||
name: 'query',
|
|
||||||
control: form.control,
|
|
||||||
});
|
|
||||||
|
|
||||||
const valueOptions = useBuildComponentIdSelectOptions(
|
|
||||||
node?.id,
|
|
||||||
node?.parentId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{ value: VariableType.Reference, label: t('flow.reference') },
|
|
||||||
{ value: VariableType.Input, label: t('flow.text') },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{fields.map((field, index) => {
|
|
||||||
const typeField = `query.${index}.type`;
|
|
||||||
const typeValue = form.watch(typeField);
|
|
||||||
return (
|
|
||||||
<div key={field.id} className="flex items-center gap-1">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={typeField}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="w-2/5">
|
|
||||||
<FormDescription />
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect
|
|
||||||
{...field}
|
|
||||||
placeholder={t('common.pleaseSelect')}
|
|
||||||
options={options}
|
|
||||||
onChange={(val) => {
|
|
||||||
field.onChange(val);
|
|
||||||
form.resetField(`query.${index}.value`);
|
|
||||||
form.resetField(`query.${index}.component_id`);
|
|
||||||
}}
|
|
||||||
></RAGFlowSelect>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={`query.${index}.${getVariableName(typeValue)}`}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex-1">
|
|
||||||
<FormDescription />
|
|
||||||
<FormControl>
|
|
||||||
{typeValue === VariableType.Reference ? (
|
|
||||||
<RAGFlowSelect
|
|
||||||
placeholder={t('common.pleaseSelect')}
|
|
||||||
{...field}
|
|
||||||
options={valueOptions}
|
|
||||||
></RAGFlowSelect>
|
|
||||||
) : (
|
|
||||||
<Input placeholder={t('common.pleaseInput')} {...field} />
|
|
||||||
)}
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Trash2
|
|
||||||
className="cursor-pointer mx-3 size-4 text-colors-text-functional-danger"
|
|
||||||
onClick={() => remove(index)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<Button onClick={append} className="mt-4" variant={'outline'} size={'sm'}>
|
|
||||||
<Plus />
|
|
||||||
{t('flow.addVariable')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DynamicInputVariable({ node }: IProps) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Collapsible defaultOpen className="group/collapsible">
|
|
||||||
<CollapsibleTrigger className="flex justify-between w-full pb-2">
|
|
||||||
<span className="font-bold text-2xl text-colors-text-neutral-strong">
|
|
||||||
{t('flow.input')}
|
|
||||||
</span>
|
|
||||||
<Button variant={'icon'} size={'icon'}>
|
|
||||||
<SideDown />
|
|
||||||
</Button>
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
<CollapsibleContent>
|
|
||||||
<DynamicVariableForm node={node}></DynamicVariableForm>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</Collapsible>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { Form } from 'antd';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
|
|
||||||
const ConcentratorForm = ({ onValuesChange, form }: IOperatorForm) => {
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
labelCol={{ span: 8 }}
|
|
||||||
wrapperCol={{ span: 16 }}
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
></Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ConcentratorForm;
|
|
|
@ -1,38 +0,0 @@
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Form, Input, Select } from 'antd';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { CrawlerResultOptions } from '../../constant';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
const CrawlerForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
const crawlerResultOptions = useMemo(() => {
|
|
||||||
return CrawlerResultOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`crawlerResultOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<Form.Item label={t('proxy')} name={'proxy'}>
|
|
||||||
<Input placeholder="like: http://127.0.0.1:8888"></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('extractType')}
|
|
||||||
name={'extract_type'}
|
|
||||||
initialValue="markdown"
|
|
||||||
>
|
|
||||||
<Select options={crawlerResultOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CrawlerForm;
|
|
|
@ -1,36 +0,0 @@
|
||||||
import TopNItem from '@/components/top-n-item';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Form, Select } from 'antd';
|
|
||||||
import { DeepLSourceLangOptions, DeepLTargetLangOptions } from '../../constant';
|
|
||||||
import { useBuildSortOptions } from '../../form-hooks';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const DeepLForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
const options = useBuildSortOptions();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<TopNItem initialValue={5}></TopNItem>
|
|
||||||
<Form.Item label={t('authKey')} name={'auth_key'}>
|
|
||||||
<Select options={options}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('sourceLang')} name={'source_lang'}>
|
|
||||||
<Select options={DeepLSourceLangOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('targetLang')} name={'target_lang'}>
|
|
||||||
<Select options={DeepLTargetLangOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeepLForm;
|
|
|
@ -1,52 +0,0 @@
|
||||||
import { TopNFormField } from '@/components/top-n-item';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { RAGFlowSelect } from '@/components/ui/select';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { Channel } from '../../constant';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
|
||||||
|
|
||||||
const DuckDuckGoForm = ({ form, node }: INextOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return Object.values(Channel).map((x) => ({ value: x, label: t(x) }));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<TopNFormField></TopNFormField>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="channel"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel tooltip={t('channelTip')}>{t('channel')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect {...field} options={options} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DuckDuckGoForm;
|
|
|
@ -1,53 +0,0 @@
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Form, Input } from 'antd';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const EmailForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
|
|
||||||
{/* SMTP服务器配置 */}
|
|
||||||
<Form.Item label={t('smtpServer')} name={'smtp_server'}>
|
|
||||||
<Input placeholder="smtp.example.com" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('smtpPort')} name={'smtp_port'}>
|
|
||||||
<Input type="number" placeholder="587" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('senderEmail')} name={'email'}>
|
|
||||||
<Input placeholder="sender@example.com" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('authCode')} name={'password'}>
|
|
||||||
<Input.Password placeholder="your_password" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('senderName')} name={'sender_name'}>
|
|
||||||
<Input placeholder="Sender Name" />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
{/* 动态参数说明 */}
|
|
||||||
<div style={{ marginBottom: 24 }}>
|
|
||||||
<h4>{t('dynamicParameters')}</h4>
|
|
||||||
<div>{t('jsonFormatTip')}</div>
|
|
||||||
<pre style={{ background: '#f5f5f5', padding: 12, borderRadius: 4 }}>
|
|
||||||
{`{
|
|
||||||
"to_email": "recipient@example.com",
|
|
||||||
"cc_email": "cc@example.com",
|
|
||||||
"subject": "Email Subject",
|
|
||||||
"content": "Email Content"
|
|
||||||
}`}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EmailForm;
|
|
|
@ -1,88 +0,0 @@
|
||||||
import LLMSelect from '@/components/llm-select';
|
|
||||||
import TopNItem from '@/components/top-n-item';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { useTestDbConnect } from '@/hooks/flow-hooks';
|
|
||||||
import { Button, Flex, Form, Input, InputNumber, Select } from 'antd';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { ExeSQLOptions } from '../../constant';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const ExeSQLForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
const { testDbConnect, loading } = useTestDbConnect();
|
|
||||||
|
|
||||||
const handleTest = useCallback(async () => {
|
|
||||||
const ret = await form?.validateFields();
|
|
||||||
testDbConnect(ret);
|
|
||||||
}, [form, testDbConnect]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<Form.Item
|
|
||||||
name={'llm_id'}
|
|
||||||
label={t('model', { keyPrefix: 'chat' })}
|
|
||||||
tooltip={t('modelTip', { keyPrefix: 'chat' })}
|
|
||||||
>
|
|
||||||
<LLMSelect></LLMSelect>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('dbType')}
|
|
||||||
name={'db_type'}
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
>
|
|
||||||
<Select options={ExeSQLOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('database')}
|
|
||||||
name={'database'}
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('username')}
|
|
||||||
name={'username'}
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('host')} name={'host'} rules={[{ required: true }]}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('port')} name={'port'} rules={[{ required: true }]}>
|
|
||||||
<InputNumber></InputNumber>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('password')}
|
|
||||||
name={'password'}
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
>
|
|
||||||
<Input.Password></Input.Password>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('loop')}
|
|
||||||
name={'loop'}
|
|
||||||
tooltip={t('loopTip')}
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
>
|
|
||||||
<InputNumber></InputNumber>
|
|
||||||
</Form.Item>
|
|
||||||
<TopNItem initialValue={30} max={1000}></TopNItem>
|
|
||||||
<Flex justify={'end'}>
|
|
||||||
<Button type={'primary'} loading={loading} onClick={handleTest}>
|
|
||||||
Test
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ExeSQLForm;
|
|
|
@ -1,78 +0,0 @@
|
||||||
import { NextLLMSelect } from '@/components/llm-select';
|
|
||||||
import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item';
|
|
||||||
import { PromptEditor } from '@/components/prompt-editor';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { Switch } from '@/components/ui/switch';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
|
|
||||||
const GenerateForm = ({ form }: INextOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="llm_id"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel tooltip={t('chat.modelTip')}>
|
|
||||||
{t('chat.model')}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<NextLLMSelect {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="prompt"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel tooltip={t('knowledgeConfiguration.promptTip')}>
|
|
||||||
{t('flow.systemPrompt')}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<PromptEditor {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="cite"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel tooltip={t('flow.citeTip')}>
|
|
||||||
{t('flow.cite')}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Switch {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<MessageHistoryWindowSizeFormField></MessageHistoryWindowSizeFormField>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GenerateForm;
|
|
|
@ -1,21 +0,0 @@
|
||||||
import TopNItem from '@/components/top-n-item';
|
|
||||||
import { Form } from 'antd';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const GithubForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<TopNItem initialValue={5}></TopNItem>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GithubForm;
|
|
|
@ -1,34 +0,0 @@
|
||||||
import TopNItem from '@/components/top-n-item';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Form, Input, Select } from 'antd';
|
|
||||||
import { GoogleCountryOptions, GoogleLanguageOptions } from '../../constant';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const GoogleForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<TopNItem initialValue={10}></TopNItem>
|
|
||||||
<Form.Item label={t('apiKey')} name={'api_key'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('country')} name={'country'}>
|
|
||||||
<Select options={GoogleCountryOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('language')} name={'language'}>
|
|
||||||
<Select options={GoogleLanguageOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GoogleForm;
|
|
|
@ -1,75 +0,0 @@
|
||||||
import TopNItem from '@/components/top-n-item';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { DatePicker, DatePickerProps, Form, Select, Switch } from 'antd';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import { useBuildSortOptions } from '../../form-hooks';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const YearPicker = ({
|
|
||||||
onChange,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
onChange?: (val: number | undefined) => void;
|
|
||||||
value?: number | undefined;
|
|
||||||
}) => {
|
|
||||||
const handleChange: DatePickerProps['onChange'] = useCallback(
|
|
||||||
(val: any) => {
|
|
||||||
const nextVal = val?.format('YYYY');
|
|
||||||
onChange?.(nextVal ? Number(nextVal) : undefined);
|
|
||||||
},
|
|
||||||
[onChange],
|
|
||||||
);
|
|
||||||
// The year needs to be converted into a number and saved to the backend
|
|
||||||
const nextValue = useMemo(() => {
|
|
||||||
if (value) {
|
|
||||||
return dayjs(value.toString());
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
return <DatePicker picker="year" onChange={handleChange} value={nextValue} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const GoogleScholarForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const options = useBuildSortOptions();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<TopNItem initialValue={5}></TopNItem>
|
|
||||||
<Form.Item
|
|
||||||
label={t('sortBy')}
|
|
||||||
name={'sort_by'}
|
|
||||||
initialValue={'relevance'}
|
|
||||||
>
|
|
||||||
<Select options={options}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('yearLow')} name={'year_low'}>
|
|
||||||
<YearPicker />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('yearHigh')} name={'year_high'}>
|
|
||||||
<YearPicker />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('patents')}
|
|
||||||
name={'patents'}
|
|
||||||
valuePropName="checked"
|
|
||||||
initialValue={true}
|
|
||||||
>
|
|
||||||
<Switch></Switch>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GoogleScholarForm;
|
|
|
@ -1,130 +0,0 @@
|
||||||
import { EditableCell, EditableRow } from '@/components/editable-cell';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { DeleteOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Collapse, Flex, Input, Select, Table, TableProps } from 'antd';
|
|
||||||
import { trim } from 'lodash';
|
|
||||||
import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
|
|
||||||
import { IInvokeVariable, RAGFlowNodeType } from '../../interface';
|
|
||||||
import { useHandleOperateParameters } from './hooks';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
node?: RAGFlowNodeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const components = {
|
|
||||||
body: {
|
|
||||||
row: EditableRow,
|
|
||||||
cell: EditableCell,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const DynamicVariablesForm = ({ node }: IProps) => {
|
|
||||||
const nodeId = node?.id;
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const options = useBuildComponentIdSelectOptions(nodeId, node?.parentId);
|
|
||||||
const {
|
|
||||||
dataSource,
|
|
||||||
handleAdd,
|
|
||||||
handleRemove,
|
|
||||||
handleSave,
|
|
||||||
handleComponentIdChange,
|
|
||||||
handleValueChange,
|
|
||||||
} = useHandleOperateParameters(nodeId!);
|
|
||||||
|
|
||||||
const columns: TableProps<IInvokeVariable>['columns'] = [
|
|
||||||
{
|
|
||||||
title: t('key'),
|
|
||||||
dataIndex: 'key',
|
|
||||||
key: 'key',
|
|
||||||
onCell: (record: IInvokeVariable) => ({
|
|
||||||
record,
|
|
||||||
editable: true,
|
|
||||||
dataIndex: 'key',
|
|
||||||
title: 'key',
|
|
||||||
handleSave,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('componentId'),
|
|
||||||
dataIndex: 'component_id',
|
|
||||||
key: 'component_id',
|
|
||||||
align: 'center',
|
|
||||||
width: 140,
|
|
||||||
render(text, record) {
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
allowClear
|
|
||||||
options={options}
|
|
||||||
value={text}
|
|
||||||
disabled={trim(record.value) !== ''}
|
|
||||||
onChange={handleComponentIdChange(record)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('value'),
|
|
||||||
dataIndex: 'value',
|
|
||||||
key: 'value',
|
|
||||||
align: 'center',
|
|
||||||
width: 140,
|
|
||||||
render(text, record) {
|
|
||||||
return (
|
|
||||||
<Input
|
|
||||||
value={text}
|
|
||||||
disabled={!!record.component_id}
|
|
||||||
onChange={handleValueChange(record)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('operation'),
|
|
||||||
dataIndex: 'operation',
|
|
||||||
width: 20,
|
|
||||||
key: 'operation',
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
render(_, record) {
|
|
||||||
return <DeleteOutlined onClick={handleRemove(record.id)} />;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Collapse
|
|
||||||
className={styles.dynamicParameterVariable}
|
|
||||||
defaultActiveKey={['1']}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
label: (
|
|
||||||
<Flex justify={'space-between'}>
|
|
||||||
<span className={styles.title}>{t('parameter')}</span>
|
|
||||||
<Button size="small" onClick={handleAdd}>
|
|
||||||
{t('add')}
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
),
|
|
||||||
children: (
|
|
||||||
<Table
|
|
||||||
dataSource={dataSource}
|
|
||||||
columns={columns}
|
|
||||||
rowKey={'id'}
|
|
||||||
components={components}
|
|
||||||
rowClassName={() => styles.editableRow}
|
|
||||||
scroll={{ x: true }}
|
|
||||||
bordered
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DynamicVariablesForm;
|
|
|
@ -1,97 +0,0 @@
|
||||||
import get from 'lodash/get';
|
|
||||||
import {
|
|
||||||
ChangeEventHandler,
|
|
||||||
MouseEventHandler,
|
|
||||||
useCallback,
|
|
||||||
useMemo,
|
|
||||||
} from 'react';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
import { IGenerateParameter, IInvokeVariable } from '../../interface';
|
|
||||||
import useGraphStore from '../../store';
|
|
||||||
|
|
||||||
export const useHandleOperateParameters = (nodeId: string) => {
|
|
||||||
const { getNode, updateNodeForm } = useGraphStore((state) => state);
|
|
||||||
const node = getNode(nodeId);
|
|
||||||
const dataSource: IGenerateParameter[] = useMemo(
|
|
||||||
() => get(node, 'data.form.variables', []) as IGenerateParameter[],
|
|
||||||
[node],
|
|
||||||
);
|
|
||||||
|
|
||||||
const changeValue = useCallback(
|
|
||||||
(row: IInvokeVariable, field: string, value: string) => {
|
|
||||||
const newData = [...dataSource];
|
|
||||||
const index = newData.findIndex((item) => row.id === item.id);
|
|
||||||
const item = newData[index];
|
|
||||||
newData.splice(index, 1, {
|
|
||||||
...item,
|
|
||||||
[field]: value,
|
|
||||||
});
|
|
||||||
|
|
||||||
updateNodeForm(nodeId, { variables: newData });
|
|
||||||
},
|
|
||||||
[dataSource, nodeId, updateNodeForm],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleComponentIdChange = useCallback(
|
|
||||||
(row: IInvokeVariable) => (value: string) => {
|
|
||||||
changeValue(row, 'component_id', value);
|
|
||||||
},
|
|
||||||
[changeValue],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleValueChange = useCallback(
|
|
||||||
(row: IInvokeVariable): ChangeEventHandler<HTMLInputElement> =>
|
|
||||||
(e) => {
|
|
||||||
changeValue(row, 'value', e.target.value);
|
|
||||||
},
|
|
||||||
[changeValue],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleRemove = useCallback(
|
|
||||||
(id?: string) => () => {
|
|
||||||
const newData = dataSource.filter((item) => item.id !== id);
|
|
||||||
updateNodeForm(nodeId, { variables: newData });
|
|
||||||
},
|
|
||||||
[updateNodeForm, nodeId, dataSource],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleAdd: MouseEventHandler = useCallback(
|
|
||||||
(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
updateNodeForm(nodeId, {
|
|
||||||
variables: [
|
|
||||||
...dataSource,
|
|
||||||
{
|
|
||||||
id: uuid(),
|
|
||||||
key: '',
|
|
||||||
component_id: undefined,
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[dataSource, nodeId, updateNodeForm],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSave = (row: IGenerateParameter) => {
|
|
||||||
const newData = [...dataSource];
|
|
||||||
const index = newData.findIndex((item) => row.id === item.id);
|
|
||||||
const item = newData[index];
|
|
||||||
newData.splice(index, 1, {
|
|
||||||
...item,
|
|
||||||
...row,
|
|
||||||
});
|
|
||||||
|
|
||||||
updateNodeForm(nodeId, { variables: newData });
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
handleAdd,
|
|
||||||
handleRemove,
|
|
||||||
handleComponentIdChange,
|
|
||||||
handleValueChange,
|
|
||||||
handleSave,
|
|
||||||
dataSource,
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,44 +0,0 @@
|
||||||
.editableRow {
|
|
||||||
:global(.editable-cell) {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.editable-cell-value-wrap) {
|
|
||||||
padding: 5px 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 30px !important;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
:global(.editable-cell-value-wrap) {
|
|
||||||
padding: 4px 11px;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dynamicParameterVariable {
|
|
||||||
background-color: #ebe9e950;
|
|
||||||
:global(.ant-collapse-content) {
|
|
||||||
background-color: #f6f6f634;
|
|
||||||
}
|
|
||||||
:global(.ant-collapse-content-box) {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
margin-bottom: 20px;
|
|
||||||
.title {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.variableType {
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
.variableValue {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addButton {
|
|
||||||
color: rgb(22, 119, 255);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
import Editor, { loader } from '@monaco-editor/react';
|
|
||||||
import { Form, Input, InputNumber, Select, Space, Switch } from 'antd';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicVariablesForm from './dynamic-variables';
|
|
||||||
|
|
||||||
loader.config({ paths: { vs: '/vs' } });
|
|
||||||
|
|
||||||
enum Method {
|
|
||||||
GET = 'GET',
|
|
||||||
POST = 'POST',
|
|
||||||
PUT = 'PUT',
|
|
||||||
}
|
|
||||||
|
|
||||||
const MethodOptions = [Method.GET, Method.POST, Method.PUT].map((x) => ({
|
|
||||||
label: x,
|
|
||||||
value: x,
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface TimeoutInputProps {
|
|
||||||
value?: number;
|
|
||||||
onChange?: (value: number | null) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TimeoutInput = ({ value, onChange }: TimeoutInputProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<Space>
|
|
||||||
<InputNumber value={value} onChange={onChange} /> {t('common.s')}
|
|
||||||
</Space>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const InvokeForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<Form.Item name={'url'} label={t('flow.url')}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={'method'}
|
|
||||||
label={t('flow.method')}
|
|
||||||
initialValue={Method.GET}
|
|
||||||
>
|
|
||||||
<Select options={MethodOptions} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name={'timeout'} label={t('flow.timeout')}>
|
|
||||||
<TimeoutInput></TimeoutInput>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name={'headers'} label={t('flow.headers')}>
|
|
||||||
<Editor height={200} defaultLanguage="json" theme="vs-dark" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name={'proxy'} label={t('flow.proxy')}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={'clean_html'}
|
|
||||||
label={t('flow.cleanHtml')}
|
|
||||||
tooltip={t('flow.cleanHtmlTip')}
|
|
||||||
>
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<DynamicVariablesForm node={node}></DynamicVariablesForm>
|
|
||||||
</Form>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default InvokeForm;
|
|
|
@ -1,94 +0,0 @@
|
||||||
import { CommaIcon, SemicolonIcon } from '@/assets/icon/Icon';
|
|
||||||
import { Form, Select } from 'antd';
|
|
||||||
import {
|
|
||||||
CornerDownLeft,
|
|
||||||
IndentIncrease,
|
|
||||||
Minus,
|
|
||||||
Slash,
|
|
||||||
Underline,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const optionList = [
|
|
||||||
{
|
|
||||||
value: ',',
|
|
||||||
icon: CommaIcon,
|
|
||||||
text: 'comma',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '\n',
|
|
||||||
icon: CornerDownLeft,
|
|
||||||
text: 'lineBreak',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'tab',
|
|
||||||
icon: IndentIncrease,
|
|
||||||
text: 'tab',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '_',
|
|
||||||
icon: Underline,
|
|
||||||
text: 'underline',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '/',
|
|
||||||
icon: Slash,
|
|
||||||
text: 'diagonal',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '-',
|
|
||||||
icon: Minus,
|
|
||||||
text: 'minus',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: ';',
|
|
||||||
icon: SemicolonIcon,
|
|
||||||
text: 'semicolon',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const IterationForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return optionList.map((x) => {
|
|
||||||
let Icon = x.icon;
|
|
||||||
|
|
||||||
return {
|
|
||||||
value: x.value,
|
|
||||||
label: (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Icon className={'size-4'}></Icon>
|
|
||||||
{t(`flow.delimiterOptions.${x.text}`)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<Form.Item
|
|
||||||
name={['delimiter']}
|
|
||||||
label={t('knowledgeDetails.delimiter')}
|
|
||||||
initialValue={`\\n!?;。;!?`}
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
tooltip={t('flow.delimiterTip')}
|
|
||||||
>
|
|
||||||
<Select options={options}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default IterationForm;
|
|
|
@ -1,145 +0,0 @@
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Form, Input, Select } from 'antd';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
Jin10CalendarDatashapeOptions,
|
|
||||||
Jin10CalendarTypeOptions,
|
|
||||||
Jin10FlashTypeOptions,
|
|
||||||
Jin10SymbolsDatatypeOptions,
|
|
||||||
Jin10SymbolsTypeOptions,
|
|
||||||
Jin10TypeOptions,
|
|
||||||
} from '../../constant';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const Jin10Form = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const jin10TypeOptions = useMemo(() => {
|
|
||||||
return Jin10TypeOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`jin10TypeOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const jin10FlashTypeOptions = useMemo(() => {
|
|
||||||
return Jin10FlashTypeOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`jin10FlashTypeOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const jin10CalendarTypeOptions = useMemo(() => {
|
|
||||||
return Jin10CalendarTypeOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`jin10CalendarTypeOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const jin10CalendarDatashapeOptions = useMemo(() => {
|
|
||||||
return Jin10CalendarDatashapeOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`jin10CalendarDatashapeOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const jin10SymbolsTypeOptions = useMemo(() => {
|
|
||||||
return Jin10SymbolsTypeOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`jin10SymbolsTypeOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const jin10SymbolsDatatypeOptions = useMemo(() => {
|
|
||||||
return Jin10SymbolsDatatypeOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`jin10SymbolsDatatypeOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<Form.Item label={t('type')} name={'type'} initialValue={'flash'}>
|
|
||||||
<Select options={jin10TypeOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('secretKey')} name={'secret_key'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item noStyle dependencies={['type']}>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const type = getFieldValue('type');
|
|
||||||
switch (type) {
|
|
||||||
case 'flash':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item label={t('flashType')} name={'flash_type'}>
|
|
||||||
<Select options={jin10FlashTypeOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('contain')} name={'contain'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('filter')} name={'filter'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'calendar':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item label={t('calendarType')} name={'calendar_type'}>
|
|
||||||
<Select options={jin10CalendarTypeOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('calendarDatashape')}
|
|
||||||
name={'calendar_datashape'}
|
|
||||||
>
|
|
||||||
<Select options={jin10CalendarDatashapeOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'symbols':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item label={t('symbolsType')} name={'symbols_type'}>
|
|
||||||
<Select options={jin10SymbolsTypeOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('symbolsDatatype')}
|
|
||||||
name={'symbols_datatype'}
|
|
||||||
>
|
|
||||||
<Select options={jin10SymbolsDatatypeOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'news':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item label={t('contain')} name={'contain'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('filter')} name={'filter'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Jin10Form;
|
|
|
@ -1,48 +0,0 @@
|
||||||
import { NextLLMSelect } from '@/components/llm-select';
|
|
||||||
import { TopNFormField } from '@/components/top-n-item';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
|
||||||
|
|
||||||
const KeywordExtractForm = ({ form, node }: INextOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="llm_id"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel tooltip={t('chat.modelTip')}>
|
|
||||||
{t('chat.model')}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<NextLLMSelect {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<TopNFormField></TopNFormField>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default KeywordExtractForm;
|
|
|
@ -1,16 +0,0 @@
|
||||||
.dynamicDeleteButton {
|
|
||||||
position: relative;
|
|
||||||
top: 4px;
|
|
||||||
margin: 0 8px;
|
|
||||||
color: #999;
|
|
||||||
font-size: 24px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
&:hover {
|
|
||||||
color: #777;
|
|
||||||
}
|
|
||||||
&[disabled] {
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
|
||||||
import { PlusCircle, Trash2 } from 'lucide-react';
|
|
||||||
import { useFieldArray } from 'react-hook-form';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
|
|
||||||
const MessageForm = ({ form }: INextOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { fields, append, remove } = useFieldArray({
|
|
||||||
name: 'messages',
|
|
||||||
control: form.control,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('flow.msg')}</FormLabel>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{fields.map((field, index) => (
|
|
||||||
<div key={field.id} className="flex items-start gap-2">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={`messages.${index}`}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex-1">
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
{...field}
|
|
||||||
placeholder={t('flow.messagePlaceholder')}
|
|
||||||
rows={5}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{fields.length > 1 && (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
type="button"
|
|
||||||
onClick={() => remove(index)}
|
|
||||||
className="cursor-pointer text-colors-text-functional-danger"
|
|
||||||
>
|
|
||||||
<Trash2 className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => append(' ')} // "" will cause the inability to add, refer to: https://github.com/orgs/react-hook-form/discussions/8485#discussioncomment-2961861
|
|
||||||
className="w-full mt-4"
|
|
||||||
>
|
|
||||||
<PlusCircle className="mr-2 h-4 w-4" />
|
|
||||||
{t('flow.addMessage')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MessageForm;
|
|
|
@ -1,46 +0,0 @@
|
||||||
import { TopNFormField } from '@/components/top-n-item';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
|
||||||
|
|
||||||
const PubMedForm = ({ form, node }: INextOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<TopNFormField></TopNFormField>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="email"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel tooltip={t('emailTip')}>{t('email')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PubMedForm;
|
|
|
@ -1,157 +0,0 @@
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { RAGFlowSelect } from '@/components/ui/select';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import {
|
|
||||||
QWeatherLangOptions,
|
|
||||||
QWeatherTimePeriodOptions,
|
|
||||||
QWeatherTypeOptions,
|
|
||||||
QWeatherUserTypeOptions,
|
|
||||||
} from '../../constant';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
|
||||||
|
|
||||||
enum FormFieldName {
|
|
||||||
Type = 'type',
|
|
||||||
UserType = 'user_type',
|
|
||||||
}
|
|
||||||
|
|
||||||
const QWeatherForm = ({ form, node }: INextOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const typeValue = form.watch(FormFieldName.Type);
|
|
||||||
|
|
||||||
const qWeatherLangOptions = useMemo(() => {
|
|
||||||
return QWeatherLangOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`flow.qWeatherLangOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const qWeatherTypeOptions = useMemo(() => {
|
|
||||||
return QWeatherTypeOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`flow.qWeatherTypeOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const qWeatherUserTypeOptions = useMemo(() => {
|
|
||||||
return QWeatherUserTypeOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`flow.qWeatherUserTypeOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const getQWeatherTimePeriodOptions = useCallback(() => {
|
|
||||||
let options = QWeatherTimePeriodOptions;
|
|
||||||
const userType = form.getValues(FormFieldName.UserType);
|
|
||||||
if (userType === 'free') {
|
|
||||||
options = options.slice(0, 3);
|
|
||||||
}
|
|
||||||
return options.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`flow.qWeatherTimePeriodOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [form, t]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="web_apikey"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('flow.webApiKey')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="lang"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('flow.lang')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect
|
|
||||||
{...field}
|
|
||||||
options={qWeatherLangOptions}
|
|
||||||
></RAGFlowSelect>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={FormFieldName.Type}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('flow.type')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect
|
|
||||||
{...field}
|
|
||||||
options={qWeatherTypeOptions}
|
|
||||||
></RAGFlowSelect>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={FormFieldName.UserType}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('flow.userType')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect
|
|
||||||
{...field}
|
|
||||||
options={qWeatherUserTypeOptions}
|
|
||||||
></RAGFlowSelect>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{typeValue === 'weather' && (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={'time_period'}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('flow.timePeriod')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect
|
|
||||||
{...field}
|
|
||||||
options={getQWeatherTimePeriodOptions()}
|
|
||||||
></RAGFlowSelect>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QWeatherForm;
|
|
|
@ -1,53 +0,0 @@
|
||||||
import { Edge } from '@xyflow/react';
|
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import { useCallback, useEffect } from 'react';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import useGraphStore from '../../store';
|
|
||||||
|
|
||||||
export const useBuildRelevantOptions = () => {
|
|
||||||
const nodes = useGraphStore((state) => state.nodes);
|
|
||||||
|
|
||||||
const buildRelevantOptions = useCallback(
|
|
||||||
(toList: string[]) => {
|
|
||||||
return nodes
|
|
||||||
.filter(
|
|
||||||
(x) => !toList.some((y) => y === x.id), // filter out selected values in other to fields from the current drop-down box options
|
|
||||||
)
|
|
||||||
.map((x) => ({ label: x.data.name, value: x.id }));
|
|
||||||
},
|
|
||||||
[nodes],
|
|
||||||
);
|
|
||||||
|
|
||||||
return buildRelevantOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTargetOfEdge = (edges: Edge[], sourceHandle: string) =>
|
|
||||||
edges.find((x) => x.sourceHandle === sourceHandle)?.target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* monitor changes in the connection and synchronize the target to the yes and no fields of the form
|
|
||||||
* similar to the categorize-form's useHandleFormValuesChange method
|
|
||||||
* @param param0
|
|
||||||
*/
|
|
||||||
export const useWatchConnectionChanges = ({ nodeId, form }: IOperatorForm) => {
|
|
||||||
const edges = useGraphStore((state) => state.edges);
|
|
||||||
const getNode = useGraphStore((state) => state.getNode);
|
|
||||||
const node = getNode(nodeId);
|
|
||||||
|
|
||||||
const watchFormChanges = useCallback(() => {
|
|
||||||
if (node) {
|
|
||||||
form?.setFieldsValue(pick(node, ['yes', 'no']));
|
|
||||||
}
|
|
||||||
}, [node, form]);
|
|
||||||
|
|
||||||
const watchConnectionChanges = useCallback(() => {
|
|
||||||
const edgeList = edges.filter((x) => x.source === nodeId);
|
|
||||||
const yes = getTargetOfEdge(edgeList, 'yes');
|
|
||||||
const no = getTargetOfEdge(edgeList, 'no');
|
|
||||||
form?.setFieldsValue({ yes, no });
|
|
||||||
}, [edges, nodeId, form]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
watchFormChanges();
|
|
||||||
}, [watchFormChanges]);
|
|
||||||
};
|
|
|
@ -1,49 +0,0 @@
|
||||||
import LLMSelect from '@/components/llm-select';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Form, Select } from 'antd';
|
|
||||||
import { Operator } from '../../constant';
|
|
||||||
import { useBuildFormSelectOptions } from '../../form-hooks';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import { useWatchConnectionChanges } from './hooks';
|
|
||||||
|
|
||||||
const RelevantForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
const buildRelevantOptions = useBuildFormSelectOptions(
|
|
||||||
Operator.Relevant,
|
|
||||||
node?.id,
|
|
||||||
);
|
|
||||||
useWatchConnectionChanges({ nodeId: node?.id, form });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
labelCol={{ span: 4 }}
|
|
||||||
wrapperCol={{ span: 20 }}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
>
|
|
||||||
<Form.Item
|
|
||||||
name={'llm_id'}
|
|
||||||
label={t('model', { keyPrefix: 'chat' })}
|
|
||||||
tooltip={t('modelTip', { keyPrefix: 'chat' })}
|
|
||||||
>
|
|
||||||
<LLMSelect></LLMSelect>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('yes')} name={'yes'}>
|
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
options={buildRelevantOptions([form?.getFieldValue('no')])}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('no')} name={'no'}>
|
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
options={buildRelevantOptions([form?.getFieldValue('yes')])}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RelevantForm;
|
|
|
@ -1,54 +0,0 @@
|
||||||
import KnowledgeBaseItem from '@/components/knowledge-base-item';
|
|
||||||
import Rerank from '@/components/rerank';
|
|
||||||
import SimilaritySlider from '@/components/similarity-slider';
|
|
||||||
import TopNItem from '@/components/top-n-item';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import type { FormProps } from 'antd';
|
|
||||||
import { Form, Input } from 'antd';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
type FieldType = {
|
|
||||||
top_n?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
|
|
||||||
console.log('Success:', values);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFinishFailed: FormProps<FieldType>['onFinishFailed'] = (errorInfo) => {
|
|
||||||
console.log('Failed:', errorInfo);
|
|
||||||
};
|
|
||||||
|
|
||||||
const RetrievalForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
onFinish={onFinish}
|
|
||||||
onFinishFailed={onFinishFailed}
|
|
||||||
autoComplete="off"
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
form={form}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<SimilaritySlider
|
|
||||||
isTooltipShown
|
|
||||||
vectorSimilarityWeightName="keywords_similarity_weight"
|
|
||||||
></SimilaritySlider>
|
|
||||||
<TopNItem></TopNItem>
|
|
||||||
<Rerank></Rerank>
|
|
||||||
<KnowledgeBaseItem></KnowledgeBaseItem>
|
|
||||||
<Form.Item
|
|
||||||
name={'empty_response'}
|
|
||||||
label={t('emptyResponse', { keyPrefix: 'chat' })}
|
|
||||||
tooltip={t('emptyResponseTip', { keyPrefix: 'chat' })}
|
|
||||||
>
|
|
||||||
<Input.TextArea placeholder="" rows={4} />
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RetrievalForm;
|
|
|
@ -1,59 +0,0 @@
|
||||||
import { KnowledgeBaseFormField } from '@/components/knowledge-base-item';
|
|
||||||
import { RerankFormFields } from '@/components/rerank';
|
|
||||||
import { SimilaritySliderFormField } from '@/components/similarity-slider';
|
|
||||||
import { TopNFormField } from '@/components/top-n-item';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
|
||||||
|
|
||||||
const RetrievalForm = ({ form, node }: INextOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<SimilaritySliderFormField
|
|
||||||
vectorSimilarityWeightName="keywords_similarity_weight"
|
|
||||||
isTooltipShown
|
|
||||||
></SimilaritySliderFormField>
|
|
||||||
<TopNFormField></TopNFormField>
|
|
||||||
<RerankFormFields></RerankFormFields>
|
|
||||||
<KnowledgeBaseFormField></KnowledgeBaseFormField>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="empty_response"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('chat.emptyResponse')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea
|
|
||||||
placeholder={t('common.namePlaceholder')}
|
|
||||||
{...field}
|
|
||||||
autoComplete="off"
|
|
||||||
rows={4}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RetrievalForm;
|
|
|
@ -1,68 +0,0 @@
|
||||||
import { NextLLMSelect } from '@/components/llm-select';
|
|
||||||
import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { RAGFlowSelect } from '@/components/ui/select';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { GoogleLanguageOptions } from '../../constant';
|
|
||||||
import { INextOperatorForm } from '../../interface';
|
|
||||||
|
|
||||||
const RewriteQuestionForm = ({ form }: INextOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
className="space-y-6"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="llm_id"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel tooltip={t('chat.modelTip')}>
|
|
||||||
{t('chat.model')}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<NextLLMSelect {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="language"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel tooltip={t('chat.languageTip')}>
|
|
||||||
{t('chat.language')}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect
|
|
||||||
options={GoogleLanguageOptions}
|
|
||||||
allowClear={true}
|
|
||||||
{...field}
|
|
||||||
></RAGFlowSelect>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MessageHistoryWindowSizeFormField></MessageHistoryWindowSizeFormField>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RewriteQuestionForm;
|
|
|
@ -1,21 +0,0 @@
|
||||||
@lightBackgroundColor: rgba(150, 150, 150, 0.07);
|
|
||||||
@darkBackgroundColor: rgba(150, 150, 150, 0.12);
|
|
||||||
|
|
||||||
.caseCard {
|
|
||||||
background-color: @lightBackgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conditionCard {
|
|
||||||
background-color: @darkBackgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.elseCase {
|
|
||||||
background-color: @lightBackgroundColor;
|
|
||||||
padding: 12px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addButton {
|
|
||||||
color: rgb(22, 119, 255);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
|
@ -1,204 +0,0 @@
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Card, Divider, Form, Input, Select } from 'antd';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import {
|
|
||||||
Operator,
|
|
||||||
SwitchElseTo,
|
|
||||||
SwitchLogicOperatorOptions,
|
|
||||||
SwitchOperatorOptions,
|
|
||||||
} from '../../constant';
|
|
||||||
import { useBuildFormSelectOptions } from '../../form-hooks';
|
|
||||||
import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import { getOtherFieldValues } from '../../utils';
|
|
||||||
|
|
||||||
import { ISwitchForm } from '@/interfaces/database/flow';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const buildCategorizeToOptions = useBuildFormSelectOptions(
|
|
||||||
Operator.Switch,
|
|
||||||
node?.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
const getSelectedConditionTos = () => {
|
|
||||||
const conditions: ISwitchForm['conditions'] =
|
|
||||||
form?.getFieldValue('conditions');
|
|
||||||
|
|
||||||
return conditions?.filter((x) => !!x).map((x) => x?.to) ?? [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const switchOperatorOptions = useMemo(() => {
|
|
||||||
return SwitchOperatorOptions.map((x) => ({
|
|
||||||
value: x.value,
|
|
||||||
label: t(`flow.switchOperatorOptions.${x.label}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const switchLogicOperatorOptions = useMemo(() => {
|
|
||||||
return SwitchLogicOperatorOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`flow.switchLogicOperatorOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const componentIdOptions = useBuildComponentIdSelectOptions(
|
|
||||||
node?.id,
|
|
||||||
node?.parentId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
name="dynamic_form_complex"
|
|
||||||
autoComplete="off"
|
|
||||||
initialValues={{ conditions: [{}] }}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<Form.List name="conditions">
|
|
||||||
{(fields, { add, remove }) => (
|
|
||||||
<div style={{ display: 'flex', rowGap: 16, flexDirection: 'column' }}>
|
|
||||||
{fields.map((field) => {
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
size="small"
|
|
||||||
title={`Case ${field.name + 1}`}
|
|
||||||
key={field.key}
|
|
||||||
className={styles.caseCard}
|
|
||||||
extra={
|
|
||||||
<CloseOutlined
|
|
||||||
onClick={() => {
|
|
||||||
remove(field.name);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Form.Item noStyle dependencies={[field.name, 'items']}>
|
|
||||||
{({ getFieldValue }) =>
|
|
||||||
getFieldValue(['conditions', field.name, 'items'])
|
|
||||||
?.length > 1 && (
|
|
||||||
<Form.Item
|
|
||||||
label={t('flow.logicalOperator')}
|
|
||||||
name={[field.name, 'logical_operator']}
|
|
||||||
>
|
|
||||||
<Select options={switchLogicOperatorOptions} />
|
|
||||||
</Form.Item>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('flow.nextStep')}
|
|
||||||
name={[field.name, 'to']}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
options={buildCategorizeToOptions([
|
|
||||||
form?.getFieldValue(SwitchElseTo),
|
|
||||||
...getOtherFieldValues(
|
|
||||||
form!,
|
|
||||||
'conditions',
|
|
||||||
field,
|
|
||||||
'to',
|
|
||||||
),
|
|
||||||
])}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label="Condition">
|
|
||||||
<Form.List name={[field.name, 'items']}>
|
|
||||||
{(subFields, subOpt) => (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
rowGap: 16,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{subFields.map((subField) => (
|
|
||||||
<Card
|
|
||||||
key={subField.key}
|
|
||||||
title={null}
|
|
||||||
size="small"
|
|
||||||
className={styles.conditionCard}
|
|
||||||
bordered
|
|
||||||
extra={
|
|
||||||
<CloseOutlined
|
|
||||||
onClick={() => {
|
|
||||||
subOpt.remove(subField.name);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Form.Item
|
|
||||||
label={t('flow.componentId')}
|
|
||||||
name={[subField.name, 'cpn_id']}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
placeholder={t('flow.componentId')}
|
|
||||||
options={componentIdOptions}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('flow.operator')}
|
|
||||||
name={[subField.name, 'operator']}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
placeholder={t('flow.operator')}
|
|
||||||
options={switchOperatorOptions}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('flow.value')}
|
|
||||||
name={[subField.name, 'value']}
|
|
||||||
>
|
|
||||||
<Input placeholder={t('flow.value')} />
|
|
||||||
</Form.Item>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
form?.setFieldValue(
|
|
||||||
['conditions', field.name, 'logical_operator'],
|
|
||||||
SwitchLogicOperatorOptions[0],
|
|
||||||
);
|
|
||||||
subOpt.add({
|
|
||||||
operator: SwitchOperatorOptions[0].value,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
block
|
|
||||||
className={styles.addButton}
|
|
||||||
>
|
|
||||||
+ Add Condition
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Form.List>
|
|
||||||
</Form.Item>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
<Button onClick={() => add()} block className={styles.addButton}>
|
|
||||||
+ Add Case
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Form.List>
|
|
||||||
<Divider />
|
|
||||||
<Form.Item
|
|
||||||
label={'ELSE'}
|
|
||||||
name={[SwitchElseTo]}
|
|
||||||
className={styles.elseCase}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
options={buildCategorizeToOptions(getSelectedConditionTos())}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SwitchForm;
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { PromptEditor } from '@/components/prompt-editor';
|
|
||||||
import { Form } from 'antd';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
|
|
||||||
const TemplateForm = ({ onValuesChange, form }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<Form.Item name={['content']} label={t('flow.content')}>
|
|
||||||
<PromptEditor></PromptEditor>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TemplateForm;
|
|
|
@ -1,83 +0,0 @@
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { DatePicker, DatePickerProps, Form, Input, Select } from 'antd';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import { TuShareSrcOptions } from '../../constant';
|
|
||||||
import { IOperatorForm } from '../../interface';
|
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
|
|
||||||
const DateTimePicker = ({
|
|
||||||
onChange,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
onChange?: (val: number | undefined) => void;
|
|
||||||
value?: number | undefined;
|
|
||||||
}) => {
|
|
||||||
const handleChange: DatePickerProps['onChange'] = useCallback(
|
|
||||||
(val: any) => {
|
|
||||||
const nextVal = val?.format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
onChange?.(nextVal ? nextVal : undefined);
|
|
||||||
},
|
|
||||||
[onChange],
|
|
||||||
);
|
|
||||||
// The value needs to be converted into a string and saved to the backend
|
|
||||||
const nextValue = useMemo(() => {
|
|
||||||
if (value) {
|
|
||||||
return dayjs(value);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DatePicker
|
|
||||||
showTime
|
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
|
||||||
onChange={handleChange}
|
|
||||||
value={nextValue}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const TuShareForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
const tuShareSrcOptions = useMemo(() => {
|
|
||||||
return TuShareSrcOptions.map((x) => ({
|
|
||||||
value: x,
|
|
||||||
label: t(`tuShareSrcOptions.${x}`),
|
|
||||||
}));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
name="basic"
|
|
||||||
autoComplete="off"
|
|
||||||
form={form}
|
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
layout={'vertical'}
|
|
||||||
>
|
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
|
||||||
<Form.Item
|
|
||||||
label={t('token')}
|
|
||||||
name={'token'}
|
|
||||||
tooltip={'Get from https://tushare.pro/'}
|
|
||||||
>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('src')} name={'src'}>
|
|
||||||
<Select options={tuShareSrcOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('startDate')} name={'start_date'}>
|
|
||||||
<DateTimePicker />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('endDate')} name={'end_date'}>
|
|
||||||
<DateTimePicker />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('keyword')} name={'keyword'}>
|
|
||||||
<Input></Input>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TuShareForm;
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue