mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-09 22:25:33 -05:00
Merge pull request #3172 from simstudioai/fix/notifs
fix(notifications): throw notification on runtime errors, move predeploy checks to update in deploy modal
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { getInputFormatExample as getInputFormatExampleUtil } from '@/lib/workflows/operations/deployment-utils'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import { runPreDeployChecks } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/hooks/use-predeploy-checks'
|
||||
import { CreateApiKeyModal } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/api-keys/components'
|
||||
import { startsWithUuid } from '@/executor/constants'
|
||||
import { useA2AAgentByWorkflow } from '@/hooks/queries/a2a/agents'
|
||||
@@ -38,6 +39,7 @@ import { useWorkspaceSettings } from '@/hooks/queries/workspace'
|
||||
import { usePermissionConfig } from '@/hooks/use-permission-config'
|
||||
import { useSettingsModalStore } from '@/stores/modals/settings/store'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { mergeSubblockState } from '@/stores/workflows/utils'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
import type { WorkflowState } from '@/stores/workflows/workflow/types'
|
||||
import { A2aDeploy } from './components/a2a/a2a'
|
||||
@@ -335,6 +337,20 @@ export function DeployModal({
|
||||
setDeployError(null)
|
||||
setDeployWarnings([])
|
||||
|
||||
const { blocks, edges, loops, parallels } = useWorkflowStore.getState()
|
||||
const liveBlocks = mergeSubblockState(blocks, workflowId)
|
||||
const checkResult = runPreDeployChecks({
|
||||
blocks: liveBlocks,
|
||||
edges,
|
||||
loops,
|
||||
parallels,
|
||||
workflowId,
|
||||
})
|
||||
if (!checkResult.passed) {
|
||||
setDeployError(checkResult.error || 'Pre-deploy validation failed')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await deployMutation.mutateAsync({ workflowId, deployChatEnabled: false })
|
||||
if (result.warnings && result.warnings.length > 0) {
|
||||
|
||||
@@ -2,9 +2,6 @@ import { useCallback, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useNotificationStore } from '@/stores/notifications'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { mergeSubblockState } from '@/stores/workflows/utils'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
import { runPreDeployChecks } from './use-predeploy-checks'
|
||||
|
||||
const logger = createLogger('useDeployment')
|
||||
|
||||
@@ -25,36 +22,16 @@ export function useDeployment({
|
||||
const [isDeploying, setIsDeploying] = useState(false)
|
||||
const setDeploymentStatus = useWorkflowRegistry((state) => state.setDeploymentStatus)
|
||||
const addNotification = useNotificationStore((state) => state.addNotification)
|
||||
const blocks = useWorkflowStore((state) => state.blocks)
|
||||
const edges = useWorkflowStore((state) => state.edges)
|
||||
const loops = useWorkflowStore((state) => state.loops)
|
||||
const parallels = useWorkflowStore((state) => state.parallels)
|
||||
|
||||
/**
|
||||
* Handle deploy button click
|
||||
* First deploy: calls API to deploy, then opens modal on success
|
||||
* Redeploy: validates client-side, then opens modal if valid
|
||||
* Already deployed: opens modal directly (validation happens on Update in modal)
|
||||
*/
|
||||
const handleDeployClick = useCallback(async () => {
|
||||
if (!workflowId) return { success: false, shouldOpenModal: false }
|
||||
|
||||
if (isDeployed) {
|
||||
const liveBlocks = mergeSubblockState(blocks, workflowId)
|
||||
const checkResult = runPreDeployChecks({
|
||||
blocks: liveBlocks,
|
||||
edges,
|
||||
loops,
|
||||
parallels,
|
||||
workflowId,
|
||||
})
|
||||
if (!checkResult.passed) {
|
||||
addNotification({
|
||||
level: 'error',
|
||||
message: checkResult.error || 'Pre-deploy validation failed',
|
||||
workflowId,
|
||||
})
|
||||
return { success: false, shouldOpenModal: false }
|
||||
}
|
||||
return { success: true, shouldOpenModal: true }
|
||||
}
|
||||
|
||||
@@ -101,17 +78,7 @@ export function useDeployment({
|
||||
} finally {
|
||||
setIsDeploying(false)
|
||||
}
|
||||
}, [
|
||||
workflowId,
|
||||
isDeployed,
|
||||
blocks,
|
||||
edges,
|
||||
loops,
|
||||
parallels,
|
||||
refetchDeployedState,
|
||||
setDeploymentStatus,
|
||||
addNotification,
|
||||
])
|
||||
}, [workflowId, isDeployed, refetchDeployedState, setDeploymentStatus, addNotification])
|
||||
|
||||
return {
|
||||
isDeploying,
|
||||
|
||||
@@ -62,6 +62,45 @@ const shouldSkipEntry = (output: any): boolean => {
|
||||
return false
|
||||
}
|
||||
|
||||
interface NotifyBlockErrorParams {
|
||||
error: unknown
|
||||
blockName: string
|
||||
workflowId?: string
|
||||
logContext: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an error notification for a block failure if error notifications are enabled.
|
||||
*/
|
||||
const notifyBlockError = ({ error, blockName, workflowId, logContext }: NotifyBlockErrorParams) => {
|
||||
const settings = getQueryClient().getQueryData<GeneralSettings>(generalSettingsKeys.settings())
|
||||
const isErrorNotificationsEnabled = settings?.errorNotificationsEnabled ?? true
|
||||
|
||||
if (!isErrorNotificationsEnabled) return
|
||||
|
||||
try {
|
||||
const errorMessage = String(error)
|
||||
const displayName = blockName || 'Unknown Block'
|
||||
const displayMessage = `${displayName}: ${errorMessage}`
|
||||
const copilotMessage = `${errorMessage}\n\nError in ${displayName}.\n\nPlease fix this.`
|
||||
|
||||
useNotificationStore.getState().addNotification({
|
||||
level: 'error',
|
||||
message: displayMessage,
|
||||
workflowId,
|
||||
action: {
|
||||
type: 'copilot',
|
||||
message: copilotMessage,
|
||||
},
|
||||
})
|
||||
} catch (notificationError) {
|
||||
logger.error('Failed to create block error notification', {
|
||||
...logContext,
|
||||
error: notificationError,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const useTerminalConsoleStore = create<ConsoleStore>()(
|
||||
devtools(
|
||||
persist(
|
||||
@@ -154,35 +193,12 @@ export const useTerminalConsoleStore = create<ConsoleStore>()(
|
||||
const newEntry = get().entries[0]
|
||||
|
||||
if (newEntry?.error) {
|
||||
const settings = getQueryClient().getQueryData<GeneralSettings>(
|
||||
generalSettingsKeys.settings()
|
||||
)
|
||||
const isErrorNotificationsEnabled = settings?.errorNotificationsEnabled ?? true
|
||||
|
||||
if (isErrorNotificationsEnabled) {
|
||||
try {
|
||||
const errorMessage = String(newEntry.error)
|
||||
const blockName = newEntry.blockName || 'Unknown Block'
|
||||
const displayMessage = `${blockName}: ${errorMessage}`
|
||||
|
||||
const copilotMessage = `${errorMessage}\n\nError in ${blockName}.\n\nPlease fix this.`
|
||||
|
||||
useNotificationStore.getState().addNotification({
|
||||
level: 'error',
|
||||
message: displayMessage,
|
||||
workflowId: entry.workflowId,
|
||||
action: {
|
||||
type: 'copilot',
|
||||
message: copilotMessage,
|
||||
},
|
||||
})
|
||||
} catch (notificationError) {
|
||||
logger.error('Failed to create block error notification', {
|
||||
entryId: newEntry.id,
|
||||
error: notificationError,
|
||||
})
|
||||
}
|
||||
}
|
||||
notifyBlockError({
|
||||
error: newEntry.error,
|
||||
blockName: newEntry.blockName || 'Unknown Block',
|
||||
workflowId: entry.workflowId,
|
||||
logContext: { entryId: newEntry.id },
|
||||
})
|
||||
}
|
||||
|
||||
return newEntry
|
||||
@@ -376,6 +392,18 @@ export const useTerminalConsoleStore = create<ConsoleStore>()(
|
||||
|
||||
return { entries: updatedEntries }
|
||||
})
|
||||
|
||||
if (typeof update === 'object' && update.error) {
|
||||
const matchingEntry = get().entries.find(
|
||||
(e) => e.blockId === blockId && e.executionId === executionId
|
||||
)
|
||||
notifyBlockError({
|
||||
error: update.error,
|
||||
blockName: matchingEntry?.blockName || 'Unknown Block',
|
||||
workflowId: matchingEntry?.workflowId,
|
||||
logContext: { blockId },
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
cancelRunningEntries: (workflowId: string) => {
|
||||
|
||||
Reference in New Issue
Block a user