Feat: The agent's external page should be able to fill in the begin parameter after being reset in task mode #9745 (#9982)

### What problem does this PR solve?

Feat: The agent's external page should be able to fill in the begin
parameter after being reset in task mode #9745

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-09-08 18:59:51 +08:00
committed by GitHub
parent e8018fde83
commit 2616f651c9
14 changed files with 234 additions and 880 deletions

View File

@ -1523,7 +1523,7 @@ This delimiter is used to split the input text into several text pieces echo of
sqlStatement: 'SQL Statement',
sqlStatementTip:
'Write your SQL query here. You can use variables, raw SQL, or mix both using variable syntax.',
frameworkPrompts: 'Framework Prompts',
frameworkPrompts: 'Framework',
},
llmTools: {
bad_calculator: {

View File

@ -1436,7 +1436,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
sqlStatement: 'SQL 语句',
sqlStatementTip:
'在此处编写您的 SQL 查询。您可以使用变量、原始 SQL或使用变量语法混合使用两者。',
frameworkPrompts: '框架提示词',
frameworkPrompts: '框架',
},
footer: {
profile: 'All rights reserved @ React',

View File

@ -1,13 +1,16 @@
import { SharedFrom } from '@/constants/chat';
import { useSetModalState } from '@/hooks/common-hooks';
import { useFetchExternalAgentInputs } from '@/hooks/use-agent-request';
import { IEventList } from '@/hooks/use-send-message';
import {
buildRequestBody,
useSendAgentMessage,
} from '@/pages/agent/chat/use-send-agent-message';
import { isEmpty } from 'lodash';
import trim from 'lodash/trim';
import { useCallback, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useSearchParams } from 'umi';
import { AgentDialogueMode } from '../constant';
export const useSendButtonDisabled = (value: string) => {
return trim(value) === '';
@ -35,12 +38,15 @@ export const useGetSharedChatSearchParams = () => {
export const useSendNextSharedMessage = (
addEventList: (data: IEventList, messageId: string) => void,
isTaskMode: boolean,
) => {
const { from, sharedId: conversationId } = useGetSharedChatSearchParams();
const url = `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`;
const { data: inputsData } = useFetchExternalAgentInputs();
const [params, setParams] = useState<any[]>([]);
const sendedTaskMessage = useRef<boolean>(false);
const isTaskMode = inputsData.mode === AgentDialogueMode.Task;
const {
visible: parameterDialogVisible,
@ -73,10 +79,27 @@ export const useSendNextSharedMessage = (
[hideParameterDialog, isTaskMode, ret],
);
const runTask = useCallback(() => {
if (
isTaskMode &&
isEmpty(inputsData?.inputs) &&
!sendedTaskMessage.current
) {
ok([]);
sendedTaskMessage.current = true;
}
}, [inputsData?.inputs, isTaskMode, ok]);
useEffect(() => {
runTask();
}, [runTask]);
return {
...ret,
hasError: false,
parameterDialogVisible,
inputsData,
isTaskMode,
hideParameterDialog,
showParameterDialog,
ok,

View File

@ -5,10 +5,7 @@ import MessageItem from '@/components/next-message-item';
import PdfDrawer from '@/components/pdf-drawer';
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
import { MessageType } from '@/constants/chat';
import {
useFetchExternalAgentInputs,
useUploadCanvasFileWithProgress,
} from '@/hooks/use-agent-request';
import { useUploadCanvasFileWithProgress } from '@/hooks/use-agent-request';
import { cn } from '@/lib/utils';
import i18n from '@/locales/config';
import DebugContent from '@/pages/agent/debug-content';
@ -18,7 +15,6 @@ import { useSendButtonDisabled } from '@/pages/chat/hooks';
import { buildMessageUuidWithRole } from '@/utils/chat';
import { isEmpty } from 'lodash';
import React, { forwardRef, useCallback } from 'react';
import { AgentDialogueMode } from '../constant';
import {
useGetSharedChatSearchParams,
useSendNextSharedMessage,
@ -43,9 +39,6 @@ const ChatContainer = () => {
clearEventList,
} = useCacheChatLog();
const { data: inputsData } = useFetchExternalAgentInputs();
const isTaskMode = inputsData.mode === AgentDialogueMode.Task;
const {
handlePressEnter,
handleInputChange,
@ -55,6 +48,8 @@ const ChatContainer = () => {
messageContainerRef,
derivedMessages,
hasError,
inputsData,
isTaskMode,
stopOutputMessage,
findReferenceByMessageId,
appendUploadResponseList,
@ -64,7 +59,8 @@ const ChatContainer = () => {
addNewestOneAnswer,
ok,
resetSession,
} = useSendNextSharedMessage(addEventList, isTaskMode);
} = useSendNextSharedMessage(addEventList);
const { buildInputList, handleOk, isWaitting } = useAwaitCompentData({
derivedMessages,
sendFormMessage,
@ -72,6 +68,12 @@ const ChatContainer = () => {
});
const sendDisabled = useSendButtonDisabled(value);
const showBeginParameterDialog = useCallback(() => {
if (inputsData && inputsData.inputs && !isEmpty(inputsData.inputs)) {
showParameterDialog();
}
}, [inputsData, showParameterDialog]);
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
useCallback(
async (files, options) => {
@ -96,10 +98,8 @@ const ChatContainer = () => {
}, [inputsData.prologue, addNewestOneAnswer, isTaskMode]);
React.useEffect(() => {
if (inputsData && inputsData.inputs && !isEmpty(inputsData.inputs)) {
showParameterDialog();
}
}, [inputsData, showParameterDialog]);
showBeginParameterDialog();
}, [showBeginParameterDialog]);
const handleInputsModalOk = (params: any[]) => {
ok(params);
@ -107,10 +107,14 @@ const ChatContainer = () => {
const handleReset = () => {
resetSession();
clearEventList();
if (isTaskMode) {
showBeginParameterDialog();
}
};
if (!conversationId) {
return <div>empty</div>;
}
return (
<>
<EmbedContainer

View File

@ -18,7 +18,7 @@ import {
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { NotebookPen } from 'lucide-react';
import { useCallback, useRef, useState } from 'react';
import { memo, useCallback, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { AgentBackground } from '../components/background';
import { AgentInstanceContext, HandleContext } from '../context';
@ -41,6 +41,7 @@ import { RagNode } from './node';
import { AgentNode } from './node/agent-node';
import { BeginNode } from './node/begin-node';
import { CategorizeNode } from './node/categorize-node';
import ChunkerNode from './node/chunker-node';
import { InnerNextStepDropdown } from './node/dropdown/next-step-dropdown';
import { GenerateNode } from './node/generate-node';
import { InvokeNode } from './node/invoke-node';
@ -49,11 +50,13 @@ 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 ParserNode from './node/parser-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';
import TokenizerNode from './node/tokenizer-node';
import { ToolNode } from './node/tool-node';
export const nodeTypes: NodeTypes = {
@ -76,6 +79,9 @@ export const nodeTypes: NodeTypes = {
iterationStartNode: IterationStartNode,
agentNode: AgentNode,
toolNode: ToolNode,
parserNode: ParserNode,
chunkerNode: ChunkerNode,
tokenizerNode: TokenizerNode,
};
const edgeTypes = {
@ -87,7 +93,7 @@ interface IProps {
hideDrawer(): void;
}
function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
function DataFlowCanvas({ drawerVisible, hideDrawer }: IProps) {
const { t } = useTranslation();
const {
nodes,
@ -323,4 +329,4 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
);
}
export default AgentCanvas;
export default memo(DataFlowCanvas);

View File

@ -0,0 +1,49 @@
import { IRagNode } from '@/interfaces/database/flow';
import { NodeProps, Position } from '@xyflow/react';
import { memo } from 'react';
import { NodeHandleId } from '../../constant';
import { needsSingleStepDebugging } from '../../utils';
import { CommonHandle } from './handle';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import NodeHeader from './node-header';
import { NodeWrapper } from './node-wrapper';
import { ToolBar } from './toolbar';
function ChunkerNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<IRagNode>) {
return (
<ToolBar
selected={selected}
id={id}
label={data.label}
showRun={needsSingleStepDebugging(data.label)}
>
<NodeWrapper selected={selected}>
<CommonHandle
id={NodeHandleId.End}
type="target"
position={Position.Left}
isConnectable={isConnectable}
style={LeftHandleStyle}
nodeId={id}
></CommonHandle>
<CommonHandle
type="source"
position={Position.Right}
isConnectable={isConnectable}
id={NodeHandleId.Start}
style={RightHandleStyle}
nodeId={id}
isConnectableEnd={false}
></CommonHandle>
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
</NodeWrapper>
</ToolBar>
);
}
export default memo(ChunkerNode);

View File

@ -0,0 +1,49 @@
import { IRagNode } from '@/interfaces/database/flow';
import { NodeProps, Position } from '@xyflow/react';
import { memo } from 'react';
import { NodeHandleId } from '../../constant';
import { needsSingleStepDebugging } from '../../utils';
import { CommonHandle } from './handle';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import NodeHeader from './node-header';
import { NodeWrapper } from './node-wrapper';
import { ToolBar } from './toolbar';
function ParserNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<IRagNode>) {
return (
<ToolBar
selected={selected}
id={id}
label={data.label}
showRun={needsSingleStepDebugging(data.label)}
>
<NodeWrapper selected={selected}>
<CommonHandle
id={NodeHandleId.End}
type="target"
position={Position.Left}
isConnectable={isConnectable}
style={LeftHandleStyle}
nodeId={id}
></CommonHandle>
<CommonHandle
type="source"
position={Position.Right}
isConnectable={isConnectable}
id={NodeHandleId.Start}
style={RightHandleStyle}
nodeId={id}
isConnectableEnd={false}
></CommonHandle>
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
</NodeWrapper>
</ToolBar>
);
}
export default memo(ParserNode);

View File

@ -0,0 +1,49 @@
import { IRagNode } from '@/interfaces/database/flow';
import { NodeProps, Position } from '@xyflow/react';
import { memo } from 'react';
import { NodeHandleId } from '../../constant';
import { needsSingleStepDebugging } from '../../utils';
import { CommonHandle } from './handle';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import NodeHeader from './node-header';
import { NodeWrapper } from './node-wrapper';
import { ToolBar } from './toolbar';
function TokenizerNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<IRagNode>) {
return (
<ToolBar
selected={selected}
id={id}
label={data.label}
showRun={needsSingleStepDebugging(data.label)}
>
<NodeWrapper selected={selected}>
<CommonHandle
id={NodeHandleId.End}
type="target"
position={Position.Left}
isConnectable={isConnectable}
style={LeftHandleStyle}
nodeId={id}
></CommonHandle>
<CommonHandle
type="source"
position={Position.Right}
isConnectable={isConnectable}
id={NodeHandleId.Start}
style={RightHandleStyle}
nodeId={id}
isConnectableEnd={false}
></CommonHandle>
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
</NodeWrapper>
</ToolBar>
);
}
export default memo(TokenizerNode);

View File

@ -8,11 +8,6 @@ import {
ProgrammingLanguage,
} from '@/constants/agent';
export enum AgentDialogueMode {
Conversational = 'conversational',
Task = 'task',
}
import {
ChatVariableEnabledField,
variableEnabledFieldMap,
@ -22,17 +17,6 @@ import i18n from '@/locales/config';
import { setInitialChatVariableEnabledFieldValue } from '@/utils/chat';
import { t } from 'i18next';
// DuckDuckGo's channel options
export enum Channel {
Text = 'text',
News = 'news',
}
export enum PromptRole {
User = 'user',
Assistant = 'assistant',
}
import {
Circle,
CircleSlash2,
@ -44,6 +28,16 @@ import {
WrapText,
} from 'lucide-react';
export enum PromptRole {
User = 'user',
Assistant = 'assistant',
}
export enum AgentDialogueMode {
Conversational = 'conversational',
Task = 'task',
}
export const BeginId = 'begin';
export enum Operator {
@ -54,26 +48,9 @@ export enum Operator {
Relevant = 'Relevant',
RewriteQuestion = 'RewriteQuestion',
KeywordExtract = 'KeywordExtract',
Baidu = 'Baidu',
DuckDuckGo = 'DuckDuckGo',
Wikipedia = 'Wikipedia',
PubMed = 'PubMed',
ArXiv = 'ArXiv',
Google = 'Google',
Bing = 'Bing',
GoogleScholar = 'GoogleScholar',
DeepL = 'DeepL',
GitHub = 'GitHub',
BaiduFanyi = 'BaiduFanyi',
QWeather = 'QWeather',
ExeSQL = 'ExeSQL',
Switch = 'Switch',
WenCai = 'WenCai',
AkShare = 'AkShare',
YahooFinance = 'YahooFinance',
Jin10 = 'Jin10',
Concentrator = 'Concentrator',
TuShare = 'TuShare',
Note = 'Note',
Crawler = 'Crawler',
Invoke = 'Invoke',
@ -84,11 +61,8 @@ export enum Operator {
WaitingDialogue = 'WaitingDialogue',
Agent = 'Agent',
Tool = 'Tool',
TavilySearch = 'TavilySearch',
TavilyExtract = 'TavilyExtract',
UserFillUp = 'UserFillUp',
StringTransform = 'StringTransform',
SearXNG = 'SearXNG',
Parser = 'Parser',
Chunker = 'Chunker',
Tokenizer = 'Tokenizer',
@ -114,112 +88,6 @@ export const AgentOperatorList = [
Operator.Agent,
];
export const componentMenuList = [
{
name: Operator.Retrieval,
},
{
name: Operator.Categorize,
},
{
name: Operator.Message,
},
{
name: Operator.RewriteQuestion,
},
{
name: Operator.KeywordExtract,
},
{
name: Operator.Switch,
},
{
name: Operator.Concentrator,
},
{
name: Operator.Iteration,
},
{
name: Operator.Code,
},
{
name: Operator.WaitingDialogue,
},
{
name: Operator.Agent,
},
{
name: Operator.Note,
},
{
name: Operator.DuckDuckGo,
},
{
name: Operator.Baidu,
},
{
name: Operator.Wikipedia,
},
{
name: Operator.PubMed,
},
{
name: Operator.ArXiv,
},
{
name: Operator.Google,
},
{
name: Operator.Bing,
},
{
name: Operator.GoogleScholar,
},
{
name: Operator.DeepL,
},
{
name: Operator.GitHub,
},
{
name: Operator.BaiduFanyi,
},
{
name: Operator.QWeather,
},
{
name: Operator.ExeSQL,
},
{
name: Operator.WenCai,
},
{
name: Operator.AkShare,
},
{
name: Operator.YahooFinance,
},
{
name: Operator.Jin10,
},
{
name: Operator.TuShare,
},
{
name: Operator.Crawler,
},
{
name: Operator.Invoke,
},
{
name: Operator.Email,
},
{
name: Operator.SearXNG,
},
];
export const SwitchOperatorOptions = [
{ value: '=', label: 'equal', icon: 'equal' },
{ value: '≠', label: 'notEqual', icon: 'not-equals' },
@ -331,161 +199,6 @@ export const initialKeywordExtractValues = {
top_n: 3,
...initialQueryBaseValues,
};
export const initialDuckValues = {
top_n: 10,
channel: Channel.Text,
query: AgentGlobals.SysQuery,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
json: {
value: [],
type: 'Array<Object>',
},
},
};
export const initialSearXNGValues = {
top_n: '10',
searxng_url: '',
query: AgentGlobals.SysQuery,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
json: {
value: [],
type: 'Array<Object>',
},
},
};
export const initialBaiduValues = {
top_n: 10,
...initialQueryBaseValues,
};
export const initialWikipediaValues = {
top_n: 10,
language: 'en',
query: AgentGlobals.SysQuery,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
},
};
export const initialPubMedValues = {
top_n: 12,
email: '',
query: AgentGlobals.SysQuery,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
},
};
export const initialArXivValues = {
top_n: 12,
sort_by: 'relevance',
query: AgentGlobals.SysQuery,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
},
};
export const initialGoogleValues = {
q: AgentGlobals.SysQuery,
start: 0,
num: 12,
api_key: '',
country: 'us',
language: 'en',
outputs: {
formalized_content: {
value: '',
type: 'string',
},
json: {
value: [],
type: 'Array<Object>',
},
},
};
export const initialBingValues = {
top_n: 10,
channel: 'Webpages',
api_key:
'YOUR_API_KEY (obtained from https://www.microsoft.com/en-us/bing/apis/bing-web-search-api)',
country: 'CH',
language: 'en',
query: '',
};
export const initialGoogleScholarValues = {
top_n: 12,
sort_by: 'relevance',
patents: true,
query: AgentGlobals.SysQuery,
year_low: undefined,
year_high: undefined,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
json: {
value: [],
type: 'Array<Object>',
},
},
};
export const initialDeepLValues = {
top_n: 5,
auth_key: 'relevance',
};
export const initialGithubValues = {
top_n: 5,
query: AgentGlobals.SysQuery,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
json: {
value: [],
type: 'Array<Object>',
},
},
};
export const initialBaiduFanyiValues = {
appid: 'xxx',
secret_key: 'xxx',
trans_type: 'translate',
...initialQueryBaseValues,
};
export const initialQWeatherValues = {
web_apikey: 'xxx',
type: 'weather',
user_type: 'free',
time_period: 'now',
...initialQueryBaseValues,
};
export const initialExeSqlValues = {
sql: '',
@ -523,54 +236,8 @@ export const initialSwitchValues = {
[SwitchElseTo]: [],
};
export const initialWenCaiValues = {
top_n: 20,
query_type: 'stock',
query: AgentGlobals.SysQuery,
outputs: {
report: {
value: '',
type: 'string',
},
},
};
export const initialAkShareValues = { top_n: 10, ...initialQueryBaseValues };
export const initialYahooFinanceValues = {
stock_code: '',
info: true,
history: false,
financials: false,
balance_sheet: false,
cash_flow_statement: false,
news: true,
outputs: {
report: {
value: '',
type: 'string',
},
},
};
export const initialJin10Values = {
type: 'flash',
secret_key: 'xxx',
flash_type: '1',
contain: '',
filter: '',
...initialQueryBaseValues,
};
export const initialConcentratorValues = {};
export const initialTuShareValues = {
token: 'xxx',
src: 'eastmoney',
start_date: '2024-01-01 09:00:00',
...initialQueryBaseValues,
};
export const initialNoteValues = {
text: '',
};
@ -650,6 +317,10 @@ export const initialCodeValues = {
export const initialWaitingDialogueValues = {};
export const initialChunkerValues = {};
export const initialTokenizerValues = {};
export const initialAgentValues = {
...initialLlmBaseValues,
description: '',
@ -717,66 +388,7 @@ export const initialStringTransformValues = {
},
};
export enum TavilySearchDepth {
Basic = 'basic',
Advanced = 'advanced',
}
export enum TavilyTopic {
News = 'news',
General = 'general',
}
export const initialTavilyValues = {
api_key: '',
query: AgentGlobals.SysQuery,
search_depth: TavilySearchDepth.Basic,
topic: TavilyTopic.General,
max_results: 5,
days: 7,
include_answer: false,
include_raw_content: true,
include_images: false,
include_image_descriptions: false,
include_domains: [],
exclude_domains: [],
outputs: {
formalized_content: {
value: '',
type: 'string',
},
json: {
value: [],
type: 'Array<Object>',
},
},
};
export enum TavilyExtractDepth {
Basic = 'basic',
Advanced = 'advanced',
}
export enum TavilyExtractFormat {
Text = 'text',
Markdown = 'markdown',
}
export const initialTavilyExtractValues = {
urls: '',
extract_depth: TavilyExtractDepth.Basic,
format: TavilyExtractFormat.Markdown,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
json: {
value: [],
type: 'Array<Object>',
},
},
};
export const initialParserValues = {};
export const CategorizeAnchorPointPositions = [
{ top: 1, right: 34 },
@ -818,27 +430,9 @@ export const RestrictedUpstreamMap = {
Operator.Message,
Operator.Relevant,
],
[Operator.Baidu]: [Operator.Begin, Operator.Retrieval],
[Operator.DuckDuckGo]: [Operator.Begin, Operator.Retrieval],
[Operator.Wikipedia]: [Operator.Begin, Operator.Retrieval],
[Operator.PubMed]: [Operator.Begin, Operator.Retrieval],
[Operator.ArXiv]: [Operator.Begin, Operator.Retrieval],
[Operator.Google]: [Operator.Begin, Operator.Retrieval],
[Operator.Bing]: [Operator.Begin, Operator.Retrieval],
[Operator.GoogleScholar]: [Operator.Begin, Operator.Retrieval],
[Operator.DeepL]: [Operator.Begin, Operator.Retrieval],
[Operator.GitHub]: [Operator.Begin, Operator.Retrieval],
[Operator.BaiduFanyi]: [Operator.Begin, Operator.Retrieval],
[Operator.QWeather]: [Operator.Begin, Operator.Retrieval],
[Operator.SearXNG]: [Operator.Begin, Operator.Retrieval],
[Operator.ExeSQL]: [Operator.Begin],
[Operator.Switch]: [Operator.Begin],
[Operator.WenCai]: [Operator.Begin],
[Operator.AkShare]: [Operator.Begin],
[Operator.YahooFinance]: [Operator.Begin],
[Operator.Jin10]: [Operator.Begin],
[Operator.Concentrator]: [Operator.Begin],
[Operator.TuShare]: [Operator.Begin],
[Operator.Crawler]: [Operator.Begin],
[Operator.Note]: [],
[Operator.Invoke]: [Operator.Begin],
@ -848,8 +442,6 @@ export const RestrictedUpstreamMap = {
[Operator.Code]: [Operator.Begin],
[Operator.WaitingDialogue]: [Operator.Begin],
[Operator.Agent]: [Operator.Begin],
[Operator.TavilySearch]: [Operator.Begin],
[Operator.TavilyExtract]: [Operator.Begin],
[Operator.StringTransform]: [Operator.Begin],
[Operator.UserFillUp]: [Operator.Begin],
[Operator.Tool]: [Operator.Begin],
@ -863,27 +455,9 @@ export const NodeMap = {
[Operator.Relevant]: 'relevantNode',
[Operator.RewriteQuestion]: 'rewriteNode',
[Operator.KeywordExtract]: 'keywordNode',
[Operator.DuckDuckGo]: 'ragNode',
[Operator.Baidu]: 'ragNode',
[Operator.Wikipedia]: 'ragNode',
[Operator.PubMed]: 'ragNode',
[Operator.ArXiv]: 'ragNode',
[Operator.Google]: 'ragNode',
[Operator.Bing]: 'ragNode',
[Operator.GoogleScholar]: 'ragNode',
[Operator.DeepL]: 'ragNode',
[Operator.GitHub]: 'ragNode',
[Operator.BaiduFanyi]: 'ragNode',
[Operator.QWeather]: 'ragNode',
[Operator.SearXNG]: 'ragNode',
[Operator.ExeSQL]: 'ragNode',
[Operator.Switch]: 'switchNode',
[Operator.Concentrator]: 'logicNode',
[Operator.WenCai]: 'ragNode',
[Operator.AkShare]: 'ragNode',
[Operator.YahooFinance]: 'ragNode',
[Operator.Jin10]: 'ragNode',
[Operator.TuShare]: 'ragNode',
[Operator.Note]: 'noteNode',
[Operator.Crawler]: 'ragNode',
[Operator.Invoke]: 'ragNode',
@ -894,10 +468,11 @@ export const NodeMap = {
[Operator.WaitingDialogue]: 'ragNode',
[Operator.Agent]: 'agentNode',
[Operator.Tool]: 'toolNode',
[Operator.TavilySearch]: 'ragNode',
[Operator.UserFillUp]: 'ragNode',
[Operator.StringTransform]: 'ragNode',
[Operator.TavilyExtract]: 'ragNode',
[Operator.Parser]: 'parserNode',
[Operator.Chunker]: 'chunkerNode',
[Operator.Tokenizer]: 'tokenizerNode',
};
export enum BeginQueryType {

View File

@ -1,69 +1,12 @@
import {
Connection,
Edge,
getOutgoers,
Node,
Position,
ReactFlowInstance,
} from '@xyflow/react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Connection, Edge, getOutgoers } from '@xyflow/react';
import { useCallback } from 'react';
// import { shallow } from 'zustand/shallow';
import { useFetchModelId } from '@/hooks/logic-hooks';
import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { humanId } from 'human-id';
import { get, lowerFirst } from 'lodash';
import { lowerFirst } from 'lodash';
import { useTranslation } from 'react-i18next';
import {
initialAgentValues,
initialAkShareValues,
initialArXivValues,
initialBaiduFanyiValues,
initialBeginValues,
initialBingValues,
initialCategorizeValues,
initialCodeValues,
initialConcentratorValues,
initialCrawlerValues,
initialDeepLValues,
initialDuckValues,
initialEmailValues,
initialExeSqlValues,
initialGithubValues,
initialGoogleScholarValues,
initialGoogleValues,
initialInvokeValues,
initialIterationValues,
initialJin10Values,
initialKeywordExtractValues,
initialMessageValues,
initialNoteValues,
initialPubMedValues,
initialQWeatherValues,
initialRelevantValues,
initialRetrievalValues,
initialRewriteQuestionValues,
initialSearXNGValues,
initialStringTransformValues,
initialSwitchValues,
initialTavilyExtractValues,
initialTavilyValues,
initialTuShareValues,
initialUserFillUpValues,
initialWaitingDialogueValues,
initialWenCaiValues,
initialWikipediaValues,
initialYahooFinanceValues,
NodeMap,
Operator,
RestrictedUpstreamMap,
} from './constant';
import { Operator, RestrictedUpstreamMap } from './constant';
import useGraphStore, { RFState } from './store';
import {
generateNodeNamesWithIncreasingIndex,
getNodeDragHandle,
getRelativePositionToIterationNode,
replaceIdWithText,
} from './utils';
import { replaceIdWithText } from './utils';
const selector = (state: RFState) => ({
nodes: state.nodes,
@ -83,83 +26,6 @@ export const useSelectCanvasData = () => {
return useGraphStore(selector);
};
export const useInitializeOperatorParams = () => {
const llmId = useFetchModelId();
const initialFormValuesMap = useMemo(() => {
return {
[Operator.Begin]: initialBeginValues,
[Operator.Retrieval]: initialRetrievalValues,
[Operator.Categorize]: { ...initialCategorizeValues, llm_id: llmId },
[Operator.Relevant]: { ...initialRelevantValues, llm_id: llmId },
[Operator.RewriteQuestion]: {
...initialRewriteQuestionValues,
llm_id: llmId,
},
[Operator.Message]: initialMessageValues,
[Operator.KeywordExtract]: {
...initialKeywordExtractValues,
llm_id: llmId,
},
[Operator.DuckDuckGo]: initialDuckValues,
[Operator.Wikipedia]: initialWikipediaValues,
[Operator.PubMed]: initialPubMedValues,
[Operator.ArXiv]: initialArXivValues,
[Operator.Google]: initialGoogleValues,
[Operator.Bing]: initialBingValues,
[Operator.GoogleScholar]: initialGoogleScholarValues,
[Operator.DeepL]: initialDeepLValues,
[Operator.SearXNG]: initialSearXNGValues,
[Operator.GitHub]: initialGithubValues,
[Operator.BaiduFanyi]: initialBaiduFanyiValues,
[Operator.QWeather]: initialQWeatherValues,
[Operator.ExeSQL]: { ...initialExeSqlValues, llm_id: llmId },
[Operator.Switch]: initialSwitchValues,
[Operator.WenCai]: initialWenCaiValues,
[Operator.AkShare]: initialAkShareValues,
[Operator.YahooFinance]: initialYahooFinanceValues,
[Operator.Jin10]: initialJin10Values,
[Operator.Concentrator]: initialConcentratorValues,
[Operator.TuShare]: initialTuShareValues,
[Operator.Note]: initialNoteValues,
[Operator.Crawler]: initialCrawlerValues,
[Operator.Invoke]: initialInvokeValues,
[Operator.Email]: initialEmailValues,
[Operator.Iteration]: initialIterationValues,
[Operator.IterationStart]: initialIterationValues,
[Operator.Code]: initialCodeValues,
[Operator.WaitingDialogue]: initialWaitingDialogueValues,
[Operator.Agent]: { ...initialAgentValues, llm_id: llmId },
[Operator.TavilySearch]: initialTavilyValues,
[Operator.TavilyExtract]: initialTavilyExtractValues,
[Operator.Tool]: {},
[Operator.UserFillUp]: initialUserFillUpValues,
[Operator.StringTransform]: initialStringTransformValues,
};
}, [llmId]);
const initializeOperatorParams = useCallback(
(operatorName: Operator) => {
return initialFormValuesMap[operatorName];
},
[initialFormValuesMap],
);
return initializeOperatorParams;
};
export const useHandleDrag = () => {
const handleDragStart = useCallback(
(operatorId: string) => (ev: React.DragEvent<HTMLDivElement>) => {
ev.dataTransfer.setData('application/@xyflow/react', operatorId);
ev.dataTransfer.effectAllowed = 'move';
},
[],
);
return { handleDragStart };
};
export const useGetNodeName = () => {
const { t } = useTranslation();
@ -169,91 +35,6 @@ export const useGetNodeName = () => {
};
};
export const useHandleDrop = () => {
const addNode = useGraphStore((state) => state.addNode);
const nodes = useGraphStore((state) => state.nodes);
const [reactFlowInstance, setReactFlowInstance] =
useState<ReactFlowInstance<any, any>>();
const initializeOperatorParams = useInitializeOperatorParams();
const getNodeName = useGetNodeName();
const onDragOver = useCallback((event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);
const onDrop = useCallback(
(event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const type = event.dataTransfer.getData('application/@xyflow/react');
// check if the dropped element is valid
if (typeof type === 'undefined' || !type) {
return;
}
// reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition
// and you don't need to subtract the reactFlowBounds.left/top anymore
// details: https://@xyflow/react.dev/whats-new/2023-11-10
const position = reactFlowInstance?.screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
const newNode: Node<any> = {
id: `${type}:${humanId()}`,
type: NodeMap[type as Operator] || 'ragNode',
position: position || {
x: 0,
y: 0,
},
data: {
label: `${type}`,
name: generateNodeNamesWithIncreasingIndex(getNodeName(type), nodes),
form: initializeOperatorParams(type as Operator),
},
sourcePosition: Position.Right,
targetPosition: Position.Left,
dragHandle: getNodeDragHandle(type),
};
if (type === Operator.Iteration) {
newNode.width = 500;
newNode.height = 250;
const iterationStartNode: Node<any> = {
id: `${Operator.IterationStart}:${humanId()}`,
type: 'iterationStartNode',
position: { x: 50, y: 100 },
// draggable: false,
data: {
label: Operator.IterationStart,
name: Operator.IterationStart,
form: {},
},
parentId: newNode.id,
extent: 'parent',
};
addNode(newNode);
addNode(iterationStartNode);
} else {
const subNodeOfIteration = getRelativePositionToIterationNode(
nodes,
position,
);
if (subNodeOfIteration) {
newNode.parentId = subNodeOfIteration.parentId;
newNode.position = subNodeOfIteration.position;
newNode.extent = 'parent';
}
addNode(newNode);
}
},
[reactFlowInstance, getNodeName, nodes, initializeOperatorParams, addNode],
);
return { onDrop, onDragOver, setReactFlowInstance, reactFlowInstance };
};
export const useValidateConnection = () => {
const { getOperatorTypeFromId, getParentIdById, edges, nodes } =
useGraphStore((state) => state);
@ -352,52 +133,3 @@ export const useDuplicateNode = () => {
return duplicateNode;
};
export const useCopyPaste = () => {
const nodes = useGraphStore((state) => state.nodes);
const duplicateNode = useDuplicateNode();
const onCopyCapture = useCallback(
(event: ClipboardEvent) => {
if (get(event, 'srcElement.tagName') !== 'BODY') return;
event.preventDefault();
const nodesStr = JSON.stringify(
nodes.filter((n) => n.selected && n.data.label !== Operator.Begin),
);
event.clipboardData?.setData('agent:nodes', nodesStr);
},
[nodes],
);
const onPasteCapture = useCallback(
(event: ClipboardEvent) => {
const nodes = JSON.parse(
event.clipboardData?.getData('agent:nodes') || '[]',
) as RAGFlowNodeType[] | undefined;
if (Array.isArray(nodes) && nodes.length) {
event.preventDefault();
nodes.forEach((n) => {
duplicateNode(n.id, n.data.label);
});
}
},
[duplicateNode],
);
useEffect(() => {
window.addEventListener('copy', onCopyCapture);
return () => {
window.removeEventListener('copy', onCopyCapture);
};
}, [onCopyCapture]);
useEffect(() => {
window.addEventListener('paste', onPasteCapture);
return () => {
window.removeEventListener('paste', onPasteCapture);
};
}, [onPasteCapture]);
};

View File

@ -10,45 +10,29 @@ import {
NodeMap,
Operator,
initialAgentValues,
initialAkShareValues,
initialArXivValues,
initialBaiduFanyiValues,
initialBeginValues,
initialBingValues,
initialCategorizeValues,
initialChunkerValues,
initialCodeValues,
initialConcentratorValues,
initialCrawlerValues,
initialDeepLValues,
initialDuckValues,
initialEmailValues,
initialExeSqlValues,
initialGithubValues,
initialGoogleScholarValues,
initialGoogleValues,
initialInvokeValues,
initialIterationStartValues,
initialIterationValues,
initialJin10Values,
initialKeywordExtractValues,
initialMessageValues,
initialNoteValues,
initialPubMedValues,
initialQWeatherValues,
initialParserValues,
initialRelevantValues,
initialRetrievalValues,
initialRewriteQuestionValues,
initialSearXNGValues,
initialStringTransformValues,
initialSwitchValues,
initialTavilyExtractValues,
initialTavilyValues,
initialTuShareValues,
initialTokenizerValues,
initialUserFillUpValues,
initialWaitingDialogueValues,
initialWenCaiValues,
initialWikipediaValues,
initialYahooFinanceValues,
} from '../constant';
import useGraphStore from '../store';
import {
@ -80,26 +64,9 @@ export const useInitializeOperatorParams = () => {
...initialKeywordExtractValues,
llm_id: llmId,
},
[Operator.DuckDuckGo]: initialDuckValues,
[Operator.Wikipedia]: initialWikipediaValues,
[Operator.PubMed]: initialPubMedValues,
[Operator.ArXiv]: initialArXivValues,
[Operator.Google]: initialGoogleValues,
[Operator.Bing]: initialBingValues,
[Operator.GoogleScholar]: initialGoogleScholarValues,
[Operator.DeepL]: initialDeepLValues,
[Operator.SearXNG]: initialSearXNGValues,
[Operator.GitHub]: initialGithubValues,
[Operator.BaiduFanyi]: initialBaiduFanyiValues,
[Operator.QWeather]: initialQWeatherValues,
[Operator.ExeSQL]: initialExeSqlValues,
[Operator.Switch]: initialSwitchValues,
[Operator.WenCai]: initialWenCaiValues,
[Operator.AkShare]: initialAkShareValues,
[Operator.YahooFinance]: initialYahooFinanceValues,
[Operator.Jin10]: initialJin10Values,
[Operator.Concentrator]: initialConcentratorValues,
[Operator.TuShare]: initialTuShareValues,
[Operator.Note]: initialNoteValues,
[Operator.Crawler]: initialCrawlerValues,
[Operator.Invoke]: initialInvokeValues,
@ -110,10 +77,11 @@ export const useInitializeOperatorParams = () => {
[Operator.WaitingDialogue]: initialWaitingDialogueValues,
[Operator.Agent]: { ...initialAgentValues, llm_id: llmId },
[Operator.Tool]: {},
[Operator.TavilySearch]: initialTavilyValues,
[Operator.UserFillUp]: initialUserFillUpValues,
[Operator.StringTransform]: initialStringTransformValues,
[Operator.TavilyExtract]: initialTavilyExtractValues,
[Operator.Parser]: initialParserValues,
[Operator.Chunker]: initialChunkerValues,
[Operator.Tokenizer]: initialTokenizerValues,
};
}, [llmId]);

View File

@ -1,70 +0,0 @@
import { omit, pick } from 'lodash';
import { useCallback } from 'react';
import { Operator } from '../constant';
import { useInitializeOperatorParams } from './use-add-node';
export function useAgentToolInitialValues() {
const { initialFormValuesMap } = useInitializeOperatorParams();
const initializeAgentToolValues = useCallback(
(operatorName: Operator) => {
const initialValues = initialFormValuesMap[operatorName];
switch (operatorName) {
case Operator.Retrieval:
return {
...omit(initialValues, 'query'),
description: '',
};
case (Operator.TavilySearch, Operator.TavilyExtract):
return {
api_key: '',
};
case Operator.ExeSQL:
return omit(initialValues, 'sql');
case Operator.Bing:
return omit(initialValues, 'query');
case Operator.YahooFinance:
return omit(initialValues, 'stock_code');
case Operator.Email:
return pick(
initialValues,
'smtp_server',
'smtp_port',
'email',
'password',
'sender_name',
);
case Operator.DuckDuckGo:
return pick(initialValues, 'top_n', 'channel');
case Operator.Wikipedia:
return pick(initialValues, 'top_n', 'language');
case Operator.Google:
return pick(initialValues, 'api_key', 'country', 'language');
case Operator.GoogleScholar:
return omit(initialValues, 'query', 'outputs');
case Operator.ArXiv:
return pick(initialValues, 'top_n', 'sort_by');
case Operator.PubMed:
return pick(initialValues, 'top_n', 'email');
case Operator.GitHub:
return pick(initialValues, 'top_n');
case Operator.WenCai:
return pick(initialValues, 'top_n', 'query_type');
case Operator.Code:
return {};
case Operator.SearXNG:
return pick(initialValues, 'searxng_url', 'top_n');
default:
return initialValues;
}
},
[initialFormValuesMap],
);
return { initializeAgentToolValues };
}

View File

@ -29,7 +29,7 @@ import {
} from 'lucide-react';
import { ComponentPropsWithoutRef, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import AgentCanvas from './canvas';
import DataFlowCanvas from './canvas';
import { DropdownProvider } from './canvas/context';
import { useHandleExportOrImportJsonFile } from './hooks/use-export-json';
import { useFetchDataOnMount } from './hooks/use-fetch-data';
@ -161,10 +161,10 @@ export default function DataFlow() {
</PageHeader>
<ReactFlowProvider>
<DropdownProvider>
<AgentCanvas
<DataFlowCanvas
drawerVisible={chatDrawerVisible}
hideDrawer={hideChatDrawer}
></AgentCanvas>
></DataFlowCanvas>
</DropdownProvider>
</ReactFlowProvider>
{fileUploadVisible && (

View File

@ -1,17 +1,3 @@
import { ReactComponent as ArxivIcon } from '@/assets/svg/arxiv.svg';
import { ReactComponent as BingIcon } from '@/assets/svg/bing.svg';
import { ReactComponent as CrawlerIcon } from '@/assets/svg/crawler.svg';
import { ReactComponent as DuckIcon } from '@/assets/svg/duck.svg';
import { ReactComponent as GithubIcon } from '@/assets/svg/github.svg';
import { ReactComponent as GoogleScholarIcon } from '@/assets/svg/google-scholar.svg';
import { ReactComponent as GoogleIcon } from '@/assets/svg/google.svg';
import { ReactComponent as PubMedIcon } from '@/assets/svg/pubmed.svg';
import { ReactComponent as SearXNGIcon } from '@/assets/svg/searxng.svg';
import { ReactComponent as TavilyIcon } from '@/assets/svg/tavily.svg';
import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg';
import { ReactComponent as WikipediaIcon } from '@/assets/svg/wikipedia.svg';
import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.svg';
import { IconFont } from '@/components/icon-font';
import { cn } from '@/lib/utils';
import { HousePlus } from 'lucide-react';
@ -39,30 +25,13 @@ export const OperatorIconMap = {
[Operator.Email]: 'sendemail-0',
};
export const SVGIconMap = {
[Operator.ArXiv]: ArxivIcon,
[Operator.GitHub]: GithubIcon,
[Operator.Bing]: BingIcon,
[Operator.DuckDuckGo]: DuckIcon,
[Operator.Google]: GoogleIcon,
[Operator.GoogleScholar]: GoogleScholarIcon,
[Operator.PubMed]: PubMedIcon,
[Operator.SearXNG]: SearXNGIcon,
[Operator.TavilyExtract]: TavilyIcon,
[Operator.TavilySearch]: TavilyIcon,
[Operator.Wikipedia]: WikipediaIcon,
[Operator.YahooFinance]: YahooFinanceIcon,
[Operator.WenCai]: WenCaiIcon,
[Operator.Crawler]: CrawlerIcon,
};
const Empty = () => {
return <div className="hidden"></div>;
};
const OperatorIcon = ({ name, className }: IProps) => {
const Icon = OperatorIconMap[name as keyof typeof OperatorIconMap] || Empty;
const SvgIcon = SVGIconMap[name as keyof typeof SVGIconMap] || Empty;
const SvgIcon = Empty;
if (name === Operator.Begin) {
return (
@ -80,7 +49,7 @@ const OperatorIcon = ({ name, className }: IProps) => {
return typeof Icon === 'string' ? (
<IconFont name={Icon} className={cn('size-5 ', className)}></IconFont>
) : (
<SvgIcon className={cn('size-5 fill-current', className)}></SvgIcon>
<SvgIcon></SvgIcon>
);
};