mirror of https://github.com/langgenius/dify.git
feat: llm node support tools
This commit is contained in:
parent
0cff94d90e
commit
d60348572e
|
|
@ -6,17 +6,20 @@ export type BoxProps = {
|
||||||
className?: string
|
className?: string
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
withBorderBottom?: boolean
|
withBorderBottom?: boolean
|
||||||
|
withBorderTop?: boolean
|
||||||
}
|
}
|
||||||
export const Box = memo(({
|
export const Box = memo(({
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
withBorderBottom,
|
withBorderBottom,
|
||||||
|
withBorderTop,
|
||||||
}: BoxProps) => {
|
}: BoxProps) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'py-2',
|
'py-2',
|
||||||
withBorderBottom && 'border-b border-divider-subtle',
|
withBorderBottom && 'border-b border-divider-subtle',
|
||||||
|
withBorderTop && 'border-t border-divider-subtle',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { cn } from '@/utils/classnames'
|
||||||
|
|
||||||
export type FieldTitleProps = {
|
export type FieldTitleProps = {
|
||||||
title?: string
|
title?: string
|
||||||
|
className?: string
|
||||||
operation?: ReactNode
|
operation?: ReactNode
|
||||||
subTitle?: string | ReactNode
|
subTitle?: string | ReactNode
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
|
|
@ -19,6 +20,7 @@ export type FieldTitleProps = {
|
||||||
}
|
}
|
||||||
export const FieldTitle = memo(({
|
export const FieldTitle = memo(({
|
||||||
title,
|
title,
|
||||||
|
className,
|
||||||
operation,
|
operation,
|
||||||
subTitle,
|
subTitle,
|
||||||
tooltip,
|
tooltip,
|
||||||
|
|
@ -31,7 +33,7 @@ export const FieldTitle = memo(({
|
||||||
const collapsedMerged = collapsed !== undefined ? collapsed : collapsedLocal
|
const collapsedMerged = collapsed !== undefined ? collapsed : collapsedLocal
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('mb-0.5', !!subTitle && 'mb-1')}>
|
<div className={cn('mb-0.5', !!subTitle && 'mb-1', className)}>
|
||||||
<div
|
<div
|
||||||
className="group/collapse flex items-center justify-between py-1"
|
className="group/collapse flex items-center justify-between py-1"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,20 @@ export type GroupProps = {
|
||||||
className?: string
|
className?: string
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
withBorderBottom?: boolean
|
withBorderBottom?: boolean
|
||||||
|
withBorderTop?: boolean
|
||||||
}
|
}
|
||||||
export const Group = memo(({
|
export const Group = memo(({
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
withBorderBottom,
|
withBorderBottom,
|
||||||
|
withBorderTop,
|
||||||
}: GroupProps) => {
|
}: GroupProps) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'px-4 py-2',
|
'px-4 py-2',
|
||||||
withBorderBottom && 'border-b border-divider-subtle',
|
withBorderBottom && 'border-b border-divider-subtle',
|
||||||
|
withBorderTop && 'border-t border-divider-subtle',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import type { CommonNodeType } from '@/app/components/workflow/types'
|
import type { CommonNodeType } from '@/app/components/workflow/types'
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
import { useStoreApi } from 'reactflow'
|
||||||
import { useNodeDataUpdate } from '@/app/components/workflow/hooks'
|
import { useNodeDataUpdate } from '@/app/components/workflow/hooks'
|
||||||
|
|
||||||
const useNodeCrud = <T>(id: string, data: CommonNodeType<T>) => {
|
const useNodeCrud = <T>(id: string, data: CommonNodeType<T>) => {
|
||||||
|
|
@ -18,3 +20,27 @@ const useNodeCrud = <T>(id: string, data: CommonNodeType<T>) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useNodeCrud
|
export default useNodeCrud
|
||||||
|
|
||||||
|
export const useNodeCurdKit = <T>(id: string) => {
|
||||||
|
const store = useStoreApi()
|
||||||
|
const { handleNodeDataUpdateWithSyncDraft } = useNodeDataUpdate()
|
||||||
|
|
||||||
|
const getNodeData = useCallback(() => {
|
||||||
|
const { getNodes } = store.getState()
|
||||||
|
const nodes = getNodes()
|
||||||
|
|
||||||
|
return nodes.find(node => node.id === id)
|
||||||
|
}, [store, id])
|
||||||
|
|
||||||
|
const handleNodeDataUpdate = useCallback((data: Partial<CommonNodeType<T>>) => {
|
||||||
|
handleNodeDataUpdateWithSyncDraft({
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}, [id, handleNodeDataUpdateWithSyncDraft])
|
||||||
|
|
||||||
|
return {
|
||||||
|
getNodeData,
|
||||||
|
handleNodeDataUpdate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,54 @@
|
||||||
|
import type { ToolValue } from '@/app/components/workflow/block-selector/types'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector'
|
||||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
import { BoxGroup } from '@/app/components/workflow/nodes/_base/components/layout'
|
||||||
|
import MaxIterations from './max-iterations'
|
||||||
|
import { useNodeTools } from './use-node-tools'
|
||||||
|
|
||||||
const i18nPrefix = 'workflow.nodes.llm'
|
const i18nPrefix = 'workflow.nodes.llm'
|
||||||
|
|
||||||
const Tools = () => {
|
type ToolsProps = {
|
||||||
|
nodeId: string
|
||||||
|
tools?: ToolValue[]
|
||||||
|
maxIterations?: number
|
||||||
|
}
|
||||||
|
const Tools = ({
|
||||||
|
nodeId,
|
||||||
|
tools = [],
|
||||||
|
maxIterations = 10,
|
||||||
|
}: ToolsProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const {
|
||||||
|
handleToolsChange,
|
||||||
|
handleMaxIterationsChange,
|
||||||
|
} = useNodeTools(nodeId)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field
|
<BoxGroup
|
||||||
title={t(`${i18nPrefix}.tools.title`)}
|
boxProps={{
|
||||||
tooltip={t('appDebug.vision.description')!}
|
withBorderBottom: true,
|
||||||
operations={(
|
withBorderTop: true,
|
||||||
<Tooltip
|
}}
|
||||||
popupContent={t('appDebug.vision.onlySupportVisionModelTip')!}
|
groupProps={{
|
||||||
>
|
className: 'px-0',
|
||||||
</Tooltip>
|
}}
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<div>
|
<MultipleToolSelector
|
||||||
<div>Tools</div>
|
nodeId={nodeId}
|
||||||
</div>
|
nodeOutputVars={[]}
|
||||||
</Field>
|
availableNodes={[]}
|
||||||
|
value={tools}
|
||||||
|
label={t(`${i18nPrefix}.tools.title`)}
|
||||||
|
tooltip={t(`${i18nPrefix}.tools.title`)}
|
||||||
|
onChange={handleToolsChange}
|
||||||
|
supportCollapse
|
||||||
|
/>
|
||||||
|
<MaxIterations
|
||||||
|
value={maxIterations}
|
||||||
|
onChange={handleMaxIterationsChange}
|
||||||
|
/>
|
||||||
|
</BoxGroup>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { memo } from 'react'
|
||||||
|
import { InputNumber } from '@/app/components/base/input-number'
|
||||||
|
import Slider from '@/app/components/base/slider'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
|
||||||
|
type MaxIterationsProps = {
|
||||||
|
value?: number
|
||||||
|
onChange?: (value: number) => void
|
||||||
|
}
|
||||||
|
const MaxIterations = ({ value = 10, onChange }: MaxIterationsProps) => {
|
||||||
|
return (
|
||||||
|
<div className="mt-3 flex h-10 items-center">
|
||||||
|
<div className="system-sm-semibold mr-0.5 truncate uppercase text-text-secondary">Max Iterations</div>
|
||||||
|
<Tooltip
|
||||||
|
popupContent="Max Iterations is the maximum number of iterations to run the tool."
|
||||||
|
triggerClassName="shrink-0 w-4 h-4"
|
||||||
|
/>
|
||||||
|
<div className="mr-3 flex grow items-center justify-end">
|
||||||
|
<Slider
|
||||||
|
className="w-[124px]"
|
||||||
|
value={value}
|
||||||
|
onChange={onChange ?? (() => {})}
|
||||||
|
min={1}
|
||||||
|
max={99}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<InputNumber
|
||||||
|
className="w-10 shrink-0"
|
||||||
|
value={value}
|
||||||
|
onChange={onChange ?? (() => {})}
|
||||||
|
min={1}
|
||||||
|
max={99}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(MaxIterations)
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import type { LLMNodeType } from '../../types'
|
||||||
|
import type { ToolValue } from '@/app/components/workflow/block-selector/types'
|
||||||
|
import { useNodeCurdKit } from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
|
||||||
|
|
||||||
|
export const useNodeTools = (nodeId: string) => {
|
||||||
|
const { handleNodeDataUpdate } = useNodeCurdKit<LLMNodeType>(nodeId)
|
||||||
|
|
||||||
|
const handleToolsChange = (tools: ToolValue[]) => {
|
||||||
|
handleNodeDataUpdate({
|
||||||
|
tools,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const handleMaxIterationsChange = (maxIterations: number) => {
|
||||||
|
handleNodeDataUpdate({
|
||||||
|
max_iterations: maxIterations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleToolsChange,
|
||||||
|
handleMaxIterationsChange,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ import VarReferencePicker from '../_base/components/variable/var-reference-picke
|
||||||
import ConfigPrompt from './components/config-prompt'
|
import ConfigPrompt from './components/config-prompt'
|
||||||
import ReasoningFormatConfig from './components/reasoning-format-config'
|
import ReasoningFormatConfig from './components/reasoning-format-config'
|
||||||
import StructureOutput from './components/structure-output'
|
import StructureOutput from './components/structure-output'
|
||||||
|
import Tools from './components/tools'
|
||||||
import useConfig from './use-config'
|
import useConfig from './use-config'
|
||||||
|
|
||||||
const i18nPrefix = 'workflow.nodes.llm'
|
const i18nPrefix = 'workflow.nodes.llm'
|
||||||
|
|
@ -233,6 +234,12 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Tools
|
||||||
|
nodeId={id}
|
||||||
|
tools={inputs.tools}
|
||||||
|
maxIterations={inputs.max_iterations}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Vision: GPT4-vision and so on */}
|
{/* Vision: GPT4-vision and so on */}
|
||||||
<ConfigVision
|
<ConfigVision
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,18 @@
|
||||||
|
import type { ToolValue } from '@/app/components/workflow/block-selector/types'
|
||||||
import type { CommonNodeType, Memory, ModelConfig, PromptItem, ValueSelector, Variable, VisionSetting } from '@/app/components/workflow/types'
|
import type { CommonNodeType, Memory, ModelConfig, PromptItem, ValueSelector, Variable, VisionSetting } from '@/app/components/workflow/types'
|
||||||
|
|
||||||
|
export type Tool = {
|
||||||
|
enabled: boolean
|
||||||
|
type: string
|
||||||
|
provider_name: 'plugin' | 'builtin' | 'api' | 'workflow' | 'app' | 'dataset-retrieval'
|
||||||
|
tool_name: string
|
||||||
|
plugin_unique_identifier?: string
|
||||||
|
credential_id?: string
|
||||||
|
parameters?: Record<string, any>
|
||||||
|
settings?: Record<string, any>
|
||||||
|
extra?: Record<string, any>
|
||||||
|
}
|
||||||
|
|
||||||
export type LLMNodeType = CommonNodeType & {
|
export type LLMNodeType = CommonNodeType & {
|
||||||
model: ModelConfig
|
model: ModelConfig
|
||||||
prompt_template: PromptItem[] | PromptItem
|
prompt_template: PromptItem[] | PromptItem
|
||||||
|
|
@ -18,6 +31,8 @@ export type LLMNodeType = CommonNodeType & {
|
||||||
structured_output_enabled?: boolean
|
structured_output_enabled?: boolean
|
||||||
structured_output?: StructuredOutput
|
structured_output?: StructuredOutput
|
||||||
reasoning_format?: 'tagged' | 'separated'
|
reasoning_format?: 'tagged' | 'separated'
|
||||||
|
tools?: ToolValue[]
|
||||||
|
max_iterations?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Type {
|
export enum Type {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue