Add analytics for workflow loading (#4966)

Needs to land after https://github.com/Comfy-Org/cloud/pull/398

## Description

- Adds a postCloudAnalytics method in `api.ts`
- Adds a workflow_loaded event
- The event contains 
  - the source (not file type, more like workflow format) one of: 
    - apiJson (I think this is the "prompt" format?)
    - graph (the richest type)
    - template: don't fully understand this but it works
- The actual data for the workflow, depends on the source type
- If available, missingModels and missingNodeTypes, so we can easily
query those

This talks to a new endpoint on the ingest server that is being added.  

## Tests
Tested manually with:
- loading an image from civitAI with missing models
- loading an image from comfy examples with no missing models
- opening a json file in the prompt format (I asked claude to generate
one - this is the format handled by the loadApiJson function)
- opening a template file (claude generated one - this is the format
handled by loadTemplateJson function)
- Testing these for both dragAndDrop and (menu --> open --> open
workflow)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-4966-Add-analytics-for-workflow-loading-24e6d73d36508170acacefb3125b7017)
by [Unito](https://www.unito.io)
This commit is contained in:
Deep Roy
2025-08-14 09:37:48 -04:00
committed by Jennifer Weber
parent 4a612b09ed
commit 86e2b1fc61
2 changed files with 37 additions and 0 deletions

View File

@@ -1275,6 +1275,28 @@ export class ComfyApi extends EventTarget {
getServerFeatures(): Record<string, unknown> {
return { ...this.serverFeatureFlags }
}
/**
* Posts analytics event to cloud analytics service
* @param eventName The name of the analytics event
* @param eventData The event data (any JSON-serializable object)
* @returns Promise resolving to the response
*/
async postCloudAnalytics(
eventName: string,
eventData: any
): Promise<Response> {
return this.fetchApi(this.internalURL('/cloud_analytics'), {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
event_name: eventName,
event_data: eventData
})
})
}
}
export const api = new ComfyApi()

View File

@@ -997,6 +997,10 @@ export class ComfyApp {
if (!templateData?.templates) {
return
}
api.postCloudAnalytics('load_workflow', {
source: 'template',
sourceData: { templateData }
})
const old = localStorage.getItem('litegrapheditor_clipboard')
@@ -1277,6 +1281,12 @@ export class ComfyApp {
const paths = await api.getFolderPaths()
this.#showMissingModelsError(missingModels, paths)
}
api.postCloudAnalytics('load_workflow', {
source: 'graph',
graph: this.graph.asSerialisable(),
missingNodeTypes,
missingModels
})
await useExtensionService().invokeExtensionsAsync(
'afterConfigureGraph',
missingNodeTypes
@@ -1585,6 +1595,11 @@ export class ComfyApp {
const missingNodeTypes = Object.values(apiData).filter(
(n) => !LiteGraph.registered_node_types[n.class_type]
)
api.postCloudAnalytics('load_workflow', {
source: 'api_json',
missingNodeTypes,
apiJson: apiData
})
if (missingNodeTypes.length) {
this.#showMissingNodesError(missingNodeTypes.map((t) => t.class_type))
return