@vertesia/workflow 0.51.0 → 0.52.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 (141) hide show
  1. package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +7 -1
  2. package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
  3. package/lib/cjs/activities/chunkDocument.js +39 -34
  4. package/lib/cjs/activities/chunkDocument.js.map +1 -1
  5. package/lib/cjs/activities/createDocumentFromOther.js +2 -2
  6. package/lib/cjs/activities/createDocumentFromOther.js.map +1 -1
  7. package/lib/cjs/activities/executeInteraction.js +11 -5
  8. package/lib/cjs/activities/executeInteraction.js.map +1 -1
  9. package/lib/cjs/activities/extractDocumentText.js +24 -6
  10. package/lib/cjs/activities/extractDocumentText.js.map +1 -1
  11. package/lib/cjs/activities/generateDocumentProperties.js +22 -4
  12. package/lib/cjs/activities/generateDocumentProperties.js.map +1 -1
  13. package/lib/cjs/activities/generateEmbeddings.js +58 -102
  14. package/lib/cjs/activities/generateEmbeddings.js.map +1 -1
  15. package/lib/cjs/activities/generateImageRendition.js +77 -34
  16. package/lib/cjs/activities/generateImageRendition.js.map +1 -1
  17. package/lib/cjs/activities/generateOrAssignContentType.js +3 -7
  18. package/lib/cjs/activities/generateOrAssignContentType.js.map +1 -1
  19. package/lib/cjs/activities/notifyWebhook.js.map +1 -1
  20. package/lib/cjs/conversion/image.js +80 -12
  21. package/lib/cjs/conversion/image.js.map +1 -1
  22. package/lib/cjs/dsl/setup/ActivityContext.js +30 -6
  23. package/lib/cjs/dsl/setup/ActivityContext.js.map +1 -1
  24. package/lib/cjs/dsl.js +1 -1
  25. package/lib/cjs/dsl.js.map +1 -1
  26. package/lib/cjs/errors.js +13 -1
  27. package/lib/cjs/errors.js.map +1 -1
  28. package/lib/cjs/iterative-generation/iterativeGenerationWorkflow.js +2 -1
  29. package/lib/cjs/iterative-generation/iterativeGenerationWorkflow.js.map +1 -1
  30. package/lib/cjs/system/notifyWebhookWorkflow.js +2 -1
  31. package/lib/cjs/system/notifyWebhookWorkflow.js.map +1 -1
  32. package/lib/cjs/system/recalculateEmbeddingsWorkflow.js +1 -1
  33. package/lib/cjs/system/recalculateEmbeddingsWorkflow.js.map +1 -1
  34. package/lib/cjs/utils/blobs.js +12 -6
  35. package/lib/cjs/utils/blobs.js.map +1 -1
  36. package/lib/cjs/utils/chunks.js +14 -0
  37. package/lib/cjs/utils/chunks.js.map +1 -0
  38. package/lib/cjs/utils/client.js +4 -3
  39. package/lib/cjs/utils/client.js.map +1 -1
  40. package/lib/cjs/utils/memory.js +2 -9
  41. package/lib/cjs/utils/memory.js.map +1 -1
  42. package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +7 -1
  43. package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
  44. package/lib/esm/activities/chunkDocument.js +39 -34
  45. package/lib/esm/activities/chunkDocument.js.map +1 -1
  46. package/lib/esm/activities/createDocumentFromOther.js +1 -1
  47. package/lib/esm/activities/createDocumentFromOther.js.map +1 -1
  48. package/lib/esm/activities/executeInteraction.js +11 -5
  49. package/lib/esm/activities/executeInteraction.js.map +1 -1
  50. package/lib/esm/activities/extractDocumentText.js +24 -6
  51. package/lib/esm/activities/extractDocumentText.js.map +1 -1
  52. package/lib/esm/activities/generateDocumentProperties.js +22 -4
  53. package/lib/esm/activities/generateDocumentProperties.js.map +1 -1
  54. package/lib/esm/activities/generateEmbeddings.js +58 -69
  55. package/lib/esm/activities/generateEmbeddings.js.map +1 -1
  56. package/lib/esm/activities/generateImageRendition.js +78 -35
  57. package/lib/esm/activities/generateImageRendition.js.map +1 -1
  58. package/lib/esm/activities/generateOrAssignContentType.js +3 -7
  59. package/lib/esm/activities/generateOrAssignContentType.js.map +1 -1
  60. package/lib/esm/activities/notifyWebhook.js.map +1 -1
  61. package/lib/esm/conversion/image.js +80 -12
  62. package/lib/esm/conversion/image.js.map +1 -1
  63. package/lib/esm/dsl/setup/ActivityContext.js +31 -7
  64. package/lib/esm/dsl/setup/ActivityContext.js.map +1 -1
  65. package/lib/esm/dsl.js +1 -1
  66. package/lib/esm/dsl.js.map +1 -1
  67. package/lib/esm/errors.js +11 -0
  68. package/lib/esm/errors.js.map +1 -1
  69. package/lib/esm/iterative-generation/iterativeGenerationWorkflow.js +2 -1
  70. package/lib/esm/iterative-generation/iterativeGenerationWorkflow.js.map +1 -1
  71. package/lib/esm/system/notifyWebhookWorkflow.js +2 -1
  72. package/lib/esm/system/notifyWebhookWorkflow.js.map +1 -1
  73. package/lib/esm/system/recalculateEmbeddingsWorkflow.js +2 -2
  74. package/lib/esm/system/recalculateEmbeddingsWorkflow.js.map +1 -1
  75. package/lib/esm/utils/blobs.js +12 -6
  76. package/lib/esm/utils/blobs.js.map +1 -1
  77. package/lib/esm/utils/chunks.js +9 -0
  78. package/lib/esm/utils/chunks.js.map +1 -0
  79. package/lib/esm/utils/client.js +4 -3
  80. package/lib/esm/utils/client.js.map +1 -1
  81. package/lib/esm/utils/memory.js +2 -7
  82. package/lib/esm/utils/memory.js.map +1 -1
  83. package/lib/types/activities/advanced/createOrUpdateDocumentFromInteractionRun.d.ts +10 -0
  84. package/lib/types/activities/advanced/createOrUpdateDocumentFromInteractionRun.d.ts.map +1 -1
  85. package/lib/types/activities/chunkDocument.d.ts +15 -0
  86. package/lib/types/activities/chunkDocument.d.ts.map +1 -1
  87. package/lib/types/activities/createDocumentFromOther.d.ts.map +1 -1
  88. package/lib/types/activities/executeInteraction.d.ts +14 -3
  89. package/lib/types/activities/executeInteraction.d.ts.map +1 -1
  90. package/lib/types/activities/generateDocumentProperties.d.ts +1 -1
  91. package/lib/types/activities/generateDocumentProperties.d.ts.map +1 -1
  92. package/lib/types/activities/generateEmbeddings.d.ts +21 -17
  93. package/lib/types/activities/generateEmbeddings.d.ts.map +1 -1
  94. package/lib/types/activities/generateImageRendition.d.ts +3 -5
  95. package/lib/types/activities/generateImageRendition.d.ts.map +1 -1
  96. package/lib/types/activities/generateOrAssignContentType.d.ts.map +1 -1
  97. package/lib/types/activities/notifyWebhook.d.ts +1 -2
  98. package/lib/types/activities/notifyWebhook.d.ts.map +1 -1
  99. package/lib/types/conversion/image.d.ts +8 -6
  100. package/lib/types/conversion/image.d.ts.map +1 -1
  101. package/lib/types/dsl/setup/ActivityContext.d.ts +3 -0
  102. package/lib/types/dsl/setup/ActivityContext.d.ts.map +1 -1
  103. package/lib/types/dsl.d.ts +1 -1
  104. package/lib/types/dsl.d.ts.map +1 -1
  105. package/lib/types/errors.d.ts +6 -0
  106. package/lib/types/errors.d.ts.map +1 -1
  107. package/lib/types/iterative-generation/iterativeGenerationWorkflow.d.ts.map +1 -1
  108. package/lib/types/system/notifyWebhookWorkflow.d.ts.map +1 -1
  109. package/lib/types/system/recalculateEmbeddingsWorkflow.d.ts +2 -17
  110. package/lib/types/system/recalculateEmbeddingsWorkflow.d.ts.map +1 -1
  111. package/lib/types/utils/blobs.d.ts.map +1 -1
  112. package/lib/types/utils/chunks.d.ts +9 -0
  113. package/lib/types/utils/chunks.d.ts.map +1 -0
  114. package/lib/types/utils/client.d.ts.map +1 -1
  115. package/lib/types/utils/memory.d.ts +1 -5
  116. package/lib/types/utils/memory.d.ts.map +1 -1
  117. package/lib/workflows-bundle.js +15394 -14602
  118. package/package.json +8 -6
  119. package/src/activities/advanced/createOrUpdateDocumentFromInteractionRun.ts +20 -1
  120. package/src/activities/chunkDocument.ts +62 -42
  121. package/src/activities/createDocumentFromOther.ts +1 -1
  122. package/src/activities/executeInteraction.ts +27 -9
  123. package/src/activities/extractDocumentText.ts +28 -7
  124. package/src/activities/generateDocumentProperties.ts +37 -16
  125. package/src/activities/generateEmbeddings.ts +91 -79
  126. package/src/activities/generateImageRendition.ts +100 -53
  127. package/src/activities/generateOrAssignContentType.ts +5 -11
  128. package/src/activities/notifyWebhook.ts +2 -2
  129. package/src/conversion/image.test.ts +110 -18
  130. package/src/conversion/image.ts +90 -15
  131. package/src/conversion/pandoc.test.ts +7 -5
  132. package/src/dsl/setup/ActivityContext.ts +57 -16
  133. package/src/dsl.ts +1 -1
  134. package/src/errors.ts +27 -6
  135. package/src/iterative-generation/iterativeGenerationWorkflow.ts +2 -1
  136. package/src/system/notifyWebhookWorkflow.ts +2 -1
  137. package/src/system/recalculateEmbeddingsWorkflow.ts +2 -2
  138. package/src/utils/blobs.ts +11 -6
  139. package/src/utils/chunks.ts +17 -0
  140. package/src/utils/client.ts +4 -3
  141. package/src/utils/memory.ts +3 -8
@@ -1,26 +1,118 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import sharp from 'sharp';
4
- import { expect, test } from 'vitest';
5
- import { imageResizer } from '../conversion/image';
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { exec } from "child_process";
4
+ import { promisify } from "util";
5
+ import { expect, test, vi, describe } from "vitest";
6
6
 
7
+ // Mock Temporal activity context
8
+ vi.mock("@temporalio/activity", () => ({
9
+ log: {
10
+ info: vi.fn(),
11
+ warn: vi.fn(),
12
+ error: vi.fn(),
13
+ },
14
+ }));
7
15
 
8
- test('should resize an image to a maximum height or width', async () => {
9
- const max_hw = 1024;
10
- const format: keyof sharp.FormatEnum = 'jpeg';
11
- const imageFile = fs.readFileSync(path.join(__dirname, '../../fixtures', 'cat-picture.jpg'));
16
+ // Import after mocking
17
+ import { imageResizer } from "../conversion/image";
12
18
 
13
- const resizer = imageResizer(max_hw, format);
19
+ const execAsync = promisify(exec);
14
20
 
15
- const resized = sharp(imageFile).pipe(resizer);
16
- const buffer = await resized.toBuffer();
17
- const metadata = await sharp(buffer).metadata();
21
+ describe("ImageMagick image resizing", () => {
22
+ test("should resize an image to a maximum height or width using ImageMagick", async () => {
23
+ const max_hw = 1024;
24
+ const format = "jpeg";
25
+ const inputImagePath = path.join(__dirname, "../../fixtures", "cat-picture.jpg");
18
26
 
19
- console.log(metadata);
20
- resized.toFile('/tmp/cat-picture.jpg');
27
+ // Make sure the input file exists
28
+ expect(fs.existsSync(inputImagePath)).toBe(true);
21
29
 
22
- expect(metadata.width).to.be.lessThanOrEqual(max_hw);
23
- expect(metadata.height).to.be.lessThanOrEqual(max_hw);
24
- expect(metadata.format).to.equal(format);
30
+ // Call the imageResizer function with a file path
31
+ const resizedImagePath = await imageResizer(inputImagePath, max_hw, format);
25
32
 
33
+ // Make sure the output file exists
34
+ expect(fs.existsSync(resizedImagePath)).toBe(true);
35
+
36
+ // Use ImageMagick identify to get metadata about the resized image
37
+ const { stdout } = await execAsync(`identify -format "%w %h %m" "${resizedImagePath}"`);
38
+ const [width, height, imageFormat] = stdout.trim().split(" ");
39
+
40
+ console.log({ width, height, imageFormat });
41
+
42
+ // Check dimensions
43
+ expect(parseInt(width)).to.be.lessThanOrEqual(max_hw);
44
+ expect(parseInt(height)).to.be.lessThanOrEqual(max_hw);
45
+
46
+ // Check format (JPEG)
47
+ expect(imageFormat.toLowerCase()).to.equal("jpeg");
48
+ });
49
+
50
+ test("should throw an error for non-existent input file", async () => {
51
+ const max_hw = 1024;
52
+ const format = "jpeg";
53
+ const nonExistentPath = path.join(__dirname, "non-existent-image.jpg");
54
+
55
+ // Verify file doesn't exist
56
+ expect(fs.existsSync(nonExistentPath)).toBe(false);
57
+
58
+ // Expect the function to throw an error
59
+ await expect(imageResizer(nonExistentPath, max_hw, format)).rejects.toThrow("Input file does not exist");
60
+ });
61
+
62
+ test("should throw error with empty format", async () => {
63
+ const max_hw = 1024;
64
+ const format = "";
65
+ const inputImagePath = path.join(__dirname, "../../fixtures", "cat-picture.jpg");
66
+
67
+ // Test for empty format validation
68
+ await expect(imageResizer(inputImagePath, max_hw, format)).rejects.toThrow("Invalid format");
69
+ });
70
+
71
+ test("should create progressive/interlaced image when enabled", async () => {
72
+ const max_hw = 800;
73
+ const format = "jpeg";
74
+ const inputImagePath = path.join(__dirname, "../../fixtures", "cat-picture.jpg");
75
+
76
+ // Make sure the input file exists
77
+ expect(fs.existsSync(inputImagePath)).toBe(true);
78
+
79
+ // Call the imageResizer function with progressive=true
80
+ const resizedImagePath = await imageResizer(inputImagePath, max_hw, format, true);
81
+
82
+ // Make sure the output file exists
83
+ expect(fs.existsSync(resizedImagePath)).toBe(true);
84
+
85
+ // Use ImageMagick identify to check if the image is interlaced
86
+ const { stdout } = await execAsync(`identify -format "%[interlace]" "${resizedImagePath}"`);
87
+ const interlaceMode = stdout.trim();
88
+
89
+ console.log({ interlaceMode });
90
+
91
+ // Check that interlace is enabled (should be 'JPEG' or 'Line' for progressive JPEG)
92
+ expect(["JPEG", "Line", "Plane"]).to.include(interlaceMode);
93
+ });
94
+
95
+ test("should create non-interlaced image when progressive is disabled", async () => {
96
+ const max_hw = 800;
97
+ const format = "jpeg";
98
+ const inputImagePath = path.join(__dirname, "../../fixtures", "cat-picture.jpg");
99
+
100
+ // Make sure the input file exists
101
+ expect(fs.existsSync(inputImagePath)).toBe(true);
102
+
103
+ // Call the imageResizer function with progressive=false
104
+ const resizedImagePath = await imageResizer(inputImagePath, max_hw, format, false);
105
+
106
+ // Make sure the output file exists
107
+ expect(fs.existsSync(resizedImagePath)).toBe(true);
108
+
109
+ // Use ImageMagick identify to check if the image is interlaced
110
+ const { stdout } = await execAsync(`identify -format "%[interlace]" "${resizedImagePath}"`);
111
+ const interlaceMode = stdout.trim().toLowerCase();
112
+
113
+ console.log({ interlaceMode });
114
+
115
+ // Check that interlace is disabled (should be 'none' or empty string)
116
+ expect(["none", ""]).to.include(interlaceMode);
117
+ });
26
118
  });
@@ -1,22 +1,97 @@
1
-
2
- import sharp from "sharp";
3
-
1
+ import { log } from "@temporalio/activity";
2
+ import { execFile as execFileCallback } from "child_process";
3
+ import fs from "fs";
4
+ import { file } from "tmp-promise";
5
+ import { promisify } from "util";
6
+ const execFile = promisify(execFileCallback);
4
7
 
5
8
  /**
6
- * Resizes an image to a maximum height or width
7
- * @param max_hw
8
- * @param format
9
- * @returns
9
+ * Resizes an image to a maximum height or width using ImageMagick
10
+ * with progressive loading when supported
11
+ * @param inputPath Input file path
12
+ * @param max_hw Maximum height or width
13
+ * @param format Output format
14
+ * @param progressive Enable progressive loading for supported formats (defaults to true)
15
+ * @returns Path to the resized image
10
16
  */
11
- export function imageResizer(max_hw: number, format: keyof sharp.FormatEnum) {
17
+ export async function imageResizer(
18
+ inputPath: string,
19
+ max_hw: number,
20
+ format: string,
21
+ progressive: boolean = true,
22
+ ): Promise<string> {
23
+ const allowedFormats = ["jpg", "jpeg", "png", "webp"];
12
24
 
13
- return sharp().resize({
14
- width: max_hw,
15
- height: max_hw,
16
- fit: sharp.fit.inside,
17
- withoutEnlargement: true,
25
+ if (!format || format.trim() === "") {
26
+ throw new Error(`Invalid format: ${format}.Supported : ${allowedFormats.join(", ")}`);
27
+ }
18
28
 
19
- }).toFormat(format);
29
+ //check that max_hw is valid
30
+ if (!Number.isInteger(max_hw) || max_hw <= 0) {
31
+ throw new Error(`Invalid max_hw value: ${max_hw}`);
32
+ }
20
33
 
21
- }
34
+ //check that inputPath exists
35
+ if (!fs.existsSync(inputPath)) {
36
+ throw new Error(`Input file does not exist: ${inputPath}`);
37
+ }
38
+
39
+ // Create a temporary file
40
+ const { path: outputPath, cleanup } = await file({ postfix: `.${format}` });
41
+ try {
42
+ // Check if input file exists
43
+ if (!fs.existsSync(inputPath)) {
44
+ throw new Error(`Input file does not exist: ${inputPath}`);
45
+ }
46
+ // Validate max_hw
47
+ if (!Number.isInteger(max_hw) || max_hw <= 0) {
48
+ throw new Error(`Invalid max_hw value: ${max_hw}`);
49
+ }
50
+
51
+ // Progressive loading options
52
+ let conversionOption = "";
53
+
54
+ // Only add progressive option for formats that support it
55
+ if (progressive) {
56
+ // JPEG and some other formats support progressive loading
57
+ const lowerFormat = format.toLowerCase();
58
+ if (lowerFormat === "jpg" || lowerFormat === "jpeg") {
59
+ conversionOption = "-interlace JPEG";
60
+ log.info(`Enabling interlaced ${lowerFormat.toUpperCase()} format`);
61
+ } else if (lowerFormat === "png") {
62
+ conversionOption = "-interlace PNG";
63
+ log.info(`Enabling interlaced ${lowerFormat.toUpperCase()} format`);
64
+ } else if (lowerFormat === "gif") {
65
+ conversionOption = "-interlace GIF";
66
+ log.info(`Enabling interlaced ${lowerFormat.toUpperCase()} format`);
67
+ }
68
+ }
22
69
 
70
+ log.info(`Resizing image using ImageMagick: ${inputPath} -> ${outputPath}`);
71
+
72
+ const { stderr } = await execFile("convert", [
73
+ inputPath,
74
+ "-resize",
75
+ `${max_hw}x${max_hw}>`,
76
+ ...(conversionOption ? conversionOption.split(" ") : []),
77
+ outputPath,
78
+ ]);
79
+
80
+ if (stderr) {
81
+ log.warn(`ImageMagick warning: ${stderr}`);
82
+ }
83
+
84
+ // Verify output exists and has content
85
+ if (!fs.existsSync(outputPath) || fs.statSync(outputPath).size === 0) {
86
+ throw new Error(`ImageMagick conversion failed: output file not created or empty`);
87
+ }
88
+
89
+ return outputPath;
90
+ } catch (error) {
91
+ // Clean up the temporary file
92
+ await cleanup();
93
+ const errorMessage = error instanceof Error ? error.message : String(error);
94
+ log.error(`Image conversion failed: ${errorMessage}`);
95
+ throw new Error(`Image conversion failed: ${errorMessage}`);
96
+ }
97
+ }
@@ -9,14 +9,16 @@ let testEnv: TestWorkflowEnvironment;
9
9
  let activityContext: MockActivityEnvironment;
10
10
 
11
11
  beforeAll(async () => {
12
- testEnv = await TestWorkflowEnvironment.createLocal();
13
- activityContext = new MockActivityEnvironment();
12
+ testEnv = await TestWorkflowEnvironment.createLocal();
13
+ activityContext = new MockActivityEnvironment();
14
14
  });
15
15
 
16
16
 
17
17
  // Add more test cases for other file types (ODT, DOCX) if needed
18
18
  test('should convert docx to markdown', async () => {
19
- const docx = fs.readFileSync(path.join(__dirname, '../../fixtures', 'us-ciia.docx'));
20
- const result = await activityContext.run(manyToMarkdown, Buffer.from(docx), 'docx');
21
- expect(result).to.include('confidential');
19
+ const filepath = path.join(__dirname, '../../fixtures', 'us-ciia.docx');
20
+ console.log("Converting file from", filepath);
21
+ const docx = fs.readFileSync(filepath);
22
+ const result = await activityContext.run(manyToMarkdown, Buffer.from(docx), 'docx');
23
+ expect(result).to.include('confidential');
22
24
  });
@@ -1,6 +1,11 @@
1
- import { log } from "@temporalio/activity";
1
+ import { log, activityInfo } from "@temporalio/activity";
2
2
  import { VertesiaClient } from "@vertesia/client";
3
- import { DSLActivityExecutionPayload, DSLWorkflowExecutionPayload, Project, WorkflowExecutionPayload } from "@vertesia/common";
3
+ import {
4
+ DSLActivityExecutionPayload,
5
+ DSLWorkflowExecutionPayload,
6
+ Project,
7
+ WorkflowExecutionPayload,
8
+ } from "@vertesia/common";
4
9
  import { NoDocumentFound, WorkflowParamNotFound } from "../../errors.js";
5
10
  import { getProjectFromToken } from "../../utils/auth.js";
6
11
  import { getVertesiaClient } from "../../utils/client.js";
@@ -8,7 +13,6 @@ import { Vars } from "../vars.js";
8
13
  import { getFetchProvider, registerFetchProviderFactory } from "./fetch/index.js";
9
14
  import { DocumentProvider, DocumentTypeProvider, InteractionRunProvider } from "./fetch/providers.js";
10
15
 
11
-
12
16
  registerFetchProviderFactory(DocumentProvider.ID, DocumentProvider.factory);
13
17
  registerFetchProviderFactory(DocumentTypeProvider.ID, DocumentTypeProvider.factory);
14
18
  registerFetchProviderFactory(InteractionRunProvider.ID, InteractionRunProvider.factory);
@@ -17,7 +21,11 @@ export class ActivityContext<ParamsT extends Record<string, any>> {
17
21
  client: VertesiaClient;
18
22
  _project?: Promise<Project | undefined>;
19
23
 
20
- constructor(public payload: DSLActivityExecutionPayload<ParamsT>, client: VertesiaClient, public params: ParamsT) {
24
+ constructor(
25
+ public payload: DSLActivityExecutionPayload<ParamsT>,
26
+ client: VertesiaClient,
27
+ public params: ParamsT,
28
+ ) {
21
29
  this.client = client;
22
30
  this.fetchProject = this.fetchProject.bind(this);
23
31
  }
@@ -29,24 +37,54 @@ export class ActivityContext<ParamsT extends Record<string, any>> {
29
37
  get objectId() {
30
38
  const objectId = this.payload.objectIds && this.payload.objectIds[0];
31
39
  if (!objectId) {
32
- log.error('No objectId found in payload');
33
- throw new WorkflowParamNotFound('objectIds[0]', (this.payload as WorkflowExecutionPayload as DSLWorkflowExecutionPayload).workflow);
40
+ log.error("No objectId found in payload");
41
+ throw new WorkflowParamNotFound(
42
+ "objectIds[0]",
43
+ (this.payload as WorkflowExecutionPayload as DSLWorkflowExecutionPayload).workflow,
44
+ );
34
45
  }
35
46
  return objectId;
36
47
  }
37
48
 
49
+ get activityInfo() {
50
+ return activityInfo();
51
+ }
52
+
53
+ get runId() {
54
+ const runId = activityInfo().workflowExecution.runId;
55
+ if (!runId) {
56
+ log.error("No runId found in activityInfo");
57
+ throw new WorkflowParamNotFound(
58
+ "runId",
59
+ (this.payload as WorkflowExecutionPayload as DSLWorkflowExecutionPayload).workflow,
60
+ );
61
+ }
62
+ return runId;
63
+ }
64
+
65
+ get workflowId() {
66
+ const workflowId = activityInfo().workflowExecution.workflowId;
67
+ if (!workflowId) {
68
+ log.error("No workflowId found in activityInfo");
69
+ throw new WorkflowParamNotFound(
70
+ "workflowId",
71
+ (this.payload as WorkflowExecutionPayload as DSLWorkflowExecutionPayload).workflow,
72
+ );
73
+ }
74
+ return workflowId;
75
+ }
76
+
38
77
  fetchProject() {
39
78
  if (!this._project) {
40
79
  this._project = _fetchProject(this.client, this.payload);
41
80
  }
42
81
  return this._project;
43
82
  }
44
-
45
83
  }
46
84
 
47
-
48
- export async function setupActivity<ParamsT extends Record<string, any>>(payload: DSLActivityExecutionPayload<ParamsT>) {
49
-
85
+ export async function setupActivity<ParamsT extends Record<string, any>>(
86
+ payload: DSLActivityExecutionPayload<ParamsT>,
87
+ ) {
50
88
  const isDebugMode = !!payload.debug_mode;
51
89
 
52
90
  const vars = new Vars({
@@ -56,13 +94,17 @@ export async function setupActivity<ParamsT extends Record<string, any>>(payload
56
94
 
57
95
  //}
58
96
  if (isDebugMode) {
59
- log.info(`Setting up activity ${payload.activity.name}`, { config: payload.config, activity: payload.activity, params: payload.params, vars });
97
+ log.info(`Setting up activity ${payload.activity.name}`, {
98
+ config: payload.config,
99
+ activity: payload.activity,
100
+ params: payload.params,
101
+ vars,
102
+ });
60
103
  }
61
104
 
62
105
  const client = getVertesiaClient(payload);
63
106
  const fetchSpecs = payload.activity.fetch;
64
107
  if (fetchSpecs) {
65
-
66
108
  const keys = Object.keys(fetchSpecs);
67
109
  if (keys.length > 0) {
68
110
  // create a new Vars instance to store the fetched data
@@ -74,7 +116,7 @@ export async function setupActivity<ParamsT extends Record<string, any>>(payload
74
116
  fetchSpec = { ...fetchSpec, query };
75
117
  }
76
118
 
77
- const provider = await getFetchProvider(client, fetchSpec);
119
+ const provider = getFetchProvider(client, fetchSpec);
78
120
 
79
121
  log.info(`Fetching data for ${key} with provider ${provider.name}`, { fetchSpec });
80
122
  const result = await provider.fetch(fetchSpec);
@@ -84,7 +126,7 @@ export async function setupActivity<ParamsT extends Record<string, any>>(payload
84
126
  } else {
85
127
  vars.setValue(key, result);
86
128
  }
87
- } else if (fetchSpec.on_not_found === 'throw') {
129
+ } else if (fetchSpec.on_not_found === "throw") {
88
130
  throw new NoDocumentFound("No documents found for: " + JSON.stringify(fetchSpec));
89
131
  } else {
90
132
  vars.setValue(key, null);
@@ -94,12 +136,11 @@ export async function setupActivity<ParamsT extends Record<string, any>>(payload
94
136
  }
95
137
 
96
138
  const params = vars.resolve() as ParamsT;
97
- log.info(`Activity ${payload.activity.name} setup complete`, { params });
139
+ log.info(`Activity ${payload.activity.name} setup complete`);
98
140
 
99
141
  return new ActivityContext<ParamsT>(payload, client, params);
100
142
  }
101
143
 
102
-
103
144
  async function _fetchProject(client: VertesiaClient, payload: WorkflowExecutionPayload) {
104
145
  const project = await getProjectFromToken(payload.auth_token);
105
146
  return project ? await client.projects.retrieve(project.id) : undefined;
package/src/dsl.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from "./dsl/dslProxyActivities.js";
2
+ export * from "./errors.js";
2
3
  export * from "./result-types.js";
3
- export * from "./errors.js";
package/src/errors.ts CHANGED
@@ -1,24 +1,45 @@
1
1
  import { DSLActivitySpec, DSLWorkflowSpec } from "@vertesia/common";
2
2
 
3
-
4
3
  export class NoDocumentFound extends Error {
5
- constructor(message: string, public ids?: string[]) {
4
+ constructor(
5
+ message: string,
6
+ public ids?: string[],
7
+ ) {
6
8
  super(message);
7
9
  this.name = "NoDocumentFound";
8
- this.ids = ids
10
+ this.ids = ids;
9
11
  }
10
12
  }
11
13
 
12
14
  export class ActivityParamNotFound extends Error {
13
- constructor(public paramName: string, public activity: DSLActivitySpec) {
15
+ constructor(
16
+ public paramName: string,
17
+ public activity: DSLActivitySpec,
18
+ ) {
14
19
  super(`Required parameter ${paramName} not found in activity ${activity.name}`);
15
20
  this.name = "ActivityParamNotFound";
16
21
  }
17
22
  }
18
23
 
24
+ export class ActivityParamInvalid extends Error {
25
+ constructor(
26
+ public paramName: string,
27
+ public activity: DSLActivitySpec,
28
+ reason?: string,
29
+ ) {
30
+ super(`${paramName} in activity ${activity.name} is invalid${reason ? ` ${reason}` : ""}`);
31
+ this.name = "ActivityParamInvalid";
32
+ }
33
+ }
34
+
19
35
  export class WorkflowParamNotFound extends Error {
20
- constructor(public paramName: string, public workflow?: DSLWorkflowSpec) {
36
+ constructor(
37
+ public paramName: string,
38
+ public workflow?: DSLWorkflowSpec,
39
+ ) {
21
40
  super(`Required parameter ${paramName} not found in workflow ${workflow?.name}`);
22
41
  this.name = "WorkflowParamNotFound";
23
42
  }
24
- }
43
+ }
44
+
45
+ export const WF_NON_RETRYABLE_ERRORS = ["NoDocumentFound", "ActivityParamNotFound", "WorkflowParamNotFound"];
@@ -1,6 +1,7 @@
1
1
  import { WorkflowExecutionPayload } from "@vertesia/common";
2
2
 
3
3
  import { log, proxyActivities } from "@temporalio/workflow";
4
+ import { WF_NON_RETRYABLE_ERRORS } from "../errors.js";
4
5
  import * as activities from "./activities/index.js";
5
6
  import { IterativeGenerationPayload, PartIndex, SECTION_ID_PLACEHOLDER } from "./types.js";
6
7
 
@@ -16,7 +17,7 @@ const {
16
17
  backoffCoefficient: 2,
17
18
  maximumAttempts: 20,
18
19
  maximumInterval: 100 * 30 * 1000, //ms
19
- nonRetryableErrorTypes: [],
20
+ nonRetryableErrorTypes: WF_NON_RETRYABLE_ERRORS,
20
21
  },
21
22
  });
22
23
 
@@ -3,6 +3,7 @@ import { log } from "@temporalio/workflow";
3
3
  import { ContentEventName, WorkflowExecutionPayload } from "@vertesia/common";
4
4
  import * as activities from "../activities/notifyWebhook.js";
5
5
  import { dslProxyActivities } from "../dsl/dslProxyActivities.js";
6
+ import { WF_NON_RETRYABLE_ERRORS } from "../errors.js";
6
7
 
7
8
  const {
8
9
  notifyWebhook
@@ -13,7 +14,7 @@ const {
13
14
  backoffCoefficient: 2,
14
15
  maximumAttempts: 5,
15
16
  maximumInterval: 100 * 30 * 1000, //ms
16
- nonRetryableErrorTypes: [],
17
+ nonRetryableErrorTypes: WF_NON_RETRYABLE_ERRORS,
17
18
  },
18
19
  });
19
20
 
@@ -2,7 +2,7 @@
2
2
  import { SupportedEmbeddingTypes, WorkflowExecutionPayload } from "@vertesia/common";
3
3
  import * as activities from "../activities/index-dsl.js";
4
4
  import { dslProxyActivities } from "../dsl/dslProxyActivities.js";
5
- import { NoDocumentFound } from "../errors.js";
5
+ import { WF_NON_RETRYABLE_ERRORS } from "../errors.js";
6
6
 
7
7
  const {
8
8
  generateEmbeddings,
@@ -13,7 +13,7 @@ const {
13
13
  backoffCoefficient: 2,
14
14
  maximumAttempts: 10,
15
15
  maximumInterval: 100 * 30 * 1000, //ms
16
- nonRetryableErrorTypes: [NoDocumentFound.name],
16
+ nonRetryableErrorTypes: WF_NON_RETRYABLE_ERRORS,
17
17
  },
18
18
  });
19
19
 
@@ -10,7 +10,12 @@ export async function fetchBlobAsStream(client: VertesiaClient, blobUri: string)
10
10
  try {
11
11
  return await client.files.downloadFile(blobUri);
12
12
  } catch (err: any) {
13
- throw new NoDocumentFound(`Failed to download blob ${blobUri}: ${err.message}`, []);
13
+ if (err.message.includes("not found")) {
14
+ //TODO improve error handling with a fetch fail error class in the client
15
+ throw new NoDocumentFound(`Failed to download blob ${blobUri}: ${err.message}`, []);
16
+ } else {
17
+ throw new Error(`Failed to download blob ${blobUri}: ${err.message}`);
18
+ }
14
19
  }
15
20
  }
16
21
  export async function fetchBlobAsBuffer(client: VertesiaClient, blobUri: string): Promise<Buffer> {
@@ -24,7 +29,7 @@ export async function fetchBlobAsBuffer(client: VertesiaClient, blobUri: string)
24
29
 
25
30
  export async function fetchBlobAsBase64(client: VertesiaClient, blobUri: string): Promise<string> {
26
31
  const buffer = await fetchBlobAsBuffer(client, blobUri);
27
- return buffer.toString('base64');
32
+ return buffer.toString("base64");
28
33
  }
29
34
 
30
35
  export async function saveBlobToFile(client: VertesiaClient, blobUri: string, toFile: string): Promise<void> {
@@ -36,8 +41,8 @@ export async function saveBlobToFile(client: VertesiaClient, blobUri: string, to
36
41
  export async function saveBlobToTempFile(client: VertesiaClient, blobUri: string, fileExt?: string): Promise<string> {
37
42
  const tmpFile = tmp.fileSync({
38
43
  prefix: "vertesia-activity-",
39
- postfix: "." + fileExt,
40
- discardDescriptor: true
44
+ postfix: fileExt ? "." + fileExt : "",
45
+ discardDescriptor: true,
41
46
  });
42
47
  await saveBlobToFile(client, blobUri, tmpFile.name);
43
48
  return tmpFile.name;
@@ -47,12 +52,12 @@ async function writeChunksToStream(chunks: AsyncIterable<Uint8Array>, out: NodeJ
47
52
  for await (const chunk of chunks) {
48
53
  if (!out.write(chunk)) {
49
54
  // If the internal buffer is full, wait until it's drained
50
- await new Promise(resolve => out.once('drain', resolve));
55
+ await new Promise((resolve) => out.once("drain", resolve));
51
56
  }
52
57
  }
53
58
  out.end(); // Close the stream when done
54
59
  }
55
60
 
56
61
  export function md5(contents: string) {
57
- return crypto.createHash('md5').update(contents).digest("hex");
62
+ return crypto.createHash("md5").update(contents).digest("hex");
58
63
  }
@@ -0,0 +1,17 @@
1
+
2
+ export interface DocPart {
3
+ line_number_start: number
4
+ line_number_end: number
5
+ name: string
6
+ type: string
7
+ }
8
+
9
+ export const getContentPart = (content: string, part: DocPart): string => {
10
+ const lines = content.split('\n');
11
+ const text = lines.filter((_l, i) => i >= part.line_number_start && i <= part.line_number_end).join('\n');
12
+ return text;
13
+ }
14
+
15
+ export const getContentParts = (content: string, parts: DocPart[]): string[] => {
16
+ return parts.map(part => getContentPart(content, part));
17
+ }
@@ -4,20 +4,21 @@
4
4
 
5
5
  import { VertesiaClient } from "@vertesia/client";
6
6
  import { WorkflowExecutionBaseParams } from "@vertesia/common";
7
+ import { WorkflowParamNotFound } from "../errors.js";
7
8
 
8
9
 
9
10
  export function getVertesiaClient(payload: WorkflowExecutionBaseParams) {
10
11
 
11
12
  if (!payload.auth_token) {
12
- throw new Error("Authentication Token is missing from WorkflowExecutionPayload.authToken");
13
+ throw new WorkflowParamNotFound("Authentication Token is missing from WorkflowExecutionPayload.authToken");
13
14
  }
14
15
 
15
16
  if (!payload.config?.studio_url) {
16
- throw new Error("Content Store URL is missing from WorkflowExecutionPayload.servers.storeUrl");
17
+ throw new WorkflowParamNotFound("Content Store URL is missing from WorkflowExecutionPayload.servers.storeUrl");
17
18
  }
18
19
 
19
20
  if (!payload.config?.store_url) {
20
- throw new Error("Content Store URL is missing from WorkflowExecutionPayload.servers.storeUrl");
21
+ throw new WorkflowParamNotFound("Content Store URL is missing from WorkflowExecutionPayload.servers.storeUrl");
21
22
  }
22
23
 
23
24
  const client = new VertesiaClient({
@@ -1,9 +1,9 @@
1
- import { VertesiaClient, StreamSource } from "@vertesia/client";
1
+ import { VertesiaClient } from "@vertesia/client";
2
+ import { NodeStreamSource } from "@vertesia/client/node";
2
3
  import { Commands, MemoryPack, buildMemoryPack as _buildMemoryPack, loadMemoryPack as _loadMemoryPack } from "@vertesia/memory";
3
4
  import { createReadStream, createWriteStream } from "fs";
4
5
  import { rm } from "fs/promises";
5
- import { readableToWebStream, webStreamToReadable } from "node-web-stream-adapters";
6
- import { Readable } from "stream";
6
+ import { webStreamToReadable } from "node-web-stream-adapters";
7
7
  import { pipeline } from "stream/promises";
8
8
 
9
9
  import tmp from "tmp";
@@ -11,11 +11,6 @@ import zlib from "zlib";
11
11
 
12
12
  tmp.setGracefulCleanup();
13
13
 
14
- export class NodeStreamSource extends StreamSource {
15
- constructor(stream: Readable, name: string, type?: string, id?: string) {
16
- super(readableToWebStream(stream), name, type, id);
17
- }
18
- }
19
14
 
20
15
  export async function publishMemoryPack(client: VertesiaClient, file: string, name: string): Promise<void> {
21
16
  const stream = createReadStream(file);