mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-02 03:30:04 +00:00
Execution Error Dialog Revamp (One click issue searching and filing) (#595)
* Add basic error dialog * 2 level error report * Add find issue button * nit * Add file issue button * Single dialog * nit * Fix long text wrapping * Merge component * Test execution error dialog
This commit is contained in:
5
.github/workflows/test-ui.yaml
vendored
5
.github/workflows/test-ui.yaml
vendored
@@ -27,6 +27,11 @@ jobs:
|
||||
with:
|
||||
repository: "Comfy-Org/ComfyUI_frontend"
|
||||
path: "ComfyUI_frontend"
|
||||
- name: Checkout ComfyUI_devtools
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: "Comfy-Org/ComfyUI_devtools"
|
||||
path: "ComfyUI/custom_nodes/ComfyUI_devtools"
|
||||
- name: Get commit message
|
||||
id: commit-message
|
||||
run: echo "::set-output name=message::$(git log -1 --pretty=%B)"
|
||||
|
||||
@@ -124,6 +124,7 @@ export class ComfyPage {
|
||||
|
||||
// Buttons
|
||||
public readonly resetViewButton: Locator
|
||||
public readonly queueButton: Locator
|
||||
|
||||
// Inputs
|
||||
public readonly workflowUploadInput: Locator
|
||||
@@ -137,6 +138,7 @@ export class ComfyPage {
|
||||
this.canvas = page.locator('#graph-canvas')
|
||||
this.widgetTextBox = page.getByPlaceholder('text').nth(1)
|
||||
this.resetViewButton = page.getByRole('button', { name: 'Reset View' })
|
||||
this.queueButton = page.getByRole('button', { name: 'Queue Prompt' })
|
||||
this.workflowUploadInput = page.locator('#comfy-file-input')
|
||||
this.searchBox = new ComfyNodeSearchBox(page)
|
||||
this.menu = new ComfyMenu(page)
|
||||
|
||||
82
browser_tests/assets/execution_error.json
Normal file
82
browser_tests/assets/execution_error.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"last_node_id": 17,
|
||||
"last_link_id": 15,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 14,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
858,
|
||||
-41
|
||||
],
|
||||
"size": {
|
||||
"0": 213.8594970703125,
|
||||
"1": 50.65289306640625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 15
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"type": "DevToolsErrorRaiseNode",
|
||||
"pos": [
|
||||
477,
|
||||
-40
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 26
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
15
|
||||
],
|
||||
"slot_index": 0,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "DevToolsErrorRaiseNode"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
15,
|
||||
17,
|
||||
0,
|
||||
14,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.2100000000000006,
|
||||
"offset": [
|
||||
-266.1038310281165,
|
||||
337.94335447664554
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -12,3 +12,16 @@ test.describe('Load workflow warning', () => {
|
||||
await expect(missingNodesWarning).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Execution error', () => {
|
||||
test('Should display an error message when an execution error occurs', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('execution_error')
|
||||
await comfyPage.queueButton.click()
|
||||
|
||||
// Wait for the element with the .comfy-execution-error selector to be visible
|
||||
const executionError = comfyPage.page.locator('.comfy-error-report')
|
||||
await expect(executionError).toBeVisible()
|
||||
})
|
||||
})
|
||||
46
package-lock.json
generated
46
package-lock.json
generated
@@ -13,6 +13,7 @@
|
||||
"@primevue/themes": "^4.0.0-rc.2",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"axios": "^1.7.4",
|
||||
"class-transformer": "^0.5.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"fuse.js": "^7.0.0",
|
||||
@@ -4485,8 +4486,7 @@
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.19",
|
||||
@@ -4525,6 +4525,17 @@
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
|
||||
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-jest": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
|
||||
@@ -5155,7 +5166,6 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
@@ -5451,7 +5461,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -6271,6 +6280,26 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
|
||||
@@ -6303,7 +6332,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
@@ -9285,7 +9313,6 @@
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -9294,7 +9321,6 @@
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
@@ -10067,6 +10093,12 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"@primevue/themes": "^4.0.0-rc.2",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"axios": "^1.7.4",
|
||||
"class-transformer": "^0.5.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"fuse.js": "^7.0.0",
|
||||
|
||||
@@ -44,6 +44,7 @@ defineEmits(['action'])
|
||||
.no-results-placeholder :deep(.p-card) {
|
||||
background-color: var(--surface-ground);
|
||||
text-align: center;
|
||||
box-shadow: unset;
|
||||
}
|
||||
|
||||
.no-results-placeholder h3 {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
closable
|
||||
closeOnEscape
|
||||
dismissableMask
|
||||
:maximizable="dialogStore.props.maximizable ?? false"
|
||||
:maximizable="maximizable"
|
||||
@hide="dialogStore.closeDialog"
|
||||
@maximize="maximized = true"
|
||||
@unmaximize="maximized = false"
|
||||
@@ -19,20 +19,20 @@
|
||||
<h3 v-else>{{ dialogStore.title || ' ' }}</h3>
|
||||
</template>
|
||||
|
||||
<component
|
||||
:is="dialogStore.component"
|
||||
v-bind="dialogStore.props"
|
||||
:maximized="maximized"
|
||||
/>
|
||||
<component :is="dialogStore.component" v-bind="contentProps" />
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import Dialog from 'primevue/dialog'
|
||||
|
||||
const dialogStore = useDialogStore()
|
||||
|
||||
const maximizable = dialogStore.props.maximizable ?? false
|
||||
const maximized = ref(false)
|
||||
const contentProps = computed(() => ({
|
||||
...dialogStore.props,
|
||||
...(dialogStore.props.maximizable ? { maximized } : {})
|
||||
}))
|
||||
</script>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
182
src/components/dialog/content/ExecutionErrorDialogContent.vue
Normal file
182
src/components/dialog/content/ExecutionErrorDialogContent.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<NoResultsPlaceholder
|
||||
icon="pi pi-exclamation-circle"
|
||||
:title="props.error.node_type"
|
||||
:message="props.error.exception_message"
|
||||
/>
|
||||
<div class="comfy-error-report">
|
||||
<Button
|
||||
v-show="!reportOpen"
|
||||
:label="$t('showReport')"
|
||||
@click="showReport"
|
||||
text
|
||||
/>
|
||||
<template v-if="reportOpen">
|
||||
<Divider />
|
||||
<ScrollPanel style="width: 100%; height: 400px; max-width: 80vw">
|
||||
<pre class="wrapper-pre">{{ reportContent }}</pre>
|
||||
</ScrollPanel>
|
||||
<Divider />
|
||||
</template>
|
||||
|
||||
<div class="action-container">
|
||||
<FindIssueButton
|
||||
:errorMessage="props.error.exception_message"
|
||||
:repoOwner="repoOwner"
|
||||
:repoName="repoName"
|
||||
/>
|
||||
<Button
|
||||
:label="$t('openNewIssue')"
|
||||
icon="pi pi-github"
|
||||
@click="openNewGithubIssue"
|
||||
class="p-button-secondary"
|
||||
/>
|
||||
<Button
|
||||
v-if="reportOpen"
|
||||
:label="$t('copyToClipboard')"
|
||||
icon="pi pi-copy"
|
||||
@click="copyReportToClipboard"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import Button from 'primevue/button'
|
||||
import Divider from 'primevue/divider'
|
||||
import ScrollPanel from 'primevue/scrollpanel'
|
||||
import FindIssueButton from '@/components/dialog/content/error/FindIssueButton.vue'
|
||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
import type { ExecutionErrorWsMessage, SystemStats } from '@/types/apiTypes'
|
||||
import { api } from '@/scripts/api'
|
||||
import { app } from '@/scripts/app'
|
||||
|
||||
const props = defineProps<{
|
||||
error: ExecutionErrorWsMessage
|
||||
}>()
|
||||
|
||||
const repoOwner = 'comfyanonymous'
|
||||
const repoName = 'ComfyUI'
|
||||
const reportContent = ref('')
|
||||
const reportOpen = ref(false)
|
||||
const showReport = () => {
|
||||
reportOpen.value = true
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
const { copy, isSupported } = useClipboard()
|
||||
|
||||
onMounted(async () => {
|
||||
generateReport(await api.getSystemStats())
|
||||
})
|
||||
|
||||
const generateReport = (systemStats: SystemStats) => {
|
||||
const MAX_JSON_LENGTH = 50000
|
||||
const workflowJSONString = JSON.stringify(app.graph.serialize())
|
||||
const workflowText =
|
||||
workflowJSONString.length > MAX_JSON_LENGTH
|
||||
? 'Workflow too large. Please manually upload the workflow from local file system.'
|
||||
: workflowJSONString
|
||||
|
||||
reportContent.value = `
|
||||
# ComfyUI Error Report
|
||||
## Error Details
|
||||
- **Node Type:** ${props.error.node_type}
|
||||
- **Exception Type:** ${props.error.exception_type}
|
||||
- **Exception Message:** ${props.error.exception_message}
|
||||
## Stack Trace
|
||||
\`\`\`
|
||||
${props.error.traceback.join('\n')}
|
||||
\`\`\`
|
||||
## System Information
|
||||
- **OS:** ${systemStats.system.os}
|
||||
- **Python Version:** ${systemStats.system.python_version}
|
||||
- **Embedded Python:** ${systemStats.system.embedded_python}
|
||||
## Devices
|
||||
${systemStats.devices
|
||||
.map(
|
||||
(device) => `
|
||||
- **Name:** ${device.name}
|
||||
- **Type:** ${device.type}
|
||||
- **VRAM Total:** ${device.vram_total}
|
||||
- **VRAM Free:** ${device.vram_free}
|
||||
- **Torch VRAM Total:** ${device.torch_vram_total}
|
||||
- **Torch VRAM Free:** ${device.torch_vram_free}
|
||||
`
|
||||
)
|
||||
.join('\n')}
|
||||
|
||||
## Attached Workflow
|
||||
Please make sure that workflow does not contain any sensitive information such as API keys or passwords.
|
||||
\`\`\`
|
||||
${workflowText}
|
||||
\`\`\`
|
||||
|
||||
## Additional Context
|
||||
(Please add any additional context or steps to reproduce the error here)
|
||||
`
|
||||
}
|
||||
|
||||
const copyReportToClipboard = async () => {
|
||||
if (isSupported) {
|
||||
try {
|
||||
await copy(reportContent.value)
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Success',
|
||||
detail: 'Report copied to clipboard',
|
||||
life: 3000
|
||||
})
|
||||
} catch (err) {
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: 'Failed to copy report',
|
||||
life: 3000
|
||||
})
|
||||
}
|
||||
} else {
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: 'Clipboard API not supported in your browser',
|
||||
life: 3000
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const openNewGithubIssue = () => {
|
||||
const issueTitle = encodeURIComponent(
|
||||
`[Bug]: ${props.error.exception_type} in ${props.error.node_type}`
|
||||
)
|
||||
const issueBody = encodeURIComponent(reportContent.value)
|
||||
const url = `https://github.com/${repoOwner}/${repoName}/issues/new?title=${issueTitle}&body=${issueBody}`
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.comfy-error-report {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.action-container {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.wrapper-pre {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.no-results-placeholder {
|
||||
padding-top: 0;
|
||||
}
|
||||
</style>
|
||||
55
src/components/dialog/content/error/FindIssueButton.vue
Normal file
55
src/components/dialog/content/error/FindIssueButton.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<Button
|
||||
@click="openGitHubIssues"
|
||||
:label="buttonLabel"
|
||||
severity="secondary"
|
||||
icon="pi pi-github"
|
||||
:badge="issueCount.toString()"
|
||||
>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useAsyncState } from '@vueuse/core'
|
||||
import axios from 'axios'
|
||||
import Button from 'primevue/button'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = defineProps<{
|
||||
errorMessage: string
|
||||
repoOwner: string
|
||||
repoName: string
|
||||
}>()
|
||||
|
||||
const GITHUB_API_URL = 'https://api.github.com/search/issues'
|
||||
|
||||
const queryString = computed(() => props.errorMessage + ' is:issue')
|
||||
const getIssueCount = async () => {
|
||||
const query = `${queryString.value} repo:${props.repoOwner}/${props.repoName}`
|
||||
const response = await axios.get(GITHUB_API_URL, {
|
||||
params: {
|
||||
q: query,
|
||||
per_page: 1
|
||||
}
|
||||
})
|
||||
return response.data.total_count
|
||||
}
|
||||
|
||||
const {
|
||||
state: issueCount,
|
||||
isLoading,
|
||||
execute
|
||||
} = useAsyncState(getIssueCount, 0)
|
||||
|
||||
const { t } = useI18n()
|
||||
const buttonLabel = computed(() => {
|
||||
return isLoading.value ? 'Loading...' : t('findIssues')
|
||||
})
|
||||
|
||||
const openGitHubIssues = () => {
|
||||
const query = encodeURIComponent(queryString.value)
|
||||
const url = `https://github.com/${props.repoOwner}/${props.repoName}/issues?q=${query}`
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
</script>
|
||||
@@ -2,6 +2,10 @@ import { createI18n } from 'vue-i18n'
|
||||
|
||||
const messages = {
|
||||
en: {
|
||||
findIssues: 'Find Issues',
|
||||
copyToClipboard: 'Copy to Clipboard',
|
||||
openNewIssue: 'Open New Issue',
|
||||
showReport: 'Show Report',
|
||||
imageFailedToLoad: 'Image failed to load',
|
||||
reconnecting: 'Reconnecting',
|
||||
reconnected: 'Reconnected',
|
||||
@@ -31,6 +35,7 @@ const messages = {
|
||||
}
|
||||
},
|
||||
zh: {
|
||||
showReport: '显示报告',
|
||||
imageFailedToLoad: '图像加载失败',
|
||||
reconnecting: '重新连接中',
|
||||
reconnected: '已重新连接',
|
||||
|
||||
@@ -43,7 +43,10 @@ import {
|
||||
} from '@/stores/nodeDefStore'
|
||||
import { Vector2 } from '@comfyorg/litegraph'
|
||||
import _ from 'lodash'
|
||||
import { showLoadWorkflowWarning } from '@/services/dialogService'
|
||||
import {
|
||||
showExecutionErrorDialog,
|
||||
showLoadWorkflowWarning
|
||||
} from '@/services/dialogService'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
import { useToastStore } from '@/stores/toastStore'
|
||||
import type { ToastMessageOptions } from 'primevue/toast'
|
||||
@@ -1666,8 +1669,7 @@ export class ComfyApp {
|
||||
|
||||
api.addEventListener('execution_error', ({ detail }) => {
|
||||
this.lastExecutionError = detail
|
||||
const formattedError = this.#formatExecutionError(detail)
|
||||
this.ui.dialog.show(formattedError)
|
||||
showExecutionErrorDialog(detail)
|
||||
this.canvas.draw(true, true)
|
||||
})
|
||||
|
||||
@@ -2552,18 +2554,6 @@ export class ComfyApp {
|
||||
return '(unknown error)'
|
||||
}
|
||||
|
||||
#formatExecutionError(error) {
|
||||
if (error == null) {
|
||||
return '(unknown error)'
|
||||
}
|
||||
|
||||
const traceback = error.traceback.join('')
|
||||
const nodeId = error.node_id
|
||||
const nodeType = error.node_type
|
||||
|
||||
return `Error occurred when executing ${nodeType}:\n\n${error.exception_message}\n\n${traceback}`
|
||||
}
|
||||
|
||||
async queuePrompt(number, batchCount = 1) {
|
||||
this.#queueItems.push({ number, batchCount })
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import { useDialogStore } from '@/stores/dialogStore'
|
||||
import LoadWorkflowWarning from '@/components/dialog/content/LoadWorkflowWarning.vue'
|
||||
import SettingDialogContent from '@/components/dialog/content/SettingDialogContent.vue'
|
||||
import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue'
|
||||
import type { ExecutionErrorWsMessage } from '@/types/apiTypes'
|
||||
import ExecutionErrorDialogContent from '@/components/dialog/content/ExecutionErrorDialogContent.vue'
|
||||
|
||||
export function showLoadWorkflowWarning(props: {
|
||||
missingNodeTypes: any[]
|
||||
@@ -24,3 +26,12 @@ export function showSettingsDialog() {
|
||||
component: SettingDialogContent
|
||||
})
|
||||
}
|
||||
|
||||
export function showExecutionErrorDialog(error: ExecutionErrorWsMessage) {
|
||||
useDialogStore().showDialog({
|
||||
component: ExecutionErrorDialogContent,
|
||||
props: {
|
||||
error
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ const zExecutionErrorWsMessage = zExecutionWsMessageBase.extend({
|
||||
executed: z.array(zNodeId),
|
||||
exception_message: z.string(),
|
||||
exception_type: z.string(),
|
||||
traceback: z.string(),
|
||||
traceback: z.array(z.string()),
|
||||
current_inputs: z.any(),
|
||||
current_outputs: z.any()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user