@output.ai/core 0.4.8 → 0.4.9-dev.pr326-21ac1cf

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/core",
3
- "version": "0.4.8",
3
+ "version": "0.4.9-dev.pr326-21ac1cf",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "exports": {
@@ -6,7 +6,8 @@ const envVarSchema = z.object( {
6
6
  OUTPUT_AWS_SECRET_ACCESS_KEY: z.string(),
7
7
  OUTPUT_TRACE_REMOTE_S3_BUCKET: z.string(),
8
8
  OUTPUT_REDIS_URL: z.string(),
9
- OUTPUT_REDIS_TRACE_TTL: z.coerce.number().int().positive().default( 60 * 60 * 24 * 7 ) // 7 days
9
+ OUTPUT_REDIS_TRACE_TTL: z.coerce.number().int().positive().default( 60 * 60 * 24 * 7 ), // 7 days
10
+ OUTPUT_TRACE_UPLOAD_DELAY_MS: z.coerce.number().int().nonnegative().default( 10_000 ) // 10s
10
11
  } );
11
12
 
12
13
  const env = {};
@@ -19,6 +20,7 @@ export const loadEnv = () => {
19
20
  env.remoteS3Bucket = parsedFields.OUTPUT_TRACE_REMOTE_S3_BUCKET;
20
21
  env.redisUrl = parsedFields.OUTPUT_REDIS_URL;
21
22
  env.redisIncompleteWorkflowsTTL = parsedFields.OUTPUT_REDIS_TRACE_TTL;
23
+ env.traceUploadDelayMs = parsedFields.OUTPUT_TRACE_UPLOAD_DELAY_MS;
22
24
  };
23
25
 
24
26
  export const getVars = () => {
@@ -83,6 +83,12 @@ export const exec = async ( { entry, executionContext } ) => {
83
83
  return;
84
84
  }
85
85
 
86
+ // Wait for straggler entries from other workers to land in Redis before uploading
87
+ const delayMs = getVars().traceUploadDelayMs;
88
+ if ( delayMs > 0 ) {
89
+ await new Promise( resolve => setTimeout( resolve, delayMs ) );
90
+ }
91
+
86
92
  const content = buildTraceTree( await getEntries( cacheKey ) );
87
93
  // if the trace tree is incomplete it will return null, in this case we can safely discard
88
94
  if ( !content ) {
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
2
 
3
3
  const loadEnvMock = vi.fn();
4
4
  const getVarsMock = vi.fn( () => ( {
@@ -29,8 +29,13 @@ vi.mock( '../../tools/build_trace_tree.js', () => ( { default: buildTraceTreeMoc
29
29
 
30
30
  describe( 'tracing/processors/s3', () => {
31
31
  beforeEach( () => {
32
+ vi.useFakeTimers();
32
33
  vi.clearAllMocks();
33
- getVarsMock.mockReturnValue( { remoteS3Bucket: 'bkt', redisIncompleteWorkflowsTTL: 3600 } );
34
+ getVarsMock.mockReturnValue( { remoteS3Bucket: 'bkt', redisIncompleteWorkflowsTTL: 3600, traceUploadDelayMs: 10_000 } );
35
+ } );
36
+
37
+ afterEach( () => {
38
+ vi.useRealTimers();
34
39
  } );
35
40
 
36
41
  it( 'init(): loads config and ensures redis client is created', async () => {
@@ -57,8 +62,10 @@ describe( 'tracing/processors/s3', () => {
57
62
 
58
63
  await exec( { ...ctx, entry: { name: 'A', phase: 'start', timestamp: startTime, parentId: 'root' } } );
59
64
  await exec( { ...ctx, entry: { name: 'A', phase: 'tick', timestamp: startTime + 1, parentId: 'root' } } );
60
- // Root end: no parentId and not start
61
- await exec( { ...ctx, entry: { name: 'A', phase: 'end', timestamp: startTime + 2 } } );
65
+ // Root end: no parentId and not start — triggers the 10s delay before upload
66
+ const endPromise = exec( { ...ctx, entry: { name: 'A', phase: 'end', timestamp: startTime + 2 } } );
67
+ await vi.advanceTimersByTimeAsync( 10_000 );
68
+ await endPromise;
62
69
 
63
70
  // Accumulation happened 3 times
64
71
  expect( redisMulti.zAdd ).toHaveBeenCalledTimes( 3 );
@@ -78,7 +85,7 @@ describe( 'tracing/processors/s3', () => {
78
85
  } );
79
86
 
80
87
  it( 'getDestination(): returns S3 URL using bucket and key from getVars', async () => {
81
- getVarsMock.mockReturnValue( { remoteS3Bucket: 'my-bucket', redisIncompleteWorkflowsTTL: 3600 } );
88
+ getVarsMock.mockReturnValue( { remoteS3Bucket: 'my-bucket', redisIncompleteWorkflowsTTL: 3600, traceUploadDelayMs: 10_000 } );
82
89
  const { getDestination } = await import( './index.js' );
83
90
  const startTime = Date.parse( '2020-01-02T03:04:05.678Z' );
84
91
  const url = getDestination( { workflowId: 'id1', workflowName: 'WF', startTime } );
@@ -111,7 +118,9 @@ describe( 'tracing/processors/s3', () => {
111
118
  zRangeMock.mockResolvedValue( [ JSON.stringify( { id: 'wf', phase: 'end', timestamp: startTime } ) ] );
112
119
  buildTraceTreeMock.mockReturnValueOnce( null );
113
120
 
114
- await exec( { ...ctx, entry: { id: 'wf', phase: 'end', timestamp: startTime } } );
121
+ const endPromise = exec( { ...ctx, entry: { id: 'wf', phase: 'end', timestamp: startTime } } );
122
+ await vi.advanceTimersByTimeAsync( 10_000 );
123
+ await endPromise;
115
124
 
116
125
  expect( buildTraceTreeMock ).toHaveBeenCalledTimes( 1 );
117
126
  expect( uploadMock ).not.toHaveBeenCalled();