mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Fix: Fixed the issue that the condition of deleting the classification operator cannot be connected anymore #3221 (#9068)
### What problem does this PR solve? Fix: Fixed the issue that the condition of deleting the classification operator cannot be connected anymore #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -4,11 +4,12 @@ export interface ICategorizeItem {
|
|||||||
examples?: { value: string }[];
|
examples?: { value: string }[];
|
||||||
index: number;
|
index: number;
|
||||||
to: string[];
|
to: string[];
|
||||||
|
uuid: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ICategorizeItemResult = Record<
|
export type ICategorizeItemResult = Record<
|
||||||
string,
|
string,
|
||||||
Omit<ICategorizeItem, 'name' | 'examples'> & { examples: string[] }
|
Omit<ICategorizeItem, 'name' | 'examples' | 'uuid'> & { examples: string[] }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export interface ISwitchCondition {
|
export interface ISwitchCondition {
|
||||||
@ -101,6 +102,7 @@ export interface IGenerateForm {
|
|||||||
|
|
||||||
export interface ICategorizeForm extends IGenerateForm {
|
export interface ICategorizeForm extends IGenerateForm {
|
||||||
category_description: ICategorizeItemResult;
|
category_description: ICategorizeItemResult;
|
||||||
|
items: ICategorizeItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRelevantForm extends IGenerateForm {
|
export interface IRelevantForm extends IGenerateForm {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
EdgeProps,
|
EdgeProps,
|
||||||
getBezierPath,
|
getBezierPath,
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
|
import { memo } from 'react';
|
||||||
import useGraphStore from '../../store';
|
import useGraphStore from '../../store';
|
||||||
|
|
||||||
import { useFetchAgent } from '@/hooks/use-agent-request';
|
import { useFetchAgent } from '@/hooks/use-agent-request';
|
||||||
@ -12,7 +13,7 @@ import { cn } from '@/lib/utils';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { NodeHandleId, Operator } from '../../constant';
|
import { NodeHandleId, Operator } from '../../constant';
|
||||||
|
|
||||||
export function ButtonEdge({
|
function InnerButtonEdge({
|
||||||
id,
|
id,
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
@ -77,7 +78,8 @@ export function ButtonEdge({
|
|||||||
const visible = useMemo(() => {
|
const visible = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
data?.isHovered &&
|
data?.isHovered &&
|
||||||
sourceHandleId !== NodeHandleId.Tool && // The connection between the agent node and the tool node does not need to display the delete button
|
sourceHandleId !== NodeHandleId.Tool &&
|
||||||
|
sourceHandleId !== NodeHandleId.AgentBottom && // The connection between the agent node and the tool node does not need to display the delete button
|
||||||
!target.startsWith(Operator.Tool)
|
!target.startsWith(Operator.Tool)
|
||||||
);
|
);
|
||||||
}, [data?.isHovered, sourceHandleId, target]);
|
}, [data?.isHovered, sourceHandleId, target]);
|
||||||
@ -120,3 +122,5 @@ export function ButtonEdge({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ButtonEdge = memo(InnerButtonEdge);
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
import { Handle, Position } from '@xyflow/react';
|
|
||||||
|
|
||||||
import React, { memo } 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 memo(CategorizeHandle);
|
|
||||||
@ -34,15 +34,15 @@ export function InnerCategorizeNode({
|
|||||||
<div className={'bg-background-card rounded-sm px-1'}>
|
<div className={'bg-background-card rounded-sm px-1'}>
|
||||||
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
||||||
</div>
|
</div>
|
||||||
{positions.map((position, idx) => {
|
{positions.map((position) => {
|
||||||
return (
|
return (
|
||||||
<div key={idx}>
|
<div key={position.uuid}>
|
||||||
<div className={'bg-background-card rounded-sm p-1'}>
|
<div className={'bg-background-card rounded-sm p-1 truncate'}>
|
||||||
{position.text}
|
{position.name}
|
||||||
</div>
|
</div>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
key={position.text}
|
// key={position.text}
|
||||||
id={position.text}
|
id={position.uuid}
|
||||||
type="source"
|
type="source"
|
||||||
position={Position.Right}
|
position={Position.Right}
|
||||||
isConnectable
|
isConnectable
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { ICategorizeItemResult } from '@/interfaces/database/agent';
|
|
||||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
||||||
import { useUpdateNodeInternals } from '@xyflow/react';
|
import { useUpdateNodeInternals } from '@xyflow/react';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { useCreateCategorizeFormSchema } from '../../form/categorize-form/use-form-schema';
|
||||||
|
|
||||||
export const useBuildCategorizeHandlePositions = ({
|
export const useBuildCategorizeHandlePositions = ({
|
||||||
data,
|
data,
|
||||||
@ -13,33 +14,35 @@ export const useBuildCategorizeHandlePositions = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
const updateNodeInternals = useUpdateNodeInternals();
|
||||||
|
|
||||||
const categoryData: ICategorizeItemResult = useMemo(() => {
|
const FormSchema = useCreateCategorizeFormSchema();
|
||||||
return get(data, `form.category_description`, {});
|
|
||||||
|
type FormSchemaType = z.infer<typeof FormSchema>;
|
||||||
|
|
||||||
|
const items: Required<FormSchemaType['items']> = useMemo(() => {
|
||||||
|
return get(data, `form.items`, []);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const positions = useMemo(() => {
|
const positions = useMemo(() => {
|
||||||
const list: Array<{
|
const list: Array<{
|
||||||
text: string;
|
|
||||||
top: number;
|
top: number;
|
||||||
idx: number;
|
name: string;
|
||||||
}> = [];
|
uuid: string;
|
||||||
|
}> &
|
||||||
|
Required<FormSchemaType['items']> = [];
|
||||||
|
|
||||||
Object.keys(categoryData)
|
items.forEach((x, idx) => {
|
||||||
.sort((a, b) => categoryData[a].index - categoryData[b].index)
|
list.push({
|
||||||
.forEach((x, idx) => {
|
...x,
|
||||||
list.push({
|
top: idx === 0 ? 86 : list[idx - 1].top + 8 + 24,
|
||||||
text: x,
|
|
||||||
idx,
|
|
||||||
top: idx === 0 ? 86 : list[idx - 1].top + 8 + 24,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}, [categoryData]);
|
}, [items]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateNodeInternals(id);
|
updateNodeInternals(id);
|
||||||
}, [id, updateNodeInternals, categoryData]);
|
}, [id, updateNodeInternals, items]);
|
||||||
|
|
||||||
return { positions };
|
return { positions };
|
||||||
};
|
};
|
||||||
|
|||||||
@ -321,7 +321,7 @@ export const initialCategorizeValues = {
|
|||||||
query: AgentGlobals.SysQuery,
|
query: AgentGlobals.SysQuery,
|
||||||
parameter: ModelVariableType.Precise,
|
parameter: ModelVariableType.Precise,
|
||||||
message_history_window_size: 1,
|
message_history_window_size: 1,
|
||||||
category_description: {},
|
items: [],
|
||||||
outputs: {
|
outputs: {
|
||||||
category_name: {
|
category_name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -760,6 +760,7 @@ export const RestrictedUpstreamMap = {
|
|||||||
[Operator.TavilyExtract]: [Operator.Begin],
|
[Operator.TavilyExtract]: [Operator.Begin],
|
||||||
[Operator.StringTransform]: [Operator.Begin],
|
[Operator.StringTransform]: [Operator.Begin],
|
||||||
[Operator.UserFillUp]: [Operator.Begin],
|
[Operator.UserFillUp]: [Operator.Begin],
|
||||||
|
[Operator.Tool]: [Operator.Begin],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NodeMap = {
|
export const NodeMap = {
|
||||||
|
|||||||
@ -28,7 +28,11 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form';
|
import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import useGraphStore from '../../store';
|
||||||
import DynamicExample from './dynamic-example';
|
import DynamicExample from './dynamic-example';
|
||||||
|
import { useCreateCategorizeFormSchema } from './use-form-schema';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
nodeId?: string;
|
nodeId?: string;
|
||||||
@ -155,6 +159,12 @@ const InnerFormSet = ({ index }: IProps & { index: number }) => {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{/* Create a hidden field to make Form instance record this */}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name={'uuid'}
|
||||||
|
render={() => <div></div>}
|
||||||
|
/>
|
||||||
<DynamicExample name={buildFieldName('examples')}></DynamicExample>
|
<DynamicExample name={buildFieldName('examples')}></DynamicExample>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
@ -164,21 +174,38 @@ const FormSet = memo(InnerFormSet);
|
|||||||
|
|
||||||
const DynamicCategorize = ({ nodeId }: IProps) => {
|
const DynamicCategorize = ({ nodeId }: IProps) => {
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
const updateNodeInternals = useUpdateNodeInternals();
|
||||||
const form = useFormContext();
|
const FormSchema = useCreateCategorizeFormSchema();
|
||||||
|
|
||||||
|
const deleteCategorizeCaseEdges = useGraphStore(
|
||||||
|
(state) => state.deleteCategorizeCaseEdges,
|
||||||
|
);
|
||||||
|
const form = useFormContext<z.infer<typeof FormSchema>>();
|
||||||
const { t } = useTranslate('flow');
|
const { t } = useTranslate('flow');
|
||||||
const { fields, remove, append } = useFieldArray({
|
const { fields, remove, append } = useFieldArray({
|
||||||
name: 'items',
|
name: 'items',
|
||||||
control: form.control,
|
control: form.control,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = useCallback(() => {
|
||||||
append({
|
append({
|
||||||
name: humanId(),
|
name: humanId(),
|
||||||
description: '',
|
description: '',
|
||||||
|
uuid: uuid(),
|
||||||
examples: [{ value: '' }],
|
examples: [{ value: '' }],
|
||||||
});
|
});
|
||||||
if (nodeId) updateNodeInternals(nodeId);
|
if (nodeId) updateNodeInternals(nodeId);
|
||||||
};
|
}, [append, nodeId, updateNodeInternals]);
|
||||||
|
|
||||||
|
const handleRemove = useCallback(
|
||||||
|
(index: number) => () => {
|
||||||
|
remove(index);
|
||||||
|
if (nodeId) {
|
||||||
|
const uuid = fields[index].uuid;
|
||||||
|
deleteCategorizeCaseEdges(nodeId, uuid);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[deleteCategorizeCaseEdges, fields, nodeId, remove],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4 ">
|
<div className="flex flex-col gap-4 ">
|
||||||
@ -194,7 +221,7 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="w-9 p-0"
|
className="w-9 p-0"
|
||||||
onClick={() => remove(index)}
|
onClick={handleRemove(index)}
|
||||||
>
|
>
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -1,50 +1,26 @@
|
|||||||
import { FormContainer } from '@/components/form-container';
|
import { FormContainer } from '@/components/form-container';
|
||||||
import { LargeModelFormField } from '@/components/large-model-form-field';
|
import { LargeModelFormField } from '@/components/large-model-form-field';
|
||||||
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
|
|
||||||
import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item';
|
import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item';
|
||||||
import { Form } from '@/components/ui/form';
|
import { Form } from '@/components/ui/form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { initialCategorizeValues } from '../../constant';
|
import { initialCategorizeValues } from '../../constant';
|
||||||
import { INextOperatorForm } from '../../interface';
|
import { INextOperatorForm } from '../../interface';
|
||||||
import { buildOutputList } from '../../utils/build-output-list';
|
import { buildOutputList } from '../../utils/build-output-list';
|
||||||
import { Output } from '../components/output';
|
import { Output } from '../components/output';
|
||||||
import { QueryVariable } from '../components/query-variable';
|
import { QueryVariable } from '../components/query-variable';
|
||||||
import DynamicCategorize from './dynamic-categorize';
|
import DynamicCategorize from './dynamic-categorize';
|
||||||
|
import { useCreateCategorizeFormSchema } from './use-form-schema';
|
||||||
import { useValues } from './use-values';
|
import { useValues } from './use-values';
|
||||||
import { useWatchFormChange } from './use-watch-change';
|
import { useWatchFormChange } from './use-watch-change';
|
||||||
|
|
||||||
const outputList = buildOutputList(initialCategorizeValues.outputs);
|
const outputList = buildOutputList(initialCategorizeValues.outputs);
|
||||||
|
|
||||||
function CategorizeForm({ node }: INextOperatorForm) {
|
function CategorizeForm({ node }: INextOperatorForm) {
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const values = useValues(node);
|
const values = useValues(node);
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = useCreateCategorizeFormSchema();
|
||||||
query: z.string().optional(),
|
|
||||||
parameter: z.string().optional(),
|
|
||||||
...LlmSettingSchema,
|
|
||||||
message_history_window_size: z.coerce.number(),
|
|
||||||
items: z.array(
|
|
||||||
z
|
|
||||||
.object({
|
|
||||||
name: z.string().min(1, t('flow.nameMessage')).trim(),
|
|
||||||
description: z.string().optional(),
|
|
||||||
examples: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
value: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
defaultValues: values,
|
defaultValues: values,
|
||||||
|
|||||||
32
web/src/pages/agent/form/categorize-form/use-form-schema.ts
Normal file
32
web/src/pages/agent/form/categorize-form/use-form-schema.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export function useCreateCategorizeFormSchema() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
query: z.string().optional(),
|
||||||
|
parameter: z.string().optional(),
|
||||||
|
...LlmSettingSchema,
|
||||||
|
message_history_window_size: z.coerce.number(),
|
||||||
|
items: z.array(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
name: z.string().min(1, t('flow.nameMessage')).trim(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
uuid: z.string(),
|
||||||
|
examples: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
value: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
return FormSchema;
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { ModelVariableType } from '@/constants/knowledge';
|
import { ModelVariableType } from '@/constants/knowledge';
|
||||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
||||||
import { get, isEmpty, isPlainObject, omit } from 'lodash';
|
import { get, isEmpty, isPlainObject } from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { buildCategorizeListFromObject } from '../../utils';
|
import { buildCategorizeListFromObject } from '../../utils';
|
||||||
|
|
||||||
@ -25,12 +25,12 @@ export function useValues(node?: RAGFlowNodeType) {
|
|||||||
get(node, 'data.form.category_description', {}),
|
get(node, 'data.form.category_description', {}),
|
||||||
);
|
);
|
||||||
if (isPlainObject(formData)) {
|
if (isPlainObject(formData)) {
|
||||||
const nextValues = {
|
// const nextValues = {
|
||||||
...omit(formData, 'category_description'),
|
// ...omit(formData, 'category_description'),
|
||||||
items,
|
// items,
|
||||||
};
|
// };
|
||||||
|
|
||||||
return nextValues;
|
return formData;
|
||||||
}
|
}
|
||||||
}, [node]);
|
}, [node]);
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import { omit } from 'lodash';
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { UseFormReturn, useWatch } from 'react-hook-form';
|
import { UseFormReturn, useWatch } from 'react-hook-form';
|
||||||
import useGraphStore from '../../store';
|
import useGraphStore from '../../store';
|
||||||
import { buildCategorizeObjectFromList } from '../../utils';
|
|
||||||
|
|
||||||
export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
|
export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
|
||||||
let values = useWatch({ control: form?.control });
|
let values = useWatch({ control: form?.control });
|
||||||
@ -10,21 +8,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Manually triggered form updates are synchronized to the canvas
|
// Manually triggered form updates are synchronized to the canvas
|
||||||
if (id && form?.formState.isDirty) {
|
if (id) {
|
||||||
values = form?.getValues();
|
values = form?.getValues();
|
||||||
let nextValues: any = values;
|
|
||||||
|
|
||||||
const categoryDescription = Array.isArray(values.items)
|
updateNodeForm(id, { ...values, items: values.items?.slice() || [] });
|
||||||
? buildCategorizeObjectFromList(values.items)
|
|
||||||
: {};
|
|
||||||
if (categoryDescription) {
|
|
||||||
nextValues = {
|
|
||||||
...omit(values, 'items'),
|
|
||||||
category_description: categoryDescription,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNodeForm(id, nextValues);
|
|
||||||
}
|
}
|
||||||
}, [form?.formState.isDirty, id, updateNodeForm, values]);
|
}, [id, updateNodeForm, values]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export function Output({ list }: OutputProps) {
|
|||||||
key={idx}
|
key={idx}
|
||||||
className="bg-background-highlight text-background-checked rounded-sm px-2 py-1"
|
className="bg-background-highlight text-background-checked rounded-sm px-2 py-1"
|
||||||
>
|
>
|
||||||
{x.title}: {x.type}
|
{x.title}: <span className="text-text-sub-title">{x.type}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -49,6 +49,7 @@ import {
|
|||||||
initialRetrievalValues,
|
initialRetrievalValues,
|
||||||
initialRewriteQuestionValues,
|
initialRewriteQuestionValues,
|
||||||
initialSwitchValues,
|
initialSwitchValues,
|
||||||
|
initialTavilyExtractValues,
|
||||||
initialTavilyValues,
|
initialTavilyValues,
|
||||||
initialTemplateValues,
|
initialTemplateValues,
|
||||||
initialTuShareValues,
|
initialTuShareValues,
|
||||||
@ -135,6 +136,7 @@ export const useInitializeOperatorParams = () => {
|
|||||||
[Operator.WaitingDialogue]: initialWaitingDialogueValues,
|
[Operator.WaitingDialogue]: initialWaitingDialogueValues,
|
||||||
[Operator.Agent]: { ...initialAgentValues, llm_id: llmId },
|
[Operator.Agent]: { ...initialAgentValues, llm_id: llmId },
|
||||||
[Operator.TavilySearch]: initialTavilyValues,
|
[Operator.TavilySearch]: initialTavilyValues,
|
||||||
|
[Operator.TavilyExtract]: initialTavilyExtractValues,
|
||||||
};
|
};
|
||||||
}, [llmId]);
|
}, [llmId]);
|
||||||
|
|
||||||
@ -331,7 +333,7 @@ export const useHandleFormValuesChange = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useValidateConnection = () => {
|
export const useValidateConnection = () => {
|
||||||
const { edges, getOperatorTypeFromId, getParentIdById } = useGraphStore(
|
const { getOperatorTypeFromId, getParentIdById } = useGraphStore(
|
||||||
(state) => state,
|
(state) => state,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -354,20 +356,19 @@ export const useValidateConnection = () => {
|
|||||||
const isSelfConnected = connection.target === connection.source;
|
const isSelfConnected = connection.target === connection.source;
|
||||||
|
|
||||||
// limit the connection between two nodes to only one connection line in one direction
|
// limit the connection between two nodes to only one connection line in one direction
|
||||||
const hasLine = edges.some(
|
// const hasLine = edges.some(
|
||||||
(x) => x.source === connection.source && x.target === connection.target,
|
// (x) => x.source === connection.source && x.target === connection.target,
|
||||||
);
|
// );
|
||||||
|
|
||||||
const ret =
|
const ret =
|
||||||
!isSelfConnected &&
|
!isSelfConnected &&
|
||||||
!hasLine &&
|
|
||||||
RestrictedUpstreamMap[
|
RestrictedUpstreamMap[
|
||||||
getOperatorTypeFromId(connection.source) as Operator
|
getOperatorTypeFromId(connection.source) as Operator
|
||||||
]?.every((x) => x !== getOperatorTypeFromId(connection.target)) &&
|
]?.every((x) => x !== getOperatorTypeFromId(connection.target)) &&
|
||||||
isSameNodeChild(connection);
|
isSameNodeChild(connection);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
[edges, getOperatorTypeFromId, isSameNodeChild],
|
[getOperatorTypeFromId, isSameNodeChild],
|
||||||
);
|
);
|
||||||
|
|
||||||
return isValidConnection;
|
return isValidConnection;
|
||||||
|
|||||||
@ -84,6 +84,7 @@ export type RFState = {
|
|||||||
setClickedNodeId: (id?: string) => void;
|
setClickedNodeId: (id?: string) => void;
|
||||||
setClickedToolId: (id?: string) => void;
|
setClickedToolId: (id?: string) => void;
|
||||||
findUpstreamNodeById: (id?: string | null) => RAGFlowNodeType | undefined;
|
findUpstreamNodeById: (id?: string | null) => RAGFlowNodeType | undefined;
|
||||||
|
deleteCategorizeCaseEdges: (source: string, sourceHandle: string) => void; // Deleting a condition of a classification operator will delete the related edge
|
||||||
};
|
};
|
||||||
|
|
||||||
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
||||||
@ -307,14 +308,14 @@ const useGraphStore = create<RFState>()(
|
|||||||
[sourceHandle as string]: undefined,
|
[sourceHandle as string]: undefined,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Operator.Categorize:
|
// case Operator.Categorize:
|
||||||
if (sourceHandle)
|
// if (sourceHandle)
|
||||||
updateNodeForm(source, undefined, [
|
// updateNodeForm(source, undefined, [
|
||||||
'category_description',
|
// 'category_description',
|
||||||
sourceHandle,
|
// sourceHandle,
|
||||||
'to',
|
// 'to',
|
||||||
]);
|
// ]);
|
||||||
break;
|
// break;
|
||||||
case Operator.Switch: {
|
case Operator.Switch: {
|
||||||
updateSwitchFormData(source, sourceHandle, target, false);
|
updateSwitchFormData(source, sourceHandle, target, false);
|
||||||
break;
|
break;
|
||||||
@ -508,6 +509,15 @@ const useGraphStore = create<RFState>()(
|
|||||||
const edge = edges.find((x) => x.target === id);
|
const edge = edges.find((x) => x.target === id);
|
||||||
return getNode(edge?.source);
|
return getNode(edge?.source);
|
||||||
},
|
},
|
||||||
|
deleteCategorizeCaseEdges: (source, sourceHandle) => {
|
||||||
|
const { edges, setEdges } = get();
|
||||||
|
setEdges(
|
||||||
|
edges.filter(
|
||||||
|
(edge) =>
|
||||||
|
!(edge.source === source && edge.sourceHandle === sourceHandle),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
})),
|
})),
|
||||||
{ name: 'graph', trace: true },
|
{ name: 'graph', trace: true },
|
||||||
),
|
),
|
||||||
|
|||||||
@ -13,13 +13,12 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { RAGFlowSelect } from '@/components/ui/select';
|
|
||||||
import { FileMimeType, Platform } from '@/constants/common';
|
import { FileMimeType, Platform } from '@/constants/common';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const options = Object.values(Platform).map((x) => ({ label: x, value: x }));
|
// const options = Object.values(Platform).map((x) => ({ label: x, value: x }));
|
||||||
|
|
||||||
export function UploadAgentForm({ hideModal, onOk }: IModalProps<any>) {
|
export function UploadAgentForm({ hideModal, onOk }: IModalProps<any>) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -72,7 +71,7 @@ export function UploadAgentForm({ hideModal, onOk }: IModalProps<any>) {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
{/* <FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="platform"
|
name="platform"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
@ -84,7 +83,7 @@ export function UploadAgentForm({ hideModal, onOk }: IModalProps<any>) {
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/> */}
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -159,7 +159,7 @@ function buildAgentTools(edges: Edge[], nodes: Node[], nodeId: string) {
|
|||||||
return {
|
return {
|
||||||
component_name: Operator.Agent,
|
component_name: Operator.Agent,
|
||||||
id,
|
id,
|
||||||
name,
|
name: name as string, // Cast name to string and provide fallback
|
||||||
params: { ...formData },
|
params: { ...formData },
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
@ -172,27 +172,29 @@ function filterTargetsBySourceHandleId(edges: Edge[], handleId: string) {
|
|||||||
return edges.filter((x) => x.sourceHandle === handleId).map((x) => x.target);
|
return edges.filter((x) => x.sourceHandle === handleId).map((x) => x.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildCategorizeTos(edges: Edge[], nodes: Node[], nodeId: string) {
|
function buildCategorize(edges: Edge[], nodes: Node[], nodeId: string) {
|
||||||
const node = nodes.find((x) => x.id === nodeId);
|
const node = nodes.find((x) => x.id === nodeId);
|
||||||
const params = { ...(node?.data.form ?? {}) } as ICategorizeForm;
|
const params = { ...(node?.data.form ?? {}) } as ICategorizeForm;
|
||||||
if (node && node.data.label === Operator.Categorize) {
|
if (node && node.data.label === Operator.Categorize) {
|
||||||
const subEdges = edges.filter((x) => x.source === nodeId);
|
const subEdges = edges.filter((x) => x.source === nodeId);
|
||||||
|
|
||||||
const categoryDescription = params.category_description || {};
|
const items = params.items || [];
|
||||||
|
|
||||||
const nextCategoryDescription = Object.entries(categoryDescription).reduce<
|
const nextCategoryDescription = items.reduce<
|
||||||
ICategorizeForm['category_description']
|
ICategorizeForm['category_description']
|
||||||
>((pre, [key, val]) => {
|
>((pre, val) => {
|
||||||
|
const key = val.name;
|
||||||
pre[key] = {
|
pre[key] = {
|
||||||
...val,
|
...omit(val, 'name', 'uuid'),
|
||||||
to: filterTargetsBySourceHandleId(subEdges, key),
|
examples: val.examples?.map((x) => x.value) || [],
|
||||||
|
to: filterTargetsBySourceHandleId(subEdges, val.uuid),
|
||||||
};
|
};
|
||||||
return pre;
|
return pre;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
params.category_description = nextCategoryDescription;
|
params.category_description = nextCategoryDescription;
|
||||||
}
|
}
|
||||||
return params;
|
return omit(params, 'items');
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildOperatorParams = (operatorName: string) =>
|
const buildOperatorParams = (operatorName: string) =>
|
||||||
@ -236,7 +238,7 @@ export const buildDslComponentsByGraph = (
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Operator.Categorize:
|
case Operator.Categorize:
|
||||||
params = buildCategorizeTos(edges, nodes, id);
|
params = buildCategorize(edges, nodes, id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user