mirror of
https://github.com/langgenius/dify.git
synced 2026-06-26 14:51:13 +08:00
Merge branch 'feat/agent-v2' of https://github.com/langgenius/dify into feat/agent-v2
This commit is contained in:
commit
d8b8439047
@ -4,6 +4,7 @@ import userEvent from '@testing-library/user-event'
|
||||
import { AgentConfigurePage } from '../page'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
refreshDebugConversation: vi.fn(),
|
||||
queryState: {
|
||||
agent: {
|
||||
data: {
|
||||
@ -65,6 +66,16 @@ vi.mock('@/service/client', () => ({
|
||||
queryOptions: () => ({ queryKey: ['agent'] }),
|
||||
queryKey: () => ['agent'],
|
||||
},
|
||||
debugConversation: {
|
||||
refresh: {
|
||||
post: {
|
||||
mutationOptions: (options?: { onSuccess?: (data: { debug_conversation_id: string }) => void }) => ({
|
||||
mutationFn: mocks.refreshDebugConversation,
|
||||
...options,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
composer: {
|
||||
get: {
|
||||
queryOptions: () => ({ queryKey: ['composer'] }),
|
||||
@ -101,17 +112,29 @@ vi.mock('../components/orchestrate', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('../components/preview/build-chat', () => ({
|
||||
AgentBuildChat: () => (
|
||||
<div role="region" aria-label="preview-chat">
|
||||
build
|
||||
AgentBuildChat: (props: {
|
||||
conversationId?: string | null
|
||||
onConversationIdChange?: (conversationId: string) => void
|
||||
}) => (
|
||||
<div role="region" aria-label="build-chat">
|
||||
<span>{`build:${props.conversationId ?? 'none'}`}</span>
|
||||
<button type="button" onClick={() => props.onConversationIdChange?.('build-conversation-new')}>
|
||||
save build conversation
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('../components/preview/preview-chat', () => ({
|
||||
AgentPreviewChat: () => (
|
||||
AgentPreviewChat: (props: {
|
||||
conversationId?: string | null
|
||||
onConversationIdChange?: (conversationId: string) => void
|
||||
}) => (
|
||||
<div role="region" aria-label="preview-chat">
|
||||
preview
|
||||
<span>{`preview:${props.conversationId ?? 'none'}`}</span>
|
||||
<button type="button" onClick={() => props.onConversationIdChange?.('preview-conversation-new')}>
|
||||
save preview conversation
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
@ -123,15 +146,19 @@ vi.mock('../components/preview/chat-features-panel', () => ({
|
||||
vi.mock('../components/preview/header', () => ({
|
||||
AgentPreviewHeader: (props: {
|
||||
mode: 'build' | 'preview'
|
||||
previewEnabled: boolean
|
||||
onModeChange: (mode: 'build' | 'preview') => void
|
||||
onRestart: () => void
|
||||
onRefresh: () => void
|
||||
}) => (
|
||||
<div>
|
||||
<div>{props.mode}</div>
|
||||
<button type="button" onClick={() => props.onModeChange('preview')}>
|
||||
<button type="button" disabled={!props.previewEnabled} onClick={() => props.onModeChange('preview')}>
|
||||
preview mode
|
||||
</button>
|
||||
<button type="button" onClick={props.onRestart}>
|
||||
<button type="button" onClick={() => props.onModeChange('build')}>
|
||||
build mode
|
||||
</button>
|
||||
<button type="button" onClick={props.onRefresh}>
|
||||
restart preview
|
||||
</button>
|
||||
</div>
|
||||
@ -147,6 +174,9 @@ vi.mock('../components/preview/versions-panel', () => ({
|
||||
describe('AgentConfigurePage', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.refreshDebugConversation.mockResolvedValue({
|
||||
debug_conversation_id: 'debug-conversation-new',
|
||||
})
|
||||
mocks.queryState.agent = {
|
||||
data: {
|
||||
icon: 'agent',
|
||||
@ -189,7 +219,7 @@ describe('AgentConfigurePage', () => {
|
||||
})
|
||||
|
||||
describe('Right panel mode', () => {
|
||||
it('should render build mode by default and switch to preview mode', async () => {
|
||||
it('should keep preview disabled and stay in build mode', async () => {
|
||||
const user = userEvent.setup()
|
||||
const queryClient = new QueryClient()
|
||||
mocks.queryState.composer = {
|
||||
@ -205,11 +235,59 @@ describe('AgentConfigurePage', () => {
|
||||
</QueryClientProvider>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('region', { name: 'preview-chat' })).toHaveTextContent('build')
|
||||
expect(screen.getByRole('region', { name: 'build-chat' })).toHaveTextContent('build:debug-conversation-old')
|
||||
expect(screen.queryByRole('region', { name: 'preview-chat' })).not.toBeInTheDocument()
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'preview mode' }))
|
||||
await user.click(screen.getByRole('button', { name: 'save build conversation' }))
|
||||
|
||||
expect(screen.getByRole('region', { name: 'preview-chat' })).toHaveTextContent('preview')
|
||||
expect(screen.getByRole('region', { name: 'build-chat' })).toHaveTextContent('build:build-conversation-new')
|
||||
expect(mocks.refreshDebugConversation).not.toHaveBeenCalled()
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'restart preview' }))
|
||||
|
||||
expect(mocks.refreshDebugConversation).toHaveBeenCalledWith({
|
||||
params: {
|
||||
agent_id: 'agent-1',
|
||||
},
|
||||
body: {
|
||||
debug_conversation_id: 'build-conversation-new',
|
||||
},
|
||||
}, expect.any(Object))
|
||||
|
||||
expect(screen.getByRole('region', { name: 'build-chat' })).toHaveTextContent('build:none')
|
||||
|
||||
const previewButton = screen.getByRole('button', { name: 'preview mode' })
|
||||
expect(previewButton).toBeDisabled()
|
||||
|
||||
await user.click(previewButton)
|
||||
|
||||
expect(screen.getByRole('region', { name: 'build-chat' })).toHaveTextContent('build:none')
|
||||
expect(screen.queryByRole('region', { name: 'preview-chat' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should keep preview disabled', async () => {
|
||||
const user = userEvent.setup()
|
||||
const queryClient = new QueryClient()
|
||||
mocks.queryState.composer = {
|
||||
data: {},
|
||||
isFetching: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
}
|
||||
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AgentConfigurePage agentId="agent-1" />
|
||||
</QueryClientProvider>,
|
||||
)
|
||||
|
||||
const previewButton = screen.getByRole('button', { name: 'preview mode' })
|
||||
expect(previewButton).toBeDisabled()
|
||||
|
||||
await user.click(previewButton)
|
||||
|
||||
expect(screen.getByRole('region', { name: 'build-chat' })).toHaveTextContent('build:debug-conversation-old')
|
||||
expect(screen.queryByRole('region', { name: 'preview-chat', hidden: true })).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -167,7 +167,7 @@ describe('AgentPreviewChat', () => {
|
||||
})
|
||||
|
||||
renderPreviewChat({
|
||||
debugConversationId: 'debug-conversation-1',
|
||||
conversationId: 'debug-conversation-1',
|
||||
})
|
||||
|
||||
await waitFor(() => expect(useChatMock).toHaveBeenCalled())
|
||||
|
||||
@ -1,91 +1,59 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { AgentPreviewHeader } from '../header'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
refreshDebugConversation: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/client', () => ({
|
||||
consoleQuery: {
|
||||
agent: {
|
||||
byAgentId: {
|
||||
get: {
|
||||
queryKey: () => ['agent'],
|
||||
},
|
||||
debugConversation: {
|
||||
refresh: {
|
||||
post: {
|
||||
mutationOptions: (options?: { onSuccess?: (data: { debug_conversation_id: string }) => void }) => ({
|
||||
mutationFn: mocks.refreshDebugConversation,
|
||||
...options,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
function renderHeader({
|
||||
mode = 'preview',
|
||||
previewEnabled = true,
|
||||
onModeChange = vi.fn(),
|
||||
onOpenVersions = vi.fn(),
|
||||
onRestart = vi.fn(),
|
||||
onRefresh = vi.fn(),
|
||||
}: {
|
||||
mode?: 'build' | 'preview'
|
||||
previewEnabled?: boolean
|
||||
onModeChange?: (mode: 'build' | 'preview') => void
|
||||
onOpenVersions?: () => void
|
||||
onRestart?: () => void
|
||||
onRefresh?: () => void
|
||||
} = {}) {
|
||||
const queryClient = new QueryClient()
|
||||
queryClient.setQueryData(['agent'], {
|
||||
debug_conversation_id: 'debug-conversation-old',
|
||||
name: 'Research Agent',
|
||||
})
|
||||
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AgentPreviewHeader
|
||||
agentId="agent-1"
|
||||
mode={mode}
|
||||
isChatFeaturesOpen={false}
|
||||
onModeChange={onModeChange}
|
||||
onToggleChatFeatures={vi.fn()}
|
||||
onOpenVersions={onOpenVersions}
|
||||
onRestart={onRestart}
|
||||
/>
|
||||
</QueryClientProvider>,
|
||||
<AgentPreviewHeader
|
||||
mode={mode}
|
||||
previewEnabled={previewEnabled}
|
||||
isChatFeaturesOpen={false}
|
||||
onModeChange={onModeChange}
|
||||
onToggleChatFeatures={vi.fn()}
|
||||
onOpenVersions={onOpenVersions}
|
||||
onRefresh={onRefresh}
|
||||
/>,
|
||||
)
|
||||
|
||||
return {
|
||||
queryClient,
|
||||
}
|
||||
}
|
||||
|
||||
describe('AgentPreviewHeader', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.refreshDebugConversation.mockResolvedValue({
|
||||
debug_conversation_id: 'debug-conversation-new',
|
||||
})
|
||||
})
|
||||
|
||||
it('should refresh debug conversation before clearing preview chat', async () => {
|
||||
const onRestart = vi.fn()
|
||||
const { queryClient } = renderHeader({ onRestart })
|
||||
it('should emit refresh from the restart button', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onRefresh = vi.fn()
|
||||
renderHeader({ mode: 'build', onRefresh })
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'agentV2.agentDetail.configure.preview.restart' }))
|
||||
await user.click(screen.getByRole('button', { name: 'agentV2.agentDetail.configure.preview.restart' }))
|
||||
|
||||
await waitFor(() => expect(mocks.refreshDebugConversation).toHaveBeenCalledWith({
|
||||
params: {
|
||||
agent_id: 'agent-1',
|
||||
},
|
||||
}, expect.any(Object)))
|
||||
expect(queryClient.getQueryData(['agent'])).toEqual(expect.objectContaining({
|
||||
debug_conversation_id: 'debug-conversation-new',
|
||||
}))
|
||||
expect(onRestart).toHaveBeenCalledTimes(1)
|
||||
expect(onRefresh).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should disable preview mode when preview is unavailable', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onModeChange = vi.fn()
|
||||
renderHeader({
|
||||
mode: 'build',
|
||||
previewEnabled: false,
|
||||
onModeChange,
|
||||
})
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.rightPanel\.preview/ }))
|
||||
|
||||
expect(onModeChange).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@ -405,10 +405,11 @@ export type AgentChatRuntimeProps = {
|
||||
agentName?: string
|
||||
agentSoulConfig?: AgentSoulConfig
|
||||
clearChatList: boolean
|
||||
debugConversationId?: string | null
|
||||
conversationId?: string | null
|
||||
inputPlaceholder: string
|
||||
renderEmptyState: (props: AgentChatRuntimeEmptyStateProps) => ReactNode
|
||||
onClearChatListChange: (clearChatList: boolean) => void
|
||||
onConversationIdChange?: (conversationId: string) => void
|
||||
onSaveDraftBeforeRun?: () => Promise<void>
|
||||
}
|
||||
|
||||
@ -420,23 +421,24 @@ export function AgentChatRuntime({
|
||||
agentName,
|
||||
agentSoulConfig,
|
||||
clearChatList,
|
||||
debugConversationId,
|
||||
conversationId,
|
||||
inputPlaceholder,
|
||||
renderEmptyState,
|
||||
onClearChatListChange,
|
||||
onConversationIdChange,
|
||||
onSaveDraftBeforeRun,
|
||||
}: AgentChatRuntimeProps) {
|
||||
const historyQuery = useQuery({
|
||||
queryKey: ['agent-preview-debug-conversation', agentId, debugConversationId],
|
||||
queryFn: () => fetchAgentConversationMessages(agentId, debugConversationId!),
|
||||
enabled: !!debugConversationId,
|
||||
queryKey: ['agent-chat-conversation-messages', agentId, conversationId],
|
||||
queryFn: () => fetchAgentConversationMessages(agentId, conversationId!),
|
||||
enabled: !!conversationId,
|
||||
})
|
||||
const initialChatTree = useMemo(
|
||||
() => getFormattedAgentDebugChatTree(historyQuery.data?.data ?? []),
|
||||
[historyQuery.data?.data],
|
||||
)
|
||||
|
||||
if (debugConversationId && historyQuery.isPending) {
|
||||
if (conversationId && historyQuery.isPending) {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<Loading type="app" />
|
||||
@ -446,7 +448,7 @@ export function AgentChatRuntime({
|
||||
|
||||
return (
|
||||
<AgentPreviewChatSession
|
||||
key={`${debugConversationId ?? 'new'}-${historyQuery.dataUpdatedAt}`}
|
||||
key={`${conversationId ?? 'new'}-${historyQuery.dataUpdatedAt}`}
|
||||
agentId={agentId}
|
||||
agentIcon={agentIcon}
|
||||
agentIconBackground={agentIconBackground}
|
||||
@ -454,11 +456,12 @@ export function AgentChatRuntime({
|
||||
agentName={agentName}
|
||||
agentSoulConfig={agentSoulConfig}
|
||||
clearChatList={clearChatList}
|
||||
debugConversationId={debugConversationId}
|
||||
conversationId={conversationId}
|
||||
initialChatTree={initialChatTree}
|
||||
inputPlaceholder={inputPlaceholder}
|
||||
renderEmptyState={renderEmptyState}
|
||||
onClearChatListChange={onClearChatListChange}
|
||||
onConversationIdChange={onConversationIdChange}
|
||||
onSaveDraftBeforeRun={onSaveDraftBeforeRun}
|
||||
/>
|
||||
)
|
||||
@ -472,11 +475,12 @@ function AgentPreviewChatSession({
|
||||
agentName,
|
||||
agentSoulConfig,
|
||||
clearChatList,
|
||||
debugConversationId,
|
||||
conversationId,
|
||||
initialChatTree,
|
||||
inputPlaceholder,
|
||||
renderEmptyState,
|
||||
onClearChatListChange,
|
||||
onConversationIdChange,
|
||||
onSaveDraftBeforeRun,
|
||||
}: {
|
||||
agentId: string
|
||||
@ -486,11 +490,12 @@ function AgentPreviewChatSession({
|
||||
agentName?: string
|
||||
agentSoulConfig?: AgentSoulConfig
|
||||
clearChatList: boolean
|
||||
debugConversationId?: string | null
|
||||
conversationId?: string | null
|
||||
initialChatTree: ChatItemInTree[]
|
||||
inputPlaceholder: string
|
||||
renderEmptyState: (props: AgentChatRuntimeEmptyStateProps) => ReactNode
|
||||
onClearChatListChange: (clearChatList: boolean) => void
|
||||
onConversationIdChange?: (conversationId: string) => void
|
||||
onSaveDraftBeforeRun?: () => Promise<void>
|
||||
}) {
|
||||
const { userProfile } = useAppContext()
|
||||
@ -533,7 +538,7 @@ function AgentPreviewChatSession({
|
||||
},
|
||||
clearChatList,
|
||||
onClearChatListChange,
|
||||
debugConversationId ?? undefined,
|
||||
conversationId ?? undefined,
|
||||
)
|
||||
|
||||
const doSend: OnSend = useCallback(async (message, files, isRegenerate = false, parentAnswer: ChatItem | null = null) => {
|
||||
@ -562,9 +567,10 @@ function AgentPreviewChatSession({
|
||||
{
|
||||
onGetConversationMessages: conversationId => fetchAgentConversationMessages(agentId, conversationId),
|
||||
onGetSuggestedQuestions: responseItemId => fetchAgentSuggestedQuestions(agentId, responseItemId),
|
||||
onConversationComplete: onConversationIdChange,
|
||||
},
|
||||
)
|
||||
}, [agentId, chatList, config.model.name, config.model.provider, handleSend, inputs, onSaveDraftBeforeRun, textGenerationModelList])
|
||||
}, [agentId, chatList, config.model.name, config.model.provider, handleSend, inputs, onConversationIdChange, onSaveDraftBeforeRun, textGenerationModelList])
|
||||
|
||||
const doRegenerate = useCallback((chatItem: ChatItem, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)
|
||||
|
||||
@ -1,50 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import type { AgentAppDetailWithSite } from '@dify/contracts/api/console/agent/types.gen'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { SegmentedControl, SegmentedControlDivider, SegmentedControlItem } from '@langgenius/dify-ui/segmented-control'
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
|
||||
type AgentConfigureRightPanelMode = 'build' | 'preview'
|
||||
|
||||
export function AgentPreviewHeader({
|
||||
agentId,
|
||||
mode,
|
||||
previewEnabled,
|
||||
isChatFeaturesOpen,
|
||||
onModeChange,
|
||||
onToggleChatFeatures,
|
||||
onOpenVersions,
|
||||
onRestart,
|
||||
onRefresh,
|
||||
refreshDisabled,
|
||||
}: {
|
||||
agentId: string
|
||||
mode: AgentConfigureRightPanelMode
|
||||
previewEnabled: boolean
|
||||
isChatFeaturesOpen: boolean
|
||||
onModeChange: (mode: AgentConfigureRightPanelMode) => void
|
||||
onToggleChatFeatures: () => void
|
||||
onOpenVersions: () => void
|
||||
onRestart: () => void
|
||||
onRefresh: () => void
|
||||
refreshDisabled?: boolean
|
||||
}) {
|
||||
const { t } = useTranslation('agentV2')
|
||||
const queryClient = useQueryClient()
|
||||
const refreshDebugConversationMutation = useMutation(consoleQuery.agent.byAgentId.debugConversation.refresh.post.mutationOptions({
|
||||
onSuccess: ({ debug_conversation_id }) => {
|
||||
queryClient.setQueryData<AgentAppDetailWithSite | undefined>(
|
||||
consoleQuery.agent.byAgentId.get.queryKey({ input: { params: { agent_id: agentId } } }),
|
||||
(agentDetail) => {
|
||||
if (!agentDetail)
|
||||
return agentDetail
|
||||
|
||||
return {
|
||||
...agentDetail,
|
||||
debug_conversation_id,
|
||||
}
|
||||
},
|
||||
)
|
||||
onRestart()
|
||||
},
|
||||
}))
|
||||
|
||||
return (
|
||||
<div className="flex h-12 shrink-0 items-center gap-3 py-2 pr-3 pl-4">
|
||||
@ -53,7 +32,7 @@ export function AgentPreviewHeader({
|
||||
value={[mode]}
|
||||
onValueChange={(value) => {
|
||||
const nextMode = value[0]
|
||||
if (nextMode)
|
||||
if (nextMode && (nextMode !== 'preview' || previewEnabled))
|
||||
onModeChange(nextMode)
|
||||
}}
|
||||
aria-label={t('agentDetail.configure.rightPanel.modeLabel')}
|
||||
@ -62,7 +41,11 @@ export function AgentPreviewHeader({
|
||||
<span aria-hidden className="i-ri-hammer-line size-4" />
|
||||
{t('agentDetail.configure.rightPanel.build')}
|
||||
</SegmentedControlItem>
|
||||
<SegmentedControlItem<AgentConfigureRightPanelMode> value="preview" className="uppercase">
|
||||
<SegmentedControlItem<AgentConfigureRightPanelMode>
|
||||
value="preview"
|
||||
disabled={!previewEnabled}
|
||||
className="uppercase"
|
||||
>
|
||||
<span aria-hidden className="i-custom-vender-other-replay-line size-4" />
|
||||
{t('agentDetail.configure.rightPanel.preview')}
|
||||
</SegmentedControlItem>
|
||||
@ -72,12 +55,8 @@ export function AgentPreviewHeader({
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => refreshDebugConversationMutation.mutate({
|
||||
params: {
|
||||
agent_id: agentId,
|
||||
},
|
||||
})}
|
||||
disabled={refreshDebugConversationMutation.isPending}
|
||||
onClick={onRefresh}
|
||||
disabled={refreshDisabled}
|
||||
className="flex size-6 items-center justify-center rounded-md p-0.5 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50"
|
||||
aria-label={t('agentDetail.configure.preview.restart')}
|
||||
>
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import type { AgentIconType, AgentSoulConfig } from '@dify/contracts/api/console/agent/types.gen'
|
||||
import type { AgentAppDetailWithSite, AgentIconType, AgentSoulConfig } from '@dify/contracts/api/console/agent/types.gen'
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { AgentComposerProvider } from '@/features/agent-v2/agent-composer/provider'
|
||||
import { useHydrateAgentSoulConfigDraft } from '@/features/agent-v2/agent-composer/store'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
import { AgentOrchestratePanel } from './components/orchestrate'
|
||||
import { AgentBuildChat } from './components/preview/build-chat'
|
||||
import { AgentChatFeaturesPanel } from './components/preview/chat-features-panel'
|
||||
@ -20,6 +22,15 @@ type AgentConfigurePageProps = {
|
||||
}
|
||||
|
||||
type AgentConfigureRightPanelMode = 'build' | 'preview'
|
||||
type AgentConfigureConversationIds = Record<AgentConfigureRightPanelMode, string | null>
|
||||
type DebugConversationRefreshInput = {
|
||||
params: {
|
||||
agent_id: string
|
||||
}
|
||||
body: {
|
||||
debug_conversation_id: string
|
||||
}
|
||||
}
|
||||
|
||||
export function AgentConfigurePage({
|
||||
agentId,
|
||||
@ -71,6 +82,7 @@ function AgentConfigurePageLoadedContent({
|
||||
onSelectVersion: (versionId: string | null) => void
|
||||
}) {
|
||||
const { t } = useTranslation('agentV2')
|
||||
const queryClient = useQueryClient()
|
||||
const [showChatFeatures, setShowChatFeatures] = useState(false)
|
||||
const [showPreviewVersions, setShowPreviewVersions] = useState(false)
|
||||
const [clearPreviewChat, setClearPreviewChat] = useState(false)
|
||||
@ -86,6 +98,57 @@ function AgentConfigurePageLoadedContent({
|
||||
} = configureData
|
||||
const agentIconType = agentQuery.data?.icon_type as AgentIconType | null | undefined
|
||||
const isViewingVersion = !!selectedVersionId
|
||||
const [conversationIds, setConversationIds] = useState<AgentConfigureConversationIds>({
|
||||
build: agentQuery.data?.debug_conversation_id ?? null,
|
||||
preview: null,
|
||||
})
|
||||
const rightPanelChatMode: AgentConfigureRightPanelMode = rightPanelMode === 'preview' ? 'build' : rightPanelMode
|
||||
const refreshDebugConversationMutation = useMutation(consoleQuery.agent.byAgentId.debugConversation.refresh.post.mutationOptions({
|
||||
onSuccess: ({ debug_conversation_id }) => {
|
||||
queryClient.setQueryData<AgentAppDetailWithSite | undefined>(
|
||||
consoleQuery.agent.byAgentId.get.queryKey({ input: { params: { agent_id: agentId } } }),
|
||||
(agentDetail) => {
|
||||
if (!agentDetail)
|
||||
return agentDetail
|
||||
|
||||
return {
|
||||
...agentDetail,
|
||||
debug_conversation_id,
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
}))
|
||||
const refreshDebugConversation = (conversationId: string) => {
|
||||
const input: DebugConversationRefreshInput = {
|
||||
params: {
|
||||
agent_id: agentId,
|
||||
},
|
||||
body: {
|
||||
debug_conversation_id: conversationId,
|
||||
},
|
||||
}
|
||||
|
||||
refreshDebugConversationMutation.mutate(
|
||||
input as unknown as Parameters<typeof refreshDebugConversationMutation.mutate>[0],
|
||||
)
|
||||
}
|
||||
const updateConversationId = (mode: AgentConfigureRightPanelMode, conversationId: string) => {
|
||||
setConversationIds(current => ({
|
||||
...current,
|
||||
[mode]: conversationId,
|
||||
}))
|
||||
}
|
||||
const restartCurrentChat = () => {
|
||||
if (rightPanelChatMode === 'build')
|
||||
refreshDebugConversation(conversationIds.build ?? '')
|
||||
|
||||
setConversationIds(current => ({
|
||||
...current,
|
||||
[rightPanelChatMode]: null,
|
||||
}))
|
||||
setClearPreviewChat(true)
|
||||
}
|
||||
|
||||
useHydrateAgentSoulConfigDraft({
|
||||
agentId,
|
||||
@ -137,13 +200,14 @@ function AgentConfigurePageLoadedContent({
|
||||
<div className="flex min-w-105 flex-1 gap-1 overflow-hidden">
|
||||
<div className="flex min-w-105 flex-1 flex-col overflow-hidden rounded-lg bg-background-gradient-bg-fill-chat-bg-2 shadow-xl shadow-shadow-shadow-5">
|
||||
<AgentPreviewHeader
|
||||
agentId={agentId}
|
||||
mode={rightPanelMode}
|
||||
mode={rightPanelChatMode}
|
||||
previewEnabled={false}
|
||||
isChatFeaturesOpen={showChatFeatures}
|
||||
onModeChange={setRightPanelMode}
|
||||
onToggleChatFeatures={() => setShowChatFeatures(open => !open)}
|
||||
onOpenVersions={() => setShowPreviewVersions(true)}
|
||||
onRestart={() => setClearPreviewChat(true)}
|
||||
onRefresh={restartCurrentChat}
|
||||
refreshDisabled={refreshDebugConversationMutation.isPending}
|
||||
/>
|
||||
|
||||
<div className="min-h-0 flex-1">
|
||||
@ -155,9 +219,10 @@ function AgentConfigurePageLoadedContent({
|
||||
agentName={agentQuery.data?.name}
|
||||
agentSoulConfig={agentSoulConfig}
|
||||
clearChatList={clearPreviewChat}
|
||||
debugConversationId={agentQuery.data?.debug_conversation_id}
|
||||
mode={rightPanelMode}
|
||||
conversationIds={conversationIds}
|
||||
mode={rightPanelChatMode}
|
||||
onClearChatListChange={setClearPreviewChat}
|
||||
onConversationIdChange={updateConversationId}
|
||||
onSaveDraftBeforeRun={saveDraft}
|
||||
/>
|
||||
</div>
|
||||
@ -184,25 +249,37 @@ function AgentConfigurePageLoadedContent({
|
||||
|
||||
function AgentRightPanelChatWithDraftConfig({
|
||||
agentSoulConfig,
|
||||
conversationIds,
|
||||
mode,
|
||||
onConversationIdChange,
|
||||
...props
|
||||
}: Omit<Parameters<typeof AgentPreviewChat>[0], 'agentSoulConfig'> & {
|
||||
}: Omit<Parameters<typeof AgentPreviewChat>[0], 'agentSoulConfig' | 'conversationId' | 'onConversationIdChange'> & {
|
||||
agentSoulConfig?: AgentSoulConfig
|
||||
conversationIds: AgentConfigureConversationIds
|
||||
mode: AgentConfigureRightPanelMode
|
||||
onConversationIdChange: (mode: AgentConfigureRightPanelMode, conversationId: string) => void
|
||||
}) {
|
||||
const previewAgentSoulConfig = useAgentPreviewSoulConfig(agentSoulConfig)
|
||||
const conversationId = conversationIds[mode]
|
||||
const handleConversationIdChange = (newConversationId: string) => {
|
||||
onConversationIdChange(mode, newConversationId)
|
||||
}
|
||||
|
||||
return mode === 'build'
|
||||
? (
|
||||
<AgentBuildChat
|
||||
{...props}
|
||||
conversationId={conversationId}
|
||||
agentSoulConfig={previewAgentSoulConfig}
|
||||
onConversationIdChange={handleConversationIdChange}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<AgentPreviewChat
|
||||
{...props}
|
||||
conversationId={conversationId}
|
||||
agentSoulConfig={previewAgentSoulConfig}
|
||||
onConversationIdChange={handleConversationIdChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user