diff --git a/js/core/src/tracing/instrumentation.ts b/js/core/src/tracing/instrumentation.ts index 9f68565518..b3d4585881 100644 --- a/js/core/src/tracing/instrumentation.ts +++ b/js/core/src/tracing/instrumentation.ts @@ -210,14 +210,15 @@ function recordPath(spanMeta: SpanMetadata, err?: any) { const paths = Array.from( traceMetadataAls.getStore()?.paths || new Set() ); - if (!paths.some((p) => p.path.startsWith(path))) { + const status = err ? 'failure' : 'success'; + if (!paths.some((p) => p.path.startsWith(path) && p.status === status)) { const now = performance.now(); const start = traceMetadataAls.getStore()?.timestamp || now; traceMetadataAls.getStore()?.paths?.add({ path: decoratedPath, - status: err ? 'failure' : 'success', error: err?.name, latency: now - start, + status, }); } spanMeta.path = decoratedPath; diff --git a/js/plugins/google-cloud/tests/metrics_test.ts b/js/plugins/google-cloud/tests/metrics_test.ts index f3253c7ee8..658bfdfa08 100644 --- a/js/plugins/google-cloud/tests/metrics_test.ts +++ b/js/plugins/google-cloud/tests/metrics_test.ts @@ -481,6 +481,49 @@ describe('GoogleCloudMetrics', () => { ]); }); + it('writes flow path failure metrics in subaction', async () => { + const flow = createFlow('testFlow', async () => { + const subPath1 = await run('sub-action-1', async () => { + const subPath2 = await run('sub-action-2', async () => { + return 'done'; + }); + return Promise.reject(new Error('failed')); + }); + return 'done'; + }); + + assert.rejects(async () => { + await runFlow(flow); + }); + + const reqPoints = await getCounterDataPoints('genkit/flow/path/requests'); + const reqStatuses = reqPoints.map((p) => [ + p.attributes.path, + p.attributes.status, + ]); + assert.deepEqual(reqStatuses, [ + [ + '/{testFlow,t:flow}/{sub-action-1,t:flowStep}/{sub-action-2,t:flowStep}', + 'success', + ], + ['/{testFlow,t:flow}/{sub-action-1,t:flowStep}', 'failure'], + ]); + const latencyPoints = await getHistogramDataPoints( + 'genkit/flow/path/latency' + ); + const latencyStatuses = latencyPoints.map((p) => [ + p.attributes.path, + p.attributes.status, + ]); + assert.deepEqual(latencyStatuses, [ + [ + '/{testFlow,t:flow}/{sub-action-1,t:flowStep}/{sub-action-2,t:flowStep}', + 'success', + ], + ['/{testFlow,t:flow}/{sub-action-1,t:flowStep}', 'failure'], + ]); + }); + it('writes flow path failure in sub-action metrics', async () => { const flow = createFlow('testFlow', async () => { const subPath1 = await run('sub-action-1', async () => {