mirror of https://github.com/langgenius/dify.git
feat: llm node support tools
This commit is contained in:
parent
e83635ee5a
commit
ececc5ec2c
|
|
@ -21,6 +21,7 @@ import BasicContent from './basic-content'
|
|||
import More from './more'
|
||||
import Operation from './operation'
|
||||
import SuggestedQuestions from './suggested-questions'
|
||||
import ToolCalls from './tool-calls'
|
||||
import WorkflowProcessItem from './workflow-process'
|
||||
|
||||
type AnswerProps = {
|
||||
|
|
@ -61,6 +62,7 @@ const Answer: FC<AnswerProps> = ({
|
|||
workflowProcess,
|
||||
allFiles,
|
||||
message_files,
|
||||
toolCalls,
|
||||
} = item
|
||||
const hasAgentThoughts = !!agent_thoughts?.length
|
||||
|
||||
|
|
@ -154,6 +156,11 @@ const Answer: FC<AnswerProps> = ({
|
|||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
!!toolCalls?.length && (
|
||||
<ToolCalls toolCalls={toolCalls} />
|
||||
)
|
||||
}
|
||||
{
|
||||
responding && contentIsEmpty && !hasAgentThoughts && (
|
||||
<div className="flex h-5 w-6 items-center justify-center">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import type { ToolCallItem } from '../../type'
|
||||
import ToolCallsItem from './item'
|
||||
|
||||
type ToolCallsProps = {
|
||||
toolCalls: ToolCallItem[]
|
||||
}
|
||||
const ToolCalls = ({
|
||||
toolCalls,
|
||||
}: ToolCallsProps) => {
|
||||
return (
|
||||
<div>
|
||||
{toolCalls.map((toolCall: ToolCallItem) => (
|
||||
<ToolCallsItem key={toolCall.tool_call_id} payload={toolCall} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ToolCalls
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
import type { ToolCallItem } from '../../type'
|
||||
import {
|
||||
RiArrowDownSLine,
|
||||
} from '@remixicon/react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
|
||||
type ToolCallsItemProps = {
|
||||
payload: ToolCallItem
|
||||
}
|
||||
const ToolCallsItem = ({
|
||||
payload,
|
||||
}: ToolCallsItemProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [expand, setExpand] = useState(false)
|
||||
return (
|
||||
<div
|
||||
className="rounded-xl bg-background-gradient-bg-fill-chat-bubble-bg-1 px-2 pb-1 pt-2"
|
||||
>
|
||||
<div className="mb-1 flex cursor-pointer items-center hover:bg-background-gradient-bg-fill-chat-bubble-bg-2" onClick={() => setExpand(!expand)}>
|
||||
<div className="mr-1 h-5 w-5 grow truncate" title={payload.tool_name}>{payload.tool_name}</div>
|
||||
{
|
||||
!!payload.tool_elapsed_time && (
|
||||
<div className="system-xs-regular mr-1 shrink-0 text-text-tertiary">
|
||||
{payload.tool_elapsed_time?.toFixed(3)}
|
||||
s
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<RiArrowDownSLine className="h-4 w-4 shrink-0" />
|
||||
</div>
|
||||
{
|
||||
expand && (
|
||||
<div className="relative px-2 pl-9">
|
||||
<div className="absolute bottom-1 left-2 top-1 w-[1px] bg-divider-regular"></div>
|
||||
{
|
||||
payload.is_thought && (
|
||||
<div className="body-sm-medium text-text-tertiary">{payload.tool_output}</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!payload.is_thought && (
|
||||
<CodeEditor
|
||||
readOnly
|
||||
title={<div>{t('common.input', { ns: 'workflow' })}</div>}
|
||||
language={CodeLanguage.json}
|
||||
value={JSON.parse(payload.tool_arguments || '{}')}
|
||||
isJSONStringifyBeauty
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
!payload.is_thought && (
|
||||
<CodeEditor
|
||||
readOnly
|
||||
className="mt-1"
|
||||
title={<div>{t('common.output', { ns: 'workflow' })}</div>}
|
||||
language={CodeLanguage.json}
|
||||
value={{
|
||||
answer: payload.tool_output,
|
||||
}}
|
||||
isJSONStringifyBeauty
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ToolCallsItem
|
||||
|
|
@ -64,6 +64,17 @@ export type CitationItem = {
|
|||
word_count: number
|
||||
}
|
||||
|
||||
export type ToolCallItem = {
|
||||
is_thought?: boolean
|
||||
tool_call_id?: string
|
||||
tool_name?: string
|
||||
tool_arguments?: string
|
||||
tool_files?: string[]
|
||||
tool_error?: string
|
||||
tool_output?: string
|
||||
tool_elapsed_time?: number
|
||||
}
|
||||
|
||||
export type IChatItem = {
|
||||
id: string
|
||||
content: string
|
||||
|
|
@ -104,6 +115,7 @@ export type IChatItem = {
|
|||
siblingIndex?: number
|
||||
prevSibling?: string
|
||||
nextSibling?: string
|
||||
toolCalls?: ToolCallItem[]
|
||||
}
|
||||
|
||||
export type Metadata = {
|
||||
|
|
|
|||
|
|
@ -270,9 +270,53 @@ export const useChat = (
|
|||
handleRun(
|
||||
bodyParams,
|
||||
{
|
||||
onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId, taskId }: any) => {
|
||||
onData: (message: string, isFirstMessage: boolean, {
|
||||
conversationId: newConversationId,
|
||||
messageId,
|
||||
taskId,
|
||||
chunk_type,
|
||||
tool_call_id,
|
||||
tool_name,
|
||||
tool_arguments,
|
||||
tool_files,
|
||||
tool_error,
|
||||
tool_elapsed_time,
|
||||
}: any) => {
|
||||
responseItem.content = responseItem.content + message
|
||||
|
||||
if (chunk_type === 'tool_call') {
|
||||
if (!responseItem.toolCalls)
|
||||
responseItem.toolCalls = []
|
||||
responseItem.toolCalls?.push({
|
||||
tool_call_id,
|
||||
tool_name,
|
||||
tool_arguments,
|
||||
tool_files,
|
||||
tool_error,
|
||||
tool_elapsed_time,
|
||||
})
|
||||
}
|
||||
|
||||
if (chunk_type === 'tool_result') {
|
||||
const currentToolCallIndex = responseItem.toolCalls?.findIndex(item => item.tool_call_id === tool_call_id) ?? -1
|
||||
|
||||
if (currentToolCallIndex > -1)
|
||||
responseItem.toolCalls![currentToolCallIndex].tool_output = message
|
||||
}
|
||||
|
||||
if (chunk_type === 'thought_start') {
|
||||
responseItem.toolCalls?.push({
|
||||
is_thought: true,
|
||||
tool_elapsed_time,
|
||||
})
|
||||
}
|
||||
|
||||
if (chunk_type === 'thought') {
|
||||
const currentThoughtIndex = responseItem.toolCalls?.findIndex(item => item.is_thought) ?? -1
|
||||
if (currentThoughtIndex > -1)
|
||||
responseItem.toolCalls![currentThoughtIndex].tool_output = message
|
||||
}
|
||||
|
||||
if (messageId && !hasSetResponseId) {
|
||||
questionItem.id = `question-${messageId}`
|
||||
responseItem.id = messageId
|
||||
|
|
|
|||
|
|
@ -40,6 +40,13 @@ export type IOnDataMoreInfo = {
|
|||
messageId: string
|
||||
errorMessage?: string
|
||||
errorCode?: string
|
||||
chunk_type?: 'text' | 'tool_call' | 'tool_result' | 'thought' | 'thought_start'
|
||||
tool_call_id?: string
|
||||
tool_name?: string
|
||||
tool_arguments?: string
|
||||
tool_files?: string[]
|
||||
tool_error?: string
|
||||
tool_elapsed_time?: number
|
||||
}
|
||||
|
||||
export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void
|
||||
|
|
@ -234,6 +241,13 @@ export const handleStream = (
|
|||
conversationId: bufferObj.conversation_id,
|
||||
taskId: bufferObj.task_id,
|
||||
messageId: bufferObj.id,
|
||||
chunk_type: bufferObj.chunk_type,
|
||||
tool_call_id: bufferObj.tool_call_id,
|
||||
tool_name: bufferObj.tool_name,
|
||||
tool_arguments: bufferObj.tool_arguments,
|
||||
tool_files: bufferObj.tool_files,
|
||||
tool_error: bufferObj.tool_error,
|
||||
tool_elapsed_time: bufferObj.tool_elapsed_time,
|
||||
})
|
||||
isFirstMessage = false
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue