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
|
||||
children?: ReactNode
|
||||
withBorderBottom?: boolean
|
||||
withBorderTop?: boolean
|
||||
}
|
||||
export const Box = memo(({
|
||||
className,
|
||||
children,
|
||||
withBorderBottom,
|
||||
withBorderTop,
|
||||
}: BoxProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'py-2',
|
||||
withBorderBottom && 'border-b border-divider-subtle',
|
||||
withBorderTop && 'border-t border-divider-subtle',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { cn } from '@/utils/classnames'
|
|||
|
||||
export type FieldTitleProps = {
|
||||
title?: string
|
||||
className?: string
|
||||
operation?: ReactNode
|
||||
subTitle?: string | ReactNode
|
||||
tooltip?: string
|
||||
|
|
@ -19,6 +20,7 @@ export type FieldTitleProps = {
|
|||
}
|
||||
export const FieldTitle = memo(({
|
||||
title,
|
||||
className,
|
||||
operation,
|
||||
subTitle,
|
||||
tooltip,
|
||||
|
|
@ -31,7 +33,7 @@ export const FieldTitle = memo(({
|
|||
const collapsedMerged = collapsed !== undefined ? collapsed : collapsedLocal
|
||||
|
||||
return (
|
||||
<div className={cn('mb-0.5', !!subTitle && 'mb-1')}>
|
||||
<div className={cn('mb-0.5', !!subTitle && 'mb-1', className)}>
|
||||
<div
|
||||
className="group/collapse flex items-center justify-between py-1"
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -6,17 +6,20 @@ export type GroupProps = {
|
|||
className?: string
|
||||
children?: ReactNode
|
||||
withBorderBottom?: boolean
|
||||
withBorderTop?: boolean
|
||||
}
|
||||
export const Group = memo(({
|
||||
className,
|
||||
children,
|
||||
withBorderBottom,
|
||||
withBorderTop,
|
||||
}: GroupProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'px-4 py-2',
|
||||
withBorderBottom && 'border-b border-divider-subtle',
|
||||
withBorderTop && 'border-t border-divider-subtle',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import type { CommonNodeType } from '@/app/components/workflow/types'
|
||||
import { useCallback } from 'react'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { useNodeDataUpdate } from '@/app/components/workflow/hooks'
|
||||
|
||||
const useNodeCrud = <T>(id: string, data: CommonNodeType<T>) => {
|
||||
|
|
@ -18,3 +20,27 @@ const useNodeCrud = <T>(id: string, data: CommonNodeType<T>) => {
|
|||
}
|
||||
|
||||
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 { useTranslation } from 'react-i18next'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector'
|
||||
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 Tools = () => {
|
||||
type ToolsProps = {
|
||||
nodeId: string
|
||||
tools?: ToolValue[]
|
||||
maxIterations?: number
|
||||
}
|
||||
const Tools = ({
|
||||
nodeId,
|
||||
tools = [],
|
||||
maxIterations = 10,
|
||||
}: ToolsProps) => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
handleToolsChange,
|
||||
handleMaxIterationsChange,
|
||||
} = useNodeTools(nodeId)
|
||||
|
||||
return (
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.tools.title`)}
|
||||
tooltip={t('appDebug.vision.description')!}
|
||||
operations={(
|
||||
<Tooltip
|
||||
popupContent={t('appDebug.vision.onlySupportVisionModelTip')!}
|
||||
>
|
||||
</Tooltip>
|
||||
)}
|
||||
<BoxGroup
|
||||
boxProps={{
|
||||
withBorderBottom: true,
|
||||
withBorderTop: true,
|
||||
}}
|
||||
groupProps={{
|
||||
className: 'px-0',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div>Tools</div>
|
||||
</div>
|
||||
</Field>
|
||||
<MultipleToolSelector
|
||||
nodeId={nodeId}
|
||||
nodeOutputVars={[]}
|
||||
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 ReasoningFormatConfig from './components/reasoning-format-config'
|
||||
import StructureOutput from './components/structure-output'
|
||||
import Tools from './components/tools'
|
||||
import useConfig from './use-config'
|
||||
|
||||
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 */}
|
||||
<ConfigVision
|
||||
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'
|
||||
|
||||
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 & {
|
||||
model: ModelConfig
|
||||
prompt_template: PromptItem[] | PromptItem
|
||||
|
|
@ -18,6 +31,8 @@ export type LLMNodeType = CommonNodeType & {
|
|||
structured_output_enabled?: boolean
|
||||
structured_output?: StructuredOutput
|
||||
reasoning_format?: 'tagged' | 'separated'
|
||||
tools?: ToolValue[]
|
||||
max_iterations?: number
|
||||
}
|
||||
|
||||
export enum Type {
|
||||
|
|
|
|||
Loading…
Reference in New Issue