refactor: consolidate error logic and split errorGroups by scope

- Add allErrorExecutionIds computed to executionStore to centralize
  error ID collection from lastNodeErrors and lastExecutionError
- Add hasInternalErrorForNode helper to executionStore to
  encapsulate prefix-based container error detection
- Replace duplicated error ID collection and prefix checks in
  RightSidePanel and SectionWidgets with store computed/helper
- Split errorGroups into allErrorGroups (unfiltered) and
  tabErrorGroups (selection-filtered) in useErrorGroups
- Add filterBySelection param (default: false) to
  addNodeErrorToGroup, processNodeErrors, processExecutionError
- Add groupedErrorMessages computed derived from allErrorGroups
  for deduped message list used by ErrorOverlay
- Migrate ErrorOverlay business logic to useErrorGroups composable,
  removing inline groupedErrors computed and redundant totalErrorCount wrapper
This commit is contained in:
jaeone94
2026-02-18 18:15:14 +09:00
parent aa1c25f98e
commit 4fbf89ae4c
5 changed files with 87 additions and 83 deletions

View File

@@ -30,14 +30,14 @@
<div class="px-4 pb-3">
<ul class="m-0 flex list-none flex-col gap-1.5 p-0">
<li
v-for="(entry, idx) in groupedErrors"
v-for="(message, idx) in groupedErrorMessages"
:key="idx"
class="flex items-baseline gap-2 text-sm leading-snug text-muted-foreground"
>
<span
class="mt-1.5 size-1 shrink-0 rounded-full bg-muted-foreground"
/>
<span>{{ entry.message }}</span>
<span>{{ message }}</span>
</li>
</ul>
</div>
@@ -57,51 +57,24 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { storeToRefs } from 'pinia'
import Button from '@/components/ui/button/Button.vue'
import type { NodeError } from '@/schemas/apiSchema'
import { useExecutionStore } from '@/stores/executionStore'
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import { useErrorGroups } from '@/components/rightSidePanel/errors/useErrorGroups'
import { cn } from '@/utils/tailwindUtil'
interface ErrorEntry {
message: string
}
const { t } = useI18n()
const executionStore = useExecutionStore()
const rightSidePanelStore = useRightSidePanelStore()
const canvasStore = useCanvasStore()
const groupedErrors = computed<ErrorEntry[]>(() => {
const messages = new Set<string>()
if (executionStore.lastPromptError) {
messages.add(executionStore.lastPromptError.message)
}
if (executionStore.lastNodeErrors) {
for (const nodeError of Object.values(
executionStore.lastNodeErrors
) as NodeError[]) {
for (const err of nodeError.errors) {
messages.add(err.message)
}
}
}
if (executionStore.lastExecutionError) {
const e = executionStore.lastExecutionError
messages.add(`${e.exception_type}: ${e.exception_message}`)
}
return Array.from(messages).map((message) => ({ message }))
})
const totalErrorCount = computed(() => executionStore.totalErrorCount)
const { totalErrorCount, isErrorOverlayOpen } = storeToRefs(executionStore)
const { groupedErrorMessages } = useErrorGroups(ref(''), t)
const errorCountLabel = computed(() =>
t(
@@ -112,7 +85,7 @@ const errorCountLabel = computed(() =>
)
const isVisible = computed(
() => executionStore.isErrorOverlayOpen && totalErrorCount.value > 0
() => isErrorOverlayOpen.value && totalErrorCount.value > 0
)
function dismiss() {

View File

@@ -41,7 +41,7 @@ const rightSidePanelStore = useRightSidePanelStore()
const settingStore = useSettingStore()
const { t } = useI18n()
const { hasAnyError } = storeToRefs(executionStore)
const { hasAnyError, allErrorExecutionIds } = storeToRefs(executionStore)
const { findParentGroup } = useGraphHierarchy()
@@ -96,17 +96,6 @@ type RightSidePanelTabList = Array<{
icon?: string
}>
const allErrorIds = computed<string[]>(() => {
const ids: string[] = []
if (executionStore.lastNodeErrors) {
ids.push(...Object.keys(executionStore.lastNodeErrors))
}
if (executionStore.lastExecutionError) {
ids.push(String(executionStore.lastExecutionError.node_id))
}
return ids
})
const hasDirectNodeError = computed(() =>
selectedNodes.value.some((node) =>
executionStore.activeGraphErrorNodeIds.has(String(node.id))
@@ -114,11 +103,10 @@ const hasDirectNodeError = computed(() =>
)
const hasContainerInternalError = computed(() => {
if (allErrorIds.value.length === 0) return false
if (allErrorExecutionIds.value.length === 0) return false
return selectedNodes.value.some((node) => {
if (!(node instanceof SubgraphNode || isGroupNode(node))) return false
const prefix = `${node.id}:`
return allErrorIds.value.some((execId) => execId.startsWith(prefix))
return executionStore.hasInternalErrorForNode(node.id)
})
})

View File

@@ -223,13 +223,7 @@ export function useErrorGroups(
const errorNodeCache = computed(() => {
const map = new Map<string, LGraphNode>()
const allExecutionIds = [
...Object.keys(executionStore.lastNodeErrors ?? {}),
...(executionStore.lastExecutionError
? [String(executionStore.lastExecutionError.node_id)]
: [])
]
for (const execId of allExecutionIds) {
for (const execId of executionStore.allErrorExecutionIds) {
const node = getNodeByExecutionId(app.rootGraph, execId)
if (node) map.set(execId, node)
}
@@ -255,9 +249,10 @@ export function useErrorGroups(
nodeId: string,
classType: string,
idPrefix: string,
errors: ErrorItem[]
errors: ErrorItem[],
filterBySelection = false
) {
if (!isErrorInSelection(nodeId)) return
if (filterBySelection && !isErrorInSelection(nodeId)) return
const groupKey = isSingleNodeSelected.value ? SINGLE_GROUP_KEY : classType
const cards = getOrCreateGroup(groupsMap, groupKey, 1)
if (!cards.has(nodeId)) {
@@ -289,7 +284,10 @@ export function useErrorGroups(
})
}
function processNodeErrors(groupsMap: Map<string, GroupEntry>) {
function processNodeErrors(
groupsMap: Map<string, GroupEntry>,
filterBySelection = false
) {
if (!executionStore.lastNodeErrors) return
for (const [nodeId, nodeError] of Object.entries(
@@ -303,31 +301,52 @@ export function useErrorGroups(
nodeError.errors.map((e) => ({
message: e.message,
details: e.details ?? undefined
}))
})),
filterBySelection
)
}
}
function processExecutionError(groupsMap: Map<string, GroupEntry>) {
function processExecutionError(
groupsMap: Map<string, GroupEntry>,
filterBySelection = false
) {
if (!executionStore.lastExecutionError) return
const e = executionStore.lastExecutionError
addNodeErrorToGroup(groupsMap, String(e.node_id), e.node_type, 'exec', [
{
message: `${e.exception_type}: ${e.exception_message}`,
details: e.traceback.join('\n'),
isRuntimeError: true
}
])
addNodeErrorToGroup(
groupsMap,
String(e.node_id),
e.node_type,
'exec',
[
{
message: `${e.exception_type}: ${e.exception_message}`,
details: e.traceback.join('\n'),
isRuntimeError: true
}
],
filterBySelection
)
}
const errorGroups = computed<ErrorGroup[]>(() => {
const allErrorGroups = computed<ErrorGroup[]>(() => {
const groupsMap = new Map<string, GroupEntry>()
processPromptError(groupsMap)
processNodeErrors(groupsMap)
processExecutionError(groupsMap)
return toSortedGroups(groupsMap)
})
const tabErrorGroups = computed<ErrorGroup[]>(() => {
const groupsMap = new Map<string, GroupEntry>()
processPromptError(groupsMap)
processNodeErrors(groupsMap, true)
processExecutionError(groupsMap, true)
return isSingleNodeSelected.value
? toSortedGroups(regroupByErrorMessage(groupsMap))
: toSortedGroups(groupsMap)
@@ -335,7 +354,19 @@ export function useErrorGroups(
const filteredGroups = computed<ErrorGroup[]>(() => {
const query = searchQuery.value.trim()
return searchErrorGroups(errorGroups.value, query)
return searchErrorGroups(tabErrorGroups.value, query)
})
const groupedErrorMessages = computed<string[]>(() => {
const messages = new Set<string>()
for (const group of allErrorGroups.value) {
for (const card of group.cards) {
for (const err of card.errors) {
messages.add(err.message)
}
}
}
return Array.from(messages)
})
/**
@@ -363,10 +394,12 @@ export function useErrorGroups(
})
return {
errorGroups,
allErrorGroups,
tabErrorGroups,
filteredGroups,
collapseState,
isSingleNodeSelected,
errorNodeCache
errorNodeCache,
groupedErrorMessages
}
}

View File

@@ -119,16 +119,7 @@ const hasContainerInternalError = computed(() => {
targetNode.value instanceof SubgraphNode || isGroupNode(targetNode.value)
if (!isContainer) return false
const errorIds: string[] = []
if (executionStore.lastNodeErrors) {
errorIds.push(...Object.keys(executionStore.lastNodeErrors))
}
if (executionStore.lastExecutionError) {
errorIds.push(String(executionStore.lastExecutionError.node_id))
}
const prefix = `${targetNode.value.id}:`
return errorIds.some((execId) => execId.startsWith(prefix))
return executionStore.hasInternalErrorForNode(targetNode.value.id)
})
const nodeHasError = computed(() => {

View File

@@ -740,6 +740,17 @@ export const useExecutionStore = defineStore('execution', () => {
() => hasExecutionError.value || hasPromptError.value || hasNodeError.value
)
const allErrorExecutionIds = computed<string[]>(() => {
const ids: string[] = []
if (lastNodeErrors.value) {
ids.push(...Object.keys(lastNodeErrors.value))
}
if (lastExecutionError.value) {
ids.push(String(lastExecutionError.value.node_id))
}
return ids
})
/** Total count of all individual errors */
const totalErrorCount = computed(() => {
let count = 0
@@ -785,6 +796,12 @@ export const useExecutionStore = defineStore('execution', () => {
return ids
})
function hasInternalErrorForNode(nodeId: string | number): boolean {
const prefix = `${nodeId}:`
return allErrorExecutionIds.value.some((id) => id.startsWith(prefix))
}
const isErrorOverlayOpen = ref(false)
function showErrorOverlay() {
@@ -804,6 +821,7 @@ export const useExecutionStore = defineStore('execution', () => {
lastExecutionError,
lastPromptError,
hasAnyError,
allErrorExecutionIds,
totalErrorCount,
lastExecutionErrorNodeId,
executingNodeId,
@@ -837,6 +855,7 @@ export const useExecutionStore = defineStore('execution', () => {
// Node error lookup helpers
getNodeErrors,
slotHasError,
hasInternalErrorForNode,
activeGraphErrorNodeIds,
isErrorOverlayOpen,
showErrorOverlay,