@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.
- package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +7 -1
- package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
- package/lib/cjs/activities/chunkDocument.js +39 -34
- package/lib/cjs/activities/chunkDocument.js.map +1 -1
- package/lib/cjs/activities/createDocumentFromOther.js +2 -2
- package/lib/cjs/activities/createDocumentFromOther.js.map +1 -1
- package/lib/cjs/activities/executeInteraction.js +11 -5
- package/lib/cjs/activities/executeInteraction.js.map +1 -1
- package/lib/cjs/activities/extractDocumentText.js +24 -6
- package/lib/cjs/activities/extractDocumentText.js.map +1 -1
- package/lib/cjs/activities/generateDocumentProperties.js +22 -4
- package/lib/cjs/activities/generateDocumentProperties.js.map +1 -1
- package/lib/cjs/activities/generateEmbeddings.js +58 -102
- package/lib/cjs/activities/generateEmbeddings.js.map +1 -1
- package/lib/cjs/activities/generateImageRendition.js +77 -34
- package/lib/cjs/activities/generateImageRendition.js.map +1 -1
- package/lib/cjs/activities/generateOrAssignContentType.js +3 -7
- package/lib/cjs/activities/generateOrAssignContentType.js.map +1 -1
- package/lib/cjs/activities/notifyWebhook.js.map +1 -1
- package/lib/cjs/conversion/image.js +80 -12
- package/lib/cjs/conversion/image.js.map +1 -1
- package/lib/cjs/dsl/setup/ActivityContext.js +30 -6
- package/lib/cjs/dsl/setup/ActivityContext.js.map +1 -1
- package/lib/cjs/dsl.js +1 -1
- package/lib/cjs/dsl.js.map +1 -1
- package/lib/cjs/errors.js +13 -1
- package/lib/cjs/errors.js.map +1 -1
- package/lib/cjs/iterative-generation/iterativeGenerationWorkflow.js +2 -1
- package/lib/cjs/iterative-generation/iterativeGenerationWorkflow.js.map +1 -1
- package/lib/cjs/system/notifyWebhookWorkflow.js +2 -1
- package/lib/cjs/system/notifyWebhookWorkflow.js.map +1 -1
- package/lib/cjs/system/recalculateEmbeddingsWorkflow.js +1 -1
- package/lib/cjs/system/recalculateEmbeddingsWorkflow.js.map +1 -1
- package/lib/cjs/utils/blobs.js +12 -6
- package/lib/cjs/utils/blobs.js.map +1 -1
- package/lib/cjs/utils/chunks.js +14 -0
- package/lib/cjs/utils/chunks.js.map +1 -0
- package/lib/cjs/utils/client.js +4 -3
- package/lib/cjs/utils/client.js.map +1 -1
- package/lib/cjs/utils/memory.js +2 -9
- package/lib/cjs/utils/memory.js.map +1 -1
- package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +7 -1
- package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
- package/lib/esm/activities/chunkDocument.js +39 -34
- package/lib/esm/activities/chunkDocument.js.map +1 -1
- package/lib/esm/activities/createDocumentFromOther.js +1 -1
- package/lib/esm/activities/createDocumentFromOther.js.map +1 -1
- package/lib/esm/activities/executeInteraction.js +11 -5
- package/lib/esm/activities/executeInteraction.js.map +1 -1
- package/lib/esm/activities/extractDocumentText.js +24 -6
- package/lib/esm/activities/extractDocumentText.js.map +1 -1
- package/lib/esm/activities/generateDocumentProperties.js +22 -4
- package/lib/esm/activities/generateDocumentProperties.js.map +1 -1
- package/lib/esm/activities/generateEmbeddings.js +58 -69
- package/lib/esm/activities/generateEmbeddings.js.map +1 -1
- package/lib/esm/activities/generateImageRendition.js +78 -35
- package/lib/esm/activities/generateImageRendition.js.map +1 -1
- package/lib/esm/activities/generateOrAssignContentType.js +3 -7
- package/lib/esm/activities/generateOrAssignContentType.js.map +1 -1
- package/lib/esm/activities/notifyWebhook.js.map +1 -1
- package/lib/esm/conversion/image.js +80 -12
- package/lib/esm/conversion/image.js.map +1 -1
- package/lib/esm/dsl/setup/ActivityContext.js +31 -7
- package/lib/esm/dsl/setup/ActivityContext.js.map +1 -1
- package/lib/esm/dsl.js +1 -1
- package/lib/esm/dsl.js.map +1 -1
- package/lib/esm/errors.js +11 -0
- package/lib/esm/errors.js.map +1 -1
- package/lib/esm/iterative-generation/iterativeGenerationWorkflow.js +2 -1
- package/lib/esm/iterative-generation/iterativeGenerationWorkflow.js.map +1 -1
- package/lib/esm/system/notifyWebhookWorkflow.js +2 -1
- package/lib/esm/system/notifyWebhookWorkflow.js.map +1 -1
- package/lib/esm/system/recalculateEmbeddingsWorkflow.js +2 -2
- package/lib/esm/system/recalculateEmbeddingsWorkflow.js.map +1 -1
- package/lib/esm/utils/blobs.js +12 -6
- package/lib/esm/utils/blobs.js.map +1 -1
- package/lib/esm/utils/chunks.js +9 -0
- package/lib/esm/utils/chunks.js.map +1 -0
- package/lib/esm/utils/client.js +4 -3
- package/lib/esm/utils/client.js.map +1 -1
- package/lib/esm/utils/memory.js +2 -7
- package/lib/esm/utils/memory.js.map +1 -1
- package/lib/types/activities/advanced/createOrUpdateDocumentFromInteractionRun.d.ts +10 -0
- package/lib/types/activities/advanced/createOrUpdateDocumentFromInteractionRun.d.ts.map +1 -1
- package/lib/types/activities/chunkDocument.d.ts +15 -0
- package/lib/types/activities/chunkDocument.d.ts.map +1 -1
- package/lib/types/activities/createDocumentFromOther.d.ts.map +1 -1
- package/lib/types/activities/executeInteraction.d.ts +14 -3
- package/lib/types/activities/executeInteraction.d.ts.map +1 -1
- package/lib/types/activities/generateDocumentProperties.d.ts +1 -1
- package/lib/types/activities/generateDocumentProperties.d.ts.map +1 -1
- package/lib/types/activities/generateEmbeddings.d.ts +21 -17
- package/lib/types/activities/generateEmbeddings.d.ts.map +1 -1
- package/lib/types/activities/generateImageRendition.d.ts +3 -5
- package/lib/types/activities/generateImageRendition.d.ts.map +1 -1
- package/lib/types/activities/generateOrAssignContentType.d.ts.map +1 -1
- package/lib/types/activities/notifyWebhook.d.ts +1 -2
- package/lib/types/activities/notifyWebhook.d.ts.map +1 -1
- package/lib/types/conversion/image.d.ts +8 -6
- package/lib/types/conversion/image.d.ts.map +1 -1
- package/lib/types/dsl/setup/ActivityContext.d.ts +3 -0
- package/lib/types/dsl/setup/ActivityContext.d.ts.map +1 -1
- package/lib/types/dsl.d.ts +1 -1
- package/lib/types/dsl.d.ts.map +1 -1
- package/lib/types/errors.d.ts +6 -0
- package/lib/types/errors.d.ts.map +1 -1
- package/lib/types/iterative-generation/iterativeGenerationWorkflow.d.ts.map +1 -1
- package/lib/types/system/notifyWebhookWorkflow.d.ts.map +1 -1
- package/lib/types/system/recalculateEmbeddingsWorkflow.d.ts +2 -17
- package/lib/types/system/recalculateEmbeddingsWorkflow.d.ts.map +1 -1
- package/lib/types/utils/blobs.d.ts.map +1 -1
- package/lib/types/utils/chunks.d.ts +9 -0
- package/lib/types/utils/chunks.d.ts.map +1 -0
- package/lib/types/utils/client.d.ts.map +1 -1
- package/lib/types/utils/memory.d.ts +1 -5
- package/lib/types/utils/memory.d.ts.map +1 -1
- package/lib/workflows-bundle.js +15394 -14602
- package/package.json +8 -6
- package/src/activities/advanced/createOrUpdateDocumentFromInteractionRun.ts +20 -1
- package/src/activities/chunkDocument.ts +62 -42
- package/src/activities/createDocumentFromOther.ts +1 -1
- package/src/activities/executeInteraction.ts +27 -9
- package/src/activities/extractDocumentText.ts +28 -7
- package/src/activities/generateDocumentProperties.ts +37 -16
- package/src/activities/generateEmbeddings.ts +91 -79
- package/src/activities/generateImageRendition.ts +100 -53
- package/src/activities/generateOrAssignContentType.ts +5 -11
- package/src/activities/notifyWebhook.ts +2 -2
- package/src/conversion/image.test.ts +110 -18
- package/src/conversion/image.ts +90 -15
- package/src/conversion/pandoc.test.ts +7 -5
- package/src/dsl/setup/ActivityContext.ts +57 -16
- package/src/dsl.ts +1 -1
- package/src/errors.ts +27 -6
- package/src/iterative-generation/iterativeGenerationWorkflow.ts +2 -1
- package/src/system/notifyWebhookWorkflow.ts +2 -1
- package/src/system/recalculateEmbeddingsWorkflow.ts +2 -2
- package/src/utils/blobs.ts +11 -6
- package/src/utils/chunks.ts +17 -0
- package/src/utils/client.ts +4 -3
- package/src/utils/memory.ts +3 -8
@@ -1,26 +1,118 @@
|
|
1
|
-
import fs from
|
2
|
-
import path from
|
3
|
-
import
|
4
|
-
import {
|
5
|
-
import {
|
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
|
-
|
9
|
-
|
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
|
-
|
19
|
+
const execAsync = promisify(exec);
|
14
20
|
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
27
|
+
// Make sure the input file exists
|
28
|
+
expect(fs.existsSync(inputImagePath)).toBe(true);
|
21
29
|
|
22
|
-
|
23
|
-
|
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
|
});
|
package/src/conversion/image.ts
CHANGED
@@ -1,22 +1,97 @@
|
|
1
|
-
|
2
|
-
import
|
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
|
-
*
|
8
|
-
* @param
|
9
|
-
* @
|
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(
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
-
|
13
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
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 {
|
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(
|
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(
|
33
|
-
throw new WorkflowParamNotFound(
|
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
|
-
|
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}`, {
|
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 =
|
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 ===
|
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
|
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
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(
|
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(
|
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(
|
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 {
|
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:
|
16
|
+
nonRetryableErrorTypes: WF_NON_RETRYABLE_ERRORS,
|
17
17
|
},
|
18
18
|
});
|
19
19
|
|
package/src/utils/blobs.ts
CHANGED
@@ -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
|
-
|
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(
|
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(
|
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(
|
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
|
+
}
|
package/src/utils/client.ts
CHANGED
@@ -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
|
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
|
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
|
21
|
+
throw new WorkflowParamNotFound("Content Store URL is missing from WorkflowExecutionPayload.servers.storeUrl");
|
21
22
|
}
|
22
23
|
|
23
24
|
const client = new VertesiaClient({
|
package/src/utils/memory.ts
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
import { VertesiaClient
|
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 {
|
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);
|