Feat: Cancel a running data flow test #9869 (#10257)

### What problem does this PR solve?

Feat: Cancel a running data flow test #9869

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-09-24 16:33:33 +08:00
committed by GitHub
parent 5715ca6b74
commit 6bf0cda16f
17 changed files with 251 additions and 84 deletions

View File

@ -28,7 +28,7 @@ const DualRangeSlider = React.forwardRef<
)} )}
{...props} {...props}
> >
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary"> <SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-border-button">
<SliderPrimitive.Range className="absolute h-full bg-accent-primary" /> <SliderPrimitive.Range className="absolute h-full bg-accent-primary" />
</SliderPrimitive.Track> </SliderPrimitive.Track>
{initialValue.map((value, index) => ( {initialValue.map((value, index) => (

View File

@ -52,6 +52,7 @@ export const enum AgentApiAction {
FetchExternalAgentInputs = 'fetchExternalAgentInputs', FetchExternalAgentInputs = 'fetchExternalAgentInputs',
SetAgentSetting = 'setAgentSetting', SetAgentSetting = 'setAgentSetting',
FetchPrompt = 'fetchPrompt', FetchPrompt = 'fetchPrompt',
CancelDataflow = 'cancelDataflow',
} }
export const EmptyDsl = { export const EmptyDsl = {
@ -387,7 +388,7 @@ export const useUploadCanvasFileWithProgress = (
files.forEach((file) => { files.forEach((file) => {
onError(file, error as Error); onError(file, error as Error);
}); });
message.error(error?.message); message.error((error as Error)?.message || 'Upload failed');
} }
}, },
}); });
@ -425,7 +426,7 @@ export const useFetchMessageTrace = (
}, },
}); });
return { data, loading, refetch, setMessageId }; return { data, loading, refetch, setMessageId, messageId };
}; };
export const useTestDbConnect = () => { export const useTestDbConnect = () => {
@ -571,7 +572,6 @@ export const useFetchAgentLog = (searchParams: IAgentLogsRequest) => {
initialData: {} as IAgentLogsResponse, initialData: {} as IAgentLogsResponse,
gcTime: 0, gcTime: 0,
queryFn: async () => { queryFn: async () => {
console.log('useFetchAgentLog', searchParams);
const { data } = await fetchAgentLogsByCanvasId(id as string, { const { data } = await fetchAgentLogsByCanvasId(id as string, {
...searchParams, ...searchParams,
}); });
@ -678,3 +678,24 @@ export const useFetchAgentList = ({
return { data, loading }; return { data, loading };
}; };
export const useCancelDataflow = () => {
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [AgentApiAction.CancelDataflow],
mutationFn: async (taskId: string) => {
const ret = await agentService.cancelDataflow(taskId);
if (ret?.data?.code === 0) {
message.success('success');
} else {
message.error(ret?.data?.data);
}
return ret?.data?.code;
},
});
return { data, loading, cancelDataflow: mutateAsync };
};

View File

@ -1579,6 +1579,7 @@ This delimiter is used to split the input text into several text pieces echo of
sqlStatementTip: sqlStatementTip:
'Write your SQL query here. You can use variables, raw SQL, or mix both using variable syntax.', 'Write your SQL query here. You can use variables, raw SQL, or mix both using variable syntax.',
frameworkPrompts: 'Framework', frameworkPrompts: 'Framework',
release: 'Publish',
}, },
llmTools: { llmTools: {
bad_calculator: { bad_calculator: {
@ -1702,6 +1703,7 @@ This delimiter is used to split the input text into several text pieces echo of
begin: 'File', begin: 'File',
parserMethod: 'Parser method', parserMethod: 'Parser method',
exportJson: 'Export JSON', exportJson: 'Export JSON',
viewResult: 'View Result',
}, },
}, },
}; };

View File

@ -1490,6 +1490,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
sqlStatementTip: sqlStatementTip:
'在此处编写您的 SQL 查询。您可以使用变量、原始 SQL或使用变量语法混合使用两者。', '在此处编写您的 SQL 查询。您可以使用变量、原始 SQL或使用变量语法混合使用两者。',
frameworkPrompts: '框架', frameworkPrompts: '框架',
release: '发布',
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',
@ -1620,6 +1621,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
begin: '文件', begin: '文件',
parserMethod: '解析方法', parserMethod: '解析方法',
exportJson: '导出 JSON', exportJson: '导出 JSON',
viewResult: '查看结果',
}, },
}, },
}; };

View File

@ -35,7 +35,6 @@ import {
useHideFormSheetOnNodeDeletion, useHideFormSheetOnNodeDeletion,
useShowDrawer, useShowDrawer,
} from '../hooks/use-show-drawer'; } from '../hooks/use-show-drawer';
import { LogSheet } from '../log-sheet';
import RunSheet from '../run-sheet'; import RunSheet from '../run-sheet';
import { ButtonEdge } from './edge'; import { ButtonEdge } from './edge';
import styles from './index.less'; import styles from './index.less';
@ -65,9 +64,10 @@ const edgeTypes = {
interface IProps { interface IProps {
drawerVisible: boolean; drawerVisible: boolean;
hideDrawer(): void; hideDrawer(): void;
showLogSheet(): void;
} }
function DataFlowCanvas({ drawerVisible, hideDrawer }: IProps) { function DataFlowCanvas({ drawerVisible, hideDrawer, showLogSheet }: IProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
nodes, nodes,
@ -147,17 +147,10 @@ function DataFlowCanvas({ drawerVisible, hideDrawer }: IProps) {
clearActiveDropdown, clearActiveDropdown,
]); ]);
const { const { run, loading: running } = useRunDataflow(
visible: logSheetVisible, showLogSheet!,
showModal: showLogSheet, hideRunOrChatDrawer,
hideModal: hideLogSheet, );
} = useSetModalState();
const {
run,
loading: running,
messageId,
} = useRunDataflow(showLogSheet!, hideRunOrChatDrawer);
const onConnect = (connection: Connection) => { const onConnect = (connection: Connection) => {
originalOnConnect(connection); originalOnConnect(connection);
@ -311,9 +304,7 @@ function DataFlowCanvas({ drawerVisible, hideDrawer }: IProps) {
loading={running} loading={running}
></RunSheet> ></RunSheet>
)} )}
{logSheetVisible && ( {/* {logSheetVisible && <LogSheet hideModal={hideLogSheet}></LogSheet>} */}
<LogSheet hideModal={hideLogSheet} messageId={messageId}></LogSheet>
)}
</div> </div>
); );
} }

View File

@ -48,3 +48,10 @@ export type HandleContextType = {
export const HandleContext = createContext<HandleContextType>( export const HandleContext = createContext<HandleContextType>(
{} as HandleContextType, {} as HandleContextType,
); );
export type LogContextType = {
messageId: string;
setMessageId: (messageId: string) => void;
};
export const LogContext = createContext<LogContextType>({} as LogContextType);

View File

@ -0,0 +1,24 @@
import { useCancelDataflow } from '@/hooks/use-agent-request';
import { useCallback } from 'react';
export function useCancelCurrentDataflow({
messageId,
setMessageId,
hideLogSheet,
}: {
messageId: string;
setMessageId: (messageId: string) => void;
hideLogSheet(): void;
}) {
const { cancelDataflow } = useCancelDataflow();
const handleCancel = useCallback(async () => {
const code = await cancelDataflow(messageId);
if (code === 0) {
setMessageId('');
hideLogSheet();
}
}, [cancelDataflow, hideLogSheet, messageId, setMessageId]);
return { handleCancel };
}

View File

@ -0,0 +1,30 @@
import { useFetchMessageTrace } from '@/hooks/use-agent-request';
import { isEmpty } from 'lodash';
import { useMemo } from 'react';
export function useFetchLog() {
const { setMessageId, data, loading, messageId } =
useFetchMessageTrace(false);
const isCompleted = useMemo(() => {
if (Array.isArray(data)) {
const latest = data?.at(-1);
return (
latest?.component_id === 'END' && !isEmpty(latest?.trace[0].message)
);
}
return true;
}, [data]);
const isLogEmpty = !data || !data.length;
return {
data,
isLogEmpty,
isCompleted,
loading,
isParsing: !isLogEmpty && !isCompleted,
messageId,
setMessageId,
};
}

View File

@ -1,8 +1,9 @@
import { useSendMessageBySSE } from '@/hooks/use-send-message'; import { useSendMessageBySSE } from '@/hooks/use-send-message';
import api from '@/utils/api'; import api from '@/utils/api';
import { get } from 'lodash'; import { get } from 'lodash';
import { useCallback, useState } from 'react'; import { useCallback, useContext } from 'react';
import { useParams } from 'umi'; import { useParams } from 'umi';
import { LogContext } from '../context';
import { useSaveGraphBeforeOpeningDebugDrawer } from './use-save-graph'; import { useSaveGraphBeforeOpeningDebugDrawer } from './use-save-graph';
export function useRunDataflow( export function useRunDataflow(
@ -11,7 +12,7 @@ export function useRunDataflow(
) { ) {
const { send } = useSendMessageBySSE(api.runCanvas); const { send } = useSendMessageBySSE(api.runCanvas);
const { id } = useParams(); const { id } = useParams();
const [messageId, setMessageId] = useState(); const { setMessageId } = useContext(LogContext);
const { handleRun: saveGraph, loading } = const { handleRun: saveGraph, loading } =
useSaveGraphBeforeOpeningDebugDrawer(showLogSheet!); useSaveGraphBeforeOpeningDebugDrawer(showLogSheet!);
@ -39,10 +40,10 @@ export function useRunDataflow(
return msgId; return msgId;
} }
}, },
[hideRunOrChatDrawer, id, saveGraph, send], [hideRunOrChatDrawer, id, saveGraph, send, setMessageId],
); );
return { run, loading: loading, messageId }; return { run, loading: loading };
} }
export type RunDataflowType = ReturnType<typeof useRunDataflow>; export type RunDataflowType = ReturnType<typeof useRunDataflow>;

View File

@ -30,13 +30,17 @@ import { ComponentPropsWithoutRef, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import DataFlowCanvas from './canvas'; import DataFlowCanvas from './canvas';
import { DropdownProvider } from './canvas/context'; import { DropdownProvider } from './canvas/context';
import { LogContext } from './context';
import { useCancelCurrentDataflow } from './hooks/use-cancel-dataflow';
import { useHandleExportOrImportJsonFile } from './hooks/use-export-json'; import { useHandleExportOrImportJsonFile } from './hooks/use-export-json';
import { useFetchDataOnMount } from './hooks/use-fetch-data'; import { useFetchDataOnMount } from './hooks/use-fetch-data';
import { useFetchLog } from './hooks/use-fetch-log';
import { import {
useSaveGraph, useSaveGraph,
useSaveGraphBeforeOpeningDebugDrawer, useSaveGraphBeforeOpeningDebugDrawer,
useWatchAgentChange, useWatchAgentChange,
} from './hooks/use-save-graph'; } from './hooks/use-save-graph';
import { LogSheet } from './log-sheet';
import { SettingDialog } from './setting-dialog'; import { SettingDialog } from './setting-dialog';
import { useAgentHistoryManager } from './use-agent-history-manager'; import { useAgentHistoryManager } from './use-agent-history-manager';
import { VersionDialog } from './version-dialog'; import { VersionDialog } from './version-dialog';
@ -65,9 +69,7 @@ export default function DataFlow() {
const { saveGraph, loading } = useSaveGraph(); const { saveGraph, loading } = useSaveGraph();
const { flowDetail: agentDetail } = useFetchDataOnMount(); const { flowDetail: agentDetail } = useFetchDataOnMount();
const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer); const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
const handleRunAgent = useCallback(() => {
handleRun();
}, [handleRun]);
const { const {
visible: versionDialogVisible, visible: versionDialogVisible,
hideModal: hideVersionDialog, hideModal: hideVersionDialog,
@ -80,6 +82,29 @@ export default function DataFlow() {
showModal: showSettingDialog, showModal: showSettingDialog,
} = useSetModalState(); } = useSetModalState();
const {
visible: logSheetVisible,
showModal: showLogSheet,
hideModal: hideLogSheet,
} = useSetModalState();
const { isParsing, data, messageId, setMessageId } = useFetchLog();
const handleRunAgent = useCallback(() => {
if (isParsing) {
// show log sheet
showLogSheet();
} else {
handleRun();
}
}, [handleRun, isParsing, showLogSheet]);
const { handleCancel } = useCancelCurrentDataflow({
messageId,
setMessageId,
hideLogSheet,
});
const time = useWatchAgentChange(chatDrawerVisible); const time = useWatchAgentChange(chatDrawerVisible);
return ( return (
@ -112,14 +137,17 @@ export default function DataFlow() {
<LaptopMinimalCheck /> {t('flow.save')} <LaptopMinimalCheck /> {t('flow.save')}
</ButtonLoading> </ButtonLoading>
<Button variant={'secondary'} onClick={handleRunAgent}> <Button variant={'secondary'} onClick={handleRunAgent}>
<CirclePlay /> <CirclePlay className={isParsing ? 'animate-spin' : ''} />
{t('flow.run')} {isParsing ? 'running' : t('flow.run')}
</Button> </Button>
<Button variant={'secondary'} onClick={showVersionDialog}> <Button variant={'secondary'} onClick={showVersionDialog}>
<History /> <History />
{t('flow.historyversion')} {t('flow.historyversion')}
</Button> </Button>
{/* <Button variant={'secondary'}>
<Send />
{t('flow.release')}
</Button> */}
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant={'secondary'}> <Button variant={'secondary'}>
@ -140,15 +168,17 @@ export default function DataFlow() {
</DropdownMenu> </DropdownMenu>
</div> </div>
</PageHeader> </PageHeader>
<ReactFlowProvider> <LogContext.Provider value={{ messageId, setMessageId }}>
<DropdownProvider> <ReactFlowProvider>
<DataFlowCanvas <DropdownProvider>
drawerVisible={chatDrawerVisible} <DataFlowCanvas
hideDrawer={hideChatDrawer} drawerVisible={chatDrawerVisible}
></DataFlowCanvas> hideDrawer={hideChatDrawer}
</DropdownProvider> showLogSheet={showLogSheet}
</ReactFlowProvider> ></DataFlowCanvas>
</DropdownProvider>
</ReactFlowProvider>
</LogContext.Provider>
{versionDialogVisible && ( {versionDialogVisible && (
<DropdownProvider> <DropdownProvider>
<VersionDialog hideModal={hideVersionDialog}></VersionDialog> <VersionDialog hideModal={hideVersionDialog}></VersionDialog>
@ -157,6 +187,14 @@ export default function DataFlow() {
{settingDialogVisible && ( {settingDialogVisible && (
<SettingDialog hideModal={hideSettingDialog}></SettingDialog> <SettingDialog hideModal={hideSettingDialog}></SettingDialog>
)} )}
{logSheetVisible && (
<LogSheet
hideModal={hideLogSheet}
isParsing={isParsing}
logs={data}
handleCancel={handleCancel}
></LogSheet>
)}
</section> </section>
); );
} }

View File

@ -7,7 +7,10 @@ import {
TimelineSeparator, TimelineSeparator,
TimelineTitle, TimelineTitle,
} from '@/components/originui/timeline'; } from '@/components/originui/timeline';
import { Progress } from '@/components/ui/progress';
import { ITraceData } from '@/interfaces/database/agent'; import { ITraceData } from '@/interfaces/database/agent';
import { cn } from '@/lib/utils';
import { File } from 'lucide-react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { Operator } from '../constant'; import { Operator } from '../constant';
import OperatorIcon from '../operator-icon'; import OperatorIcon from '../operator-icon';
@ -17,6 +20,8 @@ export type DataflowTimelineProps = {
traceList?: ITraceData[]; traceList?: ITraceData[];
}; };
const END = 'END';
interface DataflowTrace { interface DataflowTrace {
datetime: string; datetime: string;
elapsed_time: number; elapsed_time: number;
@ -48,43 +53,66 @@ export function DataflowTimeline({ traceList }: DataflowTimelineProps) {
const traces = item.trace as DataflowTrace[]; const traces = item.trace as DataflowTrace[];
const nodeLabel = getNodeLabel(item.component_id); const nodeLabel = getNodeLabel(item.component_id);
const latest = traces[traces.length - 1];
const progress = latest.progress * 100;
return ( return (
<TimelineItem <TimelineItem
key={item.component_id} key={item.component_id}
step={index} step={index}
className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8" className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8 pb-6"
> >
<TimelineHeader> <TimelineHeader>
<TimelineSeparator className="group-data-[orientation=vertical]/timeline:-left-7 group-data-[orientation=vertical]/timeline:h-[calc(100%-1.5rem-0.25rem)] group-data-[orientation=vertical]/timeline:translate-y-7 bg-accent-primary" /> <TimelineSeparator className="group-data-[orientation=vertical]/timeline:-left-7 group-data-[orientation=vertical]/timeline:h-[calc(100%-1.5rem-0.25rem)] group-data-[orientation=vertical]/timeline:translate-y-7 bg-accent-primary" />
<TimelineTitle className=""> <TimelineTitle className="">
<TimelineContent className="text-foreground mt-2 rounded-lg border px-4 py-3"> <TimelineContent
<p className="mb-2"> className={cn(
{getNodeData(item.component_id)?.name || 'END'} 'text-foreground rounded-lg border px-4 py-3',
</p> )}
>
<section className="flex items-center justify-between mb-2">
<span className="flex-1 truncate">
{getNodeData(item.component_id)?.name || END}
</span>
<div className="flex-1 flex items-center gap-5">
<Progress value={progress} className="h-1 flex-1" />
<span className="text-accent-primary text-xs">
{progress}%
</span>
</div>
</section>
<div className="divide-y space-y-1"> <div className="divide-y space-y-1">
{traces.map((x, idx) => ( {traces.map((x, idx) => (
<section <section
key={idx} key={idx}
className="text-text-secondary text-xs" className="text-text-secondary text-xs space-x-2 py-2.5 !m-0"
> >
<div className="space-x-2"> <span>{x.datetime}</span>
<span>{x.datetime}</span>
<span>{x.progress * 100}%</span>
<span>{x.elapsed_time.toString().slice(0, 6)}</span>
</div>
{item.component_id !== 'END' && ( {item.component_id !== 'END' && (
<div>{x.message}</div> <span>{x.message}</span>
)} )}
<span>{x.elapsed_time.toString().slice(0, 6)}</span>
</section> </section>
))} ))}
</div> </div>
</TimelineContent> </TimelineContent>
</TimelineTitle> </TimelineTitle>
<TimelineIndicator className="border border-accent-primary group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-6 items-center justify-center group-data-[orientation=vertical]/timeline:-left-7"> <TimelineIndicator
{nodeLabel && ( className={cn(
'border border-accent-primary group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-5 items-center justify-center group-data-[orientation=vertical]/timeline:-left-7',
{
'rounded bg-accent-primary': nodeLabel === Operator.Begin,
},
)}
>
{item.component_id === END ? (
<span className="rounded-full inline-block size-2 bg-accent-primary"></span>
) : nodeLabel === Operator.Begin ? (
<File className="size-3.5 text-bg-base"></File>
) : (
<OperatorIcon <OperatorIcon
name={nodeLabel} name={nodeLabel}
className="size-6 rounded-full" className="size-3.5 rounded-full"
></OperatorIcon> ></OperatorIcon>
)} )}
</TimelineIndicator> </TimelineIndicator>

View File

@ -5,11 +5,15 @@ import {
SheetHeader, SheetHeader,
SheetTitle, SheetTitle,
} from '@/components/ui/sheet'; } from '@/components/ui/sheet';
import { useFetchMessageTrace } from '@/hooks/use-agent-request';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { ITraceData } from '@/interfaces/database/agent';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { NotebookText, SquareArrowOutUpRight } from 'lucide-react'; import {
import { useEffect } from 'react'; ArrowUpRight,
CirclePause,
Logs,
SquareArrowOutUpRight,
} from 'lucide-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import 'react18-json-view/src/style.css'; import 'react18-json-view/src/style.css';
import { import {
@ -18,39 +22,53 @@ import {
} from '../hooks/use-download-output'; } from '../hooks/use-download-output';
import { DataflowTimeline } from './dataflow-timeline'; import { DataflowTimeline } from './dataflow-timeline';
type LogSheetProps = IModalProps<any> & { messageId?: string }; type LogSheetProps = IModalProps<any> & {
isParsing: boolean;
handleCancel(): void;
logs?: ITraceData[];
};
export function LogSheet({ hideModal, messageId }: LogSheetProps) { export function LogSheet({
hideModal,
isParsing,
logs,
handleCancel,
}: LogSheetProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const { setMessageId, data } = useFetchMessageTrace(false);
const { handleDownloadJson } = useDownloadOutput(data); const { handleDownloadJson } = useDownloadOutput(logs);
useEffect(() => {
if (messageId) {
setMessageId(messageId);
}
}, [messageId, setMessageId]);
return ( return (
<Sheet open onOpenChange={hideModal} modal={false}> <Sheet open onOpenChange={hideModal} modal={false}>
<SheetContent className={cn('top-20')}> <SheetContent className={cn('top-20')}>
<SheetHeader> <SheetHeader>
<SheetTitle className="flex items-center gap-1"> <SheetTitle className="flex items-center gap-2.5">
<NotebookText className="size-4" /> {t('flow.log')} <Logs className="size-4" /> {t('flow.log')}
<Button variant={'ghost'}>
{t('dataflow.viewResult')} <ArrowUpRight />
</Button>
</SheetTitle> </SheetTitle>
</SheetHeader> </SheetHeader>
<section className="max-h-[82vh] overflow-auto mt-6"> <section className="max-h-[82vh] overflow-auto mt-6">
<DataflowTimeline traceList={data}></DataflowTimeline> <DataflowTimeline traceList={logs}></DataflowTimeline>
</section> </section>
<Button {isParsing ? (
onClick={handleDownloadJson} <Button
disabled={isEndOutputEmpty(data)} className="w-full mt-8 bg-state-error/10 text-state-error"
className="w-full mt-8" onClick={handleCancel}
> >
<SquareArrowOutUpRight /> <CirclePause /> Cancel
{t('dataflow.exportJson')} </Button>
</Button> ) : (
<Button
onClick={handleDownloadJson}
disabled={isEndOutputEmpty(logs)}
className="w-full mt-8"
>
<SquareArrowOutUpRight />
{t('dataflow.exportJson')}
</Button>
)}
</SheetContent> </SheetContent>
</Sheet> </Sheet>
); );

View File

@ -2,9 +2,9 @@ import { IconFont } from '@/components/icon-font';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { import {
Blocks, Blocks,
File,
FileChartColumnIncreasing, FileChartColumnIncreasing,
Heading, Heading,
HousePlus,
ListMinus, ListMinus,
} from 'lucide-react'; } from 'lucide-react';
import { Operator } from './constant'; import { Operator } from './constant';
@ -15,11 +15,11 @@ interface IProps {
} }
export const OperatorIconMap = { export const OperatorIconMap = {
[Operator.Begin]: 'house-plus',
[Operator.Note]: 'notebook-pen', [Operator.Note]: 'notebook-pen',
}; };
export const SVGIconMap = { export const SVGIconMap = {
[Operator.Begin]: File,
[Operator.Parser]: FileChartColumnIncreasing, [Operator.Parser]: FileChartColumnIncreasing,
[Operator.Tokenizer]: ListMinus, [Operator.Tokenizer]: ListMinus,
[Operator.Splitter]: Blocks, [Operator.Splitter]: Blocks,
@ -42,7 +42,7 @@ const OperatorIcon = ({ name, className }: IProps) => {
className, className,
)} )}
> >
<HousePlus className="rounded size-3" /> <File className="rounded size-3" />
</div> </div>
); );
} }
@ -50,7 +50,7 @@ const OperatorIcon = ({ name, className }: IProps) => {
return typeof Icon === 'string' ? ( return typeof Icon === 'string' ? (
<IconFont name={Icon} className={cn('size-5 ', className)}></IconFont> <IconFont name={Icon} className={cn('size-5 ', className)}></IconFont>
) : ( ) : (
<SvgIcon className="size-5"></SvgIcon> <SvgIcon className={cn('size-5', className)}></SvgIcon>
); );
}; };

View File

@ -29,6 +29,7 @@ const {
fetchAgentLogs, fetchAgentLogs,
fetchExternalAgentInputs, fetchExternalAgentInputs,
prompt, prompt,
cancelDataflow,
} = api; } = api;
const methods = { const methods = {
@ -120,6 +121,10 @@ const methods = {
url: prompt, url: prompt,
method: 'get', method: 'get',
}, },
cancelDataflow: {
url: cancelDataflow,
method: 'put',
},
} as const; } as const;
const agentService = registerNextServer<keyof typeof methods>(methods); const agentService = registerNextServer<keyof typeof methods>(methods);

View File

@ -169,6 +169,7 @@ export default {
fetchExternalAgentInputs: (canvasId: string) => fetchExternalAgentInputs: (canvasId: string) =>
`${ExternalApi}${api_host}/agentbots/${canvasId}/inputs`, `${ExternalApi}${api_host}/agentbots/${canvasId}/inputs`,
prompt: `${api_host}/canvas/prompts`, prompt: `${api_host}/canvas/prompts`,
cancelDataflow: (id: string) => `${api_host}/canvas/cancel/${id}`,
// mcp server // mcp server
listMcpServer: `${api_host}/mcp_server/list`, listMcpServer: `${api_host}/mcp_server/list`,

View File

@ -28,7 +28,7 @@ module.exports = {
}, },
extend: { extend: {
colors: { colors: {
border: 'var(--colors-outline-neutral-strong)', border: 'var(--border-default)',
input: 'hsl(var(--input))', input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))', ring: 'hsl(var(--ring))',
background: 'var(--background)', background: 'var(--background)',

View File

@ -100,7 +100,6 @@
--bg-card: rgba(0, 0, 0, 0.05); --bg-card: rgba(0, 0, 0, 0.05);
--bg-component: #ffffff; --bg-component: #ffffff;
--bg-input: rgba(255, 255, 255, 0); --bg-input: rgba(255, 255, 255, 0);
--bg-accent: rgba(76, 164, 231, 0.05);
/* Button ,Body text, Input completed text */ /* Button ,Body text, Input completed text */
--text-primary: #161618; --text-primary: #161618;
--text-secondary: #75787a; --text-secondary: #75787a;