mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
feat: Enhance sub-graph components with context handling and variable management
This commit is contained in:
@ -67,6 +67,9 @@ const ConfigPanel: FC<ConfigPanelProps> = ({
|
||||
description: t('subGraphModal.whenOutputNone.defaultDesc', { ns: 'workflow' }),
|
||||
},
|
||||
]), [t])
|
||||
const selectedWhenOutputNoneOption = useMemo(() => (
|
||||
whenOutputNoneOptions.find(item => item.value === mentionConfig.null_strategy) ?? whenOutputNoneOptions[0]
|
||||
), [mentionConfig.null_strategy, whenOutputNoneOptions])
|
||||
|
||||
const handleNullStrategyChange = useCallback((item: Item) => {
|
||||
if (typeof item.value !== 'string')
|
||||
@ -94,6 +97,8 @@ const ConfigPanel: FC<ConfigPanelProps> = ({
|
||||
default_value: nextValue,
|
||||
})
|
||||
}, [mentionConfig, onMentionConfigChange])
|
||||
const defaultValue = mentionConfig.default_value ?? ''
|
||||
const shouldFormatDefaultValue = typeof defaultValue !== 'string'
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
@ -131,45 +136,54 @@ const ConfigPanel: FC<ConfigPanelProps> = ({
|
||||
</Field>
|
||||
</div>
|
||||
<div className="space-y-4 px-4 py-4">
|
||||
<Field title={t('subGraphModal.whenOutputIsNone', { ns: 'workflow' })}>
|
||||
<SimpleSelect
|
||||
items={whenOutputNoneOptions}
|
||||
defaultValue={mentionConfig.null_strategy}
|
||||
allowSearch={false}
|
||||
notClearable
|
||||
onSelect={handleNullStrategyChange}
|
||||
renderOption={({ item, selected }) => (
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center">
|
||||
{selected && (
|
||||
<RiCheckLine className="h-4 w-4 text-[14px] text-text-accent" />
|
||||
)}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="system-sm-medium text-text-secondary">{item.name}</div>
|
||||
<div className="system-xs-regular mt-0.5 text-text-tertiary">{item.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</Field>
|
||||
{mentionConfig.null_strategy === 'use_default' && (
|
||||
<div>
|
||||
<div className="system-xs-regular text-text-tertiary">
|
||||
{t('subGraphModal.defaultValueHint', { ns: 'workflow' })}
|
||||
</div>
|
||||
<div className={cn('mt-2 overflow-hidden rounded-lg border border-components-input-border-active bg-components-input-bg-normal p-1')}>
|
||||
<CodeEditor
|
||||
noWrapper
|
||||
language={CodeLanguage.json}
|
||||
value={mentionConfig.default_value ?? ''}
|
||||
onChange={handleDefaultValueChange}
|
||||
isJSONStringifyBeauty
|
||||
className="min-h-[160px]"
|
||||
<Field
|
||||
title={t('subGraphModal.whenOutputIsNone', { ns: 'workflow' })}
|
||||
operations={(
|
||||
<div className="flex items-center">
|
||||
<SimpleSelect
|
||||
items={whenOutputNoneOptions}
|
||||
defaultValue={mentionConfig.null_strategy}
|
||||
allowSearch={false}
|
||||
notClearable
|
||||
wrapperClassName="min-w-[160px]"
|
||||
onSelect={handleNullStrategyChange}
|
||||
renderOption={({ item, selected }) => (
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center">
|
||||
{selected && (
|
||||
<RiCheckLine className="h-4 w-4 text-[14px] text-text-accent" />
|
||||
)}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="system-sm-medium text-text-secondary">{item.name}</div>
|
||||
<div className="system-xs-regular mt-0.5 text-text-tertiary">{item.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
{selectedWhenOutputNoneOption?.description && (
|
||||
<div className="system-xs-regular text-text-tertiary">
|
||||
{selectedWhenOutputNoneOption.description}
|
||||
</div>
|
||||
)}
|
||||
{mentionConfig.null_strategy === 'use_default' && (
|
||||
<div className={cn('overflow-hidden rounded-lg border border-components-input-border-active bg-components-input-bg-normal p-1')}>
|
||||
<CodeEditor
|
||||
noWrapper
|
||||
language={CodeLanguage.json}
|
||||
value={defaultValue}
|
||||
onChange={handleDefaultValueChange}
|
||||
isJSONStringifyBeauty={shouldFormatDefaultValue}
|
||||
className="min-h-[160px]"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -2,12 +2,13 @@ import type { FC } from 'react'
|
||||
import type { Viewport } from 'reactflow'
|
||||
import type { SubGraphProps } from './types'
|
||||
import type { InjectWorkflowStoreSliceFn } from '@/app/components/workflow/store'
|
||||
import type { PromptItem } from '@/app/components/workflow/types'
|
||||
import { memo, useMemo } from 'react'
|
||||
import type { PromptItem, PromptTemplateItem } from '@/app/components/workflow/types'
|
||||
import { memo, useEffect, useMemo } from 'react'
|
||||
import WorkflowWithDefaultContext from '@/app/components/workflow'
|
||||
import { NODE_WIDTH_X_OFFSET, START_INITIAL_POSITION } from '@/app/components/workflow/constants'
|
||||
import { WorkflowContextProvider } from '@/app/components/workflow/context'
|
||||
import { BlockEnum, EditionType, PromptRole } from '@/app/components/workflow/types'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import { BlockEnum, EditionType, isPromptMessageContext, PromptRole } from '@/app/components/workflow/types'
|
||||
import SubGraphMain from './components/sub-graph-main'
|
||||
import { useSubGraphNodes } from './hooks'
|
||||
import { createSubGraphSlice } from './store'
|
||||
@ -38,9 +39,19 @@ const SubGraphContent: FC<SubGraphProps> = (props) => {
|
||||
onMentionConfigChange,
|
||||
extractorNode,
|
||||
toolParamValue,
|
||||
parentAvailableNodes,
|
||||
parentAvailableVars,
|
||||
onSave,
|
||||
} = props
|
||||
|
||||
const setParentAvailableVars = useStore(state => state.setParentAvailableVars)
|
||||
const setParentAvailableNodes = useStore(state => state.setParentAvailableNodes)
|
||||
|
||||
useEffect(() => {
|
||||
setParentAvailableVars?.(parentAvailableVars || [])
|
||||
setParentAvailableNodes?.(parentAvailableNodes || [])
|
||||
}, [parentAvailableNodes, parentAvailableVars, setParentAvailableNodes, setParentAvailableVars])
|
||||
|
||||
const promptText = useMemo(() => {
|
||||
if (!toolParamValue)
|
||||
return ''
|
||||
@ -95,16 +106,18 @@ const SubGraphContent: FC<SubGraphProps> = (props) => {
|
||||
if (!Array.isArray(template))
|
||||
return applyPromptText(template as PromptItem)
|
||||
|
||||
const userIndex = template.findIndex(item => item.role === PromptRole.user)
|
||||
const promptItems = template.filter((item): item is PromptItem => !isPromptMessageContext(item))
|
||||
|
||||
const userIndex = promptItems.findIndex(item => item.role === PromptRole.user)
|
||||
if (userIndex >= 0) {
|
||||
return template.map((item, index) => {
|
||||
return promptItems.map((item, index) => {
|
||||
if (index !== userIndex)
|
||||
return item
|
||||
return applyPromptText(item)
|
||||
})
|
||||
}) as PromptTemplateItem[]
|
||||
}
|
||||
|
||||
const useJinja = template.some((item: PromptItem) => item.edition_type === EditionType.jinja2)
|
||||
const useJinja = promptItems.some((item: PromptItem) => item.edition_type === EditionType.jinja2)
|
||||
const defaultUserPrompt: PromptItem = useJinja
|
||||
? {
|
||||
role: PromptRole.user,
|
||||
@ -113,13 +126,13 @@ const SubGraphContent: FC<SubGraphProps> = (props) => {
|
||||
edition_type: EditionType.jinja2,
|
||||
}
|
||||
: { role: PromptRole.user, text: promptText }
|
||||
const systemIndex = template.findIndex(item => item.role === PromptRole.system)
|
||||
const nextTemplate = [...template]
|
||||
const systemIndex = promptItems.findIndex(item => item.role === PromptRole.system)
|
||||
const nextTemplate = [...promptItems]
|
||||
if (systemIndex >= 0)
|
||||
nextTemplate.splice(systemIndex + 1, 0, defaultUserPrompt)
|
||||
else
|
||||
nextTemplate.unshift(defaultUserPrompt)
|
||||
return nextTemplate
|
||||
return nextTemplate as PromptTemplateItem[]
|
||||
})()
|
||||
|
||||
return {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { CreateSubGraphSlice, SubGraphSliceShape } from '../types'
|
||||
|
||||
const initialState: Omit<SubGraphSliceShape, 'setSubGraphContext' | 'setSubGraphNodes' | 'setSubGraphEdges' | 'setSelectedOutputVar' | 'setWhenOutputNone' | 'setDefaultValue' | 'setShowDebugPanel' | 'setIsRunning' | 'setParentAvailableVars' | 'resetSubGraph'> = {
|
||||
const initialState: Omit<SubGraphSliceShape, 'setSubGraphContext' | 'setSubGraphNodes' | 'setSubGraphEdges' | 'setSelectedOutputVar' | 'setWhenOutputNone' | 'setDefaultValue' | 'setShowDebugPanel' | 'setIsRunning' | 'setParentAvailableVars' | 'setParentAvailableNodes' | 'resetSubGraph'> = {
|
||||
parentToolNodeId: '',
|
||||
parameterKey: '',
|
||||
sourceAgentNodeId: '',
|
||||
@ -18,6 +18,7 @@ const initialState: Omit<SubGraphSliceShape, 'setSubGraphContext' | 'setSubGraph
|
||||
isRunning: false,
|
||||
|
||||
parentAvailableVars: [],
|
||||
parentAvailableNodes: [],
|
||||
}
|
||||
|
||||
export const createSubGraphSlice: CreateSubGraphSlice = set => ({
|
||||
@ -46,5 +47,7 @@ export const createSubGraphSlice: CreateSubGraphSlice = set => ({
|
||||
|
||||
setParentAvailableVars: vars => set(() => ({ parentAvailableVars: vars })),
|
||||
|
||||
setParentAvailableNodes: nodes => set(() => ({ parentAvailableNodes: nodes })),
|
||||
|
||||
resetSubGraph: () => set(() => ({ ...initialState })),
|
||||
})
|
||||
|
||||
@ -31,6 +31,8 @@ export type SubGraphProps = {
|
||||
onMentionConfigChange: (config: MentionConfig) => void
|
||||
extractorNode?: Node<LLMNodeType>
|
||||
toolParamValue?: string
|
||||
parentAvailableNodes?: Node[]
|
||||
parentAvailableVars?: NodeOutPutVar[]
|
||||
onSave?: (nodes: Node[], edges: Edge[]) => void
|
||||
}
|
||||
|
||||
@ -52,6 +54,7 @@ export type SubGraphSliceShape = {
|
||||
isRunning: boolean
|
||||
|
||||
parentAvailableVars: NodeOutPutVar[]
|
||||
parentAvailableNodes: Node[]
|
||||
|
||||
setSubGraphContext: (context: {
|
||||
parentToolNodeId: string
|
||||
@ -67,6 +70,7 @@ export type SubGraphSliceShape = {
|
||||
setShowDebugPanel: (show: boolean) => void
|
||||
setIsRunning: (running: boolean) => void
|
||||
setParentAvailableVars: (vars: NodeOutPutVar[]) => void
|
||||
setParentAvailableNodes: (nodes: Node[]) => void
|
||||
resetSubGraph: () => void
|
||||
}
|
||||
|
||||
|
||||
@ -84,6 +84,7 @@ type Props = {
|
||||
currentTool?: Tool
|
||||
currentProvider?: ToolWithProvider | TriggerWithProvider
|
||||
preferSchemaType?: boolean
|
||||
hideSearch?: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_VALUE_SELECTOR: Props['value'] = []
|
||||
@ -117,6 +118,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
currentTool,
|
||||
currentProvider,
|
||||
preferSchemaType,
|
||||
hideSearch,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const store = useStoreApi()
|
||||
@ -636,6 +638,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
isSupportFileVar={isSupportFileVar}
|
||||
zIndex={zIndex}
|
||||
preferSchemaType={preferSchemaType}
|
||||
hideSearch={hideSearch}
|
||||
/>
|
||||
)}
|
||||
</PortalToFollowElemContent>
|
||||
|
||||
@ -15,6 +15,7 @@ type Props = {
|
||||
onChange: (value: ValueSelector, varDetail: Var) => void
|
||||
itemWidth?: number
|
||||
isSupportFileVar?: boolean
|
||||
hideSearch?: boolean
|
||||
zIndex?: number
|
||||
preferSchemaType?: boolean
|
||||
}
|
||||
@ -24,6 +25,7 @@ const VarReferencePopup: FC<Props> = ({
|
||||
onChange,
|
||||
itemWidth,
|
||||
isSupportFileVar = true,
|
||||
hideSearch,
|
||||
zIndex,
|
||||
preferSchemaType,
|
||||
}) => {
|
||||
@ -35,7 +37,7 @@ const VarReferencePopup: FC<Props> = ({
|
||||
// max-h-[300px] overflow-y-auto todo: use portal to handle long list
|
||||
return (
|
||||
<div
|
||||
className="space-y-1 rounded-lg border border-components-panel-border bg-components-panel-bg p-1 shadow-lg"
|
||||
className="space-y-1 rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"
|
||||
style={{
|
||||
width: itemWidth || 228,
|
||||
}}
|
||||
@ -84,6 +86,7 @@ const VarReferencePopup: FC<Props> = ({
|
||||
showManageInputField={showManageRagInputFields}
|
||||
onManageInputField={() => setShowInputFieldPanel?.(true)}
|
||||
preferSchemaType={preferSchemaType}
|
||||
hideSearch={hideSearch}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,129 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { PromptMessageContext, ValueSelector } from '../../../types'
|
||||
import type { Node, NodeOutPutVar, Var } from '@/app/components/workflow/types'
|
||||
import { RiArrowDownSLine, RiDeleteBinLine } from '@remixicon/react'
|
||||
import { memo, useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
|
||||
import VariableLabelInSelect from '@/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-select'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
readOnly: boolean
|
||||
payload: PromptMessageContext
|
||||
contextVars: NodeOutPutVar[]
|
||||
availableNodes: Node[]
|
||||
onChange: (value: ValueSelector) => void
|
||||
onRemove: () => void
|
||||
}
|
||||
|
||||
const ConfigContextItem: FC<Props> = ({
|
||||
readOnly,
|
||||
payload,
|
||||
contextVars,
|
||||
availableNodes,
|
||||
onChange,
|
||||
onRemove,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const selectedNodeId = Array.isArray(payload.$context) ? payload.$context[0] : ''
|
||||
const selectedNode = useMemo(() => {
|
||||
return availableNodes.find(node => node.id === selectedNodeId)
|
||||
}, [availableNodes, selectedNodeId])
|
||||
const hasOptions = contextVars.length > 0
|
||||
|
||||
const handleChange = useCallback((value: ValueSelector, _item?: Var) => {
|
||||
onChange(value)
|
||||
setOpen(false)
|
||||
}, [onChange])
|
||||
|
||||
const handleToggle = useCallback(() => {
|
||||
if (readOnly)
|
||||
return
|
||||
setOpen(prev => !prev)
|
||||
}, [readOnly])
|
||||
|
||||
const handleRemove = useCallback(() => {
|
||||
onRemove()
|
||||
setOpen(false)
|
||||
}, [onRemove])
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement="bottom-start"
|
||||
offset={6}
|
||||
>
|
||||
<PortalToFollowElemTrigger asChild onClick={handleToggle}>
|
||||
<button
|
||||
type="button"
|
||||
disabled={readOnly}
|
||||
className={cn(
|
||||
'flex w-full items-center justify-between rounded-xl bg-components-panel-on-panel-item-bg px-3 py-2',
|
||||
!readOnly && 'cursor-pointer hover:bg-components-panel-on-panel-item-bg-hover',
|
||||
)}
|
||||
>
|
||||
<div className="system-xs-semibold-uppercase text-text-tertiary">
|
||||
{t('nodes.llm.context', { ns: 'workflow' })}
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<VariableLabelInSelect
|
||||
nodeType={selectedNode?.data.type || BlockEnum.Agent}
|
||||
nodeTitle={selectedNode?.data.title}
|
||||
variables={payload.$context}
|
||||
/>
|
||||
<RiArrowDownSLine className="h-4 w-4 text-text-tertiary" />
|
||||
</div>
|
||||
</button>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className="z-[1000]">
|
||||
<div className="w-[260px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg">
|
||||
{hasOptions
|
||||
? (
|
||||
<VarReferenceVars
|
||||
vars={contextVars}
|
||||
onChange={handleChange}
|
||||
hideSearch
|
||||
maxHeightClass="max-h-[34vh]"
|
||||
onClose={() => setOpen(false)}
|
||||
onBlur={() => setOpen(false)}
|
||||
autoFocus={false}
|
||||
preferSchemaType
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<div className="system-xs-regular px-3 py-2 text-center text-text-tertiary">
|
||||
{t('common.noAgentNodes', { ns: 'workflow' })}
|
||||
</div>
|
||||
)}
|
||||
{!readOnly && (
|
||||
<div className="mt-1 border-t border-divider-subtle pt-1">
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center gap-2 rounded-md px-3 py-2 text-text-secondary hover:bg-state-base-hover"
|
||||
onClick={handleRemove}
|
||||
>
|
||||
<RiDeleteBinLine className="h-4 w-4" />
|
||||
<span className="system-sm-regular">
|
||||
{t('nodes.llm.removeContext', { ns: 'workflow' })}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(ConfigContextItem)
|
||||
@ -1,19 +1,28 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { ModelConfig, PromptItem, ValueSelector, Var, Variable } from '../../../types'
|
||||
import type { ModelConfig, Node, NodeOutPutVar, PromptItem, PromptMessageContext, PromptTemplateItem, ValueSelector, Var, Variable } from '../../../types'
|
||||
import { produce } from 'immer'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ReactSortable } from 'react-sortablejs'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { v4 as uuid4 } from 'uuid'
|
||||
import { DragHandle } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import AddButton from '@/app/components/workflow/nodes/_base/components/add-button'
|
||||
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
|
||||
import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useWorkflowStore } from '../../../store'
|
||||
import { EditionType, PromptRole } from '../../../types'
|
||||
import { useWorkflow } from '../../../hooks'
|
||||
import { useStore, useWorkflowStore } from '../../../store'
|
||||
import { BlockEnum, EditionType, isPromptMessageContext, PromptRole, VarType } from '../../../types'
|
||||
import useAvailableVarList from '../../_base/hooks/use-available-var-list'
|
||||
import ConfigContextItem from './config-context-item'
|
||||
import ConfigPromptItem from './config-prompt-item'
|
||||
|
||||
const i18nPrefix = 'nodes.llm'
|
||||
@ -24,8 +33,8 @@ type Props = {
|
||||
filterVar: (payload: Var, selector: ValueSelector) => boolean
|
||||
isChatModel: boolean
|
||||
isChatApp: boolean
|
||||
payload: PromptItem | PromptItem[]
|
||||
onChange: (payload: PromptItem | PromptItem[]) => void
|
||||
payload: PromptItem | PromptTemplateItem[]
|
||||
onChange: (payload: PromptItem | PromptTemplateItem[]) => void
|
||||
isShowContext: boolean
|
||||
hasSetBlockStatus: {
|
||||
context: boolean
|
||||
@ -56,6 +65,13 @@ const ConfigPrompt: FC<Props> = ({
|
||||
const {
|
||||
setControlPromptEditorRerenderKey,
|
||||
} = workflowStore.getState()
|
||||
|
||||
const store = useStoreApi()
|
||||
const { getBeforeNodesInSameBranch } = useWorkflow()
|
||||
|
||||
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false)
|
||||
const contextMenuTriggerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const payloadWithIds = (isChatModel && Array.isArray(payload))
|
||||
? payload.map((item) => {
|
||||
const id = uuid4()
|
||||
@ -75,11 +91,78 @@ const ConfigPrompt: FC<Props> = ({
|
||||
onlyLeafNodeVar: false,
|
||||
filterVar,
|
||||
})
|
||||
const parentAvailableVars = useStore(state => state.parentAvailableVars) || []
|
||||
const parentAvailableNodes = useStore(state => state.parentAvailableNodes) || []
|
||||
|
||||
const mergedAvailableVars = useMemo(() => {
|
||||
if (!parentAvailableVars.length)
|
||||
return availableVars
|
||||
const merged = new Map<string, NodeOutPutVar>()
|
||||
availableVars.forEach((item) => {
|
||||
merged.set(item.nodeId, item)
|
||||
})
|
||||
parentAvailableVars.forEach((item) => {
|
||||
if (!merged.has(item.nodeId))
|
||||
merged.set(item.nodeId, item)
|
||||
})
|
||||
return Array.from(merged.values())
|
||||
}, [availableVars, parentAvailableVars])
|
||||
|
||||
const mergedAvailableNodesWithParent = useMemo(() => {
|
||||
if (!parentAvailableNodes.length)
|
||||
return availableNodesWithParent
|
||||
const merged = new Map<string, Node>()
|
||||
availableNodesWithParent.forEach((node) => {
|
||||
merged.set(node.id, node)
|
||||
})
|
||||
parentAvailableNodes.forEach((node) => {
|
||||
if (!merged.has(node.id))
|
||||
merged.set(node.id, node)
|
||||
})
|
||||
return Array.from(merged.values())
|
||||
}, [availableNodesWithParent, parentAvailableNodes])
|
||||
|
||||
const contextAgentNodes = useMemo(() => {
|
||||
const agentNodes = mergedAvailableNodesWithParent
|
||||
.filter(node => node.data.type === BlockEnum.Agent)
|
||||
|
||||
const { getNodes } = store.getState()
|
||||
const allNodes = getNodes()
|
||||
const currentNode = allNodes.find(n => n.id === nodeId)
|
||||
const parentNodeId = currentNode?.parentId
|
||||
|
||||
if (parentNodeId) {
|
||||
const beforeNodes = getBeforeNodesInSameBranch(parentNodeId)
|
||||
const parentAgentNodes = beforeNodes
|
||||
.filter(node => node.data.type === BlockEnum.Agent)
|
||||
.filter(node => !agentNodes.some(n => n.id === node.id))
|
||||
|
||||
agentNodes.unshift(...parentAgentNodes)
|
||||
}
|
||||
|
||||
return agentNodes
|
||||
}, [mergedAvailableNodesWithParent, nodeId, store, getBeforeNodesInSameBranch])
|
||||
|
||||
const contextVarOptions = useMemo<NodeOutPutVar[]>(() => {
|
||||
return contextAgentNodes.map(node => ({
|
||||
nodeId: node.id,
|
||||
title: node.data.title,
|
||||
vars: [
|
||||
{
|
||||
variable: 'context',
|
||||
type: VarType.arrayObject,
|
||||
schemaType: 'List[promptMessage]',
|
||||
},
|
||||
],
|
||||
}))
|
||||
}, [contextAgentNodes])
|
||||
|
||||
const handleChatModePromptChange = useCallback((index: number) => {
|
||||
return (prompt: string) => {
|
||||
const newPrompt = produce(payload as PromptItem[], (draft) => {
|
||||
draft[index][draft[index].edition_type === EditionType.jinja2 ? 'jinja2_text' : 'text'] = prompt
|
||||
const newPrompt = produce(payload as PromptTemplateItem[], (draft) => {
|
||||
const item = draft[index]
|
||||
if (!isPromptMessageContext(item))
|
||||
item[item.edition_type === EditionType.jinja2 ? 'jinja2_text' : 'text'] = prompt
|
||||
})
|
||||
onChange(newPrompt)
|
||||
}
|
||||
@ -87,8 +170,10 @@ const ConfigPrompt: FC<Props> = ({
|
||||
|
||||
const handleChatModeEditionTypeChange = useCallback((index: number) => {
|
||||
return (editionType: EditionType) => {
|
||||
const newPrompt = produce(payload as PromptItem[], (draft) => {
|
||||
draft[index].edition_type = editionType
|
||||
const newPrompt = produce(payload as PromptTemplateItem[], (draft) => {
|
||||
const item = draft[index]
|
||||
if (!isPromptMessageContext(item))
|
||||
item.edition_type = editionType
|
||||
})
|
||||
onChange(newPrompt)
|
||||
}
|
||||
@ -96,29 +181,80 @@ const ConfigPrompt: FC<Props> = ({
|
||||
|
||||
const handleChatModeMessageRoleChange = useCallback((index: number) => {
|
||||
return (role: PromptRole) => {
|
||||
const newPrompt = produce(payload as PromptItem[], (draft) => {
|
||||
draft[index].role = role
|
||||
const newPrompt = produce(payload as PromptTemplateItem[], (draft) => {
|
||||
const item = draft[index]
|
||||
if (!isPromptMessageContext(item))
|
||||
item.role = role
|
||||
})
|
||||
onChange(newPrompt)
|
||||
}
|
||||
}, [onChange, payload])
|
||||
|
||||
const handleAddPrompt = useCallback(() => {
|
||||
const newPrompt = produce(payload as PromptItem[], (draft) => {
|
||||
const newPrompt = produce(payload as PromptTemplateItem[], (draft) => {
|
||||
if (draft.length === 0) {
|
||||
draft.push({ role: PromptRole.system, text: '', id: uuid4() })
|
||||
|
||||
return
|
||||
}
|
||||
const isLastItemUser = draft[draft.length - 1].role === PromptRole.user
|
||||
const lastPromptItem = [...draft].reverse().find(item => !isPromptMessageContext(item)) as PromptItem | undefined
|
||||
const isLastItemUser = lastPromptItem?.role === PromptRole.user
|
||||
draft.push({ role: isLastItemUser ? PromptRole.assistant : PromptRole.user, text: '', id: uuid4() })
|
||||
})
|
||||
onChange(newPrompt)
|
||||
}, [onChange, payload])
|
||||
|
||||
const handleAddContext = useCallback((agentNodeId: string) => {
|
||||
const newPrompt = produce(payload as PromptTemplateItem[], (draft) => {
|
||||
const contextItem: PromptMessageContext = {
|
||||
id: uuid4(),
|
||||
$context: [agentNodeId, 'context'],
|
||||
}
|
||||
|
||||
const lastUserIndex = draft
|
||||
.map((item, idx) => ({ item, idx }))
|
||||
.reverse()
|
||||
.find(({ item }) => !isPromptMessageContext(item) && (item as PromptItem).role === PromptRole.user)
|
||||
?.idx
|
||||
|
||||
if (lastUserIndex !== undefined) {
|
||||
draft.splice(lastUserIndex, 0, contextItem)
|
||||
return
|
||||
}
|
||||
|
||||
const promptItems = draft.filter(item => !isPromptMessageContext(item)) as PromptItem[]
|
||||
const hasOnlySystem = promptItems.length === 1 && promptItems[0].role === PromptRole.system
|
||||
if (hasOnlySystem) {
|
||||
draft.push({ role: PromptRole.user, text: '', id: uuid4() })
|
||||
draft.splice(draft.length - 1, 0, contextItem)
|
||||
return
|
||||
}
|
||||
|
||||
draft.push(contextItem)
|
||||
})
|
||||
onChange(newPrompt)
|
||||
setIsContextMenuOpen(false)
|
||||
}, [onChange, payload])
|
||||
|
||||
const handleAddContextVar = useCallback((value: ValueSelector, _item?: Var) => {
|
||||
if (!Array.isArray(value) || value.length < 2)
|
||||
return
|
||||
handleAddContext(value[0])
|
||||
}, [handleAddContext])
|
||||
|
||||
const handleContextChange = useCallback((index: number) => {
|
||||
return (value: ValueSelector) => {
|
||||
const newPrompt = produce(payload as PromptTemplateItem[], (draft) => {
|
||||
const item = draft[index]
|
||||
if (isPromptMessageContext(item))
|
||||
item.$context = value
|
||||
})
|
||||
onChange(newPrompt)
|
||||
}
|
||||
}, [onChange, payload])
|
||||
|
||||
const handleRemove = useCallback((index: number) => {
|
||||
return () => {
|
||||
const newPrompt = produce(payload as PromptItem[], (draft) => {
|
||||
const newPrompt = produce(payload as PromptTemplateItem[], (draft) => {
|
||||
draft.splice(index, 1)
|
||||
})
|
||||
onChange(newPrompt)
|
||||
@ -145,11 +281,12 @@ const ConfigPrompt: FC<Props> = ({
|
||||
}, [onChange, payload])
|
||||
|
||||
const canChooseSystemRole = (() => {
|
||||
if (isChatModel && Array.isArray(payload))
|
||||
return !payload.find(item => item.role === PromptRole.system)
|
||||
|
||||
if (isChatModel && Array.isArray(payload)) {
|
||||
return !payload.find(item => !isPromptMessageContext(item) && (item as PromptItem).role === PromptRole.system)
|
||||
}
|
||||
return false
|
||||
})()
|
||||
|
||||
return (
|
||||
<div>
|
||||
{(isChatModel && Array.isArray(payload))
|
||||
@ -160,9 +297,12 @@ const ConfigPrompt: FC<Props> = ({
|
||||
className="space-y-1"
|
||||
list={payloadWithIds}
|
||||
setList={(list) => {
|
||||
if ((payload as PromptItem[])?.[0]?.role === PromptRole.system && list[0].p?.role !== PromptRole.system)
|
||||
return
|
||||
|
||||
const firstItem = (payload as PromptTemplateItem[])?.[0]
|
||||
if (firstItem && !isPromptMessageContext(firstItem) && firstItem.role === PromptRole.system) {
|
||||
const newFirstItem = list[0]?.p
|
||||
if (newFirstItem && !isPromptMessageContext(newFirstItem) && newFirstItem.role !== PromptRole.system)
|
||||
return
|
||||
}
|
||||
onChange(list.map(item => item.p))
|
||||
}}
|
||||
handle=".handle"
|
||||
@ -170,7 +310,23 @@ const ConfigPrompt: FC<Props> = ({
|
||||
animation={150}
|
||||
>
|
||||
{
|
||||
(payload as PromptItem[]).map((item, index) => {
|
||||
(payload as PromptTemplateItem[]).map((item, index) => {
|
||||
if (isPromptMessageContext(item)) {
|
||||
return (
|
||||
<div key={item.id || index} className="group relative">
|
||||
{!readOnly && <DragHandle className="handle absolute left-[-14px] top-2 hidden h-3.5 w-3.5 cursor-grab text-text-quaternary group-hover:block" />}
|
||||
<ConfigContextItem
|
||||
readOnly={readOnly}
|
||||
payload={item}
|
||||
contextVars={contextVarOptions}
|
||||
availableNodes={mergedAvailableNodesWithParent}
|
||||
onChange={handleContextChange(index)}
|
||||
onRemove={handleRemove(index)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const canDrag = (() => {
|
||||
if (readOnly)
|
||||
return false
|
||||
@ -182,7 +338,7 @@ const ConfigPrompt: FC<Props> = ({
|
||||
})()
|
||||
return (
|
||||
<div key={item.id || index} className="group relative">
|
||||
{canDrag && <DragHandle className="absolute left-[-14px] top-2 hidden h-3.5 w-3.5 text-text-quaternary group-hover:block" />}
|
||||
{canDrag && <DragHandle className="handle absolute left-[-14px] top-2 hidden h-3.5 w-3.5 cursor-grab text-text-quaternary group-hover:block" />}
|
||||
<ConfigPromptItem
|
||||
instanceId={item.role === PromptRole.system ? `${nodeId}-chat-workflow-llm-prompt-editor` : `${nodeId}-chat-workflow-llm-prompt-editor-${index}`}
|
||||
className={cn(canDrag && 'handle')}
|
||||
@ -201,8 +357,8 @@ const ConfigPrompt: FC<Props> = ({
|
||||
onRemove={handleRemove(index)}
|
||||
isShowContext={isShowContext}
|
||||
hasSetBlockStatus={hasSetBlockStatus}
|
||||
availableVars={availableVars}
|
||||
availableNodes={availableNodesWithParent}
|
||||
availableVars={mergedAvailableVars}
|
||||
availableNodes={mergedAvailableNodesWithParent}
|
||||
varList={varList}
|
||||
handleAddVariable={handleAddVariable}
|
||||
modelConfig={modelConfig}
|
||||
@ -213,11 +369,48 @@ const ConfigPrompt: FC<Props> = ({
|
||||
}
|
||||
</ReactSortable>
|
||||
</div>
|
||||
<AddButton
|
||||
className="mt-2"
|
||||
text={t(`${i18nPrefix}.addMessage`, { ns: 'workflow' })}
|
||||
onClick={handleAddPrompt}
|
||||
/>
|
||||
<div className="mt-2 grid grid-cols-[11fr_9fr] gap-2">
|
||||
<AddButton
|
||||
text={t(`${i18nPrefix}.addMessage`, { ns: 'workflow' })}
|
||||
onClick={handleAddPrompt}
|
||||
/>
|
||||
<PortalToFollowElem
|
||||
open={isContextMenuOpen}
|
||||
onOpenChange={setIsContextMenuOpen}
|
||||
placement="bottom-start"
|
||||
>
|
||||
<PortalToFollowElemTrigger className="w-full" onClick={() => setIsContextMenuOpen(!isContextMenuOpen)}>
|
||||
<div ref={contextMenuTriggerRef}>
|
||||
<AddButton
|
||||
text={t(`${i18nPrefix}.addContext`, { ns: 'workflow' })}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className="z-[1000]">
|
||||
<div className="w-[260px] rounded-lg border border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg">
|
||||
{contextVarOptions.length > 0
|
||||
? (
|
||||
<VarReferenceVars
|
||||
vars={contextVarOptions}
|
||||
onChange={handleAddContextVar}
|
||||
hideSearch
|
||||
maxHeightClass="max-h-[34vh]"
|
||||
onClose={() => setIsContextMenuOpen(false)}
|
||||
onBlur={() => setIsContextMenuOpen(false)}
|
||||
autoFocus={false}
|
||||
preferSchemaType
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<div className="system-xs-regular px-3 py-2 text-center text-text-tertiary">
|
||||
{t('common.noAgentNodes', { ns: 'workflow' })}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
@ -232,8 +425,8 @@ const ConfigPrompt: FC<Props> = ({
|
||||
isChatApp={isChatApp}
|
||||
isShowContext={isShowContext}
|
||||
hasSetBlockStatus={hasSetBlockStatus}
|
||||
nodesOutputVars={availableVars}
|
||||
availableNodes={availableNodesWithParent}
|
||||
nodesOutputVars={mergedAvailableVars}
|
||||
availableNodes={mergedAvailableNodesWithParent}
|
||||
isSupportPromptGenerator
|
||||
isSupportJinja
|
||||
editionType={(payload as PromptItem).edition_type}
|
||||
|
||||
@ -5,6 +5,7 @@ import { RiAlertFill, RiQuestionLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import AddButton2 from '@/app/components/base/button/add-button'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
@ -119,7 +120,12 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
|
||||
{/* knowledge */}
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.context`, { ns: 'workflow' })}
|
||||
title={(
|
||||
<div className="flex items-center">
|
||||
<div>{t(`${i18nPrefix}.context`, { ns: 'workflow' })}</div>
|
||||
<Badge className="ml-2" uppercase>LEGACY</Badge>
|
||||
</div>
|
||||
)}
|
||||
tooltip={t(`${i18nPrefix}.contextTooltip`, { ns: 'workflow' })!}
|
||||
>
|
||||
<>
|
||||
@ -130,6 +136,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
value={inputs.context?.variable_selector || []}
|
||||
onChange={handleContextVarChange}
|
||||
filterVar={filterVar}
|
||||
hideSearch
|
||||
/>
|
||||
{shouldShowContextTip && (
|
||||
<div className="text-xs font-normal leading-[18px] text-[#DC6803]">{t(`${i18nPrefix}.notSetContextInPromptTip`, { ns: 'workflow' })}</div>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type { CommonNodeType, Memory, ModelConfig, PromptItem, ValueSelector, Variable, VisionSetting } from '@/app/components/workflow/types'
|
||||
import type { CommonNodeType, Memory, ModelConfig, PromptItem, PromptTemplateItem, ValueSelector, Variable, VisionSetting } from '@/app/components/workflow/types'
|
||||
|
||||
export type LLMNodeType = CommonNodeType & {
|
||||
model: ModelConfig
|
||||
prompt_template: PromptItem[] | PromptItem
|
||||
prompt_template: PromptTemplateItem[] | PromptItem
|
||||
prompt_config?: {
|
||||
jinja2_variables?: Variable[]
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Memory, PromptItem, ValueSelector, Var, Variable } from '../../types'
|
||||
import type { Memory, PromptItem, PromptTemplateItem, ValueSelector, Var, Variable } from '../../types'
|
||||
import type { LLMNodeType, StructuredOutput } from './types'
|
||||
import { produce } from 'immer'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
@ -249,7 +249,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
setInputs(newInputs)
|
||||
}, [setInputs])
|
||||
|
||||
const handlePromptChange = useCallback((newPrompt: PromptItem[] | PromptItem) => {
|
||||
const handlePromptChange = useCallback((newPrompt: PromptTemplateItem[] | PromptItem) => {
|
||||
const newInputs = produce(inputRef.current, (draft) => {
|
||||
draft.prompt_template = newPrompt
|
||||
})
|
||||
|
||||
@ -10,12 +10,12 @@ import { RiCloseLine } from '@remixicon/react'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { Fragment, memo, useCallback, useEffect, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { useStore as useReactFlowStore, useStoreApi } from 'reactflow'
|
||||
import { Agent } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import { useNodesSyncDraft } from '@/app/components/workflow/hooks'
|
||||
import { useIsChatMode, useNodesSyncDraft, useWorkflow, useWorkflowVariables } from '@/app/components/workflow/hooks'
|
||||
import { VarKindType } from '@/app/components/workflow/nodes/_base/types'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import { EditionType, PromptRole } from '@/app/components/workflow/types'
|
||||
import { useStore as useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { BlockEnum, EditionType, PromptRole } from '@/app/components/workflow/types'
|
||||
import SubGraphCanvas from './sub-graph-canvas'
|
||||
|
||||
const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
@ -29,9 +29,13 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const reactflowStore = useStoreApi()
|
||||
const workflowNodes = useStore(state => state.nodes)
|
||||
const setControlPromptEditorRerenderKey = useStore(state => state.setControlPromptEditorRerenderKey)
|
||||
const workflowNodes = useWorkflowStore(state => state.nodes)
|
||||
const workflowEdges = useReactFlowStore(state => state.edges)
|
||||
const setControlPromptEditorRerenderKey = useWorkflowStore(state => state.setControlPromptEditorRerenderKey)
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const { getBeforeNodesInSameBranch } = useWorkflow()
|
||||
const { getNodeAvailableVars } = useWorkflowVariables()
|
||||
const isChatMode = useIsChatMode()
|
||||
|
||||
const extractorNodeId = `${toolNodeId}_ext_${paramKey}`
|
||||
const extractorNode = useMemo(() => {
|
||||
@ -43,6 +47,28 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
const toolParam = (toolNode?.data as ToolNodeType | undefined)?.tool_parameters?.[paramKey]
|
||||
const toolParamValue = toolParam?.value as string | undefined
|
||||
|
||||
const parentAgentNodes = useMemo(() => {
|
||||
if (!isOpen)
|
||||
return []
|
||||
const beforeNodes = getBeforeNodesInSameBranch(toolNodeId, workflowNodes, workflowEdges)
|
||||
return beforeNodes.filter(node => node.data.type === BlockEnum.Agent)
|
||||
}, [getBeforeNodesInSameBranch, isOpen, toolNodeId, workflowEdges, workflowNodes])
|
||||
|
||||
const parentAgentNodeIds = useMemo(() => {
|
||||
return parentAgentNodes.map(node => node.id)
|
||||
}, [parentAgentNodes])
|
||||
|
||||
const parentAvailableVars = useMemo(() => {
|
||||
if (!parentAgentNodeIds.length)
|
||||
return []
|
||||
const vars = getNodeAvailableVars({
|
||||
beforeNodes: parentAgentNodes,
|
||||
isChatMode,
|
||||
filterVar: () => true,
|
||||
})
|
||||
return vars.filter(nodeVar => parentAgentNodeIds.includes(nodeVar.nodeId))
|
||||
}, [getNodeAvailableVars, isChatMode, parentAgentNodeIds, parentAgentNodes])
|
||||
|
||||
const mentionConfig = useMemo<MentionConfig>(() => {
|
||||
const current = toolParam?.mention_config
|
||||
const rawSelector = Array.isArray(current?.output_selector) ? current!.output_selector : []
|
||||
@ -115,8 +141,7 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
const userPrompt = promptTemplate.find(item => item.role === PromptRole.user)
|
||||
if (userPrompt)
|
||||
return resolveText(userPrompt)
|
||||
const systemPrompt = promptTemplate.find(item => item.role === PromptRole.system)
|
||||
return resolveText(systemPrompt)
|
||||
return ''
|
||||
}
|
||||
return resolveText(promptTemplate)
|
||||
}, [])
|
||||
@ -212,6 +237,8 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
onMentionConfigChange={handleMentionConfigChange}
|
||||
extractorNode={extractorNode}
|
||||
toolParamValue={toolParamValue}
|
||||
parentAvailableNodes={parentAgentNodes}
|
||||
parentAvailableVars={parentAvailableVars}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -14,6 +14,8 @@ const SubGraphCanvas: FC<SubGraphCanvasProps> = ({
|
||||
onMentionConfigChange,
|
||||
extractorNode,
|
||||
toolParamValue,
|
||||
parentAvailableNodes,
|
||||
parentAvailableVars,
|
||||
onSave,
|
||||
}) => {
|
||||
return (
|
||||
@ -28,6 +30,8 @@ const SubGraphCanvas: FC<SubGraphCanvasProps> = ({
|
||||
onMentionConfigChange={onMentionConfigChange}
|
||||
extractorNode={extractorNode}
|
||||
toolParamValue={toolParamValue}
|
||||
parentAvailableNodes={parentAvailableNodes}
|
||||
parentAvailableVars={parentAvailableVars}
|
||||
onSave={onSave}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/types'
|
||||
import type { LLMNodeType } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type { Edge as WorkflowEdge, Node as WorkflowNode } from '@/app/components/workflow/types'
|
||||
import type { NodeOutPutVar, Edge as WorkflowEdge, Node as WorkflowNode } from '@/app/components/workflow/types'
|
||||
|
||||
type WorkflowValueSelector = string[]
|
||||
|
||||
@ -24,5 +24,7 @@ export type SubGraphCanvasProps = {
|
||||
onMentionConfigChange: (config: MentionConfig) => void
|
||||
extractorNode?: WorkflowNode<LLMNodeType>
|
||||
toolParamValue?: string
|
||||
parentAvailableNodes?: WorkflowNode[]
|
||||
parentAvailableVars?: NodeOutPutVar[]
|
||||
onSave?: (nodes: WorkflowNode[], edges: WorkflowEdge[]) => void
|
||||
}
|
||||
|
||||
@ -255,6 +255,17 @@ export type PromptItem = {
|
||||
jinja2_text?: string
|
||||
}
|
||||
|
||||
export type PromptMessageContext = {
|
||||
id?: string
|
||||
$context: ValueSelector
|
||||
}
|
||||
|
||||
export type PromptTemplateItem = PromptItem | PromptMessageContext
|
||||
|
||||
export const isPromptMessageContext = (item: PromptTemplateItem): item is PromptMessageContext => {
|
||||
return '$context' in item
|
||||
}
|
||||
|
||||
export enum MemoryRole {
|
||||
user = 'user',
|
||||
assistant = 'assistant',
|
||||
|
||||
Reference in New Issue
Block a user