pull/21369/head
jZonG 1 year ago
parent 9afcdfa8d4
commit b288258e4d

@ -0,0 +1,128 @@
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
RiArrowRightSLine,
// RiErrorWarningFill,
// RiLoader2Line,
} from '@remixicon/react'
import { useStore } from '../store'
// import { BlockEnum } from '../types'
// import Button from '@/app/components/base/button'
// import ActionButton from '@/app/components/base/action-button'
// import Tooltip from '@/app/components/base/tooltip'
// import BlockIcon from '@/app/components/workflow/block-icon'
import {
// BubbleX,
Env,
} from '@/app/components/base/icons/src/vender/line/others'
// import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
// import useCurrentVars from '../hooks/use-inspect-vars-crud'
import type { currentVarType } from './panel'
import cn from '@/utils/classnames'
type Props = {
isEnv?: boolean
isChatVar?: boolean
isSystem?: boolean
currentVar?: currentVarType
handleSelect: (state: any) => void
}
const Group = ({
isEnv,
isChatVar,
isSystem,
currentVar,
handleSelect,
}: Props) => {
const { t } = useTranslation()
const environmentVariables = useStore(s => s.environmentVariables)
// const {
// conversationVars,
// systemVars,
// nodesWithInspectVars,
// } = useCurrentVars()
const [isCollapsed, setIsCollapsed] = useState(false)
const handleSelectVar = (varItem: any, type?: string) => {
if (type === 'env') {
handleSelect({
nodeId: 'env',
nodeTitle: 'env',
nodeType: 'env',
var: {
...varItem,
type: 'env',
...(varItem.value_type === 'secret' ? { value: '******************' } : {}),
},
})
return
}
if (type === 'chat') {
handleSelect({
nodeId: 'conversation',
nodeTitle: 'conversation',
nodeType: 'conversation',
var: {
...varItem,
type: 'conversation',
},
})
return
}
if (type === 'system') {
handleSelect({
nodeId: 'sys',
nodeTitle: 'sys',
nodeType: 'sys',
var: varItem,
})
return
}
handleSelect({
nodeId: varItem.nodeId,
nodeTitle: varItem.nodeTitle,
nodeType: varItem.nodeType,
var: varItem.var,
})
}
return (
<div className='p-0.5'>
{/* node item */}
<div className='flex h-6 items-center gap-0.5'>
<RiArrowRightSLine className={cn('h-3 w-3 text-text-tertiary', !isCollapsed && 'rotate-90')} />
<div className='flex grow cursor-pointer items-center gap-1' onClick={() => setIsCollapsed(!isCollapsed)}>
<div className='system-xs-medium-uppercase truncate text-text-tertiary'>
{isEnv && t('workflow.debug.variableInspect.envNode')}
{isChatVar && t('workflow.debug.variableInspect.chatNode')}
{isSystem && t('workflow.debug.variableInspect.systemNode')}
</div>
</div>
</div>
{/* var item list */}
{!isCollapsed && (
<div className='px-0.5'>
{environmentVariables.length > 0 && environmentVariables.map(env => (
<div
key={env.id}
className={cn(
'relative flex cursor-pointer items-center gap-1 rounded-md px-3 py-1 hover:bg-state-base-hover',
env.id === currentVar?.var.id && 'bg-state-base-hover-alt hover:bg-state-base-hover-alt',
)}
onClick={() => handleSelectVar(env, 'env')}
>
<Env className='h-4 w-4 shrink-0 text-util-colors-violet-violet-600' />
<div className='system-sm-medium grow truncate text-text-secondary'>{env.name}</div>
<div className='system-xs-regular shrink-0 text-text-tertiary'>{env.value_type}</div>
</div>
))}
</div>
)}
</div>
)
}
export default Group

@ -5,27 +5,35 @@ import {
RiErrorWarningFill, RiErrorWarningFill,
RiLoader2Line, RiLoader2Line,
} from '@remixicon/react' } from '@remixicon/react'
// import { useStore } from '../store' import { useStore } from '../store'
import { BlockEnum } from '../types' import { BlockEnum } from '../types'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
// import ActionButton from '@/app/components/base/action-button' // import ActionButton from '@/app/components/base/action-button'
// import Tooltip from '@/app/components/base/tooltip' // import Tooltip from '@/app/components/base/tooltip'
import BlockIcon from '@/app/components/workflow/block-icon' import BlockIcon from '@/app/components/workflow/block-icon'
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' import { BubbleX } from '@/app/components/base/icons/src/vender/line/others'
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
import Group from './group'
import useCurrentVars from '../hooks/use-inspect-vars-crud' import useCurrentVars from '../hooks/use-inspect-vars-crud'
import type { currentVarType } from './panel'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
type Props = { type Props = {
handleMenuClick: (state: any) => void currentNodeVar?: currentVarType
handleVarSelect: (state: any) => void
} }
const Left = ({ handleMenuClick }: Props) => { const Left = ({
currentNodeVar,
handleVarSelect,
}: Props) => {
const { t } = useTranslation() const { t } = useTranslation()
// const bottomPanelWidth = useStore(s => s.bottomPanelWidth) const environmentVariables = useStore(s => s.environmentVariables)
// const setShowVariableInspectPanel = useStore(s => s.setShowVariableInspectPanel)
const { const {
conversationVars,
systemVars,
nodesWithInspectVars,
deleteAllInspectorVars, deleteAllInspectorVars,
} = useCurrentVars() } = useCurrentVars()
@ -42,29 +50,13 @@ const Left = ({ handleMenuClick }: Props) => {
{/* content */} {/* content */}
<div className='grow overflow-y-auto py-1'> <div className='grow overflow-y-auto py-1'>
{/* group ENV */} {/* group ENV */}
<div className='p-0.5'> {environmentVariables.length > 0 && (
{/* node item */} <Group
<div className='flex h-6 items-center gap-0.5'> isEnv
<RiArrowRightSLine className='h-3 w-3 rotate-90 text-text-tertiary' /> currentVar={currentNodeVar}
<div className='flex grow cursor-pointer items-center gap-1'> handleSelect={handleVarSelect}
<div className='system-xs-medium-uppercase truncate text-text-tertiary'>{t('workflow.env.envPanelTitle')}</div> />
</div> )}
</div>
{/* var item list */}
<div className='px-0.5'>
<div className={cn('relative flex cursor-pointer items-center gap-1 rounded-md px-3 py-1 hover:bg-state-base-hover')} onClick={handleMenuClick}>
<Env className='h-4 w-4 shrink-0 text-util-colors-violet-violet-600' />
<div className='system-sm-medium grow truncate text-text-secondary'>SECRET_KEY</div>
<div className='system-xs-regular shrink-0 text-text-tertiary'>string</div>
</div>
<div className={cn('relative flex cursor-pointer items-center gap-1 rounded-md px-3 py-1 hover:bg-state-base-hover', selectedNode && 'bg-state-base-hover-alt hover:bg-state-base-hover-alt')}>
{selectedNode && <span className='absolute left-1.5 top-[10.5px] h-[3px] w-[3px] rounded-full bg-text-accent-secondary'></span>}
<Env className='h-4 w-4 shrink-0 text-util-colors-violet-violet-600' />
<div className='system-sm-medium grow truncate text-text-secondary'>PORT</div>
<div className='system-xs-regular shrink-0 text-text-tertiary'>number</div>
</div>
</div>
</div>
{/* group CHAT VAR */} {/* group CHAT VAR */}
<div className='p-0.5'> <div className='p-0.5'>
{/* node item */} {/* node item */}

@ -10,14 +10,23 @@ import Empty from './empty'
import Left from './left' import Left from './left'
import Right from './right' import Right from './right'
import ActionButton from '@/app/components/base/action-button' import ActionButton from '@/app/components/base/action-button'
import type { VarInInspect } from '@/types/workflow'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
export type currentVarType = {
nodeId: string
nodeTitle: string
nodeType: string
var: VarInInspect
}
const Panel: FC = () => { const Panel: FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const bottomPanelWidth = useStore(s => s.bottomPanelWidth) const bottomPanelWidth = useStore(s => s.bottomPanelWidth)
const setShowVariableInspectPanel = useStore(s => s.setShowVariableInspectPanel) const setShowVariableInspectPanel = useStore(s => s.setShowVariableInspectPanel)
const [showLeftPanel, setShowLeftPanel] = useState(true) const [showLeftPanel, setShowLeftPanel] = useState(true)
const [currentNodeVar, setCurrentNodeVar] = useState<currentVarType>()
const environmentVariables = useStore(s => s.environmentVariables) const environmentVariables = useStore(s => s.environmentVariables)
const { const {
@ -60,11 +69,17 @@ const Panel: FC = () => {
: 'block', : 'block',
)} )}
> >
<Left handleMenuClick={setShowLeftPanel} /> <Left
currentNodeVar={currentNodeVar}
handleVarSelect={setCurrentNodeVar}
/>
</div> </div>
{/* right */} {/* right */}
<div className='w-0 grow'> <div className='w-0 grow'>
<Right handleOpenMenu={() => setShowLeftPanel(true)} /> <Right
currentNodeVar={currentNodeVar}
handleOpenMenu={() => setShowLeftPanel(true)}
/>
</div> </div>
</div> </div>
) )

@ -6,7 +6,7 @@ import {
RiMenuLine, RiMenuLine,
} from '@remixicon/react' } from '@remixicon/react'
import { useStore } from '../store' import { useStore } from '../store'
import { BlockEnum } from '../types' import type { BlockEnum } from '../types'
import useCurrentVars from '../hooks/use-inspect-vars-crud' import useCurrentVars from '../hooks/use-inspect-vars-crud'
import Empty from './empty' import Empty from './empty'
import ValueContent from './value-content' import ValueContent from './value-content'
@ -16,43 +16,29 @@ import CopyFeedback from '@/app/components/base/copy-feedback'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import BlockIcon from '@/app/components/workflow/block-icon' import BlockIcon from '@/app/components/workflow/block-icon'
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
import type { currentVarType } from './panel'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
export const currentVar = {
id: 'var-jfkldjjfkldaf-dfhekdfj',
type: 'node',
// type: 'conversation',
// type: 'environment',
name: 'out_put',
var_type: 'string',
// var_type: 'number',
// var_type: 'object',
// var_type: 'array[string]',
// var_type: 'array[number]',
// var_type: 'array[object]',
// var_type: 'file',
// var_type: 'array[file]',
value: 'tuituitui',
edited: true,
}
type Props = { type Props = {
currentNodeVar?: currentVarType
handleOpenMenu: () => void handleOpenMenu: () => void
} }
const Right = ({ handleOpenMenu }: Props) => { const Right = ({
currentNodeVar,
handleOpenMenu,
}: Props) => {
const { t } = useTranslation() const { t } = useTranslation()
const bottomPanelWidth = useStore(s => s.bottomPanelWidth) const bottomPanelWidth = useStore(s => s.bottomPanelWidth)
const setShowVariableInspectPanel = useStore(s => s.setShowVariableInspectPanel) const setShowVariableInspectPanel = useStore(s => s.setShowVariableInspectPanel)
const current = currentVar
const { const {
resetToLastRunVar, resetToLastRunVar,
} = useCurrentVars() } = useCurrentVars()
const resetValue = () => { const resetValue = () => {
resetToLastRunVar('node_id', current.name) if (!currentNodeVar) return
resetToLastRunVar(currentNodeVar.nodeId, currentNodeVar.var.name)
} }
return ( return (
@ -65,48 +51,48 @@ const Right = ({ handleOpenMenu }: Props) => {
</ActionButton> </ActionButton>
)} )}
<div className='flex w-0 grow items-center gap-1'> <div className='flex w-0 grow items-center gap-1'>
{current && ( {currentNodeVar && (
<> <>
{current.type === 'environment' && ( {currentNodeVar.nodeType === 'env' && (
<Env className='h-4 w-4 shrink-0 text-util-colors-violet-violet-600' /> <Env className='h-4 w-4 shrink-0 text-util-colors-violet-violet-600' />
)} )}
{current.type === 'conversation' && ( {currentNodeVar.nodeType === 'conversation' && (
<BubbleX className='h-4 w-4 shrink-0 text-util-colors-teal-teal-700' /> <BubbleX className='h-4 w-4 shrink-0 text-util-colors-teal-teal-700' />
)} )}
{current.type === 'node' && ( {currentNodeVar.nodeType !== 'env' && currentNodeVar.nodeType !== 'conversation' && currentNodeVar.nodeType !== 'sys' && (
<> <>
<BlockIcon <BlockIcon
className='shrink-0' className='shrink-0'
type={BlockEnum.LLM} type={currentNodeVar.nodeType as BlockEnum}
size='xs' size='xs'
/> />
<div className='system-sm-regular shrink-0 text-text-secondary'>LLM</div> <div className='system-sm-regular shrink-0 text-text-secondary'>{currentNodeVar.nodeTitle}</div>
<div className='system-sm-regular shrink-0 text-text-quaternary'>/</div> <div className='system-sm-regular shrink-0 text-text-quaternary'>/</div>
</> </>
)} )}
<div title={current.name} className='system-sm-semibold truncate text-text-secondary'>{current.name}</div> <div title={currentNodeVar.var.name} className='system-sm-semibold truncate text-text-secondary'>{currentNodeVar.var.name}</div>
<div className='system-xs-medium ml-1 shrink-0 text-text-tertiary'>{current.var_type}</div> <div className='system-xs-medium ml-1 shrink-0 text-text-tertiary'>{currentNodeVar.var.value_type}</div>
</> </>
)} )}
</div> </div>
<div className='flex shrink-0 items-center gap-1'> <div className='flex shrink-0 items-center gap-1'>
{current && ( {currentNodeVar && (
<> <>
{current.edited && ( {currentNodeVar.var.edited && (
<Badge> <Badge>
<span className='ml-[2.5px] mr-[4.5px] h-[3px] w-[3px] rounded bg-text-accent-secondary'></span> <span className='ml-[2.5px] mr-[4.5px] h-[3px] w-[3px] rounded bg-text-accent-secondary'></span>
<span className='system-2xs-semibold-uupercase'>{t('workflow.debug.variableInspect.edited')}</span> <span className='system-2xs-semibold-uupercase'>{t('workflow.debug.variableInspect.edited')}</span>
</Badge> </Badge>
)} )}
{current.edited && ( {currentNodeVar.var.edited && (
<Tooltip popupContent={t('workflow.debug.variableInspect.reset')}> <Tooltip popupContent={t('workflow.debug.variableInspect.reset')}>
<ActionButton onClick={resetValue}> <ActionButton onClick={resetValue}>
<RiArrowGoBackLine className='h-4 w-4' /> <RiArrowGoBackLine className='h-4 w-4' />
</ActionButton> </ActionButton>
</Tooltip> </Tooltip>
)} )}
{(current.type !== 'environment' || current.var_type !== 'secret') && ( {currentNodeVar.var.value_type !== 'secret' && (
<CopyFeedback content={current.value ? JSON.stringify(current.value) : ''} /> <CopyFeedback content={currentNodeVar.var.value ? JSON.stringify(currentNodeVar.var.value) : ''} />
)} )}
</> </>
)} )}
@ -117,8 +103,8 @@ const Right = ({ handleOpenMenu }: Props) => {
</div> </div>
{/* content */} {/* content */}
<div className='grow p-2'> <div className='grow p-2'>
{!current && <Empty />} {!currentNodeVar && <Empty />}
{current && <ValueContent />} {currentNodeVar && <ValueContent currentVar={currentNodeVar.var} />}
</div> </div>
</div> </div>
) )

@ -19,22 +19,24 @@ import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
import { TransferMethod } from '@/types/app' import { TransferMethod } from '@/types/app'
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
import { SupportUploadFileTypes } from '@/app/components/workflow/types' import { SupportUploadFileTypes } from '@/app/components/workflow/types'
import type { VarInInspect } from '@/types/workflow'
import { VarInInspectType } from '@/types/workflow'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
export const currentVar = { export const MOCK_DATA = {
id: 'var-jfkldjjfkldaf-dfhekdfj', id: 'var-jfkldjjfkldaf-dfhekdfj',
type: 'node', type: 'node',
// type: 'conversation', // type: 'conversation',
// type: 'environment', // type: 'environment',
name: 'out_put', name: 'out_put',
// var_type: 'string', // value_type: 'string',
// var_type: 'number', // value_type: 'number',
// var_type: 'object', // value_type: 'object',
// var_type: 'array[string]', // value_type: 'array[string]',
// var_type: 'array[number]', // value_type: 'array[number]',
// var_type: 'array[object]', // value_type: 'array[object]',
// var_type: 'file', // value_type: 'file',
var_type: 'array[file]', value_type: 'array[file]',
// value: 'tuituitui', // value: 'tuituitui',
// value: ['aaa', 'bbb', 'ccc'], // value: ['aaa', 'bbb', 'ccc'],
// value: { // value: {
@ -46,34 +48,62 @@ export const currentVar = {
edited: true, edited: true,
} }
const ValueContent = () => { type Props = {
const current = currentVar currentVar: VarInInspect
}
const ValueContent = ({
// currentVar = MOCK_DATA as any, // TODO remove this line
currentVar,
}: Props) => {
const contentContainerRef = useRef<HTMLDivElement>(null) const contentContainerRef = useRef<HTMLDivElement>(null)
const errorMessageRef = useRef<HTMLDivElement>(null) const errorMessageRef = useRef<HTMLDivElement>(null)
const [editorHeight, setEditorHeight] = useState(0) const [editorHeight, setEditorHeight] = useState(0)
const showTextEditor = current.var_type === 'secret' || current.var_type === 'string' || current.var_type === 'number' const showTextEditor = currentVar.value_type === 'secret' || currentVar.value_type === 'string' || currentVar.value_type === 'number'
const showJSONEditor = current.var_type === 'object' || current.var_type === 'array[string]' || current.var_type === 'array[number]' || current.var_type === 'array[object]' const showJSONEditor = currentVar.value_type === 'object' || currentVar.value_type === 'array[string]' || currentVar.value_type === 'array[number]' || currentVar.value_type === 'array[object]'
const showFileEditor = current.var_type === 'file' || current.var_type === 'array[file]' const showFileEditor = currentVar.value_type === 'file' || currentVar.value_type === 'array[file]'
const [value, setValue] = useState<any>(current.value ? JSON.stringify(current.value) : '') const [value, setValue] = useState<any>()
const [jsonSchema, setJsonSchema] = useState(current.value || null) const [jsonSchema, setJsonSchema] = useState()
const [json, setJson] = useState(JSON.stringify(jsonSchema, null, 2)) const [json, setJson] = useState('')
const [parseError, setParseError] = useState<Error | null>(null) const [parseError, setParseError] = useState<Error | null>(null)
const [validationError, setValidationError] = useState<string>('') const [validationError, setValidationError] = useState<string>('')
const fileFeature = useFeatures(s => s.features.file) const fileFeature = useFeatures(s => s.features.file)
const [fileValue, setFileValue] = useState<any>( const [fileValue, setFileValue] = useState<any>(
current.var_type === 'array[file]' currentVar.value_type === 'array[file]'
? current.value || [] ? currentVar.value || []
: current.value : currentVar.value
? [current.value] ? [currentVar.value]
: [], : [],
) )
// update default value when id changed
useEffect(() => {
if (showTextEditor) {
if (!currentVar.value)
return setValue('')
if (currentVar.value_type === 'number')
return setValue(JSON.stringify(currentVar.value))
setValue(currentVar.value)
}
if (showJSONEditor) {
setJsonSchema(currentVar.value || null)
setJson(currentVar.value ? JSON.stringify(currentVar.value, null, 2) : '')
}
if (showFileEditor) {
setFileValue(currentVar.value_type === 'array[file]'
? currentVar.value || []
: currentVar.value
? [currentVar.value]
: [])
}
}, [currentVar, showTextEditor, showJSONEditor, showFileEditor])
const handleTextChange = (value: string) => { const handleTextChange = (value: string) => {
if (current.var_type === 'string') if (currentVar.value_type === 'string')
setValue(value) setValue(value)
if (current.var_type === 'number') { if (currentVar.value_type === 'number') {
if (/^-?\d+(\.)?(\d+)?$/.test(value)) if (/^-?\d+(\.)?(\d+)?$/.test(value))
setValue(value) setValue(value)
} }
@ -119,7 +149,7 @@ const ValueContent = () => {
const handleEditorChange = (value: string) => { const handleEditorChange = (value: string) => {
setJson(value) setJson(value)
if (jsonValueValidate(value, current.var_type)) { if (jsonValueValidate(value, currentVar.value_type)) {
const parsed = JSON.parse(value) const parsed = JSON.parse(value)
setJsonSchema(parsed) setJsonSchema(parsed)
// TODO call api of value update // TODO call api of value update
@ -131,10 +161,10 @@ const ValueContent = () => {
setFileValue(value) setFileValue(value)
// TODO check every file upload progress // TODO check every file upload progress
// invoke update api after every file uploaded // invoke update api after every file uploaded
if (current.var_type === 'file') { if (currentVar.value_type === 'file') {
// TODO call api of value update // TODO call api of value update
} }
if (current.var_type === 'array[file]') { if (currentVar.value_type === 'array[file]') {
// TODO call api of value update // TODO call api of value update
} }
} }
@ -164,8 +194,8 @@ const ValueContent = () => {
<div className={cn('grow')} style={{ height: `${editorHeight}px` }}> <div className={cn('grow')} style={{ height: `${editorHeight}px` }}>
{showTextEditor && ( {showTextEditor && (
<Textarea <Textarea
readOnly={current.type === 'environment'} readOnly={currentVar.type === VarInInspectType.environment}
disabled={current.type === 'environment'} disabled={currentVar.type === VarInInspectType.environment}
className='h-full' className='h-full'
value={value as any} value={value as any}
onChange={debounce(e => handleTextChange(e.target.value))} onChange={debounce(e => handleTextChange(e.target.value))}
@ -198,7 +228,7 @@ const ValueContent = () => {
...FILE_EXTS[SupportUploadFileTypes.video], ...FILE_EXTS[SupportUploadFileTypes.video],
], ],
allowed_file_upload_methods: [TransferMethod.local_file, TransferMethod.remote_url], allowed_file_upload_methods: [TransferMethod.local_file, TransferMethod.remote_url],
number_limits: current.var_type === 'file' ? 1 : (fileFeature as any).fileUploadConfig?.workflow_file_upload_limit || 5, number_limits: currentVar.value_type === 'file' ? 1 : (fileFeature as any).fileUploadConfig?.workflow_file_upload_limit || 5,
fileUploadConfig: (fileFeature as any).fileUploadConfig, fileUploadConfig: (fileFeature as any).fileUploadConfig,
}} }}
/> />

@ -930,6 +930,9 @@ const translation = {
cached: 'View cached variables', cached: 'View cached variables',
clear: 'Clear', clear: 'Clear',
}, },
envNode: 'Environment',
chatNode: 'Conversation',
systemNode: 'System',
}, },
}, },
} }

@ -931,6 +931,9 @@ const translation = {
cached: '查看缓存', cached: '查看缓存',
clear: '清除', clear: '清除',
}, },
envNode: '环境变量',
chatNode: '会话变量',
systemNode: '系统变量',
}, },
}, },
} }

@ -374,7 +374,7 @@ export type NodeRunResult = NodeTracing
// Var Inspect // Var Inspect
export enum VarInInspectType { export enum VarInInspectType {
conversation = 'conversation', conversation = 'conversation',
environment = 'environment', environment = 'env',
node = 'node', node = 'node',
system = 'sys', system = 'sys',
} }

Loading…
Cancel
Save