mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-23 16:24:06 +00:00
feat(telemetry): add workflow_opened with open_source and missing node metrics (#6476)
- Adds app:workflow_opened and plumbs open_source across drag/drop, file-open button, workspace, and templates - Tracks missing_node_count and missing_node_types for both workflow_opened and workflow_imported - Reuses WorkflowOpenSource type for consistency; no breaking changes to loadGraphData callers (5th param remains options object; openSource optional) Validation - pnpm lint:fix - pnpm typecheck Notes - Telemetry only runs in cloud builds; OSS remains clean. - loadGraphData telemetry is centralized where missing_node_types is computed. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6476-feat-telemetry-add-workflow_opened-with-open_source-and-missing-node-metrics-29d6d73d365081f385c0da29958309da) by [Unito](https://www.unito.io) --------- Co-authored-by: bymyself <cbyrne@comfy.org>
This commit is contained in:
@@ -19,6 +19,7 @@ import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import type { WorkflowOpenSource } from '@/platform/telemetry/types'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
|
||||
import { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore'
|
||||
@@ -550,7 +551,7 @@ export class ComfyApp {
|
||||
event.dataTransfer.files.length &&
|
||||
event.dataTransfer.files[0].type !== 'image/bmp'
|
||||
) {
|
||||
await this.handleFile(event.dataTransfer.files[0])
|
||||
await this.handleFile(event.dataTransfer.files[0], 'file_drop')
|
||||
} else {
|
||||
// Try loading the first URI in the transfer list
|
||||
const validTypes = ['text/uri-list', 'text/x-moz-url']
|
||||
@@ -561,7 +562,10 @@ export class ComfyApp {
|
||||
const uri = event.dataTransfer.getData(match)?.split('\n')?.[0]
|
||||
if (uri) {
|
||||
const blob = await (await fetch(uri)).blob()
|
||||
await this.handleFile(new File([blob], uri, { type: blob.type }))
|
||||
await this.handleFile(
|
||||
new File([blob], uri, { type: blob.type }),
|
||||
'file_drop'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1040,12 +1044,19 @@ export class ComfyApp {
|
||||
clean: boolean = true,
|
||||
restore_view: boolean = true,
|
||||
workflow: string | null | ComfyWorkflow = null,
|
||||
{
|
||||
showMissingNodesDialog = true,
|
||||
showMissingModelsDialog = true,
|
||||
checkForRerouteMigration = false
|
||||
options: {
|
||||
showMissingNodesDialog?: boolean
|
||||
showMissingModelsDialog?: boolean
|
||||
checkForRerouteMigration?: boolean
|
||||
openSource?: WorkflowOpenSource
|
||||
} = {}
|
||||
) {
|
||||
const {
|
||||
showMissingNodesDialog = true,
|
||||
showMissingModelsDialog = true,
|
||||
checkForRerouteMigration = false,
|
||||
openSource
|
||||
} = options
|
||||
useWorkflowService().beforeLoadNewGraph()
|
||||
|
||||
if (clean !== false) {
|
||||
@@ -1275,13 +1286,15 @@ export class ComfyApp {
|
||||
missingNodeTypes
|
||||
)
|
||||
|
||||
// Track workflow import with missing node information
|
||||
useTelemetry()?.trackWorkflowImported({
|
||||
const telemetryPayload = {
|
||||
missing_node_count: missingNodeTypes.length,
|
||||
missing_node_types: missingNodeTypes.map((node) =>
|
||||
typeof node === 'string' ? node : node.type
|
||||
)
|
||||
})
|
||||
),
|
||||
open_source: openSource ?? 'unknown'
|
||||
}
|
||||
useTelemetry()?.trackWorkflowOpened(telemetryPayload)
|
||||
useTelemetry()?.trackWorkflowImported(telemetryPayload)
|
||||
await useWorkflowService().afterLoadNewGraph(
|
||||
workflow,
|
||||
this.graph.serialize() as unknown as ComfyWorkflowJSON
|
||||
@@ -1399,7 +1412,7 @@ export class ComfyApp {
|
||||
* Loads workflow data from the specified file
|
||||
* @param {File} file
|
||||
*/
|
||||
async handleFile(file: File) {
|
||||
async handleFile(file: File, openSource?: WorkflowOpenSource) {
|
||||
const removeExt = (f: string) => {
|
||||
if (!f) return f
|
||||
const p = f.lastIndexOf('.')
|
||||
@@ -1414,7 +1427,8 @@ export class ComfyApp {
|
||||
JSON.parse(pngInfo.workflow),
|
||||
true,
|
||||
true,
|
||||
fileName
|
||||
fileName,
|
||||
{ openSource }
|
||||
)
|
||||
} else if (pngInfo?.prompt) {
|
||||
this.loadApiJson(JSON.parse(pngInfo.prompt), fileName)
|
||||
@@ -1434,7 +1448,9 @@ export class ComfyApp {
|
||||
const { workflow, prompt } = await getAvifMetadata(file)
|
||||
|
||||
if (workflow) {
|
||||
this.loadGraphData(JSON.parse(workflow), true, true, fileName)
|
||||
this.loadGraphData(JSON.parse(workflow), true, true, fileName, {
|
||||
openSource
|
||||
})
|
||||
} else if (prompt) {
|
||||
this.loadApiJson(JSON.parse(prompt), fileName)
|
||||
} else {
|
||||
@@ -1447,7 +1463,9 @@ export class ComfyApp {
|
||||
const prompt = pngInfo?.prompt || pngInfo?.Prompt
|
||||
|
||||
if (workflow) {
|
||||
this.loadGraphData(JSON.parse(workflow), true, true, fileName)
|
||||
this.loadGraphData(JSON.parse(workflow), true, true, fileName, {
|
||||
openSource
|
||||
})
|
||||
} else if (prompt) {
|
||||
this.loadApiJson(JSON.parse(prompt), fileName)
|
||||
} else {
|
||||
@@ -1456,7 +1474,7 @@ export class ComfyApp {
|
||||
} else if (file.type === 'audio/mpeg') {
|
||||
const { workflow, prompt } = await getMp3Metadata(file)
|
||||
if (workflow) {
|
||||
this.loadGraphData(workflow, true, true, fileName)
|
||||
this.loadGraphData(workflow, true, true, fileName, { openSource })
|
||||
} else if (prompt) {
|
||||
this.loadApiJson(prompt, fileName)
|
||||
} else {
|
||||
@@ -1465,7 +1483,7 @@ export class ComfyApp {
|
||||
} else if (file.type === 'audio/ogg') {
|
||||
const { workflow, prompt } = await getOggMetadata(file)
|
||||
if (workflow) {
|
||||
this.loadGraphData(workflow, true, true, fileName)
|
||||
this.loadGraphData(workflow, true, true, fileName, { openSource })
|
||||
} else if (prompt) {
|
||||
this.loadApiJson(prompt, fileName)
|
||||
} else {
|
||||
@@ -1477,7 +1495,9 @@ export class ComfyApp {
|
||||
const prompt = pngInfo?.prompt || pngInfo?.Prompt
|
||||
|
||||
if (workflow) {
|
||||
this.loadGraphData(JSON.parse(workflow), true, true, fileName)
|
||||
this.loadGraphData(JSON.parse(workflow), true, true, fileName, {
|
||||
openSource
|
||||
})
|
||||
} else if (prompt) {
|
||||
this.loadApiJson(JSON.parse(prompt), fileName)
|
||||
} else {
|
||||
@@ -1486,7 +1506,9 @@ export class ComfyApp {
|
||||
} else if (file.type === 'video/webm') {
|
||||
const webmInfo = await getFromWebmFile(file)
|
||||
if (webmInfo.workflow) {
|
||||
this.loadGraphData(webmInfo.workflow, true, true, fileName)
|
||||
this.loadGraphData(webmInfo.workflow, true, true, fileName, {
|
||||
openSource
|
||||
})
|
||||
} else if (webmInfo.prompt) {
|
||||
this.loadApiJson(webmInfo.prompt, fileName)
|
||||
} else {
|
||||
@@ -1502,14 +1524,18 @@ export class ComfyApp {
|
||||
) {
|
||||
const mp4Info = await getFromIsobmffFile(file)
|
||||
if (mp4Info.workflow) {
|
||||
this.loadGraphData(mp4Info.workflow, true, true, fileName)
|
||||
this.loadGraphData(mp4Info.workflow, true, true, fileName, {
|
||||
openSource
|
||||
})
|
||||
} else if (mp4Info.prompt) {
|
||||
this.loadApiJson(mp4Info.prompt, fileName)
|
||||
}
|
||||
} else if (file.type === 'image/svg+xml' || file.name?.endsWith('.svg')) {
|
||||
const svgInfo = await getSvgMetadata(file)
|
||||
if (svgInfo.workflow) {
|
||||
this.loadGraphData(svgInfo.workflow, true, true, fileName)
|
||||
this.loadGraphData(svgInfo.workflow, true, true, fileName, {
|
||||
openSource
|
||||
})
|
||||
} else if (svgInfo.prompt) {
|
||||
this.loadApiJson(svgInfo.prompt, fileName)
|
||||
} else {
|
||||
@@ -1521,7 +1547,9 @@ export class ComfyApp {
|
||||
) {
|
||||
const gltfInfo = await getGltfBinaryMetadata(file)
|
||||
if (gltfInfo.workflow) {
|
||||
this.loadGraphData(gltfInfo.workflow, true, true, fileName)
|
||||
this.loadGraphData(gltfInfo.workflow, true, true, fileName, {
|
||||
openSource
|
||||
})
|
||||
} else if (gltfInfo.prompt) {
|
||||
this.loadApiJson(gltfInfo.prompt, fileName)
|
||||
} else {
|
||||
@@ -1544,7 +1572,8 @@ export class ComfyApp {
|
||||
JSON.parse(readerResult),
|
||||
true,
|
||||
true,
|
||||
fileName
|
||||
fileName,
|
||||
{ openSource }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1562,7 +1591,8 @@ export class ComfyApp {
|
||||
JSON.parse(info.workflow),
|
||||
true,
|
||||
true,
|
||||
fileName
|
||||
fileName,
|
||||
{ openSource }
|
||||
)
|
||||
// @ts-expect-error
|
||||
} else if (info.prompt) {
|
||||
|
||||
@@ -392,7 +392,7 @@ export class ComfyUI {
|
||||
parent: document.body,
|
||||
onchange: async () => {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
await app.handleFile(fileInput.files[0])
|
||||
await app.handleFile(fileInput.files[0], 'file_button')
|
||||
fileInput.value = ''
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user