@vertesia/workflow 0.78.0 → 0.79.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 (128) hide show
  1. package/lib/cjs/activities/advanced/createDocumentTypeFromInteractionRun.js +7 -5
  2. package/lib/cjs/activities/advanced/createDocumentTypeFromInteractionRun.js.map +1 -1
  3. package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +6 -11
  4. package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
  5. package/lib/cjs/activities/advanced/updateDocumentFromInteractionRun.js +3 -1
  6. package/lib/cjs/activities/advanced/updateDocumentFromInteractionRun.js.map +1 -1
  7. package/lib/cjs/activities/chunkDocument.js +3 -1
  8. package/lib/cjs/activities/chunkDocument.js.map +1 -1
  9. package/lib/cjs/activities/executeInteraction.js +10 -7
  10. package/lib/cjs/activities/executeInteraction.js.map +1 -1
  11. package/lib/cjs/activities/generateDocumentProperties.js +12 -6
  12. package/lib/cjs/activities/generateDocumentProperties.js.map +1 -1
  13. package/lib/cjs/activities/generateOrAssignContentType.js +13 -10
  14. package/lib/cjs/activities/generateOrAssignContentType.js.map +1 -1
  15. package/lib/cjs/activities/index-dsl.js +3 -3
  16. package/lib/cjs/activities/index-dsl.js.map +1 -1
  17. package/lib/cjs/activities/notifyWebhook.js +136 -12
  18. package/lib/cjs/activities/notifyWebhook.js.map +1 -1
  19. package/lib/cjs/activities/rateLimiter.js +30 -0
  20. package/lib/cjs/activities/rateLimiter.js.map +1 -0
  21. package/lib/cjs/conversion/image.js +4 -2
  22. package/lib/cjs/conversion/image.js.map +1 -1
  23. package/lib/cjs/dsl/dsl-workflow.js +61 -0
  24. package/lib/cjs/dsl/dsl-workflow.js.map +1 -1
  25. package/lib/cjs/errors.js +9 -1
  26. package/lib/cjs/errors.js.map +1 -1
  27. package/lib/cjs/index.js +1 -1
  28. package/lib/cjs/index.js.map +1 -1
  29. package/lib/cjs/iterative-generation/activities/generatePart.js +2 -1
  30. package/lib/cjs/iterative-generation/activities/generatePart.js.map +1 -1
  31. package/lib/cjs/iterative-generation/activities/generateToc.js +6 -1
  32. package/lib/cjs/iterative-generation/activities/generateToc.js.map +1 -1
  33. package/lib/cjs/iterative-generation/utils.js.map +1 -1
  34. package/lib/cjs/system/notifyWebhookWorkflow.js +10 -3
  35. package/lib/cjs/system/notifyWebhookWorkflow.js.map +1 -1
  36. package/lib/esm/activities/advanced/createDocumentTypeFromInteractionRun.js +7 -5
  37. package/lib/esm/activities/advanced/createDocumentTypeFromInteractionRun.js.map +1 -1
  38. package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +6 -11
  39. package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
  40. package/lib/esm/activities/advanced/updateDocumentFromInteractionRun.js +3 -1
  41. package/lib/esm/activities/advanced/updateDocumentFromInteractionRun.js.map +1 -1
  42. package/lib/esm/activities/chunkDocument.js +3 -1
  43. package/lib/esm/activities/chunkDocument.js.map +1 -1
  44. package/lib/esm/activities/executeInteraction.js +11 -8
  45. package/lib/esm/activities/executeInteraction.js.map +1 -1
  46. package/lib/esm/activities/generateDocumentProperties.js +12 -6
  47. package/lib/esm/activities/generateDocumentProperties.js.map +1 -1
  48. package/lib/esm/activities/generateOrAssignContentType.js +13 -10
  49. package/lib/esm/activities/generateOrAssignContentType.js.map +1 -1
  50. package/lib/esm/activities/index-dsl.js +1 -1
  51. package/lib/esm/activities/index-dsl.js.map +1 -1
  52. package/lib/esm/activities/notifyWebhook.js +136 -12
  53. package/lib/esm/activities/notifyWebhook.js.map +1 -1
  54. package/lib/esm/activities/rateLimiter.js +27 -0
  55. package/lib/esm/activities/rateLimiter.js.map +1 -0
  56. package/lib/esm/conversion/image.js +4 -2
  57. package/lib/esm/conversion/image.js.map +1 -1
  58. package/lib/esm/dsl/dsl-workflow.js +63 -2
  59. package/lib/esm/dsl/dsl-workflow.js.map +1 -1
  60. package/lib/esm/errors.js +7 -0
  61. package/lib/esm/errors.js.map +1 -1
  62. package/lib/esm/index.js +1 -1
  63. package/lib/esm/index.js.map +1 -1
  64. package/lib/esm/iterative-generation/activities/generatePart.js +2 -1
  65. package/lib/esm/iterative-generation/activities/generatePart.js.map +1 -1
  66. package/lib/esm/iterative-generation/activities/generateToc.js +6 -1
  67. package/lib/esm/iterative-generation/activities/generateToc.js.map +1 -1
  68. package/lib/esm/iterative-generation/utils.js.map +1 -1
  69. package/lib/esm/system/notifyWebhookWorkflow.js +11 -4
  70. package/lib/esm/system/notifyWebhookWorkflow.js.map +1 -1
  71. package/lib/tsconfig.tsbuildinfo +1 -0
  72. package/lib/types/activities/advanced/createDocumentTypeFromInteractionRun.d.ts.map +1 -1
  73. package/lib/types/activities/advanced/createOrUpdateDocumentFromInteractionRun.d.ts +1 -1
  74. package/lib/types/activities/advanced/createOrUpdateDocumentFromInteractionRun.d.ts.map +1 -1
  75. package/lib/types/activities/advanced/updateDocumentFromInteractionRun.d.ts.map +1 -1
  76. package/lib/types/activities/chunkDocument.d.ts.map +1 -1
  77. package/lib/types/activities/executeInteraction.d.ts +5 -1
  78. package/lib/types/activities/executeInteraction.d.ts.map +1 -1
  79. package/lib/types/activities/generateDocumentProperties.d.ts.map +1 -1
  80. package/lib/types/activities/generateOrAssignContentType.d.ts.map +1 -1
  81. package/lib/types/activities/index-dsl.d.ts +1 -1
  82. package/lib/types/activities/index-dsl.d.ts.map +1 -1
  83. package/lib/types/activities/notifyWebhook.d.ts +14 -3
  84. package/lib/types/activities/notifyWebhook.d.ts.map +1 -1
  85. package/lib/types/activities/rateLimiter.d.ts +11 -0
  86. package/lib/types/activities/rateLimiter.d.ts.map +1 -0
  87. package/lib/types/conversion/image.d.ts.map +1 -1
  88. package/lib/types/dsl/dsl-workflow.d.ts.map +1 -1
  89. package/lib/types/errors.d.ts +4 -0
  90. package/lib/types/errors.d.ts.map +1 -1
  91. package/lib/types/index.d.ts +1 -1
  92. package/lib/types/index.d.ts.map +1 -1
  93. package/lib/types/iterative-generation/activities/generatePart.d.ts.map +1 -1
  94. package/lib/types/iterative-generation/activities/generateToc.d.ts.map +1 -1
  95. package/lib/types/iterative-generation/utils.d.ts +2 -2
  96. package/lib/types/iterative-generation/utils.d.ts.map +1 -1
  97. package/lib/types/system/notifyWebhookWorkflow.d.ts +3 -2
  98. package/lib/types/system/notifyWebhookWorkflow.d.ts.map +1 -1
  99. package/lib/workflows-bundle.js +280 -44
  100. package/package.json +13 -5
  101. package/src/activities/advanced/createDocumentTypeFromInteractionRun.ts +8 -5
  102. package/src/activities/advanced/createOrUpdateDocumentFromInteractionRun.ts +6 -10
  103. package/src/activities/advanced/updateDocumentFromInteractionRun.ts +4 -1
  104. package/src/activities/chunkDocument.ts +4 -1
  105. package/src/activities/executeInteraction.ts +16 -9
  106. package/src/activities/generateDocumentProperties.ts +13 -7
  107. package/src/activities/generateOrAssignContentType.ts +17 -11
  108. package/src/activities/index-dsl.ts +1 -1
  109. package/src/activities/notifyWebhook.test.ts +121 -19
  110. package/src/activities/notifyWebhook.ts +164 -16
  111. package/src/activities/rateLimiter.ts +41 -0
  112. package/src/conversion/image.ts +6 -3
  113. package/src/dsl/dsl-workflow.ts +80 -0
  114. package/src/dsl/workflow-exec-child.test.ts +1 -0
  115. package/src/dsl/workflow.test.ts +1 -0
  116. package/src/errors.ts +13 -0
  117. package/src/index.ts +1 -1
  118. package/src/iterative-generation/activities/generatePart.ts +2 -1
  119. package/src/iterative-generation/activities/generateToc.ts +8 -2
  120. package/src/iterative-generation/utils.ts +2 -2
  121. package/src/system/notifyWebhookWorkflow.ts +15 -6
  122. package/lib/cjs/activities/identifyTextSections.js +0 -48
  123. package/lib/cjs/activities/identifyTextSections.js.map +0 -1
  124. package/lib/esm/activities/identifyTextSections.js +0 -45
  125. package/lib/esm/activities/identifyTextSections.js.map +0 -1
  126. package/lib/types/activities/identifyTextSections.d.ts +0 -12
  127. package/lib/types/activities/identifyTextSections.d.ts.map +0 -1
  128. package/src/activities/identifyTextSections.ts +0 -71
@@ -1,15 +1,29 @@
1
1
  import { log } from "@temporalio/activity";
2
- import { DSLActivityExecutionPayload, DSLActivitySpec } from "@vertesia/common";
2
+ import { VertesiaClient } from "@vertesia/client";
3
+ import { ApiVersions, DSLActivityExecutionPayload, DSLActivitySpec, WebHookSpec, WorkflowExecutionBaseParams } from "@vertesia/common";
3
4
  import { setupActivity } from "../dsl/setup/ActivityContext.js";
4
5
  import { WorkflowParamNotFoundError } from "../errors.js";
6
+ import { getVertesiaClientOptions } from "../utils/client.js";
5
7
 
6
8
  export interface NotifyWebhookParams {
7
- target_url: string; //URL to send the notification to
9
+ webhook: string | WebHookSpec; //URL to send the notification to
10
+ workflow_id: string; //The ID of the workflow sending the notification
11
+ workflow_type: string; //The type of workflow sending the notification (the wf function name)
12
+ workflow_run_id: string; //The ID of the specific workflow run sending the notification
13
+ event_name: string; //The event that triggered the notification (e.g. "completed", "failed", etc.)
14
+ detail?: Record<string, any>; // additional data about the event if any. It will be send to the webhook when using POST
15
+ //target_url: string; //URL to send the notification to
8
16
  method: 'GET' | 'POST'; //HTTP method to use
9
- payload: Record<string, any>; //payload to send (if POST then as JSON body, if GET then as query string)
10
- headers?: Record<string, string>; //additional headers to send
17
+ headers?: Record<string, string>; // additional headers to send
11
18
  }
12
19
 
20
+ export interface WebhookNotificationPayload {
21
+ workflow_id: string,
22
+ workflow_name: string,
23
+ workflow_run_id: string,
24
+ event_name: string,
25
+ detail?: Record<string, any>,
26
+ }
13
27
 
14
28
  export interface NotifyWebhook extends DSLActivitySpec<NotifyWebhookParams> {
15
29
  name: 'notifyWebhook';
@@ -19,32 +33,166 @@ export interface NotifyWebhook extends DSLActivitySpec<NotifyWebhookParams> {
19
33
  export async function notifyWebhook(payload: DSLActivityExecutionPayload<NotifyWebhookParams>) {
20
34
 
21
35
  const { params } = await setupActivity<NotifyWebhookParams>(payload);
22
- const { target_url, method, payload: requestPayload, headers } = params
36
+ const { webhook, method, headers: defaultHeaders } = params
37
+ // resolve the url and the api version of the webhook
38
+ let target_url: string, version: number | undefined;
39
+ if (typeof webhook === 'string') {
40
+ target_url = webhook;
41
+ } else {
42
+ target_url = webhook.url;
43
+ version = webhook.version;
44
+ }
23
45
 
24
46
  if (!target_url) throw new WorkflowParamNotFoundError('target_url');
25
47
 
26
- const body = method === 'POST' ? JSON.stringify({
27
- ...requestPayload,
28
- }) : undefined
48
+ const hasBody = params.detail && method === 'POST'; //body is sent only for POST
49
+
50
+ const headers = {
51
+ ...defaultHeaders,
52
+ };
53
+ if (hasBody) {
54
+ headers['Content-Type'] = 'application/json';
55
+ }
56
+ const body = hasBody ? await createRequestBody(payload, params, version) : undefined
29
57
 
30
58
  log.info(`Notifying webhook at ${target_url}`);
31
59
  const res = await fetch(target_url, {
32
60
  method,
33
61
  body,
34
- headers: {
35
- 'Content-Type': 'application/json',
36
- ...headers
37
- },
62
+ headers,
38
63
  }).catch(err => {
39
- log.warn(`Failed to notify webhook ${target_url}: ${err}`);
40
- throw new Error(`Failed to notify webhook ${target_url}: ${err}`);
64
+ log.error(`An error occurred while notifying webhook at ${target_url}`, { err });
65
+ throw err;
41
66
  });
42
67
 
43
68
  if (!res.ok) {
44
- log.warn(`Failed to notify webhook ${target_url} - ${res.status}: ${res.statusText}`, { res });
45
- throw new Error(`Failed to notify webhook ${target_url}: ${res.statusText}`);
69
+ log.warn(`Webhook endpoint ${target_url} returned an error - ${res.status} ${res.statusText}`, { fetchResponse: res });
70
+
71
+ // Try to get response payload for error message
72
+ let errorMessage = `Webhook Notification to ${target_url} failed with status: ${res.status} ${res.statusText}`;
73
+ try {
74
+ const responseText = await res.text();
75
+ if (responseText) {
76
+ errorMessage += ` - Response: ${responseText}`;
77
+ }
78
+ } catch (readError) {
79
+ // If we can't read the response, just use the basic error message
80
+ log.debug('Could not read response body for error', { readError });
81
+ }
82
+
83
+ throw new Error(errorMessage);
46
84
  }
47
85
 
48
86
  return { status: res.status, message: res.statusText, url: res.url }
49
87
 
50
88
  }
89
+
90
+
91
+
92
+ // --------------------------------------
93
+ // Data provider for webhooks
94
+ // this allows to customize the payload sent to the webhook depending on the
95
+ // type of workflow and the api version of the webhook
96
+ // --------------------------------------
97
+
98
+
99
+ function getWorkflowName(workflowType: string): string {
100
+ // remove trailing Workflow or _Workflow case insensitive from the workflow type
101
+ return workflowType.replace(/_?workflow$/i, '');
102
+ }
103
+
104
+ async function createRequestBody(payload: WorkflowExecutionBaseParams, params: NotifyWebhookParams, api_version: number | undefined): Promise<string> {
105
+ if (api_version === undefined || Number(api_version) < ApiVersions.COMPLETION_RESULT_V1) {
106
+ return createOldRequestBody(payload, params);
107
+ } else {
108
+ return createLatestRequestBody(payload, params, api_version);
109
+ }
110
+ }
111
+
112
+ async function createLatestRequestBody(payload: WorkflowExecutionBaseParams, params: NotifyWebhookParams, api_version: number | undefined): Promise<string> {
113
+ const data = await createEventData(payload, params, api_version);
114
+ return JSON.stringify({
115
+ workflow_id: params.workflow_id,
116
+ workflow_name: getWorkflowName(params.workflow_type),
117
+ workflow_run_id: params.workflow_run_id,
118
+ event_name: params.event_name,
119
+ detail: data,
120
+ } satisfies WebhookNotificationPayload);
121
+ }
122
+
123
+ async function createEventData(payload: WorkflowExecutionBaseParams, params: NotifyWebhookParams, api_version: number | undefined): Promise<any> {
124
+ const data = params.detail;
125
+ if (data && data.run_id && params.event_name === "workflow_completed" && params.workflow_type === 'ExecuteInteractionWorkflow') {
126
+ const client = getVersionedVertesiaClient(payload, api_version); //ensure client is initialized
127
+ // we replace the result property with the full execution run object
128
+ return await client.runs.retrieve(data.run_id);
129
+ }
130
+ return data;
131
+ }
132
+
133
+
134
+ function getVersionedVertesiaClient(payload: WorkflowExecutionBaseParams, version: string | number | undefined | null) {
135
+ // set the api version header
136
+ return new VertesiaClient(getVertesiaClientOptions(payload)).withApiVersion(version ? String(version) : null);
137
+ }
138
+
139
+
140
+ // ----------------- Compatibility code -----------------
141
+ /* Before 2025-10-08 the notifyWebhook POST body was in the format:
142
+
143
+ {
144
+ "workflowId": "generation:ExecuteInteractionWorkflow:WhatColor:ox8wu6t4",
145
+ "runId": "0199c2c6-818f-77eb-b931-c1ba8b9e5184",
146
+ "status": "completed",
147
+ "result": {
148
+ "run_id": "68e616274b0e9bb510462378",
149
+ "status": "completed",
150
+ "result": {"Color": "white"}
151
+ }
152
+ }
153
+
154
+ After Versions.COMPLETION_RESULT_V1 (20250925) when the completion result interface changed we improved the
155
+ payload to the current format:
156
+
157
+ {
158
+ "workflow_id": "generation:ExecuteInteractionWorkflow:WhatColor:bdedqjqj6",
159
+ "workflow_name": "ExecuteInteraction",
160
+ "workflow_run_id": "0199c2d4-6b1d-7cf2-a1e5-4cac6778091e",
161
+ "event_name": "workflow_completed",
162
+ "detail": ExecutionRun
163
+ }
164
+
165
+ where ExecutionRun contains a result property with the new completion result format.
166
+
167
+ "result": [
168
+ {
169
+ "type": "json",
170
+ "value": {
171
+ "Color": "white"
172
+ }
173
+ }
174
+ ],
175
+
176
+ */
177
+
178
+ //@ts-ignore
179
+ async function createOldRequestBody(payload: WorkflowExecutionBaseParams, params: NotifyWebhookParams): Promise<string> {
180
+ let data = params.detail;
181
+ if (data && data.run_id && params.event_name === "workflow_completed" && params.workflow_type === 'ExecuteInteractionWorkflow') {
182
+ const client = getVersionedVertesiaClient(payload, null); //ensure client is using no specific version
183
+ const run = await client.runs.retrieve(data.run_id);
184
+ // since we use an unversioned client the run will be in old format so we don't need to tranform the result
185
+ const result = run.result;
186
+ data = {
187
+ workflowId: params.workflow_id,
188
+ runId: params.workflow_run_id,
189
+ status: params.event_name === 'workflow_completed' ? 'completed' : params.event_name,
190
+ result: {
191
+ run_id: run.id,
192
+ status: run.status,
193
+ result: result || null
194
+ }
195
+ };
196
+ }
197
+ return JSON.stringify(data || {});
198
+ }
@@ -0,0 +1,41 @@
1
+ import { DSLActivityExecutionPayload, RateLimitRequestPayload } from "@vertesia/common";
2
+ import { activityInfo, log } from "@temporalio/activity";
3
+ import { setupActivity } from "../dsl/setup/ActivityContext.js";
4
+
5
+ export interface RateLimitParams {
6
+ interactionIdOrEndpoint: string;
7
+ environmentId?: string;
8
+ modelId?: string;
9
+ }
10
+
11
+ export interface RateLimitResult {
12
+ delayMs: number;
13
+ }
14
+
15
+ export async function checkRateLimit(payload: DSLActivityExecutionPayload<RateLimitParams>): Promise<RateLimitResult> {
16
+ const { client, params } = await setupActivity<RateLimitParams>(payload);
17
+ const { environmentId, modelId } = params;
18
+
19
+ const result: RateLimitResult = {
20
+ delayMs: 0,
21
+ }
22
+
23
+ try {
24
+ // Call the studio-server endpoint to get rate limit delay using the Vertesia client
25
+ const info = activityInfo();
26
+ const requestPayload: RateLimitRequestPayload = {
27
+ interaction: params.interactionIdOrEndpoint,
28
+ workflow_run_id: info.workflowExecution.runId,
29
+ environment_id: environmentId,
30
+ model_id: modelId,
31
+ };
32
+
33
+ const response = await client.interactions.requestSlot(requestPayload);
34
+ result.delayMs = response.delay_ms;
35
+ } catch (error) {
36
+ log.warn('Failed to call rate limit API:', {error});
37
+ throw error;
38
+ }
39
+
40
+ return result;
41
+ }
@@ -78,6 +78,12 @@ export async function imageResizer(
78
78
  const command = `convert`
79
79
  let args = [inputPath];
80
80
 
81
+ // Add JPEG shrink-on-load optimization
82
+ args.push("-define", `jpeg:size=${max_hw * 3}x${max_hw * 3}`);
83
+
84
+ // Remove metadata
85
+ args.push("-strip");
86
+
81
87
  // https://usage.imagemagick.org/filter/nicolas/#downsample
82
88
  // Add colorspace correction if enabled
83
89
  if (colorspaceCorrection) {
@@ -110,9 +116,6 @@ export async function imageResizer(
110
116
  }
111
117
  }
112
118
 
113
- // Add JPEG shrink-on-load optimization
114
- args.push("-define", `jpeg:size=${max_hw * 3}x${max_hw * 3}`);
115
-
116
119
  // Resize operation
117
120
  args.push("-resize", `${max_hw}x${max_hw}>`);
118
121
 
@@ -7,6 +7,7 @@ import {
7
7
  log,
8
8
  patched,
9
9
  proxyActivities,
10
+ sleep,
10
11
  startChild,
11
12
  UntypedActivities,
12
13
  } from "@temporalio/workflow";
@@ -18,6 +19,7 @@ import {
18
19
  DSLWorkflowExecutionPayload,
19
20
  DSLWorkflowSpec,
20
21
  getDocumentIds,
22
+ getTenantId,
21
23
  WorkflowExecutionPayload
22
24
  } from "@vertesia/common";
23
25
  import ms, { StringValue } from 'ms';
@@ -25,6 +27,8 @@ import { HandleDslErrorParams } from "../activities/handleError.js";
25
27
  import * as activities from "../activities/index.js";
26
28
  import { WF_NON_RETRYABLE_ERRORS, WorkflowParamNotFoundError } from "../errors.js";
27
29
  import { Vars } from "./vars.js";
30
+ import { RateLimitParams } from "../activities/rateLimiter.js";
31
+
28
32
 
29
33
  interface BaseActivityPayload extends WorkflowExecutionPayload {
30
34
  workflow_name: string;
@@ -172,6 +176,7 @@ async function startChildWorkflow(step: DSLChildWorkflowStep, payload: DSLWorkfl
172
176
  AccountId: [payload.account_id],
173
177
  DocumentId: getDocumentIds(payload),
174
178
  ProjectId: [payload.project_id],
179
+ TenantId: [getTenantId(payload.account_id, payload.project_id)],
175
180
  InitiatedBy: payload.initiated_by ? [payload.initiated_by] : [],
176
181
  },
177
182
  });
@@ -204,6 +209,7 @@ async function executeChildWorkflow(step: DSLChildWorkflowStep, payload: DSLWork
204
209
  AccountId: [payload.account_id],
205
210
  DocumentId: getDocumentIds(payload),
206
211
  ProjectId: [payload.project_id],
212
+ TenantId: [getTenantId(payload.account_id, payload.project_id)],
207
213
  InitiatedBy: payload.initiated_by ? [payload.initiated_by] : [],
208
214
  },
209
215
  });
@@ -218,6 +224,48 @@ async function executeChildWorkflow(step: DSLChildWorkflowStep, payload: DSLWork
218
224
  }
219
225
  }
220
226
 
227
+ function buildRateLimitParams(activity: DSLActivitySpec, executionPayload: DSLActivityExecutionPayload<any>): RateLimitParams {
228
+ const params = executionPayload.params;
229
+ let interactionId: string;
230
+
231
+ switch (activity.name) {
232
+ case "executeInteraction":
233
+ interactionId = params.interactionName;
234
+ break;
235
+
236
+ case "generateDocumentProperties":
237
+ interactionId = params.interactionName || "sys:ExtractInformation";
238
+ break;
239
+
240
+ case "identifyTextSections":
241
+ interactionId = params.interactionName || "sys:IdentifyTextSections";
242
+ break;
243
+
244
+ case "generateOrAssignContentType":
245
+ interactionId = params.interactionNames?.selectDocumentType || "sys:SelectDocumentType";
246
+ break;
247
+
248
+ case "chunkDocument":
249
+ interactionId = params.interactionName || "sys:ChunkDocument";
250
+ break;
251
+
252
+ default:
253
+ // For any other rate-limited activities, try to extract what we can
254
+ interactionId = params.interactionName;
255
+ break;
256
+ }
257
+
258
+ if (!interactionId) {
259
+ throw new Error(`No interaction ID could be determined for activity ${activity.name}`);
260
+ }
261
+
262
+ return {
263
+ interactionIdOrEndpoint: interactionId,
264
+ environmentId: params.environment,
265
+ modelId: params.model,
266
+ };
267
+ }
268
+
221
269
  async function runActivity(activity: DSLActivitySpec, basePayload: BaseActivityPayload, vars: Vars, defaultProxy: ActivityInterfaceFor<UntypedActivities>, defaultOptions: ActivityOptions) {
222
270
  if (basePayload.debug_mode) {
223
271
  log.debug(`Workflow vars before executing activity ${activity.name}`, { vars: vars.resolve() });
@@ -245,6 +293,38 @@ async function runActivity(activity: DSLActivitySpec, basePayload: BaseActivityP
245
293
  });
246
294
  }
247
295
 
296
+ if (patched('system-activity-taskqueue')) {
297
+ // hack: do nothing, remove later
298
+ // https://github.com/vertesia/composableai/pull/544/files
299
+ }
300
+
301
+ // call rate limiter depending on the activity type
302
+ const rateLimitedActivities = [
303
+ "chunkDocument",
304
+ "executeInteraction",
305
+ "generateDocumentProperties",
306
+ "generateOrAssignContentType",
307
+ "identifyTextSections",
308
+ ];
309
+
310
+ if (activity.name && rateLimitedActivities.includes(activity.name)) {
311
+ log.info(`Applying rate limit for activity ${activity.name}`);
312
+ // Apply rate limiting logic here
313
+ // Check rate limit first - loop until no delay
314
+ const rateLimitParams = buildRateLimitParams(activity, executionPayload);
315
+
316
+ const rateLimitPayload = dslActivityPayload(basePayload, activity, rateLimitParams);
317
+ let rateLimitResult = await proxy.checkRateLimit(rateLimitPayload);
318
+
319
+ while (rateLimitResult.delayMs > 0) {
320
+ log.info(`Rate limit delay applied: ${rateLimitResult.delayMs}ms`);
321
+ await sleep(rateLimitResult.delayMs);
322
+
323
+ // Check again after sleeping
324
+ rateLimitResult = await proxy.checkRateLimit(rateLimitPayload);
325
+ }
326
+ }
327
+
248
328
  const fn = proxy[activity.name];
249
329
  if (activity.parallel) {
250
330
  //TODO execute in parallel
@@ -129,6 +129,7 @@ describe('DSL Workflow with child workflows', () => {
129
129
  AccountId: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
130
130
  DocumentId: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
131
131
  ProjectId: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
132
+ TenantId: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
132
133
  InitiatedBy: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
133
134
  },
134
135
  });
@@ -60,6 +60,7 @@ describe('DSL Workflow', () => {
60
60
  AccountId: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
61
61
  DocumentId: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
62
62
  ProjectId: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
63
+ TenantId: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
63
64
  InitiatedBy: protos.temporal.api.enums.v1.IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD,
64
65
  },
65
66
  });
package/src/errors.ts CHANGED
@@ -51,6 +51,19 @@ export class WorkflowParamNotFoundError extends ApplicationFailure {
51
51
  }
52
52
  }
53
53
 
54
+ export class ResourceExhaustedError extends ApplicationFailure {
55
+ constructor(
56
+ public statusCode: number = 429,
57
+ message?: string,
58
+ ) {
59
+ super(
60
+ message || "Resource exhausted - rate limit exceeded",
61
+ "ResourceExhaustedError",
62
+ true, // non-retryable
63
+ );
64
+ }
65
+ }
66
+
54
67
  export const WF_NON_RETRYABLE_ERRORS = [
55
68
  "DocumentNotFoundError",
56
69
  "ActivityParamInvalidError",
package/src/index.ts CHANGED
@@ -18,11 +18,11 @@ export * from "./activities/executeInteraction.js";
18
18
  export * from "./activities/extractDocumentText.js";
19
19
  export * from "./activities/generateDocumentProperties.js";
20
20
  export * from "./activities/generateEmbeddings.js";
21
- export * from "./activities/identifyTextSections.js";
22
21
  export * from "./activities/renditions/generateImageRendition.js";
23
22
  export * from "./activities/renditions/generateVideoRendition.js";
24
23
  export * from "./activities/generateOrAssignContentType.js";
25
24
  export * from "./activities/notifyWebhook.js";
25
+ export * from "./activities/rateLimiter.js";
26
26
  export * from "./activities/setDocumentStatus.js";
27
27
  export * from "./iterative-generation/activities/index.js";
28
28
 
@@ -5,6 +5,7 @@ import { getVertesiaClient } from "../../utils/client.js";
5
5
  import { buildAndPublishMemoryPack, loadMemoryPack } from "../../utils/memory.js";
6
6
  import { IterativeGenerationPayload, OutputMemoryMeta, Section, TocPart, TocSection } from "../types.js";
7
7
  import { executeWithVars, expectMemoryIsConsistent } from "../utils.js";
8
+ import { completionResultToString } from "@llumiverse/common";
8
9
 
9
10
  export async function it_gen_generatePart(payload: WorkflowExecutionPayload, path: number[]) {
10
11
  const vars = payload.vars as IterativeGenerationPayload;
@@ -56,7 +57,7 @@ export async function it_gen_generatePart(payload: WorkflowExecutionPayload, pat
56
57
  }
57
58
  });
58
59
 
59
- const result = r.result as string;
60
+ const result = r.result.map(completionResultToString).join('\n');
60
61
  content[content.length - 1].content += result;
61
62
  meta.lastProcessedPart = path;
62
63
  await buildAndPublishMemoryPack(client, `${memory}/output`, async ({ copyText }) => {
@@ -3,6 +3,7 @@ import { getVertesiaClient } from "../../utils/client.js";
3
3
  import { buildAndPublishMemoryPack } from "../../utils/memory.js";
4
4
  import { IterativeGenerationPayload, OutputMemoryMeta, Toc, TocIndex } from "../types.js";
5
5
  import { executeWithVars, tocIndex } from "../utils.js";
6
+ import { parseCompletionResultsToJson } from "@llumiverse/common";
6
7
 
7
8
  const defaultTocSchema = {
8
9
  "type": "object",
@@ -84,14 +85,19 @@ export async function it_gen_generateToc(payload: WorkflowExecutionPayload): Pro
84
85
 
85
86
  const run = await executeWithVars(client, vars.interaction, vars, undefined, schema);
86
87
 
87
- const toc = run.result as Toc;
88
+ //Parse the CompletionResult[] to get a Toc object
89
+ const jsonResults = parseCompletionResultsToJson(run.result);
90
+
91
+ const toc: Toc = {
92
+ sections: jsonResults.sections
93
+ };
88
94
 
89
95
  await buildAndPublishMemoryPack(client, `${vars.memory}/output`, async () => {
90
96
  return {
91
97
  toc,
92
98
  lastProcessedPart: undefined, // the part index (a number array)
93
99
  previouslyGenerated: ""
94
- } as OutputMemoryMeta
100
+ } satisfies OutputMemoryMeta
95
101
  });
96
102
 
97
103
  return tocIndex(toc);
@@ -15,7 +15,7 @@ export interface ExecuteOptions {
15
15
  result_schema?: Record<string, any>;
16
16
  }
17
17
 
18
- export async function execute<T = any>(client: VertesiaClient, options: ExecuteOptions): Promise<ExecutionRun<any, T>> {
18
+ export async function execute(client: VertesiaClient, options: ExecuteOptions): Promise<ExecutionRun> {
19
19
  return client.interactions.executeByName(options.interaction, {
20
20
  data: {
21
21
  ...options.memory_mapping,
@@ -30,7 +30,7 @@ export async function execute<T = any>(client: VertesiaClient, options: ExecuteO
30
30
  });
31
31
  }
32
32
 
33
- export function executeWithVars<T = any>(client: VertesiaClient, interaction: string, vars: Record<string, any>, mapping?: Record<string, any>, result_schema?: Record<string, any>): Promise<ExecutionRun<any, T>> {
33
+ export function executeWithVars(client: VertesiaClient, interaction: string, vars: Record<string, any>, mapping?: Record<string, any>, result_schema?: Record<string, any>): Promise<ExecutionRun> {
34
34
  if (mapping) {
35
35
  mapping = { ...vars.input_mapping, ...mapping };
36
36
  } else {
@@ -1,6 +1,6 @@
1
1
 
2
- import { log } from "@temporalio/workflow";
3
- import { WorkflowExecutionPayload } from "@vertesia/common";
2
+ import { log, workflowInfo } from "@temporalio/workflow";
3
+ import { WebHookSpec, WorkflowExecutionPayload } from "@vertesia/common";
4
4
  import * as activities from "../activities/notifyWebhook.js";
5
5
  import { dslProxyActivities } from "../dsl/dslProxyActivities.js";
6
6
  import { WF_NON_RETRYABLE_ERRORS } from "../errors.js";
@@ -19,19 +19,25 @@ const {
19
19
  });
20
20
 
21
21
  export interface NotifyWebhookWorfklowParams {
22
- endpoints: string[],
22
+ workflow_type: string;
23
+ endpoints: (string | WebHookSpec)[],
23
24
  data: Record<string, any>
24
25
  }
25
26
 
26
27
 
27
28
  export async function notifyWebhookWorkflow(payload: WorkflowExecutionPayload<NotifyWebhookWorfklowParams>): Promise<any> {
28
29
 
30
+ const info = workflowInfo();
29
31
  const { objectIds, vars } = payload;
30
32
  const notifications = [];
31
33
  const endpoints = vars.endpoints ?? (vars as any).webhooks ?? [];
32
34
  const data = vars.data ?? (vars as any).webhook_data ?? undefined;
35
+ const workflow_type = vars.workflow_type ?? info.workflowType;
33
36
  const eventName = payload.event;
34
37
 
38
+ const workflowId = info.parent?.workflowId || info.workflowId;
39
+ const workflowRunId = info.parent?.runId || info.runId;
40
+
35
41
  if (!endpoints.length) {
36
42
  log.info(`No webhooks to notify`);
37
43
  return { notifications: [], message: "No webhooks to notify" };
@@ -39,11 +45,14 @@ export async function notifyWebhookWorkflow(payload: WorkflowExecutionPayload<No
39
45
 
40
46
  for (const ep of endpoints) {
41
47
  const n = notifyWebhook(payload, {
42
- target_url: ep,
48
+ webhook: ep,
43
49
  method: 'POST',
44
- payload: {
50
+ workflow_type,
51
+ workflow_id: workflowId,
52
+ workflow_run_id: workflowRunId,
53
+ event_name: eventName,
54
+ detail: {
45
55
  object_ids: objectIds,
46
- event: eventName,
47
56
  data
48
57
  }
49
58
  }).then(res => {
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.identifyTextSections = identifyTextSections;
4
- const activity_1 = require("@temporalio/activity");
5
- const ActivityContext_js_1 = require("../dsl/setup/ActivityContext.js");
6
- const executeInteraction_js_1 = require("./executeInteraction.js");
7
- const INT_GENERATE_TEXT_PARTS = "sys:IdentifyTextSections";
8
- async function identifyTextSections(payload) {
9
- const context = await (0, ActivityContext_js_1.setupActivity)(payload);
10
- const { params, client, objectId } = context;
11
- const interactionName = params.interactionName ?? INT_GENERATE_TEXT_PARTS;
12
- const project = await context.fetchProject();
13
- const doc = await client.objects.retrieve(objectId, "+text");
14
- const text = doc.text;
15
- if (!text || text.length === 0) {
16
- activity_1.log.warn(`No text found for object ${objectId}`);
17
- return;
18
- }
19
- //instrument the text with line numbers
20
- const lines = text.split('\n');
21
- const instrumented = lines.map((l, i) => `{%${i}%}${l}`).join('\n');
22
- const promptData = {
23
- content: instrumented ?? undefined,
24
- human_context: project?.configuration?.human_context ?? undefined,
25
- };
26
- const infoRes = await (0, executeInteraction_js_1.executeInteractionFromActivity)(client, interactionName, {
27
- ...params,
28
- include_previous_error: true,
29
- validate_result: true,
30
- }, promptData, payload.debug_mode ?? false);
31
- const parts = infoRes.result.parts;
32
- if (!parts || !Array.isArray(parts) || parts.length === 0) {
33
- activity_1.log.warn(`No text parts generated for object ${objectId}`);
34
- return;
35
- }
36
- const existingMetadata = doc.metadata;
37
- const updatedMetadata = {
38
- type: "document",
39
- ...existingMetadata,
40
- generation_runs: existingMetadata?.generation_runs ?? [],
41
- sections: parts
42
- };
43
- await client.objects.update(doc.id, {
44
- metadata: updatedMetadata,
45
- });
46
- return { status: "completed" };
47
- }
48
- //# sourceMappingURL=identifyTextSections.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"identifyTextSections.js","sourceRoot":"","sources":["../../../src/activities/identifyTextSections.ts"],"names":[],"mappings":";;AAaA,oDAyDC;AAtED,mDAA2C;AAE3C,wEAAgE;AAChE,mEAAqG;AAErG,MAAM,uBAAuB,GAAG,0BAA0B,CAAC;AAQpD,KAAK,UAAU,oBAAoB,CACtC,OAAgE;IAEhE,MAAM,OAAO,GAAG,MAAM,IAAA,kCAAa,EAA6B,OAAO,CAAC,CAAC;IACzE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAC7C,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,IAAI,uBAAuB,CAAC;IAE1E,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC;IAE7C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE7D,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACtB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,cAAG,CAAC,IAAI,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC;QACjD,OAAO;IACX,CAAC;IAED,uCAAuC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC9B,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEnE,MAAM,UAAU,GAAG;QACf,OAAO,EAAE,YAAY,IAAI,SAAS;QAClC,aAAa,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,IAAI,SAAS;KACpE,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,IAAA,sDAA8B,EAChD,MAAM,EACN,eAAe,EACf;QACI,GAAG,MAAM;QACT,sBAAsB,EAAE,IAAI;QAC5B,eAAe,EAAE,IAAI;KACxB,EACD,UAAU,EACV,OAAO,CAAC,UAAU,IAAI,KAAK,CAC9B,CAAC;IAEF,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;IACnC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,cAAG,CAAC,IAAI,CAAC,sCAAsC,QAAQ,EAAE,CAAC,CAAC;QAC3D,OAAO;IACX,CAAC;IAED,MAAM,gBAAgB,GAAG,GAAG,CAAC,QAAwC,CAAC;IACtE,MAAM,eAAe,GAAqB;QACtC,IAAI,EAAE,UAAU;QAChB,GAAG,gBAAgB;QACnB,eAAe,EAAE,gBAAgB,EAAE,eAAe,IAAI,EAAE;QACxD,QAAQ,EAAE,KAAK;KAClB,CAAC;IAEF,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE;QAChC,QAAQ,EAAE,eAAe;KAC5B,CAAC,CAAC;IAEH,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AACnC,CAAC"}