mirror of
https://github.com/langgenius/dify.git
synced 2026-10-16 15:35:32 +08:00
refactor(web): clarify App modes and scope workflow editor resources (#44171)
This commit is contained in:
@@ -2487,11 +2487,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts": {
|
||||
"typescript/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/app/components/workflow/hooks/use-serial-async-callback.ts": {
|
||||
"typescript/no-explicit-any": {
|
||||
"count": 1
|
||||
@@ -2787,7 +2782,7 @@
|
||||
"count": 1
|
||||
},
|
||||
"typescript/no-explicit-any": {
|
||||
"count": 21
|
||||
"count": 20
|
||||
}
|
||||
},
|
||||
"web/app/components/workflow/nodes/_base/hooks/use-output-var-list.ts": {
|
||||
@@ -4250,11 +4245,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/service/use-flow.ts": {
|
||||
"eslint-react/no-unnecessary-use-prefix": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/service/use-log.ts": {
|
||||
"@tanstack/query/prefer-query-options": {
|
||||
"count": 7
|
||||
@@ -4333,13 +4323,13 @@
|
||||
},
|
||||
"web/service/use-workflow.ts": {
|
||||
"@tanstack/query/prefer-query-options": {
|
||||
"count": 9
|
||||
"count": 6
|
||||
},
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"typescript/no-explicit-any": {
|
||||
"count": 3
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/service/use-workspace.ts": {
|
||||
@@ -4380,7 +4370,7 @@
|
||||
},
|
||||
"web/types/workflow.ts": {
|
||||
"typescript/no-explicit-any": {
|
||||
"count": 17
|
||||
"count": 16
|
||||
}
|
||||
},
|
||||
"web/utils/completion-params.spec.ts": {
|
||||
@@ -4423,4 +4413,4 @@
|
||||
"count": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-15
@@ -9,7 +9,6 @@ import {
|
||||
seedAppDetail,
|
||||
} from '@/test/console/query-data'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { AppACLPermission } from '@/utils/permission'
|
||||
import AppDetailLayout from '../layout-main'
|
||||
|
||||
@@ -63,7 +62,7 @@ const createAppDetail = (overrides: Partial<AppDetailWithSite> = {}) =>
|
||||
createAppDetailFixture({
|
||||
id: 'app-1',
|
||||
name: 'Demo App',
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
permission_keys: [AppACLPermission.ViewLayout, AppACLPermission.Monitor],
|
||||
...overrides,
|
||||
})
|
||||
@@ -148,15 +147,15 @@ describe('AppDetailLayout', () => {
|
||||
|
||||
describe('Document title', () => {
|
||||
it.each([
|
||||
['/app/app-1/workflow', 'common.appMenus.promptEng', AppModeEnum.WORKFLOW],
|
||||
['/app/app-1/configuration', 'common.appMenus.promptEng', AppModeEnum.CHAT],
|
||||
['/app/app-1/access-point', 'common.appMenus.accessPoint', AppModeEnum.WORKFLOW],
|
||||
['/app/app-1/deploy', 'common.appMenus.deploy', AppModeEnum.WORKFLOW],
|
||||
['/app/app-1/logs', 'common.appMenus.logs', AppModeEnum.WORKFLOW],
|
||||
['/app/app-1/annotations', 'common.appMenus.annotations', AppModeEnum.CHAT],
|
||||
['/app/app-1/overview', 'common.appMenus.overview', AppModeEnum.WORKFLOW],
|
||||
['/app/app-1/access-config', 'navigation.settings.resourceAccess', AppModeEnum.WORKFLOW],
|
||||
])('identifies the current detail page for %s', async (pathname, pageTitle, mode) => {
|
||||
['/app/app-1/workflow', 'common.appMenus.promptEng', 'workflow'],
|
||||
['/app/app-1/configuration', 'common.appMenus.promptEng', 'chat'],
|
||||
['/app/app-1/access-point', 'common.appMenus.accessPoint', 'workflow'],
|
||||
['/app/app-1/deploy', 'common.appMenus.deploy', 'workflow'],
|
||||
['/app/app-1/logs', 'common.appMenus.logs', 'workflow'],
|
||||
['/app/app-1/annotations', 'common.appMenus.annotations', 'chat'],
|
||||
['/app/app-1/overview', 'common.appMenus.overview', 'workflow'],
|
||||
['/app/app-1/access-config', 'navigation.settings.resourceAccess', 'workflow'],
|
||||
] as const)('identifies the current detail page for %s', async (pathname, pageTitle, mode) => {
|
||||
mockPathname = pathname
|
||||
mockAppResponse.mockResolvedValue(
|
||||
createAppDetail({
|
||||
@@ -543,7 +542,7 @@ describe('AppDetailLayout', () => {
|
||||
mockPathname = '/app/app-1/access-config'
|
||||
mockAppResponse.mockResolvedValue(
|
||||
createAppDetail({
|
||||
mode: AppModeEnum.AGENT,
|
||||
mode: 'agent' as const,
|
||||
bound_agent_id: 'agent-1',
|
||||
permission_keys: [AppACLPermission.AccessConfig],
|
||||
}),
|
||||
@@ -570,7 +569,7 @@ describe('AppDetailLayout', () => {
|
||||
</AppDetailLayout>,
|
||||
{
|
||||
cachedAppDetail: createAppDetail({
|
||||
mode: AppModeEnum.AGENT,
|
||||
mode: 'agent' as const,
|
||||
bound_agent_id: 'agent-1',
|
||||
permission_keys: [AppACLPermission.AccessConfig],
|
||||
}),
|
||||
@@ -608,7 +607,7 @@ describe('AppDetailLayout', () => {
|
||||
mockPathname = '/app/app-1/annotations'
|
||||
mockAppResponse.mockResolvedValue(
|
||||
createAppDetail({
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
permission_keys: [AppACLPermission.Monitor],
|
||||
}),
|
||||
)
|
||||
@@ -629,7 +628,7 @@ describe('AppDetailLayout', () => {
|
||||
mockPathname = '/app/app-1/annotations'
|
||||
mockAppResponse.mockResolvedValue(
|
||||
createAppDetail({
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
permission_keys: [AppACLPermission.LogAndAnnotation],
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -20,7 +20,6 @@ import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { redirect, usePathname } from '@/next/navigation'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { isNotFoundError } from '@/service/http-error'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getRedirectionPath } from '@/utils/app-redirection'
|
||||
import { getAppACLCapabilities } from '@/utils/permission'
|
||||
|
||||
@@ -51,8 +50,7 @@ const getAppDetailRedirectPath = (
|
||||
permissionOptions: ResourceMaintainerPermissionOptions,
|
||||
) => {
|
||||
const appACLCapabilities = getAppACLCapabilities(appDetail.permission_keys, permissionOptions)
|
||||
const isWorkflowApp =
|
||||
appDetail.mode === AppModeEnum.WORKFLOW || appDetail.mode === AppModeEnum.ADVANCED_CHAT
|
||||
const isWorkflowApp = appDetail.mode === 'workflow' || appDetail.mode === 'advanced-chat'
|
||||
const isLayoutPath = pathname.endsWith('configuration') || pathname.endsWith('workflow')
|
||||
const isLogsPath = pathname.endsWith('logs')
|
||||
const isAnnotationsPath = pathname.endsWith('annotations')
|
||||
@@ -65,8 +63,7 @@ const getAppDetailRedirectPath = (
|
||||
(isLogsPath && !appACLCapabilities.canAccessLogAndAnnotation) ||
|
||||
(isAnnotationsPath && !appACLCapabilities.canAccessLogAndAnnotation) ||
|
||||
(isOverviewPath && !appACLCapabilities.canMonitor) ||
|
||||
(isAccessConfigPath &&
|
||||
(appDetail.mode === AppModeEnum.AGENT || !appACLCapabilities.canAccessConfig)) ||
|
||||
(isAccessConfigPath && (appDetail.mode === 'agent' || !appACLCapabilities.canAccessConfig)) ||
|
||||
(isAccessPointPath && !appACLCapabilities.canViewAccessPoint) ||
|
||||
(isDeployPath && (!isWorkflowApp || !appACLCapabilities.canDeploy))
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@ import { screen } from '@testing-library/react'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { createConsoleQueryClient, renderWithConsoleQuery } from '@/test/console/query-data'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { AppACLPermission } from '@/utils/permission'
|
||||
import AppDetailSection from '../app-detail-section'
|
||||
|
||||
@@ -221,18 +220,18 @@ describe('AppDetailSection', () => {
|
||||
it.each([
|
||||
{
|
||||
label: 'the app is not a workflow app',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
permissionKeys: [AppACLPermission.Deploy],
|
||||
},
|
||||
{
|
||||
label: 'app deploy ACL permission is missing',
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
permissionKeys: [AppACLPermission.Monitor],
|
||||
},
|
||||
])('should hide deploy navigation when $label', ({ mode, permissionKeys }) => {
|
||||
] as const)('should hide deploy navigation when $label', ({ mode, permissionKeys }) => {
|
||||
// Arrange
|
||||
mockAppMode = mode
|
||||
mockAppPermissionKeys = permissionKeys
|
||||
mockAppPermissionKeys = [...permissionKeys]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
@@ -241,7 +240,7 @@ describe('AppDetailSection', () => {
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.deploy' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it.each([AppModeEnum.CHAT, AppModeEnum.AGENT_CHAT])(
|
||||
it.each(['chat', 'agent-chat'] as const)(
|
||||
'should render resource access navigation for %s apps when app access config permission is granted',
|
||||
(mode) => {
|
||||
// Arrange
|
||||
@@ -263,7 +262,7 @@ describe('AppDetailSection', () => {
|
||||
|
||||
it('should hide resource access navigation for Agent apps', () => {
|
||||
// Arrange
|
||||
mockAppMode = AppModeEnum.AGENT
|
||||
mockAppMode = 'agent'
|
||||
mockAppPermissionKeys = [AppACLPermission.AccessConfig]
|
||||
|
||||
// Act
|
||||
|
||||
@@ -13,7 +13,6 @@ import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { usePathname } from '@/next/navigation'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getAppACLCapabilities } from '@/utils/permission'
|
||||
import { AppInfoView } from './app-info'
|
||||
import AppInfoHeader from './app-info/app-info-header'
|
||||
@@ -71,11 +70,9 @@ function AppDetailContent({ appId, expand }: { appId: string; expand: boolean })
|
||||
if (!appDetail || !systemFeatures || !currentUserId) return []
|
||||
|
||||
const appId = appDetail.id
|
||||
const isWorkflowApp =
|
||||
appDetail.mode === AppModeEnum.WORKFLOW || appDetail.mode === AppModeEnum.ADVANCED_CHAT
|
||||
const supportsAnnotations =
|
||||
appDetail.mode !== AppModeEnum.WORKFLOW && appDetail.mode !== AppModeEnum.COMPLETION
|
||||
const supportsResourceAccess = appDetail.mode !== AppModeEnum.AGENT
|
||||
const isWorkflowApp = appDetail.mode === 'workflow' || appDetail.mode === 'advanced-chat'
|
||||
const supportsAnnotations = appDetail.mode !== 'workflow' && appDetail.mode !== 'completion'
|
||||
const supportsResourceAccess = appDetail.mode !== 'agent'
|
||||
const appACLCapabilities = getAppACLCapabilities(appDetail.permission_keys, {
|
||||
currentUserId,
|
||||
resourceMaintainer: appDetail.maintainer,
|
||||
|
||||
@@ -3,7 +3,6 @@ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import * as React from 'react'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import AppInfoModals from '../app-info-modals'
|
||||
|
||||
vi.mock('next/dynamic', () => ({
|
||||
@@ -112,7 +111,7 @@ const createAppDetail = (overrides: Partial<AppDetailWithSite> = {}) =>
|
||||
createAppDetailFixture({
|
||||
id: 'app-1',
|
||||
name: 'Test App',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
icon: '🤖',
|
||||
icon_type: 'emoji',
|
||||
icon_background: '#FFEAD5',
|
||||
|
||||
@@ -5,7 +5,6 @@ import * as React from 'react'
|
||||
import { createAccountProfileQueryWrapper } from '@/test/console/account-profile'
|
||||
import { render as renderWithConsoleState } from '@/test/console/render'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { AppACLPermission } from '@/utils/permission'
|
||||
import AppInfoTrigger from '../app-info-trigger'
|
||||
|
||||
@@ -41,7 +40,7 @@ const createAppDetail = (overrides: Partial<AppDetailWithSite> = {}) =>
|
||||
createAppDetailFixture({
|
||||
id: 'app-1',
|
||||
name: 'Test App',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
icon: '🤖',
|
||||
icon_type: 'emoji',
|
||||
icon_background: '#FFEAD5',
|
||||
@@ -74,7 +73,7 @@ describe('AppInfoTrigger', () => {
|
||||
it('renders expanded app metadata without making the app info clickable', async () => {
|
||||
const user = userEvent.setup()
|
||||
const props = createProps({
|
||||
appDetail: createAppDetail({ name: 'My Chatbot', mode: AppModeEnum.ADVANCED_CHAT }),
|
||||
appDetail: createAppDetail({ name: 'My Chatbot', mode: 'advanced-chat' as const }),
|
||||
})
|
||||
render(<AppInfoTrigger {...props} />)
|
||||
|
||||
@@ -119,7 +118,7 @@ describe('AppInfoTrigger', () => {
|
||||
it('shows import DSL for workflow apps without a workflow conversion operation', async () => {
|
||||
const user = userEvent.setup()
|
||||
const props = createProps({
|
||||
appDetail: createAppDetail({ mode: AppModeEnum.WORKFLOW }),
|
||||
appDetail: createAppDetail({ mode: 'workflow' as const }),
|
||||
})
|
||||
render(<AppInfoTrigger {...props} />)
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { TFunction } from 'i18next'
|
||||
import { withSelectorKey } from '@/test/i18n-mock'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getAppModeLabel } from '../app-mode-labels'
|
||||
|
||||
describe('getAppModeLabel', () => {
|
||||
@@ -10,19 +9,19 @@ describe('getAppModeLabel', () => {
|
||||
}, 'app') as unknown as TFunction<['app']>
|
||||
|
||||
it('should return advanced chat label', () => {
|
||||
expect(getAppModeLabel(AppModeEnum.ADVANCED_CHAT, t)).toBe('app.types.advanced')
|
||||
expect(getAppModeLabel('advanced-chat', t)).toBe('app.types.advanced')
|
||||
})
|
||||
|
||||
it('should return agent chat label', () => {
|
||||
expect(getAppModeLabel(AppModeEnum.AGENT_CHAT, t)).toBe('app.types.agent')
|
||||
expect(getAppModeLabel('agent-chat', t)).toBe('app.types.agent')
|
||||
})
|
||||
|
||||
it('should return chatbot label', () => {
|
||||
expect(getAppModeLabel(AppModeEnum.CHAT, t)).toBe('app.types.chatbot')
|
||||
expect(getAppModeLabel('chat', t)).toBe('app.types.chatbot')
|
||||
})
|
||||
|
||||
it('should return completion label', () => {
|
||||
expect(getAppModeLabel(AppModeEnum.COMPLETION, t)).toBe('app.types.completion')
|
||||
expect(getAppModeLabel('completion', t)).toBe('app.types.completion')
|
||||
})
|
||||
|
||||
it('should return workflow label for unknown mode', () => {
|
||||
@@ -30,6 +29,6 @@ describe('getAppModeLabel', () => {
|
||||
})
|
||||
|
||||
it('should return workflow label for workflow mode', () => {
|
||||
expect(getAppModeLabel(AppModeEnum.WORKFLOW, t)).toBe('app.types.workflow')
|
||||
expect(getAppModeLabel('workflow', t)).toBe('app.types.workflow')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,7 +3,6 @@ import { act, waitFor } from '@testing-library/react'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { createConsoleQueryClient, renderHookWithConsoleQuery } from '@/test/console/query-data'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import { useAppInfoActions } from '../use-app-info-actions'
|
||||
|
||||
@@ -140,7 +139,7 @@ describe('useAppInfoActions', () => {
|
||||
mockAppDetail = createAppDetailFixture({
|
||||
id: 'app-1',
|
||||
name: 'Test App',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
icon: '🤖',
|
||||
icon_type: 'emoji',
|
||||
icon_background: '#FFEAD5',
|
||||
@@ -370,7 +369,7 @@ describe('useAppInfoActions', () => {
|
||||
})
|
||||
|
||||
it('should open export warning modal for workflow mode', async () => {
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: AppModeEnum.WORKFLOW })
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: 'workflow' as const })
|
||||
|
||||
const { result } = renderActions()
|
||||
|
||||
@@ -382,7 +381,7 @@ describe('useAppInfoActions', () => {
|
||||
})
|
||||
|
||||
it('should open export warning modal for advanced_chat mode', async () => {
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: AppModeEnum.ADVANCED_CHAT })
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: 'advanced-chat' as const })
|
||||
|
||||
const { result } = renderActions()
|
||||
|
||||
@@ -396,7 +395,7 @@ describe('useAppInfoActions', () => {
|
||||
|
||||
describe('handleConfirmExport', () => {
|
||||
it('keeps the warning open after failure so the user can retry', async () => {
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: AppModeEnum.WORKFLOW })
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: 'workflow' as const })
|
||||
mockExportWorkflowAppDsl
|
||||
.mockResolvedValueOnce({ status: 'failed' })
|
||||
.mockResolvedValueOnce({ status: 'downloaded' })
|
||||
@@ -415,7 +414,7 @@ describe('useAppInfoActions', () => {
|
||||
})
|
||||
|
||||
it('should export directly when no secret env variables', async () => {
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: AppModeEnum.WORKFLOW })
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: 'workflow' as const })
|
||||
const { result } = renderActions()
|
||||
|
||||
await act(async () => {
|
||||
@@ -429,7 +428,7 @@ describe('useAppInfoActions', () => {
|
||||
})
|
||||
|
||||
it('should set secret env list when secret variables exist', async () => {
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: AppModeEnum.WORKFLOW })
|
||||
mockAppDetail = createAppDetailFixture({ ...mockAppDetail, mode: 'workflow' as const })
|
||||
const secretVars = [{ value_type: 'secret', name: 'API_KEY', value: 'secret' }]
|
||||
mockExportWorkflowAppDsl.mockResolvedValue({
|
||||
status: 'confirmation-required',
|
||||
|
||||
@@ -7,7 +7,6 @@ import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getAppACLCapabilities, hasPermission } from '@/utils/permission'
|
||||
import AppInfoHeader from './app-info-header'
|
||||
import { getAppModeLabel } from './app-mode-labels'
|
||||
@@ -74,7 +73,7 @@ const AppInfoTrigger = ({
|
||||
]
|
||||
: []),
|
||||
...(appACLCapabilities.canImportExportDSL &&
|
||||
(appDetail.mode === AppModeEnum.ADVANCED_CHAT || appDetail.mode === AppModeEnum.WORKFLOW)
|
||||
(appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow')
|
||||
? [
|
||||
{
|
||||
id: 'import',
|
||||
@@ -99,8 +98,7 @@ const AppInfoTrigger = ({
|
||||
: []
|
||||
|
||||
const workflowConversionOperations: Operation[] =
|
||||
appACLCapabilities.canEdit &&
|
||||
(appDetail.mode === AppModeEnum.COMPLETION || appDetail.mode === AppModeEnum.CHAT)
|
||||
appACLCapabilities.canEdit && (appDetail.mode === 'completion' || appDetail.mode === 'chat')
|
||||
? [
|
||||
{
|
||||
id: 'switch',
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import type { TFunction } from 'i18next'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
|
||||
export function getAppModeLabel(mode: string, t: TFunction<['app']>): string {
|
||||
switch (mode) {
|
||||
case AppModeEnum.ADVANCED_CHAT:
|
||||
case 'advanced-chat':
|
||||
return t(($) => $['types.advanced'], { ns: 'app' })
|
||||
case AppModeEnum.AGENT_CHAT:
|
||||
case 'agent-chat':
|
||||
return t(($) => $['types.agent'], { ns: 'app' })
|
||||
case AppModeEnum.CHAT:
|
||||
case 'chat':
|
||||
return t(($) => $['types.chatbot'], { ns: 'app' })
|
||||
case AppModeEnum.COMPLETION:
|
||||
case 'completion':
|
||||
return t(($) => $['types.completion'], { ns: 'app' })
|
||||
default:
|
||||
return t(($) => $['types.workflow'], { ns: 'app' })
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
markAppDeletionSucceeded,
|
||||
} from '@/service/app-deletion'
|
||||
import { consoleClient, consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
|
||||
export type AppInfoModalType =
|
||||
@@ -234,7 +233,7 @@ export function useAppInfoActions({
|
||||
|
||||
const exportCheck = useCallback(async () => {
|
||||
if (isExporting) return
|
||||
if (appMode !== AppModeEnum.WORKFLOW && appMode !== AppModeEnum.ADVANCED_CHAT) {
|
||||
if (appMode !== 'workflow' && appMode !== 'advanced-chat') {
|
||||
onExport()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { createConsoleQueryClient, renderWithConsoleQuery } from '@/test/console/query-data'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { AppACLPermission } from '@/utils/permission'
|
||||
import AppAccessConfigPage from '../index'
|
||||
|
||||
@@ -375,7 +374,7 @@ describe('AppAccessConfigPage', () => {
|
||||
it('should not mount access config data hooks for Agent apps', () => {
|
||||
appDetail = createAppDetailFixture({
|
||||
id: 'app-1',
|
||||
mode: AppModeEnum.AGENT,
|
||||
mode: 'agent' as const,
|
||||
maintainer: 'account-1',
|
||||
permission_keys: [AppACLPermission.AccessConfig],
|
||||
})
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
useUpdateAppUserAccessSettings,
|
||||
} from '@/service/access-control/use-app-access-config'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getAppACLCapabilities } from '@/utils/permission'
|
||||
|
||||
type AppAccessConfigPageProps = {
|
||||
@@ -265,7 +264,7 @@ const AppAccessConfigPage = ({ appId }: AppAccessConfigPageProps) => {
|
||||
],
|
||||
)
|
||||
|
||||
if (appDetail.mode === AppModeEnum.AGENT || !appACLCapabilities.canAccessConfig) return null
|
||||
if (appDetail.mode === 'agent' || !appACLCapabilities.canAccessConfig) return null
|
||||
|
||||
return <AppAccessConfigContent appId={appId} maintainerId={appDetail.maintainer} />
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { AppMode } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { ReactElement } from 'react'
|
||||
import type { AppModeEnum } from '@/types/app'
|
||||
import { screen } from '@testing-library/react'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { createHttpError } from '@/service/http-error'
|
||||
@@ -43,7 +43,7 @@ function render(ui: ReactElement) {
|
||||
queryClient.setQueryData(['system-features'], { webapp_auth: { enabled: true } })
|
||||
queryClient.setQueryData(
|
||||
consoleQuery.apps.byAppId.get.queryKey({ input: { params: { app_id: 'app-1' } } }),
|
||||
createAppDetailFixture({ ...mocks.appInfo, mode: mocks.appInfo.mode as AppModeEnum }),
|
||||
createAppDetailFixture({ ...mocks.appInfo, mode: mocks.appInfo.mode as AppMode }),
|
||||
)
|
||||
return renderWithConsole(ui, { wrapper: createQueryClientWrapper(queryClient) })
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,3 +1,4 @@
|
||||
import type { AppMode } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { ReactElement } from 'react'
|
||||
import { screen, waitFor, within } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
@@ -9,7 +10,6 @@ import { QueryClientTestProvider } from '@/test/console/query-provider'
|
||||
import { render } from '@/test/console/render'
|
||||
import { createAppDetailFixture, createAppSiteFixture } from '@/test/fixtures/app'
|
||||
import { createTestQueryClient } from '@/test/query-client'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { EnvironmentServiceApiCard } from '../deployed-environment-access-points/environment-service-api-card'
|
||||
import { EnvironmentWebAppCard } from '../deployed-environment-access-points/environment-web-app-card'
|
||||
|
||||
@@ -32,7 +32,7 @@ vi.mock('@/features/system-features/client', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
let mockAppMode: AppModeEnum = AppModeEnum.WORKFLOW
|
||||
let mockAppMode: AppMode = 'workflow'
|
||||
|
||||
vi.mock('@/context/i18n', () => ({
|
||||
useDocLink: () => (path: string) => `https://docs.example.test/en${path}`,
|
||||
@@ -149,7 +149,7 @@ describe('environment access point cards', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.saveSiteConfig.mockResolvedValue(true)
|
||||
mockAppMode = AppModeEnum.WORKFLOW
|
||||
mockAppMode = 'workflow'
|
||||
vi.stubGlobal('fetch', async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const request = new Request(input, init)
|
||||
const path = new URL(request.url).pathname
|
||||
@@ -247,7 +247,7 @@ describe('environment access point cards', () => {
|
||||
)
|
||||
|
||||
it('sends a chatflow app to the chat web app shell', async () => {
|
||||
mockAppMode = AppModeEnum.ADVANCED_CHAT
|
||||
mockAppMode = 'advanced-chat'
|
||||
|
||||
renderCard(<EnvironmentWebAppCard appId="app-1" environmentId="staging" canManageAccessPoint />)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { AppMode } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { AppEnvironment } from '@dify/contracts/enterprise-app-deploy/types.gen'
|
||||
import type { ReactNode } from 'react'
|
||||
import type { AccessPoint as AccessPointType } from '@/app/components/app/deploy/utils/access-point'
|
||||
@@ -11,11 +12,10 @@ import { QueryClientTestProvider } from '@/test/console/query-provider'
|
||||
import { render } from '@/test/console/render'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { createTestQueryClient } from '@/test/query-client'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { AppACLPermission } from '@/utils/permission'
|
||||
import AccessPoint from '..'
|
||||
|
||||
let appMode: AppModeEnum = AppModeEnum.WORKFLOW
|
||||
let appMode: AppMode = 'workflow'
|
||||
let appPermissionKeys: string[] = [AppACLPermission.AccessPointView]
|
||||
const accessPointMocks = vi.hoisted(() => ({
|
||||
builtIn: vi.fn(),
|
||||
@@ -133,7 +133,7 @@ const renderAccessPoint = ({
|
||||
describe('AccessPoint', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
appMode = AppModeEnum.WORKFLOW
|
||||
appMode = 'workflow'
|
||||
appPermissionKeys = [AppACLPermission.AccessPointView]
|
||||
})
|
||||
|
||||
@@ -243,7 +243,7 @@ describe('AccessPoint', () => {
|
||||
})
|
||||
|
||||
it('hides environment tabs for app types without multi-environment support', () => {
|
||||
appMode = AppModeEnum.CHAT
|
||||
appMode = 'chat'
|
||||
|
||||
renderAccessPoint()
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import { toast } from '@/app/notifications'
|
||||
import { render } from '@/test/console/render'
|
||||
import { createAppDetailFixture, createAppModelConfigFixture } from '@/test/fixtures/app'
|
||||
import { createTestQueryClient } from '@/test/query-client'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { MCPAccessPointCard } from '../built-in-access-points/mcp-card'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
@@ -65,7 +64,7 @@ vi.mock('@/app/components/tools/mcp/mcp-server-modal', () => ({
|
||||
const appInfo = createAppDetailFixture({
|
||||
api_base_url: 'https://api.example.test/v1',
|
||||
id: 'app-1',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
model_config: createAppModelConfigFixture({
|
||||
updated_at: 1_710_000_000,
|
||||
user_input_form: [
|
||||
@@ -82,7 +81,7 @@ const appInfo = createAppDetailFixture({
|
||||
|
||||
const workflowAppInfo = createAppDetailFixture({
|
||||
...appInfo,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
model_config: null,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AppDetailWithSite } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { AppDetailWithSite, AppMode } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import { QueryClientProvider, useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
@@ -7,7 +7,6 @@ import { consoleQuery } from '@/service/console'
|
||||
import { render } from '@/test/console/render'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { createTestQueryClient } from '@/test/query-client'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { ServiceApiAccessPointCard } from '../built-in-access-points/service-api-card'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
@@ -53,7 +52,7 @@ vi.mock('../built-in-access-points/api-secret-key-button', () => ({
|
||||
}))
|
||||
|
||||
function createAppInfo(
|
||||
mode: AppModeEnum,
|
||||
mode: AppMode,
|
||||
overrides: Partial<AppDetailWithSite> = {},
|
||||
): AppDetailWithSite {
|
||||
return createAppDetailFixture({
|
||||
@@ -66,7 +65,7 @@ function createAppInfo(
|
||||
}
|
||||
|
||||
function renderCard(
|
||||
mode: AppModeEnum,
|
||||
mode: AppMode,
|
||||
availability: 'available' | 'loading' | 'unavailable' = 'available',
|
||||
canManage = true,
|
||||
overrides: Partial<AppDetailWithSite> = {},
|
||||
@@ -124,12 +123,12 @@ describe('ServiceApiAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it.each([
|
||||
[AppModeEnum.ADVANCED_CHAT, '/api-reference/guides/chatflow'],
|
||||
[AppModeEnum.WORKFLOW, '/api-reference/guides/workflow'],
|
||||
[AppModeEnum.CHAT, '/api-reference/guides/chat'],
|
||||
[AppModeEnum.AGENT_CHAT, '/api-reference/guides/chat'],
|
||||
[AppModeEnum.COMPLETION, '/api-reference/guides/completion'],
|
||||
])('links %s apps to the matching external API reference', (mode, path) => {
|
||||
['advanced-chat', '/api-reference/guides/chatflow'],
|
||||
['workflow', '/api-reference/guides/workflow'],
|
||||
['chat', '/api-reference/guides/chat'],
|
||||
['agent-chat', '/api-reference/guides/chat'],
|
||||
['completion', '/api-reference/guides/completion'],
|
||||
] as const)('links %s apps to the matching external API reference', (mode, path) => {
|
||||
renderCard(mode)
|
||||
|
||||
const apiReferenceLink = screen.getByRole('link', { name: /apiInfo\.doc/ })
|
||||
@@ -141,7 +140,7 @@ describe('ServiceApiAccessPointCard', () => {
|
||||
|
||||
it('updates API status through the generated contract', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderCard(AppModeEnum.WORKFLOW)
|
||||
renderCard('workflow')
|
||||
|
||||
await user.click(screen.getByRole('switch'))
|
||||
|
||||
@@ -154,7 +153,7 @@ describe('ServiceApiAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it('shows loading without reporting an environment failure', () => {
|
||||
renderCard(AppModeEnum.WORKFLOW, 'loading')
|
||||
renderCard('workflow', 'loading')
|
||||
|
||||
const card = screen.getByRole('region', { name: /serviceApi\.title/ })
|
||||
expect(card).toHaveAttribute('aria-busy', 'true')
|
||||
@@ -165,7 +164,7 @@ describe('ServiceApiAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it('keeps API keys and external documentation available when the API is stopped', () => {
|
||||
renderCard(AppModeEnum.WORKFLOW, 'available', true, { enable_api: false })
|
||||
renderCard('workflow', 'available', true, { enable_api: false })
|
||||
|
||||
expect(screen.getByRole('button', { name: 'api-secret-keys' })).toBeEnabled()
|
||||
const apiReferenceLink = screen.getByRole('link', { name: /apiInfo\.doc/ })
|
||||
@@ -176,14 +175,14 @@ describe('ServiceApiAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it('disables API management without Access Point management permission', () => {
|
||||
renderCard(AppModeEnum.WORKFLOW, 'available', false)
|
||||
renderCard('workflow', 'available', false)
|
||||
|
||||
expect(screen.getByRole('button', { name: 'api-secret-keys' })).toBeDisabled()
|
||||
expect(screen.getByRole('switch')).toHaveAttribute('aria-disabled', 'true')
|
||||
})
|
||||
|
||||
it('disables API keys and external documentation when the access point is unavailable', () => {
|
||||
renderCard(AppModeEnum.WORKFLOW, 'unavailable')
|
||||
renderCard('workflow', 'unavailable')
|
||||
|
||||
expect(screen.getByRole('button', { name: 'api-secret-keys' })).toBeDisabled()
|
||||
expect(screen.getByRole('button', { name: /apiInfo\.doc/ })).toBeDisabled()
|
||||
@@ -193,7 +192,7 @@ describe('ServiceApiAccessPointCard', () => {
|
||||
const user = userEvent.setup()
|
||||
const toggle = createDeferredPromise<{ enable_api: boolean }>()
|
||||
mocks.apiEnable.mockReturnValueOnce(toggle.promise)
|
||||
renderCard(AppModeEnum.WORKFLOW)
|
||||
renderCard('workflow')
|
||||
|
||||
const accessSwitch = screen.getByRole('switch')
|
||||
await user.click(accessSwitch)
|
||||
@@ -212,7 +211,7 @@ describe('ServiceApiAccessPointCard', () => {
|
||||
it('keeps successful status changes silent', async () => {
|
||||
const user = userEvent.setup()
|
||||
mocks.apiEnable.mockResolvedValueOnce(createAppDetailFixture({ enable_api: false }))
|
||||
renderCard(AppModeEnum.WORKFLOW)
|
||||
renderCard('workflow')
|
||||
|
||||
const accessSwitch = screen.getByRole('switch')
|
||||
await user.click(accessSwitch)
|
||||
|
||||
@@ -6,7 +6,6 @@ import { toast } from '@/app/notifications'
|
||||
import { render } from '@/test/console/render'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { createTestQueryClient } from '@/test/query-client'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { TriggerAccessPointCard } from '../built-in-access-points/trigger-card'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
@@ -71,7 +70,7 @@ vi.mock('@/app/components/workflow/block-icon', () => ({
|
||||
|
||||
const appInfo = createAppDetailFixture({
|
||||
id: 'app-1',
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
})
|
||||
|
||||
function createTrigger(id: string, status: AppTrigger['status']): AppTrigger {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { AppDetailWithSite, AppSiteResponse } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type {
|
||||
AppDetailWithSite,
|
||||
AppMode,
|
||||
AppSiteResponse,
|
||||
} from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { PublishedWorkflow } from '../shared/utils'
|
||||
import type { InputVar, Node } from '@/app/components/workflow/types'
|
||||
import { QueryClientProvider, useSuspenseQuery } from '@tanstack/react-query'
|
||||
@@ -12,7 +16,6 @@ import { seedAccountProfileQuery } from '@/test/console/account-profile'
|
||||
import { render } from '@/test/console/render'
|
||||
import { createAppDetailFixture, createAppSiteFixture } from '@/test/fixtures/app'
|
||||
import { createTestQueryClient } from '@/test/query-client'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { WebAppAccessPointCard } from '../built-in-access-points/web-app-card'
|
||||
|
||||
@@ -92,7 +95,7 @@ vi.mock('@/context/i18n', () => ({
|
||||
useDocLink: () => (path: string) => `https://docs.example.test/en${path}`,
|
||||
}))
|
||||
|
||||
function createAppInfo(mode: AppModeEnum): AppDetailWithSite {
|
||||
function createAppInfo(mode: AppMode): AppDetailWithSite {
|
||||
return createAppDetailFixture({
|
||||
access_mode: AccessMode.PUBLIC,
|
||||
api_base_url: 'https://api.example.test/v1',
|
||||
@@ -111,7 +114,7 @@ function createAppInfo(mode: AppModeEnum): AppDetailWithSite {
|
||||
}
|
||||
|
||||
function renderCard(
|
||||
mode: AppModeEnum,
|
||||
mode: AppMode,
|
||||
availability: 'available' | 'loading' | 'unavailable' = 'available',
|
||||
workflow?: PublishedWorkflow,
|
||||
{
|
||||
@@ -259,7 +262,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it('disables site actions when the app has no site', () => {
|
||||
renderCard(AppModeEnum.CHAT, 'available', undefined, { appOverrides: { site: null } })
|
||||
renderCard('chat', 'available', undefined, { appOverrides: { site: null } })
|
||||
|
||||
expect(screen.getByRole('button', { name: /settings\.settings/ })).toBeDisabled()
|
||||
expect(
|
||||
@@ -272,7 +275,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it('does not present an unknown access mode as public or allow launching', () => {
|
||||
renderCard(AppModeEnum.CHAT, 'available', undefined, { accessMode: null })
|
||||
renderCard('chat', 'available', undefined, { accessMode: null })
|
||||
|
||||
expect(
|
||||
screen.queryByRole('button', { name: /accessControlDialog\.accessItems\.anyone/ }),
|
||||
@@ -287,7 +290,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it('shows the current access mode without a redundant section label', () => {
|
||||
renderCard(AppModeEnum.CHAT)
|
||||
renderCard('chat')
|
||||
|
||||
expect(screen.queryByText(/publishApp\.title/)).not.toBeInTheDocument()
|
||||
expect(
|
||||
@@ -298,7 +301,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
it('disables Open and explains when the user cannot access the Web App', async () => {
|
||||
const user = userEvent.setup()
|
||||
mocks.getUserCanAccess.mockResolvedValue({ result: false })
|
||||
renderCard(AppModeEnum.WORKFLOW, 'available', undefined, {
|
||||
renderCard('workflow', 'available', undefined, {
|
||||
accessMode: AccessMode.SPECIFIC_GROUPS_MEMBERS,
|
||||
})
|
||||
|
||||
@@ -319,7 +322,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
it('keeps Open disabled until access is granted', async () => {
|
||||
const permission = createDeferredPromise<{ result: boolean }>()
|
||||
mocks.getUserCanAccess.mockReturnValueOnce(permission.promise)
|
||||
renderCard(AppModeEnum.WORKFLOW)
|
||||
renderCard('workflow')
|
||||
|
||||
expect(screen.getByRole('button', { name: /studio\.accessPoint\.open/ })).toHaveAttribute(
|
||||
'aria-disabled',
|
||||
@@ -336,7 +339,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
|
||||
it('allows opening external-member Web Apps independently of platform access', () => {
|
||||
mocks.getUserCanAccess.mockResolvedValue({ result: false })
|
||||
renderCard(AppModeEnum.WORKFLOW, 'available', undefined, {
|
||||
renderCard('workflow', 'available', undefined, {
|
||||
accessMode: AccessMode.EXTERNAL_MEMBERS,
|
||||
})
|
||||
|
||||
@@ -347,7 +350,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it('allows opening Web Apps without querying permissions when access control is disabled', () => {
|
||||
renderCard(AppModeEnum.CHAT, 'available', undefined, { showAccessControl: false })
|
||||
renderCard('chat', 'available', undefined, { showAccessControl: false })
|
||||
|
||||
expect(screen.getByRole('link', { name: /studio\.accessPoint\.open/ })).toHaveAttribute(
|
||||
'href',
|
||||
@@ -359,7 +362,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
it('updates Open after saving Web App access permissions', async () => {
|
||||
const user = userEvent.setup()
|
||||
mocks.getUserCanAccess.mockResolvedValue({ result: false })
|
||||
renderCard(AppModeEnum.CHAT, 'available', undefined, {
|
||||
renderCard('chat', 'available', undefined, {
|
||||
accessMode: AccessMode.SPECIFIC_GROUPS_MEMBERS,
|
||||
})
|
||||
|
||||
@@ -380,7 +383,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it.each([AppModeEnum.WORKFLOW, AppModeEnum.COMPLETION])(
|
||||
it.each(['workflow', 'completion'] as const)(
|
||||
'does not offer Embed into site for %s apps',
|
||||
(mode) => {
|
||||
renderCard(mode)
|
||||
@@ -393,7 +396,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
|
||||
it('keeps Embed into site for non-workflow Web apps', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderCard(AppModeEnum.CHAT)
|
||||
renderCard('chat')
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /embedIntoSite/ }))
|
||||
|
||||
@@ -404,7 +407,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
|
||||
it('updates site status through the generated contract', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderCard(AppModeEnum.CHAT)
|
||||
renderCard('chat')
|
||||
|
||||
await user.click(screen.getByRole('switch'))
|
||||
|
||||
@@ -418,7 +421,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
|
||||
it('keeps site status changes behind Access Point management permission', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderCard(AppModeEnum.CHAT, 'available', undefined, { canManageAccessPoint: false })
|
||||
renderCard('chat', 'available', undefined, { canManageAccessPoint: false })
|
||||
|
||||
await user.click(screen.getByRole('switch'))
|
||||
|
||||
@@ -429,7 +432,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
const user = userEvent.setup()
|
||||
const refresh = createDeferredPromise<AppDetailWithSite | null>()
|
||||
mocks.getAppDetail.mockReturnValueOnce(refresh.promise)
|
||||
renderCard(AppModeEnum.CHAT)
|
||||
renderCard('chat')
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /overview\.appInfo\.regenerate/ }))
|
||||
await user.click(screen.getByRole('button', { name: /operation\.confirm/ }))
|
||||
@@ -456,7 +459,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
const user = userEvent.setup()
|
||||
const error = new Error('request failed')
|
||||
mocks.siteEnable.mockRejectedValueOnce(error)
|
||||
renderCard(AppModeEnum.CHAT)
|
||||
renderCard('chat')
|
||||
|
||||
await user.click(screen.getByRole('switch'))
|
||||
|
||||
@@ -467,7 +470,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
|
||||
it('passes hidden Chatflow inputs to the embed dialog', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderCard(AppModeEnum.ADVANCED_CHAT, 'available', workflowWithHiddenInput)
|
||||
renderCard('advanced-chat', 'available', workflowWithHiddenInput)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /embedIntoSite/ }))
|
||||
|
||||
@@ -478,7 +481,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
it('configures hidden workflow inputs before opening the Web App', async () => {
|
||||
const user = userEvent.setup()
|
||||
const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null)
|
||||
renderCard(AppModeEnum.WORKFLOW, 'available', workflowWithHiddenInput)
|
||||
renderCard('workflow', 'available', workflowWithHiddenInput)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /operation\.config/ }))
|
||||
await user.type(screen.getByLabelText('Secret'), 'top-secret')
|
||||
@@ -496,7 +499,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
const user = userEvent.setup()
|
||||
const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null)
|
||||
mocks.getUserCanAccess.mockResolvedValue({ result: false })
|
||||
renderCard(AppModeEnum.WORKFLOW, 'available', workflowWithHiddenInput, {
|
||||
renderCard('workflow', 'available', workflowWithHiddenInput, {
|
||||
accessMode: AccessMode.SPECIFIC_GROUPS_MEMBERS,
|
||||
})
|
||||
|
||||
@@ -514,7 +517,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it('shows loading without reporting an environment failure', () => {
|
||||
renderCard(AppModeEnum.WORKFLOW, 'loading')
|
||||
renderCard('workflow', 'loading')
|
||||
|
||||
const card = screen.getByRole('region', { name: /webApp\.title/ })
|
||||
expect(card).toHaveAttribute('aria-busy', 'true')
|
||||
@@ -525,7 +528,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
})
|
||||
|
||||
it('does not announce an unavailable access control entry as loading', () => {
|
||||
renderCard(AppModeEnum.WORKFLOW, 'unavailable')
|
||||
renderCard('workflow', 'unavailable')
|
||||
|
||||
const card = screen.getByRole('region', { name: /webApp\.title/ })
|
||||
expect(within(card).queryByRole('status', { name: 'common.loading' })).not.toBeInTheDocument()
|
||||
@@ -538,7 +541,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
mocks.siteEnable
|
||||
.mockReturnValueOnce(firstToggle.promise)
|
||||
.mockReturnValueOnce(secondToggle.promise)
|
||||
renderCard(AppModeEnum.CHAT)
|
||||
renderCard('chat')
|
||||
|
||||
const accessSwitch = screen.getByRole('switch')
|
||||
await user.click(accessSwitch)
|
||||
@@ -573,7 +576,7 @@ describe('WebAppAccessPointCard', () => {
|
||||
|
||||
it('disables Web App management actions without Access Point management', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderCard(AppModeEnum.CHAT, 'available', undefined, { canManageAccessPoint: false })
|
||||
renderCard('chat', 'available', undefined, { canManageAccessPoint: false })
|
||||
|
||||
expect(screen.getByRole('switch')).toHaveAttribute('aria-disabled', 'true')
|
||||
expect(screen.getByRole('button', { name: /embedIntoSite/ })).toBeDisabled()
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
useMCPServerDetail,
|
||||
useRefreshMCPServerCode,
|
||||
} from '@/service/use-tools'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { useAccessPointStatusLabel } from '../shared/use-access-point-status-label'
|
||||
import { getPublishedWorkflowNodes, isAdvancedApp } from '../shared/utils'
|
||||
|
||||
@@ -50,7 +49,7 @@ export function MCPAccessPointCard({
|
||||
const { t } = useTranslation(['appOverview', 'common', 'deployments', 'tools'])
|
||||
const advancedApp = isAdvancedApp(appInfo)
|
||||
const basicApp = !advancedApp
|
||||
const workflowApp = appInfo.mode === AppModeEnum.WORKFLOW
|
||||
const workflowApp = appInfo.mode === 'workflow'
|
||||
const [showServerModal, setShowServerModal] = useState(false)
|
||||
const [showRegenerate, setShowRegenerate] = useState(false)
|
||||
const basicConfig = appInfo.model_config
|
||||
|
||||
@@ -37,7 +37,6 @@ import {
|
||||
useGetUserCanAccessApp,
|
||||
} from '@/service/access-control/use-app-access-control'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { useAccessPointStatusLabel } from '../shared/use-access-point-status-label'
|
||||
import { getBuiltInAccessUrls, getHiddenStartInputs } from '../shared/utils'
|
||||
import {
|
||||
@@ -122,8 +121,7 @@ export function WebAppAccessPointCard({
|
||||
: appInfo.enable_site
|
||||
const running = siteAvailability === 'available' && optimisticEnabled
|
||||
const actionsAvailable = running && Boolean(webAppUrl) && !toggleSiteMutation.isPending
|
||||
const supportsEmbedded =
|
||||
appInfo.mode !== AppModeEnum.COMPLETION && appInfo.mode !== AppModeEnum.WORKFLOW
|
||||
const supportsEmbedded = appInfo.mode !== 'completion' && appInfo.mode !== 'workflow'
|
||||
const hiddenLaunchVariables = getHiddenStartInputs(workflow)
|
||||
const { data: accessSubjects } = useAppWhiteListSubjects(
|
||||
appInfo.id,
|
||||
@@ -220,9 +218,7 @@ export function WebAppAccessPointCard({
|
||||
/>
|
||||
<SettingsDialog
|
||||
key={appInfo.id}
|
||||
isChat={
|
||||
appInfo.mode !== AppModeEnum.COMPLETION && appInfo.mode !== AppModeEnum.WORKFLOW
|
||||
}
|
||||
isChat={appInfo.mode !== 'completion' && appInfo.mode !== 'workflow'}
|
||||
canDeploy={canDeploy}
|
||||
appInfo={site ? { id: appInfo.id, mode: appInfo.mode, site } : undefined}
|
||||
disabled={siteAvailability !== 'available' || !canManageAccessPoint}
|
||||
|
||||
+1
-2
@@ -1,10 +1,9 @@
|
||||
import type { EnvironmentSite } from '@dify/contracts/enterprise-app-deploy/types.gen'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { basePath } from '@/utils/var'
|
||||
|
||||
export function getEnvironmentWebAppUrl(site?: EnvironmentSite, mode?: string) {
|
||||
if (!site?.app_base_url || !site.code) return ''
|
||||
|
||||
const route = mode === AppModeEnum.ADVANCED_CHAT ? 'chat' : 'workflow'
|
||||
const route = mode === 'advanced-chat' ? 'chat' : 'workflow'
|
||||
return `${site.app_base_url.replace(/\/$/, '')}${basePath}/environment/${route}/${site.code}`
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import { ACCESS_POINT_ORDER } from '@/app/components/app/deploy/utils/access-poi
|
||||
import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getAppACLCapabilities } from '@/utils/permission'
|
||||
import { BuiltInAccessPoints } from './built-in-access-points'
|
||||
import { DeployedEnvironmentAccessPoints } from './deployed-environment-access-points'
|
||||
@@ -162,7 +161,7 @@ export default function AccessPoint({ appId }: AccessPointProps) {
|
||||
workspacePermissionKeys,
|
||||
})
|
||||
const showEnvironmentTabs =
|
||||
(appDetail?.mode === AppModeEnum.WORKFLOW || appDetail?.mode === AppModeEnum.ADVANCED_CHAT) &&
|
||||
(appDetail?.mode === 'workflow' || appDetail?.mode === 'advanced-chat') &&
|
||||
capabilities.canViewAccessPoint
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,7 +5,6 @@ import type {
|
||||
import type { InputVar, Node } from '@/app/components/workflow/types'
|
||||
import type { DocPathWithoutLang } from '@/types/doc-paths'
|
||||
import { BlockEnum, isTriggerNode } from '@/app/components/workflow/types'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { basePath } from '@/utils/var'
|
||||
|
||||
export type PublishedWorkflow = WorkflowResponse | null | undefined
|
||||
@@ -25,7 +24,7 @@ export function getAppApiReferencePath(appMode: AppDetailWithSite['mode']) {
|
||||
}
|
||||
|
||||
export function getPublishedWorkflowState(appInfo: AppDetailWithSite, workflow: PublishedWorkflow) {
|
||||
const isWorkflowApp = appInfo.mode === AppModeEnum.WORKFLOW
|
||||
const isWorkflowApp = appInfo.mode === 'workflow'
|
||||
const nodes = getPublishedWorkflowNodes(workflow)
|
||||
const hasStartNode = nodes.some((node) => node.data.type === BlockEnum.Start)
|
||||
const hasTriggerNode = nodes.some((node) => isTriggerNode(node.data.type))
|
||||
@@ -46,9 +45,7 @@ export function getPublishedWorkflowNodes(workflow: PublishedWorkflow) {
|
||||
|
||||
export function getBuiltInAccessUrls(appInfo: AppDetailWithSite) {
|
||||
const appMode =
|
||||
appInfo.mode === AppModeEnum.COMPLETION || appInfo.mode === AppModeEnum.WORKFLOW
|
||||
? appInfo.mode
|
||||
: AppModeEnum.CHAT
|
||||
appInfo.mode === 'completion' || appInfo.mode === 'workflow' ? appInfo.mode : 'chat'
|
||||
|
||||
return {
|
||||
api: appInfo.api_base_url ?? '',
|
||||
@@ -69,5 +66,5 @@ export function getHiddenStartInputs(workflow: PublishedWorkflow) {
|
||||
}
|
||||
|
||||
export function isAdvancedApp(appInfo: AppDetailWithSite) {
|
||||
return appInfo.mode === AppModeEnum.WORKFLOW || appInfo.mode === AppModeEnum.ADVANCED_CHAT
|
||||
return appInfo.mode === 'workflow' || appInfo.mode === 'advanced-chat'
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
} from '@/service/annotation'
|
||||
import { renderWithConsoleQuery } from '@/test/console/query-data'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import Annotation from '../index'
|
||||
import { AnnotationEnableStatus, JobStatus } from '../type'
|
||||
|
||||
@@ -181,7 +180,7 @@ const updateAnnotationStatusMock = updateAnnotationStatus as Mock
|
||||
|
||||
const appDetail = createAppDetailFixture({
|
||||
id: 'app-id',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
})
|
||||
|
||||
const createAnnotation = (overrides: Partial<AnnotationItem> = {}): AnnotationItem => ({
|
||||
|
||||
@@ -35,7 +35,6 @@ import {
|
||||
updateAnnotationStatus,
|
||||
} from '@/service/annotation'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { sleep } from '@/utils'
|
||||
import PageTitle from '../log-annotation/page-title'
|
||||
import EmptyElement from './empty-element'
|
||||
@@ -55,7 +54,7 @@ const Annotation: FC<Props> = (props) => {
|
||||
const docLink = useDocLink()
|
||||
const [isShowEdit, setIsShowEdit] = useState(false)
|
||||
const [annotationConfig, setAnnotationConfig] = useState<AnnotationReplyConfig | null>(null)
|
||||
const [isChatApp] = useState(appDetail.mode !== AppModeEnum.COMPLETION)
|
||||
const [isChatApp] = useState(appDetail.mode !== 'completion')
|
||||
const [controlRefreshSwitch, setControlRefreshSwitch] = useState(() => Date.now())
|
||||
const deploymentEdition = useAtomValue(deploymentEditionAtom)
|
||||
const { data: annotationQuota } = useQuery(
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
renderWithConsoleQuery as renderWithQuery,
|
||||
} from '@/test/console/query-data'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { AppACLPermission } from '@/utils/permission'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { AppPublisher } from '../index'
|
||||
@@ -269,7 +268,7 @@ describe('AppPublisher', () => {
|
||||
mockAppDetail = {
|
||||
id: 'app-1',
|
||||
name: 'Demo App',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
maintainer: 'user-2',
|
||||
permission_keys: [],
|
||||
access_mode: AccessMode.SPECIFIC_GROUPS_MEMBERS,
|
||||
@@ -346,7 +345,7 @@ describe('AppPublisher', () => {
|
||||
const invalidateQueries = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
permission_keys: [AppACLPermission.Deploy],
|
||||
}
|
||||
mockOnPublish.mockResolvedValue(undefined)
|
||||
@@ -398,7 +397,7 @@ describe('AppPublisher', () => {
|
||||
const invalidateQueries = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
permission_keys: [],
|
||||
}
|
||||
mockOnPublish.mockResolvedValue(undefined)
|
||||
@@ -423,7 +422,7 @@ describe('AppPublisher', () => {
|
||||
const user = userEvent.setup()
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
}
|
||||
mockPublishedWorkflow = {
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
@@ -458,7 +457,7 @@ describe('AppPublisher', () => {
|
||||
expect(mockUpdateWorkflow).toHaveBeenCalledWith(
|
||||
{
|
||||
appId: 'app-1',
|
||||
appMode: AppModeEnum.WORKFLOW,
|
||||
appMode: 'workflow' as const,
|
||||
url: '/apps/app-1/workflows/workflow-version-5',
|
||||
title: 'Release 6',
|
||||
releaseNotes: 'Updated notes',
|
||||
@@ -480,7 +479,7 @@ describe('AppPublisher', () => {
|
||||
const queryClient = createConsoleQueryClient()
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
permission_keys: [AppACLPermission.Deploy],
|
||||
}
|
||||
const environmentsQuery =
|
||||
@@ -513,7 +512,7 @@ describe('AppPublisher', () => {
|
||||
it('should keep the workflow publisher single-environment without app deploy ACL', () => {
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
permission_keys: [],
|
||||
}
|
||||
|
||||
@@ -581,7 +580,7 @@ describe('AppPublisher', () => {
|
||||
}
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
permission_keys: [AppACLPermission.Deploy],
|
||||
}
|
||||
const environmentsQuery =
|
||||
@@ -684,7 +683,7 @@ describe('AppPublisher', () => {
|
||||
const detailRequests: Request[] = []
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
permission_keys: [AppACLPermission.Deploy],
|
||||
}
|
||||
const environmentsQuery =
|
||||
@@ -832,7 +831,7 @@ describe('AppPublisher', () => {
|
||||
it('should keep workflow tool drawer mounted after closing the publish popover', () => {
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
}
|
||||
|
||||
render(<AppPublisher appId="app-1" publishedAt={Date.now()} toolPublished />)
|
||||
@@ -853,7 +852,7 @@ describe('AppPublisher', () => {
|
||||
it('should show one success toast when automatically publishing a workflow tool', async () => {
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
}
|
||||
mockOnPublish.mockImplementation(async (_params, options?: { showSuccessToast?: boolean }) => {
|
||||
if (options?.showSuccessToast !== false) mockToastSuccess('common.api.actionSuccess')
|
||||
@@ -876,7 +875,7 @@ describe('AppPublisher', () => {
|
||||
it('should not show a success toast when workflow tool creation fails after publishing', async () => {
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
}
|
||||
mockOnPublish.mockImplementation(async (_params, options?: { showSuccessToast?: boolean }) => {
|
||||
if (options?.showSuccessToast !== false) mockToastSuccess('common.api.actionSuccess')
|
||||
@@ -900,7 +899,7 @@ describe('AppPublisher', () => {
|
||||
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
}
|
||||
mockOnPublish.mockRejectedValueOnce(new Error('publish failed'))
|
||||
|
||||
@@ -922,7 +921,7 @@ describe('AppPublisher', () => {
|
||||
mockWorkspacePermissionKeys = []
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
}
|
||||
|
||||
render(<AppPublisher appId="app-1" publishedAt={Date.now()} />)
|
||||
@@ -1066,7 +1065,7 @@ describe('AppPublisher', () => {
|
||||
const user = userEvent.setup()
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
}
|
||||
mockPublishedWorkflow = {
|
||||
created_at: 1_710_000_100,
|
||||
@@ -1102,7 +1101,7 @@ describe('AppPublisher', () => {
|
||||
}
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow' as const,
|
||||
}
|
||||
mockFetchPublishedWorkflow.mockResolvedValue({
|
||||
created_at: 1_710_000_300,
|
||||
|
||||
@@ -2,7 +2,6 @@ import { detectPlatform } from '@tanstack/react-hotkeys'
|
||||
import { act, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { useState } from 'react'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { PublisherPanel } from '../publisher-content/publisher-panel'
|
||||
|
||||
vi.mock('../environment-deployment-flow', () => ({
|
||||
@@ -38,7 +37,7 @@ function PublisherPanelHarness({
|
||||
builtInPublisher={{
|
||||
actions: {
|
||||
appId: 'app-1',
|
||||
appMode: AppModeEnum.CHAT,
|
||||
appMode: 'chat' as const,
|
||||
appURL: 'https://example.com/app',
|
||||
canViewAccessPoint: false,
|
||||
disabledFunctionButton: false,
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useRef } from 'react'
|
||||
import { expectLoadingButton } from '@/test/button'
|
||||
import { createConsoleQueryWrapper } from '@/test/console/query-data'
|
||||
import { render as renderWithConsoleState } from '@/test/console/render'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { PublisherActionsSection } from '../built-in-publisher/actions-section'
|
||||
import { PublisherSummarySection } from '../built-in-publisher/summary-section'
|
||||
import { usePublishController } from '../publisher-content/use-publish-controller'
|
||||
@@ -82,7 +81,7 @@ function PublisherPopup({
|
||||
|
||||
function PublishingPopup({ onPublish }: { onPublish: () => Promise<void> }) {
|
||||
const publish = usePublishController({
|
||||
appMode: AppModeEnum.CHAT,
|
||||
appMode: 'chat' as const,
|
||||
supportsMultiEnvironment: false,
|
||||
onClose: () => {},
|
||||
onPublish,
|
||||
@@ -535,7 +534,7 @@ describe('app-publisher sections', () => {
|
||||
render(
|
||||
<PublisherActionsSection
|
||||
appId={'workflow-app'}
|
||||
appMode={AppModeEnum.WORKFLOW}
|
||||
appMode={'workflow'}
|
||||
appURL="https://example.com/app"
|
||||
canViewAccessPoint
|
||||
disabledFunctionButton={false}
|
||||
@@ -595,7 +594,7 @@ describe('app-publisher sections', () => {
|
||||
render(
|
||||
<PublisherActionsSection
|
||||
appId={'workflow-app'}
|
||||
appMode={AppModeEnum.WORKFLOW}
|
||||
appMode={'workflow'}
|
||||
appURL="https://example.com/app"
|
||||
canViewAccessPoint
|
||||
disabledFunctionButton={false}
|
||||
@@ -626,7 +625,7 @@ describe('app-publisher sections', () => {
|
||||
it('should show the disabled reason below setup and configured workflow tool actions', () => {
|
||||
const commonProps = {
|
||||
appId: 'workflow-app',
|
||||
appMode: AppModeEnum.WORKFLOW,
|
||||
appMode: 'workflow' as const,
|
||||
appURL: 'https://example.com/app',
|
||||
canViewAccessPoint: true,
|
||||
disabledFunctionButton: false,
|
||||
@@ -666,7 +665,7 @@ describe('app-publisher sections', () => {
|
||||
const onConfigureWorkflowTool = vi.fn()
|
||||
const commonProps = {
|
||||
appId: 'workflow-app',
|
||||
appMode: AppModeEnum.WORKFLOW,
|
||||
appMode: 'workflow' as const,
|
||||
appURL: 'https://example.com/app',
|
||||
canViewAccessPoint: true,
|
||||
disabledFunctionButton: false,
|
||||
@@ -710,7 +709,7 @@ describe('app-publisher sections', () => {
|
||||
render(
|
||||
<PublisherActionsSection
|
||||
appId={'trigger-app'}
|
||||
appMode={AppModeEnum.WORKFLOW}
|
||||
appMode={'workflow'}
|
||||
appURL="https://example.com/app"
|
||||
canViewAccessPoint
|
||||
disabledFunctionButton={false}
|
||||
@@ -740,7 +739,7 @@ describe('app-publisher sections', () => {
|
||||
render(
|
||||
<PublisherActionsSection
|
||||
appId={'workflow-app'}
|
||||
appMode={AppModeEnum.WORKFLOW}
|
||||
appMode={'workflow'}
|
||||
appURL="https://example.com/app"
|
||||
canViewAccessPoint={false}
|
||||
disabledFunctionButton={false}
|
||||
@@ -765,7 +764,7 @@ describe('app-publisher sections', () => {
|
||||
render(
|
||||
<PublisherActionsSection
|
||||
appId={'workflow-app'}
|
||||
appMode={AppModeEnum.WORKFLOW}
|
||||
appMode={'workflow'}
|
||||
appURL="https://example.com/app"
|
||||
canViewAccessPoint
|
||||
disabledFunctionButton
|
||||
@@ -795,7 +794,7 @@ describe('app-publisher sections', () => {
|
||||
render(
|
||||
<PublisherActionsSection
|
||||
appId={'workflow-app'}
|
||||
appMode={AppModeEnum.WORKFLOW}
|
||||
appMode={'workflow'}
|
||||
appURL="https://example.com/app"
|
||||
canViewAccessPoint
|
||||
disabledFunctionButton
|
||||
@@ -822,7 +821,7 @@ describe('app-publisher sections', () => {
|
||||
render(
|
||||
<PublisherActionsSection
|
||||
appId={'workflow-app'}
|
||||
appMode={AppModeEnum.WORKFLOW}
|
||||
appMode={'workflow'}
|
||||
appURL="https://example.com/app"
|
||||
canViewAccessPoint
|
||||
disabledFunctionButton
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import type { TFunction } from 'i18next'
|
||||
import { withSelectorKey } from '@/test/i18n-mock'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { getDisabledFunctionTooltip, getPublisherAppMode, getPublisherAppUrl } from '../utils'
|
||||
|
||||
describe('app-publisher utils', () => {
|
||||
describe('getPublisherAppMode', () => {
|
||||
it('should normalize chat-like apps to chat mode', () => {
|
||||
expect(getPublisherAppMode(AppModeEnum.AGENT_CHAT)).toBe(AppModeEnum.CHAT)
|
||||
expect(getPublisherAppMode('agent-chat')).toBe('chat')
|
||||
})
|
||||
|
||||
it('should keep completion mode unchanged', () => {
|
||||
expect(getPublisherAppMode(AppModeEnum.COMPLETION)).toBe(AppModeEnum.COMPLETION)
|
||||
expect(getPublisherAppMode('completion')).toBe('completion')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -21,7 +20,7 @@ describe('app-publisher utils', () => {
|
||||
getPublisherAppUrl({
|
||||
appBaseUrl: 'https://example.com',
|
||||
accessToken: 'token-1',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
}),
|
||||
).toBe(`https://example.com${basePath}/chat/token-1`)
|
||||
})
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { AppPublisherProps } from '../types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import SuggestedAction from '../suggested-action'
|
||||
import WorkflowToolAction from '../workflow-tool-action'
|
||||
|
||||
@@ -58,7 +57,7 @@ export function PublisherActionsSection({
|
||||
|
||||
const hasPublishedVersion = Boolean(publishedAt)
|
||||
const showOpenWebApp = !hasTriggerNode
|
||||
const showWorkflowTool = appMode === AppModeEnum.WORKFLOW && !hasHumanInputNode && !hasTriggerNode
|
||||
const showWorkflowTool = appMode === 'workflow' && !hasHumanInputNode && !hasTriggerNode
|
||||
const navigationDisabled = !hasPublishedVersion
|
||||
const workflowToolDisabled =
|
||||
!hasPublishedVersion || !workflowToolAvailable || (toolPublished && workflowToolIsLoading)
|
||||
|
||||
@@ -7,13 +7,12 @@ import { useTranslation } from 'react-i18next'
|
||||
import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getAppACLCapabilities } from '@/utils/permission'
|
||||
import { PublisherContent } from './publisher-content'
|
||||
import { appPublisherOpenAtom, AppPublisherStateBoundary } from './state'
|
||||
|
||||
export function AppPublisher(props: AppPublisherProps) {
|
||||
const { t } = useTranslation(['workflow'])
|
||||
const { t } = useTranslation(['workflow'] as const)
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
@@ -45,8 +44,7 @@ function AppPublisherContent(props: AppPublisherProps) {
|
||||
workspacePermissionKeys,
|
||||
})
|
||||
const supportsMultiEnvironment =
|
||||
(appDetail.mode === AppModeEnum.WORKFLOW || appDetail.mode === AppModeEnum.ADVANCED_CHAT) &&
|
||||
canDeploy
|
||||
(appDetail.mode === 'workflow' || appDetail.mode === 'advanced-chat') && canDeploy
|
||||
|
||||
return (
|
||||
<AppPublisherStateBoundary
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
appWorkflowQueryOptions,
|
||||
appWorkflowVersionsInfiniteQueryOptions,
|
||||
} from '@/service/workflow-queries'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
|
||||
type UsePublishControllerParams = Pick<
|
||||
AppPublisherProps,
|
||||
@@ -66,11 +65,8 @@ export function usePublishController({
|
||||
const queryClient = useQueryClient()
|
||||
const workflowStore = use(WorkflowContext)
|
||||
const invalidateAppWorkflow = useInvalidateAppWorkflow()
|
||||
const isWorkflowApp = appMode === AppModeEnum.WORKFLOW || appMode === AppModeEnum.ADVANCED_CHAT
|
||||
const isChatApp =
|
||||
appMode === AppModeEnum.CHAT ||
|
||||
appMode === AppModeEnum.AGENT_CHAT ||
|
||||
appMode === AppModeEnum.COMPLETION
|
||||
const isWorkflowApp = appMode === 'workflow' || appMode === 'advanced-chat'
|
||||
const isChatApp = appMode === 'chat' || appMode === 'agent-chat' || appMode === 'completion'
|
||||
const {
|
||||
data: publishedWorkflow,
|
||||
isError: isPublishedWorkflowError,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useCanManageTools } from '@/app/components/tools/hooks/use-tool-permissions'
|
||||
import { useConfigureButton } from '@/app/components/tools/workflow-tool/hooks/use-configure-button'
|
||||
import { appDefaultIconBackground } from '@/config'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
|
||||
type UseWorkflowToolParams = {
|
||||
appDescription?: string
|
||||
@@ -50,10 +49,10 @@ export function useWorkflowTool({
|
||||
toolPublished,
|
||||
workflowToolAvailable,
|
||||
}: UseWorkflowToolParams) {
|
||||
const { t } = useTranslation(['workflow'])
|
||||
const { t } = useTranslation(['workflow'] as const)
|
||||
const [drawerOpen, setDrawerOpen] = useState(false)
|
||||
const canManageTools = useCanManageTools()
|
||||
const visible = appMode === AppModeEnum.WORKFLOW && !hasHumanInputNode && !hasTriggerNode
|
||||
const visible = appMode === 'workflow' && !hasHumanInputNode && !hasTriggerNode
|
||||
const published = Boolean(toolPublished)
|
||||
const message =
|
||||
!hasPublishedVersion || !workflowToolAvailable
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { AppMode } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { TFunction } from 'i18next'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { basePath } from '@/utils/var'
|
||||
|
||||
export const getPublisherAppMode = (mode?: AppMode) => {
|
||||
if (mode !== AppModeEnum.COMPLETION && mode !== AppModeEnum.WORKFLOW) return AppModeEnum.CHAT
|
||||
if (mode !== 'completion' && mode !== 'workflow') return 'chat'
|
||||
|
||||
return mode
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import * as React from 'react'
|
||||
import { PluginCategoryEnum } from '@/app/components/plugins/types'
|
||||
import { CollectionType } from '@/app/components/tools/types'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import ConfigurationView from '../configuration-view'
|
||||
|
||||
const mockIsAgentV2Enabled = vi.hoisted(() => vi.fn(() => false))
|
||||
@@ -96,7 +96,7 @@ const createContextValue = (): ComponentProps<typeof ConfigContext.Provider>['va
|
||||
appId: 'app-1',
|
||||
onOpenFeatures: vi.fn(),
|
||||
isTrailFinished: false,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
modelModeType: ModelModeType.chat,
|
||||
promptMode: 'simple' as never,
|
||||
setPromptMode: vi.fn(),
|
||||
@@ -351,7 +351,7 @@ describe('ConfigurationView', () => {
|
||||
it('should show the legacy Agent badge for legacy Agent apps when Agent v2 is enabled', async () => {
|
||||
mockIsAgentV2Enabled.mockReturnValue(true)
|
||||
const contextValue = createContextValue()
|
||||
contextValue.mode = AppModeEnum.AGENT_CHAT
|
||||
contextValue.mode = 'agent-chat'
|
||||
|
||||
render(<ConfigurationView {...createViewModel({ contextValue })} />)
|
||||
|
||||
@@ -375,7 +375,7 @@ describe('ConfigurationView', () => {
|
||||
|
||||
it('should not show the legacy Agent badge when Agent v2 is disabled', () => {
|
||||
const contextValue = createContextValue()
|
||||
contextValue.mode = AppModeEnum.AGENT_CHAT
|
||||
contextValue.mode = 'agent-chat'
|
||||
|
||||
render(<ConfigurationView {...createViewModel({ contextValue })} />)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ModelConfig } from '@/models/debug'
|
||||
import { AppModeEnum, ModelModeType, Resolution, TransferMethod } from '@/types/app'
|
||||
import { ModelModeType, Resolution, TransferMethod } from '@/types/app'
|
||||
import {
|
||||
buildConfigurationFeaturesData,
|
||||
getConfigurationPublishingState,
|
||||
@@ -129,7 +129,7 @@ describe('configuration utils', () => {
|
||||
hasSetContextVar: false,
|
||||
hasSelectedDataSets: false,
|
||||
isAdvancedMode: true,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
modelModeType: ModelModeType.completion,
|
||||
promptTemplate: 'ignored',
|
||||
})
|
||||
@@ -158,7 +158,7 @@ describe('configuration utils', () => {
|
||||
hasSetContextVar: false,
|
||||
hasSelectedDataSets: true,
|
||||
isAdvancedMode: false,
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
modelModeType: ModelModeType.completion,
|
||||
promptTemplate: 'Prompt',
|
||||
})
|
||||
@@ -188,7 +188,7 @@ describe('configuration utils', () => {
|
||||
hasSetContextVar: true,
|
||||
hasSelectedDataSets: false,
|
||||
isAdvancedMode: true,
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
modelModeType: ModelModeType.chat,
|
||||
promptTemplate: 'ignored',
|
||||
})
|
||||
@@ -217,7 +217,7 @@ describe('configuration utils', () => {
|
||||
hasSetContextVar: true,
|
||||
hasSelectedDataSets: false,
|
||||
isAdvancedMode: true,
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
modelModeType: ModelModeType.completion,
|
||||
promptTemplate: 'ignored',
|
||||
})
|
||||
|
||||
+1
-2
@@ -4,7 +4,6 @@ import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/prompt-editor/plugins/variable-block'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import AdvancedPromptInput from '../advanced-prompt-input'
|
||||
|
||||
const mockEmit = vi.fn()
|
||||
@@ -82,7 +81,7 @@ vi.mock('../prompt-editor-height-resize-wrap', () => ({
|
||||
|
||||
const createContextValue = () =>
|
||||
({
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
hasSetBlockStatus: {
|
||||
context: false,
|
||||
history: false,
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as React from 'react'
|
||||
import { MAX_PROMPT_MESSAGE_LENGTH } from '@/config'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { PromptRole } from '@/models/debug'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import Prompt from '../index'
|
||||
|
||||
type DebugConfiguration = {
|
||||
@@ -113,7 +113,7 @@ const renderComponent = (
|
||||
contextOverrides: Partial<DebugConfiguration> = {},
|
||||
) => {
|
||||
const mergedProps: IPromptProps = {
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
promptTemplate: 'initial template',
|
||||
promptVariables: defaultPromptVariables,
|
||||
onChange: vi.fn(),
|
||||
@@ -144,7 +144,7 @@ describe('Prompt config component', () => {
|
||||
|
||||
const simplePrompt = screen.getByTestId('simple-prompt-input')
|
||||
expect(simplePrompt)!.toBeInTheDocument()
|
||||
expect(simplePrompt)!.toHaveAttribute('data-mode', AppModeEnum.CHAT)
|
||||
expect(simplePrompt)!.toHaveAttribute('data-mode', 'chat')
|
||||
expect(mockSimplePromptInputProps?.promptTemplate).toBe('initial template')
|
||||
fireEvent.click(simplePrompt)
|
||||
expect(onChange).toHaveBeenCalledWith('mocked prompt', defaultPromptVariables)
|
||||
|
||||
+2
-5
@@ -1,5 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import Prompt from '../simple-prompt-input'
|
||||
|
||||
vi.mock('@/app/components/base/features/hooks', () => ({
|
||||
@@ -9,7 +8,7 @@ vi.mock('@/app/components/base/features/hooks', () => ({
|
||||
}))
|
||||
|
||||
describe('SimplePromptInput accessibility', () => {
|
||||
it.each([AppModeEnum.CHAT, AppModeEnum.COMPLETION])(
|
||||
it.each(['chat', 'completion'] as const)(
|
||||
'names the real editor from the visible section heading in %s mode',
|
||||
(mode) => {
|
||||
render(<Prompt mode={mode} promptTemplate="" promptVariables={[]} noResize />)
|
||||
@@ -22,9 +21,7 @@ describe('SimplePromptInput accessibility', () => {
|
||||
)
|
||||
|
||||
it('keeps an accessible editor name when embedded without a section heading', () => {
|
||||
render(
|
||||
<Prompt mode={AppModeEnum.CHAT} promptTemplate="" promptVariables={[]} noTitle noResize />,
|
||||
)
|
||||
render(<Prompt mode={'chat'} promptTemplate="" promptVariables={[]} noTitle noResize />)
|
||||
expect(screen.getByRole('textbox', { name: 'appDebug.chatSubTitle' })).toBeInTheDocument()
|
||||
expect(screen.queryByRole('heading')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
+6
-12
@@ -2,7 +2,6 @@ import type { ReactNode } from 'react'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/prompt-editor/plugins/variable-block'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import Prompt from '../simple-prompt-input'
|
||||
|
||||
const mockEmit = vi.fn()
|
||||
@@ -144,7 +143,7 @@ describe('SimplePromptInput', () => {
|
||||
render(
|
||||
<ConfigContext.Provider value={createContextValue()}>
|
||||
<Prompt
|
||||
mode={AppModeEnum.CHAT}
|
||||
mode={'chat'}
|
||||
promptTemplate="Hello {{new_var}}"
|
||||
promptVariables={[]}
|
||||
onChange={mockOnChange}
|
||||
@@ -170,7 +169,7 @@ describe('SimplePromptInput', () => {
|
||||
render(
|
||||
<ConfigContext.Provider value={createContextValue()}>
|
||||
<Prompt
|
||||
mode={AppModeEnum.CHAT}
|
||||
mode={'chat'}
|
||||
promptTemplate="Hello"
|
||||
promptVariables={[
|
||||
{ key: 'existing_var', name: 'Existing', type: 'string', required: true },
|
||||
@@ -215,12 +214,7 @@ describe('SimplePromptInput', () => {
|
||||
it('should apply automatic generation results to prompt and opening statement', () => {
|
||||
render(
|
||||
<ConfigContext.Provider value={createContextValue()}>
|
||||
<Prompt
|
||||
mode={AppModeEnum.CHAT}
|
||||
promptTemplate="Hello"
|
||||
promptVariables={[]}
|
||||
onChange={mockOnChange}
|
||||
/>
|
||||
<Prompt mode={'chat'} promptTemplate="Hello" promptVariables={[]} onChange={mockOnChange} />
|
||||
</ConfigContext.Provider>,
|
||||
)
|
||||
|
||||
@@ -275,7 +269,7 @@ describe('SimplePromptInput', () => {
|
||||
})}
|
||||
>
|
||||
<Prompt
|
||||
mode={AppModeEnum.CHAT}
|
||||
mode={'chat'}
|
||||
promptTemplate="Hello {{existing_var}}"
|
||||
promptVariables={[
|
||||
{ key: 'existing_var', name: 'Existing', type: 'string', required: true },
|
||||
@@ -300,7 +294,7 @@ describe('SimplePromptInput', () => {
|
||||
})}
|
||||
>
|
||||
<Prompt
|
||||
mode={AppModeEnum.CHAT}
|
||||
mode={'chat'}
|
||||
promptTemplate="Hello {{search_api}} {{existing_var}}"
|
||||
promptVariables={[
|
||||
{ key: 'existing_var', name: 'Existing', type: 'string', required: true },
|
||||
@@ -321,7 +315,7 @@ describe('SimplePromptInput', () => {
|
||||
render(
|
||||
<ConfigContext.Provider value={createContextValue()}>
|
||||
<Prompt
|
||||
mode={AppModeEnum.CHAT}
|
||||
mode={'chat'}
|
||||
promptTemplate="Hello {{existing_var}}"
|
||||
promptVariables={[{ key: 'existing_var', name: '', type: 'string', required: true }]}
|
||||
onChange={mockOnChange}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import type { ExternalDataTool } from '@/models/common'
|
||||
import type { PromptRole, PromptVariable } from '@/models/debug'
|
||||
@@ -20,7 +21,6 @@ import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/promp
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getNewVar, getVars } from '@/utils/var'
|
||||
import ConfirmAddVar from './confirm-add-var'
|
||||
import MessageTypeSelector from './message-type-selector'
|
||||
@@ -102,7 +102,7 @@ const AdvancedPromptInput: FC<Props> = ({
|
||||
},
|
||||
})
|
||||
}
|
||||
const isChatApp = mode !== AppModeEnum.COMPLETION
|
||||
const isChatApp = mode !== 'completion'
|
||||
const [isCopied, setIsCopied] = React.useState(false)
|
||||
|
||||
const promptVariablesObj = (() => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { PromptItem, PromptVariable } from '@/models/debug'
|
||||
import type { AppModeEnum } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { RiAddLine } from '@remixicon/react'
|
||||
import { produce } from 'immer'
|
||||
@@ -16,7 +17,7 @@ import { ModelModeType } from '@/types/app'
|
||||
import SimplePromptInput from './simple-prompt-input'
|
||||
|
||||
export type IPromptProps = {
|
||||
mode: AppModeEnum
|
||||
mode: ConfigurationAppMode
|
||||
promptTemplate: string
|
||||
promptVariables: PromptVariable[]
|
||||
readonly?: boolean
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { ExternalDataTool } from '@/models/common'
|
||||
import type { PromptVariable } from '@/models/debug'
|
||||
import type { GenRes } from '@/service/debug'
|
||||
@@ -24,13 +26,12 @@ import ConfigContext from '@/context/debug-configuration'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getNewVar, getVars } from '@/utils/var'
|
||||
import ConfirmAddVar from './confirm-add-var'
|
||||
import PromptEditorHeightResizeWrap from './prompt-editor-height-resize-wrap'
|
||||
|
||||
type ISimplePromptInput = {
|
||||
mode: AppModeEnum
|
||||
mode: ConfigurationAppMode
|
||||
promptTemplate: string
|
||||
promptVariables: PromptVariable[]
|
||||
readonly?: boolean
|
||||
@@ -54,7 +55,7 @@ const Prompt: FC<ISimplePromptInput> = ({
|
||||
const { t } = useTranslation(['appDebug'])
|
||||
const titleId = useId()
|
||||
const title =
|
||||
mode !== AppModeEnum.COMPLETION
|
||||
mode !== 'completion'
|
||||
? t(($) => $.chatSubTitle, { ns: 'appDebug' })
|
||||
: t(($) => $.completionSubTitle, { ns: 'appDebug' })
|
||||
const media = useBreakpoints()
|
||||
@@ -66,6 +67,7 @@ const Prompt: FC<ISimplePromptInput> = ({
|
||||
const {
|
||||
appId,
|
||||
modelConfig,
|
||||
modelModeType,
|
||||
dataSets,
|
||||
setModelConfig,
|
||||
setPrevPromptConfig,
|
||||
@@ -176,7 +178,7 @@ const Prompt: FC<ISimplePromptInput> = ({
|
||||
setModelConfig(newModelConfig)
|
||||
setPrevPromptConfig(modelConfig.configs)
|
||||
|
||||
if (mode !== AppModeEnum.COMPLETION) {
|
||||
if (mode !== 'completion') {
|
||||
setIntroduction(res.opening_statement || '')
|
||||
const newFeatures = produce(features, (draft) => {
|
||||
draft.opening = {
|
||||
@@ -314,7 +316,7 @@ const Prompt: FC<ISimplePromptInput> = ({
|
||||
{showAutomatic && (
|
||||
<GetAutomaticResModal
|
||||
flowId={appId}
|
||||
mode={mode as AppModeEnum}
|
||||
modelMode={modelModeType}
|
||||
isShow={showAutomatic}
|
||||
onClose={showAutomaticFalse}
|
||||
onFinished={handleAutomaticRes}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { vi } from 'vite-plus/test'
|
||||
import { toast } from '@/app/components/app/configuration/toast'
|
||||
import DebugConfigurationContext from '@/context/debug-configuration'
|
||||
import { renderWithConsoleQuery as render } from '@/test/console/query-data'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import ConfigVar, { ADD_EXTERNAL_DATA_TOOL } from '../index'
|
||||
|
||||
const toastErrorSpy = vi.spyOn(toast, 'error').mockReturnValue('toast-error')
|
||||
@@ -69,7 +68,7 @@ type DebugConfigurationState = React.ComponentProps<
|
||||
>['value']
|
||||
|
||||
const defaultDebugConfigValue = {
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
dataSets: [],
|
||||
modelConfig: {
|
||||
model_id: 'test-model',
|
||||
@@ -399,7 +398,7 @@ describe('ConfigVar', () => {
|
||||
onPromptVariablesChange,
|
||||
},
|
||||
{
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
dataSets: [{ id: 'dataset-1' } as DebugConfigurationState['dataSets'][number]],
|
||||
},
|
||||
)
|
||||
|
||||
+2
-3
@@ -10,7 +10,6 @@ import {
|
||||
seedAppDetail,
|
||||
} from '@/test/console/query-data'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import ConfigModal from '../index'
|
||||
|
||||
vi.mock('@/next/navigation', async (importOriginal) => ({
|
||||
@@ -116,12 +115,12 @@ const createPayload = (overrides: Partial<InputVar> = {}): InputVar => ({
|
||||
|
||||
const renderConfigModal = (payload: InputVar = createPayload()) => {
|
||||
const queryClient = createConsoleQueryClient()
|
||||
seedAppDetail(queryClient, createAppDetailFixture({ id: 'app-1', mode: AppModeEnum.CHAT }))
|
||||
seedAppDetail(queryClient, createAppDetailFixture({ id: 'app-1', mode: 'chat' as const }))
|
||||
return renderWithConsoleQuery(
|
||||
<DebugConfigurationContext.Provider
|
||||
value={
|
||||
{
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
dataSets: [],
|
||||
modelConfig: { model_id: 'model-1' },
|
||||
} as any
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import {
|
||||
seedAppDetail,
|
||||
} from '@/test/console/query-data'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum, TransferMethod } from '@/types/app'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import ConfigModal from '../index'
|
||||
|
||||
vi.mock('@/next/navigation', async (importOriginal) => ({
|
||||
@@ -37,7 +37,7 @@ vi.mock('@monaco-editor/react', async () => ({
|
||||
|
||||
const render = (ui: React.ReactElement) => {
|
||||
const queryClient = createConsoleQueryClient()
|
||||
seedAppDetail(queryClient, createAppDetailFixture({ id: 'app-1', mode: AppModeEnum.CHAT }))
|
||||
seedAppDetail(queryClient, createAppDetailFixture({ id: 'app-1', mode: 'chat' as const }))
|
||||
return renderWithConsoleQuery(ui, { queryClient })
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { ChangeEvent, FC } from 'react'
|
||||
import type { Item as SelectItem } from './type-select'
|
||||
import type { ConfigModalValidationError } from './utils'
|
||||
@@ -15,7 +16,6 @@ import ConfigContext from '@/context/debug-configuration'
|
||||
import { useParams } from '@/next/navigation'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { commonQueryKeys } from '@/service/use-common'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import {
|
||||
checkKeys,
|
||||
getNewVarInWorkflow,
|
||||
@@ -69,8 +69,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
||||
input: appId ? { params: { app_id: appId } } : skipToken,
|
||||
}),
|
||||
)
|
||||
const isBasicApp =
|
||||
appDetail?.mode !== AppModeEnum.ADVANCED_CHAT && appDetail?.mode !== AppModeEnum.WORKFLOW
|
||||
const isBasicApp = appDetail?.mode !== 'advanced-chat' && appDetail?.mode !== 'workflow'
|
||||
const jsonSchemaStr = useMemo(
|
||||
() => getJsonSchemaEditorValue(type, tempPayload.json_schema),
|
||||
[tempPayload.json_schema, type],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import type { InputVar } from '@/app/components/workflow/types'
|
||||
import type { ExternalDataTool } from '@/models/common'
|
||||
@@ -27,7 +28,6 @@ import { InputVarType } from '@/app/components/workflow/types'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getNewVar, hasDuplicateStr } from '@/utils/var'
|
||||
import Panel from '../base/feature-panel'
|
||||
import EditModal from './config-modal'
|
||||
@@ -244,7 +244,7 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
|
||||
(index: number) => {
|
||||
const removeVar = promptVariables[index]
|
||||
|
||||
if (mode === AppModeEnum.COMPLETION && dataSets.length > 0 && removeVar!.is_context_var) {
|
||||
if (mode === 'completion' && dataSets.length > 0 && removeVar!.is_context_var) {
|
||||
showDeleteContextVarModal()
|
||||
setRemoveIndex(index)
|
||||
return
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { AppMode } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { ModelConfig, PromptVariable } from '@/models/debug'
|
||||
import type { ToolItem } from '@/types/app'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import * as React from 'react'
|
||||
import * as useContextSelector from 'use-context-selector'
|
||||
import { AgentStrategy, AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { AgentStrategy, ModelModeType } from '@/types/app'
|
||||
import Config from '../index'
|
||||
|
||||
vi.mock('use-context-selector', async (importOriginal) => {
|
||||
@@ -64,7 +65,7 @@ vi.mock('../../config-prompt/conversation-history/history-panel', () => ({
|
||||
}))
|
||||
|
||||
type MockContext = {
|
||||
mode: AppModeEnum
|
||||
mode: AppMode
|
||||
isAdvancedMode: boolean
|
||||
modelModeType: ModelModeType
|
||||
isAgent: boolean
|
||||
@@ -118,7 +119,7 @@ const createModelConfig = (overrides: Partial<ModelConfig> = {}): ModelConfig =>
|
||||
})
|
||||
|
||||
const createContextValue = (overrides: Partial<MockContext> = {}): MockContext => ({
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
isAdvancedMode: false,
|
||||
modelModeType: ModelModeType.chat,
|
||||
isAgent: false,
|
||||
@@ -173,11 +174,11 @@ describe('Config - Rendering', () => {
|
||||
expect(screen.getByTestId('agent-tools')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should display HistoryPanel only when advanced chat completion values apply', () => {
|
||||
it('should display HistoryPanel for a chat app using an advanced completion prompt', () => {
|
||||
const showHistoryModal = vi.fn()
|
||||
renderConfig({
|
||||
isAdvancedMode: true,
|
||||
mode: AppModeEnum.ADVANCED_CHAT,
|
||||
mode: 'chat' as const,
|
||||
modelModeType: ModelModeType.completion,
|
||||
hasSetBlockStatus: {
|
||||
context: false,
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { page } from 'vite-plus/test/browser'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import GetAutomaticRes from '../get-automatic-res'
|
||||
|
||||
vi.mock('react-i18next', async () => {
|
||||
@@ -71,7 +70,7 @@ describe('Prompt generator reflow', () => {
|
||||
const screen = await render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<GetAutomaticRes
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
onClose={vi.fn()}
|
||||
onFinished={vi.fn()}
|
||||
|
||||
+10
-8
@@ -1,5 +1,4 @@
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import GetAutomaticRes from '../get-automatic-res'
|
||||
|
||||
const mockGenerateBasicAppFirstTimeRule = vi.fn()
|
||||
@@ -53,7 +52,9 @@ vi.mock(
|
||||
}) => (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setModel({ modelId: 'gpt-4o-mini', provider: 'openai', mode: 'chat' })}
|
||||
onClick={() =>
|
||||
setModel({ modelId: 'gpt-4o-mini', provider: 'openai', mode: 'chat' as const })
|
||||
}
|
||||
>
|
||||
change-model
|
||||
</button>
|
||||
@@ -121,7 +122,7 @@ describe('GetAutomaticRes', () => {
|
||||
it('should apply a basic suggestion and persist model updates', async () => {
|
||||
render(
|
||||
<GetAutomaticRes
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
onClose={mockOnClose}
|
||||
onFinished={mockOnFinished}
|
||||
@@ -152,7 +153,7 @@ describe('GetAutomaticRes', () => {
|
||||
|
||||
render(
|
||||
<GetAutomaticRes
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
onClose={mockOnClose}
|
||||
onFinished={mockOnFinished}
|
||||
@@ -176,7 +177,7 @@ describe('GetAutomaticRes', () => {
|
||||
it('should block generation when instruction is empty', () => {
|
||||
render(
|
||||
<GetAutomaticRes
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
onClose={mockOnClose}
|
||||
onFinished={mockOnFinished}
|
||||
@@ -203,7 +204,7 @@ describe('GetAutomaticRes', () => {
|
||||
|
||||
render(
|
||||
<GetAutomaticRes
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
onClose={mockOnClose}
|
||||
onFinished={mockOnFinished}
|
||||
@@ -254,7 +255,7 @@ describe('GetAutomaticRes', () => {
|
||||
|
||||
render(
|
||||
<GetAutomaticRes
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
onClose={mockOnClose}
|
||||
onFinished={mockOnFinished}
|
||||
@@ -291,7 +292,7 @@ describe('GetAutomaticRes', () => {
|
||||
|
||||
render(
|
||||
<GetAutomaticRes
|
||||
mode={AppModeEnum.ADVANCED_CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
onClose={mockOnClose}
|
||||
onFinished={mockOnFinished}
|
||||
@@ -315,6 +316,7 @@ describe('GetAutomaticRes', () => {
|
||||
current: 'current prompt',
|
||||
instruction: 'workflow instruction',
|
||||
ideal_output: 'ideal output',
|
||||
model_config: expect.objectContaining({ mode: 'chat' }),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
// type
|
||||
import type { GenRes } from '@/service/debug'
|
||||
import type { AppModeEnum, CompletionParams, Model, ModelModeType } from '@/types/app'
|
||||
import type { CompletionParams, Model, ModelModeType } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Dialog, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
@@ -31,7 +32,7 @@ import useGenData from './use-gen-data'
|
||||
const i18nPrefix = 'generate'
|
||||
|
||||
type IGetAutomaticResProps = {
|
||||
mode: AppModeEnum
|
||||
modelMode: ModelModeType
|
||||
isShow: boolean
|
||||
onClose: () => void
|
||||
onFinished: (res: GenRes) => void
|
||||
@@ -60,7 +61,7 @@ const TryLabel: FC<{
|
||||
}
|
||||
|
||||
const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
|
||||
mode,
|
||||
modelMode,
|
||||
isShow,
|
||||
onClose,
|
||||
flowId,
|
||||
@@ -84,10 +85,10 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
|
||||
return {
|
||||
name: defaultModel?.model ?? '',
|
||||
provider: defaultModel?.provider.provider ?? '',
|
||||
mode: mode as unknown as ModelModeType,
|
||||
mode: modelMode,
|
||||
completion_params: {} as CompletionParams,
|
||||
}
|
||||
}, [defaultModel, mode, selectedModel, storedModel])
|
||||
}, [defaultModel, modelMode, selectedModel, storedModel])
|
||||
const tryList = [
|
||||
{
|
||||
iconClassName: 'i-ri-terminal-box-line',
|
||||
|
||||
+10
-8
@@ -1,6 +1,5 @@
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import GetCodeGeneratorResModal from '../get-code-generator-res'
|
||||
|
||||
const mockGenerateRule = vi.fn()
|
||||
@@ -52,7 +51,9 @@ vi.mock(
|
||||
}) => (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setModel({ modelId: 'gpt-4o-mini', provider: 'openai', mode: 'chat' })}
|
||||
onClick={() =>
|
||||
setModel({ modelId: 'gpt-4o-mini', provider: 'openai', mode: 'chat' as const })
|
||||
}
|
||||
>
|
||||
change-model
|
||||
</button>
|
||||
@@ -119,7 +120,7 @@ describe('GetCodeGeneratorResModal', () => {
|
||||
flowId="flow-1"
|
||||
nodeId="node-1"
|
||||
currentCode="print(1)"
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
codeLanguages={CodeLanguage.python3}
|
||||
onClose={mockOnClose}
|
||||
@@ -146,7 +147,7 @@ describe('GetCodeGeneratorResModal', () => {
|
||||
flowId="flow-1"
|
||||
nodeId="node-1"
|
||||
currentCode="print(1)"
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
codeLanguages={CodeLanguage.python3}
|
||||
onClose={mockOnClose}
|
||||
@@ -173,7 +174,7 @@ describe('GetCodeGeneratorResModal', () => {
|
||||
flowId="flow-1"
|
||||
nodeId="node-1"
|
||||
currentCode="print(1)"
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
codeLanguages={CodeLanguage.python3}
|
||||
onClose={mockOnClose}
|
||||
@@ -200,7 +201,7 @@ describe('GetCodeGeneratorResModal', () => {
|
||||
flowId="flow-1"
|
||||
nodeId="node-1"
|
||||
currentCode="print(1)"
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="completion"
|
||||
isShow
|
||||
codeLanguages={CodeLanguage.python3}
|
||||
onClose={mockOnClose}
|
||||
@@ -221,6 +222,7 @@ describe('GetCodeGeneratorResModal', () => {
|
||||
instruction: 'code instruction',
|
||||
ideal_output: 'code output',
|
||||
language: 'python',
|
||||
model_config: expect.objectContaining({ mode: 'completion' }),
|
||||
}),
|
||||
)
|
||||
})
|
||||
@@ -255,7 +257,7 @@ describe('GetCodeGeneratorResModal', () => {
|
||||
flowId="flow-1"
|
||||
nodeId="node-1"
|
||||
currentCode="print(1)"
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
codeLanguages={CodeLanguage.python3}
|
||||
onClose={mockOnClose}
|
||||
@@ -295,7 +297,7 @@ describe('GetCodeGeneratorResModal', () => {
|
||||
flowId="flow-1"
|
||||
nodeId="node-1"
|
||||
currentCode="print(1)"
|
||||
mode={AppModeEnum.CHAT}
|
||||
modelMode="chat"
|
||||
isShow
|
||||
codeLanguages={CodeLanguage.javascript}
|
||||
onClose={mockOnClose}
|
||||
|
||||
+5
-5
@@ -2,7 +2,7 @@ import type { FC } from 'react'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import type { GenRes } from '@/service/debug'
|
||||
import type { AppModeEnum, CompletionParams, Model, ModelModeType } from '@/types/app'
|
||||
import type { CompletionParams, Model, ModelModeType } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
@@ -42,7 +42,7 @@ type IGetCodeGeneratorResProps = {
|
||||
flowId: string
|
||||
nodeId: string
|
||||
currentCode?: string
|
||||
mode: AppModeEnum
|
||||
modelMode: ModelModeType
|
||||
isShow: boolean
|
||||
codeLanguages: CodeLanguage
|
||||
onClose: () => void
|
||||
@@ -53,7 +53,7 @@ export const GetCodeGeneratorResModal: FC<IGetCodeGeneratorResProps> = ({
|
||||
flowId,
|
||||
nodeId,
|
||||
currentCode,
|
||||
mode,
|
||||
modelMode,
|
||||
isShow,
|
||||
codeLanguages,
|
||||
onClose,
|
||||
@@ -80,10 +80,10 @@ export const GetCodeGeneratorResModal: FC<IGetCodeGeneratorResProps> = ({
|
||||
return {
|
||||
name: defaultModel?.model ?? '',
|
||||
provider: defaultModel?.provider.provider ?? '',
|
||||
mode: mode as unknown as ModelModeType,
|
||||
mode: modelMode,
|
||||
completion_params: defaultCompletionParams,
|
||||
}
|
||||
}, [defaultModel, mode, selectedModel, storedModel])
|
||||
}, [defaultModel, modelMode, selectedModel, storedModel])
|
||||
const [instructionFromSessionStorage, setInstructionFromSessionStorage] =
|
||||
useSessionStorageState<string>(`improve-instruction-${flowId}-${nodeId}`)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import type { ModelConfig, PromptVariable } from '@/models/debug'
|
||||
import { produce } from 'immer'
|
||||
@@ -7,7 +8,7 @@ import { useContext } from 'use-context-selector'
|
||||
import ConfigPrompt from '@/app/components/app/configuration/config-prompt'
|
||||
import ConfigVar from '@/app/components/app/configuration/config-var'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import HistoryPanel from '../config-prompt/conversation-history/history-panel'
|
||||
import ConfigVision from '../config-vision'
|
||||
import DatasetConfig from '../dataset-config'
|
||||
@@ -30,9 +31,7 @@ const Config: FC = () => {
|
||||
setPrevPromptConfig,
|
||||
dataSets,
|
||||
} = useContext(ConfigContext)
|
||||
const isChatApp = [AppModeEnum.ADVANCED_CHAT, AppModeEnum.AGENT_CHAT, AppModeEnum.CHAT].includes(
|
||||
mode,
|
||||
)
|
||||
const isChatApp = mode === 'agent-chat' || mode === 'chat'
|
||||
const formattingChangedDispatcher = useFormattingChangedDispatcher()
|
||||
|
||||
const promptTemplate = modelConfig.configs.prompt_template
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import type { ConfigurationViewModel } from '../hooks/configuration-view-model'
|
||||
import type { InstallBundleCompleteCallback } from '@/app/components/plugins/install-plugin/install-bundle'
|
||||
@@ -40,7 +41,7 @@ import ModelParameterModal from '@/app/components/header/account-setting/model-p
|
||||
import PluginDependency from '@/app/components/workflow/plugin-dependency'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { isAgentV2Enabled } from '@/features/agent-v2/feature-flag'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import { LegacyAgentBadge } from './legacy-agent-badge'
|
||||
|
||||
const ConfigurationView: FC<ConfigurationViewModel> = ({
|
||||
@@ -80,7 +81,7 @@ const ConfigurationView: FC<ConfigurationViewModel> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation(['appDebug', 'common'])
|
||||
const debugWithMultipleModel = appPublisherProps.debugWithMultipleModel
|
||||
const showLegacyAgentBadge = isAgentV2Enabled() && contextValue.mode === AppModeEnum.AGENT_CHAT
|
||||
const showLegacyAgentBadge = isAgentV2Enabled() && contextValue.mode === 'agent-chat'
|
||||
const handlePluginInstallComplete: InstallBundleCompleteCallback = (plugins, installStatus) => {
|
||||
if (!installStatus.some((status) => status.success)) return
|
||||
|
||||
@@ -289,7 +290,7 @@ const ConfigurationView: FC<ConfigurationViewModel> = ({
|
||||
show
|
||||
inWorkflow={false}
|
||||
showFileUpload={false}
|
||||
isChatMode={contextValue.mode !== AppModeEnum.COMPLETION}
|
||||
isChatMode={contextValue.mode !== 'completion'}
|
||||
disabled={!!contextValue.readonly}
|
||||
onChange={onFeaturesChange}
|
||||
onClose={onCloseFeaturePanel}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { getSelectedDatasetsMode } from '@/app/components/workflow/nodes/knowled
|
||||
import { DatasetPermission, DataSourceType } from '@/models/datasets'
|
||||
import { createConsoleQueryWrapper } from '@/test/console/query-data'
|
||||
import { render as renderWithConsoleState } from '@/test/console/render'
|
||||
import { AppModeEnum, ModelModeType, RETRIEVE_TYPE } from '@/types/app'
|
||||
import { ModelModeType, RETRIEVE_TYPE } from '@/types/app'
|
||||
import { DatasetACLPermission, getDatasetACLCapabilities } from '@/utils/permission'
|
||||
import DatasetConfig from '../index'
|
||||
|
||||
@@ -185,7 +185,7 @@ vi.mock(
|
||||
|
||||
// Mock context
|
||||
const mockConfigContext: any = {
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
modelModeType: ModelModeType.chat,
|
||||
isAgent: false,
|
||||
dataSets: [],
|
||||
@@ -516,7 +516,7 @@ describe('DatasetConfig', () => {
|
||||
it('should show context variable selector in completion mode with datasets', () => {
|
||||
const dataset = createMockDataset()
|
||||
renderDatasetConfig({
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
dataSets: [dataset],
|
||||
modelConfig: {
|
||||
configs: {
|
||||
@@ -537,7 +537,7 @@ describe('DatasetConfig', () => {
|
||||
it('should not show context variable selector in chat mode', () => {
|
||||
const dataset = createMockDataset()
|
||||
renderDatasetConfig({
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
dataSets: [dataset],
|
||||
modelConfig: {
|
||||
configs: {
|
||||
@@ -555,7 +555,7 @@ describe('DatasetConfig', () => {
|
||||
const user = userEvent.setup()
|
||||
const dataset = createMockDataset()
|
||||
renderDatasetConfig({
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
dataSets: [dataset],
|
||||
modelConfig: {
|
||||
configs: {
|
||||
@@ -877,7 +877,7 @@ describe('DatasetConfig', () => {
|
||||
// Context var is only shown when datasets exist AND there are prompt_variables
|
||||
// Test with no datasets to ensure context var is not shown
|
||||
renderDatasetConfig({
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
dataSets: [],
|
||||
modelConfig: {
|
||||
configs: {
|
||||
@@ -960,7 +960,7 @@ describe('DatasetConfig', () => {
|
||||
metadata_model_config: {
|
||||
provider: 'openai',
|
||||
name: 'gpt-3.5-turbo',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
completion_params: { temperature: 0.7 },
|
||||
},
|
||||
},
|
||||
@@ -976,7 +976,7 @@ describe('DatasetConfig', () => {
|
||||
metadata_model_config: {
|
||||
provider: 'openai',
|
||||
name: 'gpt-4o-mini',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
completion_params: { temperature: 0.7 },
|
||||
},
|
||||
}),
|
||||
@@ -993,7 +993,7 @@ describe('DatasetConfig', () => {
|
||||
metadata_model_config: {
|
||||
provider: 'openai',
|
||||
name: 'gpt-3.5-turbo',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
completion_params: { temperature: 0.5, max_tokens: 100 },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getDatasetACLCapabilities } from '@/utils/permission'
|
||||
import FeaturePanel from '../base/feature-panel'
|
||||
import { OperationButton } from '../base/operation-button'
|
||||
@@ -269,7 +268,7 @@ const DatasetConfig: FC<Props> = ({ readonly, hideMetadataFilter }) => {
|
||||
draft.metadata_model_config = {
|
||||
provider: model.provider,
|
||||
name: model.modelId,
|
||||
mode: model.mode || AppModeEnum.CHAT,
|
||||
mode: model.mode || 'chat',
|
||||
completion_params: draft.metadata_model_config?.completion_params || { temperature: 0.7 },
|
||||
}
|
||||
})
|
||||
@@ -355,7 +354,7 @@ const DatasetConfig: FC<Props> = ({ readonly, hideMetadataFilter }) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!readonly && mode === AppModeEnum.COMPLETION && dataSet.length > 0 && (
|
||||
{!readonly && mode === 'completion' && dataSet.length > 0 && (
|
||||
<ContextVar
|
||||
value={selectedContextVar?.key}
|
||||
options={promptVariablesToSelect}
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as React from 'react'
|
||||
import Log from '@/app/components/base/chat/chat/log'
|
||||
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { AppModeEnum, ModelModeType, TransferMethod } from '@/types/app'
|
||||
import { ModelModeType, TransferMethod } from '@/types/app'
|
||||
import Debug from '../index'
|
||||
import { APP_CHAT_WITH_MULTIPLE_MODEL, APP_CHAT_WITH_MULTIPLE_MODEL_RESTART } from '../types'
|
||||
|
||||
@@ -345,7 +345,7 @@ const createContextValue = (overrides: Partial<DebugContextValue> = {}): DebugCo
|
||||
appId: 'app-id',
|
||||
onOpenFeatures: vi.fn(),
|
||||
isTrailFinished: false,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
modelModeType: ModelModeType.chat,
|
||||
promptMode: 'simple' as DebugContextValue['promptMode'],
|
||||
setPromptMode: vi.fn(),
|
||||
@@ -676,7 +676,7 @@ describe('Debug', () => {
|
||||
const { notify } = renderDebug({
|
||||
contextValue: {
|
||||
isAdvancedMode: true,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
modelModeType: ModelModeType.completion,
|
||||
hasSetBlockStatus: { context: false, history: false, query: true },
|
||||
},
|
||||
@@ -693,7 +693,7 @@ describe('Debug', () => {
|
||||
const { notify } = renderDebug({
|
||||
contextValue: {
|
||||
isAdvancedMode: true,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
modelModeType: ModelModeType.completion,
|
||||
hasSetBlockStatus: { context: false, history: true, query: false },
|
||||
},
|
||||
@@ -711,7 +711,7 @@ describe('Debug', () => {
|
||||
it('should render prompt value panel and no-result placeholder', () => {
|
||||
renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -722,7 +722,7 @@ describe('Debug', () => {
|
||||
it('should notify when required input is missing', () => {
|
||||
const { notify } = renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
inputs: {},
|
||||
modelConfig: {
|
||||
...createContextValue().modelConfig,
|
||||
@@ -752,7 +752,7 @@ describe('Debug', () => {
|
||||
it('should notify when local file upload is still pending', () => {
|
||||
const { notify } = renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
modelConfig: {
|
||||
...createContextValue().modelConfig,
|
||||
configs: {
|
||||
@@ -776,7 +776,7 @@ describe('Debug', () => {
|
||||
it('should show cannot-query-dataset warning when dataset context variable is missing', () => {
|
||||
renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
dataSets: [{ id: 'dataset-1' }] as DebugContextValue['dataSets'],
|
||||
hasSetContextVar: false,
|
||||
modelConfig: {
|
||||
@@ -800,7 +800,7 @@ describe('Debug', () => {
|
||||
const user = userEvent.setup()
|
||||
const { notify } = renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
isAdvancedMode: true,
|
||||
chatPromptConfig: { prompt: [{ text: 'Missing role' }] },
|
||||
},
|
||||
@@ -817,7 +817,7 @@ describe('Debug', () => {
|
||||
const user = userEvent.setup()
|
||||
const file = { ...mockState.mockFeaturesState.file, enabled: true, number_limits: 1.5 }
|
||||
mockState.mockFeaturesState.file = file
|
||||
const { notify } = renderDebug({ contextValue: { mode: AppModeEnum.COMPLETION } })
|
||||
const { notify } = renderDebug({ contextValue: { mode: 'completion' as const } })
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Send' }))
|
||||
|
||||
@@ -870,7 +870,7 @@ describe('Debug', () => {
|
||||
|
||||
renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
promptMode: 'simple' as DebugContextValue['promptMode'],
|
||||
textToSpeechConfig: { enabled: true, voice: 'alloy', language: 'en' },
|
||||
modelConfig: {
|
||||
@@ -924,7 +924,7 @@ describe('Debug', () => {
|
||||
mockState.mockSendCompletionMessage.mockImplementation(() => undefined)
|
||||
const { notify } = renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
modelConfig: {
|
||||
...createContextValue().modelConfig,
|
||||
configs: {
|
||||
@@ -975,7 +975,7 @@ describe('Debug', () => {
|
||||
|
||||
renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
modelConfig: {
|
||||
...createContextValue().modelConfig,
|
||||
configs: {
|
||||
@@ -999,7 +999,7 @@ describe('Debug', () => {
|
||||
handlers.onData('Completion answer', true, { messageId: 'completion-1' })
|
||||
handlers.onCompleted()
|
||||
})
|
||||
renderDebug({ contextValue: { mode: AppModeEnum.COMPLETION } })
|
||||
renderDebug({ contextValue: { mode: 'completion' as const } })
|
||||
await user.click(screen.getByRole('button', { name: 'Send' }))
|
||||
await user.click(
|
||||
within(screen.getByRole('region', { name: 'First answer' })).getByRole('button', {
|
||||
@@ -1080,7 +1080,7 @@ describe('Debug', () => {
|
||||
it('should emit completion event in multiple-model completion mode', () => {
|
||||
renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
modelConfig: {
|
||||
...createContextValue().modelConfig,
|
||||
configs: {
|
||||
@@ -1166,7 +1166,7 @@ describe('Debug', () => {
|
||||
it('should update feature store according to multiple-model vision support', () => {
|
||||
renderDebug({
|
||||
contextValue: {
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
},
|
||||
props: {
|
||||
debugWithMultipleModel: true,
|
||||
|
||||
+9
-10
@@ -3,7 +3,6 @@ import type { ModelAndParameter } from '../../types'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { ModelStatusEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import DebugItem from '../debug-item'
|
||||
|
||||
const mockUseDebugConfigurationContext = vi.fn()
|
||||
@@ -98,7 +97,7 @@ describe('DebugItem', () => {
|
||||
capturedModelParameterTriggerProps = null
|
||||
|
||||
mockUseDebugConfigurationContext.mockReturnValue({
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
})
|
||||
|
||||
mockUseDebugWithMultipleModelContext.mockReturnValue({
|
||||
@@ -156,7 +155,7 @@ describe('DebugItem', () => {
|
||||
|
||||
describe('ChatItem rendering', () => {
|
||||
it('should render ChatItem in CHAT mode with active model', () => {
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: AppModeEnum.CHAT })
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: 'chat' as const })
|
||||
mockModelListQuery.mockReturnValue({
|
||||
data: createTextGenerationModelList([
|
||||
{ provider: 'openai', model: 'gpt-3.5-turbo', status: ModelStatusEnum.active },
|
||||
@@ -170,7 +169,7 @@ describe('DebugItem', () => {
|
||||
})
|
||||
|
||||
it('should render ChatItem in AGENT_CHAT mode with active model', () => {
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: AppModeEnum.AGENT_CHAT })
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: 'agent-chat' as const })
|
||||
mockModelListQuery.mockReturnValue({
|
||||
data: createTextGenerationModelList([
|
||||
{ provider: 'openai', model: 'gpt-3.5-turbo', status: ModelStatusEnum.active },
|
||||
@@ -183,7 +182,7 @@ describe('DebugItem', () => {
|
||||
})
|
||||
|
||||
it('should not render ChatItem when model is not active', () => {
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: AppModeEnum.CHAT })
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: 'chat' as const })
|
||||
mockModelListQuery.mockReturnValue({
|
||||
data: createTextGenerationModelList([
|
||||
{ provider: 'openai', model: 'gpt-3.5-turbo', status: ModelStatusEnum.disabled },
|
||||
@@ -196,7 +195,7 @@ describe('DebugItem', () => {
|
||||
})
|
||||
|
||||
it('should not render ChatItem when provider not found', () => {
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: AppModeEnum.CHAT })
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: 'chat' as const })
|
||||
mockModelListQuery.mockReturnValue({
|
||||
data: createTextGenerationModelList([
|
||||
{ provider: 'anthropic', model: 'claude-3', status: ModelStatusEnum.active },
|
||||
@@ -209,7 +208,7 @@ describe('DebugItem', () => {
|
||||
})
|
||||
|
||||
it('should not render ChatItem when model not found', () => {
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: AppModeEnum.CHAT })
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: 'chat' as const })
|
||||
mockModelListQuery.mockReturnValue({
|
||||
data: createTextGenerationModelList([
|
||||
{ provider: 'openai', model: 'gpt-4', status: ModelStatusEnum.active },
|
||||
@@ -224,7 +223,7 @@ describe('DebugItem', () => {
|
||||
|
||||
describe('TextGenerationItem rendering', () => {
|
||||
it('should render TextGenerationItem in COMPLETION mode with active model', () => {
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: AppModeEnum.COMPLETION })
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: 'completion' as const })
|
||||
mockModelListQuery.mockReturnValue({
|
||||
data: createTextGenerationModelList([
|
||||
{ provider: 'openai', model: 'gpt-3.5-turbo', status: ModelStatusEnum.active },
|
||||
@@ -238,7 +237,7 @@ describe('DebugItem', () => {
|
||||
})
|
||||
|
||||
it('should not render TextGenerationItem when provider is not found', () => {
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: AppModeEnum.COMPLETION })
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: 'completion' as const })
|
||||
mockModelListQuery.mockReturnValue({
|
||||
data: createTextGenerationModelList([
|
||||
{ provider: 'anthropic', model: 'claude-3', status: ModelStatusEnum.active },
|
||||
@@ -503,7 +502,7 @@ describe('DebugItem', () => {
|
||||
})
|
||||
|
||||
it('should handle model with quotaExceeded status', () => {
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: AppModeEnum.CHAT })
|
||||
mockUseDebugConfigurationContext.mockReturnValue({ mode: 'chat' as const })
|
||||
mockModelListQuery.mockReturnValue({
|
||||
data: createTextGenerationModelList([
|
||||
{ provider: 'anthropic', model: 'not-matching', status: ModelStatusEnum.quotaExceeded },
|
||||
|
||||
+7
-6
@@ -1,3 +1,4 @@
|
||||
import type { AppMode } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { CSSProperties } from 'react'
|
||||
import type { ModelAndParameter } from '../../types'
|
||||
import type { DebugWithMultipleModelContextType } from '../context'
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
DEFAULT_CHAT_PROMPT_CONFIG,
|
||||
DEFAULT_COMPLETION_PROMPT_CONFIG,
|
||||
} from '@/config'
|
||||
import { AppModeEnum, ModelModeType, Resolution, TransferMethod } from '@/types/app'
|
||||
import { ModelModeType, Resolution, TransferMethod } from '@/types/app'
|
||||
import { APP_CHAT_WITH_MULTIPLE_MODEL } from '../../types'
|
||||
import DebugWithMultipleModel from '../index'
|
||||
|
||||
@@ -161,7 +162,7 @@ const createModelConfig = (promptVariables: PromptVariableWithMeta[] = []): Mode
|
||||
|
||||
type DebugConfiguration = {
|
||||
onOpenFeatures: () => void
|
||||
mode: AppModeEnum
|
||||
mode: AppMode
|
||||
inputs: Inputs
|
||||
modelConfig: ModelConfig
|
||||
readonly: boolean
|
||||
@@ -172,7 +173,7 @@ const createDebugConfiguration = (
|
||||
overrides: Partial<DebugConfiguration> = {},
|
||||
): DebugConfiguration => ({
|
||||
onOpenFeatures: vi.fn(),
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
inputs: {},
|
||||
modelConfig: createModelConfig(),
|
||||
readonly: false,
|
||||
@@ -536,7 +537,7 @@ describe('DebugWithMultipleModel', () => {
|
||||
// Arrange
|
||||
mockUseDebugConfigurationContext.mockReturnValue(
|
||||
createDebugConfiguration({
|
||||
mode: AppModeEnum.AGENT_CHAT,
|
||||
mode: 'agent-chat' as const,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -552,7 +553,7 @@ describe('DebugWithMultipleModel', () => {
|
||||
// Arrange
|
||||
mockUseDebugConfigurationContext.mockReturnValue(
|
||||
createDebugConfiguration({
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
}),
|
||||
)
|
||||
const multipleModelConfigs = [createModelAndParameter()]
|
||||
@@ -842,7 +843,7 @@ describe('DebugWithMultipleModel', () => {
|
||||
it('should set full height when chat input is hidden', () => {
|
||||
mockUseDebugConfigurationContext.mockReturnValue(
|
||||
createDebugConfiguration({
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useDebugConfigurationContext } from '@/context/debug-configuration'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import ChatItem from './chat-item'
|
||||
import { useDebugWithMultipleModelContext } from './context'
|
||||
import ModelParameterTrigger from './model-parameter-trigger'
|
||||
@@ -124,13 +123,13 @@ const DebugItem: FC<DebugItemProps> = ({ modelAndParameter, className, style })
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div style={{ height: 'calc(100% - 40px)' }}>
|
||||
{(mode === AppModeEnum.CHAT || mode === AppModeEnum.AGENT_CHAT) &&
|
||||
{(mode === 'chat' || mode === 'agent-chat') &&
|
||||
currentProvider &&
|
||||
currentModel &&
|
||||
currentModel.status === ModelStatusEnum.active && (
|
||||
<ChatItem modelAndParameter={modelAndParameter} />
|
||||
)}
|
||||
{mode === AppModeEnum.COMPLETION &&
|
||||
{mode === 'completion' &&
|
||||
currentProvider &&
|
||||
currentModel &&
|
||||
currentModel.status === ModelStatusEnum.active && (
|
||||
|
||||
@@ -7,7 +7,6 @@ import ChatInputArea from '@/app/components/base/chat/chat/chat-input-area'
|
||||
import { useFeatures } from '@/app/components/base/features/hooks'
|
||||
import { useDebugConfigurationContext } from '@/context/debug-configuration'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { APP_CHAT_WITH_MULTIPLE_MODEL } from '../types'
|
||||
import { useDebugWithMultipleModelContext } from './context'
|
||||
import { DebugWithMultipleModelContextProvider } from './context-provider'
|
||||
@@ -28,7 +27,7 @@ const DebugWithMultipleModel = () => {
|
||||
const { multipleModelConfigs, checkCanSend } = useDebugWithMultipleModelContext()
|
||||
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const isChatMode = mode === AppModeEnum.CHAT || mode === AppModeEnum.AGENT_CHAT
|
||||
const isChatMode = mode === 'chat' || mode === 'agent-chat'
|
||||
|
||||
const handleSend = useCallback(
|
||||
(message: string, files?: FileEntity[]) => {
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import { CollectionType } from '@/app/components/tools/types'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { PromptMode } from '@/models/debug'
|
||||
import { renderWithAccountProfile as render } from '@/test/console/account-profile'
|
||||
import { AgentStrategy, AppModeEnum, ModelModeType, Resolution, TransferMethod } from '@/types/app'
|
||||
import { AgentStrategy, ModelModeType, Resolution, TransferMethod } from '@/types/app'
|
||||
import DebugWithSingleModel from '../index'
|
||||
|
||||
// ============================================================================
|
||||
@@ -135,7 +135,7 @@ const mockDebugConfigContext = {
|
||||
appId: 'test-app-id',
|
||||
onOpenFeatures: vi.fn(),
|
||||
isTrailFinished: false,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
modelModeType: ModelModeType.chat,
|
||||
promptMode: PromptMode.simple,
|
||||
setPromptMode: vi.fn(),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { AppModelConfigPayload } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { FC } from 'react'
|
||||
import type { DebugWithSingleModelRefType } from './debug-with-single-model'
|
||||
@@ -44,7 +45,7 @@ import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { sendCompletionMessage } from '@/service/debug'
|
||||
import { AppSourceType } from '@/service/share'
|
||||
import { AppModeEnum, ModelModeType, TransferMethod } from '@/types/app'
|
||||
import { ModelModeType, TransferMethod } from '@/types/app'
|
||||
import { formatBooleanInputs, promptVariablesToUserInputsForm } from '@/utils/model-config'
|
||||
import GroupName from '../base/group-name'
|
||||
import CannotQueryDataset from '../base/warning-mask/cannot-query-dataset'
|
||||
@@ -147,7 +148,7 @@ const Debug: FC<IDebug> = ({
|
||||
const [completionFiles, setCompletionFiles] = useState<VisionFile[]>([])
|
||||
|
||||
const checkCanSend = useCallback(() => {
|
||||
if (isAdvancedMode && mode !== AppModeEnum.COMPLETION) {
|
||||
if (isAdvancedMode && mode !== 'completion') {
|
||||
if (modelModeType === ModelModeType.completion) {
|
||||
if (!hasSetBlockStatus.history) {
|
||||
toast.error(t(($) => $['otherError.historyNoBeEmpty'], { ns: 'appDebug' }))
|
||||
@@ -413,9 +414,7 @@ const Debug: FC<IDebug> = ({
|
||||
|
||||
const handleOpenLog = (item: IChatItem) => {
|
||||
const container =
|
||||
debugWithMultipleModel || mode === AppModeEnum.COMPLETION
|
||||
? ref.current
|
||||
: singleChatContainerRef.current
|
||||
debugWithMultipleModel || mode === 'completion' ? ref.current : singleChatContainerRef.current
|
||||
if (container) setLogModalWidth(document.body.clientWidth - (container.clientWidth + 16) - 8)
|
||||
setSelectedLogItem(item)
|
||||
}
|
||||
@@ -423,9 +422,7 @@ const Debug: FC<IDebug> = ({
|
||||
useEffect(() => {
|
||||
if (!selectedLogItem) return
|
||||
const container =
|
||||
debugWithMultipleModel || mode === AppModeEnum.COMPLETION
|
||||
? ref.current
|
||||
: singleChatContainerRef.current
|
||||
debugWithMultipleModel || mode === 'completion' ? ref.current : singleChatContainerRef.current
|
||||
if (!container) return
|
||||
|
||||
const adjustModalWidth = () => {
|
||||
@@ -471,7 +468,7 @@ const Debug: FC<IDebug> = ({
|
||||
<div className="mx-2 h-3.5 w-px bg-divider-regular" />
|
||||
</>
|
||||
) : null}
|
||||
{mode !== AppModeEnum.COMPLETION && (
|
||||
{mode !== 'completion' && (
|
||||
<>
|
||||
{canTestAndRun && (
|
||||
<Tooltip>
|
||||
@@ -525,14 +522,14 @@ const Debug: FC<IDebug> = ({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{mode !== AppModeEnum.COMPLETION && (
|
||||
{mode !== 'completion' && (
|
||||
<CollapsiblePanel render={<div className="mx-3" />}>
|
||||
<ChatUserInput inputs={inputs} />
|
||||
</CollapsiblePanel>
|
||||
)}
|
||||
{mode === AppModeEnum.COMPLETION && (
|
||||
{mode === 'completion' && (
|
||||
<PromptValuePanel
|
||||
appType={mode as AppModeEnum}
|
||||
appType={mode}
|
||||
onSend={handleSendTextCompletion}
|
||||
inputs={inputs}
|
||||
visionConfig={{
|
||||
@@ -582,7 +579,7 @@ const Debug: FC<IDebug> = ({
|
||||
</div>
|
||||
)}
|
||||
{/* Chat */}
|
||||
{mode !== AppModeEnum.COMPLETION && (
|
||||
{mode !== 'completion' && (
|
||||
<div className="h-0 grow overflow-hidden">
|
||||
<DebugWithSingleModel
|
||||
ref={debugWithSingleModelRef}
|
||||
@@ -593,7 +590,7 @@ const Debug: FC<IDebug> = ({
|
||||
</div>
|
||||
)}
|
||||
{/* Text Generation */}
|
||||
{mode === AppModeEnum.COMPLETION && (
|
||||
{mode === 'completion' && (
|
||||
<>
|
||||
{(completionRes || isResponding) && (
|
||||
<>
|
||||
|
||||
+9
-9
@@ -7,7 +7,7 @@ import {
|
||||
} from '@/app/components/base/prompt-editor/constants'
|
||||
import { PromptMode, PromptRole } from '@/models/debug'
|
||||
import { fetchPromptTemplate } from '@/service/debug'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import useAdvancedPromptConfig from '../use-advanced-prompt-config'
|
||||
|
||||
vi.mock('@/service/debug', () => ({
|
||||
@@ -25,7 +25,7 @@ describe('useAdvancedPromptConfig', () => {
|
||||
const handleUserChangedPrompt = vi.fn()
|
||||
const { result } = renderHook(() =>
|
||||
useAdvancedPromptConfig({
|
||||
appMode: AppModeEnum.CHAT,
|
||||
appMode: 'chat' as const,
|
||||
modelModeType: ModelModeType.chat,
|
||||
modelName: 'gpt-4o',
|
||||
promptMode: PromptMode.advanced,
|
||||
@@ -55,7 +55,7 @@ describe('useAdvancedPromptConfig', () => {
|
||||
it('should derive simple prompt block status from the pre-prompt', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useAdvancedPromptConfig({
|
||||
appMode: AppModeEnum.COMPLETION,
|
||||
appMode: 'completion' as const,
|
||||
modelModeType: ModelModeType.completion,
|
||||
modelName: 'gpt-4o',
|
||||
promptMode: PromptMode.simple,
|
||||
@@ -79,7 +79,7 @@ describe('useAdvancedPromptConfig', () => {
|
||||
const handleUserChangedPrompt = vi.fn()
|
||||
const { result } = renderHook(() =>
|
||||
useAdvancedPromptConfig({
|
||||
appMode: AppModeEnum.CHAT,
|
||||
appMode: 'chat' as const,
|
||||
modelModeType: ModelModeType.chat,
|
||||
modelName: 'gpt-4o',
|
||||
promptMode: PromptMode.simple,
|
||||
@@ -118,7 +118,7 @@ describe('useAdvancedPromptConfig', () => {
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useAdvancedPromptConfig({
|
||||
appMode: AppModeEnum.COMPLETION,
|
||||
appMode: 'completion' as const,
|
||||
modelModeType: ModelModeType.completion,
|
||||
modelName: 'gpt-4o',
|
||||
promptMode: PromptMode.simple,
|
||||
@@ -137,7 +137,7 @@ describe('useAdvancedPromptConfig', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchPromptTemplate).toHaveBeenCalledWith({
|
||||
appMode: AppModeEnum.COMPLETION,
|
||||
appMode: 'completion' as const,
|
||||
mode: ModelModeType.completion,
|
||||
modelName: 'gpt-4o',
|
||||
hasSetDataSet: true,
|
||||
@@ -168,7 +168,7 @@ describe('useAdvancedPromptConfig', () => {
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useAdvancedPromptConfig({
|
||||
appMode: AppModeEnum.CHAT,
|
||||
appMode: 'chat' as const,
|
||||
modelModeType: ModelModeType.chat,
|
||||
modelName: 'gpt-4o',
|
||||
promptMode: PromptMode.simple,
|
||||
@@ -211,7 +211,7 @@ describe('useAdvancedPromptConfig', () => {
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useAdvancedPromptConfig({
|
||||
appMode: AppModeEnum.CHAT,
|
||||
appMode: 'chat' as const,
|
||||
modelModeType: ModelModeType.completion,
|
||||
modelName: 'gpt-4o',
|
||||
promptMode: PromptMode.advanced,
|
||||
@@ -268,7 +268,7 @@ describe('useAdvancedPromptConfig', () => {
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useAdvancedPromptConfig({
|
||||
appMode: AppModeEnum.COMPLETION,
|
||||
appMode: 'completion' as const,
|
||||
modelModeType: ModelModeType.completion,
|
||||
modelName: 'gpt-4o',
|
||||
promptMode: PromptMode.advanced,
|
||||
|
||||
+13
-14
@@ -6,7 +6,6 @@ import { createAppDetailFixture, createAppModelConfigFixture } from '@/test/fixt
|
||||
import { withSelectorKey } from '@/test/i18n-mock'
|
||||
import {
|
||||
AgentStrategy,
|
||||
AppModeEnum,
|
||||
ModelModeType,
|
||||
Resolution,
|
||||
RETRIEVE_TYPE,
|
||||
@@ -133,7 +132,7 @@ describe('useConfiguration utils', () => {
|
||||
mockFetchCollectionList.mockResolvedValue([])
|
||||
mockFetchAppDetailDirect.mockResolvedValue(
|
||||
createAppDetailFixture({
|
||||
mode: AppModeEnum.AGENT_CHAT,
|
||||
mode: 'agent-chat' as const,
|
||||
model_config: createAppModelConfigFixture({
|
||||
model: {
|
||||
provider: 'langgenius/openai/openai',
|
||||
@@ -277,7 +276,7 @@ describe('useConfiguration utils', () => {
|
||||
top_k: 4,
|
||||
} as any,
|
||||
deletedTools: [{ type: 'tool', provider_id: 'tool-1', tool_name: 'search' }],
|
||||
mode: AppModeEnum.AGENT_CHAT,
|
||||
mode: 'agent-chat' as const,
|
||||
nextDataSets: [{ id: 'dataset-1' }] as any,
|
||||
})
|
||||
|
||||
@@ -336,7 +335,7 @@ describe('useConfiguration utils', () => {
|
||||
datasets: { datasets: [] },
|
||||
retrieval_model: RETRIEVE_TYPE.multiWay,
|
||||
} as any,
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
nextDataSets: [],
|
||||
})
|
||||
|
||||
@@ -369,7 +368,7 @@ describe('useConfiguration utils', () => {
|
||||
datasets: { datasets: [] },
|
||||
retrieval_model: RETRIEVE_TYPE.multiWay,
|
||||
} as any,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
nextDataSets: [],
|
||||
})
|
||||
|
||||
@@ -496,7 +495,7 @@ describe('useConfiguration utils', () => {
|
||||
])
|
||||
mockFetchAppDetailDirect.mockResolvedValue({
|
||||
deleted_tools: [],
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
model_config: createAppModelConfigFixture({
|
||||
prompt_type: 'advanced',
|
||||
chat_prompt_config: {
|
||||
@@ -581,7 +580,7 @@ describe('useConfiguration utils', () => {
|
||||
})
|
||||
mockFetchAppDetailDirect.mockResolvedValue({
|
||||
deleted_tools: [],
|
||||
mode: AppModeEnum.AGENT_CHAT,
|
||||
mode: 'agent-chat' as const,
|
||||
model_config: createAppModelConfigFixture({
|
||||
prompt_type: 'simple',
|
||||
chat_prompt_config: { prompt: [] },
|
||||
@@ -642,7 +641,7 @@ describe('useConfiguration utils', () => {
|
||||
mockFetchCollectionList.mockResolvedValue([])
|
||||
mockFetchAppDetailDirect.mockResolvedValue({
|
||||
deleted_tools: [],
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
model_config: createAppModelConfigFixture({
|
||||
prompt_type: 'simple',
|
||||
chat_prompt_config: { prompt: [] },
|
||||
@@ -839,7 +838,7 @@ describe('useConfiguration utils', () => {
|
||||
backendModelConfig,
|
||||
collectionList: [],
|
||||
datasetConfigs: buildConfigurationDatasetConfigs({ backendModelConfig, nextDataSets: [] }),
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
nextDataSets: [],
|
||||
})
|
||||
const setPublishedConfig = vi.fn()
|
||||
@@ -862,7 +861,7 @@ describe('useConfiguration utils', () => {
|
||||
hasSetBlockStatus: { history: true, query: true },
|
||||
isAdvancedMode: true,
|
||||
isFunctionCall: false,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
modelConfig: config.modelConfig,
|
||||
promptEmpty: false,
|
||||
promptMode: 'advanced',
|
||||
@@ -911,7 +910,7 @@ describe('useConfiguration utils', () => {
|
||||
},
|
||||
isAdvancedMode: true,
|
||||
isFunctionCall: true,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
modelConfig: {
|
||||
agentConfig: {
|
||||
enabled: true,
|
||||
@@ -1019,7 +1018,7 @@ describe('useConfiguration utils', () => {
|
||||
},
|
||||
isAdvancedMode: true,
|
||||
isFunctionCall: false,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
modelConfig: {
|
||||
configs: {
|
||||
prompt_template: 'hello',
|
||||
@@ -1080,7 +1079,7 @@ describe('useConfiguration utils', () => {
|
||||
handleSetVisionConfig,
|
||||
isAdvancedMode: true,
|
||||
migrateToDefaultPrompt,
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat',
|
||||
modelConfig: {
|
||||
model_id: 'gpt-4o-mini',
|
||||
provider: 'langgenius/openai/openai',
|
||||
@@ -1137,7 +1136,7 @@ describe('useConfiguration utils', () => {
|
||||
handleSetVisionConfig,
|
||||
isAdvancedMode: true,
|
||||
migrateToDefaultPrompt,
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
modelConfig: {
|
||||
model_id: 'gpt-4o-mini',
|
||||
provider: 'langgenius/openai/openai',
|
||||
|
||||
@@ -12,7 +12,7 @@ import { seedAppDetail } from '@/test/console/query-data'
|
||||
import { renderHook as renderHookWithConsoleState } from '@/test/console/render'
|
||||
import { createAppDetailFixture, createAppModelConfigFixture } from '@/test/fixtures/app'
|
||||
import { createTestQueryClient } from '@/test/query-client'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import { AppACLPermission } from '@/utils/permission'
|
||||
import { useConfiguration } from '../use-configuration'
|
||||
|
||||
@@ -24,7 +24,7 @@ const createRouteAppDetail = (overrides: Partial<AppDetailWithSite> = {}) =>
|
||||
model_config: createAppModelConfigFixture({
|
||||
updated_at: 1710000000,
|
||||
}),
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
permission_keys: mockAppPermissionKeys,
|
||||
...overrides,
|
||||
})
|
||||
@@ -252,7 +252,7 @@ describe('useConfiguration', () => {
|
||||
vi.mocked(consoleClient.apps.byAppId.modelConfig.post).mockResolvedValue(undefined as never)
|
||||
mockFetchAppDetailDirect.mockResolvedValue({
|
||||
deleted_tools: [],
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
model_config: createAppModelConfigFixture({
|
||||
prompt_type: 'advanced',
|
||||
chat_prompt_config: {
|
||||
@@ -675,7 +675,7 @@ describe('useConfiguration', () => {
|
||||
mockCurrentModelFeatures = ['vision', 'document', 'audio', 'video']
|
||||
mockFetchAppDetailDirect.mockResolvedValueOnce({
|
||||
deleted_tools: [],
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
model_config: createAppModelConfigFixture({
|
||||
prompt_type: 'simple',
|
||||
chat_prompt_config: { prompt: [] },
|
||||
@@ -788,7 +788,7 @@ describe('useConfiguration', () => {
|
||||
})
|
||||
mockFetchAppDetailDirect.mockResolvedValueOnce({
|
||||
deleted_tools: [],
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
model_config: createAppModelConfigFixture({
|
||||
prompt_type: 'simple',
|
||||
chat_prompt_config: { prompt: [] },
|
||||
|
||||
@@ -4,7 +4,6 @@ import { PromptMode } from '@/models/debug'
|
||||
import { consoleClient } from '@/service/console'
|
||||
import { fetchDatasets } from '@/service/datasets'
|
||||
import { fetchCollectionList } from '@/service/tools'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { withCollectionIconBasePath } from '../../utils'
|
||||
import { buildConfigurationDatasetConfigs } from './dataset'
|
||||
import { normalizeChatPromptConfig, normalizeCompletionPromptConfig } from './prompt-config'
|
||||
@@ -24,11 +23,7 @@ export async function loadConfigurationState({
|
||||
const collectionList = withCollectionIconBasePath(await fetchCollectionList(), basePath)
|
||||
const response = await consoleClient.apps.byAppId.get({ params: { app_id: appId } })
|
||||
const mode = response.mode
|
||||
if (
|
||||
mode !== AppModeEnum.CHAT &&
|
||||
mode !== AppModeEnum.AGENT_CHAT &&
|
||||
mode !== AppModeEnum.COMPLETION
|
||||
)
|
||||
if (mode !== 'chat' && mode !== 'agent-chat' && mode !== 'completion')
|
||||
throw new Error(`App mode ${mode} does not use model configuration`)
|
||||
const backendModelConfig = response.model_config
|
||||
if (!backendModelConfig) throw new Error(`App ${appId} has no model configuration`)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { TFunction } from 'i18next'
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { ModelConfig } from '@/models/debug'
|
||||
import type { VisionSettings } from '@/types/app'
|
||||
import { produce } from 'immer'
|
||||
import { useMemo } from 'react'
|
||||
import { toast } from '@/app/components/app/configuration/toast'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params'
|
||||
|
||||
type ModelChangeHandlerOptions = {
|
||||
@@ -23,7 +24,7 @@ type ModelChangeHandlerOptions = {
|
||||
handleSetVisionConfig: (config: VisionSettings, notNoticeFormattingChanged?: boolean) => void
|
||||
isAdvancedMode: boolean
|
||||
migrateToDefaultPrompt: (force?: boolean, modelModeType?: ModelModeType) => Promise<void>
|
||||
mode: AppModeEnum
|
||||
mode: ConfigurationAppMode
|
||||
modelConfig: ModelConfig
|
||||
resolvedModelModeType: ModelModeType
|
||||
setCompletionParams: (value: FormValue) => void
|
||||
@@ -61,7 +62,7 @@ export const createModelChangeHandler =
|
||||
}) => {
|
||||
if (isAdvancedMode) {
|
||||
if (nextModelMode === ModelModeType.completion) {
|
||||
if (mode !== AppModeEnum.COMPLETION) {
|
||||
if (mode !== 'completion') {
|
||||
if (
|
||||
!completionPromptConfig.prompt?.text ||
|
||||
!completionPromptConfig.conversation_histories_role.assistant_prefix ||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { AppModelConfigPayload } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { TFunction } from 'i18next'
|
||||
import type { ConfigurationPublishConfig } from './types'
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { Features as FeaturesData } from '@/app/components/base/features/types'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
@@ -21,7 +22,7 @@ import { produce } from 'immer'
|
||||
import { toast } from '@/app/components/app/configuration/toast'
|
||||
import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
|
||||
import { PromptMode } from '@/models/debug'
|
||||
import { AgentStrategy, AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { AgentStrategy, ModelModeType } from '@/types/app'
|
||||
import { promptVariablesToUserInputsForm } from '@/utils/model-config'
|
||||
import { normalizeChatPromptConfig, normalizeCompletionPromptConfig } from './prompt-config'
|
||||
|
||||
@@ -152,7 +153,7 @@ export const createPublishHandler =
|
||||
hasSetBlockStatus: { history: boolean; query: boolean }
|
||||
isAdvancedMode: boolean
|
||||
isFunctionCall: boolean
|
||||
mode: AppModeEnum
|
||||
mode: ConfigurationAppMode
|
||||
modelConfig: ModelConfig
|
||||
promptEmpty: boolean
|
||||
promptMode: PromptMode
|
||||
@@ -178,7 +179,7 @@ export const createPublishHandler =
|
||||
}
|
||||
if (
|
||||
isAdvancedMode &&
|
||||
mode !== AppModeEnum.COMPLETION &&
|
||||
mode !== 'completion' &&
|
||||
resolvedModelModeType === ModelModeType.completion
|
||||
) {
|
||||
if (!hasSetBlockStatus.history) {
|
||||
|
||||
+5
-4
@@ -3,6 +3,7 @@ import type {
|
||||
DeletedTool,
|
||||
} from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { ConfigurationPublishConfig } from './types'
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { Collection } from '@/app/components/tools/types'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import type { AnnotationReplyConfig, DatasetConfigs, ModelConfig } from '@/models/debug'
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
} from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import { ANNOTATION_DEFAULT, DEFAULT_AGENT_SETTING } from '@/config'
|
||||
import { PromptMode } from '@/models/debug'
|
||||
import { AgentStrategy, AppModeEnum } from '@/types/app'
|
||||
import { AgentStrategy } from '@/types/app'
|
||||
import { correctModelProvider, correctToolProvider } from '@/utils'
|
||||
import { userInputsFormToPromptVariables } from '@/utils/model-config'
|
||||
import { matchesProviderReference } from '@/utils/provider-reference'
|
||||
@@ -50,7 +51,7 @@ function buildPublishedModelConfig({
|
||||
backendModelConfig: AppModelConfigResponse
|
||||
collectionList: Collection[]
|
||||
deletedTools?: DeletedTool[]
|
||||
mode: AppModeEnum
|
||||
mode: ConfigurationAppMode
|
||||
nextDataSets: DataSet[]
|
||||
}): ModelConfig {
|
||||
const model = backendModelConfig.model
|
||||
@@ -107,7 +108,7 @@ function buildPublishedModelConfig({
|
||||
external_data_tools: externalDataTools,
|
||||
dataSets: nextDataSets,
|
||||
agentConfig:
|
||||
mode === AppModeEnum.AGENT_CHAT
|
||||
mode === 'agent-chat'
|
||||
? {
|
||||
...backendModelConfig.agent_mode,
|
||||
enabled: true,
|
||||
@@ -170,7 +171,7 @@ export function buildPublishedConfig({
|
||||
collectionList: Collection[]
|
||||
datasetConfigs: DatasetConfigs
|
||||
deletedTools?: DeletedTool[]
|
||||
mode: AppModeEnum
|
||||
mode: ConfigurationAppMode
|
||||
nextDataSets: DataSet[]
|
||||
}): ConfigurationPublishConfig {
|
||||
const modelConfig = buildPublishedModelConfig({
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import type { AppType } from '@/app/components/apps/app-type'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { DatasetConfigs, ModelConfig, PromptMode } from '@/models/debug'
|
||||
|
||||
export type ConfigurationAppMode = Extract<AppType, 'chat' | 'agent-chat' | 'completion'>
|
||||
|
||||
export type ConfigurationPublishConfig = {
|
||||
modelConfig: ModelConfig
|
||||
completionParams: FormValue
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import type { Dispatch, SetStateAction } from 'react'
|
||||
import type { ConfigurationPublishConfig } from './types'
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { Collection } from '@/app/components/tools/types'
|
||||
import type { AnnotationReplyConfig } from '@/models/debug'
|
||||
import type { AppModeEnum } from '@/types/app'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { loadConfigurationState } from './load'
|
||||
@@ -13,7 +13,7 @@ type ConfigurationLoaderOperations = {
|
||||
setAnnotationConfig: (config: AnnotationReplyConfig, notSetFormatChanged?: boolean) => void
|
||||
setCollectionList: Dispatch<SetStateAction<Collection[]>>
|
||||
setHasFetchedDetail: Dispatch<SetStateAction<boolean>>
|
||||
setMode: Dispatch<SetStateAction<AppModeEnum>>
|
||||
setMode: Dispatch<SetStateAction<ConfigurationAppMode>>
|
||||
setPublishedConfig: Dispatch<SetStateAction<ConfigurationPublishConfig | null>>
|
||||
syncToPublishedConfig: (config: ConfigurationPublishConfig) => void
|
||||
}
|
||||
|
||||
+3
-2
@@ -2,12 +2,13 @@ import type { TFunction } from 'i18next'
|
||||
import type { Dispatch, SetStateAction } from 'react'
|
||||
import type { ConfigurationPublishConfig } from './types'
|
||||
import type { AppPublisherPublishParams } from '@/app/components/app/app-publisher/types'
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { Features as FeaturesData } from '@/app/components/base/features/types'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import type { DatasetConfigs, ModelConfig, PromptMode } from '@/models/debug'
|
||||
import type { ConsoleClient } from '@/service/console'
|
||||
import type { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import type { ModelModeType } from '@/types/app'
|
||||
import { useCallback } from 'react'
|
||||
import { createPublishHandler } from './publish'
|
||||
|
||||
@@ -25,7 +26,7 @@ type UseConfigurationPublishParams = {
|
||||
hasSetBlockStatus: { history: boolean; query: boolean }
|
||||
isAdvancedMode: boolean
|
||||
isFunctionCall: boolean
|
||||
mode: AppModeEnum
|
||||
mode: ConfigurationAppMode
|
||||
modelConfig: ModelConfig
|
||||
promptEmpty: boolean
|
||||
promptMode: PromptMode
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type {
|
||||
ChatPromptConfig,
|
||||
@@ -17,10 +18,10 @@ import {
|
||||
import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
|
||||
import { PromptMode } from '@/models/debug'
|
||||
import { fetchPromptTemplate } from '@/service/debug'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
|
||||
type Param = {
|
||||
appMode?: AppModeEnum
|
||||
appMode?: ConfigurationAppMode
|
||||
modelModeType: ModelModeType
|
||||
modelName: string
|
||||
promptMode: PromptMode
|
||||
@@ -175,9 +176,7 @@ const useAdvancedPromptConfig = ({
|
||||
)
|
||||
|
||||
if (
|
||||
[AppModeEnum.ADVANCED_CHAT, AppModeEnum.AGENT_CHAT, AppModeEnum.CHAT].includes(
|
||||
appMode,
|
||||
) &&
|
||||
(appMode === 'agent-chat' || appMode === 'chat') &&
|
||||
completionPromptConfig.conversation_histories_role.assistant_prefix &&
|
||||
completionPromptConfig.conversation_histories_role.user_prefix
|
||||
)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import type { ConfigurationPublishConfig } from './configuration-lifecycle/types'
|
||||
import type { ConfigurationViewModel } from './configuration-view-model'
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
||||
import type { Collection } from '@/app/components/tools/types'
|
||||
import type { Inputs, ModelConfig, PromptConfig, PromptVariable } from '@/models/debug'
|
||||
@@ -25,7 +27,7 @@ import {
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { PromptMode } from '@/models/debug'
|
||||
import { useFileUploadConfig } from '@/service/use-common'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import { supportFunctionCall } from '@/utils/tool-call'
|
||||
import { buildConfigurationFeaturesData, getConfigurationPublishingState } from '../utils'
|
||||
import { buildConfigurationContextValue } from './build-configuration-context'
|
||||
@@ -61,7 +63,7 @@ export const useConfiguration = (): ConfigurationViewModel => {
|
||||
const [hasFetchedDetail, setHasFetchedDetail] = useState(false)
|
||||
// oxlint-disable-next-line eslint-react/use-state -- This custom hook returns a state object.
|
||||
const featureConfiguration = useFeatureConfigurationState()
|
||||
const [mode, setMode] = useState<AppModeEnum>(AppModeEnum.CHAT)
|
||||
const [mode, setMode] = useState<ConfigurationAppMode>('chat')
|
||||
const [publishedConfig, setPublishedConfig] = useState<ConfigurationPublishConfig | null>(null)
|
||||
const [conversationId, setConversationId] = useState<string | null>('')
|
||||
|
||||
@@ -106,7 +108,7 @@ export const useConfiguration = (): ConfigurationViewModel => {
|
||||
visionConfig,
|
||||
} = modelConfiguration
|
||||
const modelModeType = modelConfig.mode
|
||||
const isAgent = mode === AppModeEnum.AGENT_CHAT
|
||||
const isAgent = mode === 'agent-chat'
|
||||
|
||||
const [collectionList, setCollectionList] = useState<Collection[]>([])
|
||||
// oxlint-disable-next-line eslint-react/use-state -- This custom hook returns a state object.
|
||||
|
||||
@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event'
|
||||
import * as React from 'react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vite-plus/test'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { AppModeEnum, ModelModeType, Resolution } from '@/types/app'
|
||||
import { ModelModeType, Resolution } from '@/types/app'
|
||||
import PromptValuePanel from '../index'
|
||||
|
||||
const mockOnOpenFeatures = vi.fn()
|
||||
@@ -78,7 +78,7 @@ const baseContextValue: any = {
|
||||
},
|
||||
setInputs: mockSetInputs,
|
||||
canTestAndRun: true,
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
isAdvancedMode: false,
|
||||
completionPromptConfig: {
|
||||
prompt: { text: 'completion' },
|
||||
@@ -88,7 +88,7 @@ const baseContextValue: any = {
|
||||
} as any
|
||||
|
||||
const defaultProps: IPromptValuePanelProps = {
|
||||
appType: AppModeEnum.COMPLETION,
|
||||
appType: 'completion',
|
||||
onSend: mockOnSend,
|
||||
inputs: { textVar: 'initial', boolVar: false },
|
||||
visionConfig: { enabled: false, number_limits: 0, detail: Resolution.low, transfer_methods: [] },
|
||||
@@ -142,10 +142,10 @@ describe('PromptValuePanel', () => {
|
||||
it('disables run when mode is not completion', () => {
|
||||
renderPanel({
|
||||
context: {
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
},
|
||||
props: {
|
||||
appType: AppModeEnum.CHAT,
|
||||
appType: 'chat',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { Inputs } from '@/models/debug'
|
||||
import type { VisionFile, VisionSettings } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
@@ -25,10 +27,10 @@ import FeatureBar from '@/app/components/base/features/new-feature-panel/feature
|
||||
import TextGenerationImageUploader from '@/app/components/base/image-uploader/text-generation-image-uploader'
|
||||
import BoolInput from '@/app/components/workflow/nodes/_base/components/before-run-form/bool-input'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { AppModeEnum, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
|
||||
export type IPromptValuePanelProps = {
|
||||
appType: AppModeEnum
|
||||
appType: ConfigurationAppMode
|
||||
onSend?: () => void
|
||||
inputs: Inputs
|
||||
visionConfig: VisionSettings
|
||||
@@ -93,7 +95,7 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
||||
}, [promptVariables, inputs, setInputs])
|
||||
|
||||
const canNotRun = useMemo(() => {
|
||||
if (mode !== AppModeEnum.COMPLETION) return true
|
||||
if (mode !== 'completion') return true
|
||||
|
||||
if (isAdvancedMode) {
|
||||
if (modelModeType === ModelModeType.chat)
|
||||
@@ -320,7 +322,7 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
||||
<div className="mx-3">
|
||||
<FeatureBar
|
||||
showFileUpload={false}
|
||||
isChatMode={appType !== AppModeEnum.COMPLETION}
|
||||
isChatMode={appType !== 'completion'}
|
||||
onFeatureBarClick={onOpenFeatures}
|
||||
disabled={readonly}
|
||||
hideEditEntrance={readonly}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ConfigurationAppMode } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types'
|
||||
import type { Features as FeaturesData, FileUpload } from '@/app/components/base/features/types'
|
||||
import type { Collection } from '@/app/components/tools/types'
|
||||
import type {
|
||||
@@ -8,7 +9,7 @@ import type {
|
||||
} from '@/models/debug'
|
||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { AppModeEnum, ModelModeType, Resolution } from '@/types/app'
|
||||
import { ModelModeType, Resolution } from '@/types/app'
|
||||
|
||||
export const withCollectionIconBasePath = (collectionList: Collection[], prefix?: string) => {
|
||||
if (!prefix) return collectionList
|
||||
@@ -84,12 +85,12 @@ export const getConfigurationPublishingState = ({
|
||||
hasSetContextVar: boolean
|
||||
hasSelectedDataSets: boolean
|
||||
isAdvancedMode: boolean
|
||||
mode: AppModeEnum
|
||||
mode: ConfigurationAppMode
|
||||
modelModeType: ModelModeType
|
||||
promptTemplate: string
|
||||
}) => {
|
||||
const promptEmpty = (() => {
|
||||
if (mode !== AppModeEnum.COMPLETION) return false
|
||||
if (mode !== 'completion') return false
|
||||
|
||||
if (isAdvancedMode) {
|
||||
if (modelModeType === ModelModeType.chat)
|
||||
@@ -102,7 +103,7 @@ export const getConfigurationPublishingState = ({
|
||||
})()
|
||||
|
||||
const cannotPublish = (() => {
|
||||
if (mode !== AppModeEnum.COMPLETION) {
|
||||
if (mode !== 'completion') {
|
||||
if (!isAdvancedMode) return false
|
||||
|
||||
if (modelModeType === ModelModeType.completion) {
|
||||
@@ -118,6 +119,6 @@ export const getConfigurationPublishingState = ({
|
||||
return {
|
||||
promptEmpty,
|
||||
cannotPublish,
|
||||
contextVarEmpty: mode === AppModeEnum.COMPLETION && hasSelectedDataSets && !hasSetContextVar,
|
||||
contextVarEmpty: mode === 'completion' && hasSelectedDataSets && !hasSetContextVar,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { trackEvent } from '@/app/components/base/amplitude'
|
||||
import { renderWithConsoleQuery } from '@/test/console/query-data'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import AppCard from '../index'
|
||||
|
||||
vi.mock('@/app/components/base/amplitude', () => ({ trackEvent: vi.fn() }))
|
||||
@@ -16,7 +15,7 @@ const app = {
|
||||
can_trial: true,
|
||||
app: {
|
||||
id: 'app-1',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
icon_type: 'emoji',
|
||||
icon: '🤖',
|
||||
icon_background: '#FFEAD5',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { RecommendedAppResponse } from '@dify/contracts/api/console/explore/types.gen'
|
||||
import type { AppType } from '@/app/components/apps/app-type'
|
||||
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { InputGroup, InputGroupAddon, InputGroupInput } from '@langgenius/dify-ui/input-group'
|
||||
@@ -23,7 +24,6 @@ import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { useCanImportAgents } from '@/features/agent-v2/permissions'
|
||||
import { useImportDSL } from '@/hooks/use-import-dsl'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { trackCreateApp } from '@/utils/create-app-tracking'
|
||||
import { hasPermission } from '@/utils/permission'
|
||||
import AppCard from '../app-card'
|
||||
@@ -62,7 +62,7 @@ const Apps = ({ onClose, onCreateFromBlank, templateMode }: AppsProps) => {
|
||||
searchInputRef.current?.focus()
|
||||
}
|
||||
|
||||
const [currentType, setCurrentType] = useState<AppModeEnum[]>([])
|
||||
const [currentType, setCurrentType] = useState<AppType[]>([])
|
||||
const [currCategory, setCurrCategory] = useState<AppCategories | string>(allCategoriesEn)
|
||||
|
||||
const { data, isLoading } = useQuery(
|
||||
@@ -74,7 +74,7 @@ const Apps = ({ onClose, onCreateFromBlank, templateMode }: AppsProps) => {
|
||||
() =>
|
||||
[...(data?.recommended_apps ?? [])]
|
||||
.filter((item) =>
|
||||
templateMode ? item.app?.mode === templateMode : item.app?.mode !== AppModeEnum.AGENT,
|
||||
templateMode ? item.app?.mode === templateMode : item.app?.mode !== 'agent',
|
||||
)
|
||||
.sort((a, b) => (a.position ?? 0) - (b.position ?? 0)),
|
||||
[data?.recommended_apps, templateMode],
|
||||
@@ -101,18 +101,11 @@ const Apps = ({ onClose, onCreateFromBlank, templateMode }: AppsProps) => {
|
||||
})
|
||||
if (currentType.length === 0) return filteredByCategory
|
||||
return filteredByCategory.filter((item) => {
|
||||
if (currentType.includes(AppModeEnum.CHAT) && item.app?.mode === AppModeEnum.CHAT) return true
|
||||
if (
|
||||
currentType.includes(AppModeEnum.ADVANCED_CHAT) &&
|
||||
item.app?.mode === AppModeEnum.ADVANCED_CHAT
|
||||
)
|
||||
return true
|
||||
if (currentType.includes(AppModeEnum.AGENT_CHAT) && item.app?.mode === AppModeEnum.AGENT_CHAT)
|
||||
return true
|
||||
if (currentType.includes(AppModeEnum.COMPLETION) && item.app?.mode === AppModeEnum.COMPLETION)
|
||||
return true
|
||||
if (currentType.includes(AppModeEnum.WORKFLOW) && item.app?.mode === AppModeEnum.WORKFLOW)
|
||||
return true
|
||||
if (currentType.includes('chat') && item.app?.mode === 'chat') return true
|
||||
if (currentType.includes('advanced-chat') && item.app?.mode === 'advanced-chat') return true
|
||||
if (currentType.includes('agent-chat') && item.app?.mode === 'agent-chat') return true
|
||||
if (currentType.includes('completion') && item.app?.mode === 'completion') return true
|
||||
if (currentType.includes('workflow') && item.app?.mode === 'workflow') return true
|
||||
return false
|
||||
})
|
||||
}, [currentType, activeCategory, allCategoriesEn, data, allList])
|
||||
@@ -152,7 +145,7 @@ const Apps = ({ onClose, onCreateFromBlank, templateMode }: AppsProps) => {
|
||||
})
|
||||
if (
|
||||
!canCreateTemplate(detail.mode) ||
|
||||
(templateMode ? detail.mode !== templateMode : detail.mode === AppModeEnum.AGENT)
|
||||
(templateMode ? detail.mode !== templateMode : detail.mode === 'agent')
|
||||
)
|
||||
throw new Error('Template mode does not match this picker')
|
||||
await handleImportDSL(
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useRouter } from '@/next/navigation'
|
||||
import { renderWithConsoleQuery } from '@/test/console/query-data'
|
||||
import { mockEmojiData } from '@/test/emoji-picker'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import { trackCreateApp } from '@/utils/create-app-tracking'
|
||||
import CreateAppModal from '../index'
|
||||
@@ -109,7 +108,7 @@ const renderModal = () => {
|
||||
show
|
||||
onClose={onClose}
|
||||
onCreateFromTemplate={onCreateFromTemplate}
|
||||
defaultAppMode={AppModeEnum.ADVANCED_CHAT}
|
||||
defaultAppMode={'advanced-chat'}
|
||||
/>,
|
||||
)
|
||||
return { onClose, onCreateFromTemplate }
|
||||
@@ -138,7 +137,7 @@ describe('CreateAppModal', () => {
|
||||
|
||||
it('creates an app and fires callbacks', async () => {
|
||||
const mockApp = createAppDetailFixture({
|
||||
mode: AppModeEnum.ADVANCED_CHAT,
|
||||
mode: 'advanced-chat' as const,
|
||||
maintainer: 'user-1',
|
||||
})
|
||||
mockCreateApp.mockResolvedValue(mockApp)
|
||||
@@ -155,13 +154,13 @@ describe('CreateAppModal', () => {
|
||||
icon_type: 'emoji',
|
||||
icon: '🤖',
|
||||
icon_background: '#FFEAD5',
|
||||
mode: AppModeEnum.ADVANCED_CHAT,
|
||||
mode: 'advanced-chat' as const,
|
||||
}),
|
||||
)
|
||||
|
||||
expect(mockTrackCreateApp).toHaveBeenCalledWith({
|
||||
source: 'studio_blank',
|
||||
appMode: AppModeEnum.ADVANCED_CHAT,
|
||||
appMode: 'advanced-chat' as const,
|
||||
})
|
||||
expect(onClose).toHaveBeenCalledTimes(1)
|
||||
await waitFor(() =>
|
||||
@@ -176,7 +175,7 @@ describe('CreateAppModal', () => {
|
||||
|
||||
it('waits for create_app tracking before redirecting after blank app creation', async () => {
|
||||
const mockApp = createAppDetailFixture({
|
||||
mode: AppModeEnum.ADVANCED_CHAT,
|
||||
mode: 'advanced-chat' as const,
|
||||
maintainer: 'user-1',
|
||||
})
|
||||
let resolveTracking: (() => void) | undefined
|
||||
@@ -196,7 +195,7 @@ describe('CreateAppModal', () => {
|
||||
await waitFor(() => {
|
||||
expect(mockTrackCreateApp).toHaveBeenCalledWith({
|
||||
source: 'studio_blank',
|
||||
appMode: AppModeEnum.ADVANCED_CHAT,
|
||||
appMode: 'advanced-chat' as const,
|
||||
})
|
||||
})
|
||||
const createButton = screen.getByRole('button', { name: /app\.newApp\.Create/ })
|
||||
@@ -251,7 +250,7 @@ describe('CreateAppModal', () => {
|
||||
async ({ platform, modifier }) => {
|
||||
vi.spyOn(navigator, 'platform', 'get').mockReturnValue(platform)
|
||||
mockCreateApp.mockResolvedValue(
|
||||
createAppDetailFixture({ id: 'chat-app', mode: AppModeEnum.CHAT }),
|
||||
createAppDetailFixture({ id: 'chat-app', mode: 'chat' as const }),
|
||||
)
|
||||
renderModal()
|
||||
|
||||
@@ -288,7 +287,7 @@ describe('CreateAppModal', () => {
|
||||
icon_type: 'emoji',
|
||||
icon: '🤖',
|
||||
icon_background: '#F3FEE7',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
})
|
||||
})
|
||||
},
|
||||
@@ -302,7 +301,7 @@ describe('CreateAppModal', () => {
|
||||
async ({ platform, modifier }) => {
|
||||
vi.spyOn(navigator, 'platform', 'get').mockReturnValue(platform)
|
||||
mockCreateApp.mockResolvedValue(
|
||||
createAppDetailFixture({ id: 'reopened-app', mode: AppModeEnum.ADVANCED_CHAT }),
|
||||
createAppDetailFixture({ id: 'reopened-app', mode: 'advanced-chat' as const }),
|
||||
)
|
||||
const onClose = vi.fn()
|
||||
const modal = (show: boolean) => (
|
||||
@@ -310,7 +309,7 @@ describe('CreateAppModal', () => {
|
||||
show={show}
|
||||
onClose={onClose}
|
||||
onCreateFromTemplate={vi.fn()}
|
||||
defaultAppMode={AppModeEnum.ADVANCED_CHAT}
|
||||
defaultAppMode={'advanced-chat'}
|
||||
/>
|
||||
)
|
||||
const { rerender } = render(modal(true))
|
||||
@@ -366,7 +365,7 @@ describe('CreateAppModal', () => {
|
||||
|
||||
it('should switch between app types before creating a completion app', async () => {
|
||||
mockCreateApp.mockResolvedValue(
|
||||
createAppDetailFixture({ id: 'completion-app', mode: AppModeEnum.COMPLETION }),
|
||||
createAppDetailFixture({ id: 'completion-app', mode: 'completion' as const }),
|
||||
)
|
||||
renderModal()
|
||||
|
||||
@@ -385,7 +384,7 @@ describe('CreateAppModal', () => {
|
||||
expect(mockCreateApp).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'Completion App',
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
}),
|
||||
)
|
||||
})
|
||||
@@ -416,7 +415,7 @@ describe('CreateAppModal', () => {
|
||||
|
||||
expect(mockCreateApp).toHaveBeenCalledTimes(1)
|
||||
|
||||
resolveCreate?.(createAppDetailFixture({ id: 'slow-app', mode: AppModeEnum.ADVANCED_CHAT }))
|
||||
resolveCreate?.(createAppDetailFixture({ id: 'slow-app', mode: 'advanced-chat' as const }))
|
||||
await waitFor(() => {
|
||||
expect(mockGetRedirection).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { Hotkey } from '@tanstack/react-hotkeys'
|
||||
import type { AppType } from '@/app/components/apps/app-type'
|
||||
import type { IconPickerValue } from '@/app/components/base/icon-picker'
|
||||
import { zPostAppsBody } from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
@@ -29,7 +30,6 @@ import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import { trackCreateApp } from '@/utils/create-app-tracking'
|
||||
import { hasPermission } from '@/utils/permission'
|
||||
@@ -39,17 +39,13 @@ import { CreateAppDialogShell } from '../create-app-dialog-shell'
|
||||
type CreateAppProps = {
|
||||
onClose: () => void
|
||||
onCreateFromTemplate?: () => void
|
||||
defaultAppMode?: AppModeEnum
|
||||
defaultAppMode?: AppType
|
||||
}
|
||||
|
||||
const CREATE_APP_HOTKEY = 'Mod+Enter' satisfies Hotkey
|
||||
|
||||
const shouldExpandBeginnerAppTypes = (appMode?: AppModeEnum) => {
|
||||
return (
|
||||
appMode === AppModeEnum.CHAT ||
|
||||
appMode === AppModeEnum.AGENT_CHAT ||
|
||||
appMode === AppModeEnum.COMPLETION
|
||||
)
|
||||
const shouldExpandBeginnerAppTypes = (appMode?: AppType) => {
|
||||
return appMode === 'chat' || appMode === 'agent-chat' || appMode === 'completion'
|
||||
}
|
||||
|
||||
function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppProps) {
|
||||
@@ -58,7 +54,7 @@ function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppP
|
||||
const nameInputId = useId()
|
||||
const contentRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [appMode, setAppMode] = useState<AppModeEnum>(defaultAppMode || AppModeEnum.ADVANCED_CHAT)
|
||||
const [appMode, setAppMode] = useState<AppType>(defaultAppMode || 'advanced-chat')
|
||||
const [appIcon, setAppIcon] = useState<IconPickerValue>({
|
||||
type: 'emoji',
|
||||
icon: '🤖',
|
||||
@@ -209,7 +205,7 @@ function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppP
|
||||
<div>
|
||||
<div className="flex flex-row gap-2">
|
||||
<AppTypeCard
|
||||
active={appMode === AppModeEnum.WORKFLOW}
|
||||
active={appMode === 'workflow'}
|
||||
title={t(($) => $['types.workflow'], { ns: 'app' })}
|
||||
description={t(($) => $['newApp.workflowShortDescription'], { ns: 'app' })}
|
||||
icon={
|
||||
@@ -221,11 +217,11 @@ function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppP
|
||||
</div>
|
||||
}
|
||||
onClick={() => {
|
||||
setAppMode(AppModeEnum.WORKFLOW)
|
||||
setAppMode('workflow')
|
||||
}}
|
||||
/>
|
||||
<AppTypeCard
|
||||
active={appMode === AppModeEnum.ADVANCED_CHAT}
|
||||
active={appMode === 'advanced-chat'}
|
||||
title={t(($) => $['types.advanced'], { ns: 'app' })}
|
||||
description={t(($) => $['newApp.advancedShortDescription'], { ns: 'app' })}
|
||||
icon={
|
||||
@@ -237,7 +233,7 @@ function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppP
|
||||
</div>
|
||||
}
|
||||
onClick={() => {
|
||||
setAppMode(AppModeEnum.ADVANCED_CHAT)
|
||||
setAppMode('advanced-chat')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -261,7 +257,7 @@ function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppP
|
||||
{isAppTypeExpanded && (
|
||||
<div className="flex flex-row gap-2">
|
||||
<AppTypeCard
|
||||
active={appMode === AppModeEnum.CHAT}
|
||||
active={appMode === 'chat'}
|
||||
title={t(($) => $['types.chatbot'], { ns: 'app' })}
|
||||
description={t(($) => $['newApp.chatbotShortDescription'], { ns: 'app' })}
|
||||
icon={
|
||||
@@ -273,11 +269,11 @@ function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppP
|
||||
</div>
|
||||
}
|
||||
onClick={() => {
|
||||
setAppMode(AppModeEnum.CHAT)
|
||||
setAppMode('chat')
|
||||
}}
|
||||
/>
|
||||
<AppTypeCard
|
||||
active={appMode === AppModeEnum.AGENT_CHAT}
|
||||
active={appMode === 'agent-chat'}
|
||||
title={t(($) => $['types.agent'], { ns: 'app' })}
|
||||
description={t(($) => $['newApp.agentShortDescription'], { ns: 'app' })}
|
||||
icon={
|
||||
@@ -289,11 +285,11 @@ function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppP
|
||||
</div>
|
||||
}
|
||||
onClick={() => {
|
||||
setAppMode(AppModeEnum.AGENT_CHAT)
|
||||
setAppMode('agent-chat')
|
||||
}}
|
||||
/>
|
||||
<AppTypeCard
|
||||
active={appMode === AppModeEnum.COMPLETION}
|
||||
active={appMode === 'completion'}
|
||||
title={t(($) => $['newApp.completeApp'], { ns: 'app' })}
|
||||
description={t(($) => $['newApp.completionShortDescription'], { ns: 'app' })}
|
||||
icon={
|
||||
@@ -305,7 +301,7 @@ function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppP
|
||||
</div>
|
||||
}
|
||||
onClick={() => {
|
||||
setAppMode(AppModeEnum.COMPLETION)
|
||||
setAppMode('completion')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -400,20 +396,11 @@ function CreateApp({ onClose, onCreateFromTemplate, defaultAppMode }: CreateAppP
|
||||
'repeating-linear-gradient(135deg, transparent, transparent 2px, rgba(16,24,40,0.04) 4px,transparent 3px, transparent 6px)',
|
||||
}}
|
||||
>
|
||||
<AppScreenShot show={appMode === AppModeEnum.CHAT} mode={AppModeEnum.CHAT} />
|
||||
<AppScreenShot
|
||||
show={appMode === AppModeEnum.ADVANCED_CHAT}
|
||||
mode={AppModeEnum.ADVANCED_CHAT}
|
||||
/>
|
||||
<AppScreenShot
|
||||
show={appMode === AppModeEnum.AGENT_CHAT}
|
||||
mode={AppModeEnum.AGENT_CHAT}
|
||||
/>
|
||||
<AppScreenShot
|
||||
show={appMode === AppModeEnum.COMPLETION}
|
||||
mode={AppModeEnum.COMPLETION}
|
||||
/>
|
||||
<AppScreenShot show={appMode === AppModeEnum.WORKFLOW} mode={AppModeEnum.WORKFLOW} />
|
||||
<AppScreenShot show={appMode === 'chat'} mode={'chat'} />
|
||||
<AppScreenShot show={appMode === 'advanced-chat'} mode={'advanced-chat'} />
|
||||
<AppScreenShot show={appMode === 'agent-chat'} mode={'agent-chat'} />
|
||||
<AppScreenShot show={appMode === 'completion'} mode={'completion'} />
|
||||
<AppScreenShot show={appMode === 'workflow'} mode={'workflow'} />
|
||||
</div>
|
||||
<div className="absolute inset-x-0 border-b border-b-divider-subtle"></div>
|
||||
</div>
|
||||
@@ -479,31 +466,31 @@ function AppTypeCard({ icon, title, description, active, onClick }: AppTypeCardP
|
||||
)
|
||||
}
|
||||
|
||||
function AppPreview({ mode }: { mode: AppModeEnum }) {
|
||||
function AppPreview({ mode }: { mode: AppType }) {
|
||||
const { t } = useTranslation(['app'])
|
||||
const previewInfo = (() => {
|
||||
switch (mode) {
|
||||
case AppModeEnum.CHAT:
|
||||
case 'chat':
|
||||
return {
|
||||
title: t(($) => $['types.chatbot'], { ns: 'app' }),
|
||||
description: t(($) => $['newApp.chatbotUserDescription'], { ns: 'app' }),
|
||||
}
|
||||
case AppModeEnum.ADVANCED_CHAT:
|
||||
case 'advanced-chat':
|
||||
return {
|
||||
title: t(($) => $['types.advanced'], { ns: 'app' }),
|
||||
description: t(($) => $['newApp.advancedUserDescription'], { ns: 'app' }),
|
||||
}
|
||||
case AppModeEnum.AGENT_CHAT:
|
||||
case 'agent-chat':
|
||||
return {
|
||||
title: t(($) => $['types.agent'], { ns: 'app' }),
|
||||
description: t(($) => $['newApp.agentUserDescription'], { ns: 'app' }),
|
||||
}
|
||||
case AppModeEnum.COMPLETION:
|
||||
case 'completion':
|
||||
return {
|
||||
title: t(($) => $['newApp.completeApp'], { ns: 'app' }),
|
||||
description: t(($) => $['newApp.completionUserDescription'], { ns: 'app' }),
|
||||
}
|
||||
case AppModeEnum.WORKFLOW:
|
||||
case 'workflow':
|
||||
return {
|
||||
title: t(($) => $['types.workflow'], { ns: 'app' }),
|
||||
description: t(($) => $['newApp.workflowUserDescription'], { ns: 'app' }),
|
||||
@@ -525,14 +512,14 @@ function AppPreview({ mode }: { mode: AppModeEnum }) {
|
||||
)
|
||||
}
|
||||
|
||||
function AppScreenShot({ mode, show }: { mode: AppModeEnum; show: boolean }) {
|
||||
function AppScreenShot({ mode, show }: { mode: AppType; show: boolean }) {
|
||||
const { theme } = useTheme()
|
||||
const modeToImageMap = {
|
||||
[AppModeEnum.CHAT]: 'Chatbot',
|
||||
[AppModeEnum.ADVANCED_CHAT]: 'Chatflow',
|
||||
[AppModeEnum.AGENT_CHAT]: 'Agent',
|
||||
[AppModeEnum.COMPLETION]: 'TextGenerator',
|
||||
[AppModeEnum.WORKFLOW]: 'Workflow',
|
||||
chat: 'Chatbot',
|
||||
'advanced-chat': 'Chatflow',
|
||||
'agent-chat': 'Agent',
|
||||
completion: 'TextGenerator',
|
||||
workflow: 'Workflow',
|
||||
}
|
||||
return (
|
||||
<picture>
|
||||
|
||||
@@ -4,7 +4,6 @@ import userEvent from '@testing-library/user-event'
|
||||
import { DSLImportMode, DSLImportStatus } from '@/models/app'
|
||||
import { httpErrorFromResponse } from '@/service/http-error'
|
||||
import { renderWithConsoleQuery } from '@/test/console/query-data'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import CreateFromDSLModal from '../index'
|
||||
import { CreateFromDSLModalTab } from '../types'
|
||||
|
||||
@@ -163,7 +162,7 @@ describe('CreateFromDSLModal', () => {
|
||||
id: 'package-import',
|
||||
status: 'completed-with-warnings',
|
||||
app_id: 'agent-app',
|
||||
app_mode: AppModeEnum.AGENT,
|
||||
app_mode: 'agent',
|
||||
warnings: [
|
||||
{ code: 'agent_skill_missing', path: 'skills.s_000001', message: 'Missing Skill' },
|
||||
],
|
||||
@@ -180,7 +179,7 @@ describe('CreateFromDSLModal', () => {
|
||||
})
|
||||
expect(mockResolveImportedAppRedirectionTarget).toHaveBeenCalledWith({
|
||||
id: 'agent-app',
|
||||
mode: AppModeEnum.AGENT,
|
||||
mode: 'agent' as const,
|
||||
permission_keys: undefined,
|
||||
})
|
||||
},
|
||||
@@ -193,7 +192,7 @@ describe('CreateFromDSLModal', () => {
|
||||
mockImportDSL.mockResolvedValue({
|
||||
id: 'import',
|
||||
status: 'completed',
|
||||
app_mode: AppModeEnum.AGENT,
|
||||
app_mode: 'agent',
|
||||
})
|
||||
render(<CreateFromDSLModal show onClose={vi.fn()} />)
|
||||
const input = document.querySelector<HTMLInputElement>('input[type="file"]')
|
||||
@@ -309,7 +308,7 @@ describe('CreateFromDSLModal', () => {
|
||||
id: 'import-1',
|
||||
status: DSLImportStatus.COMPLETED,
|
||||
app_id: 'app-1',
|
||||
app_mode: AppModeEnum.CHAT,
|
||||
app_mode: 'chat',
|
||||
permission_keys: ['app.acl.view_layout'],
|
||||
})
|
||||
|
||||
@@ -336,7 +335,7 @@ describe('CreateFromDSLModal', () => {
|
||||
})
|
||||
expect(mockTrackCreateApp).toHaveBeenCalledWith({
|
||||
source: 'studio_upload',
|
||||
appMode: AppModeEnum.CHAT,
|
||||
appMode: 'chat' as const,
|
||||
})
|
||||
expect(handleSuccess).toHaveBeenCalledTimes(1)
|
||||
expect(handleClose).toHaveBeenCalledTimes(1)
|
||||
@@ -358,7 +357,7 @@ describe('CreateFromDSLModal', () => {
|
||||
id: 'import-no-permissions',
|
||||
status: DSLImportStatus.COMPLETED,
|
||||
app_id: 'app-no-permissions',
|
||||
app_mode: AppModeEnum.WORKFLOW,
|
||||
app_mode: 'workflow',
|
||||
})
|
||||
|
||||
render(
|
||||
@@ -375,7 +374,7 @@ describe('CreateFromDSLModal', () => {
|
||||
})
|
||||
|
||||
expect(mockGetRedirection).toHaveBeenCalledWith(
|
||||
{ id: 'app-no-permissions', mode: AppModeEnum.WORKFLOW, permission_keys: undefined },
|
||||
{ id: 'app-no-permissions', mode: 'workflow' as const, permission_keys: undefined },
|
||||
mockPush,
|
||||
{
|
||||
currentUserId: 'user-1',
|
||||
@@ -391,7 +390,7 @@ describe('CreateFromDSLModal', () => {
|
||||
id: 'import-2',
|
||||
status: DSLImportStatus.COMPLETED_WITH_WARNINGS,
|
||||
app_id: 'app-2',
|
||||
app_mode: AppModeEnum.AGENT,
|
||||
app_mode: 'agent',
|
||||
permission_keys: ['app.acl.view_layout'],
|
||||
warnings: [
|
||||
{
|
||||
@@ -446,7 +445,7 @@ describe('CreateFromDSLModal', () => {
|
||||
id: 'import-file-in-flight',
|
||||
status: DSLImportStatus.COMPLETED,
|
||||
app_id: 'app-1',
|
||||
app_mode: AppModeEnum.CHAT,
|
||||
app_mode: 'chat',
|
||||
permission_keys: ['app.acl.view_layout'],
|
||||
})
|
||||
|
||||
@@ -517,7 +516,7 @@ describe('CreateFromDSLModal', () => {
|
||||
mockImportDSLConfirm.mockResolvedValue({
|
||||
status: DSLImportStatus.COMPLETED,
|
||||
app_id: 'app-3',
|
||||
app_mode: AppModeEnum.WORKFLOW,
|
||||
app_mode: 'workflow',
|
||||
permission_keys: ['app.acl.view_layout'],
|
||||
})
|
||||
|
||||
@@ -548,10 +547,10 @@ describe('CreateFromDSLModal', () => {
|
||||
})
|
||||
expect(mockTrackCreateApp).toHaveBeenCalledWith({
|
||||
source: 'studio_upload',
|
||||
appMode: AppModeEnum.WORKFLOW,
|
||||
appMode: 'workflow' as const,
|
||||
})
|
||||
expect(mockGetRedirection).toHaveBeenCalledWith(
|
||||
{ id: 'app-3', mode: AppModeEnum.WORKFLOW, permission_keys: ['app.acl.view_layout'] },
|
||||
{ id: 'app-3', mode: 'workflow' as const, permission_keys: ['app.acl.view_layout'] },
|
||||
mockPush,
|
||||
{
|
||||
currentUserId: 'user-1',
|
||||
@@ -569,7 +568,7 @@ describe('CreateFromDSLModal', () => {
|
||||
const completed = {
|
||||
status: DSLImportStatus.COMPLETED,
|
||||
app_id: 'created-app',
|
||||
app_mode: AppModeEnum.WORKFLOW,
|
||||
app_mode: 'workflow',
|
||||
}
|
||||
mockImportDSL.mockResolvedValue(
|
||||
confirmed ? { id: 'pending-import', status: DSLImportStatus.PENDING } : completed,
|
||||
@@ -606,7 +605,7 @@ describe('CreateFromDSLModal', () => {
|
||||
mockImportDSLConfirm.mockResolvedValue({
|
||||
status: DSLImportStatus.COMPLETED_WITH_WARNINGS,
|
||||
app_id: 'agent-app-1',
|
||||
app_mode: AppModeEnum.AGENT,
|
||||
app_mode: 'agent',
|
||||
warnings: [
|
||||
{
|
||||
code: 'agent_tool_authorization_required',
|
||||
@@ -618,7 +617,7 @@ describe('CreateFromDSLModal', () => {
|
||||
})
|
||||
mockResolveImportedAppRedirectionTarget.mockResolvedValueOnce({
|
||||
id: 'agent-app-1',
|
||||
mode: AppModeEnum.AGENT,
|
||||
mode: 'agent' as const,
|
||||
permission_keys: undefined,
|
||||
bound_agent_id: 'agent-1',
|
||||
})
|
||||
@@ -645,13 +644,13 @@ describe('CreateFromDSLModal', () => {
|
||||
})
|
||||
expect(mockResolveImportedAppRedirectionTarget).toHaveBeenCalledWith({
|
||||
id: 'agent-app-1',
|
||||
mode: AppModeEnum.AGENT,
|
||||
mode: 'agent' as const,
|
||||
permission_keys: undefined,
|
||||
})
|
||||
expect(mockGetRedirection).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: 'agent-app-1',
|
||||
mode: AppModeEnum.AGENT,
|
||||
mode: 'agent' as const,
|
||||
bound_agent_id: 'agent-1',
|
||||
}),
|
||||
mockPush,
|
||||
@@ -771,7 +770,7 @@ describe('CreateFromDSLModal', () => {
|
||||
id: 'import-in-flight',
|
||||
status: DSLImportStatus.COMPLETED,
|
||||
app_id: 'app-1',
|
||||
app_mode: AppModeEnum.CHAT,
|
||||
app_mode: 'chat',
|
||||
permission_keys: ['app.acl.view_layout'],
|
||||
})
|
||||
})
|
||||
@@ -834,7 +833,7 @@ describe('CreateFromDSLModal', () => {
|
||||
id: 'import-confirming',
|
||||
status: DSLImportStatus.COMPLETED,
|
||||
app_id: 'app-confirming',
|
||||
app_mode: AppModeEnum.WORKFLOW,
|
||||
app_mode: 'workflow',
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -882,7 +881,7 @@ describe('CreateFromDSLModal', () => {
|
||||
mockImportDSL.mockResolvedValue({
|
||||
id: 'package-import',
|
||||
status: 'completed',
|
||||
app_mode: AppModeEnum.AGENT,
|
||||
app_mode: 'agent',
|
||||
})
|
||||
render(<CreateFromDSLModal show onClose={vi.fn()} />)
|
||||
expect(screen.getByText('apps-full')).toBeInTheDocument()
|
||||
@@ -918,7 +917,7 @@ describe('CreateFromDSLModal', () => {
|
||||
mockImportDSL.mockResolvedValue({
|
||||
id: 'package-import',
|
||||
status: 'completed',
|
||||
app_mode: AppModeEnum.AGENT,
|
||||
app_mode: 'agent',
|
||||
})
|
||||
render(<CreateFromDSLModal show onClose={vi.fn()} />)
|
||||
expect(
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { AppImportPayload, Import } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { AppImportPayload, AppMode, Import } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { Hotkey } from '@tanstack/react-hotkeys'
|
||||
import type { AppModeEnum } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import {
|
||||
Dialog,
|
||||
@@ -30,7 +29,6 @@ import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum as AppMode } from '@/types/app'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import { trackCreateApp } from '@/utils/create-app-tracking'
|
||||
import { getErrorMessage } from '@/utils/error-message'
|
||||
@@ -65,20 +63,20 @@ type ImportSource =
|
||||
|
||||
const CREATE_FROM_DSL_HOTKEY = 'Mod+Enter' satisfies Hotkey
|
||||
|
||||
function getImportedAppMode(mode?: string | null): AppModeEnum | undefined {
|
||||
function getImportedAppMode(mode?: string | null): AppMode | undefined {
|
||||
switch (mode) {
|
||||
case AppMode.COMPLETION:
|
||||
return AppMode.COMPLETION
|
||||
case AppMode.WORKFLOW:
|
||||
return AppMode.WORKFLOW
|
||||
case AppMode.CHAT:
|
||||
return AppMode.CHAT
|
||||
case AppMode.ADVANCED_CHAT:
|
||||
return AppMode.ADVANCED_CHAT
|
||||
case AppMode.AGENT_CHAT:
|
||||
return AppMode.AGENT_CHAT
|
||||
case AppMode.AGENT:
|
||||
return AppMode.AGENT
|
||||
case 'completion':
|
||||
return 'completion'
|
||||
case 'workflow':
|
||||
return 'workflow'
|
||||
case 'chat':
|
||||
return 'chat'
|
||||
case 'advanced-chat':
|
||||
return 'advanced-chat'
|
||||
case 'agent-chat':
|
||||
return 'agent-chat'
|
||||
case 'agent':
|
||||
return 'agent'
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
seedAppDetail,
|
||||
} from '@/test/console/query-data'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { AppACLPermission } from '@/utils/permission'
|
||||
import AppDeploy from '..'
|
||||
import { EnvironmentTable } from '../environment-table'
|
||||
@@ -745,7 +744,7 @@ function render(
|
||||
queryClient,
|
||||
createAppDetailFixture({
|
||||
...mockBuiltInEnvironment.appDetail,
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
mode: 'workflow',
|
||||
permission_keys: appPermissionKeys,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -18,7 +18,6 @@ import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { getDocLanguage } from '@/i18n/language'
|
||||
import { useParams } from '@/next/navigation'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getAppACLCapabilities } from '@/utils/permission'
|
||||
import { BuiltInEnvironmentCard } from './built-in-environment-card'
|
||||
import { EnvironmentTable } from './environment-table'
|
||||
@@ -170,10 +169,7 @@ export default function AppDeploy() {
|
||||
workspacePermissionKeys,
|
||||
})
|
||||
|
||||
if (
|
||||
(appDetail.mode !== AppModeEnum.WORKFLOW && appDetail.mode !== AppModeEnum.ADVANCED_CHAT) ||
|
||||
!canDeploy
|
||||
)
|
||||
if ((appDetail.mode !== 'workflow' && appDetail.mode !== 'advanced-chat') || !canDeploy)
|
||||
return null
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,7 +6,6 @@ import { PageType } from '@/app/components/base/features/new-feature-panel/annot
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { createConsoleQueryClient, renderWithConsoleQuery } from '@/test/console/query-data'
|
||||
import { createAppDetailFixture, createAppSiteFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import LogAnnotation from '../index'
|
||||
|
||||
vi.mock('@/app/components/app/annotation', () => ({
|
||||
@@ -75,7 +74,7 @@ describe('LogAnnotation', () => {
|
||||
|
||||
it('should render log content without the old page tabs', () => {
|
||||
// Arrange
|
||||
appDetail = createMockApp({ mode: AppModeEnum.CHAT })
|
||||
appDetail = createMockApp({ mode: 'chat' })
|
||||
|
||||
// Act
|
||||
render(<LogAnnotation appId="app-123" pageType={PageType.log} />)
|
||||
@@ -88,7 +87,7 @@ describe('LogAnnotation', () => {
|
||||
|
||||
it('should render completion logs without the old page tabs', () => {
|
||||
// Arrange
|
||||
appDetail = createMockApp({ mode: AppModeEnum.COMPLETION })
|
||||
appDetail = createMockApp({ mode: 'completion' as const })
|
||||
|
||||
// Act
|
||||
render(<LogAnnotation appId="app-123" pageType={PageType.log} />)
|
||||
@@ -101,7 +100,7 @@ describe('LogAnnotation', () => {
|
||||
|
||||
it('should hide tabs and render workflow log in workflow mode', () => {
|
||||
// Arrange
|
||||
appDetail = createMockApp({ mode: AppModeEnum.WORKFLOW })
|
||||
appDetail = createMockApp({ mode: 'workflow' as const })
|
||||
|
||||
// Act
|
||||
render(<LogAnnotation appId="app-123" pageType={PageType.log} />)
|
||||
@@ -116,7 +115,7 @@ describe('LogAnnotation', () => {
|
||||
describe('Props', () => {
|
||||
it('should render log content when page type is log', () => {
|
||||
// Arrange
|
||||
appDetail = createMockApp({ mode: AppModeEnum.CHAT })
|
||||
appDetail = createMockApp({ mode: 'chat' })
|
||||
|
||||
// Act
|
||||
render(<LogAnnotation appId="app-123" pageType={PageType.log} />)
|
||||
@@ -128,7 +127,7 @@ describe('LogAnnotation', () => {
|
||||
|
||||
it('should render annotation content when page type is annotation', () => {
|
||||
// Arrange
|
||||
appDetail = createMockApp({ mode: AppModeEnum.CHAT })
|
||||
appDetail = createMockApp({ mode: 'chat' })
|
||||
|
||||
// Act
|
||||
render(<LogAnnotation appId="app-123" pageType={PageType.annotation} />)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import * as React from 'react'
|
||||
@@ -7,7 +8,6 @@ import Log from '@/app/components/app/log'
|
||||
import WorkflowLog from '@/app/components/app/workflow-log'
|
||||
import { PageType } from '@/app/components/base/features/new-feature-panel/annotation-reply/type'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
|
||||
type Props = Readonly<{
|
||||
appId: string
|
||||
@@ -24,11 +24,11 @@ const LogAnnotation: FC<Props> = ({ appId, pageType }) => {
|
||||
return (
|
||||
<div className="flex h-full flex-col px-6 pt-3">
|
||||
<div className="h-0 grow">
|
||||
{pageType === PageType.log && appDetail.mode !== AppModeEnum.WORKFLOW && (
|
||||
{pageType === PageType.log && appDetail.mode !== 'workflow' && (
|
||||
<Log appDetail={appDetail} />
|
||||
)}
|
||||
{pageType === PageType.annotation && <Annotation appDetail={appDetail} />}
|
||||
{pageType === PageType.log && appDetail.mode === AppModeEnum.WORKFLOW && (
|
||||
{pageType === PageType.log && appDetail.mode === 'workflow' && (
|
||||
<WorkflowLog appDetail={appDetail} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { AppDetailWithSite } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { AppDetailWithSite, AppMode } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import { screen } from '@testing-library/react'
|
||||
import { renderWithConsoleQuery as render } from '@/test/console/query-data'
|
||||
import { createAppDetailFixture, createAppSiteFixture } from '@/test/fixtures/app'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import EmptyElement from '../empty-element'
|
||||
|
||||
vi.mock('react-i18next', async () => {
|
||||
@@ -39,7 +38,7 @@ vi.mock('@/utils/var', () => ({
|
||||
}))
|
||||
|
||||
describe('EmptyElement', () => {
|
||||
const createMockAppDetail = (mode: AppModeEnum) =>
|
||||
const createMockAppDetail = (mode: AppMode) =>
|
||||
createAppDetailFixture({
|
||||
id: 'test-app-id',
|
||||
name: 'Test App',
|
||||
@@ -59,14 +58,14 @@ describe('EmptyElement', () => {
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should render empty element with title', () => {
|
||||
const appDetail = createMockAppDetail(AppModeEnum.CHAT)
|
||||
const appDetail = createMockAppDetail('chat')
|
||||
render(<EmptyElement appDetail={appDetail} />)
|
||||
|
||||
expect(screen.getByText('table.empty.element.title')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render Trans component with i18n key', () => {
|
||||
const appDetail = createMockAppDetail(AppModeEnum.CHAT)
|
||||
const appDetail = createMockAppDetail('chat')
|
||||
render(<EmptyElement appDetail={appDetail} />)
|
||||
|
||||
expect(screen.getByText('table.empty.element.content', { exact: false })).toBeInTheDocument()
|
||||
@@ -75,7 +74,7 @@ describe('EmptyElement', () => {
|
||||
|
||||
describe('App Mode Handling', () => {
|
||||
it('should use CHAT mode for chat apps', () => {
|
||||
const appDetail = createMockAppDetail(AppModeEnum.CHAT)
|
||||
const appDetail = createMockAppDetail('chat')
|
||||
render(<EmptyElement appDetail={appDetail} />)
|
||||
|
||||
const link = screen.getAllByRole('link')[0]
|
||||
@@ -83,7 +82,7 @@ describe('EmptyElement', () => {
|
||||
})
|
||||
|
||||
it('should use COMPLETION mode for completion apps', () => {
|
||||
const appDetail = createMockAppDetail(AppModeEnum.COMPLETION)
|
||||
const appDetail = createMockAppDetail('completion')
|
||||
render(<EmptyElement appDetail={appDetail} />)
|
||||
|
||||
const link = screen.getAllByRole('link')[0]
|
||||
@@ -91,7 +90,7 @@ describe('EmptyElement', () => {
|
||||
})
|
||||
|
||||
it('should use WORKFLOW mode for workflow apps', () => {
|
||||
const appDetail = createMockAppDetail(AppModeEnum.WORKFLOW)
|
||||
const appDetail = createMockAppDetail('workflow')
|
||||
render(<EmptyElement appDetail={appDetail} />)
|
||||
|
||||
const link = screen.getAllByRole('link')[0]
|
||||
@@ -99,7 +98,7 @@ describe('EmptyElement', () => {
|
||||
})
|
||||
|
||||
it('should use CHAT mode for advanced-chat apps', () => {
|
||||
const appDetail = createMockAppDetail(AppModeEnum.ADVANCED_CHAT)
|
||||
const appDetail = createMockAppDetail('advanced-chat')
|
||||
render(<EmptyElement appDetail={appDetail} />)
|
||||
|
||||
const link = screen.getAllByRole('link')[0]
|
||||
@@ -107,7 +106,7 @@ describe('EmptyElement', () => {
|
||||
})
|
||||
|
||||
it('should use CHAT mode for agent-chat apps', () => {
|
||||
const appDetail = createMockAppDetail(AppModeEnum.AGENT_CHAT)
|
||||
const appDetail = createMockAppDetail('agent-chat')
|
||||
render(<EmptyElement appDetail={appDetail} />)
|
||||
|
||||
const link = screen.getAllByRole('link')[0]
|
||||
@@ -128,7 +127,7 @@ describe('EmptyElement', () => {
|
||||
|
||||
describe('Links', () => {
|
||||
it('should render share link with correct attributes', () => {
|
||||
const appDetail = createMockAppDetail(AppModeEnum.CHAT)
|
||||
const appDetail = createMockAppDetail('chat')
|
||||
render(<EmptyElement appDetail={appDetail} />)
|
||||
|
||||
const links = screen.getAllByRole('link')
|
||||
@@ -139,7 +138,7 @@ describe('EmptyElement', () => {
|
||||
})
|
||||
|
||||
it('should render test link with redirection path', () => {
|
||||
const appDetail = createMockAppDetail(AppModeEnum.CHAT)
|
||||
const appDetail = createMockAppDetail('chat')
|
||||
render(<EmptyElement appDetail={appDetail} />)
|
||||
|
||||
const links = screen.getAllByRole('link')
|
||||
|
||||
@@ -3,7 +3,6 @@ import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import dayjs from 'dayjs'
|
||||
import { APP_PAGE_LIMIT } from '@/config'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import Logs from '../index'
|
||||
|
||||
vi.mock('@/context/i18n', () => ({
|
||||
@@ -94,7 +93,7 @@ describe('Logs', () => {
|
||||
appDetail={
|
||||
{
|
||||
id: 'app-1',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
} as any
|
||||
}
|
||||
/>,
|
||||
@@ -125,7 +124,7 @@ describe('Logs', () => {
|
||||
appDetail={
|
||||
{
|
||||
id: 'app-2',
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
mode: 'completion' as const,
|
||||
} as any
|
||||
}
|
||||
/>,
|
||||
@@ -150,7 +149,7 @@ describe('Logs', () => {
|
||||
appDetail={
|
||||
{
|
||||
id: 'app-3',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
} as any
|
||||
}
|
||||
/>,
|
||||
@@ -174,7 +173,7 @@ describe('Logs', () => {
|
||||
appDetail={
|
||||
{
|
||||
id: 'app-sandbox-last-30-days',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
} as any
|
||||
}
|
||||
/>,
|
||||
@@ -200,7 +199,7 @@ describe('Logs', () => {
|
||||
const user = userEvent.setup()
|
||||
const appDetail = {
|
||||
id: 'app-period-transition',
|
||||
mode: AppModeEnum.CHAT,
|
||||
mode: 'chat' as const,
|
||||
} as any
|
||||
mockUseChatConversations.mockReturnValue({
|
||||
data: { total: 0 },
|
||||
|
||||
@@ -8,7 +8,6 @@ import { QueryClientTestProvider } from '@/test/console/query-provider'
|
||||
import { render } from '@/test/console/render'
|
||||
import { createAppDetailFixture } from '@/test/fixtures/app'
|
||||
import { renderWithNuqs } from '@/test/nuqs-testing'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import ConversationList from '../list'
|
||||
|
||||
const mockFetchChatMessages = vi.fn()
|
||||
@@ -304,7 +303,7 @@ const createChatMessage = (id: string, overrides: Record<string, unknown> = {})
|
||||
})
|
||||
|
||||
const renderConversationList = ({
|
||||
appDetail = { id: 'app-1', mode: AppModeEnum.CHAT } as any,
|
||||
appDetail = { id: 'app-1', mode: 'chat' as const } as any,
|
||||
logs = createLogs() as any,
|
||||
searchParams = '?page=2',
|
||||
}: {
|
||||
@@ -518,36 +517,39 @@ describe('ConversationList', () => {
|
||||
)
|
||||
|
||||
it.each([
|
||||
['chatbot', AppModeEnum.CHAT, 'false'],
|
||||
['agent', AppModeEnum.AGENT_CHAT, 'false'],
|
||||
['chatflow', AppModeEnum.ADVANCED_CHAT, 'true'],
|
||||
])('should expose run details only for %s conversation answers', async (_, mode, expected) => {
|
||||
mockChatConversationDetail = {
|
||||
id: 'conversation-1',
|
||||
created_at: 1710000000,
|
||||
model_config: {
|
||||
model: 'gpt-4o',
|
||||
configs: {
|
||||
introduction: 'Hello there',
|
||||
['chatbot', 'chat', 'false'],
|
||||
['agent', 'agent-chat', 'false'],
|
||||
['chatflow', 'advanced-chat', 'true'],
|
||||
] as const)(
|
||||
'should expose run details only for %s conversation answers',
|
||||
async (_, mode, expected) => {
|
||||
mockChatConversationDetail = {
|
||||
id: 'conversation-1',
|
||||
created_at: 1710000000,
|
||||
model_config: {
|
||||
model: 'gpt-4o',
|
||||
configs: {
|
||||
introduction: 'Hello there',
|
||||
},
|
||||
user_input_form: [],
|
||||
},
|
||||
user_input_form: [],
|
||||
},
|
||||
message: {
|
||||
inputs: {},
|
||||
},
|
||||
}
|
||||
mockFetchChatMessages.mockResolvedValue({
|
||||
data: [createChatMessage('message-1')],
|
||||
has_more: false,
|
||||
})
|
||||
message: {
|
||||
inputs: {},
|
||||
},
|
||||
}
|
||||
mockFetchChatMessages.mockResolvedValue({
|
||||
data: [createChatMessage('message-1')],
|
||||
has_more: false,
|
||||
})
|
||||
|
||||
renderConversationList({
|
||||
appDetail: { id: 'app-1', mode } as any,
|
||||
searchParams: '?conversation_id=conversation-1',
|
||||
})
|
||||
renderConversationList({
|
||||
appDetail: { id: 'app-1', mode } as any,
|
||||
searchParams: '?conversation_id=conversation-1',
|
||||
})
|
||||
|
||||
expect(await screen.findByTestId('chat-panel')).toHaveAttribute('data-log-enabled', expected)
|
||||
})
|
||||
expect(await screen.findByTestId('chat-panel')).toHaveAttribute('data-log-enabled', expected)
|
||||
},
|
||||
)
|
||||
|
||||
it('should render completion details and refetch after feedback updates', async () => {
|
||||
mockCompletionConversationDetail = {
|
||||
@@ -575,7 +577,7 @@ describe('ConversationList', () => {
|
||||
}
|
||||
|
||||
renderConversationList({
|
||||
appDetail: { id: 'app-1', mode: AppModeEnum.COMPLETION } as any,
|
||||
appDetail: { id: 'app-1', mode: 'completion' as const } as any,
|
||||
logs: createCompletionLogs() as any,
|
||||
searchParams: '?page=2&conversation_id=conversation-1',
|
||||
})
|
||||
@@ -605,7 +607,7 @@ describe('ConversationList', () => {
|
||||
|
||||
it('should render chatflow status cells and feedback counters for advanced chat logs', () => {
|
||||
renderConversationList({
|
||||
appDetail: { id: 'app-1', mode: AppModeEnum.ADVANCED_CHAT } as any,
|
||||
appDetail: { id: 'app-1', mode: 'advanced-chat' as const } as any,
|
||||
logs: {
|
||||
data: [
|
||||
{
|
||||
@@ -744,7 +746,7 @@ describe('ConversationList', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it.each(['workflow', 'agent', 'prompt'])(
|
||||
it.each(['workflow', 'agent', 'prompt'] as const)(
|
||||
'should open and close the selected %s log within the conversation',
|
||||
async (kind) => {
|
||||
mockChatConversationDetail = {
|
||||
@@ -753,7 +755,7 @@ describe('ConversationList', () => {
|
||||
message: { inputs: {} },
|
||||
}
|
||||
renderConversationList({
|
||||
appDetail: { id: 'app-1', mode: AppModeEnum.ADVANCED_CHAT },
|
||||
appDetail: { id: 'app-1', mode: 'advanced-chat' as const },
|
||||
searchParams: '?conversation_id=conversation-1',
|
||||
})
|
||||
const user = userEvent.setup()
|
||||
@@ -776,7 +778,7 @@ describe('ConversationList', () => {
|
||||
message: { inputs: {} },
|
||||
}
|
||||
renderConversationList({
|
||||
appDetail: { id: 'app-1', mode: AppModeEnum.ADVANCED_CHAT },
|
||||
appDetail: { id: 'app-1', mode: 'advanced-chat' as const },
|
||||
searchParams: '?conversation_id=conversation-1',
|
||||
})
|
||||
const user = userEvent.setup()
|
||||
@@ -791,7 +793,7 @@ describe('ConversationList', () => {
|
||||
it('should clear a selected log when URL navigation changes or closes the conversation', async () => {
|
||||
const user = userEvent.setup()
|
||||
const queryClient = createAccountProfileQueryClient({ timezone: 'Asia/Shanghai' })
|
||||
const appDetail = createAppDetailFixture({ mode: AppModeEnum.ADVANCED_CHAT })
|
||||
const appDetail = createAppDetailFixture({ mode: 'advanced-chat' as const })
|
||||
const view = (searchParams: string) => (
|
||||
<NuqsTestingAdapter searchParams={searchParams} hasMemory>
|
||||
<QueryClientTestProvider queryClient={queryClient}>
|
||||
|
||||
@@ -10,7 +10,6 @@ import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import Link from '@/next/link'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getRedirectionPath } from '@/utils/app-redirection'
|
||||
import { basePath } from '@/utils/var'
|
||||
|
||||
@@ -46,8 +45,7 @@ const EmptyElement: FC<{ appDetail: AppDetailWithSite }> = ({ appDetail }) => {
|
||||
const isRbacEnabled = systemFeatures.rbac_enabled
|
||||
|
||||
const getWebAppType = (appType: AppDetailWithSite['mode']) => {
|
||||
if (appType !== AppModeEnum.COMPLETION && appType !== AppModeEnum.WORKFLOW)
|
||||
return AppModeEnum.CHAT
|
||||
if (appType !== 'completion' && appType !== 'workflow') return 'chat'
|
||||
return appType
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import { APP_PAGE_LIMIT } from '@/config'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { usePathname, useRouter, useSearchParams } from '@/next/navigation'
|
||||
import { useChatConversations, useCompletionConversations } from '@/service/use-log'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import PageTitle from '../log-annotation/page-title'
|
||||
import {
|
||||
resolveLogTimePeriod,
|
||||
@@ -96,7 +95,7 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
|
||||
}, [appDetail.id, currPage, limit, queryParams])
|
||||
|
||||
// Get the app type first
|
||||
const isChatMode = appDetail.mode !== AppModeEnum.COMPLETION
|
||||
const isChatMode = appDetail.mode !== 'completion'
|
||||
|
||||
const query = {
|
||||
page: currPage + 1,
|
||||
|
||||
@@ -61,7 +61,6 @@ import {
|
||||
} from '@/service/log'
|
||||
import { AppSourceType } from '@/service/share'
|
||||
import { useChatConversationDetail, useCompletionConversationDetail } from '@/service/use-log'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import PromptLogModal from '../../base/prompt-log-modal'
|
||||
import {
|
||||
applyAnnotationAdded,
|
||||
@@ -301,12 +300,7 @@ function DetailPanel({ appDetail, detail, onClose, onFeedback }: IDetailPanel) {
|
||||
|
||||
// Only load initial messages, don't auto-load more
|
||||
useEffect(() => {
|
||||
if (
|
||||
appDetail.id &&
|
||||
detail.id &&
|
||||
appDetail.mode !== AppModeEnum.COMPLETION &&
|
||||
!fetchInitiated.current
|
||||
) {
|
||||
if (appDetail.id && detail.id && appDetail.mode !== 'completion' && !fetchInitiated.current) {
|
||||
// Mark as initialized, but don't auto-load more messages
|
||||
fetchInitiated.current = true
|
||||
// Still call fetchData to get initial messages
|
||||
@@ -400,8 +394,8 @@ function DetailPanel({ appDetail, detail, onClose, onFeedback }: IDetailPanel) {
|
||||
}
|
||||
}, [hasMore, isLoading, loadMoreMessages])
|
||||
|
||||
const isChatMode = appDetail.mode !== AppModeEnum.COMPLETION
|
||||
const isAdvanced = appDetail.mode === AppModeEnum.ADVANCED_CHAT
|
||||
const isChatMode = appDetail.mode !== 'completion'
|
||||
const isAdvanced = appDetail.mode === 'advanced-chat'
|
||||
const logKind = selectedLogItem?.workflow_run_id
|
||||
? 'workflow'
|
||||
: selectedLogItem?.agent_thoughts?.length
|
||||
@@ -752,8 +746,8 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
|
||||
const closingConversationIdRef = useRef<string | null>(null)
|
||||
const pendingConversationIdRef = useRef<string | null>(null)
|
||||
const pendingConversationCacheRef = useRef<ConversationSelection | undefined>(undefined)
|
||||
const isChatMode = appDetail.mode !== AppModeEnum.COMPLETION // Whether the app is a chat app
|
||||
const isChatflow = appDetail.mode === AppModeEnum.ADVANCED_CHAT // Whether the app is a chatflow app
|
||||
const isChatMode = appDetail.mode !== 'completion' // Whether the app is a chat app
|
||||
const isChatflow = appDetail.mode === 'advanced-chat' // Whether the app is a chatflow app
|
||||
const activeConversationId =
|
||||
conversationIdInUrl ?? pendingConversationIdRef.current ?? currentConversation?.id
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { AppMode } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Button, buttonVariants } from '@langgenius/dify-ui/button'
|
||||
@@ -14,7 +15,6 @@ import {
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
|
||||
type CustomizeDialogProps = {
|
||||
disabled?: boolean
|
||||
@@ -62,7 +62,7 @@ export function CustomizeDialog({
|
||||
}: CustomizeDialogProps) {
|
||||
const { t } = useTranslation(['appOverview', 'common'])
|
||||
const docLink = useDocLink()
|
||||
const isChatApp = mode === AppModeEnum.CHAT || mode === AppModeEnum.ADVANCED_CHAT
|
||||
const isChatApp = mode === 'chat' || mode === 'advanced-chat'
|
||||
const repository =
|
||||
sourceCodeRepository ?? (isChatApp ? 'webapp-conversation' : 'webapp-text-generator')
|
||||
const apiDocLink = docLink('/api-reference/guides/get-started')
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user