diff --git a/web/src/components/highlight-markdown/index.tsx b/web/src/components/highlight-markdown/index.tsx index 9b21155e1..3d91b501c 100644 --- a/web/src/components/highlight-markdown/index.tsx +++ b/web/src/components/highlight-markdown/index.tsx @@ -1,7 +1,10 @@ import classNames from 'classnames'; import Markdown from 'react-markdown'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import { + oneDark, + oneLight, +} from 'react-syntax-highlighter/dist/esm/styles/prism'; import rehypeKatex from 'rehype-katex'; import rehypeRaw from 'rehype-raw'; import remarkGfm from 'remark-gfm'; @@ -34,7 +37,7 @@ const HightLightMarkdown = ({ {...rest} PreTag="div" language={match[1]} - style={dark && oneDark} + style={dark ? oneDark : oneLight} > {String(children).replace(/\n$/, '')} diff --git a/web/src/components/llm-select/next.tsx b/web/src/components/llm-select/next.tsx index a90e88131..075b43e5a 100644 --- a/web/src/components/llm-select/next.tsx +++ b/web/src/components/llm-select/next.tsx @@ -3,7 +3,6 @@ import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks'; import * as SelectPrimitive from '@radix-ui/react-select'; import { forwardRef, memo, useState } from 'react'; import { LlmSettingFieldItems } from '../llm-setting-items/next'; -import { SelectWithSearch } from '../originui/select-with-search'; import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'; import { Select, SelectTrigger, SelectValue } from '../ui/select'; @@ -18,21 +17,13 @@ interface IProps { const NextInnerLLMSelect = forwardRef< React.ElementRef, IProps ->(({ value, disabled, onChange }, ref) => { +>(({ value, disabled }, ref) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const modelOptions = useComposeLlmOptionsByModelTypes([ LlmModelType.Chat, LlmModelType.Image2text, ]); - return ( - - ); - return ( + + {/* + */} diff --git a/web/src/components/originui/select-with-search.tsx b/web/src/components/originui/select-with-search.tsx index ef036b773..9627fe027 100644 --- a/web/src/components/originui/select-with-search.tsx +++ b/web/src/components/originui/select-with-search.tsx @@ -32,6 +32,7 @@ import { RAGFlowSelectOptionType } from '../ui/select'; export type SelectWithSearchFlagOptionType = { label: ReactNode; value?: string; + disabled?: boolean; options?: RAGFlowSelectOptionType[]; }; @@ -119,6 +120,7 @@ export const SelectWithSearch = forwardRef< @@ -138,6 +140,7 @@ export const SelectWithSearch = forwardRef< {group.label} diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index 60cec288d..2a081b67f 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -2,6 +2,7 @@ import message from '@/components/ui/message'; import { AgentGlobals } from '@/constants/agent'; import { ITraceData } from '@/interfaces/database/agent'; import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow'; +import { IDebugSingleRequestBody } from '@/interfaces/request/agent'; import i18n from '@/locales/config'; import { BeginId } from '@/pages/agent/constant'; import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks'; @@ -30,6 +31,8 @@ export const enum AgentApiAction { UploadCanvasFile = 'uploadCanvasFile', Trace = 'trace', TestDbConnect = 'testDbConnect', + DebugSingle = 'debugSingle', + FetchInputForm = 'fetchInputForm', } export const EmptyDsl = { @@ -353,3 +356,43 @@ export const useTestDbConnect = () => { return { data, loading, testDbConnect: mutateAsync }; }; + +export const useDebugSingle = () => { + const { id } = useParams(); + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [AgentApiAction.FetchInputForm], + mutationFn: async (params: IDebugSingleRequestBody) => { + const ret = await flowService.debugSingle({ id, ...params }); + if (ret?.data?.code !== 0) { + message.error(ret?.data?.message); + } + return ret?.data?.data; + }, + }); + + return { data, loading, debugSingle: mutateAsync }; +}; + +export const useFetchInputForm = (componentId?: string) => { + const { id } = useParams(); + + const { data } = useQuery>({ + queryKey: [AgentApiAction.FetchInputForm], + initialData: {}, + enabled: !!id && !!componentId, + queryFn: async () => { + const { data } = await flowService.inputForm({ + id, + component_id: componentId, + }); + + return data.data; + }, + }); + + return data; +}; diff --git a/web/src/interfaces/request/agent.ts b/web/src/interfaces/request/agent.ts new file mode 100644 index 000000000..1c6ee8b88 --- /dev/null +++ b/web/src/interfaces/request/agent.ts @@ -0,0 +1,4 @@ +export interface IDebugSingleRequestBody { + component_id: string; + params: Record; +} diff --git a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx index 0b3575181..388741d6b 100644 --- a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx @@ -53,7 +53,7 @@ function AccordionOperators() { return ( diff --git a/web/src/pages/agent/canvas/node/iteration-node.tsx b/web/src/pages/agent/canvas/node/iteration-node.tsx index 8f672f85c..81508b535 100644 --- a/web/src/pages/agent/canvas/node/iteration-node.tsx +++ b/web/src/pages/agent/canvas/node/iteration-node.tsx @@ -21,10 +21,8 @@ export function InnerIterationNode({ isConnectable = true, selected, }: NodeProps) { - // const { theme } = useTheme(); - return ( - +
store.deleteNodeById); const deleteIterationNodeById = useGraphStore( (store) => store.deleteIterationNodeById, @@ -63,9 +70,11 @@ export function ToolBar({ selected, children, label, id }: ToolBarProps) {
- - - + {showRun && ( + + + + )}{' '} diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index d0b7272a8..390ea9d7b 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -320,6 +320,11 @@ export const initialCategorizeValues = { parameter: ModelVariableType.Precise, message_history_window_size: 1, category_description: {}, + outputs: { + category_name: { + type: 'string', + }, + }, }; export const initialMessageValues = { diff --git a/web/src/pages/agent/form-sheet/next.tsx b/web/src/pages/agent/form-sheet/next.tsx index 88b13cd45..5e16098a0 100644 --- a/web/src/pages/agent/form-sheet/next.tsx +++ b/web/src/pages/agent/form-sheet/next.tsx @@ -20,7 +20,7 @@ import OperatorIcon from '../operator-icon'; import useGraphStore from '../store'; import { needsSingleStepDebugging } from '../utils'; import { FormConfigMap } from './form-config-map'; -import SingleDebugDrawer from './single-debug-drawer'; +import SingleDebugSheet from './single-debug-sheet'; interface IProps { node?: RAGFlowNodeType; @@ -115,11 +115,11 @@ const FormSheet = ({
{singleDebugDrawerVisible && ( - + > )} ); diff --git a/web/src/pages/agent/form-sheet/single-debug-drawer/index.tsx b/web/src/pages/agent/form-sheet/single-debug-drawer/index.tsx deleted file mode 100644 index f9908ef7e..000000000 --- a/web/src/pages/agent/form-sheet/single-debug-drawer/index.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import CopyToClipboard from '@/components/copy-to-clipboard'; -import { useDebugSingle, useFetchInputElements } from '@/hooks/flow-hooks'; -import { IModalProps } from '@/interfaces/common'; -import { CloseOutlined } from '@ant-design/icons'; -import { Drawer } from 'antd'; -import { isEmpty } from 'lodash'; -import { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import JsonView from 'react18-json-view'; -import 'react18-json-view/src/style.css'; -import DebugContent from '../../debug-content'; - -interface IProps { - componentId?: string; -} - -const SingleDebugDrawer = ({ - componentId, - visible, - hideModal, -}: IModalProps & IProps) => { - const { t } = useTranslation(); - const { data: list } = useFetchInputElements(componentId); - const { debugSingle, data, loading } = useDebugSingle(); - - const onOk = useCallback( - (nextValues: any[]) => { - if (componentId) { - debugSingle({ component_id: componentId, params: nextValues }); - } - }, - [componentId, debugSingle], - ); - - const content = JSON.stringify(data, null, 2); - - return ( - - {t('flow.testRun')} - - - } - width={'100%'} - onClose={hideModal} - open={visible} - getContainer={false} - mask={false} - placement={'bottom'} - height={'95%'} - closeIcon={null} - > -
- - {!isEmpty(data) ? ( -
-
- JSON - -
- -
- ) : null} -
-
- ); -}; - -export default SingleDebugDrawer; diff --git a/web/src/pages/agent/form-sheet/single-debug-sheet/index.tsx b/web/src/pages/agent/form-sheet/single-debug-sheet/index.tsx new file mode 100644 index 000000000..e7ec20459 --- /dev/null +++ b/web/src/pages/agent/form-sheet/single-debug-sheet/index.tsx @@ -0,0 +1,80 @@ +import CopyToClipboard from '@/components/copy-to-clipboard'; +import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet'; +import { useDebugSingle } from '@/hooks/flow-hooks'; +import { useFetchInputForm } from '@/hooks/use-agent-request'; +import { IModalProps } from '@/interfaces/common'; +import { isEmpty } from 'lodash'; +import { X } from 'lucide-react'; +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import JsonView from 'react18-json-view'; +import 'react18-json-view/src/style.css'; +import DebugContent from '../../debug-content'; +import { buildBeginInputListFromObject } from '../../form/begin-form/utils'; + +interface IProps { + componentId?: string; +} + +const SingleDebugSheet = ({ + componentId, + visible, + hideModal, +}: IModalProps & IProps) => { + const { t } = useTranslation(); + const inputForm = useFetchInputForm(componentId); + const { debugSingle, data, loading } = useDebugSingle(); + + const list = useMemo(() => { + return buildBeginInputListFromObject(inputForm); + }, [inputForm]); + + const onOk = useCallback( + (nextValues: any[]) => { + if (componentId) { + debugSingle({ component_id: componentId, params: nextValues }); + } + }, + [componentId, debugSingle], + ); + + const content = JSON.stringify(data, null, 2); + + return ( + + + +
+ {t('flow.testRun')} + +
+
+
+ + {!isEmpty(data) ? ( +
+
+ JSON + +
+ +
+ ) : null} +
+
+
+ ); +}; + +export default SingleDebugSheet; diff --git a/web/src/pages/agent/form/categorize-form/dynamic-categorize.tsx b/web/src/pages/agent/form/categorize-form/dynamic-categorize.tsx index 7deb4f4ff..02354223d 100644 --- a/web/src/pages/agent/form/categorize-form/dynamic-categorize.tsx +++ b/web/src/pages/agent/form/categorize-form/dynamic-categorize.tsx @@ -28,8 +28,6 @@ import { useState, } from 'react'; import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form'; -import { Operator } from '../../constant'; -import { useBuildFormSelectOptions } from '../../form-hooks'; import DynamicExample from './dynamic-example'; interface IProps { @@ -107,13 +105,9 @@ const InnerNameInput = ({ const NameInput = memo(InnerNameInput); -const InnerFormSet = ({ nodeId, index }: IProps & { index: number }) => { +const InnerFormSet = ({ index }: IProps & { index: number }) => { const form = useFormContext(); const { t } = useTranslate('flow'); - const buildCategorizeToOptions = useBuildFormSelectOptions( - Operator.Categorize, - nodeId, - ); const buildFieldName = useCallback( (name: string) => { diff --git a/web/src/pages/agent/form/categorize-form/dynamic-example.tsx b/web/src/pages/agent/form/categorize-form/dynamic-example.tsx index d2c690b71..35d95cbc6 100644 --- a/web/src/pages/agent/form/categorize-form/dynamic-example.tsx +++ b/web/src/pages/agent/form/categorize-form/dynamic-example.tsx @@ -25,7 +25,7 @@ const DynamicExample = ({ name }: DynamicExampleProps) => { return ( - {t('flow.msg')} + {t('flow.examples')}
{fields.map((field, index) => (
diff --git a/web/src/pages/agent/form/categorize-form/index.tsx b/web/src/pages/agent/form/categorize-form/index.tsx index 5bf0b91d3..03d569466 100644 --- a/web/src/pages/agent/form/categorize-form/index.tsx +++ b/web/src/pages/agent/form/categorize-form/index.tsx @@ -8,12 +8,17 @@ import { memo } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; +import { initialCategorizeValues } from '../../constant'; import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { Output } from '../components/output'; import { QueryVariable } from '../components/query-variable'; import DynamicCategorize from './dynamic-categorize'; import { useValues } from './use-values'; import { useWatchFormChange } from './use-watch-change'; +const outputList = buildOutputList(initialCategorizeValues.outputs); + function CategorizeForm({ node }: INextOperatorForm) { const { t } = useTranslation(); @@ -62,6 +67,7 @@ function CategorizeForm({ node }: INextOperatorForm) { + ); diff --git a/web/src/pages/agent/index.tsx b/web/src/pages/agent/index.tsx index c5f415ac2..904ddd78b 100644 --- a/web/src/pages/agent/index.tsx +++ b/web/src/pages/agent/index.tsx @@ -17,6 +17,7 @@ import { Download, History, Key, + LaptopMinimalCheck, Logs, ScreenShare, Upload, @@ -91,7 +92,7 @@ export default function Agent() { onClick={() => saveGraph()} loading={loading} > - Save + Save