@vertesia/workflow 0.67.0 → 0.69.0

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 (52) hide show
  1. package/lib/cjs/activities/executeInteraction.js +3 -3
  2. package/lib/cjs/activities/executeInteraction.js.map +1 -1
  3. package/lib/cjs/activities/generateEmbeddings.js +0 -4
  4. package/lib/cjs/activities/generateEmbeddings.js.map +1 -1
  5. package/lib/cjs/activities/generateOrAssignContentType.js +0 -1
  6. package/lib/cjs/activities/generateOrAssignContentType.js.map +1 -1
  7. package/lib/cjs/activities/notifyWebhook.js +0 -1
  8. package/lib/cjs/activities/notifyWebhook.js.map +1 -1
  9. package/lib/cjs/activities/renditions/generateImageRendition.js +1 -5
  10. package/lib/cjs/activities/renditions/generateImageRendition.js.map +1 -1
  11. package/lib/cjs/conversion/image.js +63 -10
  12. package/lib/cjs/conversion/image.js.map +1 -1
  13. package/lib/cjs/dsl/dsl-workflow.js +6 -4
  14. package/lib/cjs/dsl/dsl-workflow.js.map +1 -1
  15. package/lib/cjs/system/notifyWebhookWorkflow.js +4 -5
  16. package/lib/cjs/system/notifyWebhookWorkflow.js.map +1 -1
  17. package/lib/esm/activities/executeInteraction.js +3 -3
  18. package/lib/esm/activities/executeInteraction.js.map +1 -1
  19. package/lib/esm/activities/generateEmbeddings.js +0 -4
  20. package/lib/esm/activities/generateEmbeddings.js.map +1 -1
  21. package/lib/esm/activities/generateOrAssignContentType.js +0 -1
  22. package/lib/esm/activities/generateOrAssignContentType.js.map +1 -1
  23. package/lib/esm/activities/notifyWebhook.js +0 -1
  24. package/lib/esm/activities/notifyWebhook.js.map +1 -1
  25. package/lib/esm/activities/renditions/generateImageRendition.js +1 -5
  26. package/lib/esm/activities/renditions/generateImageRendition.js.map +1 -1
  27. package/lib/esm/conversion/image.js +63 -10
  28. package/lib/esm/conversion/image.js.map +1 -1
  29. package/lib/esm/dsl/dsl-workflow.js +6 -4
  30. package/lib/esm/dsl/dsl-workflow.js.map +1 -1
  31. package/lib/esm/system/notifyWebhookWorkflow.js +4 -5
  32. package/lib/esm/system/notifyWebhookWorkflow.js.map +1 -1
  33. package/lib/types/activities/generateOrAssignContentType.d.ts.map +1 -1
  34. package/lib/types/activities/notifyWebhook.d.ts.map +1 -1
  35. package/lib/types/activities/renditions/generateImageRendition.d.ts.map +1 -1
  36. package/lib/types/conversion/image.d.ts +4 -2
  37. package/lib/types/conversion/image.d.ts.map +1 -1
  38. package/lib/types/dsl/dsl-workflow.d.ts.map +1 -1
  39. package/lib/types/system/notifyWebhookWorkflow.d.ts +5 -1
  40. package/lib/types/system/notifyWebhookWorkflow.d.ts.map +1 -1
  41. package/lib/workflows-bundle.js +793 -138
  42. package/package.json +5 -5
  43. package/src/activities/executeInteraction.ts +3 -3
  44. package/src/activities/generateEmbeddings.ts +0 -4
  45. package/src/activities/generateOrAssignContentType.ts +0 -1
  46. package/src/activities/notifyWebhook.ts +1 -2
  47. package/src/activities/renditions/generateImageRendition.ts +1 -6
  48. package/src/conversion/image.test.ts +3 -3
  49. package/src/conversion/image.ts +72 -10
  50. package/src/dsl/dsl-workflow.ts +6 -4
  51. package/src/dsl/workflow-exec-child.test.ts +73 -2
  52. package/src/system/notifyWebhookWorkflow.ts +11 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/workflow",
3
- "version": "0.67.0",
3
+ "version": "0.69.0",
4
4
  "type": "module",
5
5
  "description": "Composable prompts workflow dsl",
6
6
  "main": "./lib/esm/index.js",
@@ -50,10 +50,10 @@
50
50
  "tmp": "^0.2.3",
51
51
  "tmp-promise": "^3.0.3",
52
52
  "yaml": "^2.6.0",
53
- "@vertesia/client": "0.67.0",
54
- "@vertesia/api-fetch-client": "0.67.0",
55
- "@llumiverse/common": "0.20.0",
56
- "@vertesia/common": "0.67.0"
53
+ "@vertesia/api-fetch-client": "0.69.0",
54
+ "@vertesia/common": "0.69.0",
55
+ "@llumiverse/common": "0.21.0",
56
+ "@vertesia/client": "0.69.0"
57
57
  },
58
58
  "ts_dual_module": {
59
59
  "outDir": "lib",
@@ -177,7 +177,7 @@ export async function executeInteractionFromActivity(
177
177
  const userTags = params.tags;
178
178
  const info = activityInfo();
179
179
  const runId = info.workflowExecution.runId;
180
- let tags = ["workflow", `tmpRunId:${runId}`]; //TODO use wf:wfName
180
+ let tags = ["workflow"];
181
181
  if (userTags) {
182
182
  tags = tags.concat(userTags);
183
183
  }
@@ -191,9 +191,9 @@ export async function executeInteractionFromActivity(
191
191
  if (params.include_previous_error) {
192
192
  //retrieve last failed run if any
193
193
  if (info.attempt > 1) {
194
- log.info("Retrying, searching for previous run", { tags: ["tmpRunId:" + runId] });
194
+ log.info("Retrying, searching for previous run", { prev_run_id: runId });
195
195
  const payload: RunSearchPayload = {
196
- query: { tags: ["tmpRunId:" + info.workflowExecution.runId] },
196
+ query: { workflow_run_ids: [runId] },
197
197
  limit: 1,
198
198
  };
199
199
  const previousRun = await client.runs.search(payload).then((res) => {
@@ -158,9 +158,6 @@ async function generateTextEmbeddings(
158
158
  { document, client, type, config }: ExecuteGenerateEmbeddingsParams,
159
159
  parts?: DocPart[],
160
160
  ) {
161
- // if (!force && document.embeddings[type]?.etag === (document.text_etag ?? md5(document.text))) {
162
- // return { id: objectId, status: "skipped", message: "embeddings already generated" }
163
- // }
164
161
 
165
162
  if (!document) {
166
163
  return { status: "error", message: "document is null or undefined" };
@@ -401,7 +398,6 @@ async function generateImageEmbeddings({
401
398
 
402
399
  const resRnd = await client.store.objects.getRendition(document.id, {
403
400
  format: ImageRenditionFormat.jpeg,
404
- max_hw: 1024,
405
401
  generate_if_missing: true,
406
402
  sign_url: true,
407
403
  });
@@ -104,7 +104,6 @@ export async function generateOrAssignContentType(
104
104
  return undefined;
105
105
  }
106
106
  const res = await client.objects.getRendition(objectId, {
107
- max_hw: 1024,
108
107
  format: ImageRenditionFormat.jpeg,
109
108
  generate_if_missing: true,
110
109
  });
@@ -25,7 +25,6 @@ export async function notifyWebhook(payload: DSLActivityExecutionPayload<NotifyW
25
25
 
26
26
  const body = method === 'POST' ? JSON.stringify({
27
27
  ...requestPayload,
28
- ...params
29
28
  }) : undefined
30
29
 
31
30
  log.info(`Notifying webhook at ${target_url}`);
@@ -48,4 +47,4 @@ export async function notifyWebhook(payload: DSLActivityExecutionPayload<NotifyW
48
47
 
49
48
  return { status: res.status, message: res.statusText, url: res.url }
50
49
 
51
- }
50
+ }
@@ -27,7 +27,7 @@ export async function generateImageRendition(
27
27
  // Fix: Use maxHeightWidth if max_hw is not provided
28
28
  const params = {
29
29
  ...originParams,
30
- max_hw: originParams.max_hw || (originParams as any).maxHeightWidth || 1024, // Default to 1024 if both are missing
30
+ max_hw: originParams.max_hw || (originParams as any).maxHeightWidth || 1596, // Default to 1596 if both are missing
31
31
  format: originParams.format || (originParams as any).format_output || "png", // Default to png if format is missing
32
32
  };
33
33
 
@@ -49,11 +49,6 @@ export async function generateImageRendition(
49
49
  throw new DocumentNotFoundError(`Document ${objectId} not found`, [objectId]);
50
50
  }
51
51
 
52
- if (!inputObject.content?.source) {
53
- log.error(`Document ${objectId} has no etag or source`);
54
- throw new DocumentNotFoundError(`Document ${objectId} has no etag or source`, [objectId]);
55
- }
56
-
57
52
  if (!params.format) {
58
53
  log.error(`Format not found`);
59
54
  throw new WorkflowParamNotFoundError(`format`);
@@ -20,7 +20,7 @@ const execAsync = promisify(exec);
20
20
 
21
21
  describe("ImageMagick image resizing", () => {
22
22
  test("should resize an image to a maximum height or width using ImageMagick", async () => {
23
- const max_hw = 1024;
23
+ const max_hw = 1596;
24
24
  const format = "jpeg";
25
25
  const inputImagePath = path.join(__dirname, "../../fixtures", "cat-picture.jpg");
26
26
 
@@ -48,7 +48,7 @@ describe("ImageMagick image resizing", () => {
48
48
  });
49
49
 
50
50
  test("should throw an error for non-existent input file", async () => {
51
- const max_hw = 1024;
51
+ const max_hw = 1596;
52
52
  const format = "jpeg";
53
53
  const nonExistentPath = path.join(__dirname, "non-existent-image.jpg");
54
54
 
@@ -60,7 +60,7 @@ describe("ImageMagick image resizing", () => {
60
60
  });
61
61
 
62
62
  test("should throw error with empty format", async () => {
63
- const max_hw = 1024;
63
+ const max_hw = 1596;
64
64
  const format = "";
65
65
  const inputImagePath = path.join(__dirname, "../../fixtures", "cat-picture.jpg");
66
66
 
@@ -7,11 +7,13 @@ const execFile = promisify(execFileCallback);
7
7
 
8
8
  /**
9
9
  * Resizes an image to a maximum height or width using ImageMagick
10
- * with progressive loading when supported
10
+ * with progressive loading when supported and colorspace correction
11
11
  * @param inputPath Input file path
12
12
  * @param max_hw Maximum height or width
13
13
  * @param format Output format
14
14
  * @param progressive Enable progressive loading for supported formats (defaults to true)
15
+ * @param colorspaceCorrection Enable colorspace correction (defaults to true), not recommended for Q8 image magick.
16
+ * @param colorspace Colorspace to use for processing ('RGB', 'LAB', 'LUV', 'sigmoidal') (defaults to 'RGB')
15
17
  * @returns Path to the resized image
16
18
  */
17
19
  export async function imageResizer(
@@ -19,8 +21,10 @@ export async function imageResizer(
19
21
  max_hw: number,
20
22
  format: string,
21
23
  progressive: boolean = true,
24
+ colorspaceCorrection: boolean = true,
25
+ colorspace: 'RGB' | 'LAB' | 'LUV' | 'sigmoidal' = 'RGB'
22
26
  ): Promise<string> {
23
- log.info(`[image-resizer] Resizing image: ${inputPath} to max_hw: ${max_hw}, format: ${format}, progressive: ${progressive}`);
27
+ log.info(`[image-resizer] Resizing image: ${inputPath} to max_hw: ${max_hw}, format: ${format}, progressive: ${progressive}, colorspaceCorrection: ${colorspaceCorrection ? colorspace : 'disabled'}`);
24
28
 
25
29
  const allowedFormats = ["jpg", "jpeg", "png", "webp"];
26
30
 
@@ -72,13 +76,71 @@ export async function imageResizer(
72
76
  log.info(`Resizing image using ImageMagick: ${inputPath} -> ${outputPath}`);
73
77
 
74
78
  const command = `convert`
75
- const args = [
76
- inputPath,
77
- "-resize",
78
- `${max_hw}x${max_hw}>`,
79
- ...(conversionOption ? conversionOption.split(" ") : []),
80
- outputPath,
81
- ];
79
+ let args = [inputPath];
80
+
81
+ // https://usage.imagemagick.org/filter/nicolas/#downsample
82
+ // Add colorspace correction if enabled
83
+ if (colorspaceCorrection) {
84
+ switch (colorspace) {
85
+ case 'RGB':
86
+ // Linear light, recommended default
87
+ // Convert from sRGB to linear RGB for processing
88
+ args.push("-colorspace", "RGB");
89
+ log.info("Using linear RGB colorspace for resize processing");
90
+ break;
91
+ case 'LAB':
92
+ // Perceptual linear light
93
+ // Use LAB colorspace which separates intensity from color
94
+ // Better for avoiding color clipping and distortion
95
+ args.push("-colorspace", "LAB");
96
+ log.info("Using LAB colorspace for resize processing");
97
+ break;
98
+ case 'LUV':
99
+ // Perceptual linear light
100
+ // Alternative to LAB with perceptually uniform color deltas
101
+ args.push("-colorspace", "LUV");
102
+ log.info("Using LUV colorspace for resize processing");
103
+ break;
104
+ case 'sigmoidal':
105
+ // Sigmoidal colorspace modification to reduce ringing artifacts
106
+ args.push("-colorspace", "RGB");
107
+ args.push("+sigmoidal-contrast", "6.5,50%");
108
+ log.info("Using sigmoidal contrast modification for resize processing");
109
+ break;
110
+ }
111
+ }
112
+
113
+ // Add JPEG shrink-on-load optimization
114
+ args.push("-define", `jpeg:size=${max_hw * 3}x${max_hw * 3}`);
115
+
116
+ // Resize operation
117
+ args.push("-resize", `${max_hw}x${max_hw}>`);
118
+
119
+ // Restore colorspace after processing
120
+ if (colorspaceCorrection) {
121
+ switch (colorspace) {
122
+ case 'RGB':
123
+ case 'LAB':
124
+ case 'LUV':
125
+ // Convert back to sRGB for output
126
+ args.push("-colorspace", "sRGB");
127
+ break;
128
+ case 'sigmoidal':
129
+ // Restore from sigmoidal modification and convert to sRGB
130
+ args.push("-sigmoidal-contrast", "6.5,50%");
131
+ args.push("-colorspace", "sRGB");
132
+ break;
133
+ }
134
+ }
135
+
136
+ // Add progressive/interlace options
137
+ if (conversionOption) {
138
+ args.push(...conversionOption.split(" "));
139
+ }
140
+
141
+ // Output path
142
+ args.push(outputPath);
143
+
82
144
  log.info(`ImageMagick command: ${command} ${args.join(" ")}`);
83
145
 
84
146
  const { stderr } = await execFile(command, args);
@@ -100,4 +162,4 @@ export async function imageResizer(
100
162
  log.error(`Image conversion failed: ${errorMessage}`);
101
163
  throw new Error(`Image conversion failed: ${errorMessage}`);
102
164
  }
103
- }
165
+ }
@@ -151,8 +151,9 @@ async function handleError(originalError: any, basePayload: BaseActivityPayload,
151
151
  async function startChildWorkflow(step: DSLChildWorkflowStep, payload: DSLWorkflowExecutionPayload, vars: Vars, debug_mode?: boolean) {
152
152
  const resolvedVars = vars.resolve();
153
153
  if (step.vars) {
154
- // copy user vars (from step definition) to the resolved vars
155
- Object.assign(resolvedVars, step.vars);
154
+ // copy user vars (from step definition) to the resolved vars, resolving any expressions
155
+ const resolvedStepVars = vars.resolveParams(step.vars);
156
+ Object.assign(resolvedVars, resolvedStepVars);
156
157
  }
157
158
  if (debug_mode) {
158
159
  log.debug(`Workflow vars before starting child workflow ${step.name}`, { vars: resolvedVars });
@@ -182,8 +183,9 @@ async function startChildWorkflow(step: DSLChildWorkflowStep, payload: DSLWorkfl
182
183
  async function executeChildWorkflow(step: DSLChildWorkflowStep, payload: DSLWorkflowExecutionPayload, vars: Vars, debug_mode?: boolean) {
183
184
  const resolvedVars = vars.resolve();
184
185
  if (step.vars) {
185
- // copy user vars (from step definition) to the resolved vars
186
- Object.assign(resolvedVars, step.vars);
186
+ // copy user vars (from step definition) to the resolved vars, resolving any expressions
187
+ const resolvedStepVars = vars.resolveParams(step.vars);
188
+ Object.assign(resolvedVars, resolvedStepVars);
187
189
  }
188
190
  if (debug_mode) {
189
191
  log.debug(`Workflow vars before executing child workflow ${step.name}`, { vars: resolvedVars });
@@ -83,6 +83,35 @@ const steps2: DSLWorkflowStep[] = [
83
83
  }
84
84
  ]
85
85
 
86
+ const steps3: DSLWorkflowStep[] = [
87
+ {
88
+ type: 'activity',
89
+ name: 'sayHelloFromParent',
90
+ output: 'parent',
91
+ import: ["name"],
92
+ },
93
+ {
94
+ type: 'workflow',
95
+ name: 'dslWorkflow',
96
+ output: 'child',
97
+ spec: {
98
+ name: 'testChildWorkflow',
99
+ steps: childSteps,
100
+ vars: {}
101
+ },
102
+ vars: {
103
+ storeUrl: 'store:${objectIds[0]}',
104
+ name: '${name}'
105
+ }
106
+ },
107
+ {
108
+ type: 'activity',
109
+ name: 'prepareResult',
110
+ import: ["parent", "child"],
111
+ output: 'result',
112
+ }
113
+ ]
114
+
86
115
 
87
116
  // ========== test env setup ==========
88
117
 
@@ -128,7 +157,6 @@ describe('DSL Workflow with child workflows', () => {
128
157
  vars: {},
129
158
  account_id: '123',
130
159
  project_id: '123',
131
- timestamp: Date.now(),
132
160
  wf_rule_name: 'test',
133
161
  auth_token: 'test',
134
162
  config: {
@@ -173,7 +201,6 @@ describe('DSL Workflow with child workflows', () => {
173
201
  vars: {},
174
202
  account_id: '123',
175
203
  project_id: '123',
176
- timestamp: Date.now(),
177
204
  wf_rule_name: 'test',
178
205
  auth_token: 'test',
179
206
  config: {
@@ -198,4 +225,48 @@ describe('DSL Workflow with child workflows', () => {
198
225
  expect(result).toEqual([`Parent: Hello, ${name}!`, `DSL Child: Hello, ${name}!`]);
199
226
 
200
227
  });
228
+
229
+ test('execute DSL child workflow with variable resolution', async () => {
230
+ const { client, nativeConnection } = testEnv;
231
+ const taskQueue = 'test';
232
+
233
+ const name = 'Baz';
234
+
235
+ const worker = await Worker.create({
236
+ connection: nativeConnection,
237
+ taskQueue,
238
+ workflowsPath: new URL("./test/test-child-workflow.ts", import.meta.url).pathname,
239
+ activities: { sayHelloFromParent, prepareResult, sayHelloFromDSLChild },
240
+ });
241
+
242
+ const payload: DSLWorkflowExecutionPayload = {
243
+ event: ContentEventName.create,
244
+ objectIds: ['doc123'],
245
+ vars: {},
246
+ account_id: '123',
247
+ project_id: '123',
248
+ wf_rule_name: 'test',
249
+ auth_token: 'test',
250
+ config: {
251
+ studio_url: process.env.CP_STUDIO_URL || "http://localhost:8081",
252
+ store_url: process.env.CP_STORE_URL || "http://localhost:8082",
253
+ },
254
+ workflow: {
255
+ steps: steps3,
256
+ vars: {
257
+ name,
258
+ },
259
+ name: 'test',
260
+ }
261
+ }
262
+
263
+ let result = await worker.runUntil(client.workflow.execute(dslWorkflow, {
264
+ args: [payload],
265
+ workflowId: 'test-vars',
266
+ taskQueue,
267
+ }));
268
+
269
+ expect(result).toEqual([`Parent: Hello, ${name}!`, `DSL Child: Hello, ${name}!`]);
270
+
271
+ });
201
272
  });
@@ -1,6 +1,6 @@
1
1
 
2
2
  import { log } from "@temporalio/workflow";
3
- import { ContentEventName, WorkflowExecutionPayload } from "@vertesia/common";
3
+ import { WorkflowExecutionPayload } from "@vertesia/common";
4
4
  import * as activities from "../activities/notifyWebhook.js";
5
5
  import { dslProxyActivities } from "../dsl/dslProxyActivities.js";
6
6
  import { WF_NON_RETRYABLE_ERRORS } from "../errors.js";
@@ -18,13 +18,19 @@ const {
18
18
  },
19
19
  });
20
20
 
21
+ export interface NotifyWebhookWorfklowParams {
22
+ endpoints: string[],
23
+ data: Record<string, any>
24
+ }
25
+
21
26
 
22
- export async function notifyWebhookWorkflow(payload: WorkflowExecutionPayload): Promise<any> {
27
+ export async function notifyWebhookWorkflow(payload: WorkflowExecutionPayload<NotifyWebhookWorfklowParams>): Promise<any> {
23
28
 
24
29
  const { objectIds, vars } = payload;
25
30
  const notifications = [];
26
- const endpoints = vars?.webhooks || [];
27
- const eventName = vars.event || ContentEventName.workflow_finished;
31
+ const endpoints = vars.endpoints ?? (vars as any).webhooks ?? [];
32
+ const data = vars.data ?? (vars as any).webhook_data ?? undefined;
33
+ const eventName = payload.event;
28
34
 
29
35
  if (!endpoints.length) {
30
36
  log.info(`No webhooks to notify`);
@@ -38,8 +44,7 @@ export async function notifyWebhookWorkflow(payload: WorkflowExecutionPayload):
38
44
  payload: {
39
45
  object_ids: objectIds,
40
46
  event: eventName,
41
- data: vars.webhook_data ?? undefined,
42
- vars
47
+ data
43
48
  }
44
49
  }).then(res => {
45
50
  log.info(`Webhook notified at ${ep} with response code: ${res.status}`, { res });