@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.
- package/lib/cjs/activities/advanced/createDocumentTypeFromInteractionRun.js +7 -5
- package/lib/cjs/activities/advanced/createDocumentTypeFromInteractionRun.js.map +1 -1
- package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +6 -11
- package/lib/cjs/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
- package/lib/cjs/activities/advanced/updateDocumentFromInteractionRun.js +3 -1
- package/lib/cjs/activities/advanced/updateDocumentFromInteractionRun.js.map +1 -1
- package/lib/cjs/activities/chunkDocument.js +3 -1
- package/lib/cjs/activities/chunkDocument.js.map +1 -1
- package/lib/cjs/activities/executeInteraction.js +10 -7
- package/lib/cjs/activities/executeInteraction.js.map +1 -1
- package/lib/cjs/activities/generateDocumentProperties.js +12 -6
- package/lib/cjs/activities/generateDocumentProperties.js.map +1 -1
- package/lib/cjs/activities/generateOrAssignContentType.js +13 -10
- package/lib/cjs/activities/generateOrAssignContentType.js.map +1 -1
- package/lib/cjs/activities/index-dsl.js +3 -3
- package/lib/cjs/activities/index-dsl.js.map +1 -1
- package/lib/cjs/activities/notifyWebhook.js +136 -12
- package/lib/cjs/activities/notifyWebhook.js.map +1 -1
- package/lib/cjs/activities/rateLimiter.js +30 -0
- package/lib/cjs/activities/rateLimiter.js.map +1 -0
- package/lib/cjs/conversion/image.js +4 -2
- package/lib/cjs/conversion/image.js.map +1 -1
- package/lib/cjs/dsl/dsl-workflow.js +61 -0
- package/lib/cjs/dsl/dsl-workflow.js.map +1 -1
- package/lib/cjs/errors.js +9 -1
- package/lib/cjs/errors.js.map +1 -1
- package/lib/cjs/index.js +1 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/iterative-generation/activities/generatePart.js +2 -1
- package/lib/cjs/iterative-generation/activities/generatePart.js.map +1 -1
- package/lib/cjs/iterative-generation/activities/generateToc.js +6 -1
- package/lib/cjs/iterative-generation/activities/generateToc.js.map +1 -1
- package/lib/cjs/iterative-generation/utils.js.map +1 -1
- package/lib/cjs/system/notifyWebhookWorkflow.js +10 -3
- package/lib/cjs/system/notifyWebhookWorkflow.js.map +1 -1
- package/lib/esm/activities/advanced/createDocumentTypeFromInteractionRun.js +7 -5
- package/lib/esm/activities/advanced/createDocumentTypeFromInteractionRun.js.map +1 -1
- package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js +6 -11
- package/lib/esm/activities/advanced/createOrUpdateDocumentFromInteractionRun.js.map +1 -1
- package/lib/esm/activities/advanced/updateDocumentFromInteractionRun.js +3 -1
- package/lib/esm/activities/advanced/updateDocumentFromInteractionRun.js.map +1 -1
- package/lib/esm/activities/chunkDocument.js +3 -1
- package/lib/esm/activities/chunkDocument.js.map +1 -1
- package/lib/esm/activities/executeInteraction.js +11 -8
- package/lib/esm/activities/executeInteraction.js.map +1 -1
- package/lib/esm/activities/generateDocumentProperties.js +12 -6
- package/lib/esm/activities/generateDocumentProperties.js.map +1 -1
- package/lib/esm/activities/generateOrAssignContentType.js +13 -10
- package/lib/esm/activities/generateOrAssignContentType.js.map +1 -1
- package/lib/esm/activities/index-dsl.js +1 -1
- package/lib/esm/activities/index-dsl.js.map +1 -1
- package/lib/esm/activities/notifyWebhook.js +136 -12
- package/lib/esm/activities/notifyWebhook.js.map +1 -1
- package/lib/esm/activities/rateLimiter.js +27 -0
- package/lib/esm/activities/rateLimiter.js.map +1 -0
- package/lib/esm/conversion/image.js +4 -2
- package/lib/esm/conversion/image.js.map +1 -1
- package/lib/esm/dsl/dsl-workflow.js +63 -2
- package/lib/esm/dsl/dsl-workflow.js.map +1 -1
- package/lib/esm/errors.js +7 -0
- package/lib/esm/errors.js.map +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/iterative-generation/activities/generatePart.js +2 -1
- package/lib/esm/iterative-generation/activities/generatePart.js.map +1 -1
- package/lib/esm/iterative-generation/activities/generateToc.js +6 -1
- package/lib/esm/iterative-generation/activities/generateToc.js.map +1 -1
- package/lib/esm/iterative-generation/utils.js.map +1 -1
- package/lib/esm/system/notifyWebhookWorkflow.js +11 -4
- package/lib/esm/system/notifyWebhookWorkflow.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/types/activities/advanced/createDocumentTypeFromInteractionRun.d.ts.map +1 -1
- package/lib/types/activities/advanced/createOrUpdateDocumentFromInteractionRun.d.ts +1 -1
- package/lib/types/activities/advanced/createOrUpdateDocumentFromInteractionRun.d.ts.map +1 -1
- package/lib/types/activities/advanced/updateDocumentFromInteractionRun.d.ts.map +1 -1
- package/lib/types/activities/chunkDocument.d.ts.map +1 -1
- package/lib/types/activities/executeInteraction.d.ts +5 -1
- package/lib/types/activities/executeInteraction.d.ts.map +1 -1
- package/lib/types/activities/generateDocumentProperties.d.ts.map +1 -1
- package/lib/types/activities/generateOrAssignContentType.d.ts.map +1 -1
- package/lib/types/activities/index-dsl.d.ts +1 -1
- package/lib/types/activities/index-dsl.d.ts.map +1 -1
- package/lib/types/activities/notifyWebhook.d.ts +14 -3
- package/lib/types/activities/notifyWebhook.d.ts.map +1 -1
- package/lib/types/activities/rateLimiter.d.ts +11 -0
- package/lib/types/activities/rateLimiter.d.ts.map +1 -0
- package/lib/types/conversion/image.d.ts.map +1 -1
- package/lib/types/dsl/dsl-workflow.d.ts.map +1 -1
- package/lib/types/errors.d.ts +4 -0
- package/lib/types/errors.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/iterative-generation/activities/generatePart.d.ts.map +1 -1
- package/lib/types/iterative-generation/activities/generateToc.d.ts.map +1 -1
- package/lib/types/iterative-generation/utils.d.ts +2 -2
- package/lib/types/iterative-generation/utils.d.ts.map +1 -1
- package/lib/types/system/notifyWebhookWorkflow.d.ts +3 -2
- package/lib/types/system/notifyWebhookWorkflow.d.ts.map +1 -1
- package/lib/workflows-bundle.js +280 -44
- package/package.json +13 -5
- package/src/activities/advanced/createDocumentTypeFromInteractionRun.ts +8 -5
- package/src/activities/advanced/createOrUpdateDocumentFromInteractionRun.ts +6 -10
- package/src/activities/advanced/updateDocumentFromInteractionRun.ts +4 -1
- package/src/activities/chunkDocument.ts +4 -1
- package/src/activities/executeInteraction.ts +16 -9
- package/src/activities/generateDocumentProperties.ts +13 -7
- package/src/activities/generateOrAssignContentType.ts +17 -11
- package/src/activities/index-dsl.ts +1 -1
- package/src/activities/notifyWebhook.test.ts +121 -19
- package/src/activities/notifyWebhook.ts +164 -16
- package/src/activities/rateLimiter.ts +41 -0
- package/src/conversion/image.ts +6 -3
- package/src/dsl/dsl-workflow.ts +80 -0
- package/src/dsl/workflow-exec-child.test.ts +1 -0
- package/src/dsl/workflow.test.ts +1 -0
- package/src/errors.ts +13 -0
- package/src/index.ts +1 -1
- package/src/iterative-generation/activities/generatePart.ts +2 -1
- package/src/iterative-generation/activities/generateToc.ts +8 -2
- package/src/iterative-generation/utils.ts +2 -2
- package/src/system/notifyWebhookWorkflow.ts +15 -6
- package/lib/cjs/activities/identifyTextSections.js +0 -48
- package/lib/cjs/activities/identifyTextSections.js.map +0 -1
- package/lib/esm/activities/identifyTextSections.js +0 -45
- package/lib/esm/activities/identifyTextSections.js.map +0 -1
- package/lib/types/activities/identifyTextSections.d.ts +0 -12
- package/lib/types/activities/identifyTextSections.d.ts.map +0 -1
- package/src/activities/identifyTextSections.ts +0 -71
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import { log } from "@temporalio/activity";
|
|
2
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
27
|
-
|
|
28
|
-
|
|
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.
|
|
40
|
-
throw
|
|
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(`
|
|
45
|
-
|
|
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
|
+
}
|
package/src/conversion/image.ts
CHANGED
|
@@ -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
|
|
package/src/dsl/dsl-workflow.ts
CHANGED
|
@@ -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
|
});
|
package/src/dsl/workflow.test.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
48
|
+
webhook: ep,
|
|
43
49
|
method: 'POST',
|
|
44
|
-
|
|
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"}
|