@vertesia/workflow 1.5.0-dev.20260713.151751Z → 1.5.0-dev.20260717.131047Z
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/activities/generateEmbeddings.d.ts.map +1 -1
- package/lib/activities/generateEmbeddings.js +14 -22
- package/lib/activities/generateEmbeddings.js.map +1 -1
- package/lib/dsl/dslProxyActivities.d.ts +1 -0
- package/lib/dsl/dslProxyActivities.d.ts.map +1 -1
- package/lib/dsl/dslProxyActivities.js +9 -0
- package/lib/dsl/dslProxyActivities.js.map +1 -1
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +5 -0
- package/lib/errors.js.map +1 -1
- package/lib/workflows-bundle.js +391 -379
- package/package.json +14 -13
- package/src/activities/generateEmbeddings.test.ts +114 -0
- package/src/activities/generateEmbeddings.ts +17 -24
- package/src/dsl/dslProxyActivities.test.ts +23 -0
- package/src/dsl/dslProxyActivities.ts +11 -0
- package/src/errors.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertesia/workflow",
|
|
3
|
-
"version": "1.5.0-dev.
|
|
3
|
+
"version": "1.5.0-dev.20260717.131047Z",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vertesia workflow DSL",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
},
|
|
15
15
|
"license": "Apache-2.0",
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@smithy/types": "^4.15.
|
|
18
|
-
"@temporalio/proto": "^1.
|
|
19
|
-
"@temporalio/testing": "^1.
|
|
20
|
-
"@temporalio/worker": "^1.
|
|
17
|
+
"@smithy/types": "^4.15.1",
|
|
18
|
+
"@temporalio/proto": "^1.20.2",
|
|
19
|
+
"@temporalio/testing": "^1.20.2",
|
|
20
|
+
"@temporalio/worker": "^1.20.2",
|
|
21
21
|
"@types/jsonwebtoken": "^9.0.10",
|
|
22
22
|
"@types/node": "^24.13.2",
|
|
23
23
|
"@types/papaparse": "^5.5.2",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"@vertesia/tsconfig": "0.1.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@aws-sdk/client-s3": "^3.
|
|
31
|
-
"@aws-sdk/client-textract": "^3.
|
|
32
|
-
"@aws-sdk/credential-providers": "^3.
|
|
33
|
-
"@temporalio/activity": "^1.
|
|
34
|
-
"@temporalio/workflow": "^1.
|
|
30
|
+
"@aws-sdk/client-s3": "^3.1083.0",
|
|
31
|
+
"@aws-sdk/client-textract": "^3.1083.0",
|
|
32
|
+
"@aws-sdk/credential-providers": "^3.1081.0",
|
|
33
|
+
"@temporalio/activity": "^1.20.2",
|
|
34
|
+
"@temporalio/workflow": "^1.20.2",
|
|
35
35
|
"fast-deep-equal": "^3.1.3",
|
|
36
36
|
"jsonwebtoken": "^9.0.3",
|
|
37
37
|
"mime": "^4.1.0",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"undici": "^7.28.0",
|
|
48
48
|
"yaml": "^2.9.0",
|
|
49
49
|
"@llumiverse/common": "1.5.0-dev.20260713.114357Z",
|
|
50
|
-
"@vertesia/client": "1.5.0-dev.
|
|
51
|
-
"@vertesia/api-fetch-client": "1.5.0-dev.
|
|
52
|
-
"@vertesia/common": "1.5.0-dev.
|
|
50
|
+
"@vertesia/client": "1.5.0-dev.20260717.131047Z",
|
|
51
|
+
"@vertesia/api-fetch-client": "1.5.0-dev.20260717.131047Z",
|
|
52
|
+
"@vertesia/common": "1.5.0-dev.20260717.131047Z"
|
|
53
53
|
},
|
|
54
54
|
"exports": {
|
|
55
55
|
".": {
|
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
"agents",
|
|
104
104
|
"typescript"
|
|
105
105
|
],
|
|
106
|
+
"gitHead": "8aaad228827b2927db8e6aa75b27bfac90fec36f",
|
|
106
107
|
"scripts": {
|
|
107
108
|
"lint": "biome lint src",
|
|
108
109
|
"test": "vitest run",
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { MockActivityEnvironment } from '@temporalio/testing';
|
|
2
|
+
import type { VertesiaClient } from '@vertesia/client';
|
|
3
|
+
import {
|
|
4
|
+
ContentEventName,
|
|
5
|
+
type ContentObject,
|
|
6
|
+
type DSLActivityExecutionPayload,
|
|
7
|
+
SupportedEmbeddingTypes,
|
|
8
|
+
} from '@vertesia/common';
|
|
9
|
+
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
10
|
+
import type { ActivityContext } from '../dsl/setup/ActivityContext.js';
|
|
11
|
+
import { type GenerateEmbeddingsParams, generateEmbeddings } from './generateEmbeddings.js';
|
|
12
|
+
|
|
13
|
+
vi.mock('../dsl/setup/ActivityContext.js', async (importOriginal) => {
|
|
14
|
+
const actual = await importOriginal<typeof import('../dsl/setup/ActivityContext.js')>();
|
|
15
|
+
return { ...actual, setupActivity: vi.fn() };
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
let testEnv: MockActivityEnvironment;
|
|
19
|
+
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
testEnv = new MockActivityEnvironment();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const createPayload = (params: GenerateEmbeddingsParams): DSLActivityExecutionPayload<GenerateEmbeddingsParams> => {
|
|
29
|
+
const activityParams: GenerateEmbeddingsParams & Record<string, unknown> = { ...params };
|
|
30
|
+
return {
|
|
31
|
+
auth_token: 'mock-token',
|
|
32
|
+
account_id: 'test-account',
|
|
33
|
+
project_id: 'test-project',
|
|
34
|
+
params,
|
|
35
|
+
config: { studio_url: 'http://mock-studio', store_url: 'http://mock-store' },
|
|
36
|
+
workflow_name: 'StandardDocumentIntake',
|
|
37
|
+
event: ContentEventName.create,
|
|
38
|
+
objectIds: ['properties-only-object'],
|
|
39
|
+
input: { inputType: 'objectIds', objectIds: ['properties-only-object'] },
|
|
40
|
+
vars: {},
|
|
41
|
+
activity: { name: 'generateEmbeddings', params: activityParams },
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
describe('generateEmbeddings', () => {
|
|
46
|
+
it('should generate property embeddings for an object without content', async () => {
|
|
47
|
+
const { setupActivity } = await import('../dsl/setup/ActivityContext.js');
|
|
48
|
+
const document = {
|
|
49
|
+
id: 'properties-only-object',
|
|
50
|
+
properties: {
|
|
51
|
+
title: 'Properties-only object',
|
|
52
|
+
category: 'metadata',
|
|
53
|
+
},
|
|
54
|
+
} satisfies Partial<ContentObject>;
|
|
55
|
+
const embeddingResponse = {
|
|
56
|
+
model: 'embedding-model',
|
|
57
|
+
results: [{ outputs: [{ values: [0.1, 0.2, 0.3] }] }],
|
|
58
|
+
};
|
|
59
|
+
const client = {
|
|
60
|
+
objects: {
|
|
61
|
+
retrieve: vi.fn().mockResolvedValue(document),
|
|
62
|
+
setEmbedding: vi.fn().mockResolvedValue(undefined),
|
|
63
|
+
},
|
|
64
|
+
environments: {
|
|
65
|
+
embeddings: vi.fn().mockResolvedValue(embeddingResponse),
|
|
66
|
+
},
|
|
67
|
+
} as unknown as VertesiaClient;
|
|
68
|
+
const params = {
|
|
69
|
+
type: SupportedEmbeddingTypes.properties,
|
|
70
|
+
force: false,
|
|
71
|
+
} satisfies GenerateEmbeddingsParams;
|
|
72
|
+
|
|
73
|
+
vi.mocked(setupActivity).mockResolvedValue({
|
|
74
|
+
client,
|
|
75
|
+
objectId: document.id,
|
|
76
|
+
params,
|
|
77
|
+
fetchProject: vi.fn().mockResolvedValue({
|
|
78
|
+
name: 'Test Project',
|
|
79
|
+
namespace: 'test-project',
|
|
80
|
+
configuration: {
|
|
81
|
+
embeddings: {
|
|
82
|
+
[SupportedEmbeddingTypes.properties]: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
environment: 'test-environment',
|
|
85
|
+
max_tokens: 8000,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
} as unknown as ActivityContext<GenerateEmbeddingsParams>);
|
|
91
|
+
|
|
92
|
+
const result = await testEnv.run(generateEmbeddings, createPayload(params));
|
|
93
|
+
|
|
94
|
+
expect(result).toEqual({
|
|
95
|
+
id: document.id,
|
|
96
|
+
type: SupportedEmbeddingTypes.properties,
|
|
97
|
+
status: 'completed',
|
|
98
|
+
len: 3,
|
|
99
|
+
});
|
|
100
|
+
expect(client.objects.retrieve).toHaveBeenCalledWith(
|
|
101
|
+
document.id,
|
|
102
|
+
'+text +parts +embeddings +tokens +properties',
|
|
103
|
+
);
|
|
104
|
+
expect(client.environments.embeddings).toHaveBeenCalledWith('test-environment', {
|
|
105
|
+
inputs: [{ type: 'text', text: JSON.stringify(document.properties) }],
|
|
106
|
+
model: undefined,
|
|
107
|
+
});
|
|
108
|
+
expect(client.objects.setEmbedding).toHaveBeenCalledWith(document.id, SupportedEmbeddingTypes.properties, {
|
|
109
|
+
values: embeddingResponse.results[0].outputs[0].values,
|
|
110
|
+
model: embeddingResponse.model,
|
|
111
|
+
etag: expect.any(String),
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -113,10 +113,6 @@ export async function generateEmbeddings(payload: DSLActivityExecutionPayload<Ge
|
|
|
113
113
|
throw new DocumentNotFoundError('Document not found', [objectId]);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
if (!document.content) {
|
|
117
|
-
throw new DocumentNotFoundError('Document content not found', [objectId]);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
116
|
let res:
|
|
121
117
|
| Awaited<ReturnType<typeof generateTextEmbeddings>>
|
|
122
118
|
| Awaited<ReturnType<typeof generateImageEmbeddings>>
|
|
@@ -174,14 +170,19 @@ async function generateTextEmbeddings({ document, client, type, config, force }:
|
|
|
174
170
|
};
|
|
175
171
|
}
|
|
176
172
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
173
|
+
const sourceText =
|
|
174
|
+
type === SupportedEmbeddingTypes.text
|
|
175
|
+
? document.text
|
|
176
|
+
: document.properties
|
|
177
|
+
? JSON.stringify(document.properties)
|
|
178
|
+
: undefined;
|
|
179
|
+
|
|
180
|
+
if (!sourceText) {
|
|
181
181
|
return {
|
|
182
182
|
id: document.id,
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
type,
|
|
184
|
+
status: 'skipped',
|
|
185
|
+
message: type === SupportedEmbeddingTypes.text ? 'no text found' : 'no properties found',
|
|
185
186
|
};
|
|
186
187
|
}
|
|
187
188
|
|
|
@@ -192,12 +193,12 @@ async function generateTextEmbeddings({ document, client, type, config, force }:
|
|
|
192
193
|
);
|
|
193
194
|
}
|
|
194
195
|
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
const sourceEtag =
|
|
197
|
+
type === SupportedEmbeddingTypes.text ? (document.text_etag ?? md5(sourceText)) : md5(sourceText);
|
|
197
198
|
|
|
198
199
|
// Skip if embeddings already exist with matching etag (unless force=true)
|
|
199
200
|
const existingEmbedding = document.embeddings?.[type];
|
|
200
|
-
if (!force && existingEmbedding?.etag &&
|
|
201
|
+
if (!force && existingEmbedding?.etag && existingEmbedding.etag === sourceEtag) {
|
|
201
202
|
log.debug(`Skipping ${type} embeddings for document ${document.id} - etag unchanged`);
|
|
202
203
|
return {
|
|
203
204
|
id: document.id,
|
|
@@ -208,15 +209,7 @@ async function generateTextEmbeddings({ document, client, type, config, force }:
|
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
// Count tokens if needed, do not rely on existing token count
|
|
211
|
-
|
|
212
|
-
if (type === SupportedEmbeddingTypes.text && document.text) {
|
|
213
|
-
tokenCount = countTokens(document.text).count;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (type === SupportedEmbeddingTypes.properties && document.properties) {
|
|
217
|
-
const propertiesText = JSON.stringify(document.properties);
|
|
218
|
-
tokenCount = countTokens(propertiesText).count;
|
|
219
|
-
}
|
|
212
|
+
const tokenCount = countTokens(sourceText).count;
|
|
220
213
|
|
|
221
214
|
const maxTokens = config.max_tokens ?? 8000;
|
|
222
215
|
|
|
@@ -233,7 +226,7 @@ async function generateTextEmbeddings({ document, client, type, config, force }:
|
|
|
233
226
|
} else {
|
|
234
227
|
log.debug(`Generating ${type} embeddings for document`);
|
|
235
228
|
|
|
236
|
-
const res = await generateEmbeddingsFromStudio(
|
|
229
|
+
const res = await generateEmbeddingsFromStudio(sourceText, environment, client);
|
|
237
230
|
const values = res?.results?.[0]?.outputs?.[0]?.values;
|
|
238
231
|
if (!values) {
|
|
239
232
|
return {
|
|
@@ -249,7 +242,7 @@ async function generateTextEmbeddings({ document, client, type, config, force }:
|
|
|
249
242
|
await client.objects.setEmbedding(document.id, type, {
|
|
250
243
|
values,
|
|
251
244
|
model: res.model,
|
|
252
|
-
etag:
|
|
245
|
+
etag: sourceEtag,
|
|
253
246
|
});
|
|
254
247
|
|
|
255
248
|
return {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { stripWorkflowContinuationFromVars } from './dslProxyActivities.js';
|
|
3
|
+
|
|
4
|
+
describe('stripWorkflowContinuationFromVars', () => {
|
|
5
|
+
it('removes continue-as-new state from activity vars', () => {
|
|
6
|
+
const vars = {
|
|
7
|
+
interaction: 'sys:AppDeveloper',
|
|
8
|
+
_continuation: {
|
|
9
|
+
conversationState: { large: true },
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
expect(stripWorkflowContinuationFromVars(vars)).toEqual({
|
|
14
|
+
interaction: 'sys:AppDeveloper',
|
|
15
|
+
});
|
|
16
|
+
expect(vars).toHaveProperty('_continuation');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns primitive and array vars unchanged', () => {
|
|
20
|
+
expect(stripWorkflowContinuationFromVars(undefined)).toBeUndefined();
|
|
21
|
+
expect(stripWorkflowContinuationFromVars(['a'])).toEqual(['a']);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -14,6 +14,16 @@ export type DslSimplifiedActivityFunction<ParamsT extends object = Record<string
|
|
|
14
14
|
params: ParamsT,
|
|
15
15
|
) => Promise<ReturnT>;
|
|
16
16
|
|
|
17
|
+
export function stripWorkflowContinuationFromVars<T>(vars: T): T {
|
|
18
|
+
if (!vars || typeof vars !== 'object' || Array.isArray(vars) || !('_continuation' in vars)) {
|
|
19
|
+
return vars;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const rest = { ...(vars as Record<string, unknown>) };
|
|
23
|
+
delete rest._continuation;
|
|
24
|
+
return rest as T;
|
|
25
|
+
}
|
|
26
|
+
|
|
17
27
|
export function dslProxyActivities<ActivitiesT extends object>(workflowName: string, options: ActivityOptions = {}) {
|
|
18
28
|
type DslActivities = {
|
|
19
29
|
[K in keyof ActivitiesT]: ActivitiesT[K] extends DslActivityFunction<infer ParamsT, infer ReturnT>
|
|
@@ -34,6 +44,7 @@ export function dslProxyActivities<ActivitiesT extends object>(workflowName: str
|
|
|
34
44
|
return (payload: WorkflowExecutionPayload, params: Record<string, unknown>) => {
|
|
35
45
|
return activityFn({
|
|
36
46
|
...payload,
|
|
47
|
+
vars: stripWorkflowContinuationFromVars(payload.vars),
|
|
37
48
|
activity: {
|
|
38
49
|
name: prop as string,
|
|
39
50
|
},
|
package/src/errors.ts
CHANGED
|
@@ -116,4 +116,9 @@ export const WF_NON_RETRYABLE_ERRORS = [
|
|
|
116
116
|
'TokenExpiredError',
|
|
117
117
|
'ZenoClientNotFoundError',
|
|
118
118
|
'WorkflowExecutionError',
|
|
119
|
+
// Temporal raises NotFoundError when an activity isn't registered on the worker
|
|
120
|
+
// (e.g. a new tool whose activity barrel export was missed). Retrying can never
|
|
121
|
+
// make it appear within a worker version, and the retried failure carries the
|
|
122
|
+
// full activity list which blows the payload size limit — so fail fast.
|
|
123
|
+
'NotFoundError',
|
|
119
124
|
];
|