feat: enchance prompt and code (#23633)

Co-authored-by: stream <stream@dify.ai>
Co-authored-by: Stream <1542763342@qq.com>
Co-authored-by: Stream <Stream_2@qq.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Joel
2025-08-18 12:29:12 +08:00
committed by GitHub
parent a7fe0e3f87
commit de9c5f10b3
66 changed files with 2654 additions and 275 deletions

View File

@@ -87,6 +87,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
headerClassName='bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase'
containerBackgroundClassName='bg-transparent'
gradientBorder={false}
nodeId={nodeId}
isSupportPromptGenerator={!!def.auto_generate?.type}
titleTooltip={schema.tooltip && renderI18nObject(schema.tooltip)}
editorContainerClassName='px-0'

View File

@@ -7,25 +7,32 @@ import type { CodeLanguage } from '../../code/types'
import { Generator } from '@/app/components/base/icons/src/vender/other'
import { ActionButton } from '@/app/components/base/action-button'
import { AppType } from '@/types/app'
import type { CodeGenRes } from '@/service/debug'
import type { GenRes } from '@/service/debug'
import { GetCodeGeneratorResModal } from '@/app/components/app/configuration/config/code-generator/get-code-generator-res'
import { useHooksStore } from '../../../hooks-store'
type Props = {
nodeId: string
currentCode?: string
className?: string
onGenerated?: (prompt: string) => void
codeLanguages: CodeLanguage
}
const CodeGenerateBtn: FC<Props> = ({
nodeId,
currentCode,
className,
codeLanguages,
onGenerated,
}) => {
const [showAutomatic, { setTrue: showAutomaticTrue, setFalse: showAutomaticFalse }] = useBoolean(false)
const handleAutomaticRes = useCallback((res: CodeGenRes) => {
onGenerated?.(res.code)
const handleAutomaticRes = useCallback((res: GenRes) => {
onGenerated?.(res.modified)
showAutomaticFalse()
}, [onGenerated, showAutomaticFalse])
const configsMap = useHooksStore(s => s.configsMap)
return (
<div className={cn(className)}>
<ActionButton
@@ -40,6 +47,9 @@ const CodeGenerateBtn: FC<Props> = ({
codeLanguages={codeLanguages}
onClose={showAutomaticFalse}
onFinished={handleAutomaticRes}
flowId={configsMap?.flowId || ''}
nodeId={nodeId}
currentCode={currentCode}
/>
)}
</div>

View File

@@ -16,8 +16,10 @@ import useToggleExpend from '@/app/components/workflow/nodes/_base/hooks/use-tog
import type { FileEntity } from '@/app/components/base/file-uploader/types'
import FileListInLog from '@/app/components/base/file-uploader/file-list-in-log'
import ActionButton from '@/app/components/base/action-button'
import type { Node, NodeOutPutVar } from '@/app/components/workflow/types'
type Props = {
nodeId?: string
className?: string
title: React.JSX.Element | string
headerRight?: React.JSX.Element
@@ -35,9 +37,12 @@ type Props = {
showFileList?: boolean
showCodeGenerator?: boolean
tip?: React.JSX.Element
nodesOutputVars?: NodeOutPutVar[]
availableNodes?: Node[]
}
const Base: FC<Props> = ({
nodeId,
className,
title,
headerRight,
@@ -86,7 +91,12 @@ const Base: FC<Props> = ({
{headerRight}
{showCodeGenerator && codeLanguages && (
<div className='ml-1'>
<CodeGeneratorButton onGenerated={onGenerated} codeLanguages={codeLanguages} />
<CodeGeneratorButton
onGenerated={onGenerated}
codeLanguages={codeLanguages}
currentCode={value}
nodeId={nodeId!}
/>
</div>
)}
<ActionButton className='ml-1' onClick={handleCopy}>

View File

@@ -20,6 +20,7 @@ loader.config({ paths: { vs: `${basePath}/vs` } })
const CODE_EDITOR_LINE_HEIGHT = 18
export type Props = {
nodeId?: string
value?: string | object
placeholder?: React.JSX.Element | string
onChange?: (value: string) => void
@@ -47,6 +48,7 @@ export const languageMap = {
}
const CodeEditor: FC<Props> = ({
nodeId,
value = '',
placeholder = '',
onChange = noop,
@@ -175,6 +177,7 @@ const CodeEditor: FC<Props> = ({
</div>
: (
<Base
nodeId={nodeId}
className='relative'
title={title}
value={outPutValue}

View File

@@ -41,6 +41,7 @@ type Props = {
className?: string
headerClassName?: string
instanceId?: string
nodeId?: string
title: string | React.JSX.Element
value: string
onChange: (value: string) => void
@@ -83,6 +84,7 @@ const Editor: FC<Props> = ({
className,
headerClassName,
instanceId,
nodeId,
title,
value,
onChange,
@@ -159,7 +161,13 @@ const Editor: FC<Props> = ({
<div className='flex items-center'>
<div className='text-xs font-medium leading-[18px] text-text-tertiary'>{value?.length || 0}</div>
{isSupportPromptGenerator && (
<PromptGeneratorBtn className='ml-[5px]' onGenerated={onGenerated} modelConfig={modelConfig} />
<PromptGeneratorBtn
nodeId={nodeId!}
className='ml-[5px]'
onGenerated={onGenerated}
modelConfig={modelConfig}
currentPrompt={value}
/>
)}
<div className='ml-2 mr-2 h-3 w-px bg-divider-regular'></div>

View File

@@ -20,7 +20,9 @@ import { varTypeToStructType } from './utils'
import type { Field } from '@/app/components/workflow/nodes/llm/types'
import { FILE_STRUCT } from '@/app/components/workflow/constants'
import { noop } from 'lodash-es'
import { CodeAssistant, MagicEdit } from '@/app/components/base/icons/src/vender/line/general'
import { VariableIconWithColor } from '@/app/components/workflow/nodes/_base/components/variable/variable-label'
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
type ObjectChildrenProps = {
nodeId: string
@@ -44,7 +46,10 @@ type ItemProps = {
isSupportFileVar?: boolean
isException?: boolean
isLoopVar?: boolean
isFlat?: boolean
isInCodeGeneratorInstructionEditor?: boolean
zIndex?: number
className?: string
}
const objVarTypes = [VarType.object, VarType.file]
@@ -59,7 +64,10 @@ const Item: FC<ItemProps> = ({
isSupportFileVar,
isException,
isLoopVar,
isFlat,
isInCodeGeneratorInstructionEditor,
zIndex,
className,
}) => {
const isStructureOutput = itemData.type === VarType.object && (itemData.children as StructuredOutput)?.schema?.properties
const isFile = itemData.type === VarType.file && !isStructureOutput
@@ -67,6 +75,29 @@ const Item: FC<ItemProps> = ({
const isSys = itemData.variable.startsWith('sys.')
const isEnv = itemData.variable.startsWith('env.')
const isChatVar = itemData.variable.startsWith('conversation.')
const flatVarIcon = useMemo(() => {
if (!isFlat)
return null
const variable = itemData.variable
let Icon
switch (variable) {
case 'current':
Icon = isInCodeGeneratorInstructionEditor ? CodeAssistant : MagicEdit
return <Icon className='h-3.5 w-3.5 shrink-0 text-util-colors-violet-violet-600' />
case 'error_message':
return <Variable02 className='h-3.5 w-3.5 shrink-0 text-util-colors-orange-dark-orange-dark-600' />
default:
return <Variable02 className='h-3.5 w-3.5 shrink-0 text-text-accent' />
}
}, [isFlat, isInCodeGeneratorInstructionEditor, itemData.variable])
const varName = useMemo(() => {
if (!isFlat)
return itemData.variable
if (itemData.variable === 'current')
return isInCodeGeneratorInstructionEditor ? 'current_code' : 'current_prompt'
return itemData.variable
}, [isFlat, isInCodeGeneratorInstructionEditor, itemData.variable])
const objStructuredOutput: StructuredOutput | null = useMemo(() => {
if (!isObj) return null
@@ -122,7 +153,10 @@ const Item: FC<ItemProps> = ({
if (!isSupportFileVar && isFile)
return
if (isSys || isEnv || isChatVar) { // system variable | environment variable | conversation variable
if (isFlat) {
onChange([itemData.variable], itemData)
}
else if (isSys || isEnv || isChatVar) { // system variable | environment variable | conversation variable
onChange([...objPath, ...itemData.variable.split('.')], itemData)
}
else {
@@ -147,18 +181,22 @@ const Item: FC<ItemProps> = ({
className={cn(
(isObj || isStructureOutput) ? ' pr-1' : 'pr-[18px]',
isHovering && ((isObj || isStructureOutput) ? 'bg-components-panel-on-panel-item-bg-hover' : 'bg-state-base-hover'),
'relative flex h-6 w-full cursor-pointer items-center rounded-md pl-3')
'relative flex h-6 w-full cursor-pointer items-center rounded-md pl-3',
className,
)
}
onClick={handleChosen}
onMouseDown={e => e.preventDefault()}
>
<div className='flex w-0 grow items-center'>
<VariableIconWithColor
{!isFlat && <VariableIconWithColor
variableCategory={variableCategory}
isExceptionVariable={isException}
/>
/>}
{isFlat && flatVarIcon}
{!isEnv && !isChatVar && (
<div title={itemData.variable} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{itemData.variable}</div>
<div title={itemData.variable} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{varName}</div>
)}
{isEnv && (
<div title={itemData.variable} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{itemData.variable.replace('env.', '')}</div>
@@ -264,6 +302,7 @@ type Props = {
onClose?: () => void
onBlur?: () => void
zIndex?: number
isInCodeGeneratorInstructionEditor?: boolean
autoFocus?: boolean
}
const VarReferenceVars: FC<Props> = ({
@@ -277,6 +316,7 @@ const VarReferenceVars: FC<Props> = ({
onClose,
onBlur,
zIndex,
isInCodeGeneratorInstructionEditor,
autoFocus = true,
}) => {
const { t } = useTranslation()
@@ -319,7 +359,7 @@ const VarReferenceVars: FC<Props> = ({
{
!hideSearch && (
<>
<div className={cn('var-search-input-wrapper mx-2 mb-1 mt-2', searchBoxClassName)} onClick={e => e.stopPropagation()}>
<div className={cn('var-search-input-wrapper mx-2 mb-2 mt-2', searchBoxClassName)} onClick={e => e.stopPropagation()}>
<Input
className='var-search-input'
showLeftIcon
@@ -345,11 +385,13 @@ const VarReferenceVars: FC<Props> = ({
{
filteredVars.map((item, i) => (
<div key={i}>
<div
className='system-xs-medium-uppercase truncate px-3 leading-[22px] text-text-tertiary'
title={item.title}
>{item.title}</div>
<div key={i} className={cn(!item.isFlat && 'mt-3', i === 0 && item.isFlat && 'mt-2')}>
{!item.isFlat && (
<div
className='system-xs-medium-uppercase truncate px-3 leading-[22px] text-text-tertiary'
title={item.title}
>{item.title}</div>
)}
{item.vars.map((v, j) => (
<Item
key={j}
@@ -362,13 +404,22 @@ const VarReferenceVars: FC<Props> = ({
isSupportFileVar={isSupportFileVar}
isException={v.isException}
isLoopVar={item.isLoop}
isFlat={item.isFlat}
isInCodeGeneratorInstructionEditor={isInCodeGeneratorInstructionEditor}
zIndex={zIndex}
/>
))}
{item.isFlat && !filteredVars[i + 1]?.isFlat && !!filteredVars.find(item => !item.isFlat) && (
<div className='relative mt-[14px] flex items-center space-x-1'>
<div className='h-0 w-3 shrink-0 border border-divider-subtle'></div>
<div className='system-2xs-semibold-uppercase text-text-tertiary'>{t('workflow.debug.lastOutput')}</div>
<div className='h-0 shrink-0 grow border border-divider-subtle'></div>
</div>
)}
</div>))
}
</div>
: <div className='pl-3 text-xs font-medium uppercase leading-[18px] text-gray-500'>{t('workflow.common.noVar')}</div>}
: <div className='mt-2 pl-3 text-xs font-medium uppercase leading-[18px] text-gray-500'>{t('workflow.common.noVar')}</div>}
</>
)
}

View File

@@ -1,18 +1,19 @@
import { useEffect, useState } from 'react'
type Params = {
ref: React.RefObject<HTMLDivElement>
ref?: React.RefObject<HTMLDivElement | null>
hasFooter?: boolean
isInNode?: boolean
}
const useToggleExpend = ({ ref, hasFooter = true, isInNode }: Params) => {
const [isExpand, setIsExpand] = useState(false)
const [wrapHeight, setWrapHeight] = useState(ref.current?.clientHeight)
const [wrapHeight, setWrapHeight] = useState(ref?.current?.clientHeight)
const editorExpandHeight = isExpand ? wrapHeight! - (hasFooter ? 56 : 29) : 0
useEffect(() => {
if (!ref?.current) return
setWrapHeight(ref.current?.clientHeight)
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isExpand])
const wrapClassName = (() => {