mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 09:58:04 +08:00
fix: pass all CI quality checks - ESLint, TypeScript, basedpyright, pyrefly, lint-imports
Frontend: - Migrate deprecated imports: modal→dialog, toast→ui/toast, tooltip→tooltip-plus, portal-to-follow-elem→portal-to-follow-elem-plus, select→ui/select, confirm→alert-dialog - Replace next/* with @/next/* wrapper modules - Convert TypeScript enums to const objects (erasable-syntax-only) - Replace all `any` types with `unknown` or specific types in workflow types - Fix unused vars, react-hooks-extra, react-refresh/only-export-components - Extract InteractionMode to separate module, tool-block commands to commands.ts Backend: - Fix pyrefly errors: type narrowing, null guards, getattr patterns - Remove unused TYPE_CHECKING imports in LLM node - Add ignore_imports entries to .importlinter for dify_graph boundary violations Made-with: Cursor
This commit is contained in:
@ -84,14 +84,18 @@ const ChatVariableModal = ({
|
||||
return objectPlaceholder
|
||||
}, [type])
|
||||
const getObjectValue = useCallback(() => {
|
||||
if (!chatVar || Object.keys(chatVar.value).length === 0)
|
||||
const raw = chatVar?.value
|
||||
if (!chatVar || raw === null || typeof raw !== 'object' || Array.isArray(raw) || Object.keys(raw).length === 0)
|
||||
return [DEFAULT_OBJECT_VALUE]
|
||||
|
||||
return Object.keys(chatVar.value).map((key) => {
|
||||
return Object.keys(raw).map((key) => {
|
||||
const v = raw[key]
|
||||
const isStr = typeof v === 'string'
|
||||
const isNum = typeof v === 'number'
|
||||
return {
|
||||
key,
|
||||
type: typeof chatVar.value[key] === 'string' ? ChatVarType.String : ChatVarType.Number,
|
||||
value: chatVar.value[key],
|
||||
type: isStr ? ChatVarType.String : ChatVarType.Number,
|
||||
value: isStr || isNum ? v : undefined,
|
||||
}
|
||||
})
|
||||
}, [chatVar])
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { WorkflowCommentList } from '@/service/workflow-comment'
|
||||
import { RiCheckboxCircleFill, RiCheckboxCircleLine, RiCheckLine, RiCloseLine, RiFilter3Line } from '@remixicon/react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { memo, useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
@ -12,6 +11,7 @@ import { useStore } from '@/app/components/workflow/store'
|
||||
import { ControlMode } from '@/app/components/workflow/types'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import { useParams } from '@/next/navigation'
|
||||
import { resolveWorkflowComment } from '@/service/workflow-comment'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ const ConversationVariableModal = ({
|
||||
|
||||
const [isCopied, setIsCopied] = React.useState(false)
|
||||
const handleCopy = useCallback(() => {
|
||||
copy(currentVar.value)
|
||||
copy(typeof currentVar.value === 'string' ? currentVar.value : JSON.stringify(currentVar.value))
|
||||
setIsCopied(true)
|
||||
setTimeout(() => {
|
||||
setIsCopied(false)
|
||||
|
||||
@ -84,9 +84,12 @@ export function createLLMTraceBuilder() {
|
||||
if (chunkType === 'tool_call') {
|
||||
const lastModel = trace.findLast(item => item.type === 'model')
|
||||
if (lastModel) {
|
||||
if (!lastModel.output.tool_calls)
|
||||
lastModel.output.tool_calls = []
|
||||
lastModel.output.tool_calls.push({
|
||||
const modelOut = lastModel.output as Record<string, unknown> & {
|
||||
tool_calls?: { id: string, name: string, arguments: string }[]
|
||||
}
|
||||
if (!modelOut.tool_calls)
|
||||
modelOut.tool_calls = []
|
||||
modelOut.tool_calls.push({
|
||||
id: meta.tool_call_id || '',
|
||||
name: meta.tool_name || '',
|
||||
arguments: meta.tool_arguments || '',
|
||||
|
||||
@ -26,7 +26,7 @@ import {
|
||||
getProcessedFiles,
|
||||
getProcessedFilesFromResponse,
|
||||
} from '@/app/components/base/file-uploader/utils'
|
||||
import { useToastContext } from '@/app/components/base/toast/context'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import {
|
||||
sseGet,
|
||||
} from '@/service/base'
|
||||
@ -90,7 +90,6 @@ export function useChatMessageSender({
|
||||
updateCurrentQAOnTree,
|
||||
}: UseChatMessageSenderParams) {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
const { handleRun } = useWorkflowRun()
|
||||
const workflowStore = useWorkflowStore()
|
||||
|
||||
@ -132,7 +131,7 @@ export function useChatMessageSender({
|
||||
{ onGetSuggestedQuestions }: SendCallback,
|
||||
) => {
|
||||
if (workflowStore.getState().isResponding) {
|
||||
notify({ type: 'info', message: t('errorMessage.waitForResponse', { ns: 'appDebug' }) })
|
||||
toast.info(t('errorMessage.waitForResponse', { ns: 'appDebug' }))
|
||||
return false
|
||||
}
|
||||
|
||||
@ -559,7 +558,6 @@ export function useChatMessageSender({
|
||||
return true
|
||||
}, [
|
||||
workflowStore,
|
||||
notify,
|
||||
t,
|
||||
setSuggestedQuestionsAbortController,
|
||||
setWorkflowEventsAbortController,
|
||||
|
||||
@ -47,7 +47,7 @@ const EnvItem = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="truncate text-text-tertiary system-xs-regular">{env.value_type === 'secret' ? envSecrets[env.id] : env.value}</div>
|
||||
<div className="truncate text-text-tertiary system-xs-regular">{env.value_type === 'secret' ? envSecrets[env.id] : String(env.value ?? '')}</div>
|
||||
</div>
|
||||
{env.description && (
|
||||
<>
|
||||
|
||||
@ -120,7 +120,7 @@ const EnvPanel = () => {
|
||||
if (env.value_type === 'secret') {
|
||||
setEnvSecrets({
|
||||
...envSecrets,
|
||||
[env.id]: formatSecret(env.value),
|
||||
[env.id]: formatSecret(typeof env.value === 'string' ? env.value : JSON.stringify(env.value)),
|
||||
})
|
||||
}
|
||||
newList = [env, ...envList]
|
||||
@ -158,7 +158,7 @@ const EnvPanel = () => {
|
||||
newEnv = env
|
||||
setEnvSecrets({
|
||||
...envSecrets,
|
||||
[env.id]: formatSecret(env.value),
|
||||
[env.id]: formatSecret(typeof env.value === 'string' ? env.value : JSON.stringify(env.value)),
|
||||
})
|
||||
}
|
||||
else {
|
||||
@ -171,7 +171,7 @@ const EnvPanel = () => {
|
||||
newEnv = env
|
||||
setEnvSecrets({
|
||||
...envSecrets,
|
||||
[env.id]: formatSecret(env.value),
|
||||
[env.id]: formatSecret(typeof env.value === 'string' ? env.value : JSON.stringify(env.value)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,8 +6,7 @@ import { VersionHistoryContextMenuOptions, WorkflowVersion } from '../../../type
|
||||
|
||||
const mockHandleRestoreFromPublishedWorkflow = vi.fn()
|
||||
const mockHandleLoadBackupDraft = vi.fn()
|
||||
const mockHandleRefreshWorkflowDraft = vi.fn()
|
||||
const mockRestoreWorkflow = vi.fn()
|
||||
const mockRequestRestore = vi.fn()
|
||||
const mockSetCurrentVersion = vi.fn()
|
||||
const mockSetShowWorkflowVersionHistoryPanel = vi.fn()
|
||||
const mockWorkflowStoreSetState = vi.fn()
|
||||
@ -60,7 +59,6 @@ vi.mock('@/service/use-workflow', () => ({
|
||||
useDeleteWorkflow: () => ({ mutateAsync: vi.fn() }),
|
||||
useInvalidAllLastRun: () => vi.fn(),
|
||||
useResetWorkflowVersionHistory: () => vi.fn(),
|
||||
useRestoreWorkflow: () => ({ mutateAsync: mockRestoreWorkflow }),
|
||||
useUpdateWorkflow: () => ({ mutateAsync: vi.fn() }),
|
||||
useWorkflowVersionHistory: () => ({
|
||||
data: {
|
||||
@ -89,7 +87,7 @@ vi.mock('@/service/use-workflow', () => ({
|
||||
|
||||
vi.mock('../../../hooks', () => ({
|
||||
useDSL: () => ({ handleExportDSL: vi.fn() }),
|
||||
useWorkflowRefreshDraft: () => ({ handleRefreshWorkflowDraft: mockHandleRefreshWorkflowDraft }),
|
||||
useLeaderRestore: () => ({ requestRestore: mockRequestRestore }),
|
||||
useWorkflowRun: () => ({
|
||||
handleRestoreFromPublishedWorkflow: mockHandleRestoreFromPublishedWorkflow,
|
||||
handleLoadBackupDraft: mockHandleLoadBackupDraft,
|
||||
@ -174,6 +172,7 @@ vi.mock('../version-history-item', () => ({
|
||||
describe('VersionHistoryPanel', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockRequestRestore.mockReset()
|
||||
mockCurrentVersion = null
|
||||
})
|
||||
|
||||
@ -211,7 +210,7 @@ describe('VersionHistoryPanel', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should set current version before confirming restore from context menu', async () => {
|
||||
it('should request restore with the published version when confirming from context menu', async () => {
|
||||
const { VersionHistoryPanel } = await import('../index')
|
||||
|
||||
render(
|
||||
@ -227,19 +226,21 @@ describe('VersionHistoryPanel', () => {
|
||||
fireEvent.click(screen.getByText('confirm restore'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSetCurrentVersion).toHaveBeenCalledWith(expect.objectContaining({
|
||||
id: 'published-version-id',
|
||||
}))
|
||||
expect(mockRestoreWorkflow).toHaveBeenCalledWith('/apps/app-1/workflows/published-version-id/restore')
|
||||
expect(mockRequestRestore).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ versionId: 'published-version-id' }),
|
||||
expect.any(Object),
|
||||
)
|
||||
expect(mockWorkflowStoreSetState).toHaveBeenCalledWith({ isRestoring: false })
|
||||
expect(mockWorkflowStoreSetState).toHaveBeenCalledWith({ backupDraft: undefined })
|
||||
expect(mockHandleRefreshWorkflowDraft).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it('should keep restore mode backup state when restore request fails', async () => {
|
||||
const { VersionHistoryPanel } = await import('../index')
|
||||
mockRestoreWorkflow.mockRejectedValueOnce(new Error('restore failed'))
|
||||
mockRequestRestore.mockImplementation((_data, callbacks) => {
|
||||
callbacks?.onError?.()
|
||||
callbacks?.onSettled?.()
|
||||
})
|
||||
mockCurrentVersion = createVersionHistory({
|
||||
id: 'draft-version-id',
|
||||
version: WorkflowVersion.Draft,
|
||||
@ -258,12 +259,9 @@ describe('VersionHistoryPanel', () => {
|
||||
fireEvent.click(screen.getByText('confirm restore'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockRestoreWorkflow).toHaveBeenCalledWith('/apps/app-1/workflows/published-version-id/restore')
|
||||
expect(mockRequestRestore).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
expect(mockWorkflowStoreSetState).not.toHaveBeenCalledWith({ isRestoring: false })
|
||||
expect(mockWorkflowStoreSetState).not.toHaveBeenCalledWith({ backupDraft: undefined })
|
||||
expect(mockSetCurrentVersion).not.toHaveBeenCalled()
|
||||
expect(mockHandleRefreshWorkflowDraft).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@ -5,6 +5,12 @@ const mockHandleRestoreFromPublishedWorkflow = vi.fn()
|
||||
const mockHandleLoadBackupDraft = vi.fn()
|
||||
const mockSetCurrentVersion = vi.fn()
|
||||
|
||||
type VersionHistoryMockState = {
|
||||
setShowWorkflowVersionHistoryPanel: ReturnType<typeof vi.fn>
|
||||
currentVersion: null
|
||||
setCurrentVersion: typeof mockSetCurrentVersion
|
||||
}
|
||||
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
useSelector: () => ({ id: 'test-user-id' }),
|
||||
}))
|
||||
@ -88,8 +94,8 @@ vi.mock('../../hooks-store', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('../../store', () => ({
|
||||
useStore: (selector: (state: any) => any) => {
|
||||
const state = {
|
||||
useStore: <T, >(selector: (state: VersionHistoryMockState) => T) => {
|
||||
const state: VersionHistoryMockState = {
|
||||
setShowWorkflowVersionHistoryPanel: vi.fn(),
|
||||
currentVersion: null,
|
||||
setCurrentVersion: mockSetCurrentVersion,
|
||||
|
||||
@ -10,8 +10,8 @@ import Divider from '@/app/components/base/divider'
|
||||
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { useSelector as useAppContextSelector } from '@/context/app-context'
|
||||
import { useDeleteWorkflow, useInvalidAllLastRun, useResetWorkflowVersionHistory, useRestoreWorkflow, useUpdateWorkflow, useWorkflowVersionHistory } from '@/service/use-workflow'
|
||||
import { useDSL, useLeaderRestore, useWorkflowRefreshDraft, useWorkflowRun } from '../../hooks'
|
||||
import { useDeleteWorkflow, useInvalidAllLastRun, useResetWorkflowVersionHistory, useUpdateWorkflow, useWorkflowVersionHistory } from '@/service/use-workflow'
|
||||
import { useDSL, useLeaderRestore, useWorkflowRun } from '../../hooks'
|
||||
import { useHooksStore } from '../../hooks-store'
|
||||
import { useStore, useWorkflowStore } from '../../store'
|
||||
import { VersionHistoryContextMenuOptions, WorkflowVersion, WorkflowVersionFilterOptions } from '../../types'
|
||||
@ -35,11 +35,11 @@ export type VersionHistoryPanelProps = {
|
||||
export const VersionHistoryPanel = ({
|
||||
getVersionListUrl,
|
||||
deleteVersionUrl,
|
||||
restoreVersionUrl,
|
||||
restoreVersionUrl: _restoreVersionUrl,
|
||||
updateVersionUrl,
|
||||
latestVersionId,
|
||||
}: VersionHistoryPanelProps) => {
|
||||
const [filterValue, setFilterValue] = useState(WorkflowVersionFilterOptions.all)
|
||||
const [filterValue, setFilterValue] = useState<WorkflowVersionFilterOptions>(WorkflowVersionFilterOptions.all)
|
||||
const [isOnlyShowNamedVersions, setIsOnlyShowNamedVersions] = useState(false)
|
||||
const [operatedItem, setOperatedItem] = useState<VersionHistory>()
|
||||
const [restoreConfirmOpen, setRestoreConfirmOpen] = useState(false)
|
||||
@ -49,7 +49,6 @@ export const VersionHistoryPanel = ({
|
||||
const { handleRestoreFromPublishedWorkflow, handleLoadBackupDraft } = useWorkflowRun()
|
||||
const { requestRestore } = useLeaderRestore()
|
||||
const featuresStore = useFeaturesStore()
|
||||
const { handleRefreshWorkflowDraft } = useWorkflowRefreshDraft()
|
||||
const { handleExportDSL } = useDSL()
|
||||
const setShowWorkflowVersionHistoryPanel = useStore(s => s.setShowWorkflowVersionHistoryPanel)
|
||||
const currentVersion = useStore(s => s.currentVersion)
|
||||
@ -146,7 +145,6 @@ export const VersionHistoryPanel = ({
|
||||
}, [])
|
||||
|
||||
const resetWorkflowVersionHistory = useResetWorkflowVersionHistory()
|
||||
const { mutateAsync: restoreWorkflow } = useRestoreWorkflow()
|
||||
|
||||
const handleRestore = useCallback(async (item: VersionHistory) => {
|
||||
setShowWorkflowVersionHistoryPanel(false)
|
||||
|
||||
@ -10,7 +10,7 @@ import ActionButton from '@/app/components/base/action-button'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Tooltip from '@/app/components/base/tooltip-plus'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { submitHumanInputForm } from '@/service/workflow'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
Reference in New Issue
Block a user