mirror of
https://github.com/langgenius/dify.git
synced 2026-06-26 14:51:13 +08:00
feat: build draft
This commit is contained in:
parent
d070074164
commit
69e9fa68db
@ -1,9 +1,13 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { AgentConfigurePage } from '../page'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
applyBuildDraft: vi.fn(),
|
||||
checkoutBuildDraft: vi.fn(),
|
||||
discardBuildDraft: vi.fn(),
|
||||
refreshDebugConversation: vi.fn(),
|
||||
queryState: {
|
||||
agent: {
|
||||
@ -15,21 +19,35 @@ const mocks = vi.hoisted(() => ({
|
||||
name: 'Research Agent',
|
||||
},
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
},
|
||||
composer: {
|
||||
data: undefined as unknown,
|
||||
isFetching: true,
|
||||
isError: false,
|
||||
isPending: true,
|
||||
isSuccess: false,
|
||||
refetch: vi.fn(),
|
||||
},
|
||||
version: {
|
||||
data: undefined as unknown,
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: false,
|
||||
},
|
||||
buildDraft: {
|
||||
data: undefined as unknown,
|
||||
dataUpdatedAt: 0,
|
||||
error: null as unknown,
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: false,
|
||||
refetch: vi.fn(),
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
@ -47,10 +65,13 @@ vi.mock('@tanstack/react-query', async (importOriginal) => {
|
||||
return mocks.queryState.composer
|
||||
if (queryKey === 'version')
|
||||
return mocks.queryState.version
|
||||
if (queryKey === 'build-draft')
|
||||
return mocks.queryState.buildDraft
|
||||
|
||||
return {
|
||||
data: undefined,
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: false,
|
||||
}
|
||||
@ -85,6 +106,24 @@ vi.mock('@/service/client', () => ({
|
||||
mutationOptions: () => ({ mutationFn: vi.fn() }),
|
||||
},
|
||||
},
|
||||
buildDraft: {
|
||||
get: {
|
||||
queryOptions: () => ({ queryKey: ['build-draft'] }),
|
||||
},
|
||||
checkout: {
|
||||
post: {
|
||||
mutationOptions: () => ({ mutationFn: mocks.checkoutBuildDraft }),
|
||||
},
|
||||
},
|
||||
apply: {
|
||||
post: {
|
||||
mutationOptions: () => ({ mutationFn: mocks.applyBuildDraft }),
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
mutationOptions: () => ({ mutationFn: mocks.discardBuildDraft }),
|
||||
},
|
||||
},
|
||||
versions: {
|
||||
byVersionId: {
|
||||
get: {
|
||||
@ -108,7 +147,31 @@ vi.mock('@/app/components/header/account-setting/model-provider-page/hooks', ()
|
||||
}))
|
||||
|
||||
vi.mock('../components/orchestrate', () => ({
|
||||
AgentOrchestratePanel: () => <div role="region" aria-label="orchestrate-panel" />,
|
||||
AgentOrchestratePanel: (props: {
|
||||
bottomBar?: ReactNode
|
||||
readOnly?: boolean
|
||||
showPublishBar?: boolean
|
||||
}) => (
|
||||
<div role="region" aria-label="orchestrate-panel">
|
||||
<span>{`readonly:${props.readOnly ? 'yes' : 'no'}`}</span>
|
||||
<span>{`publish:${props.showPublishBar ? 'yes' : 'no'}`}</span>
|
||||
{props.bottomBar}
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('../components/orchestrate/build-draft-bar', () => ({
|
||||
AgentBuildDraftBar: (props: {
|
||||
changesCount: number
|
||||
onApply: () => void
|
||||
onDiscard: () => void
|
||||
}) => (
|
||||
<div role="region" aria-label="build-draft-bar">
|
||||
<span>{`changes:${props.changesCount}`}</span>
|
||||
<button type="button" onClick={props.onApply}>apply build draft</button>
|
||||
<button type="button" onClick={props.onDiscard}>discard build draft</button>
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('../components/preview/build-chat', () => ({
|
||||
@ -148,6 +211,7 @@ vi.mock('../components/preview/header', () => ({
|
||||
mode: 'build' | 'preview'
|
||||
previewEnabled: boolean
|
||||
onModeChange: (mode: 'build' | 'preview') => void
|
||||
onOpenVersions: () => void
|
||||
onRefresh: () => void
|
||||
}) => (
|
||||
<div>
|
||||
@ -158,6 +222,9 @@ vi.mock('../components/preview/header', () => ({
|
||||
<button type="button" onClick={() => props.onModeChange('build')}>
|
||||
build mode
|
||||
</button>
|
||||
<button type="button" onClick={props.onOpenVersions}>
|
||||
open versions
|
||||
</button>
|
||||
<button type="button" onClick={props.onRefresh}>
|
||||
restart preview
|
||||
</button>
|
||||
@ -177,6 +244,13 @@ describe('AgentConfigurePage', () => {
|
||||
mocks.refreshDebugConversation.mockResolvedValue({
|
||||
debug_conversation_id: 'debug-conversation-new',
|
||||
})
|
||||
mocks.applyBuildDraft.mockResolvedValue({ result: 'success', draft: {} })
|
||||
mocks.checkoutBuildDraft.mockResolvedValue({
|
||||
variant: 'agent_app',
|
||||
draft: {},
|
||||
agent_soul: {},
|
||||
})
|
||||
mocks.discardBuildDraft.mockResolvedValue({ result: 'success' })
|
||||
mocks.queryState.agent = {
|
||||
data: {
|
||||
icon: 'agent',
|
||||
@ -186,21 +260,35 @@ describe('AgentConfigurePage', () => {
|
||||
debug_conversation_id: 'debug-conversation-old',
|
||||
},
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
}
|
||||
mocks.queryState.composer = {
|
||||
data: undefined as unknown,
|
||||
isFetching: true,
|
||||
isError: false,
|
||||
isPending: true,
|
||||
isSuccess: false,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
mocks.queryState.version = {
|
||||
data: undefined as unknown,
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: false,
|
||||
}
|
||||
mocks.queryState.buildDraft = {
|
||||
data: undefined as unknown,
|
||||
dataUpdatedAt: 0,
|
||||
error: null,
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: false,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
describe('Loading state', () => {
|
||||
@ -225,8 +313,10 @@ describe('AgentConfigurePage', () => {
|
||||
mocks.queryState.composer = {
|
||||
data: {},
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
|
||||
render(
|
||||
@ -271,8 +361,10 @@ describe('AgentConfigurePage', () => {
|
||||
mocks.queryState.composer = {
|
||||
data: {},
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
|
||||
render(
|
||||
@ -289,5 +381,249 @@ describe('AgentConfigurePage', () => {
|
||||
expect(screen.getByRole('region', { name: 'build-chat' })).toHaveTextContent('build:debug-conversation-old')
|
||||
expect(screen.queryByRole('region', { name: 'preview-chat', hidden: true })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should stay in normal draft mode when build draft returns 404 even if a debug conversation exists', () => {
|
||||
const queryClient = new QueryClient()
|
||||
mocks.queryState.composer = {
|
||||
data: {
|
||||
agent_soul: {
|
||||
prompt: {
|
||||
system_prompt: 'draft prompt',
|
||||
},
|
||||
},
|
||||
},
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
mocks.queryState.buildDraft = {
|
||||
data: undefined as unknown,
|
||||
dataUpdatedAt: 0,
|
||||
error: new Response(null, { status: 404 }),
|
||||
isFetching: false,
|
||||
isError: true,
|
||||
isPending: false,
|
||||
isSuccess: false,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AgentConfigurePage agentId="agent-1" />
|
||||
</QueryClientProvider>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('region', { name: 'orchestrate-panel' })).toHaveTextContent('readonly:no')
|
||||
expect(screen.getByRole('region', { name: 'orchestrate-panel' })).toHaveTextContent('publish:yes')
|
||||
expect(screen.queryByRole('region', { name: 'build-draft-bar' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should enter build draft mode when build draft data exists', () => {
|
||||
const queryClient = new QueryClient()
|
||||
mocks.queryState.composer = {
|
||||
data: {
|
||||
agent_soul: {
|
||||
prompt: {
|
||||
system_prompt: 'draft prompt',
|
||||
},
|
||||
},
|
||||
},
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
mocks.queryState.buildDraft = {
|
||||
data: {
|
||||
agent_soul: {
|
||||
prompt: {
|
||||
system_prompt: 'build prompt',
|
||||
},
|
||||
},
|
||||
draft: {},
|
||||
variant: 'agent_app',
|
||||
},
|
||||
dataUpdatedAt: 1,
|
||||
error: null,
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AgentConfigurePage agentId="agent-1" />
|
||||
</QueryClientProvider>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('region', { name: 'orchestrate-panel' })).toHaveTextContent('readonly:yes')
|
||||
expect(screen.getByRole('region', { name: 'orchestrate-panel' })).toHaveTextContent('publish:no')
|
||||
expect(screen.getByRole('region', { name: 'build-draft-bar' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should switch soul source to view version when selecting a version from build draft mode', async () => {
|
||||
const user = userEvent.setup()
|
||||
const queryClient = new QueryClient()
|
||||
mocks.queryState.composer = {
|
||||
data: {
|
||||
agent_soul: {
|
||||
prompt: {
|
||||
system_prompt: 'draft prompt',
|
||||
},
|
||||
},
|
||||
},
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
mocks.queryState.buildDraft = {
|
||||
data: {
|
||||
agent_soul: {
|
||||
prompt: {
|
||||
system_prompt: 'build prompt',
|
||||
},
|
||||
},
|
||||
draft: {},
|
||||
variant: 'agent_app',
|
||||
},
|
||||
dataUpdatedAt: 1,
|
||||
error: null,
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AgentConfigurePage agentId="agent-1" />
|
||||
</QueryClientProvider>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('region', { name: 'build-draft-bar' })).toBeInTheDocument()
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'open versions' }))
|
||||
await user.click(screen.getByRole('button', { name: 'select version' }))
|
||||
|
||||
expect(screen.getByRole('region', { name: 'orchestrate-panel' })).toHaveTextContent('readonly:yes')
|
||||
expect(screen.getByRole('region', { name: 'orchestrate-panel' })).toHaveTextContent('publish:yes')
|
||||
expect(screen.queryByRole('region', { name: 'build-draft-bar' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should apply the build draft and start a new build session', async () => {
|
||||
const user = userEvent.setup()
|
||||
const queryClient = new QueryClient()
|
||||
const refetchComposer = vi.fn().mockResolvedValue({})
|
||||
mocks.queryState.composer = {
|
||||
data: {},
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: refetchComposer,
|
||||
}
|
||||
mocks.queryState.buildDraft = {
|
||||
data: {
|
||||
agent_soul: {},
|
||||
draft: {},
|
||||
variant: 'agent_app',
|
||||
},
|
||||
dataUpdatedAt: 1,
|
||||
error: null,
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AgentConfigurePage agentId="agent-1" />
|
||||
</QueryClientProvider>,
|
||||
)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'apply build draft' }))
|
||||
|
||||
await waitFor(() => expect(mocks.applyBuildDraft).toHaveBeenCalledWith(
|
||||
{
|
||||
params: {
|
||||
agent_id: 'agent-1',
|
||||
},
|
||||
},
|
||||
expect.any(Object),
|
||||
))
|
||||
expect(mocks.refreshDebugConversation).toHaveBeenCalledWith({
|
||||
params: {
|
||||
agent_id: 'agent-1',
|
||||
},
|
||||
body: {
|
||||
debug_conversation_id: 'debug-conversation-old',
|
||||
},
|
||||
}, expect.any(Object))
|
||||
expect(refetchComposer).toHaveBeenCalled()
|
||||
expect(screen.getByRole('region', { name: 'build-chat' })).toHaveTextContent('build:none')
|
||||
})
|
||||
|
||||
it('should discard the build draft and start a new build session', async () => {
|
||||
const user = userEvent.setup()
|
||||
const queryClient = new QueryClient()
|
||||
mocks.queryState.composer = {
|
||||
data: {},
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
mocks.queryState.buildDraft = {
|
||||
data: {
|
||||
agent_soul: {},
|
||||
draft: {},
|
||||
variant: 'agent_app',
|
||||
},
|
||||
dataUpdatedAt: 1,
|
||||
error: null,
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
refetch: vi.fn(),
|
||||
}
|
||||
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AgentConfigurePage agentId="agent-1" />
|
||||
</QueryClientProvider>,
|
||||
)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'discard build draft' }))
|
||||
|
||||
await waitFor(() => expect(mocks.discardBuildDraft).toHaveBeenCalledWith(
|
||||
{
|
||||
params: {
|
||||
agent_id: 'agent-1',
|
||||
},
|
||||
},
|
||||
expect.any(Object),
|
||||
))
|
||||
expect(mocks.refreshDebugConversation).toHaveBeenCalledWith({
|
||||
params: {
|
||||
agent_id: 'agent-1',
|
||||
},
|
||||
body: {
|
||||
debug_conversation_id: 'debug-conversation-old',
|
||||
},
|
||||
}, expect.any(Object))
|
||||
expect(screen.getByRole('region', { name: 'build-chat' })).toHaveTextContent('build:none')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { PublishBarBottomActions } from './publish-bar'
|
||||
|
||||
type AgentBuildDraftBarProps = {
|
||||
changesCount: number
|
||||
isApplying?: boolean
|
||||
isDiscarding?: boolean
|
||||
onApply: () => void
|
||||
onDiscard: () => void
|
||||
}
|
||||
|
||||
export function AgentBuildDraftBar({
|
||||
changesCount,
|
||||
isApplying = false,
|
||||
isDiscarding = false,
|
||||
onApply,
|
||||
onDiscard,
|
||||
}: AgentBuildDraftBarProps) {
|
||||
const { t } = useTranslation('agentV2')
|
||||
const { t: tCustom } = useTranslation('custom')
|
||||
const isPending = isApplying || isDiscarding
|
||||
const metaLabel = changesCount > 0
|
||||
? t('agentDetail.configure.buildDraft.changes', { count: changesCount })
|
||||
: t('agentDetail.configure.buildDraft.noChanges')
|
||||
|
||||
return (
|
||||
<PublishBarBottomActions>
|
||||
<div className="pointer-events-auto flex max-w-full min-w-0 items-center gap-2 overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur py-2 pr-2 pl-4 shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]">
|
||||
<div className="flex min-w-0 flex-1 flex-col justify-center gap-0.5 pr-2">
|
||||
<p className="min-w-0 truncate system-sm-semibold text-text-primary">
|
||||
{t('agentDetail.configure.buildDraft.title')}
|
||||
</p>
|
||||
<p className="min-w-0 truncate system-xs-regular text-text-tertiary">
|
||||
{metaLabel}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="primary"
|
||||
loading={isApplying}
|
||||
disabled={isPending}
|
||||
className="h-8 rounded-lg px-3"
|
||||
onClick={onApply}
|
||||
>
|
||||
{tCustom('apply')}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
loading={isDiscarding}
|
||||
disabled={isPending}
|
||||
className="h-8 rounded-lg px-3"
|
||||
onClick={onDiscard}
|
||||
>
|
||||
{t('agentDetail.configure.buildDraft.discard')}
|
||||
</Button>
|
||||
</div>
|
||||
</PublishBarBottomActions>
|
||||
)
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { AgentConfigSnapshotDetailResponse, AgentConfigSnapshotSummaryResponse } from '@dify/contracts/api/console/agent/types.gen'
|
||||
import type { ReactNode } from 'react'
|
||||
import type { DefaultModel, Model } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { ScrollArea } from '@langgenius/dify-ui/scroll-area'
|
||||
@ -36,6 +37,7 @@ type AgentOrchestratePanelProps = {
|
||||
selectedVersionSnapshot?: AgentConfigSnapshotSummaryResponse | null
|
||||
showHeader?: boolean
|
||||
showPublishBar?: boolean
|
||||
bottomBar?: ReactNode
|
||||
onSelectModel: (model: DefaultModel) => void
|
||||
onPublish: () => void | Promise<void>
|
||||
onExitVersions?: () => void
|
||||
@ -59,6 +61,7 @@ export function AgentOrchestratePanel({
|
||||
selectedVersionSnapshot,
|
||||
showHeader = true,
|
||||
showPublishBar = true,
|
||||
bottomBar,
|
||||
onSelectModel,
|
||||
onPublish,
|
||||
onExitVersions,
|
||||
@ -67,6 +70,7 @@ export function AgentOrchestratePanel({
|
||||
const { t } = useTranslation('agentV2')
|
||||
const orchestrateHeadingId = 'agent-configure-orchestrate-heading'
|
||||
const orchestrateLabel = t('agentDetail.configure.orchestrate')
|
||||
const hasBottomBar = showPublishBar || !!bottomBar
|
||||
const driveApiContext = useMemo(() => appId && nodeId
|
||||
? {
|
||||
agentId,
|
||||
@ -92,8 +96,8 @@ export function AgentOrchestratePanel({
|
||||
labelledBy={showHeader ? orchestrateHeadingId : undefined}
|
||||
slotClassNames={{
|
||||
viewport: 'overscroll-contain',
|
||||
content: cn('min-h-full px-4 py-3', showPublishBar && 'pb-20'),
|
||||
scrollbar: showPublishBar ? 'z-20' : undefined,
|
||||
content: cn('min-h-full px-4 py-3', hasBottomBar && 'pb-20'),
|
||||
scrollbar: hasBottomBar ? 'z-20' : undefined,
|
||||
}}
|
||||
>
|
||||
<AgentDriveApiContextProvider value={driveApiContext}>
|
||||
@ -115,7 +119,8 @@ export function AgentOrchestratePanel({
|
||||
</div>
|
||||
</AgentOrchestrateReadOnlyContext>
|
||||
|
||||
{showPublishBar && (
|
||||
{bottomBar}
|
||||
{showPublishBar && !bottomBar && (
|
||||
<AgentConfigurePublishBar
|
||||
agentId={agentId}
|
||||
activeConfigIsPublished={activeConfigIsPublished}
|
||||
|
||||
@ -303,7 +303,7 @@ export function AgentConfigurePublishBar({
|
||||
)
|
||||
}
|
||||
|
||||
function PublishBarBottomActions({
|
||||
export function PublishBarBottomActions({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode
|
||||
|
||||
@ -270,6 +270,23 @@ describe('AgentPreviewChat', () => {
|
||||
expect(handleSendMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should send build chat with the debug build draft type', async () => {
|
||||
renderPreviewChat({
|
||||
draftType: 'debug_build',
|
||||
})
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'send' }))
|
||||
|
||||
await waitFor(() => expect(handleSendMock).toHaveBeenCalledTimes(1))
|
||||
expect(handleSendMock).toHaveBeenCalledWith(
|
||||
'agent/agent-1/chat-messages',
|
||||
expect.objectContaining({
|
||||
draft_type: 'debug_build',
|
||||
}),
|
||||
expect.any(Object),
|
||||
)
|
||||
})
|
||||
|
||||
it('should keep preview file upload disabled by default', async () => {
|
||||
renderPreviewChat()
|
||||
|
||||
|
||||
@ -444,10 +444,12 @@ export type AgentChatRuntimeProps = {
|
||||
agentSoulConfig?: AgentSoulConfig
|
||||
clearChatList: boolean
|
||||
conversationId?: string | null
|
||||
draftType?: 'debug_build'
|
||||
inputPlaceholder: string
|
||||
sendButtonLabel?: string
|
||||
renderEmptyState: (props: AgentChatRuntimeEmptyStateProps) => ReactNode
|
||||
onClearChatListChange: (clearChatList: boolean) => void
|
||||
onConversationComplete?: (conversationId: string) => void
|
||||
onConversationIdChange?: (conversationId: string) => void
|
||||
onSaveDraftBeforeRun?: () => Promise<void>
|
||||
}
|
||||
@ -461,10 +463,12 @@ export function AgentChatRuntime({
|
||||
agentSoulConfig,
|
||||
clearChatList,
|
||||
conversationId,
|
||||
draftType,
|
||||
inputPlaceholder,
|
||||
sendButtonLabel,
|
||||
renderEmptyState,
|
||||
onClearChatListChange,
|
||||
onConversationComplete,
|
||||
onConversationIdChange,
|
||||
onSaveDraftBeforeRun,
|
||||
}: AgentChatRuntimeProps) {
|
||||
@ -497,11 +501,13 @@ export function AgentChatRuntime({
|
||||
agentSoulConfig={agentSoulConfig}
|
||||
clearChatList={clearChatList}
|
||||
conversationId={conversationId}
|
||||
draftType={draftType}
|
||||
initialChatTree={initialChatTree}
|
||||
inputPlaceholder={inputPlaceholder}
|
||||
sendButtonLabel={sendButtonLabel}
|
||||
renderEmptyState={renderEmptyState}
|
||||
onClearChatListChange={onClearChatListChange}
|
||||
onConversationComplete={onConversationComplete}
|
||||
onConversationIdChange={onConversationIdChange}
|
||||
onSaveDraftBeforeRun={onSaveDraftBeforeRun}
|
||||
/>
|
||||
@ -517,11 +523,13 @@ function AgentPreviewChatSession({
|
||||
agentSoulConfig,
|
||||
clearChatList,
|
||||
conversationId,
|
||||
draftType,
|
||||
initialChatTree,
|
||||
inputPlaceholder,
|
||||
sendButtonLabel,
|
||||
renderEmptyState,
|
||||
onClearChatListChange,
|
||||
onConversationComplete,
|
||||
onConversationIdChange,
|
||||
onSaveDraftBeforeRun,
|
||||
}: {
|
||||
@ -533,11 +541,13 @@ function AgentPreviewChatSession({
|
||||
agentSoulConfig?: AgentSoulConfig
|
||||
clearChatList: boolean
|
||||
conversationId?: string | null
|
||||
draftType?: 'debug_build'
|
||||
initialChatTree: ChatItemInTree[]
|
||||
inputPlaceholder: string
|
||||
sendButtonLabel?: string
|
||||
renderEmptyState: (props: AgentChatRuntimeEmptyStateProps) => ReactNode
|
||||
onClearChatListChange: (clearChatList: boolean) => void
|
||||
onConversationComplete?: (conversationId: string) => void
|
||||
onConversationIdChange?: (conversationId: string) => void
|
||||
onSaveDraftBeforeRun?: () => Promise<void>
|
||||
}) {
|
||||
@ -600,6 +610,8 @@ function AgentPreviewChatSession({
|
||||
inputs,
|
||||
parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || null,
|
||||
}
|
||||
if (draftType)
|
||||
data.draft_type = draftType
|
||||
|
||||
if (files?.length && supportVision)
|
||||
data.files = files
|
||||
@ -610,10 +622,13 @@ function AgentPreviewChatSession({
|
||||
{
|
||||
onGetConversationMessages: conversationId => fetchAgentConversationMessages(agentId, conversationId),
|
||||
onGetSuggestedQuestions: responseItemId => fetchAgentSuggestedQuestions(agentId, responseItemId),
|
||||
onConversationComplete: onConversationIdChange,
|
||||
onConversationComplete: (conversationId) => {
|
||||
onConversationIdChange?.(conversationId)
|
||||
onConversationComplete?.(conversationId)
|
||||
},
|
||||
},
|
||||
)
|
||||
}, [agentId, chatList, config.model.name, config.model.provider, handleSend, inputs, onConversationIdChange, onSaveDraftBeforeRun, textGenerationModelList])
|
||||
}, [agentId, chatList, config.model.name, config.model.provider, draftType, handleSend, inputs, onConversationComplete, onConversationIdChange, onSaveDraftBeforeRun, textGenerationModelList])
|
||||
|
||||
const doRegenerate = useCallback((chatItem: ChatItem, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)
|
||||
|
||||
@ -2,13 +2,14 @@
|
||||
|
||||
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 { useCallback, 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 { AgentBuildDraftBar } from './components/orchestrate/build-draft-bar'
|
||||
import { AgentBuildPanelBackground } from './components/preview/build-background'
|
||||
import { AgentBuildChat } from './components/preview/build-chat'
|
||||
import { AgentChatFeaturesPanel } from './components/preview/chat-features-panel'
|
||||
@ -16,6 +17,7 @@ import { AgentPreviewHeader } from './components/preview/header'
|
||||
import { AgentPreviewChat } from './components/preview/preview-chat'
|
||||
import { AgentPreviewVersionsPanel } from './components/preview/versions-panel'
|
||||
import { useAgentConfigureData, useAgentConfigureModelOptions, useAgentPreviewSoulConfig } from './hooks'
|
||||
import { useAgentConfigureBuildDraftActions, useAgentConfigureBuildDraftData } from './use-agent-configure-build-draft'
|
||||
import { useAgentConfigureSync } from './use-agent-configure-sync'
|
||||
|
||||
type AgentConfigurePageProps = {
|
||||
@ -104,6 +106,13 @@ function AgentConfigurePageLoadedContent({
|
||||
preview: null,
|
||||
})
|
||||
const rightPanelChatMode: AgentConfigureRightPanelMode = rightPanelMode === 'preview' ? 'build' : rightPanelMode
|
||||
const buildDraft = useAgentConfigureBuildDraftData({
|
||||
agentId,
|
||||
activeVersionId,
|
||||
composerAgentSoulConfig: composerQuery.data?.agent_soul,
|
||||
isViewingVersion,
|
||||
normalAgentSoulConfig: agentSoulConfig,
|
||||
})
|
||||
const refreshDebugConversationMutation = useMutation(consoleQuery.agent.byAgentId.debugConversation.refresh.post.mutationOptions({
|
||||
onSuccess: ({ debug_conversation_id }) => {
|
||||
queryClient.setQueryData<AgentAppDetailWithSite | undefined>(
|
||||
@ -120,20 +129,33 @@ function AgentConfigurePageLoadedContent({
|
||||
)
|
||||
},
|
||||
}))
|
||||
const refreshDebugConversation = (conversationId: string) => {
|
||||
const input: DebugConversationRefreshInput = {
|
||||
params: {
|
||||
agent_id: agentId,
|
||||
},
|
||||
body: {
|
||||
debug_conversation_id: conversationId,
|
||||
},
|
||||
}
|
||||
const {
|
||||
mutate: refreshDebugConversationRequest,
|
||||
mutateAsync: refreshDebugConversationRequestAsync,
|
||||
isPending: isRefreshingDebugConversation,
|
||||
} = refreshDebugConversationMutation
|
||||
const refreshDebugConversationInput = useCallback((conversationId: string): DebugConversationRefreshInput => ({
|
||||
params: {
|
||||
agent_id: agentId,
|
||||
},
|
||||
body: {
|
||||
debug_conversation_id: conversationId,
|
||||
},
|
||||
}), [agentId])
|
||||
const refreshDebugConversation = useCallback((conversationId: string) => {
|
||||
const input = refreshDebugConversationInput(conversationId)
|
||||
|
||||
refreshDebugConversationMutation.mutate(
|
||||
input as unknown as Parameters<typeof refreshDebugConversationMutation.mutate>[0],
|
||||
refreshDebugConversationRequest(
|
||||
input as unknown as Parameters<typeof refreshDebugConversationRequest>[0],
|
||||
)
|
||||
}
|
||||
}, [refreshDebugConversationInput, refreshDebugConversationRequest])
|
||||
const refreshDebugConversationAsync = useCallback((conversationId: string) => {
|
||||
const input = refreshDebugConversationInput(conversationId)
|
||||
|
||||
return refreshDebugConversationRequestAsync(
|
||||
input as unknown as Parameters<typeof refreshDebugConversationRequestAsync>[0],
|
||||
)
|
||||
}, [refreshDebugConversationInput, refreshDebugConversationRequestAsync])
|
||||
const updateConversationId = (mode: AgentConfigureRightPanelMode, conversationId: string) => {
|
||||
setConversationIds(current => ({
|
||||
...current,
|
||||
@ -150,11 +172,19 @@ function AgentConfigurePageLoadedContent({
|
||||
}))
|
||||
setClearPreviewChat(true)
|
||||
}
|
||||
const resetBuildChatSession = useCallback(async () => {
|
||||
await refreshDebugConversationAsync(conversationIds.build ?? '')
|
||||
setConversationIds(current => ({
|
||||
...current,
|
||||
build: null,
|
||||
}))
|
||||
setClearPreviewChat(true)
|
||||
}, [conversationIds.build, refreshDebugConversationAsync])
|
||||
|
||||
useHydrateAgentSoulConfigDraft({
|
||||
agentId,
|
||||
activeVersionId,
|
||||
config: agentSoulConfig,
|
||||
activeVersionId: buildDraft.activeVersionId,
|
||||
config: buildDraft.agentSoulConfig,
|
||||
})
|
||||
const {
|
||||
currentModel,
|
||||
@ -170,8 +200,33 @@ function AgentConfigurePageLoadedContent({
|
||||
agentId,
|
||||
baseConfig: agentSoulConfig,
|
||||
currentModel,
|
||||
enabled: composerQuery.isSuccess && !selectedVersionId,
|
||||
enabled: composerQuery.isSuccess && !selectedVersionId && !buildDraft.isActive,
|
||||
})
|
||||
const buildDraftActions = useAgentConfigureBuildDraftActions({
|
||||
agentId,
|
||||
isActive: buildDraft.isActive,
|
||||
refetchBuildDraft: buildDraft.refetch,
|
||||
refetchComposer: composerQuery.refetch,
|
||||
resetBuildChatSession,
|
||||
saveDraft,
|
||||
setSoulSourceOverride: buildDraft.setSoulSourceOverride,
|
||||
})
|
||||
const selectVersion = useCallback((versionId: string | null) => {
|
||||
buildDraft.setSoulSourceOverride(versionId ? 'view-version' : null)
|
||||
onSelectVersion(versionId)
|
||||
}, [buildDraft, onSelectVersion])
|
||||
|
||||
if (buildDraft.isPending) {
|
||||
return (
|
||||
<section
|
||||
aria-label={t('agentDetail.sections.configure')}
|
||||
aria-busy
|
||||
className="flex h-full min-w-0 flex-1 items-center justify-center p-1"
|
||||
>
|
||||
<Loading type="app" />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<section
|
||||
@ -183,18 +238,34 @@ function AgentConfigurePageLoadedContent({
|
||||
agentId={agentId}
|
||||
activeConfigIsPublished={agentQuery.data?.active_config_is_published}
|
||||
activeConfigSnapshot={activeConfigSnapshot}
|
||||
agentSoulConfig={agentSoulConfig}
|
||||
agentSoulConfig={buildDraft.agentSoulConfig}
|
||||
agentName={agentQuery.data?.name}
|
||||
currentModel={currentModel}
|
||||
textGenerationModelList={textGenerationModelList}
|
||||
draftSavedAt={draftSavedAt}
|
||||
isPublishing={isPublishing}
|
||||
readOnly={isViewingVersion}
|
||||
readOnly={isViewingVersion || buildDraft.isActive}
|
||||
selectedVersionSnapshot={isViewingVersion ? activeConfigSnapshot : undefined}
|
||||
showPublishBar={!buildDraft.isActive}
|
||||
bottomBar={buildDraft.isActive
|
||||
? (
|
||||
<AgentBuildDraftBar
|
||||
changesCount={buildDraft.changesCount}
|
||||
isApplying={buildDraftActions.isApplyingBuildDraft}
|
||||
isDiscarding={buildDraftActions.isDiscardingBuildDraft}
|
||||
onApply={() => {
|
||||
void buildDraftActions.applyBuildDraft()
|
||||
}}
|
||||
onDiscard={() => {
|
||||
void buildDraftActions.discardBuildDraft()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
: undefined}
|
||||
onSelectModel={setConfigureModel}
|
||||
onPublish={publishDraft}
|
||||
onOpenVersions={() => setShowPreviewVersions(true)}
|
||||
onExitVersions={() => onSelectVersion(null)}
|
||||
onExitVersions={() => selectVersion(null)}
|
||||
/>
|
||||
|
||||
{/* Preview area */}
|
||||
@ -209,7 +280,7 @@ function AgentConfigurePageLoadedContent({
|
||||
onToggleChatFeatures={() => setShowChatFeatures(open => !open)}
|
||||
onOpenVersions={() => setShowPreviewVersions(true)}
|
||||
onRefresh={restartCurrentChat}
|
||||
refreshDisabled={refreshDebugConversationMutation.isPending}
|
||||
refreshDisabled={isRefreshingDebugConversation}
|
||||
/>
|
||||
|
||||
<div className="relative z-1 min-h-0 flex-1">
|
||||
@ -219,13 +290,15 @@ function AgentConfigurePageLoadedContent({
|
||||
agentIconBackground={agentQuery.data?.icon_background}
|
||||
agentIconType={agentIconType}
|
||||
agentName={agentQuery.data?.name}
|
||||
agentSoulConfig={agentSoulConfig}
|
||||
agentSoulConfig={buildDraft.agentSoulConfig}
|
||||
clearChatList={clearPreviewChat}
|
||||
conversationIds={conversationIds}
|
||||
draftType={rightPanelChatMode === 'build' ? 'debug_build' : undefined}
|
||||
mode={rightPanelChatMode}
|
||||
onClearChatListChange={setClearPreviewChat}
|
||||
onConversationComplete={buildDraftActions.refreshBuildDraftAfterBuildChat}
|
||||
onConversationIdChange={updateConversationId}
|
||||
onSaveDraftBeforeRun={saveDraft}
|
||||
onSaveDraftBeforeRun={rightPanelChatMode === 'build' ? buildDraftActions.prepareBuildDraftBeforeRun : saveDraft}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -234,7 +307,7 @@ function AgentConfigurePageLoadedContent({
|
||||
<AgentPreviewVersionsPanel
|
||||
agentId={agentId}
|
||||
activeVersionId={activeVersionId}
|
||||
onSelectVersion={onSelectVersion}
|
||||
onSelectVersion={selectVersion}
|
||||
onClose={() => setShowPreviewVersions(false)}
|
||||
/>
|
||||
)}
|
||||
@ -253,12 +326,14 @@ function AgentRightPanelChatWithDraftConfig({
|
||||
agentSoulConfig,
|
||||
conversationIds,
|
||||
mode,
|
||||
onConversationComplete,
|
||||
onConversationIdChange,
|
||||
...props
|
||||
}: Omit<Parameters<typeof AgentPreviewChat>[0], 'agentSoulConfig' | 'conversationId' | 'onConversationIdChange'> & {
|
||||
}: Omit<Parameters<typeof AgentPreviewChat>[0], 'agentSoulConfig' | 'conversationId' | 'onConversationComplete' | 'onConversationIdChange'> & {
|
||||
agentSoulConfig?: AgentSoulConfig
|
||||
conversationIds: AgentConfigureConversationIds
|
||||
mode: AgentConfigureRightPanelMode
|
||||
onConversationComplete?: (mode: AgentConfigureRightPanelMode) => void
|
||||
onConversationIdChange: (mode: AgentConfigureRightPanelMode, conversationId: string) => void
|
||||
}) {
|
||||
const previewAgentSoulConfig = useAgentPreviewSoulConfig(agentSoulConfig)
|
||||
@ -266,6 +341,9 @@ function AgentRightPanelChatWithDraftConfig({
|
||||
const handleConversationIdChange = (newConversationId: string) => {
|
||||
onConversationIdChange(mode, newConversationId)
|
||||
}
|
||||
const handleConversationComplete = () => {
|
||||
onConversationComplete?.(mode)
|
||||
}
|
||||
|
||||
return mode === 'build'
|
||||
? (
|
||||
@ -273,6 +351,7 @@ function AgentRightPanelChatWithDraftConfig({
|
||||
{...props}
|
||||
conversationId={conversationId}
|
||||
agentSoulConfig={previewAgentSoulConfig}
|
||||
onConversationComplete={handleConversationComplete}
|
||||
onConversationIdChange={handleConversationIdChange}
|
||||
/>
|
||||
)
|
||||
@ -281,6 +360,7 @@ function AgentRightPanelChatWithDraftConfig({
|
||||
{...props}
|
||||
conversationId={conversationId}
|
||||
agentSoulConfig={previewAgentSoulConfig}
|
||||
onConversationComplete={handleConversationComplete}
|
||||
onConversationIdChange={handleConversationIdChange}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -0,0 +1,205 @@
|
||||
'use client'
|
||||
|
||||
import type { AgentSoulConfig } from '@dify/contracts/api/console/agent/types.gen'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { agentSoulConfigToFormState } from '@/features/agent-v2/agent-composer/conversions'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
|
||||
export type AgentConfigureSoulSource = 'draft' | 'build-draft' | 'view-version'
|
||||
|
||||
const isNotFoundResponse = (error: unknown) => error instanceof Response && error.status === 404
|
||||
|
||||
export function useAgentConfigureBuildDraftData({
|
||||
agentId,
|
||||
activeVersionId,
|
||||
composerAgentSoulConfig,
|
||||
isViewingVersion,
|
||||
normalAgentSoulConfig,
|
||||
}: {
|
||||
agentId: string
|
||||
activeVersionId: string | null | undefined
|
||||
composerAgentSoulConfig?: AgentSoulConfig
|
||||
isViewingVersion: boolean
|
||||
normalAgentSoulConfig?: AgentSoulConfig
|
||||
}) {
|
||||
const [soulSourceOverride, setSoulSourceOverride] = useState<AgentConfigureSoulSource | null>(null)
|
||||
const buildDraftQueryOptions = consoleQuery.agent.byAgentId.buildDraft.get.queryOptions({
|
||||
input: {
|
||||
params: {
|
||||
agent_id: agentId,
|
||||
},
|
||||
},
|
||||
})
|
||||
const buildDraftQuery = useQuery({
|
||||
...buildDraftQueryOptions,
|
||||
enabled: !isViewingVersion && soulSourceOverride !== 'draft' && soulSourceOverride !== 'view-version',
|
||||
queryFn: async (context) => {
|
||||
try {
|
||||
return await buildDraftQueryOptions.queryFn(context)
|
||||
}
|
||||
catch (error) {
|
||||
if (isNotFoundResponse(error))
|
||||
setSoulSourceOverride('draft')
|
||||
throw error
|
||||
}
|
||||
},
|
||||
retry: false,
|
||||
})
|
||||
const {
|
||||
data: buildDraftData,
|
||||
dataUpdatedAt: buildDraftDataUpdatedAt,
|
||||
error: buildDraftError,
|
||||
isError: isBuildDraftError,
|
||||
isPending: isBuildDraftPending,
|
||||
refetch: refetchBuildDraft,
|
||||
} = buildDraftQuery
|
||||
const buildDraftNotFound = isNotFoundResponse(buildDraftError)
|
||||
const soulSource: AgentConfigureSoulSource = isViewingVersion
|
||||
? 'view-version'
|
||||
: soulSourceOverride ?? (!buildDraftNotFound && !!buildDraftData && !isBuildDraftError ? 'build-draft' : 'draft')
|
||||
const isBuildDraftActive = soulSource === 'build-draft'
|
||||
const buildDraftAgentSoulConfig = buildDraftData?.agent_soul as AgentSoulConfig | undefined
|
||||
const visibleAgentSoulConfig = isBuildDraftActive ? buildDraftAgentSoulConfig : normalAgentSoulConfig
|
||||
const buildDraftChangesCount = useMemo(() => {
|
||||
if (!buildDraftAgentSoulConfig || !composerAgentSoulConfig)
|
||||
return 0
|
||||
|
||||
const normalDraft = agentSoulConfigToFormState(composerAgentSoulConfig)
|
||||
const buildDraft = agentSoulConfigToFormState(buildDraftAgentSoulConfig)
|
||||
|
||||
return (Object.keys(buildDraft) as Array<keyof typeof buildDraft>)
|
||||
.filter(key => JSON.stringify(buildDraft[key]) !== JSON.stringify(normalDraft[key]))
|
||||
.length
|
||||
}, [buildDraftAgentSoulConfig, composerAgentSoulConfig])
|
||||
|
||||
return {
|
||||
activeVersionId: isBuildDraftActive ? `build-draft:${buildDraftDataUpdatedAt}` : activeVersionId,
|
||||
agentSoulConfig: visibleAgentSoulConfig,
|
||||
changesCount: buildDraftChangesCount,
|
||||
isActive: isBuildDraftActive,
|
||||
isPending: !isViewingVersion && soulSourceOverride !== 'draft' && soulSourceOverride !== 'view-version' && isBuildDraftPending,
|
||||
refetch: refetchBuildDraft,
|
||||
setSoulSourceOverride,
|
||||
soulSource,
|
||||
}
|
||||
}
|
||||
|
||||
export function useAgentConfigureBuildDraftActions({
|
||||
agentId,
|
||||
isActive,
|
||||
refetchBuildDraft,
|
||||
refetchComposer,
|
||||
resetBuildChatSession,
|
||||
saveDraft,
|
||||
setSoulSourceOverride,
|
||||
}: {
|
||||
agentId: string
|
||||
isActive: boolean
|
||||
refetchBuildDraft: () => Promise<unknown>
|
||||
refetchComposer: () => Promise<unknown>
|
||||
resetBuildChatSession: () => Promise<void>
|
||||
saveDraft: () => Promise<void>
|
||||
setSoulSourceOverride: (source: AgentConfigureSoulSource | null) => void
|
||||
}) {
|
||||
const { t: tCommon } = useTranslation('common')
|
||||
const queryClient = useQueryClient()
|
||||
const buildDraftRefreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const buildDraftQueryOptions = consoleQuery.agent.byAgentId.buildDraft.get.queryOptions({
|
||||
input: {
|
||||
params: {
|
||||
agent_id: agentId,
|
||||
},
|
||||
},
|
||||
})
|
||||
const checkoutBuildDraftMutation = useMutation(consoleQuery.agent.byAgentId.buildDraft.checkout.post.mutationOptions())
|
||||
const applyBuildDraftMutation = useMutation(consoleQuery.agent.byAgentId.buildDraft.apply.post.mutationOptions())
|
||||
const discardBuildDraftMutation = useMutation(consoleQuery.agent.byAgentId.buildDraft.delete.mutationOptions())
|
||||
const { mutateAsync: checkoutBuildDraft } = checkoutBuildDraftMutation
|
||||
const { mutateAsync: applyBuildDraftRequest, isPending: isApplyingBuildDraft } = applyBuildDraftMutation
|
||||
const { mutateAsync: discardBuildDraftRequest, isPending: isDiscardingBuildDraft } = discardBuildDraftMutation
|
||||
|
||||
const prepareBuildDraftBeforeRun = useCallback(async () => {
|
||||
if (!isActive)
|
||||
await saveDraft()
|
||||
|
||||
const buildDraft = await checkoutBuildDraft({
|
||||
params: {
|
||||
agent_id: agentId,
|
||||
},
|
||||
body: {
|
||||
force: false,
|
||||
},
|
||||
})
|
||||
queryClient.setQueryData(buildDraftQueryOptions.queryKey, buildDraft)
|
||||
setSoulSourceOverride('build-draft')
|
||||
}, [agentId, buildDraftQueryOptions.queryKey, checkoutBuildDraft, isActive, queryClient, saveDraft, setSoulSourceOverride])
|
||||
|
||||
const refreshBuildDraftAfterBuildChat = useCallback(() => {
|
||||
if (buildDraftRefreshTimerRef.current)
|
||||
clearTimeout(buildDraftRefreshTimerRef.current)
|
||||
|
||||
buildDraftRefreshTimerRef.current = setTimeout(() => {
|
||||
buildDraftRefreshTimerRef.current = null
|
||||
void refetchBuildDraft()
|
||||
}, 1000)
|
||||
}, [refetchBuildDraft])
|
||||
|
||||
const exitBuildDraftMode = useCallback(async (shouldRefetchComposer: boolean) => {
|
||||
setSoulSourceOverride('draft')
|
||||
await resetBuildChatSession()
|
||||
queryClient.removeQueries({
|
||||
queryKey: buildDraftQueryOptions.queryKey,
|
||||
})
|
||||
if (shouldRefetchComposer)
|
||||
await refetchComposer()
|
||||
}, [buildDraftQueryOptions.queryKey, queryClient, refetchComposer, resetBuildChatSession, setSoulSourceOverride])
|
||||
|
||||
const applyBuildDraft = async () => {
|
||||
try {
|
||||
await applyBuildDraftRequest({
|
||||
params: {
|
||||
agent_id: agentId,
|
||||
},
|
||||
})
|
||||
await exitBuildDraftMode(true)
|
||||
toast.success(tCommon('api.actionSuccess'))
|
||||
}
|
||||
catch {
|
||||
toast.error(tCommon('api.actionFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
const discardBuildDraft = async () => {
|
||||
try {
|
||||
await discardBuildDraftRequest({
|
||||
params: {
|
||||
agent_id: agentId,
|
||||
},
|
||||
})
|
||||
await exitBuildDraftMode(false)
|
||||
toast.success(tCommon('api.actionSuccess'))
|
||||
}
|
||||
catch {
|
||||
toast.error(tCommon('api.actionFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (buildDraftRefreshTimerRef.current)
|
||||
clearTimeout(buildDraftRefreshTimerRef.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
applyBuildDraft,
|
||||
discardBuildDraft,
|
||||
isApplyingBuildDraft,
|
||||
isDiscardingBuildDraft,
|
||||
prepareBuildDraftBeforeRun,
|
||||
refreshBuildDraftAfterBuildChat,
|
||||
}
|
||||
}
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "ابنِ وكيلك عبر الدردشة",
|
||||
"agentDetail.configure.build.inputPlaceholder": "صِف ما يجب أن يفعله وكيلك",
|
||||
"agentDetail.configure.build.startBuild": "ابدأ البناء",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} تغييرات للتطبيق",
|
||||
"agentDetail.configure.buildDraft.discard": "تجاهل",
|
||||
"agentDetail.configure.buildDraft.noChanges": "لا توجد تغييرات للتطبيق",
|
||||
"agentDetail.configure.buildDraft.title": "مسودة البناء",
|
||||
"agentDetail.configure.chatFeatures.description": "شكّل تجربة الدردشة للمستخدم النهائي على Web app وأسطح الدردشة.",
|
||||
"agentDetail.configure.chatFeatures.title": "ميزات الدردشة",
|
||||
"agentDetail.configure.files.add": "إضافة ملف",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Agent per Chat erstellen",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Beschreiben Sie, was Ihr Agent tun soll",
|
||||
"agentDetail.configure.build.startBuild": "Build starten",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} Änderungen anzuwenden",
|
||||
"agentDetail.configure.buildDraft.discard": "Verwerfen",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Keine Änderungen anzuwenden",
|
||||
"agentDetail.configure.buildDraft.title": "Build-Entwurf",
|
||||
"agentDetail.configure.chatFeatures.description": "Gestalten Sie das Chat-Erlebnis für Endnutzer in Ihrer Webapp und in Chat-Oberflächen.",
|
||||
"agentDetail.configure.chatFeatures.title": "Chat-Funktionen",
|
||||
"agentDetail.configure.files.add": "Datei hinzufügen",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Build your agent by chatting",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Describe what your agent should do",
|
||||
"agentDetail.configure.build.startBuild": "Start build",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} changes to apply",
|
||||
"agentDetail.configure.buildDraft.discard": "Discard",
|
||||
"agentDetail.configure.buildDraft.noChanges": "No changes to apply",
|
||||
"agentDetail.configure.buildDraft.title": "Build draft",
|
||||
"agentDetail.configure.chatFeatures.description": "Shape the end-user chat experience on your web app and chat surfaces.",
|
||||
"agentDetail.configure.chatFeatures.title": "Chat Features",
|
||||
"agentDetail.configure.files.add": "Add file",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Crea tu agente chateando",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Describe qué debe hacer tu agente",
|
||||
"agentDetail.configure.build.startBuild": "Iniciar compilación",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} cambios por aplicar",
|
||||
"agentDetail.configure.buildDraft.discard": "Descartar",
|
||||
"agentDetail.configure.buildDraft.noChanges": "No hay cambios por aplicar",
|
||||
"agentDetail.configure.buildDraft.title": "Borrador de compilación",
|
||||
"agentDetail.configure.chatFeatures.description": "Da forma a la experiencia de chat del usuario final en tu webapp y superficies de chat.",
|
||||
"agentDetail.configure.chatFeatures.title": "Funciones de chat",
|
||||
"agentDetail.configure.files.add": "Agregar archivo",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "عامل خود را با چت بسازید",
|
||||
"agentDetail.configure.build.inputPlaceholder": "توضیح دهید عامل شما باید چه کاری انجام دهد",
|
||||
"agentDetail.configure.build.startBuild": "شروع ساخت",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} تغییر برای اعمال",
|
||||
"agentDetail.configure.buildDraft.discard": "رد کردن",
|
||||
"agentDetail.configure.buildDraft.noChanges": "تغییری برای اعمال وجود ندارد",
|
||||
"agentDetail.configure.buildDraft.title": "پیش نویس ساخت",
|
||||
"agentDetail.configure.chatFeatures.description": "تجربه چت کاربر نهایی را در Web app و سطوح چت خود شکل دهید.",
|
||||
"agentDetail.configure.chatFeatures.title": "ویژگیهای چت",
|
||||
"agentDetail.configure.files.add": "افزودن فایل",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Créez votre agent par chat",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Décrivez ce que votre agent doit faire",
|
||||
"agentDetail.configure.build.startBuild": "Démarrer la création",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} modifications à appliquer",
|
||||
"agentDetail.configure.buildDraft.discard": "Ignorer",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Aucune modification à appliquer",
|
||||
"agentDetail.configure.buildDraft.title": "Brouillon de build",
|
||||
"agentDetail.configure.chatFeatures.description": "Façonnez l’expérience de chat de l’utilisateur final sur votre webapp et vos surfaces de chat.",
|
||||
"agentDetail.configure.chatFeatures.title": "Fonctionnalités de chat",
|
||||
"agentDetail.configure.files.add": "Ajouter un fichier",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "चैट करके अपना एजेंट बनाएं",
|
||||
"agentDetail.configure.build.inputPlaceholder": "बताएं कि आपका एजेंट क्या करे",
|
||||
"agentDetail.configure.build.startBuild": "बिल्ड शुरू करें",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} बदलाव लागू करने हैं",
|
||||
"agentDetail.configure.buildDraft.discard": "छोड़ें",
|
||||
"agentDetail.configure.buildDraft.noChanges": "लागू करने के लिए कोई बदलाव नहीं",
|
||||
"agentDetail.configure.buildDraft.title": "बिल्ड ड्राफ्ट",
|
||||
"agentDetail.configure.chatFeatures.description": "अपने Web app और चैट सतहों पर अंतिम-उपयोगकर्ता चैट अनुभव को आकार दें।",
|
||||
"agentDetail.configure.chatFeatures.title": "चैट सुविधाएँ",
|
||||
"agentDetail.configure.files.add": "फ़ाइल जोड़ें",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Bangun agen Anda lewat chat",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Jelaskan apa yang harus dilakukan agen Anda",
|
||||
"agentDetail.configure.build.startBuild": "Mulai build",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} perubahan untuk diterapkan",
|
||||
"agentDetail.configure.buildDraft.discard": "Buang",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Tidak ada perubahan untuk diterapkan",
|
||||
"agentDetail.configure.buildDraft.title": "Draf build",
|
||||
"agentDetail.configure.chatFeatures.description": "Bentuk pengalaman chat pengguna akhir di Web app dan permukaan chat Anda.",
|
||||
"agentDetail.configure.chatFeatures.title": "Fitur Chat",
|
||||
"agentDetail.configure.files.add": "Tambahkan file",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Crea il tuo agente con la chat",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Descrivi cosa dovrebbe fare il tuo agente",
|
||||
"agentDetail.configure.build.startBuild": "Avvia build",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} modifiche da applicare",
|
||||
"agentDetail.configure.buildDraft.discard": "Scarta",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Nessuna modifica da applicare",
|
||||
"agentDetail.configure.buildDraft.title": "Bozza di build",
|
||||
"agentDetail.configure.chatFeatures.description": "Definisci l’esperienza di chat per l’utente finale sulla tua webapp e sulle superfici di chat.",
|
||||
"agentDetail.configure.chatFeatures.title": "Funzionalità chat",
|
||||
"agentDetail.configure.files.add": "Aggiungi file",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "チャットでエージェントを作成",
|
||||
"agentDetail.configure.build.inputPlaceholder": "エージェントに実行させたいことを説明",
|
||||
"agentDetail.configure.build.startBuild": "ビルドを開始",
|
||||
"agentDetail.configure.buildDraft.changes": "適用する変更 {{count}} 件",
|
||||
"agentDetail.configure.buildDraft.discard": "破棄",
|
||||
"agentDetail.configure.buildDraft.noChanges": "適用する変更はありません",
|
||||
"agentDetail.configure.buildDraft.title": "ビルドドラフト",
|
||||
"agentDetail.configure.chatFeatures.description": "Web app やチャット画面でのエンドユーザー向けチャット体験を設定します。",
|
||||
"agentDetail.configure.chatFeatures.title": "チャット機能",
|
||||
"agentDetail.configure.files.add": "ファイルを追加",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "채팅으로 에이전트 만들기",
|
||||
"agentDetail.configure.build.inputPlaceholder": "에이전트가 해야 할 일을 설명하세요",
|
||||
"agentDetail.configure.build.startBuild": "빌드 시작",
|
||||
"agentDetail.configure.buildDraft.changes": "적용할 변경 사항 {{count}}개",
|
||||
"agentDetail.configure.buildDraft.discard": "폐기",
|
||||
"agentDetail.configure.buildDraft.noChanges": "적용할 변경 사항 없음",
|
||||
"agentDetail.configure.buildDraft.title": "빌드 초안",
|
||||
"agentDetail.configure.chatFeatures.description": "Web app 및 채팅 화면에서의 최종 사용자 채팅 경험을 구성합니다.",
|
||||
"agentDetail.configure.chatFeatures.title": "채팅 기능",
|
||||
"agentDetail.configure.files.add": "파일 추가",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Bouw je agent via chat",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Beschrijf wat je agent moet doen",
|
||||
"agentDetail.configure.build.startBuild": "Build starten",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} wijzigingen om toe te passen",
|
||||
"agentDetail.configure.buildDraft.discard": "Negeren",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Geen wijzigingen om toe te passen",
|
||||
"agentDetail.configure.buildDraft.title": "Buildconcept",
|
||||
"agentDetail.configure.chatFeatures.description": "Geef vorm aan de chatervaring voor eindgebruikers in je webapp en chatoppervlakken.",
|
||||
"agentDetail.configure.chatFeatures.title": "Chatfuncties",
|
||||
"agentDetail.configure.files.add": "Bestand toevoegen",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Buduj agenta przez czat",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Opisz, co agent ma robić",
|
||||
"agentDetail.configure.build.startBuild": "Rozpocznij budowanie",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} zmian do zastosowania",
|
||||
"agentDetail.configure.buildDraft.discard": "Odrzuć",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Brak zmian do zastosowania",
|
||||
"agentDetail.configure.buildDraft.title": "Szkic budowania",
|
||||
"agentDetail.configure.chatFeatures.description": "Ukształtuj doświadczenie czatu użytkownika końcowego w aplikacji webowej i powierzchniach czatu.",
|
||||
"agentDetail.configure.chatFeatures.title": "Funkcje czatu",
|
||||
"agentDetail.configure.files.add": "Dodaj plik",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Crie seu agente conversando",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Descreva o que seu agente deve fazer",
|
||||
"agentDetail.configure.build.startBuild": "Iniciar build",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} alterações para aplicar",
|
||||
"agentDetail.configure.buildDraft.discard": "Descartar",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Nenhuma alteração para aplicar",
|
||||
"agentDetail.configure.buildDraft.title": "Rascunho de build",
|
||||
"agentDetail.configure.chatFeatures.description": "Modele a experiência de chat do usuário final no seu webapp e superfícies de chat.",
|
||||
"agentDetail.configure.chatFeatures.title": "Recursos de chat",
|
||||
"agentDetail.configure.files.add": "Adicionar arquivo",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Construiește agentul prin chat",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Descrie ce ar trebui să facă agentul tău",
|
||||
"agentDetail.configure.build.startBuild": "Pornește build-ul",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} modificări de aplicat",
|
||||
"agentDetail.configure.buildDraft.discard": "Renunță",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Nu există modificări de aplicat",
|
||||
"agentDetail.configure.buildDraft.title": "Schiță de build",
|
||||
"agentDetail.configure.chatFeatures.description": "Modelează experiența de chat a utilizatorului final pe webapp-ul tău și pe suprafețele de chat.",
|
||||
"agentDetail.configure.chatFeatures.title": "Funcții de chat",
|
||||
"agentDetail.configure.files.add": "Adaugă fișier",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Создайте агента в чате",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Опишите, что должен делать агент",
|
||||
"agentDetail.configure.build.startBuild": "Начать сборку",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} изменений для применения",
|
||||
"agentDetail.configure.buildDraft.discard": "Отменить",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Нет изменений для применения",
|
||||
"agentDetail.configure.buildDraft.title": "Черновик сборки",
|
||||
"agentDetail.configure.chatFeatures.description": "Настройте чат-опыт конечного пользователя в вашем веб-приложении и чат-поверхностях.",
|
||||
"agentDetail.configure.chatFeatures.title": "Функции чата",
|
||||
"agentDetail.configure.files.add": "Добавить файл",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Izdelajte agenta s klepetom",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Opišite, kaj naj vaš agent počne",
|
||||
"agentDetail.configure.build.startBuild": "Začni gradnjo",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} sprememb za uporabo",
|
||||
"agentDetail.configure.buildDraft.discard": "Zavrzi",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Ni sprememb za uporabo",
|
||||
"agentDetail.configure.buildDraft.title": "Osnutek gradnje",
|
||||
"agentDetail.configure.chatFeatures.description": "Oblikujte uporabniško izkušnjo klepeta v vaši spletni aplikaciji in klepetalnih površinah.",
|
||||
"agentDetail.configure.chatFeatures.title": "Funkcije klepeta",
|
||||
"agentDetail.configure.files.add": "Dodaj datoteko",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "สร้างเอเจนต์ของคุณด้วยการแชท",
|
||||
"agentDetail.configure.build.inputPlaceholder": "อธิบายว่าเอเจนต์ของคุณควรทำอะไร",
|
||||
"agentDetail.configure.build.startBuild": "เริ่มสร้าง",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} รายการเปลี่ยนแปลงที่ต้องใช้",
|
||||
"agentDetail.configure.buildDraft.discard": "ทิ้ง",
|
||||
"agentDetail.configure.buildDraft.noChanges": "ไม่มีการเปลี่ยนแปลงที่ต้องใช้",
|
||||
"agentDetail.configure.buildDraft.title": "ฉบับร่าง Build",
|
||||
"agentDetail.configure.chatFeatures.description": "กำหนดประสบการณ์การแชทของผู้ใช้ปลายทางบน Web app และหน้าจอแชท",
|
||||
"agentDetail.configure.chatFeatures.title": "ฟีเจอร์แชท",
|
||||
"agentDetail.configure.files.add": "เพิ่มไฟล์",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Aracınızı sohbet ederek oluşturun",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Aracınızın ne yapması gerektiğini açıklayın",
|
||||
"agentDetail.configure.build.startBuild": "Build’i başlat",
|
||||
"agentDetail.configure.buildDraft.changes": "Uygulanacak {{count}} değişiklik",
|
||||
"agentDetail.configure.buildDraft.discard": "Vazgeç",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Uygulanacak değişiklik yok",
|
||||
"agentDetail.configure.buildDraft.title": "Build taslağı",
|
||||
"agentDetail.configure.chatFeatures.description": "Web app ve sohbet yüzeylerinizde son kullanıcı sohbet deneyimini şekillendirin.",
|
||||
"agentDetail.configure.chatFeatures.title": "Sohbet Özellikleri",
|
||||
"agentDetail.configure.files.add": "Dosya ekle",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Створіть агента через чат",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Опишіть, що має робити ваш агент",
|
||||
"agentDetail.configure.build.startBuild": "Почати збірку",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} змін для застосування",
|
||||
"agentDetail.configure.buildDraft.discard": "Відхилити",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Немає змін для застосування",
|
||||
"agentDetail.configure.buildDraft.title": "Чернетка збірки",
|
||||
"agentDetail.configure.chatFeatures.description": "Налаштуйте чат-досвід кінцевого користувача у вашому веб-застосунку та чат-поверхнях.",
|
||||
"agentDetail.configure.chatFeatures.title": "Функції чату",
|
||||
"agentDetail.configure.files.add": "Додати файл",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "Xây dựng tác nhân bằng trò chuyện",
|
||||
"agentDetail.configure.build.inputPlaceholder": "Mô tả tác nhân của bạn nên làm gì",
|
||||
"agentDetail.configure.build.startBuild": "Bắt đầu build",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} thay đổi cần áp dụng",
|
||||
"agentDetail.configure.buildDraft.discard": "Hủy bỏ",
|
||||
"agentDetail.configure.buildDraft.noChanges": "Không có thay đổi cần áp dụng",
|
||||
"agentDetail.configure.buildDraft.title": "Bản nháp build",
|
||||
"agentDetail.configure.chatFeatures.description": "Định hình trải nghiệm trò chuyện cho người dùng cuối trên Web app và các bề mặt trò chuyện.",
|
||||
"agentDetail.configure.chatFeatures.title": "Tính năng trò chuyện",
|
||||
"agentDetail.configure.files.add": "Thêm tệp",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "通过对话构建 Agent",
|
||||
"agentDetail.configure.build.inputPlaceholder": "描述你的 Agent 应该做什么",
|
||||
"agentDetail.configure.build.startBuild": "开始构建",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} 项待应用更改",
|
||||
"agentDetail.configure.buildDraft.discard": "放弃",
|
||||
"agentDetail.configure.buildDraft.noChanges": "没有待应用的更改",
|
||||
"agentDetail.configure.buildDraft.title": "Build 草稿",
|
||||
"agentDetail.configure.chatFeatures.description": "配置 Web app 和聊天界面的终端用户聊天体验。",
|
||||
"agentDetail.configure.chatFeatures.title": "Chat 功能",
|
||||
"agentDetail.configure.files.add": "添加文件",
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"agentDetail.configure.build.empty.title": "透過對話建置 Agent",
|
||||
"agentDetail.configure.build.inputPlaceholder": "描述你的 Agent 應該做什麼",
|
||||
"agentDetail.configure.build.startBuild": "開始構建",
|
||||
"agentDetail.configure.buildDraft.changes": "{{count}} 項待套用變更",
|
||||
"agentDetail.configure.buildDraft.discard": "放棄",
|
||||
"agentDetail.configure.buildDraft.noChanges": "沒有待套用的變更",
|
||||
"agentDetail.configure.buildDraft.title": "Build 草稿",
|
||||
"agentDetail.configure.chatFeatures.description": "配置 Web app 和聊天介面的終端使用者聊天體驗。",
|
||||
"agentDetail.configure.chatFeatures.title": "Chat 功能",
|
||||
"agentDetail.configure.files.add": "新增檔案",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user