mirror of
https://github.com/langgenius/dify.git
synced 2026-09-08 11:04:27 +08:00
fix: inline agent panel not always show
This commit is contained in:
parent
31ee7bb467
commit
ce5033c554
@ -89,6 +89,7 @@ const CandidateNodeMain: FC<Props> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldCreateInlineAgentBinding) {
|
if (shouldCreateInlineAgentBinding) {
|
||||||
|
workflowStore.getState().setOpenInlineAgentPanelNodeId(candidateNode.id)
|
||||||
createInlineAgentBinding(candidateNode.id, {
|
createInlineAgentBinding(candidateNode.id, {
|
||||||
onError: () => {
|
onError: () => {
|
||||||
const { nodes, setNodes } = collaborativeWorkflow.getState()
|
const { nodes, setNodes } = collaborativeWorkflow.getState()
|
||||||
@ -105,7 +106,6 @@ const CandidateNodeMain: FC<Props> = ({
|
|||||||
delete node.data._isTempNode
|
delete node.data._isTempNode
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
workflowStore.getState().setOpenInlineAgentPanelNodeId(candidateNode.id)
|
|
||||||
handleSyncWorkflowDraft(true, true)
|
handleSyncWorkflowDraft(true, true)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -193,6 +193,7 @@ export const useNodesInteractions = () => {
|
|||||||
const createInlineAgentBindingForNode = useCallback((nodeId: string, options?: {
|
const createInlineAgentBindingForNode = useCallback((nodeId: string, options?: {
|
||||||
onError?: () => void
|
onError?: () => void
|
||||||
}) => {
|
}) => {
|
||||||
|
workflowStore.getState().setOpenInlineAgentPanelNodeId(nodeId)
|
||||||
createInlineAgentBinding(nodeId, {
|
createInlineAgentBinding(nodeId, {
|
||||||
onError: () => {
|
onError: () => {
|
||||||
options?.onError?.()
|
options?.onError?.()
|
||||||
@ -208,7 +209,6 @@ export const useNodesInteractions = () => {
|
|||||||
delete node.data._isTempNode
|
delete node.data._isTempNode
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
workflowStore.getState().setOpenInlineAgentPanelNodeId(nodeId)
|
|
||||||
handleSyncWorkflowDraft(true, true)
|
handleSyncWorkflowDraft(true, true)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -27,7 +27,7 @@ const {
|
|||||||
mockHandleNodeDataUpdateWithSyncDraft: vi.fn((_payload, options) => options?.callback?.onSuccess?.()),
|
mockHandleNodeDataUpdateWithSyncDraft: vi.fn((_payload, options) => options?.callback?.onSuccess?.()),
|
||||||
mockInsertNodes: vi.fn(),
|
mockInsertNodes: vi.fn(),
|
||||||
mockOrchestrateDrawerPanelProps: [] as Array<{
|
mockOrchestrateDrawerPanelProps: [] as Array<{
|
||||||
agentId: string
|
agentId?: string
|
||||||
inlineComposerState?: unknown
|
inlineComposerState?: unknown
|
||||||
isInline: boolean
|
isInline: boolean
|
||||||
nodeId: string
|
nodeId: string
|
||||||
@ -150,7 +150,7 @@ vi.mock('../hooks', () => ({
|
|||||||
|
|
||||||
vi.mock('../components/agent-orchestrate-drawer-panel', () => ({
|
vi.mock('../components/agent-orchestrate-drawer-panel', () => ({
|
||||||
AgentOrchestrateDrawerPanel: (props: {
|
AgentOrchestrateDrawerPanel: (props: {
|
||||||
agentId: string
|
agentId?: string
|
||||||
appId?: string
|
appId?: string
|
||||||
inlineComposerState?: unknown
|
inlineComposerState?: unknown
|
||||||
isInline: boolean
|
isInline: boolean
|
||||||
@ -321,6 +321,7 @@ describe('agent/panel', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('renders a pending inline agent state while the binding is being created', () => {
|
it('renders a pending inline agent state while the binding is being created', () => {
|
||||||
|
mockStoreState.openInlineAgentPanelNodeId = 'agent-node'
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<AgentV2Panel
|
<AgentV2Panel
|
||||||
id="agent-node"
|
id="agent-node"
|
||||||
@ -337,6 +338,16 @@ describe('agent/panel', () => {
|
|||||||
expect(screen.queryByText(/^workflow\.errorMsg\.fieldRequired/)).not.toBeInTheDocument()
|
expect(screen.queryByText(/^workflow\.errorMsg\.fieldRequired/)).not.toBeInTheDocument()
|
||||||
expect(container.querySelector('[aria-busy="true"]')).toBeInTheDocument()
|
expect(container.querySelector('[aria-busy="true"]')).toBeInTheDocument()
|
||||||
expect(screen.getByRole('button', { name: 'workflow.nodes.agent.roster.change' })).toBeDisabled()
|
expect(screen.getByRole('button', { name: 'workflow.nodes.agent.roster.change' })).toBeDisabled()
|
||||||
|
expect(screen.getByRole('dialog', { name: 'workflow.nodes.agent.roster.inlineSetup.name' })).toBeInTheDocument()
|
||||||
|
expect(screen.getByRole('region', { name: 'inline-orchestrate-panel' })).toBeInTheDocument()
|
||||||
|
expect(screen.queryByText('workflow.nodes.agent.roster.editInConsole')).not.toBeInTheDocument()
|
||||||
|
expect(screen.queryByText('workflow.nodes.agent.roster.makeCopy')).not.toBeInTheDocument()
|
||||||
|
expect(mockOrchestrateDrawerPanelProps.at(-1)).toMatchObject({
|
||||||
|
agentId: undefined,
|
||||||
|
isInline: true,
|
||||||
|
nodeId: 'agent-node',
|
||||||
|
open: true,
|
||||||
|
})
|
||||||
expect(screen.getByText('workflow.nodes.agent.task.label')).toBeInTheDocument()
|
expect(screen.getByText('workflow.nodes.agent.task.label')).toBeInTheDocument()
|
||||||
expect(screen.getByText('workflow.nodes.agent.outputVars.text')).toBeInTheDocument()
|
expect(screen.getByText('workflow.nodes.agent.outputVars.text')).toBeInTheDocument()
|
||||||
expect(container.querySelector('[inert]')).toBeInTheDocument()
|
expect(container.querySelector('[inert]')).toBeInTheDocument()
|
||||||
@ -570,10 +581,30 @@ describe('agent/panel', () => {
|
|||||||
fireEvent.click(screen.getByRole('button', { name: 'workflow.nodes.agent.roster.change' }))
|
fireEvent.click(screen.getByRole('button', { name: 'workflow.nodes.agent.roster.change' }))
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Start from Scratch' }))
|
fireEvent.click(screen.getByRole('button', { name: 'Start from Scratch' }))
|
||||||
|
|
||||||
|
expect(mockStoreState.setOpenInlineAgentPanelNodeId).toHaveBeenCalledWith('agent-node')
|
||||||
expect(mockCreateInlineAgentBinding).toHaveBeenCalledWith('agent-node', expect.objectContaining({
|
expect(mockCreateInlineAgentBinding).toHaveBeenCalledWith('agent-node', expect.objectContaining({
|
||||||
onSuccess: expect.any(Function),
|
onSuccess: expect.any(Function),
|
||||||
}))
|
}))
|
||||||
expect(mockStoreState.setOpenInlineAgentPanelNodeId).toHaveBeenCalledWith('agent-node')
|
expect(mockHandleNodeDataUpdateWithSyncDraft).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
id: 'agent-node',
|
||||||
|
data: expect.objectContaining({
|
||||||
|
agent_binding: {
|
||||||
|
binding_type: 'inline_agent',
|
||||||
|
},
|
||||||
|
agent_task: 'Keep this task',
|
||||||
|
agent_declared_outputs: [{
|
||||||
|
name: 'summary',
|
||||||
|
type: 'string',
|
||||||
|
}],
|
||||||
|
_openInlineAgentPanel: true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
expect.objectContaining({
|
||||||
|
sync: true,
|
||||||
|
notRefreshWhenSyncError: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
expect(mockHandleNodeDataUpdateWithSyncDraft).toHaveBeenCalledWith(
|
expect(mockHandleNodeDataUpdateWithSyncDraft).toHaveBeenCalledWith(
|
||||||
{
|
{
|
||||||
id: 'agent-node',
|
id: 'agent-node',
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { consoleQuery } from '@/service/client'
|
|||||||
import { useWorkflowInlineAgentConfigureSync } from '../agent-soul-config'
|
import { useWorkflowInlineAgentConfigureSync } from '../agent-soul-config'
|
||||||
|
|
||||||
type AgentOrchestrateDrawerPanelProps = {
|
type AgentOrchestrateDrawerPanelProps = {
|
||||||
agentId: string
|
agentId?: string
|
||||||
appId?: string
|
appId?: string
|
||||||
inlineComposerState?: WorkflowAgentComposerResponse
|
inlineComposerState?: WorkflowAgentComposerResponse
|
||||||
isInline: boolean
|
isInline: boolean
|
||||||
@ -40,7 +40,7 @@ function AgentOrchestrateDrawerPanelContent({
|
|||||||
open,
|
open,
|
||||||
}: AgentOrchestrateDrawerPanelProps) {
|
}: AgentOrchestrateDrawerPanelProps) {
|
||||||
const rosterComposerQuery = useQuery(consoleQuery.agent.byAgentId.composer.get.queryOptions({
|
const rosterComposerQuery = useQuery(consoleQuery.agent.byAgentId.composer.get.queryOptions({
|
||||||
input: open && !isInline
|
input: open && !isInline && agentId
|
||||||
? {
|
? {
|
||||||
params: {
|
params: {
|
||||||
agent_id: agentId,
|
agent_id: agentId,
|
||||||
@ -62,12 +62,12 @@ function AgentOrchestrateDrawerPanelContent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
useHydrateAgentSoulConfigDraft({
|
useHydrateAgentSoulConfigDraft({
|
||||||
agentId: isInline ? `${nodeId}:${agentId}` : agentId,
|
agentId: isInline ? `${nodeId}:${agentId ?? 'pending'}` : agentId ?? nodeId,
|
||||||
activeVersionId: activeConfigSnapshot?.id,
|
activeVersionId: activeConfigSnapshot?.id,
|
||||||
config: agentSoulConfig as AgentSoulConfig | undefined,
|
config: agentSoulConfig as AgentSoulConfig | undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!agentSoulConfig) {
|
if (!agentId || !agentSoulConfig) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full min-h-80 items-center justify-center bg-components-panel-bg">
|
<div className="flex h-full min-h-80 items-center justify-center bg-components-panel-bg">
|
||||||
<Loading type="app" />
|
<Loading type="app" />
|
||||||
|
|||||||
@ -40,18 +40,18 @@ export function AgentV2Panel({
|
|||||||
const inlineAgentId = inputs.agent_binding?.binding_type === 'inline_agent' ? inputs.agent_binding.agent_id : undefined
|
const inlineAgentId = inputs.agent_binding?.binding_type === 'inline_agent' ? inputs.agent_binding.agent_id : undefined
|
||||||
const isInlineAgentReady = hasValidInlineAgentBinding(inputs)
|
const isInlineAgentReady = hasValidInlineAgentBinding(inputs)
|
||||||
const isInlineAgentPending = inputs.agent_binding?.binding_type === 'inline_agent' && !isInlineAgentReady
|
const isInlineAgentPending = inputs.agent_binding?.binding_type === 'inline_agent' && !isInlineAgentReady
|
||||||
const isInlineAgentPanelOpen = isInlineAgentReady && openInlineAgentPanelNodeId === id
|
const isInlineAgentPanelOpen = (isInlineAgentReady || isInlineAgentPending) && openInlineAgentPanelNodeId === id
|
||||||
const rosterAgentQuery = useAgentRosterDetail(rosterAgentId)
|
const rosterAgentQuery = useAgentRosterDetail(rosterAgentId)
|
||||||
const inlineAgentQuery = useWorkflowInlineAgentDetail(id, inlineAgentId)
|
const inlineAgentQuery = useWorkflowInlineAgentDetail(id, inlineAgentId)
|
||||||
const { createInlineAgentBinding, isCreatingInlineAgent } = useCreateInlineAgentBinding()
|
const { createInlineAgentBinding, isCreatingInlineAgent } = useCreateInlineAgentBinding()
|
||||||
const inlineAgent = inlineAgentQuery.data?.agent
|
const inlineAgent = inlineAgentQuery.data?.agent
|
||||||
const isAgentPanelOpen = isInlineAgentReady ? isInlineAgentPanelOpen : isRosterAgentPanelOpen
|
const isAgentPanelOpen = isInlineAgentReady || isInlineAgentPending ? isInlineAgentPanelOpen : isRosterAgentPanelOpen
|
||||||
const isInlineAgentLoading = isInlineAgentReady && !inlineAgent
|
const isInlineAgentLoading = isInlineAgentPending || (isInlineAgentReady && !inlineAgent)
|
||||||
const isAgentBindingPending = isInlineAgentPending || isCreatingInlineAgent
|
const isAgentBindingPending = isInlineAgentPending || isCreatingInlineAgent
|
||||||
const canStartFromScratch = inputs.agent_binding?.binding_type !== 'inline_agent'
|
const canStartFromScratch = inputs.agent_binding?.binding_type !== 'inline_agent'
|
||||||
const displayedAgent = rosterAgentQuery.data ?? (inlineAgentId && isInlineAgentReady
|
const displayedAgent = rosterAgentQuery.data ?? (isInlineAgentPending || isInlineAgentReady
|
||||||
? {
|
? {
|
||||||
id: inlineAgentId,
|
id: inlineAgentId ?? id,
|
||||||
name: inlineAgent?.name || t('nodes.agent.roster.inlineSetup.name', { ns: 'workflow' }),
|
name: inlineAgent?.name || t('nodes.agent.roster.inlineSetup.name', { ns: 'workflow' }),
|
||||||
description: inlineAgent?.description,
|
description: inlineAgent?.description,
|
||||||
role: t('nodes.agent.roster.inlineSetup.type', { ns: 'workflow' }),
|
role: t('nodes.agent.roster.inlineSetup.type', { ns: 'workflow' }),
|
||||||
@ -114,12 +114,31 @@ export function AgentV2Panel({
|
|||||||
}, [handleNodeDataUpdateWithSyncDraft, id, inputs, setOpenInlineAgentPanelNodeId])
|
}, [handleNodeDataUpdateWithSyncDraft, id, inputs, setOpenInlineAgentPanelNodeId])
|
||||||
|
|
||||||
const handleStartFromScratch = useCallback(() => {
|
const handleStartFromScratch = useCallback(() => {
|
||||||
|
setIsRosterAgentPanelOpen(false)
|
||||||
|
setIsInlineAgentPanelOpenedFromTrigger(false)
|
||||||
|
setOpenInlineAgentPanelNodeId(id)
|
||||||
|
|
||||||
|
const pendingInputs = produce(inputsRef.current, (draft) => {
|
||||||
|
delete (draft as AgentV2NodeType & { agent_roster?: unknown }).agent_roster
|
||||||
|
draft.agent_binding = {
|
||||||
|
binding_type: 'inline_agent',
|
||||||
|
}
|
||||||
|
draft._openInlineAgentPanel = true
|
||||||
|
})
|
||||||
|
inputsRef.current = pendingInputs
|
||||||
|
handleNodeDataUpdateWithSyncDraft(
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
data: pendingInputs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sync: true,
|
||||||
|
notRefreshWhenSyncError: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
createInlineAgentBinding(id, {
|
createInlineAgentBinding(id, {
|
||||||
onSuccess: (binding) => {
|
onSuccess: (binding) => {
|
||||||
setIsRosterAgentPanelOpen(false)
|
|
||||||
setIsInlineAgentPanelOpenedFromTrigger(false)
|
|
||||||
setOpenInlineAgentPanelNodeId(id)
|
|
||||||
|
|
||||||
const newInputs = produce(inputsRef.current, (draft) => {
|
const newInputs = produce(inputsRef.current, (draft) => {
|
||||||
delete (draft as AgentV2NodeType & { agent_roster?: unknown }).agent_roster
|
delete (draft as AgentV2NodeType & { agent_roster?: unknown }).agent_roster
|
||||||
draft.agent_binding = binding
|
draft.agent_binding = binding
|
||||||
@ -196,27 +215,27 @@ export function AgentV2Panel({
|
|||||||
<div className="border-b border-divider-subtle">
|
<div className="border-b border-divider-subtle">
|
||||||
<AgentRosterField
|
<AgentRosterField
|
||||||
agent={displayedAgent}
|
agent={displayedAgent}
|
||||||
agentId={rosterAgentId ?? inlineAgentId ?? undefined}
|
agentId={rosterAgentId ?? inlineAgentId ?? (isInlineAgentPending ? id : undefined)}
|
||||||
canOpenPanel={!isInlineAgentPending}
|
canOpenPanel
|
||||||
isInlineSetup={isInlineAgentReady}
|
isInlineSetup={isInlineAgentReady || isInlineAgentPending}
|
||||||
isLoading={isInlineAgentLoading}
|
isLoading={isInlineAgentLoading}
|
||||||
isPanelOpen={isAgentPanelOpen}
|
isPanelOpen={isAgentPanelOpen}
|
||||||
isPending={isAgentBindingPending}
|
isPending={isAgentBindingPending}
|
||||||
panelBody={isAgentPanelOpen && displayedAgent
|
panelBody={isAgentPanelOpen && displayedAgent
|
||||||
? (
|
? (
|
||||||
<AgentOrchestrateDrawerPanel
|
<AgentOrchestrateDrawerPanel
|
||||||
agentId={displayedAgent.id}
|
agentId={inlineAgentId ?? rosterAgentId}
|
||||||
appId={appId}
|
appId={appId}
|
||||||
inlineComposerState={inlineAgentQuery.data}
|
inlineComposerState={inlineAgentQuery.data}
|
||||||
isInline={isInlineAgentReady}
|
isInline={isInlineAgentReady || isInlineAgentPending}
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
open={isAgentPanelOpen}
|
open={isAgentPanelOpen}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
: undefined}
|
: undefined}
|
||||||
panelMode={isInlineAgentReady && !isInlineAgentPanelOpenedFromTrigger ? 'setup' : 'detail'}
|
panelMode={isInlineAgentPending || (isInlineAgentReady && !isInlineAgentPanelOpenedFromTrigger) ? 'setup' : 'detail'}
|
||||||
portalContainerRef={drawerPortalContainerRef}
|
portalContainerRef={drawerPortalContainerRef}
|
||||||
showPanelDetailActions={!isInlineAgentReady}
|
showPanelDetailActions={!isInlineAgentReady && !isInlineAgentPending}
|
||||||
onChange={handleRosterChange}
|
onChange={handleRosterChange}
|
||||||
onPanelOpenChange={handleAgentPanelOpenChange}
|
onPanelOpenChange={handleAgentPanelOpenChange}
|
||||||
onStartFromScratch={canStartFromScratch ? handleStartFromScratch : undefined}
|
onStartFromScratch={canStartFromScratch ? handleStartFromScratch : undefined}
|
||||||
|
|||||||
@ -125,6 +125,7 @@ const AddBlock = ({
|
|||||||
workflowStore.setState({
|
workflowStore.setState({
|
||||||
candidateNode: undefined,
|
candidateNode: undefined,
|
||||||
})
|
})
|
||||||
|
workflowStore.getState().setOpenInlineAgentPanelNodeId(newNode.id)
|
||||||
saveStateToHistory(WorkflowHistoryEvent.NodeAdd, { nodeId: newNode.id })
|
saveStateToHistory(WorkflowHistoryEvent.NodeAdd, { nodeId: newNode.id })
|
||||||
createInlineAgentBinding(newNode.id, {
|
createInlineAgentBinding(newNode.id, {
|
||||||
onSuccess: (binding) => {
|
onSuccess: (binding) => {
|
||||||
@ -138,7 +139,6 @@ const AddBlock = ({
|
|||||||
delete node.data._isTempNode
|
delete node.data._isTempNode
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
workflowStore.getState().setOpenInlineAgentPanelNodeId(newNode.id)
|
|
||||||
handleSyncWorkflowDraft(true, true)
|
handleSyncWorkflowDraft(true, true)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user