### What problem does this PR solve? feat: Display input parameters on operator nodes #3240 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.14.0
| "@radix-ui/react-dropdown-menu": "^2.1.2", | "@radix-ui/react-dropdown-menu": "^2.1.2", | ||||
| "@radix-ui/react-icons": "^1.3.1", | "@radix-ui/react-icons": "^1.3.1", | ||||
| "@radix-ui/react-label": "^2.1.0", | "@radix-ui/react-label": "^2.1.0", | ||||
| "@radix-ui/react-popover": "^1.1.2", | |||||
| "@radix-ui/react-select": "^2.1.2", | "@radix-ui/react-select": "^2.1.2", | ||||
| "@radix-ui/react-separator": "^1.1.0", | "@radix-ui/react-separator": "^1.1.0", | ||||
| "@radix-ui/react-slot": "^1.1.0", | "@radix-ui/react-slot": "^1.1.0", | ||||
| } | } | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/@radix-ui/react-popover": { | |||||
| "version": "1.1.2", | |||||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-popover/-/react-popover-1.1.2.tgz", | |||||
| "integrity": "sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w==", | |||||
| "dependencies": { | |||||
| "@radix-ui/primitive": "1.1.0", | |||||
| "@radix-ui/react-compose-refs": "1.1.0", | |||||
| "@radix-ui/react-context": "1.1.1", | |||||
| "@radix-ui/react-dismissable-layer": "1.1.1", | |||||
| "@radix-ui/react-focus-guards": "1.1.1", | |||||
| "@radix-ui/react-focus-scope": "1.1.0", | |||||
| "@radix-ui/react-id": "1.1.0", | |||||
| "@radix-ui/react-popper": "1.2.0", | |||||
| "@radix-ui/react-portal": "1.1.2", | |||||
| "@radix-ui/react-presence": "1.1.1", | |||||
| "@radix-ui/react-primitive": "2.0.0", | |||||
| "@radix-ui/react-slot": "1.1.0", | |||||
| "@radix-ui/react-use-controllable-state": "1.1.0", | |||||
| "aria-hidden": "^1.1.1", | |||||
| "react-remove-scroll": "2.6.0" | |||||
| }, | |||||
| "peerDependencies": { | |||||
| "@types/react": "*", | |||||
| "@types/react-dom": "*", | |||||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||||
| }, | |||||
| "peerDependenciesMeta": { | |||||
| "@types/react": { | |||||
| "optional": true | |||||
| }, | |||||
| "@types/react-dom": { | |||||
| "optional": true | |||||
| } | |||||
| } | |||||
| }, | |||||
| "node_modules/@radix-ui/react-popper": { | "node_modules/@radix-ui/react-popper": { | ||||
| "version": "1.2.0", | "version": "1.2.0", | ||||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", | "resolved": "https://registry.npmmirror.com/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", |
| "@radix-ui/react-dropdown-menu": "^2.1.2", | "@radix-ui/react-dropdown-menu": "^2.1.2", | ||||
| "@radix-ui/react-icons": "^1.3.1", | "@radix-ui/react-icons": "^1.3.1", | ||||
| "@radix-ui/react-label": "^2.1.0", | "@radix-ui/react-label": "^2.1.0", | ||||
| "@radix-ui/react-popover": "^1.1.2", | |||||
| "@radix-ui/react-select": "^2.1.2", | "@radix-ui/react-select": "^2.1.2", | ||||
| "@radix-ui/react-separator": "^1.1.0", | "@radix-ui/react-separator": "^1.1.0", | ||||
| "@radix-ui/react-slot": "^1.1.0", | "@radix-ui/react-slot": "^1.1.0", |
| 'use client'; | |||||
| import * as PopoverPrimitive from '@radix-ui/react-popover'; | |||||
| import * as React from 'react'; | |||||
| import { cn } from '@/lib/utils'; | |||||
| const Popover = PopoverPrimitive.Root; | |||||
| const PopoverTrigger = PopoverPrimitive.Trigger; | |||||
| const PopoverContent = React.forwardRef< | |||||
| React.ElementRef<typeof PopoverPrimitive.Content>, | |||||
| React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> | |||||
| >(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( | |||||
| <PopoverPrimitive.Portal> | |||||
| <PopoverPrimitive.Content | |||||
| ref={ref} | |||||
| align={align} | |||||
| sideOffset={sideOffset} | |||||
| className={cn( | |||||
| 'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', | |||||
| className, | |||||
| )} | |||||
| {...props} | |||||
| /> | |||||
| </PopoverPrimitive.Portal> | |||||
| )); | |||||
| PopoverContent.displayName = PopoverPrimitive.Content.displayName; | |||||
| export { Popover, PopoverContent, PopoverTrigger }; |
| import * as React from 'react'; | |||||
| import { cn } from '@/lib/utils'; | |||||
| const Table = React.forwardRef< | |||||
| HTMLTableElement, | |||||
| React.HTMLAttributes<HTMLTableElement> | |||||
| >(({ className, ...props }, ref) => ( | |||||
| <div className="relative w-full overflow-auto"> | |||||
| <table | |||||
| ref={ref} | |||||
| className={cn('w-full caption-bottom text-sm', className)} | |||||
| {...props} | |||||
| /> | |||||
| </div> | |||||
| )); | |||||
| Table.displayName = 'Table'; | |||||
| const TableHeader = React.forwardRef< | |||||
| HTMLTableSectionElement, | |||||
| React.HTMLAttributes<HTMLTableSectionElement> | |||||
| >(({ className, ...props }, ref) => ( | |||||
| <thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} /> | |||||
| )); | |||||
| TableHeader.displayName = 'TableHeader'; | |||||
| const TableBody = React.forwardRef< | |||||
| HTMLTableSectionElement, | |||||
| React.HTMLAttributes<HTMLTableSectionElement> | |||||
| >(({ className, ...props }, ref) => ( | |||||
| <tbody | |||||
| ref={ref} | |||||
| className={cn('[&_tr:last-child]:border-0', className)} | |||||
| {...props} | |||||
| /> | |||||
| )); | |||||
| TableBody.displayName = 'TableBody'; | |||||
| const TableFooter = React.forwardRef< | |||||
| HTMLTableSectionElement, | |||||
| React.HTMLAttributes<HTMLTableSectionElement> | |||||
| >(({ className, ...props }, ref) => ( | |||||
| <tfoot | |||||
| ref={ref} | |||||
| className={cn( | |||||
| 'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', | |||||
| className, | |||||
| )} | |||||
| {...props} | |||||
| /> | |||||
| )); | |||||
| TableFooter.displayName = 'TableFooter'; | |||||
| const TableRow = React.forwardRef< | |||||
| HTMLTableRowElement, | |||||
| React.HTMLAttributes<HTMLTableRowElement> | |||||
| >(({ className, ...props }, ref) => ( | |||||
| <tr | |||||
| ref={ref} | |||||
| className={cn( | |||||
| 'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', | |||||
| className, | |||||
| )} | |||||
| {...props} | |||||
| /> | |||||
| )); | |||||
| TableRow.displayName = 'TableRow'; | |||||
| const TableHead = React.forwardRef< | |||||
| HTMLTableCellElement, | |||||
| React.ThHTMLAttributes<HTMLTableCellElement> | |||||
| >(({ className, ...props }, ref) => ( | |||||
| <th | |||||
| ref={ref} | |||||
| className={cn( | |||||
| 'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0', | |||||
| className, | |||||
| )} | |||||
| {...props} | |||||
| /> | |||||
| )); | |||||
| TableHead.displayName = 'TableHead'; | |||||
| const TableCell = React.forwardRef< | |||||
| HTMLTableCellElement, | |||||
| React.TdHTMLAttributes<HTMLTableCellElement> | |||||
| >(({ className, ...props }, ref) => ( | |||||
| <td | |||||
| ref={ref} | |||||
| className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)} | |||||
| {...props} | |||||
| /> | |||||
| )); | |||||
| TableCell.displayName = 'TableCell'; | |||||
| const TableCaption = React.forwardRef< | |||||
| HTMLTableCaptionElement, | |||||
| React.HTMLAttributes<HTMLTableCaptionElement> | |||||
| >(({ className, ...props }, ref) => ( | |||||
| <caption | |||||
| ref={ref} | |||||
| className={cn('mt-4 text-sm text-muted-foreground', className)} | |||||
| {...props} | |||||
| /> | |||||
| )); | |||||
| TableCaption.displayName = 'TableCaption'; | |||||
| export { | |||||
| Table, | |||||
| TableBody, | |||||
| TableCaption, | |||||
| TableCell, | |||||
| TableFooter, | |||||
| TableHead, | |||||
| TableHeader, | |||||
| TableRow, | |||||
| }; |
| export interface IModalProps<T> { | export interface IModalProps<T> { | ||||
| showModal?(): void; | showModal?(): void; | ||||
| hideModal(): void; | |||||
| hideModal?(): void; | |||||
| switchVisible?(visible: boolean): void; | |||||
| visible?: boolean; | visible?: boolean; | ||||
| loading?: boolean; | loading?: boolean; | ||||
| onOk?(payload?: T): Promise<any> | void; | onOk?(payload?: T): Promise<any> | void; |
| 'If the response is HTML formatted and only the primary content wanted, please toggle it on.', | 'If the response is HTML formatted and only the primary content wanted, please toggle it on.', | ||||
| reference: 'Reference', | reference: 'Reference', | ||||
| input: 'Input', | input: 'Input', | ||||
| output: 'Output', | |||||
| parameter: 'Parameter', | parameter: 'Parameter', | ||||
| howUseId: 'How to use agent ID?', | howUseId: 'How to use agent ID?', | ||||
| content: 'Content', | |||||
| operationResults: 'Operation Results', | |||||
| }, | }, | ||||
| footer: { | footer: { | ||||
| profile: 'All rights reserved @ React', | profile: 'All rights reserved @ React', |
| cleanHtmlTip: '如果回應是 HTML 格式並且只需要主要內容,請將其開啟。', | cleanHtmlTip: '如果回應是 HTML 格式並且只需要主要內容,請將其開啟。', | ||||
| reference: '引用', | reference: '引用', | ||||
| input: '輸入', | input: '輸入', | ||||
| output: '輸出', | |||||
| parameter: '參數', | parameter: '參數', | ||||
| howUseId: '如何使用Agent ID?', | howUseId: '如何使用Agent ID?', | ||||
| content: '內容', | |||||
| operationResults: '運行結果', | |||||
| }, | }, | ||||
| footer: { | footer: { | ||||
| profile: '“保留所有權利 @ react”', | profile: '“保留所有權利 @ react”', |
| cleanHtmlTip: '如果响应是 HTML 格式且只需要主要内容,请将其打开。', | cleanHtmlTip: '如果响应是 HTML 格式且只需要主要内容,请将其打开。', | ||||
| reference: '引用', | reference: '引用', | ||||
| input: '输入', | input: '输入', | ||||
| output: '输出', | |||||
| parameter: '参数', | parameter: '参数', | ||||
| howUseId: '如何使用Agent ID?', | howUseId: '如何使用Agent ID?', | ||||
| content: '内容', | |||||
| operationResults: '运行结果', | |||||
| }, | }, | ||||
| footer: { | footer: { | ||||
| profile: 'All rights reserved @ React', | profile: 'All rights reserved @ React', |
| import { Button } from '@/components/ui/button'; | |||||
| import { | |||||
| Card, | |||||
| CardContent, | |||||
| CardDescription, | |||||
| CardFooter, | |||||
| CardHeader, | |||||
| CardTitle, | |||||
| } from '@/components/ui/card'; | |||||
| import { Input } from '@/components/ui/input'; | |||||
| import { Label } from '@/components/ui/label'; | |||||
| import { | |||||
| Select, | |||||
| SelectContent, | |||||
| SelectItem, | |||||
| SelectTrigger, | |||||
| SelectValue, | |||||
| } from '@/components/ui/select'; | |||||
| export function CardWithForm() { | |||||
| return ( | |||||
| <Card className="w-[350px]"> | |||||
| <CardHeader> | |||||
| <CardTitle>Create project</CardTitle> | |||||
| <CardDescription>Deploy your new project in one-click.</CardDescription> | |||||
| </CardHeader> | |||||
| <CardContent> | |||||
| <form> | |||||
| <div className="grid w-full items-center gap-4"> | |||||
| <div className="flex flex-col space-y-1.5"> | |||||
| <Label htmlFor="name">Name</Label> | |||||
| <Input id="name" placeholder="Name of your project" /> | |||||
| </div> | |||||
| <div className="flex flex-col space-y-1.5"> | |||||
| <Label htmlFor="framework">Framework</Label> | |||||
| <Select> | |||||
| <SelectTrigger id="framework"> | |||||
| <SelectValue placeholder="Select" /> | |||||
| </SelectTrigger> | |||||
| <SelectContent position="popper"> | |||||
| <SelectItem value="next">Next.js</SelectItem> | |||||
| <SelectItem value="sveltekit">SvelteKit</SelectItem> | |||||
| <SelectItem value="astro">Astro</SelectItem> | |||||
| <SelectItem value="nuxt">Nuxt.js</SelectItem> | |||||
| </SelectContent> | |||||
| </Select> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| </CardContent> | |||||
| <CardFooter className="flex justify-between"> | |||||
| <Button variant="outline">Cancel</Button> | |||||
| <Button>Deploy</Button> | |||||
| </CardFooter> | |||||
| </Card> | |||||
| ); | |||||
| } |
| import { useBuildCategorizeHandlePositions } from './hooks'; | import { useBuildCategorizeHandlePositions } from './hooks'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) { | export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) { | ||||
| const { positions } = useBuildCategorizeHandlePositions({ data, id }); | const { positions } = useBuildCategorizeHandlePositions({ data, id }); | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| type="target" | |||||
| position={Position.Left} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'a'} | |||||
| ></Handle> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| type="target" | |||||
| position={Position.Left} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'a'} | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <Flex vertical gap={8}> | |||||
| <div className={styles.nodeText}> | |||||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||||
| </div> | |||||
| {positions.map((position, idx) => { | |||||
| return ( | |||||
| <div key={idx}> | |||||
| <div className={styles.nodeText}>{position.text}</div> | |||||
| <Handle | |||||
| key={position.text} | |||||
| id={position.text} | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| style={{ ...RightHandleStyle, top: position.top }} | |||||
| ></Handle> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </Flex> | |||||
| </section> | |||||
| </NodePopover> | |||||
| <Flex vertical gap={8}> | |||||
| <div className={styles.nodeText}> | |||||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||||
| </div> | |||||
| {positions.map((position, idx) => { | |||||
| return ( | |||||
| <div key={idx}> | |||||
| <div className={styles.nodeText}>{position.text}</div> | |||||
| <Handle | |||||
| key={position.text} | |||||
| id={position.text} | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| style={{ ...RightHandleStyle, top: position.top }} | |||||
| ></Handle> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </Flex> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| export function GenerateNode({ | export function GenerateNode({ | ||||
| id, | id, | ||||
| const getLabel = useGetComponentLabelByValue(id); | const getLabel = useGetComponentLabelByValue(id); | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <div className={styles.nodeText}> | |||||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||||
| </div> | |||||
| <Flex gap={8} vertical className={styles.generateParameters}> | |||||
| {parameters.map((x) => ( | |||||
| <Flex | |||||
| key={x.id} | |||||
| align="center" | |||||
| gap={6} | |||||
| className={styles.conditionBlock} | |||||
| > | |||||
| <label htmlFor="">{x.key}</label> | |||||
| <span className={styles.parameterValue}> | |||||
| {getLabel(x.component_id)} | |||||
| </span> | |||||
| </Flex> | |||||
| ))} | |||||
| </Flex> | |||||
| </section> | |||||
| </NodePopover> | |||||
| <div className={styles.nodeText}> | |||||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||||
| </div> | |||||
| <Flex gap={8} vertical className={styles.generateParameters}> | |||||
| {parameters.map((x) => ( | |||||
| <Flex | |||||
| key={x.id} | |||||
| align="center" | |||||
| gap={6} | |||||
| className={styles.conditionBlock} | |||||
| > | |||||
| <label htmlFor="">{x.key}</label> | |||||
| <span className={styles.parameterValue}> | |||||
| {getLabel(x.component_id)} | |||||
| </span> | |||||
| </Flex> | |||||
| ))} | |||||
| </Flex> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| export function RagNode({ | export function RagNode({ | ||||
| id, | id, | ||||
| selected, | selected, | ||||
| }: NodeProps<NodeData>) { | }: NodeProps<NodeData>) { | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.ragNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| id="b" | |||||
| style={RightHandleStyle} | |||||
| ></Handle> | |||||
| <NodeHeader id={id} name={data.name} label={data.label}></NodeHeader> | |||||
| </section> | |||||
| </NodePopover> | |||||
| <section | |||||
| className={classNames(styles.ragNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| id="b" | |||||
| style={RightHandleStyle} | |||||
| ></Handle> | |||||
| <NodeHeader id={id} name={data.name} label={data.label}></NodeHeader> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| export function InvokeNode({ | export function InvokeNode({ | ||||
| id, | id, | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const url = get(data, 'form.url'); | const url = get(data, 'form.url'); | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.ragNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| id="b" | |||||
| style={RightHandleStyle} | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <Flex vertical> | |||||
| <div>{t('flow.url')}</div> | |||||
| <div className={styles.nodeText}>{url}</div> | |||||
| </Flex> | |||||
| </section> | |||||
| </NodePopover> | |||||
| <section | |||||
| className={classNames(styles.ragNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| id="b" | |||||
| style={RightHandleStyle} | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <Flex vertical> | |||||
| <div>{t('flow.url')}</div> | |||||
| <div className={styles.nodeText}>{url}</div> | |||||
| </Flex> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| export function KeywordNode({ | export function KeywordNode({ | ||||
| id, | id, | ||||
| selected, | selected, | ||||
| }: NodeProps<NodeData>) { | }: NodeProps<NodeData>) { | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <div className={styles.nodeText}> | |||||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||||
| </div> | |||||
| </section> | |||||
| </NodePopover> | |||||
| <div className={styles.nodeText}> | |||||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||||
| </div> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| export function LogicNode({ | export function LogicNode({ | ||||
| id, | id, | ||||
| selected, | selected, | ||||
| }: NodeProps<NodeData>) { | }: NodeProps<NodeData>) { | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <NodeHeader id={id} name={data.name} label={data.label}></NodeHeader> | |||||
| </section> | |||||
| </NodePopover> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <NodeHeader id={id} name={data.name} label={data.label}></NodeHeader> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| export function MessageNode({ | export function MessageNode({ | ||||
| id, | id, | ||||
| const messages: string[] = get(data, 'form.messages', []); | const messages: string[] = get(data, 'form.messages', []); | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={classNames({ | |||||
| [styles.nodeHeader]: messages.length > 0, | |||||
| })} | })} | ||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={classNames({ | |||||
| [styles.nodeHeader]: messages.length > 0, | |||||
| })} | |||||
| ></NodeHeader> | |||||
| ></NodeHeader> | |||||
| <Flex vertical gap={8} className={styles.messageNodeContainer}> | |||||
| {messages.map((message, idx) => { | |||||
| return ( | |||||
| <div className={styles.nodeText} key={idx}> | |||||
| {message} | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </Flex> | |||||
| </section> | |||||
| </NodePopover> | |||||
| <Flex vertical gap={8} className={styles.messageNodeContainer}> | |||||
| {messages.map((message, idx) => { | |||||
| return ( | |||||
| <div className={styles.nodeText} key={idx}> | |||||
| {message} | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </Flex> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { Flex } from 'antd'; | import { Flex } from 'antd'; | ||||
| import { Operator, operatorMap } from '../../constant'; | import { Operator, operatorMap } from '../../constant'; | ||||
| import OperatorIcon from '../../operator-icon'; | import OperatorIcon from '../../operator-icon'; | ||||
| import NodeDropdown from './dropdown'; | import NodeDropdown from './dropdown'; | ||||
| import { useTranslate } from '@/hooks/common-hooks'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import { NextNodePopover } from './popover'; | |||||
| interface IProps { | interface IProps { | ||||
| id: string; | id: string; | ||||
| label: string; | |||||
| name: string; | |||||
| label?: string; | |||||
| name?: string; | |||||
| gap?: number; | gap?: number; | ||||
| className?: string; | className?: string; | ||||
| } | } | ||||
| export function RunStatus({ id, name }: IProps) { | |||||
| const { t } = useTranslate('flow'); | |||||
| return ( | |||||
| <section className="flex justify-end items-center pb-1 "> | |||||
| <NextNodePopover nodeId={id} name={name}> | |||||
| <span className="text-blue-600 cursor-pointer text-[10px]"> | |||||
| {t('operationResults')} | |||||
| </span> | |||||
| </NextNodePopover> | |||||
| </section> | |||||
| ); | |||||
| } | |||||
| const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => { | const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => { | ||||
| return ( | return ( | ||||
| <Flex | |||||
| flex={1} | |||||
| align="center" | |||||
| justify={'space-between'} | |||||
| gap={gap} | |||||
| className={className} | |||||
| > | |||||
| <OperatorIcon | |||||
| name={label as Operator} | |||||
| color={operatorMap[label as Operator].color} | |||||
| ></OperatorIcon> | |||||
| <span className={styles.nodeTitle}>{name}</span> | |||||
| <NodeDropdown id={id}></NodeDropdown> | |||||
| </Flex> | |||||
| <section className="haha"> | |||||
| {label !== Operator.Answer && <RunStatus id={id} name={name}></RunStatus>} | |||||
| <Flex | |||||
| flex={1} | |||||
| align="center" | |||||
| justify={'space-between'} | |||||
| gap={gap} | |||||
| className={className} | |||||
| > | |||||
| <OperatorIcon | |||||
| name={label as Operator} | |||||
| color={operatorMap[label as Operator].color} | |||||
| ></OperatorIcon> | |||||
| <span className={styles.nodeTitle}>{name}</span> | |||||
| <NodeDropdown id={id}></NodeDropdown> | |||||
| </Flex> | |||||
| </section> | |||||
| ); | ); | ||||
| }; | }; | ||||
| import { useFetchFlow } from '@/hooks/flow-hooks'; | import { useFetchFlow } from '@/hooks/flow-hooks'; | ||||
| import { Popover } from 'antd'; | |||||
| import get from 'lodash/get'; | import get from 'lodash/get'; | ||||
| import React, { useMemo } from 'react'; | |||||
| import React, { MouseEventHandler, useCallback, useMemo } from 'react'; | |||||
| import JsonView from 'react18-json-view'; | import JsonView from 'react18-json-view'; | ||||
| import 'react18-json-view/src/style.css'; | import 'react18-json-view/src/style.css'; | ||||
| import { Operator } from '../../constant'; | |||||
| import { useReplaceIdWithText } from '../../hooks'; | import { useReplaceIdWithText } from '../../hooks'; | ||||
| import styles from './index.less'; | |||||
| import { | |||||
| Popover, | |||||
| PopoverContent, | |||||
| PopoverTrigger, | |||||
| } from '@/components/ui/popover'; | |||||
| import { | |||||
| Table, | |||||
| TableBody, | |||||
| TableCell, | |||||
| TableHead, | |||||
| TableHeader, | |||||
| TableRow, | |||||
| } from '@/components/ui/table'; | |||||
| import { useTranslate } from '@/hooks/common-hooks'; | |||||
| interface IProps extends React.PropsWithChildren { | interface IProps extends React.PropsWithChildren { | ||||
| nodeId: string; | nodeId: string; | ||||
| name?: string; | |||||
| } | } | ||||
| const NodePopover = ({ children, nodeId }: IProps) => { | |||||
| export function NextNodePopover({ children, nodeId, name }: IProps) { | |||||
| const { t } = useTranslate('flow'); | |||||
| const { data } = useFetchFlow(); | const { data } = useFetchFlow(); | ||||
| const component = useMemo(() => { | const component = useMemo(() => { | ||||
| return get(data, ['dsl', 'components', nodeId], {}); | return get(data, ['dsl', 'components', nodeId], {}); | ||||
| }, [nodeId, data]); | }, [nodeId, data]); | ||||
| const inputs: Array<{ component_id: string; content: string }> = get( | |||||
| component, | |||||
| ['obj', 'params', 'inputs'], | |||||
| [], | |||||
| ); | |||||
| const output = get(component, ['obj', 'params', 'output'], {}); | const output = get(component, ['obj', 'params', 'output'], {}); | ||||
| const componentName = get(component, ['obj', 'component_name'], ''); | |||||
| const replacedOutput = useReplaceIdWithText(output); | |||||
| const content = | |||||
| componentName !== Operator.Answer ? ( | |||||
| <div | |||||
| onClick={(e) => { | |||||
| e.preventDefault(); | |||||
| e.stopPropagation(); | |||||
| }} | |||||
| > | |||||
| <JsonView | |||||
| src={replacedOutput} | |||||
| displaySize={30} | |||||
| className={styles.jsonView} | |||||
| /> | |||||
| </div> | |||||
| ) : undefined; | |||||
| const { replacedOutput, getNameById } = useReplaceIdWithText(output); | |||||
| const stopPropagation: MouseEventHandler = useCallback((e) => { | |||||
| e.stopPropagation(); | |||||
| }, []); | |||||
| return ( | return ( | ||||
| <Popover content={content} placement="right" destroyTooltipOnHide> | |||||
| {children} | |||||
| <Popover> | |||||
| <PopoverTrigger onClick={stopPropagation} asChild> | |||||
| {children} | |||||
| </PopoverTrigger> | |||||
| <PopoverContent | |||||
| align={'start'} | |||||
| side={'right'} | |||||
| sideOffset={20} | |||||
| onClick={stopPropagation} | |||||
| className="w-[400px]" | |||||
| > | |||||
| <div className="mb-3 font-semibold text-[16px]"> | |||||
| {name} {t('operationResults')} | |||||
| </div> | |||||
| <div className="flex w-full gap-4 flex-col"> | |||||
| <div className="flex flex-col space-y-1.5"> | |||||
| <span className="font-semibold text-[14px]">{t('input')}</span> | |||||
| <div className="bg-gray-100 p-1 rounded"> | |||||
| <Table> | |||||
| <TableHeader> | |||||
| <TableRow> | |||||
| <TableHead>{t('componentId')}</TableHead> | |||||
| <TableHead className="w-[60px]">{t('content')}</TableHead> | |||||
| </TableRow> | |||||
| </TableHeader> | |||||
| <TableBody> | |||||
| {inputs.map((x, idx) => ( | |||||
| <TableRow key={idx}> | |||||
| <TableCell>{getNameById(x.component_id)}</TableCell> | |||||
| <TableCell className="truncate">{x.content}</TableCell> | |||||
| </TableRow> | |||||
| ))} | |||||
| </TableBody> | |||||
| </Table> | |||||
| </div> | |||||
| </div> | |||||
| <div className="flex flex-col space-y-1.5"> | |||||
| <span className="font-semibold text-[14px]">{t('output')}</span> | |||||
| <div className="bg-gray-100 p-1 rounded"> | |||||
| <JsonView | |||||
| src={replacedOutput} | |||||
| displaySize={30} | |||||
| className="w-full max-h-[300px] break-words overflow-auto" | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </PopoverContent> | |||||
| </Popover> | </Popover> | ||||
| ); | ); | ||||
| }; | |||||
| export default NodePopover; | |||||
| } |
| import { Handle, NodeProps, Position } from 'reactflow'; | import { Handle, NodeProps, Position } from 'reactflow'; | ||||
| import { NodeData } from '../../interface'; | import { NodeData } from '../../interface'; | ||||
| import { RightHandleStyle } from './handle-icon'; | import { RightHandleStyle } from './handle-icon'; | ||||
| import NodePopover from './popover'; | |||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| const yes = get(data, 'form.yes'); | const yes = get(data, 'form.yes'); | ||||
| const no = get(data, 'form.no'); | const no = get(data, 'form.no'); | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| type="target" | |||||
| position={Position.Left} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'a'} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'yes'} | |||||
| style={{ ...RightHandleStyle, top: 59 }} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'no'} | |||||
| style={{ ...RightHandleStyle, top: 112 }} | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| type="target" | |||||
| position={Position.Left} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'a'} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'yes'} | |||||
| style={{ ...RightHandleStyle, top: 59 }} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'no'} | |||||
| style={{ ...RightHandleStyle, top: 112 }} | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <Flex vertical gap={10}> | |||||
| <Flex vertical> | |||||
| <div className={styles.relevantLabel}>Yes</div> | |||||
| <div className={styles.nodeText}>{yes}</div> | |||||
| </Flex> | |||||
| <Flex vertical> | |||||
| <div className={styles.relevantLabel}>No</div> | |||||
| <div className={styles.nodeText}>{no}</div> | |||||
| </Flex> | |||||
| <Flex vertical gap={10}> | |||||
| <Flex vertical> | |||||
| <div className={styles.relevantLabel}>Yes</div> | |||||
| <div className={styles.nodeText}>{yes}</div> | |||||
| </Flex> | </Flex> | ||||
| </section> | |||||
| </NodePopover> | |||||
| <Flex vertical> | |||||
| <div className={styles.relevantLabel}>No</div> | |||||
| <div className={styles.nodeText}>{no}</div> | |||||
| </Flex> | |||||
| </Flex> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| export function RetrievalNode({ | export function RetrievalNode({ | ||||
| id, | id, | ||||
| }, [knowledgeList, knowledgeBaseIds]); | }, [knowledgeList, knowledgeBaseIds]); | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={classNames({ | |||||
| [styles.nodeHeader]: knowledgeBaseIds.length > 0, | |||||
| })} | })} | ||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={classNames({ | |||||
| [styles.nodeHeader]: knowledgeBaseIds.length > 0, | |||||
| })} | |||||
| ></NodeHeader> | |||||
| <Flex vertical gap={8}> | |||||
| {knowledgeBases.map((knowledge) => { | |||||
| return ( | |||||
| <div className={styles.nodeText} key={knowledge.id}> | |||||
| <Flex align={'center'} gap={6}> | |||||
| <Avatar | |||||
| size={26} | |||||
| icon={<UserOutlined />} | |||||
| src={knowledge.avatar} | |||||
| /> | |||||
| <Flex className={styles.knowledgeNodeName} flex={1}> | |||||
| {knowledge.name} | |||||
| </Flex> | |||||
| ></NodeHeader> | |||||
| <Flex vertical gap={8}> | |||||
| {knowledgeBases.map((knowledge) => { | |||||
| return ( | |||||
| <div className={styles.nodeText} key={knowledge.id}> | |||||
| <Flex align={'center'} gap={6}> | |||||
| <Avatar | |||||
| size={26} | |||||
| icon={<UserOutlined />} | |||||
| src={knowledge.avatar} | |||||
| /> | |||||
| <Flex className={styles.knowledgeNodeName} flex={1}> | |||||
| {knowledge.name} | |||||
| </Flex> | </Flex> | ||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </Flex> | |||||
| </section> | |||||
| </NodePopover> | |||||
| </Flex> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </Flex> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| export function RewriteNode({ | export function RewriteNode({ | ||||
| id, | id, | ||||
| selected, | selected, | ||||
| }: NodeProps<NodeData>) { | }: NodeProps<NodeData>) { | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| id="c" | |||||
| type="source" | |||||
| position={Position.Left} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={LeftHandleStyle} | |||||
| ></Handle> | |||||
| <Handle | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable={isConnectable} | |||||
| className={styles.handle} | |||||
| style={RightHandleStyle} | |||||
| id="b" | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <div className={styles.nodeText}> | |||||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||||
| </div> | |||||
| </section> | |||||
| </NodePopover> | |||||
| <div className={styles.nodeText}> | |||||
| <LLMLabel value={get(data, 'form.llm_id')}></LLMLabel> | |||||
| </div> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| import { useBuildSwitchHandlePositions } from './hooks'; | import { useBuildSwitchHandlePositions } from './hooks'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import NodePopover from './popover'; | |||||
| const getConditionKey = (idx: number, length: number) => { | const getConditionKey = (idx: number, length: number) => { | ||||
| if (idx === 0 && length !== 1) { | if (idx === 0 && length !== 1) { | ||||
| const { positions } = useBuildSwitchHandlePositions({ data, id }); | const { positions } = useBuildSwitchHandlePositions({ data, id }); | ||||
| return ( | return ( | ||||
| <NodePopover nodeId={id}> | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| type="target" | |||||
| position={Position.Left} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'a'} | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <Flex vertical gap={10}> | |||||
| {positions.map((position, idx) => { | |||||
| return ( | |||||
| <div key={idx}> | |||||
| <Flex vertical> | |||||
| <Flex justify={'space-between'}> | |||||
| <span>{idx < positions.length - 1 && position.text}</span> | |||||
| <span>{getConditionKey(idx, positions.length)}</span> | |||||
| </Flex> | |||||
| {position.condition && ( | |||||
| <ConditionBlock | |||||
| nodeId={id} | |||||
| condition={position.condition} | |||||
| ></ConditionBlock> | |||||
| )} | |||||
| <section | |||||
| className={classNames(styles.logicNode, { | |||||
| [styles.selectedNode]: selected, | |||||
| })} | |||||
| > | |||||
| <Handle | |||||
| type="target" | |||||
| position={Position.Left} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| id={'a'} | |||||
| ></Handle> | |||||
| <NodeHeader | |||||
| id={id} | |||||
| name={data.name} | |||||
| label={data.label} | |||||
| className={styles.nodeHeader} | |||||
| ></NodeHeader> | |||||
| <Flex vertical gap={10}> | |||||
| {positions.map((position, idx) => { | |||||
| return ( | |||||
| <div key={idx}> | |||||
| <Flex vertical> | |||||
| <Flex justify={'space-between'}> | |||||
| <span>{idx < positions.length - 1 && position.text}</span> | |||||
| <span>{getConditionKey(idx, positions.length)}</span> | |||||
| </Flex> | </Flex> | ||||
| <Handle | |||||
| key={position.text} | |||||
| id={position.text} | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| style={{ ...RightHandleStyle, top: position.top }} | |||||
| ></Handle> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </Flex> | |||||
| </section> | |||||
| </NodePopover> | |||||
| {position.condition && ( | |||||
| <ConditionBlock | |||||
| nodeId={id} | |||||
| condition={position.condition} | |||||
| ></ConditionBlock> | |||||
| )} | |||||
| </Flex> | |||||
| <Handle | |||||
| key={position.text} | |||||
| id={position.text} | |||||
| type="source" | |||||
| position={Position.Right} | |||||
| isConnectable | |||||
| className={styles.handle} | |||||
| style={{ ...RightHandleStyle, top: position.top }} | |||||
| ></Handle> | |||||
| </div> | |||||
| ); | |||||
| })} | |||||
| </Flex> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| return getNode(id)?.data.name; | return getNode(id)?.data.name; | ||||
| }; | }; | ||||
| return replaceIdWithText(output, getNameById); | |||||
| return { | |||||
| replacedOutput: replaceIdWithText(output, getNameById), | |||||
| getNameById, | |||||
| }; | |||||
| }; | }; | ||||
| /** | /** |