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