@vertesia/workflow 0.65.0 → 0.67.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/generateOrAssignContentType.js +1 -0
- package/lib/cjs/activities/generateOrAssignContentType.js.map +1 -1
- package/lib/cjs/activities/renditions/generateImageRendition.js +14 -1
- package/lib/cjs/activities/renditions/generateImageRendition.js.map +1 -1
- package/lib/cjs/activities/renditions/generateVideoRendition.js +5 -1
- package/lib/cjs/activities/renditions/generateVideoRendition.js.map +1 -1
- package/lib/cjs/errors.js +1 -60
- package/lib/cjs/errors.js.map +1 -1
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/utils/renditions.js +20 -20
- package/lib/cjs/utils/renditions.js.map +1 -1
- package/lib/cjs/utils/storage.js +55 -0
- package/lib/cjs/utils/storage.js.map +1 -0
- package/lib/esm/activities/generateOrAssignContentType.js +1 -0
- package/lib/esm/activities/generateOrAssignContentType.js.map +1 -1
- package/lib/esm/activities/renditions/generateImageRendition.js +14 -1
- package/lib/esm/activities/renditions/generateImageRendition.js.map +1 -1
- package/lib/esm/activities/renditions/generateVideoRendition.js +5 -1
- package/lib/esm/activities/renditions/generateVideoRendition.js.map +1 -1
- package/lib/esm/errors.js +0 -55
- package/lib/esm/errors.js.map +1 -1
- package/lib/esm/index.js +1 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/utils/renditions.js +20 -20
- package/lib/esm/utils/renditions.js.map +1 -1
- package/lib/esm/utils/storage.js +46 -0
- package/lib/esm/utils/storage.js.map +1 -0
- package/lib/types/activities/renditions/generateImageRendition.d.ts.map +1 -1
- package/lib/types/activities/renditions/generateVideoRendition.d.ts.map +1 -1
- package/lib/types/errors.d.ts +0 -31
- package/lib/types/errors.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/utils/renditions.d.ts +3 -3
- package/lib/types/utils/renditions.d.ts.map +1 -1
- package/lib/types/utils/storage.d.ts +16 -0
- package/lib/types/utils/storage.d.ts.map +1 -0
- package/lib/workflows-bundle.js +427 -358
- package/package.json +7 -6
- package/src/activities/generateOrAssignContentType.ts +1 -0
- package/src/activities/renditions/generateImageRendition.ts +97 -80
- package/src/activities/renditions/generateVideoRendition.ts +6 -1
- package/src/errors.ts +0 -58
- package/src/index.ts +1 -0
- package/src/utils/renditions.ts +24 -21
- package/src/utils/storage.ts +61 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vertesia/workflow",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.67.0",
|
4
4
|
"type": "module",
|
5
5
|
"description": "Composable prompts workflow dsl",
|
6
6
|
"main": "./lib/esm/index.js",
|
@@ -39,8 +39,10 @@
|
|
39
39
|
"@vertesia/memory": "^0.43.0",
|
40
40
|
"fast-deep-equal": "^3.1.3",
|
41
41
|
"jsonwebtoken": "^9.0.2",
|
42
|
+
"mime": "^4.0.0",
|
42
43
|
"ms": "3.0.0-canary.1",
|
43
44
|
"node-web-stream-adapters": "^0.2.1",
|
45
|
+
"p-limit": "^6.2.0",
|
44
46
|
"papaparse": "^5.4.1",
|
45
47
|
"seedrandom": "^3.0.5",
|
46
48
|
"sharp": "^0.33.4",
|
@@ -48,11 +50,10 @@
|
|
48
50
|
"tmp": "^0.2.3",
|
49
51
|
"tmp-promise": "^3.0.3",
|
50
52
|
"yaml": "^2.6.0",
|
51
|
-
"
|
52
|
-
"@
|
53
|
-
"@
|
54
|
-
"@vertesia/
|
55
|
-
"@vertesia/api-fetch-client": "0.65.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"
|
56
57
|
},
|
57
58
|
"ts_dual_module": {
|
58
59
|
"outDir": "lib",
|
@@ -202,6 +202,7 @@ async function generateNewType(
|
|
202
202
|
log.info("Generated schema for type", genTypeRes.result.metadata_schema);
|
203
203
|
const typeData: CreateContentObjectTypePayload = {
|
204
204
|
name: genTypeRes.result.document_type,
|
205
|
+
description: genTypeRes.result.document_type_description,
|
205
206
|
object_schema: genTypeRes.result.metadata_schema,
|
206
207
|
is_chunkable: genTypeRes.result.is_chunkable,
|
207
208
|
table_layout: genTypeRes.result.table_layout,
|
@@ -4,96 +4,113 @@ import { setupActivity } from "../../dsl/setup/ActivityContext.js";
|
|
4
4
|
import { DocumentNotFoundError, WorkflowParamNotFoundError } from "../../errors.js";
|
5
5
|
import { saveBlobToTempFile } from "../../utils/blobs.js";
|
6
6
|
import {
|
7
|
-
|
8
|
-
|
7
|
+
ImageRenditionParams,
|
8
|
+
uploadRenditionPages,
|
9
9
|
} from "../../utils/renditions.js";
|
10
10
|
|
11
|
-
interface GenerateImageRenditionParams extends ImageRenditionParams {}
|
11
|
+
interface GenerateImageRenditionParams extends ImageRenditionParams { }
|
12
12
|
|
13
13
|
export interface GenerateImageRendition
|
14
|
-
|
15
|
-
|
14
|
+
extends DSLActivitySpec<GenerateImageRenditionParams> {
|
15
|
+
name: "generateImageRendition";
|
16
16
|
}
|
17
17
|
|
18
18
|
export async function generateImageRendition(
|
19
|
-
|
19
|
+
payload: DSLActivityExecutionPayload<GenerateImageRenditionParams>,
|
20
20
|
) {
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
21
|
+
const {
|
22
|
+
client,
|
23
|
+
objectId,
|
24
|
+
params: originParams,
|
25
|
+
} = await setupActivity<GenerateImageRenditionParams>(payload);
|
26
|
+
|
27
|
+
// Fix: Use maxHeightWidth if max_hw is not provided
|
28
|
+
const params = {
|
29
|
+
...originParams,
|
30
|
+
max_hw: originParams.max_hw || (originParams as any).maxHeightWidth || 1024, // Default to 1024 if both are missing
|
31
|
+
format: originParams.format || (originParams as any).format_output || "png", // Default to png if format is missing
|
32
|
+
};
|
33
|
+
|
34
|
+
log.info(`Generating image rendition for ${objectId}`, {
|
35
|
+
originParams,
|
36
|
+
params,
|
37
|
+
});
|
38
|
+
|
39
|
+
const inputObject = await client.objects.retrieve(objectId).catch((err) => {
|
40
|
+
log.error(`Failed to retrieve document ${objectId}`, { err });
|
41
|
+
if (err.message.includes("not found")) {
|
42
|
+
throw new DocumentNotFoundError(`Document ${objectId} not found`, [objectId]);
|
43
|
+
}
|
44
|
+
throw err;
|
45
|
+
});
|
46
|
+
|
47
|
+
if (!inputObject) {
|
48
|
+
log.error(`Document ${objectId} not found`);
|
49
|
+
throw new DocumentNotFoundError(`Document ${objectId} not found`, [objectId]);
|
43
50
|
}
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
)
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
+
if (!params.format) {
|
58
|
+
log.error(`Format not found`);
|
59
|
+
throw new WorkflowParamNotFoundError(`format`);
|
60
|
+
}
|
61
|
+
|
62
|
+
if (!inputObject.content?.source) {
|
63
|
+
log.error(`Document ${objectId} has no source`);
|
64
|
+
throw new DocumentNotFoundError(`Document ${objectId} has no source`, [objectId]);
|
65
|
+
}
|
66
|
+
|
67
|
+
if (
|
68
|
+
!inputObject.content.type ||
|
69
|
+
!inputObject.content.type?.startsWith("image/")
|
70
|
+
) {
|
71
|
+
log.error(
|
72
|
+
`Document ${objectId} is not an image or a video: ${inputObject.content.type}`,
|
73
|
+
);
|
74
|
+
throw new DocumentNotFoundError(
|
75
|
+
`Document ${objectId} is not an image or a video: ${inputObject.content.type}`,
|
76
|
+
[objectId],
|
77
|
+
);
|
78
|
+
}
|
79
|
+
|
80
|
+
//array of rendition files to upload
|
81
|
+
let renditionPages: string[] = [];
|
82
|
+
|
83
|
+
const imageFile = await saveBlobToTempFile(
|
84
|
+
client,
|
85
|
+
inputObject.content.source,
|
67
86
|
);
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
[imageFile],
|
84
|
-
params,
|
85
|
-
);
|
86
|
-
|
87
|
-
if (!uploaded || !uploaded.length || !uploaded[0]) {
|
88
|
-
log.error(`Failed to upload rendition for ${objectId}`, { uploaded });
|
89
|
-
throw new Error(
|
90
|
-
`Failed to upload rendition for ${objectId} - upload object is empty`,
|
87
|
+
log.info(`Image ${objectId} copied to ${imageFile}`);
|
88
|
+
renditionPages.push(imageFile);
|
89
|
+
|
90
|
+
|
91
|
+
//IF no etag, log and use use object id as etag
|
92
|
+
if (!inputObject.content.etag) {
|
93
|
+
log.warn(`Document ${objectId} has no etag, using object id as etag`);
|
94
|
+
}
|
95
|
+
const contentEtag = inputObject.content.etag ?? inputObject.id;
|
96
|
+
|
97
|
+
const uploaded = await uploadRenditionPages(
|
98
|
+
client,
|
99
|
+
contentEtag,
|
100
|
+
[imageFile],
|
101
|
+
params,
|
91
102
|
);
|
92
|
-
}
|
93
103
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
104
|
+
if (!uploaded || !uploaded.length || !uploaded[0]) {
|
105
|
+
log.error(`Failed to upload rendition for ${objectId}`, { uploaded });
|
106
|
+
throw new Error(
|
107
|
+
`Failed to upload rendition for ${objectId} - upload object is empty`,
|
108
|
+
);
|
109
|
+
}
|
110
|
+
|
111
|
+
return {
|
112
|
+
uploads: uploaded.map((u) => u),
|
113
|
+
format: params.format,
|
114
|
+
status: "success",
|
115
|
+
};
|
99
116
|
}
|
@@ -271,10 +271,15 @@ export async function generateVideoRendition(
|
|
271
271
|
}
|
272
272
|
}
|
273
273
|
|
274
|
+
if (!inputObject.content?.etag) {
|
275
|
+
log.warn(`Document ${objectId} has no etag, using object id as etag`);
|
276
|
+
}
|
277
|
+
const etag = inputObject.content.etag ?? inputObject.id;
|
278
|
+
|
274
279
|
// Update the final upload call to handle multiple thumbnails
|
275
280
|
const uploaded = await uploadRenditionPages(
|
276
281
|
client,
|
277
|
-
|
282
|
+
etag,
|
278
283
|
renditionPages,
|
279
284
|
params,
|
280
285
|
);
|
package/src/errors.ts
CHANGED
@@ -1,20 +1,6 @@
|
|
1
1
|
import { ApplicationFailure } from "@temporalio/workflow";
|
2
2
|
import { DSLActivitySpec, DSLWorkflowSpec } from "@vertesia/common";
|
3
3
|
|
4
|
-
/**
|
5
|
-
* @deprecated Use {@link DocumentNotFoundError} instead.
|
6
|
-
*/
|
7
|
-
export class NoDocumentFound extends Error {
|
8
|
-
constructor(
|
9
|
-
message: string,
|
10
|
-
public ids?: string[],
|
11
|
-
) {
|
12
|
-
super(message);
|
13
|
-
this.name = "NoDocumentFound";
|
14
|
-
this.ids = ids;
|
15
|
-
}
|
16
|
-
}
|
17
|
-
|
18
4
|
export class DocumentNotFoundError extends ApplicationFailure {
|
19
5
|
constructor(message: string, public ids?: string[]) {
|
20
6
|
super(
|
@@ -25,19 +11,6 @@ export class DocumentNotFoundError extends ApplicationFailure {
|
|
25
11
|
}
|
26
12
|
}
|
27
13
|
|
28
|
-
/**
|
29
|
-
* @deprecated Use {@link ActivityParamNotFoundError} instead.
|
30
|
-
*/
|
31
|
-
export class ActivityParamNotFound extends Error {
|
32
|
-
constructor(
|
33
|
-
public paramName: string,
|
34
|
-
public activity: DSLActivitySpec,
|
35
|
-
) {
|
36
|
-
super(`Required parameter ${paramName} not found in activity ${activity.name}`);
|
37
|
-
this.name = "ActivityParamNotFound";
|
38
|
-
}
|
39
|
-
}
|
40
|
-
|
41
14
|
export class ActivityParamNotFoundError extends ApplicationFailure {
|
42
15
|
constructor(
|
43
16
|
public paramName: string,
|
@@ -51,20 +24,6 @@ export class ActivityParamNotFoundError extends ApplicationFailure {
|
|
51
24
|
}
|
52
25
|
}
|
53
26
|
|
54
|
-
/**
|
55
|
-
* @deprecated Use {@link ActivityParamInvalidError} instead.
|
56
|
-
*/
|
57
|
-
export class ActivityParamInvalid extends Error {
|
58
|
-
constructor(
|
59
|
-
public paramName: string,
|
60
|
-
public activity: DSLActivitySpec,
|
61
|
-
reason?: string,
|
62
|
-
) {
|
63
|
-
super(`${paramName} in activity ${activity.name} is invalid${reason ? ` ${reason}` : ""}`);
|
64
|
-
this.name = "ActivityParamInvalid";
|
65
|
-
}
|
66
|
-
}
|
67
|
-
|
68
27
|
export class ActivityParamInvalidError extends ApplicationFailure {
|
69
28
|
constructor(
|
70
29
|
public paramName: string,
|
@@ -79,19 +38,6 @@ export class ActivityParamInvalidError extends ApplicationFailure {
|
|
79
38
|
}
|
80
39
|
}
|
81
40
|
|
82
|
-
/**
|
83
|
-
* @deprecated Use {@link WorkflowParamNotFoundError} instead.
|
84
|
-
*/
|
85
|
-
export class WorkflowParamNotFound extends Error {
|
86
|
-
constructor(
|
87
|
-
public paramName: string,
|
88
|
-
public workflow?: DSLWorkflowSpec,
|
89
|
-
) {
|
90
|
-
super(`Required parameter ${paramName} not found in workflow ${workflow?.name}`);
|
91
|
-
this.name = "WorkflowParamNotFound";
|
92
|
-
}
|
93
|
-
}
|
94
|
-
|
95
41
|
export class WorkflowParamNotFoundError extends ApplicationFailure {
|
96
42
|
constructor(
|
97
43
|
public paramName: string,
|
@@ -106,12 +52,8 @@ export class WorkflowParamNotFoundError extends ApplicationFailure {
|
|
106
52
|
}
|
107
53
|
|
108
54
|
export const WF_NON_RETRYABLE_ERRORS = [
|
109
|
-
"NoDocumentFound",
|
110
55
|
"DocumentNotFoundError",
|
111
|
-
"ActivityParamInvalid",
|
112
56
|
"ActivityParamInvalidError",
|
113
|
-
"ActivityParamNotFound",
|
114
57
|
"ActivityParamNotFoundError",
|
115
|
-
"WorkflowParamNotFound",
|
116
58
|
"WorkflowParamNotFoundError",
|
117
59
|
];
|
package/src/index.ts
CHANGED
package/src/utils/renditions.ts
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
import { log } from "@temporalio/activity";
|
2
|
-
import { imageResizer } from "../conversion/image.js";
|
3
|
-
import fs from "fs";
|
4
2
|
import { VertesiaClient } from "@vertesia/client";
|
5
3
|
import { NodeStreamSource } from "@vertesia/client/node";
|
6
4
|
import { ImageRenditionFormat } from "@vertesia/common";
|
7
|
-
import
|
5
|
+
import fs from "fs";
|
6
|
+
import pLimit from 'p-limit';
|
7
|
+
import { imageResizer } from "../conversion/image.js";
|
8
8
|
|
9
9
|
export interface ImageRenditionParams {
|
10
10
|
max_hw: number; //maximum size of the longest side of the image
|
@@ -19,10 +19,10 @@ export interface ImageRenditionParams {
|
|
19
19
|
* @returns
|
20
20
|
*/
|
21
21
|
export function getRenditionsPath(
|
22
|
-
|
22
|
+
contentEtag: string,
|
23
23
|
params: ImageRenditionParams,
|
24
24
|
) {
|
25
|
-
const path = `renditions/${
|
25
|
+
const path = `renditions/${contentEtag}/${params.max_hw}`;
|
26
26
|
return path;
|
27
27
|
}
|
28
28
|
|
@@ -30,15 +30,15 @@ export function getRenditionsPath(
|
|
30
30
|
* Get a specific page path for a rendition
|
31
31
|
*/
|
32
32
|
export function getRenditionPagePath(
|
33
|
-
|
33
|
+
contentEtag: string,
|
34
34
|
params: ImageRenditionParams,
|
35
|
-
pageNumber: number | string =
|
35
|
+
pageNumber: number | string = 0,
|
36
36
|
) {
|
37
|
-
//if number, pad to
|
37
|
+
//if number, pad to 4 char
|
38
38
|
if (typeof pageNumber === "number") {
|
39
|
-
pageNumber = String(pageNumber).padStart(
|
39
|
+
pageNumber = String(pageNumber).padStart(4, "0");
|
40
40
|
}
|
41
|
-
const path = getRenditionsPath(
|
41
|
+
const path = getRenditionsPath(contentEtag, params);
|
42
42
|
const pagePath = `${path}/${pageNumber}.${params.format}`;
|
43
43
|
return pagePath;
|
44
44
|
}
|
@@ -48,21 +48,24 @@ export function getRenditionPagePath(
|
|
48
48
|
*/
|
49
49
|
export async function uploadRenditionPages(
|
50
50
|
client: VertesiaClient,
|
51
|
-
|
51
|
+
contentEtag: string,
|
52
52
|
files: string[],
|
53
53
|
params: ImageRenditionParams,
|
54
|
+
concurrency?: number,
|
54
55
|
) {
|
55
56
|
log.info(
|
56
|
-
`Uploading rendition for ${
|
57
|
+
`Uploading rendition for etag ${contentEtag} with ${files.length} pages (max_hw: ${params.max_hw}, format: ${params.format})`,
|
57
58
|
{ files },
|
58
59
|
);
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
|
61
|
+
const limit = pLimit(concurrency ?? 20);
|
62
|
+
|
63
|
+
const uploads = files.map((file, i) => limit(async () => {
|
64
|
+
const pageId = getRenditionPagePath(contentEtag, params, i);
|
62
65
|
let resizedImagePath = null;
|
63
66
|
|
64
67
|
try {
|
65
|
-
log.info(`Resizing image for ${
|
68
|
+
log.info(`Resizing image for ${contentEtag} page ${i}`, {
|
66
69
|
file,
|
67
70
|
params,
|
68
71
|
});
|
@@ -85,7 +88,7 @@ export async function uploadRenditionPages(
|
|
85
88
|
);
|
86
89
|
|
87
90
|
log.info(
|
88
|
-
`Uploading rendition for ${
|
91
|
+
`Uploading rendition for ${contentEtag} page ${i} with max_hw: ${params.max_hw} and format: ${params.format}`,
|
89
92
|
{
|
90
93
|
resizedImagePath,
|
91
94
|
fileId,
|
@@ -98,7 +101,7 @@ export async function uploadRenditionPages(
|
|
98
101
|
.uploadFile(source)
|
99
102
|
.catch((err) => {
|
100
103
|
log.error(
|
101
|
-
`Failed to upload rendition for ${
|
104
|
+
`Failed to upload rendition for ${contentEtag} page ${i}`,
|
102
105
|
{
|
103
106
|
error: err,
|
104
107
|
errorMessage: err.message,
|
@@ -107,18 +110,18 @@ export async function uploadRenditionPages(
|
|
107
110
|
);
|
108
111
|
return Promise.reject(`Upload failed: ${err.message}`);
|
109
112
|
});
|
110
|
-
log.info(`Rendition uploaded for ${
|
113
|
+
log.info(`Rendition uploaded for ${contentEtag} page ${i}`, {
|
111
114
|
result,
|
112
115
|
});
|
113
116
|
|
114
117
|
return result;
|
115
118
|
} catch (err: any) {
|
116
|
-
log.error(`Failed to upload rendition for ${
|
119
|
+
log.error(`Failed to upload rendition for ${contentEtag} page ${i}`, {
|
117
120
|
error: err,
|
118
121
|
});
|
119
122
|
return Promise.reject(`Upload failed: ${err.message}`);
|
120
123
|
}
|
121
|
-
});
|
124
|
+
}));
|
122
125
|
|
123
126
|
return Promise.all(uploads);
|
124
127
|
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import { activityInfo, log } from "@temporalio/activity";
|
2
|
+
import { ApplicationFailure } from "@temporalio/workflow";
|
3
|
+
import { VertesiaClient } from "@vertesia/client";
|
4
|
+
import { NodeStreamSource } from "@vertesia/client/node";
|
5
|
+
import { basename } from "path";
|
6
|
+
import { Readable } from "stream";
|
7
|
+
import mime from "mime";
|
8
|
+
import { fetchBlobAsBuffer } from "../utils/blobs.js";
|
9
|
+
|
10
|
+
|
11
|
+
export const agentStoragePath = (runId: string) => `agents/${runId}`;
|
12
|
+
|
13
|
+
/**
|
14
|
+
*
|
15
|
+
* Save an artifact generated by an agent to cloud storage
|
16
|
+
*
|
17
|
+
* @param client
|
18
|
+
* @param name
|
19
|
+
* @param fileContent
|
20
|
+
* @param mimeType
|
21
|
+
* @returns
|
22
|
+
*/
|
23
|
+
export async function saveAgentArtifact(
|
24
|
+
client: VertesiaClient,
|
25
|
+
name: string,
|
26
|
+
fileContent: Readable,
|
27
|
+
mimeType: string = "application/json",
|
28
|
+
) {
|
29
|
+
const { runId } = activityInfo().workflowExecution;
|
30
|
+
const ext = mime.getExtension(mimeType);
|
31
|
+
if (!name) {
|
32
|
+
throw ApplicationFailure.nonRetryable(`Name is required`);
|
33
|
+
}
|
34
|
+
|
35
|
+
//create the file path and append extension if needed
|
36
|
+
const filePath = agentStoragePath(runId) + "/" + name + (ext && !name.endsWith(ext) ? "." + ext : "");
|
37
|
+
log.info(`Storing agent artifact ${filePath} for run ${runId}`);
|
38
|
+
|
39
|
+
try {
|
40
|
+
const source = new NodeStreamSource(fileContent, `${runId}-${basename(filePath)}`, mimeType, filePath);
|
41
|
+
return await client.files.uploadFile(source);
|
42
|
+
} catch (err: any) {
|
43
|
+
log.error(`Failed to save agent artifact for run ${runId}`, {
|
44
|
+
err,
|
45
|
+
file: filePath,
|
46
|
+
});
|
47
|
+
throw ApplicationFailure.nonRetryable(
|
48
|
+
`Failed to save agent artifact for run ${runId}`,
|
49
|
+
"SaveAgentArtifactError",
|
50
|
+
{
|
51
|
+
error: err,
|
52
|
+
},
|
53
|
+
);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
export async function fetchAgentArtifact(client: VertesiaClient, name: string) {
|
58
|
+
const { runId } = activityInfo().workflowExecution;
|
59
|
+
const filePath = agentStoragePath(runId) + "/" + name;
|
60
|
+
return fetchBlobAsBuffer(client, filePath);
|
61
|
+
}
|