@vertesia/workflow 1.0.0-dev.20260128.144200 → 1.0.0-dev.20260225.024852Z

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.
Files changed (132) hide show
  1. package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +1 -1
  2. package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
  3. package/lib/cjs/activities/chunkDocument.js +3 -1
  4. package/lib/cjs/activities/chunkDocument.js.map +1 -1
  5. package/lib/cjs/activities/extractDocumentText.js +56 -16
  6. package/lib/cjs/activities/extractDocumentText.js.map +1 -1
  7. package/lib/cjs/activities/generateDocumentProperties.js +4 -2
  8. package/lib/cjs/activities/generateDocumentProperties.js.map +1 -1
  9. package/lib/cjs/activities/generateEmbeddings.js +20 -10
  10. package/lib/cjs/activities/generateEmbeddings.js.map +1 -1
  11. package/lib/cjs/activities/generateOrAssignContentType.js +2 -2
  12. package/lib/cjs/activities/generateOrAssignContentType.js.map +1 -1
  13. package/lib/cjs/activities/index-dsl.js +7 -7
  14. package/lib/cjs/activities/index-dsl.js.map +1 -1
  15. package/lib/cjs/activities/media/saveGladiaTranscription.js +38 -24
  16. package/lib/cjs/activities/media/saveGladiaTranscription.js.map +1 -1
  17. package/lib/cjs/activities/media/transcribeMediaWithGladia.js +41 -24
  18. package/lib/cjs/activities/media/transcribeMediaWithGladia.js.map +1 -1
  19. package/lib/cjs/activities/notifyWebhook.js +11 -2
  20. package/lib/cjs/activities/notifyWebhook.js.map +1 -1
  21. package/lib/cjs/activities/renditions/generateImageRendition.js +2 -2
  22. package/lib/cjs/activities/renditions/generateImageRendition.js.map +1 -1
  23. package/lib/cjs/activities/setDocumentStatus.js +13 -2
  24. package/lib/cjs/activities/setDocumentStatus.js.map +1 -1
  25. package/lib/cjs/conversion/image.js +10 -10
  26. package/lib/cjs/conversion/image.js.map +1 -1
  27. package/lib/cjs/dsl/dsl-workflow.js +44 -7
  28. package/lib/cjs/dsl/dsl-workflow.js.map +1 -1
  29. package/lib/cjs/dsl/setup/ActivityContext.js +56 -0
  30. package/lib/cjs/dsl/setup/ActivityContext.js.map +1 -1
  31. package/lib/cjs/errors.js +11 -1
  32. package/lib/cjs/errors.js.map +1 -1
  33. package/lib/cjs/index.js +6 -5
  34. package/lib/cjs/index.js.map +1 -1
  35. package/lib/cjs/result-types.js.map +1 -1
  36. package/lib/cjs/utils/renditions.js +9 -5
  37. package/lib/cjs/utils/renditions.js.map +1 -1
  38. package/lib/cjs/utils/text-preview-utils.js +43 -0
  39. package/lib/cjs/utils/text-preview-utils.js.map +1 -0
  40. package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +1 -1
  41. package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
  42. package/lib/esm/activities/chunkDocument.js +3 -1
  43. package/lib/esm/activities/chunkDocument.js.map +1 -1
  44. package/lib/esm/activities/extractDocumentText.js +56 -16
  45. package/lib/esm/activities/extractDocumentText.js.map +1 -1
  46. package/lib/esm/activities/generateDocumentProperties.js +4 -2
  47. package/lib/esm/activities/generateDocumentProperties.js.map +1 -1
  48. package/lib/esm/activities/generateEmbeddings.js +20 -10
  49. package/lib/esm/activities/generateEmbeddings.js.map +1 -1
  50. package/lib/esm/activities/generateOrAssignContentType.js +2 -2
  51. package/lib/esm/activities/generateOrAssignContentType.js.map +1 -1
  52. package/lib/esm/activities/index-dsl.js +3 -3
  53. package/lib/esm/activities/index-dsl.js.map +1 -1
  54. package/lib/esm/activities/media/saveGladiaTranscription.js +38 -24
  55. package/lib/esm/activities/media/saveGladiaTranscription.js.map +1 -1
  56. package/lib/esm/activities/media/transcribeMediaWithGladia.js +41 -24
  57. package/lib/esm/activities/media/transcribeMediaWithGladia.js.map +1 -1
  58. package/lib/esm/activities/notifyWebhook.js +11 -2
  59. package/lib/esm/activities/notifyWebhook.js.map +1 -1
  60. package/lib/esm/activities/renditions/generateImageRendition.js +2 -2
  61. package/lib/esm/activities/renditions/generateImageRendition.js.map +1 -1
  62. package/lib/esm/activities/setDocumentStatus.js +13 -2
  63. package/lib/esm/activities/setDocumentStatus.js.map +1 -1
  64. package/lib/esm/conversion/image.js +10 -10
  65. package/lib/esm/conversion/image.js.map +1 -1
  66. package/lib/esm/dsl/dsl-workflow.js +44 -7
  67. package/lib/esm/dsl/dsl-workflow.js.map +1 -1
  68. package/lib/esm/dsl/setup/ActivityContext.js +57 -1
  69. package/lib/esm/dsl/setup/ActivityContext.js.map +1 -1
  70. package/lib/esm/errors.js +9 -0
  71. package/lib/esm/errors.js.map +1 -1
  72. package/lib/esm/index.js +6 -5
  73. package/lib/esm/index.js.map +1 -1
  74. package/lib/esm/result-types.js.map +1 -1
  75. package/lib/esm/utils/renditions.js +9 -5
  76. package/lib/esm/utils/renditions.js.map +1 -1
  77. package/lib/esm/utils/text-preview-utils.js +38 -0
  78. package/lib/esm/utils/text-preview-utils.js.map +1 -0
  79. package/lib/tsconfig.tsbuildinfo +1 -1
  80. package/lib/types/activities/chunkDocument.d.ts.map +1 -1
  81. package/lib/types/activities/extractDocumentText.d.ts +1 -0
  82. package/lib/types/activities/extractDocumentText.d.ts.map +1 -1
  83. package/lib/types/activities/generateDocumentProperties.d.ts.map +1 -1
  84. package/lib/types/activities/generateEmbeddings.d.ts.map +1 -1
  85. package/lib/types/activities/index-dsl.d.ts +3 -3
  86. package/lib/types/activities/index-dsl.d.ts.map +1 -1
  87. package/lib/types/activities/media/saveGladiaTranscription.d.ts +1 -0
  88. package/lib/types/activities/media/saveGladiaTranscription.d.ts.map +1 -1
  89. package/lib/types/activities/media/transcribeMediaWithGladia.d.ts +1 -0
  90. package/lib/types/activities/media/transcribeMediaWithGladia.d.ts.map +1 -1
  91. package/lib/types/activities/setDocumentStatus.d.ts +1 -1
  92. package/lib/types/activities/setDocumentStatus.d.ts.map +1 -1
  93. package/lib/types/dsl/dsl-workflow.d.ts.map +1 -1
  94. package/lib/types/dsl/setup/ActivityContext.d.ts +32 -2
  95. package/lib/types/dsl/setup/ActivityContext.d.ts.map +1 -1
  96. package/lib/types/errors.d.ts +4 -0
  97. package/lib/types/errors.d.ts.map +1 -1
  98. package/lib/types/index.d.ts +6 -5
  99. package/lib/types/index.d.ts.map +1 -1
  100. package/lib/types/result-types.d.ts +5 -1
  101. package/lib/types/result-types.d.ts.map +1 -1
  102. package/lib/types/utils/renditions.d.ts +2 -0
  103. package/lib/types/utils/renditions.d.ts.map +1 -1
  104. package/lib/types/utils/text-preview-utils.d.ts +15 -0
  105. package/lib/types/utils/text-preview-utils.d.ts.map +1 -0
  106. package/lib/workflows-bundle.js +11747 -11141
  107. package/package.json +6 -7
  108. package/src/activities/advanced/createOrUpdateDocumentFromInteractionRun.ts +1 -1
  109. package/src/activities/chunkDocument.ts +3 -1
  110. package/src/activities/extractDocumentText.ts +85 -26
  111. package/src/activities/generateDocumentProperties.ts +4 -2
  112. package/src/activities/generateEmbeddings.ts +22 -14
  113. package/src/activities/generateOrAssignContentType.ts +2 -2
  114. package/src/activities/index-dsl.ts +4 -3
  115. package/src/activities/media/saveGladiaTranscription.test.ts +406 -0
  116. package/src/activities/media/saveGladiaTranscription.ts +41 -26
  117. package/src/activities/media/transcribeMediaWithGladia.test.ts +583 -0
  118. package/src/activities/media/transcribeMediaWithGladia.ts +46 -25
  119. package/src/activities/notifyWebhook.test.ts +121 -8
  120. package/src/activities/notifyWebhook.ts +10 -2
  121. package/src/activities/renditions/generateImageRendition.ts +2 -2
  122. package/src/activities/setDocumentStatus.ts +12 -4
  123. package/src/conversion/image.test.ts +1 -0
  124. package/src/conversion/image.ts +10 -10
  125. package/src/dsl/dsl-workflow.ts +57 -9
  126. package/src/dsl/setup/ActivityContext.ts +73 -0
  127. package/src/dsl.ts +1 -0
  128. package/src/errors.ts +15 -0
  129. package/src/index.ts +6 -5
  130. package/src/result-types.ts +5 -1
  131. package/src/utils/renditions.ts +11 -5
  132. package/src/utils/text-preview-utils.ts +62 -0
@@ -9,6 +9,7 @@ import { TextExtractionResult, TextExtractionStatus } from "../../index.js";
9
9
  export interface TranscriptMediaParams {
10
10
  environmentId?: string;
11
11
  force?: boolean;
12
+ output_storage_path?: string;
12
13
  }
13
14
 
14
15
  export interface TranscriptMedia extends DSLActivitySpec<TranscriptMediaParams> {
@@ -28,39 +29,59 @@ const GLADIA_URL = "https://api.gladia.io/v2";
28
29
 
29
30
  export async function transcribeMedia(payload: DSLActivityExecutionPayload<TranscriptMediaParams>): Promise<TranscriptMediaResult> {
30
31
 
31
- const { params, client, objectId } = await setupActivity<TranscriptMediaParams>(payload);
32
+ const context = await setupActivity<TranscriptMediaParams>(payload);
33
+ const { params, client, inputType } = context;
32
34
 
33
35
  const gladiaConfig = await client.projects.integrations.retrieve(payload.project_id, SupportedIntegrations.gladia) as GladiaConfiguration | undefined;
34
36
  if (!gladiaConfig || !gladiaConfig.enabled) {
35
37
  return {
36
38
  hasText: false,
37
- objectId,
39
+ objectId: inputType === 'objectIds' ? context.objectId : undefined,
38
40
  status: TextExtractionStatus.error,
39
41
  error: "Gladia integration not enabled",
40
42
  }
41
43
  }
42
44
 
43
- const object = await client.objects.retrieve(objectId, "+text");
44
45
  const gladiaClient = new FetchClient(gladiaConfig.url ?? GLADIA_URL);
45
46
  gladiaClient.withHeaders({ "x-gladia-key": gladiaConfig.api_key });
46
47
 
47
- if (object.text && !params.force) {
48
- return { hasText: true, objectId, status: TextExtractionStatus.skipped, message: "text already present and force not enabled" }
49
- }
48
+ let mediaSource: string;
49
+ let storageId: string;
50
50
 
51
- if (!object.content?.source) {
52
- throw new DocumentNotFoundError(`No source found for object ${objectId}`);
53
- }
51
+ if (inputType === 'objectIds') {
52
+ // Object mode: fetch from object store
53
+ const objectId = context.objectId;
54
+ const object = await client.objects.retrieve(objectId, "+text");
55
+
56
+ if (object.text && !params.force) {
57
+ return { hasText: true, objectId, status: TextExtractionStatus.skipped, message: "text already present and force not enabled" }
58
+ }
59
+
60
+ if (!object.content?.source) {
61
+ throw new DocumentNotFoundError(`No source found for object ${objectId}`);
62
+ }
54
63
 
55
- // Check for audio rendition in video metadata (preferred for videos)
56
- let mediaSource: string = object.content.source;
57
- if (object.metadata?.type === ContentNature.Video) {
58
- const videoMetadata = object.metadata as VideoMetadata;
59
- const audioRendition = videoMetadata.renditions?.find(r => r.name === AUDIO_RENDITION_NAME);
60
- if (audioRendition?.content?.source) {
61
- mediaSource = audioRendition.content.source;
62
- log.info(`Found audio rendition for video object ${objectId}`, { mediaSource });
64
+ // Check for audio rendition in video metadata (preferred for videos)
65
+ mediaSource = object.content.source;
66
+ if (object.metadata?.type === ContentNature.Video) {
67
+ const videoMetadata = object.metadata as VideoMetadata;
68
+ const audioRendition = videoMetadata.renditions?.find(r => r.name === AUDIO_RENDITION_NAME);
69
+ if (audioRendition?.content?.source) {
70
+ mediaSource = audioRendition.content.source;
71
+ log.info(`Found audio rendition for video object ${objectId}`, { mediaSource });
72
+ }
63
73
  }
74
+
75
+ storageId = objectId;
76
+ } else {
77
+ // File mode: use file input
78
+ const file = context.file;
79
+ if (!params.output_storage_path) {
80
+ throw new DocumentNotFoundError("output_storage_path is required when using file input");
81
+ }
82
+
83
+ mediaSource = file.url;
84
+ storageId = params.output_storage_path;
64
85
  }
65
86
 
66
87
  // Get download URL for the media source
@@ -70,12 +91,12 @@ export async function transcribeMedia(payload: DSLActivityExecutionPayload<Trans
70
91
  throw new DocumentNotFoundError(`Error fetching media URL for ${mediaSource}`);
71
92
  }
72
93
 
73
- log.info(`Using media URL for transcription`, { objectId, mediaUrl: mediaSource });
94
+ log.info(`Using media URL for transcription`, { storageId, mediaUrl: mediaSource });
74
95
 
75
96
  const taskToken = Buffer.from(activityInfo().taskToken).toString('base64url');
76
- const callbackUrl = generateCallbackUrlForGladia(client.store.baseUrl, taskToken, objectId);
97
+ const callbackUrl = generateCallbackUrlForGladia(client.store.baseUrl, taskToken);
77
98
 
78
- log.info(`Transcribing media ${mediaUrl} with Gladia`, { objectId, callbackUrl });
99
+ log.info(`Transcribing media ${mediaUrl} with Gladia`, { storageId, callbackUrl });
79
100
 
80
101
  try {
81
102
  const res = await gladiaClient.post("/transcription", {
@@ -90,25 +111,25 @@ export async function transcribeMedia(payload: DSLActivityExecutionPayload<Trans
90
111
  }
91
112
  }
92
113
  }) as GladiaTranscriptRequestResponse;
93
- log.info(`Transcription request sent to Gladia`, { objectId, res });
114
+ log.info(`Transcription request sent to Gladia`, { storageId, res });
94
115
  } catch (error: any) {
95
116
  if (error instanceof RequestError && error.status === 422) {
96
117
  return {
97
118
  hasText: false,
98
- objectId,
119
+ objectId: storageId,
99
120
  status: TextExtractionStatus.error,
100
121
  error: `Gladia transcription error: ${error.message}`,
101
122
  }
102
123
  }
103
- log.error(`Error sending transcription request to Gladia for object ${objectId}`, { error });
124
+ log.error(`Error sending transcription request to Gladia for storage ${storageId}`, { error });
104
125
  throw error;
105
126
  }
106
127
 
107
128
  throw new CompleteAsyncError();
108
129
  }
109
130
 
110
- function generateCallbackUrlForGladia(baseUrl: string, taskToken: string, objectId: string) {
111
- return `${baseUrl}/webhooks/gladia/${objectId}?task_token=${taskToken}`;
131
+ function generateCallbackUrlForGladia(baseUrl: string, taskToken: string) {
132
+ return `${baseUrl}/webhooks/gladia?task_token=${taskToken}`;
112
133
  }
113
134
 
114
135
  interface GladiaTranscriptRequestResponse {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  MockActivityEnvironment,
3
3
  } from "@temporalio/testing";
4
- import { ContentEventName, DSLActivityExecutionPayload } from "@vertesia/common";
4
+ import { ApiVersions, ContentEventName, DSLActivityExecutionPayload, WebHookSpec } from "@vertesia/common";
5
5
  import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
6
- import { notifyWebhook, NotifyWebhookParams } from "./notifyWebhook.js";
6
+ import { notifyWebhook, NotifyWebhookParams, WebhookNotificationPayload } from "./notifyWebhook.js";
7
7
 
8
8
  // Mock fetch globally
9
9
  vi.stubGlobal('fetch', vi.fn());
@@ -63,10 +63,15 @@ describe("Webhook should be notified", () => {
63
63
  const payload = createTestPayload();
64
64
  const res = await testEnv.run(notifyWebhook, payload);
65
65
 
66
- // Verify fetch was called with correct parameters
66
+ // Verify fetch was called with correct parameters (old format wraps detail in result)
67
67
  expect(mockFetch).toHaveBeenCalledWith(defaultParams.webhook, {
68
68
  method: 'POST',
69
- body: JSON.stringify({ message: 'Hello World' }),
69
+ body: JSON.stringify({
70
+ workflowId: 'wf_id',
71
+ runId: 'wf_run_id',
72
+ status: 'completed',
73
+ result: { message: 'Hello World' }
74
+ }),
70
75
  headers: {
71
76
  'Content-Type': 'application/json',
72
77
  },
@@ -98,10 +103,15 @@ describe("Webhook should be notified", () => {
98
103
  `Webhook Notification to ${defaultParams.webhook} failed with status: 500 Internal Server Error - Response: {"error": "Database connection failed", "code": "DB_ERROR"}`
99
104
  );
100
105
 
101
- // Verify fetch was called with correct parameters
106
+ // Verify fetch was called with correct parameters (old format wraps detail in result)
102
107
  expect(mockFetch).toHaveBeenCalledWith(defaultParams.webhook, {
103
108
  method: 'POST',
104
- body: JSON.stringify({ message: 'Hello World' }),
109
+ body: JSON.stringify({
110
+ workflowId: 'wf_id',
111
+ runId: 'wf_run_id',
112
+ status: 'completed',
113
+ result: { message: 'Hello World' }
114
+ }),
105
115
  headers: {
106
116
  'Content-Type': 'application/json',
107
117
  },
@@ -121,14 +131,117 @@ describe("Webhook should be notified", () => {
121
131
  // Expect the function to throw the network error
122
132
  await expect(testEnv.run(notifyWebhook, payload)).rejects.toThrow('Network request failed');
123
133
 
124
- // Verify fetch was called with correct parameters
134
+ // Verify fetch was called with correct parameters (old format wraps detail in result)
125
135
  expect(mockFetch).toHaveBeenCalledWith(defaultParams.webhook, {
126
136
  method: 'POST',
127
- body: JSON.stringify({ message: 'Hello World' }),
137
+ body: JSON.stringify({
138
+ workflowId: 'wf_id',
139
+ runId: 'wf_run_id',
140
+ status: 'completed',
141
+ result: { message: 'Hello World' }
142
+ }),
128
143
  headers: {
129
144
  'Content-Type': 'application/json',
130
145
  },
131
146
  });
132
147
  });
133
148
 
149
+ it("test POST with undefined detail still sends body with workflow info (new format)", async () => {
150
+ // Mock successful response
151
+ const mockResponse = {
152
+ ok: true,
153
+ status: 200,
154
+ statusText: 'OK',
155
+ url: 'https://vertesia.test'
156
+ };
157
+ mockFetch.mockResolvedValueOnce(mockResponse as Response);
158
+
159
+ // Create webhook spec with version to use new format
160
+ const webhookSpec: WebHookSpec = {
161
+ url: 'https://vertesia.test',
162
+ version: ApiVersions.COMPLETION_RESULT_V1
163
+ };
164
+
165
+ // Create payload with undefined detail (like workflow_failed events)
166
+ const payload = createTestPayload({
167
+ webhook: webhookSpec,
168
+ detail: undefined,
169
+ event_name: 'workflow_failed'
170
+ });
171
+
172
+ const res = await testEnv.run(notifyWebhook, payload);
173
+
174
+ // Verify fetch was called
175
+ expect(mockFetch).toHaveBeenCalledTimes(1);
176
+ const [_url, options] = mockFetch.mock.calls[0];
177
+
178
+ // Verify the body parameter is NOT undefined
179
+ expect(options?.body).toBeDefined();
180
+
181
+ // Verify the body contains workflow info
182
+ const bodyData = JSON.parse(options?.body as string) as WebhookNotificationPayload;
183
+ expect(bodyData.workflow_id).toBe('wf_id');
184
+ expect(bodyData.workflow_name).toBe('wfFuncName');
185
+ expect(bodyData.workflow_run_id).toBe('wf_run_id');
186
+ expect(bodyData.event_name).toBe('workflow_failed');
187
+ // detail should not be present when undefined (JSON.stringify omits it)
188
+ expect(bodyData.detail).toBeUndefined();
189
+
190
+ // Verify Content-Type header is set
191
+ const headers = options?.headers as Record<string, string>;
192
+ expect(headers['Content-Type']).toBe('application/json');
193
+
194
+ // Verify response
195
+ expect(res).toEqual({
196
+ status: 200,
197
+ message: 'OK',
198
+ url: webhookSpec.url
199
+ });
200
+ });
201
+
202
+ it("test POST with undefined detail still sends body with workflow info (old format)", async () => {
203
+ // Mock successful response
204
+ const mockResponse = {
205
+ ok: true,
206
+ status: 200,
207
+ statusText: 'OK',
208
+ url: 'https://vertesia.test'
209
+ };
210
+ mockFetch.mockResolvedValueOnce(mockResponse as Response);
211
+
212
+ // Create payload with string webhook (old format) and undefined detail
213
+ const payload = createTestPayload({
214
+ webhook: 'https://vertesia.test',
215
+ detail: undefined,
216
+ event_name: 'workflow_completed'
217
+ });
218
+
219
+ const res = await testEnv.run(notifyWebhook, payload);
220
+
221
+ // Verify fetch was called
222
+ expect(mockFetch).toHaveBeenCalledTimes(1);
223
+ const [_url, options] = mockFetch.mock.calls[0];
224
+
225
+ // Verify the body parameter is NOT undefined
226
+ expect(options?.body).toBeDefined();
227
+
228
+ // Verify the body contains workflow info in old format
229
+ const bodyData = JSON.parse(options?.body as string);
230
+ expect(bodyData.workflowId).toBe('wf_id');
231
+ expect(bodyData.runId).toBe('wf_run_id');
232
+ expect(bodyData.status).toBe('completed');
233
+ expect(bodyData.result).toBeNull(); // null when detail is undefined
234
+
235
+ // Verify Content-Type header is set
236
+ const headers = options?.headers as Record<string, string>;
237
+ expect(headers['Content-Type']).toBe('application/json');
238
+
239
+ // Verify response
240
+ expect(res).toEqual({
241
+ status: 200,
242
+ message: 'OK',
243
+ url: 'https://vertesia.test'
244
+ });
245
+ });
246
+
134
247
  });
@@ -45,7 +45,7 @@ export async function notifyWebhook(payload: DSLActivityExecutionPayload<NotifyW
45
45
 
46
46
  if (!target_url) throw new WorkflowParamNotFoundError('target_url');
47
47
 
48
- const hasBody = params.detail && method === 'POST'; //body is sent only for POST
48
+ const hasBody = method === 'POST'; //body is sent only for POST, always includes workflow info
49
49
 
50
50
  const headers = {
51
51
  ...defaultHeaders,
@@ -194,6 +194,14 @@ async function createOldRequestBody(payload: WorkflowExecutionBaseParams, params
194
194
  result: result || null
195
195
  }
196
196
  };
197
+ } else {
198
+ // Always include workflow metadata in old format, even when detail is undefined
199
+ data = {
200
+ workflowId: params.workflow_id,
201
+ runId: params.workflow_run_id,
202
+ status: params.event_name === 'workflow_completed' ? 'completed' : params.event_name,
203
+ result: data || null
204
+ };
197
205
  }
198
- return JSON.stringify(data || {});
206
+ return JSON.stringify(data);
199
207
  }
@@ -31,7 +31,7 @@ export async function generateImageRendition(
31
31
  format: originParams.format || (originParams as any).format_output || "png", // Default to png if format is missing
32
32
  };
33
33
 
34
- log.info(`Generating image rendition for ${objectId}`, {
34
+ log.debug(`Generating image rendition for ${objectId}`, {
35
35
  originParams,
36
36
  params,
37
37
  });
@@ -79,7 +79,7 @@ export async function generateImageRendition(
79
79
  client,
80
80
  inputObject.content.source,
81
81
  );
82
- log.info(`Image ${objectId} copied to ${imageFile}`);
82
+ log.debug(`Image ${objectId} copied to ${imageFile}`);
83
83
  renditionPages.push(imageFile);
84
84
 
85
85
 
@@ -1,3 +1,4 @@
1
+ import { log } from "@temporalio/activity";
1
2
  import { ContentObjectStatus, DSLActivityExecutionPayload, DSLActivitySpec } from "@vertesia/common";
2
3
  import { setupActivity } from "../dsl/setup/ActivityContext.js";
3
4
 
@@ -18,8 +19,15 @@ export interface SetDocumentStatus extends DSLActivitySpec<SetDocumentStatusPara
18
19
  export async function setDocumentStatus(payload: DSLActivityExecutionPayload<SetDocumentStatusParams>) {
19
20
  const { client, params, objectId } = await setupActivity<SetDocumentStatusParams>(payload);
20
21
 
21
- const res = await client.objects.update(objectId, { status: params.status });
22
-
23
- return res.status;
24
-
22
+ try {
23
+ const res = await client.objects.update(objectId, { status: params.status });
24
+ return res.status;
25
+ } catch (err: any) {
26
+ // If document was deleted, nothing to update - log warning and continue
27
+ if (err.status === 404 || err.name === 'ZenoClientNotFoundError') {
28
+ log.warn(`Document ${objectId} not found - may have been deleted. Skipping status update to '${params.status}'`);
29
+ return undefined; // Signal that document wasn't found
30
+ }
31
+ throw err;
32
+ }
25
33
  }
@@ -7,6 +7,7 @@ import { expect, test, vi, describe } from "vitest";
7
7
  // Mock Temporal activity context
8
8
  vi.mock("@temporalio/activity", () => ({
9
9
  log: {
10
+ debug: vi.fn(),
10
11
  info: vi.fn(),
11
12
  warn: vi.fn(),
12
13
  error: vi.fn(),
@@ -24,7 +24,7 @@ export async function imageResizer(
24
24
  colorspaceCorrection: boolean = true,
25
25
  colorspace: 'RGB' | 'LAB' | 'LUV' | 'sigmoidal' = 'RGB'
26
26
  ): Promise<string> {
27
- log.info(`[image-resizer] Resizing image: ${inputPath} to max_hw: ${max_hw}, format: ${format}, progressive: ${progressive}, colorspaceCorrection: ${colorspaceCorrection ? colorspace : 'disabled'}`);
27
+ log.debug(`[image-resizer] Resizing image: ${inputPath} to max_hw: ${max_hw}, format: ${format}, progressive: ${progressive}, colorspaceCorrection: ${colorspaceCorrection ? colorspace : 'disabled'}`);
28
28
 
29
29
  const allowedFormats = ["jpg", "jpeg", "png", "webp"];
30
30
 
@@ -63,17 +63,17 @@ export async function imageResizer(
63
63
  const lowerFormat = format.toLowerCase();
64
64
  if (lowerFormat === "jpg" || lowerFormat === "jpeg") {
65
65
  conversionOption = "-interlace JPEG";
66
- log.info(`Enabling interlaced ${lowerFormat.toUpperCase()} format`);
66
+ log.debug(`Enabling interlaced ${lowerFormat.toUpperCase()} format`);
67
67
  } else if (lowerFormat === "png") {
68
68
  conversionOption = "-interlace PNG";
69
- log.info(`Enabling interlaced ${lowerFormat.toUpperCase()} format`);
69
+ log.debug(`Enabling interlaced ${lowerFormat.toUpperCase()} format`);
70
70
  } else if (lowerFormat === "gif") {
71
71
  conversionOption = "-interlace GIF";
72
- log.info(`Enabling interlaced ${lowerFormat.toUpperCase()} format`);
72
+ log.debug(`Enabling interlaced ${lowerFormat.toUpperCase()} format`);
73
73
  }
74
74
  }
75
75
 
76
- log.info(`Resizing image using ImageMagick: ${inputPath} -> ${outputPath}`);
76
+ log.debug(`Resizing image using ImageMagick: ${inputPath} -> ${outputPath}`);
77
77
 
78
78
  const command = `convert`
79
79
  let args = [inputPath];
@@ -92,26 +92,26 @@ export async function imageResizer(
92
92
  // Linear light, recommended default
93
93
  // Convert from sRGB to linear RGB for processing
94
94
  args.push("-colorspace", "RGB");
95
- log.info("Using linear RGB colorspace for resize processing");
95
+ log.debug("Using linear RGB colorspace for resize processing");
96
96
  break;
97
97
  case 'LAB':
98
98
  // Perceptual linear light
99
99
  // Use LAB colorspace which separates intensity from color
100
100
  // Better for avoiding color clipping and distortion
101
101
  args.push("-colorspace", "LAB");
102
- log.info("Using LAB colorspace for resize processing");
102
+ log.debug("Using LAB colorspace for resize processing");
103
103
  break;
104
104
  case 'LUV':
105
105
  // Perceptual linear light
106
106
  // Alternative to LAB with perceptually uniform color deltas
107
107
  args.push("-colorspace", "LUV");
108
- log.info("Using LUV colorspace for resize processing");
108
+ log.debug("Using LUV colorspace for resize processing");
109
109
  break;
110
110
  case 'sigmoidal':
111
111
  // Sigmoidal colorspace modification to reduce ringing artifacts
112
112
  args.push("-colorspace", "RGB");
113
113
  args.push("+sigmoidal-contrast", "6.5,50%");
114
- log.info("Using sigmoidal contrast modification for resize processing");
114
+ log.debug("Using sigmoidal contrast modification for resize processing");
115
115
  break;
116
116
  }
117
117
  }
@@ -144,7 +144,7 @@ export async function imageResizer(
144
144
  // Output path
145
145
  args.push(outputPath);
146
146
 
147
- log.info(`ImageMagick command: ${command} ${args.join(" ")}`);
147
+ log.debug(`ImageMagick command: ${command} ${args.join(" ")}`);
148
148
 
149
149
  const { stderr } = await execFile(command, args);
150
150
 
@@ -20,14 +20,15 @@ import {
20
20
  DSLWorkflowSpec,
21
21
  getDocumentIds,
22
22
  getTenantId,
23
- WorkflowExecutionPayload
23
+ WorkflowExecutionPayload,
24
+ WorkflowInputFile
24
25
  } from "@vertesia/common";
25
26
  import ms, { StringValue } from 'ms';
26
27
  import { HandleDslErrorParams } from "../activities/handleError.js";
27
28
  import * as activities from "../activities/index.js";
29
+ import { RateLimitParams } from "../activities/rateLimiter.js";
28
30
  import { WF_NON_RETRYABLE_ERRORS, WorkflowParamNotFoundError } from "../errors.js";
29
31
  import { Vars } from "./vars.js";
30
- import { RateLimitParams } from "../activities/rateLimiter.js";
31
32
 
32
33
 
33
34
  interface BaseActivityPayload extends WorkflowExecutionPayload {
@@ -49,6 +50,23 @@ export async function dslWorkflow(payload: DSLWorkflowExecutionPayload) {
49
50
  if (!definition) {
50
51
  throw new WorkflowParamNotFoundError("workflow");
51
52
  }
53
+
54
+ // Normalize input: convert legacy objectIds format to new input format
55
+ if (!payload.input && payload.objectIds) {
56
+ payload.input = {
57
+ inputType: 'objectIds',
58
+ objectIds: payload.objectIds
59
+ };
60
+ }
61
+
62
+ // Validate that workflow has input
63
+ if (!payload.input && !payload.objectIds) {
64
+ throw new WorkflowParamNotFoundError(
65
+ "input",
66
+ definition
67
+ );
68
+ }
69
+
52
70
  // the base payload will be used to create the activities payload
53
71
  const basePayload: BaseActivityPayload = {
54
72
  ...payload,
@@ -73,12 +91,33 @@ export async function dslWorkflow(payload: DSLWorkflowExecutionPayload) {
73
91
  });
74
92
  const defaultProxy = proxyActivities(defaultOptions);
75
93
  log.debug("Default activity proxy is ready");
76
- // merge default vars with the payload vars and add objectIds and objectId
94
+
95
+ // Merge default vars with the payload vars and add input variables
96
+ const inputType = payload.input?.inputType || 'objectIds';
97
+
98
+ // Extract objectIds and files based on input type
99
+ let objectIds: string[] = [];
100
+ let files: WorkflowInputFile[] = [];
101
+
102
+ if (payload.input) {
103
+ if (payload.input.inputType === 'objectIds') {
104
+ objectIds = payload.input.objectIds;
105
+ } else if (payload.input.inputType === 'files') {
106
+ files = payload.input.files;
107
+ }
108
+ }
109
+
77
110
  const vars = new Vars({
78
111
  ...definition.vars,
79
112
  ...payload.vars,
80
- objectIds: payload.objectIds || [],
81
- objectId: payload.objectIds ? payload.objectIds[0] : undefined
113
+ // Add input type variables
114
+ inputType,
115
+ // Add objectIds variables (for objectIds input or backward compatibility)
116
+ objectIds,
117
+ objectId: objectIds[0],
118
+ // Add files variables (for files input)
119
+ files,
120
+ file: files[0],
82
121
  });
83
122
 
84
123
  log.info("Executing workflow", { payload });
@@ -153,6 +192,10 @@ async function handleError(originalError: any, basePayload: BaseActivityPayload,
153
192
  }
154
193
 
155
194
  async function startChildWorkflow(step: DSLChildWorkflowStep, payload: DSLWorkflowExecutionPayload, vars: Vars, debug_mode?: boolean) {
195
+ if (step.condition && !vars.match(step.condition)) {
196
+ log.info("Child workflow skipped: condition not satisfied", { workflow: step.name, condition: step.condition });
197
+ return;
198
+ }
156
199
  const resolvedVars = vars.resolve();
157
200
  if (step.vars) {
158
201
  // copy user vars (from step definition) to the resolved vars, resolving any expressions
@@ -187,6 +230,10 @@ async function startChildWorkflow(step: DSLChildWorkflowStep, payload: DSLWorkfl
187
230
  }
188
231
 
189
232
  async function executeChildWorkflow(step: DSLChildWorkflowStep, payload: DSLWorkflowExecutionPayload, vars: Vars, debug_mode?: boolean) {
233
+ if (step.condition && !vars.match(step.condition)) {
234
+ log.info("Child workflow skipped: condition not satisfied", { workflow: step.name, condition: step.condition });
235
+ return;
236
+ }
190
237
  const resolvedVars = vars.resolve();
191
238
  if (step.vars) {
192
239
  // copy user vars (from step definition) to the resolved vars, resolving any expressions
@@ -282,9 +329,10 @@ async function runActivity(activity: DSLActivitySpec, basePayload: BaseActivityP
282
329
  log.info("Activity skipped: condition not satisfied", activity.condition);
283
330
  return;
284
331
  }
332
+
285
333
  const importParams = vars.createImportVars(activity.import);
286
334
  const executionPayload = dslActivityPayload(basePayload, activity, importParams);
287
- log.info("Executing activity: " + activity.name, { payload: executionPayload });
335
+ log.debug("Executing activity: " + activity.name, { payload: executionPayload });
288
336
 
289
337
  let proxy = defaultProxy;
290
338
  if (activity.options) {
@@ -316,7 +364,7 @@ async function runActivity(activity: DSLActivitySpec, basePayload: BaseActivityP
316
364
  ];
317
365
 
318
366
  if (activity.name && rateLimitedActivities.includes(activity.name)) {
319
- log.info(`Applying rate limit for activity ${activity.name}`);
367
+ log.debug(`Applying rate limit for activity ${activity.name}`);
320
368
  // Apply rate limiting logic here
321
369
  // Check rate limit first - loop until no delay
322
370
  const rateLimitParams = buildRateLimitParams(activity, executionPayload);
@@ -325,7 +373,7 @@ async function runActivity(activity: DSLActivitySpec, basePayload: BaseActivityP
325
373
  let rateLimitResult = await proxy.checkRateLimit(rateLimitPayload);
326
374
 
327
375
  while (rateLimitResult.delayMs > 0) {
328
- log.info(`Rate limit delay applied: ${rateLimitResult.delayMs}ms`);
376
+ log.debug(`Rate limit delay applied: ${rateLimitResult.delayMs}ms`);
329
377
  await sleep(rateLimitResult.delayMs);
330
378
 
331
379
  // Check again after sleeping
@@ -338,7 +386,7 @@ async function runActivity(activity: DSLActivitySpec, basePayload: BaseActivityP
338
386
  //TODO execute in parallel
339
387
  log.info("Parallel execution not yet implemented");
340
388
  } else {
341
- log.info("Executing activity: " + activity.name, { importParams });
389
+ log.debug("Executing activity: " + activity.name, { importParams });
342
390
  const result = await fn(executionPayload);
343
391
  if (activity.output) {
344
392
  vars.setValue(activity.output, result);