fix: chunk workflow failure tracking data (#38598)

Co-authored-by: CodingOnStar <hanxujiang@dify.com>
This commit is contained in:
Coding On Star 2026-07-09 16:33:47 +08:00 committed by GitHub
parent dc1131b6df
commit 2f99652203
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 80 additions and 10 deletions

View File

@ -29,6 +29,17 @@ type WorkflowStoreState = {
setListeningTriggerNodeId?: (value: string | null) => void
}
type WorkflowRunFailedTrackingEvent = {
workflow_id: string
reason?: string
node_type?: string
data: {
workflow_status?: string
workflow_tracing_count?: number
workflow_data_chunks?: string[]
}
}
const mocks = vi.hoisted(() => {
const appStoreState = {
appDetail: {
@ -445,8 +456,7 @@ describe('useWorkflowRun', () => {
data: {
workflow_status: WorkflowRunningStatus.Running,
workflow_tracing_count: 1,
workflow_data: workflowData,
workflow_data_json: JSON.stringify(workflowData),
workflow_data_chunks: [JSON.stringify(workflowData)],
},
})
@ -460,12 +470,54 @@ describe('useWorkflowRun', () => {
data: {
workflow_status: WorkflowRunningStatus.Running,
workflow_tracing_count: 1,
workflow_data: workflowData,
workflow_data_json: JSON.stringify(workflowData),
workflow_data_chunks: [JSON.stringify(workflowData)],
},
})
})
it('should split serialized workflow data into chunks when it exceeds the amplitude string limit', async () => {
const { result } = renderHook(() => useWorkflowRun())
await act(async () => {
await result.current.handleRun({ inputs: { query: 'hello' } })
})
const baseCallbackFactoryContext = mocks.mockCreateBaseWorkflowRunCallbacks.mock.calls.at(-1)?.[0] as {
trackWorkflowRunFailed: (params: unknown, workflowData: unknown) => void
}
const workflowData = {
result: {
status: WorkflowRunningStatus.Running,
outputs: {
text: 'x'.repeat(2200),
},
},
tracing: [{ node_id: 'node-1', status: 'running' }],
}
baseCallbackFactoryContext.trackWorkflowRunFailed({ error: 'failed', node_type: 'llm' }, workflowData)
const trackingEvent = mocks.mockTrackEvent.mock.calls.at(-1)?.[1] as WorkflowRunFailedTrackingEvent
const chunks = trackingEvent.data.workflow_data_chunks
if (!chunks)
throw new Error('Expected workflow data chunks to be tracked')
expect(mocks.mockTrackEvent).toHaveBeenCalledWith('workflow_run_failed', {
workflow_id: 'flow-1',
reason: 'failed',
node_type: 'llm',
data: {
workflow_status: WorkflowRunningStatus.Running,
workflow_tracing_count: 1,
workflow_data_chunks: chunks,
},
})
expect(chunks.length).toBeGreaterThan(1)
expect(chunks.every(chunk => chunk.length <= 900)).toBe(true)
expect(chunks.join('')).toBe(JSON.stringify(workflowData))
expect(trackingEvent.data).not.toHaveProperty('workflow_data')
})
it('should track workflow failures when the error or workflow data is malformed', async () => {
const { result } = renderHook(() => useWorkflowRun())
@ -486,8 +538,6 @@ describe('useWorkflowRun', () => {
data: {
workflow_status: undefined,
workflow_tracing_count: undefined,
workflow_data: undefined,
workflow_data_json: undefined,
},
})
@ -507,8 +557,6 @@ describe('useWorkflowRun', () => {
data: {
workflow_status: undefined,
workflow_tracing_count: undefined,
workflow_data: circularWorkflowData,
workflow_data_json: undefined,
},
})
})

View File

@ -67,6 +67,8 @@ type WorkflowDebugWindow = Window & {
__allTriggersDebugAbortController?: DebugAbortController
}
const WORKFLOW_DATA_CHUNK_SIZE = 900
const stringifyWorkflowData = (workflowData: unknown) => {
if (!workflowData)
return undefined
@ -79,6 +81,26 @@ const stringifyWorkflowData = (workflowData: unknown) => {
}
}
const chunkWorkflowData = (serializedWorkflowData: string) => {
const workflowDataChars = Array.from(serializedWorkflowData)
const chunks: string[] = []
for (let index = 0; index < workflowDataChars.length; index += WORKFLOW_DATA_CHUNK_SIZE)
chunks.push(workflowDataChars.slice(index, index + WORKFLOW_DATA_CHUNK_SIZE).join(''))
return chunks
}
const buildWorkflowDataTrackingProperties = (workflowData: unknown) => {
const serializedWorkflowData = stringifyWorkflowData(workflowData)
if (!serializedWorkflowData)
return {}
return {
workflow_data_chunks: chunkWorkflowData(serializedWorkflowData),
}
}
const getWorkflowStatus = (workflowData: unknown) => {
if (typeof workflowData !== 'object' || workflowData === null)
return undefined
@ -367,6 +389,7 @@ const useWorkflowRunBase = (doSyncWorkflowDraft: DoSyncWorkflowDraft) => {
const nodeType = typeof payload?.node_type === 'string'
? payload.node_type
: undefined
const workflowDataTrackingProperties = buildWorkflowDataTrackingProperties(workflowData)
trackEvent('workflow_run_failed', {
workflow_id: flowId,
@ -375,8 +398,7 @@ const useWorkflowRunBase = (doSyncWorkflowDraft: DoSyncWorkflowDraft) => {
data: {
workflow_status: getWorkflowStatus(workflowData),
workflow_tracing_count: getWorkflowTracingCount(workflowData),
workflow_data: workflowData,
workflow_data_json: stringifyWorkflowData(workflowData),
...workflowDataTrackingProperties,
},
})
}