@upstash/workflow 0.2.23 → 0.3.0-rc1
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/astro.d.mts +4 -6
- package/astro.d.ts +4 -6
- package/astro.js +892 -960
- package/astro.mjs +1 -1
- package/{chunk-GZRDB6Z5.mjs → chunk-2Z32SOYM.mjs} +850 -951
- package/cloudflare.d.mts +4 -6
- package/cloudflare.d.ts +4 -6
- package/cloudflare.js +892 -960
- package/cloudflare.mjs +1 -1
- package/express.d.mts +4 -6
- package/express.d.ts +4 -6
- package/express.js +902 -965
- package/express.mjs +11 -6
- package/h3.d.mts +4 -6
- package/h3.d.ts +4 -6
- package/h3.js +906 -973
- package/h3.mjs +16 -15
- package/hono.d.mts +4 -6
- package/hono.d.ts +4 -6
- package/hono.js +892 -960
- package/hono.mjs +1 -1
- package/index.d.mts +98 -66
- package/index.d.ts +98 -66
- package/index.js +886 -978
- package/index.mjs +30 -23
- package/nextjs.d.mts +6 -8
- package/nextjs.d.ts +6 -8
- package/nextjs.js +893 -961
- package/nextjs.mjs +2 -2
- package/package.json +1 -1
- package/{serve-many-BCV7INWe.d.ts → serve-many-DhB8-zPD.d.mts} +2 -2
- package/{serve-many-B5Vbacm6.d.mts → serve-many-qnfynN1x.d.ts} +2 -2
- package/solidjs.d.mts +2 -4
- package/solidjs.d.ts +2 -4
- package/solidjs.js +860 -924
- package/solidjs.mjs +6 -2
- package/svelte.d.mts +7 -10
- package/svelte.d.ts +7 -10
- package/svelte.js +905 -966
- package/svelte.mjs +14 -7
- package/tanstack.d.mts +4 -6
- package/tanstack.d.ts +4 -6
- package/tanstack.js +892 -960
- package/tanstack.mjs +1 -1
- package/{types-BD06btU6.d.ts → types-pEje3VEB.d.mts} +193 -537
- package/{types-BD06btU6.d.mts → types-pEje3VEB.d.ts} +193 -537
package/solidjs.js
CHANGED
|
@@ -36,26 +36,36 @@ var WorkflowError = class extends import_qstash.QstashError {
|
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
var WorkflowAbort = class extends Error {
|
|
39
|
-
stepInfo;
|
|
40
39
|
stepName;
|
|
40
|
+
stepInfo;
|
|
41
41
|
/**
|
|
42
|
-
* whether workflow is to be canceled on abort
|
|
43
|
-
*/
|
|
44
|
-
cancelWorkflow;
|
|
45
|
-
/**
|
|
46
|
-
*
|
|
47
42
|
* @param stepName name of the aborting step
|
|
48
43
|
* @param stepInfo step information
|
|
49
|
-
* @param cancelWorkflow
|
|
50
44
|
*/
|
|
51
|
-
constructor(stepName, stepInfo
|
|
45
|
+
constructor(stepName, stepInfo) {
|
|
52
46
|
super(
|
|
53
47
|
`This is an Upstash Workflow error thrown after a step executes. It is expected to be raised. Make sure that you await for each step. Also, if you are using try/catch blocks, you should not wrap context.run/sleep/sleepUntil/call methods with try/catch. Aborting workflow after executing step '${stepName}'.`
|
|
54
48
|
);
|
|
55
49
|
this.name = "WorkflowAbort";
|
|
56
50
|
this.stepName = stepName;
|
|
57
51
|
this.stepInfo = stepInfo;
|
|
58
|
-
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var WorkflowAuthError = class extends WorkflowAbort {
|
|
55
|
+
/**
|
|
56
|
+
* @param stepName name of the step found during authorization
|
|
57
|
+
*/
|
|
58
|
+
constructor(stepName) {
|
|
59
|
+
super(stepName);
|
|
60
|
+
this.name = "WorkflowAuthError";
|
|
61
|
+
this.message = `This is an Upstash Workflow error thrown during authorization check. Found step '${stepName}' during dry-run.`;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var WorkflowCancelAbort = class extends WorkflowAbort {
|
|
65
|
+
constructor() {
|
|
66
|
+
super("cancel");
|
|
67
|
+
this.name = "WorkflowCancelAbort";
|
|
68
|
+
this.message = "Workflow has been canceled by user via context.cancel().";
|
|
59
69
|
}
|
|
60
70
|
};
|
|
61
71
|
var WorkflowNonRetryableError = class extends WorkflowAbort {
|
|
@@ -63,22 +73,22 @@ var WorkflowNonRetryableError = class extends WorkflowAbort {
|
|
|
63
73
|
* @param message error message to be displayed
|
|
64
74
|
*/
|
|
65
75
|
constructor(message) {
|
|
66
|
-
super("
|
|
76
|
+
super("non-retryable-error");
|
|
67
77
|
this.name = "WorkflowNonRetryableError";
|
|
68
|
-
|
|
78
|
+
this.message = message ?? "Workflow failed with non-retryable error.";
|
|
69
79
|
}
|
|
70
80
|
};
|
|
71
81
|
var WorkflowRetryAfterError = class extends WorkflowAbort {
|
|
72
82
|
retryAfter;
|
|
73
83
|
/**
|
|
74
|
-
* @param retryAfter time in seconds after which the workflow should be retried
|
|
75
84
|
* @param message error message to be displayed
|
|
85
|
+
* @param retryAfter time in seconds after which the workflow should be retried
|
|
76
86
|
*/
|
|
77
87
|
constructor(message, retryAfter) {
|
|
78
|
-
super("retry"
|
|
88
|
+
super("retry-after-error");
|
|
79
89
|
this.name = "WorkflowRetryAfterError";
|
|
90
|
+
this.message = message;
|
|
80
91
|
this.retryAfter = retryAfter;
|
|
81
|
-
if (message) this.message = message;
|
|
82
92
|
}
|
|
83
93
|
};
|
|
84
94
|
var formatWorkflowError = (error) => {
|
|
@@ -130,15 +140,21 @@ var makeCancelRequest = async (requester, workflowRunId) => {
|
|
|
130
140
|
});
|
|
131
141
|
return true;
|
|
132
142
|
};
|
|
133
|
-
var getSteps = async (requester, workflowRunId, messageId,
|
|
143
|
+
var getSteps = async (requester, workflowRunId, messageId, dispatchDebug) => {
|
|
134
144
|
try {
|
|
135
145
|
const steps = await requester.request({
|
|
136
146
|
path: ["v2", "workflows", "runs", workflowRunId],
|
|
137
147
|
parseResponseAsJson: true
|
|
138
148
|
});
|
|
149
|
+
if (steps.length === 1) {
|
|
150
|
+
return {
|
|
151
|
+
steps,
|
|
152
|
+
workflowRunEnded: false
|
|
153
|
+
};
|
|
154
|
+
}
|
|
139
155
|
if (!messageId) {
|
|
140
|
-
await
|
|
141
|
-
|
|
156
|
+
await dispatchDebug?.("onInfo", {
|
|
157
|
+
info: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
|
|
142
158
|
});
|
|
143
159
|
return { steps, workflowRunEnded: false };
|
|
144
160
|
} else {
|
|
@@ -147,16 +163,15 @@ var getSteps = async (requester, workflowRunId, messageId, debug) => {
|
|
|
147
163
|
return { steps: [], workflowRunEnded: false };
|
|
148
164
|
}
|
|
149
165
|
const filteredSteps = steps.slice(0, index + 1);
|
|
150
|
-
await
|
|
151
|
-
|
|
166
|
+
await dispatchDebug?.("onInfo", {
|
|
167
|
+
info: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
|
|
152
168
|
});
|
|
153
169
|
return { steps: filteredSteps, workflowRunEnded: false };
|
|
154
170
|
}
|
|
155
171
|
} catch (error) {
|
|
156
172
|
if (isInstanceOf(error, import_qstash2.QstashError) && error.status === 404) {
|
|
157
|
-
await
|
|
158
|
-
|
|
159
|
-
error
|
|
173
|
+
await dispatchDebug?.("onWarning", {
|
|
174
|
+
warning: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed."
|
|
160
175
|
});
|
|
161
176
|
return { steps: void 0, workflowRunEnded: true };
|
|
162
177
|
} else {
|
|
@@ -170,20 +185,22 @@ var WORKFLOW_ID_HEADER = "Upstash-Workflow-RunId";
|
|
|
170
185
|
var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
|
|
171
186
|
var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
|
|
172
187
|
var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
188
|
+
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
173
189
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
174
190
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
175
191
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
192
|
+
var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
|
|
193
|
+
var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
|
|
176
194
|
var WORKFLOW_PROTOCOL_VERSION = "1";
|
|
177
195
|
var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
|
|
178
196
|
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
179
197
|
var NO_CONCURRENCY = 1;
|
|
180
198
|
var DEFAULT_RETRIES = 3;
|
|
181
|
-
var VERSION = "
|
|
199
|
+
var VERSION = "v1.0.0";
|
|
182
200
|
var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
|
|
183
201
|
var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
184
202
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
185
203
|
var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
|
|
186
|
-
var TELEMETRY_HEADER_AGENT = "Upstash-Telemetry-Agent";
|
|
187
204
|
|
|
188
205
|
// src/context/auto-executor.ts
|
|
189
206
|
var import_qstash5 = require("@upstash/qstash");
|
|
@@ -246,6 +263,15 @@ function getQStashUrl(qstashClient) {
|
|
|
246
263
|
function getEventId() {
|
|
247
264
|
return `evt_${nanoid(15)}`;
|
|
248
265
|
}
|
|
266
|
+
function stringifyBody(body) {
|
|
267
|
+
if (body === void 0) {
|
|
268
|
+
return void 0;
|
|
269
|
+
}
|
|
270
|
+
if (typeof body === "string") {
|
|
271
|
+
return body;
|
|
272
|
+
}
|
|
273
|
+
return JSON.stringify(body);
|
|
274
|
+
}
|
|
249
275
|
|
|
250
276
|
// node_modules/neverthrow/dist/index.es.js
|
|
251
277
|
var defaultErrorConfig = {
|
|
@@ -587,9 +613,9 @@ var Ok = class {
|
|
|
587
613
|
}
|
|
588
614
|
safeUnwrap() {
|
|
589
615
|
const value = this.value;
|
|
590
|
-
return function* () {
|
|
616
|
+
return (function* () {
|
|
591
617
|
return value;
|
|
592
|
-
}();
|
|
618
|
+
})();
|
|
593
619
|
}
|
|
594
620
|
_unsafeUnwrap(_) {
|
|
595
621
|
return this.value;
|
|
@@ -648,10 +674,10 @@ var Err = class {
|
|
|
648
674
|
}
|
|
649
675
|
safeUnwrap() {
|
|
650
676
|
const error = this.error;
|
|
651
|
-
return function* () {
|
|
677
|
+
return (function* () {
|
|
652
678
|
yield err(error);
|
|
653
679
|
throw new Error("Do not use this generator out of `safeTry`");
|
|
654
|
-
}();
|
|
680
|
+
})();
|
|
655
681
|
}
|
|
656
682
|
_unsafeUnwrap(config) {
|
|
657
683
|
throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
|
|
@@ -689,23 +715,26 @@ var triggerFirstInvocation = async (params) => {
|
|
|
689
715
|
invokeCount,
|
|
690
716
|
delay,
|
|
691
717
|
notBefore,
|
|
692
|
-
|
|
718
|
+
failureUrl,
|
|
719
|
+
retries,
|
|
720
|
+
retryDelay,
|
|
721
|
+
flowControl,
|
|
722
|
+
unknownSdk
|
|
693
723
|
}) => {
|
|
694
724
|
const { headers } = getHeaders({
|
|
695
725
|
initHeaderValue: "true",
|
|
696
726
|
workflowConfig: {
|
|
697
727
|
workflowRunId: workflowContext.workflowRunId,
|
|
698
728
|
workflowUrl: workflowContext.url,
|
|
699
|
-
failureUrl
|
|
700
|
-
retries
|
|
701
|
-
retryDelay
|
|
729
|
+
failureUrl,
|
|
730
|
+
retries,
|
|
731
|
+
retryDelay,
|
|
702
732
|
telemetry,
|
|
703
|
-
flowControl
|
|
733
|
+
flowControl,
|
|
704
734
|
useJSONContent: useJSONContent ?? false
|
|
705
735
|
},
|
|
706
736
|
invokeCount: invokeCount ?? 0,
|
|
707
|
-
userHeaders: workflowContext.headers
|
|
708
|
-
keepTriggerConfig
|
|
737
|
+
userHeaders: workflowContext.headers
|
|
709
738
|
});
|
|
710
739
|
if (workflowContext.headers.get("content-type")) {
|
|
711
740
|
headers["content-type"] = workflowContext.headers.get("content-type");
|
|
@@ -713,6 +742,9 @@ var triggerFirstInvocation = async (params) => {
|
|
|
713
742
|
if (useJSONContent) {
|
|
714
743
|
headers["content-type"] = "application/json";
|
|
715
744
|
}
|
|
745
|
+
if (unknownSdk) {
|
|
746
|
+
headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
|
|
747
|
+
}
|
|
716
748
|
if (workflowContext.label) {
|
|
717
749
|
headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
|
|
718
750
|
}
|
|
@@ -733,21 +765,15 @@ var triggerFirstInvocation = async (params) => {
|
|
|
733
765
|
for (let i = 0; i < results.length; i++) {
|
|
734
766
|
const result = results[i];
|
|
735
767
|
const invocationParams = firstInvocationParams[i];
|
|
768
|
+
invocationParams.middlewareManager?.assignContext(invocationParams.workflowContext);
|
|
736
769
|
if (result.deduplicated) {
|
|
737
|
-
await invocationParams.
|
|
738
|
-
|
|
739
|
-
headers: invocationBatch[i].headers,
|
|
740
|
-
requestPayload: invocationParams.workflowContext.requestPayload,
|
|
741
|
-
url: invocationParams.workflowContext.url,
|
|
742
|
-
messageId: result.messageId
|
|
770
|
+
await invocationParams.middlewareManager?.dispatchDebug("onWarning", {
|
|
771
|
+
warning: `Workflow run ${invocationParams.workflowContext.workflowRunId} already exists. A new one isn't created.`
|
|
743
772
|
});
|
|
744
773
|
invocationStatuses.push("workflow-run-already-exists");
|
|
745
774
|
} else {
|
|
746
|
-
await invocationParams.
|
|
747
|
-
|
|
748
|
-
requestPayload: invocationParams.workflowContext.requestPayload,
|
|
749
|
-
url: invocationParams.workflowContext.url,
|
|
750
|
-
messageId: result.messageId
|
|
775
|
+
await invocationParams.middlewareManager?.dispatchDebug("onInfo", {
|
|
776
|
+
info: `Workflow run started successfully with URL ${invocationParams.workflowContext.url}.`
|
|
751
777
|
});
|
|
752
778
|
invocationStatuses.push("success");
|
|
753
779
|
}
|
|
@@ -769,7 +795,7 @@ var triggerRouteFunction = async ({
|
|
|
769
795
|
onCleanup,
|
|
770
796
|
onStep,
|
|
771
797
|
onCancel,
|
|
772
|
-
|
|
798
|
+
middlewareManager
|
|
773
799
|
}) => {
|
|
774
800
|
try {
|
|
775
801
|
const result = await onStep();
|
|
@@ -778,27 +804,25 @@ var triggerRouteFunction = async ({
|
|
|
778
804
|
} catch (error) {
|
|
779
805
|
const error_ = error;
|
|
780
806
|
if (isInstanceOf(error, import_qstash3.QstashError) && error.status === 400) {
|
|
781
|
-
await
|
|
782
|
-
|
|
783
|
-
name: error.name,
|
|
784
|
-
errorMessage: error.message
|
|
807
|
+
await middlewareManager?.dispatchDebug("onWarning", {
|
|
808
|
+
warning: `Tried to append to a cancelled workflow. Exiting without publishing. Error: ${error.message}`
|
|
785
809
|
});
|
|
786
810
|
return ok("workflow-was-finished");
|
|
787
811
|
} else if (isInstanceOf(error_, WorkflowNonRetryableError) || isInstanceOf(error_, WorkflowRetryAfterError)) {
|
|
788
812
|
return ok(error_);
|
|
789
|
-
} else if (
|
|
790
|
-
return err(error_);
|
|
791
|
-
} else if (error_.cancelWorkflow) {
|
|
813
|
+
} else if (isInstanceOf(error_, WorkflowCancelAbort)) {
|
|
792
814
|
await onCancel();
|
|
793
815
|
return ok("workflow-finished");
|
|
794
|
-
} else {
|
|
816
|
+
} else if (isInstanceOf(error_, WorkflowAbort)) {
|
|
795
817
|
return ok("step-finished");
|
|
818
|
+
} else {
|
|
819
|
+
return err(error_);
|
|
796
820
|
}
|
|
797
821
|
}
|
|
798
822
|
};
|
|
799
|
-
var triggerWorkflowDelete = async (workflowContext, result,
|
|
800
|
-
await
|
|
801
|
-
|
|
823
|
+
var triggerWorkflowDelete = async (workflowContext, result, cancel = false, dispatchDebug) => {
|
|
824
|
+
await dispatchDebug?.("onInfo", {
|
|
825
|
+
info: `Deleting workflow run ${workflowContext.workflowRunId} from QStash` + (cancel ? " with cancel=true." : ".")
|
|
802
826
|
});
|
|
803
827
|
await workflowContext.qstashClient.http.request({
|
|
804
828
|
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
@@ -806,18 +830,16 @@ var triggerWorkflowDelete = async (workflowContext, result, debug, cancel = fals
|
|
|
806
830
|
parseResponseAsJson: false,
|
|
807
831
|
body: JSON.stringify(result)
|
|
808
832
|
});
|
|
809
|
-
await
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
`workflow run ${workflowContext.workflowRunId} deleted.`
|
|
813
|
-
);
|
|
833
|
+
await dispatchDebug?.("onInfo", {
|
|
834
|
+
info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
|
|
835
|
+
});
|
|
814
836
|
};
|
|
815
837
|
var recreateUserHeaders = (headers) => {
|
|
816
838
|
const filteredHeaders = new Headers();
|
|
817
839
|
const pairs = headers.entries();
|
|
818
840
|
for (const [header, value] of pairs) {
|
|
819
841
|
const headerLowerCase = header.toLowerCase();
|
|
820
|
-
const isUserHeader = !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
|
|
842
|
+
const isUserHeader = headerLowerCase !== "upstash-region" && !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
|
|
821
843
|
!headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
|
|
822
844
|
headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
|
|
823
845
|
headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
|
|
@@ -832,12 +854,8 @@ var handleThirdPartyCallResult = async ({
|
|
|
832
854
|
requestPayload,
|
|
833
855
|
client,
|
|
834
856
|
workflowUrl,
|
|
835
|
-
failureUrl,
|
|
836
|
-
retries,
|
|
837
|
-
retryDelay,
|
|
838
857
|
telemetry,
|
|
839
|
-
|
|
840
|
-
debug
|
|
858
|
+
middlewareManager
|
|
841
859
|
}) => {
|
|
842
860
|
try {
|
|
843
861
|
if (request.headers.get("Upstash-Workflow-Callback")) {
|
|
@@ -854,7 +872,7 @@ var handleThirdPartyCallResult = async ({
|
|
|
854
872
|
client.http,
|
|
855
873
|
workflowRunId2,
|
|
856
874
|
messageId,
|
|
857
|
-
|
|
875
|
+
middlewareManager?.dispatchDebug.bind(middlewareManager)
|
|
858
876
|
);
|
|
859
877
|
if (workflowRunEnded) {
|
|
860
878
|
return ok("workflow-ended");
|
|
@@ -868,9 +886,8 @@ var handleThirdPartyCallResult = async ({
|
|
|
868
886
|
}
|
|
869
887
|
const callbackMessage = JSON.parse(callbackPayload);
|
|
870
888
|
if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
|
|
871
|
-
await
|
|
872
|
-
|
|
873
|
-
body: atob(callbackMessage.body ?? "")
|
|
889
|
+
await middlewareManager?.dispatchDebug("onWarning", {
|
|
890
|
+
warning: `Third party call returned status ${callbackMessage.status}. Retrying (${callbackMessage.retried} out of ${callbackMessage.maxRetries}).`
|
|
874
891
|
});
|
|
875
892
|
console.warn(
|
|
876
893
|
`Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
|
|
@@ -903,11 +920,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
903
920
|
workflowConfig: {
|
|
904
921
|
workflowRunId,
|
|
905
922
|
workflowUrl,
|
|
906
|
-
|
|
907
|
-
retries,
|
|
908
|
-
retryDelay,
|
|
909
|
-
telemetry,
|
|
910
|
-
flowControl
|
|
923
|
+
telemetry
|
|
911
924
|
},
|
|
912
925
|
userHeaders,
|
|
913
926
|
invokeCount: Number(invokeCount)
|
|
@@ -924,19 +937,17 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
924
937
|
out: JSON.stringify(callResponse),
|
|
925
938
|
concurrent: Number(concurrentString)
|
|
926
939
|
};
|
|
927
|
-
await
|
|
928
|
-
|
|
929
|
-
headers: requestHeaders,
|
|
930
|
-
url: workflowUrl
|
|
940
|
+
await middlewareManager?.dispatchDebug("onInfo", {
|
|
941
|
+
info: `Submitting third party call result, step ${stepName} (${stepIdString}).`
|
|
931
942
|
});
|
|
932
|
-
|
|
943
|
+
await client.publishJSON({
|
|
933
944
|
headers: requestHeaders,
|
|
934
945
|
method: "POST",
|
|
935
946
|
body: callResultStep,
|
|
936
947
|
url: workflowUrl
|
|
937
948
|
});
|
|
938
|
-
await
|
|
939
|
-
|
|
949
|
+
await middlewareManager?.dispatchDebug("onInfo", {
|
|
950
|
+
info: `Third party call result submitted successfully, step ${stepName} (${stepIdString}).`
|
|
940
951
|
});
|
|
941
952
|
return ok("is-call-return");
|
|
942
953
|
} else {
|
|
@@ -994,8 +1005,8 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
994
1005
|
);
|
|
995
1006
|
}
|
|
996
1007
|
if (typeof stepName !== "string") {
|
|
997
|
-
|
|
998
|
-
|
|
1008
|
+
throw new WorkflowError(
|
|
1009
|
+
`A workflow step name must be a string. Received "${stepName}" (${typeof stepName}).`
|
|
999
1010
|
);
|
|
1000
1011
|
}
|
|
1001
1012
|
this.stepName = stepName;
|
|
@@ -1057,12 +1068,8 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1057
1068
|
workflowConfig: {
|
|
1058
1069
|
workflowRunId: context.workflowRunId,
|
|
1059
1070
|
workflowUrl: context.url,
|
|
1060
|
-
failureUrl: context.failureUrl,
|
|
1061
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1062
|
-
retryDelay: context.retryDelay,
|
|
1063
1071
|
useJSONContent: false,
|
|
1064
|
-
telemetry
|
|
1065
|
-
flowControl: context.flowControl
|
|
1072
|
+
telemetry
|
|
1066
1073
|
},
|
|
1067
1074
|
userHeaders: context.headers,
|
|
1068
1075
|
invokeCount,
|
|
@@ -1078,9 +1085,6 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1078
1085
|
body,
|
|
1079
1086
|
headers,
|
|
1080
1087
|
method: "POST",
|
|
1081
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1082
|
-
retryDelay: context.retryDelay,
|
|
1083
|
-
flowControl: context.flowControl,
|
|
1084
1088
|
url: context.url
|
|
1085
1089
|
}
|
|
1086
1090
|
]);
|
|
@@ -1151,9 +1155,6 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1151
1155
|
headers,
|
|
1152
1156
|
method: "POST",
|
|
1153
1157
|
url: context.url,
|
|
1154
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1155
|
-
retryDelay: context.retryDelay,
|
|
1156
|
-
flowControl: context.flowControl,
|
|
1157
1158
|
delay: isParallel ? void 0 : this.sleep
|
|
1158
1159
|
}
|
|
1159
1160
|
]);
|
|
@@ -1196,9 +1197,6 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1196
1197
|
headers,
|
|
1197
1198
|
method: "POST",
|
|
1198
1199
|
url: context.url,
|
|
1199
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1200
|
-
retryDelay: context.retryDelay,
|
|
1201
|
-
flowControl: context.flowControl,
|
|
1202
1200
|
notBefore: isParallel ? void 0 : this.sleepUntil
|
|
1203
1201
|
}
|
|
1204
1202
|
]);
|
|
@@ -1213,20 +1211,18 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1213
1211
|
retryDelay;
|
|
1214
1212
|
timeout;
|
|
1215
1213
|
flowControl;
|
|
1216
|
-
stringifyBody;
|
|
1217
1214
|
stepType = "Call";
|
|
1218
1215
|
allowUndefinedOut = false;
|
|
1219
|
-
constructor(
|
|
1220
|
-
super(context, stepName);
|
|
1221
|
-
this.url = url;
|
|
1222
|
-
this.method = method;
|
|
1223
|
-
this.body = body;
|
|
1224
|
-
this.headers = headers;
|
|
1225
|
-
this.retries = retries;
|
|
1226
|
-
this.retryDelay = retryDelay;
|
|
1227
|
-
this.timeout = timeout;
|
|
1228
|
-
this.flowControl = flowControl;
|
|
1229
|
-
this.stringifyBody = stringifyBody;
|
|
1216
|
+
constructor(params) {
|
|
1217
|
+
super(params.context, params.stepName);
|
|
1218
|
+
this.url = params.url;
|
|
1219
|
+
this.method = params.method ?? "GET";
|
|
1220
|
+
this.body = params.body;
|
|
1221
|
+
this.headers = params.headers ?? {};
|
|
1222
|
+
this.retries = params.retries ?? 0;
|
|
1223
|
+
this.retryDelay = params.retryDelay;
|
|
1224
|
+
this.timeout = params.timeout;
|
|
1225
|
+
this.flowControl = params.flowControl;
|
|
1230
1226
|
}
|
|
1231
1227
|
getPlanStep(concurrent, targetStep) {
|
|
1232
1228
|
return {
|
|
@@ -1323,7 +1319,7 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1323
1319
|
"Upstash-Callback-Workflow-CallType": "fromCallback",
|
|
1324
1320
|
"Upstash-Callback-Workflow-Init": "false",
|
|
1325
1321
|
"Upstash-Callback-Workflow-Url": context.url,
|
|
1326
|
-
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger",
|
|
1322
|
+
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1327
1323
|
"Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
|
|
1328
1324
|
"Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
|
|
1329
1325
|
"Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
|
|
@@ -1336,22 +1332,10 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1336
1332
|
};
|
|
1337
1333
|
}
|
|
1338
1334
|
async submitStep({ context, headers }) {
|
|
1339
|
-
let callBody;
|
|
1340
|
-
if (this.stringifyBody) {
|
|
1341
|
-
callBody = JSON.stringify(this.body);
|
|
1342
|
-
} else {
|
|
1343
|
-
if (typeof this.body === "string") {
|
|
1344
|
-
callBody = this.body;
|
|
1345
|
-
} else {
|
|
1346
|
-
throw new WorkflowError(
|
|
1347
|
-
"When stringifyBody is false, body must be a string. Please check the body type of your call step."
|
|
1348
|
-
);
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
1335
|
return await context.qstashClient.batch([
|
|
1352
1336
|
{
|
|
1353
1337
|
headers,
|
|
1354
|
-
body:
|
|
1338
|
+
body: this.body,
|
|
1355
1339
|
method: this.method,
|
|
1356
1340
|
url: this.url,
|
|
1357
1341
|
retries: DEFAULT_RETRIES === this.retries ? void 0 : this.retries,
|
|
@@ -1471,28 +1455,10 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1471
1455
|
* workflow id of the invoked workflow
|
|
1472
1456
|
*/
|
|
1473
1457
|
workflowId;
|
|
1474
|
-
constructor(context, stepName, {
|
|
1475
|
-
workflow,
|
|
1476
|
-
body,
|
|
1477
|
-
headers = {},
|
|
1478
|
-
workflowRunId,
|
|
1479
|
-
retries,
|
|
1480
|
-
retryDelay,
|
|
1481
|
-
flowControl,
|
|
1482
|
-
stringifyBody = true
|
|
1483
|
-
}) {
|
|
1458
|
+
constructor(context, stepName, params) {
|
|
1484
1459
|
super(context, stepName);
|
|
1485
|
-
this.params =
|
|
1486
|
-
|
|
1487
|
-
body,
|
|
1488
|
-
headers,
|
|
1489
|
-
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1490
|
-
retries,
|
|
1491
|
-
retryDelay,
|
|
1492
|
-
flowControl,
|
|
1493
|
-
stringifyBody
|
|
1494
|
-
};
|
|
1495
|
-
const { workflowId } = workflow;
|
|
1460
|
+
this.params = params;
|
|
1461
|
+
const { workflowId } = params.workflow;
|
|
1496
1462
|
if (!workflowId) {
|
|
1497
1463
|
throw new WorkflowError("You can only invoke workflow which has a workflowId");
|
|
1498
1464
|
}
|
|
@@ -1532,11 +1498,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1532
1498
|
workflowConfig: {
|
|
1533
1499
|
workflowRunId: context.workflowRunId,
|
|
1534
1500
|
workflowUrl: context.url,
|
|
1535
|
-
failureUrl: context.failureUrl,
|
|
1536
|
-
retries: context.retries,
|
|
1537
|
-
retryDelay: context.retryDelay,
|
|
1538
1501
|
telemetry,
|
|
1539
|
-
flowControl: context.flowControl,
|
|
1540
1502
|
useJSONContent: false
|
|
1541
1503
|
},
|
|
1542
1504
|
userHeaders: context.headers,
|
|
@@ -1546,20 +1508,8 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1546
1508
|
invokerHeaders[key] = value;
|
|
1547
1509
|
});
|
|
1548
1510
|
invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
|
|
1549
|
-
let invokeBody;
|
|
1550
|
-
if (this.params.stringifyBody) {
|
|
1551
|
-
invokeBody = JSON.stringify(this.params.body);
|
|
1552
|
-
} else {
|
|
1553
|
-
if (typeof this.params.body === "string") {
|
|
1554
|
-
invokeBody = this.params.body;
|
|
1555
|
-
} else {
|
|
1556
|
-
throw new WorkflowError(
|
|
1557
|
-
"When stringifyBody is false, body must be a string. Please check the body type of your invoke step."
|
|
1558
|
-
);
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
1511
|
const request = {
|
|
1562
|
-
body:
|
|
1512
|
+
body: stringifyBody(this.params.body),
|
|
1563
1513
|
headers: Object.fromEntries(
|
|
1564
1514
|
Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
|
|
1565
1515
|
),
|
|
@@ -1570,34 +1520,19 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1570
1520
|
return JSON.stringify(request);
|
|
1571
1521
|
}
|
|
1572
1522
|
getHeaders({ context, telemetry, invokeCount }) {
|
|
1573
|
-
const {
|
|
1574
|
-
workflow,
|
|
1575
|
-
headers = {},
|
|
1576
|
-
workflowRunId = getWorkflowRunId(),
|
|
1577
|
-
retries,
|
|
1578
|
-
retryDelay,
|
|
1579
|
-
flowControl
|
|
1580
|
-
} = this.params;
|
|
1523
|
+
const { workflow, headers = {}, workflowRunId, retries, retryDelay, flowControl } = this.params;
|
|
1581
1524
|
const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
|
|
1582
|
-
const {
|
|
1583
|
-
retries: workflowRetries,
|
|
1584
|
-
retryDelay: workflowRetryDelay,
|
|
1585
|
-
failureFunction,
|
|
1586
|
-
failureUrl,
|
|
1587
|
-
useJSONContent,
|
|
1588
|
-
flowControl: workflowFlowControl
|
|
1589
|
-
} = workflow.options;
|
|
1590
1525
|
const { headers: triggerHeaders, contentType } = getHeaders({
|
|
1591
1526
|
initHeaderValue: "true",
|
|
1592
1527
|
workflowConfig: {
|
|
1593
|
-
workflowRunId,
|
|
1528
|
+
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1594
1529
|
workflowUrl: newUrl,
|
|
1595
|
-
retries
|
|
1596
|
-
retryDelay
|
|
1530
|
+
retries,
|
|
1531
|
+
retryDelay,
|
|
1597
1532
|
telemetry,
|
|
1598
|
-
failureUrl:
|
|
1599
|
-
flowControl
|
|
1600
|
-
useJSONContent: useJSONContent ?? false
|
|
1533
|
+
failureUrl: newUrl,
|
|
1534
|
+
flowControl,
|
|
1535
|
+
useJSONContent: workflow.useJSONContent ?? false
|
|
1601
1536
|
},
|
|
1602
1537
|
invokeCount: invokeCount + 1,
|
|
1603
1538
|
userHeaders: new Headers(headers)
|
|
@@ -1699,20 +1634,6 @@ var LazyWaitForEventStep = class extends LazyWaitEventStep {
|
|
|
1699
1634
|
}
|
|
1700
1635
|
};
|
|
1701
1636
|
|
|
1702
|
-
// src/agents/constants.ts
|
|
1703
|
-
var AGENT_NAME_HEADER = "upstash-agent-name";
|
|
1704
|
-
var MANAGER_AGENT_PROMPT = `You are an agent orchestrating other AI Agents.
|
|
1705
|
-
|
|
1706
|
-
These other agents have tools available to them.
|
|
1707
|
-
|
|
1708
|
-
Given a prompt, utilize these agents to address requests.
|
|
1709
|
-
|
|
1710
|
-
Don't always call all the agents provided to you at the same time. You can call one and use it's response to call another.
|
|
1711
|
-
|
|
1712
|
-
Avoid calling the same agent twice in one turn. Instead, prefer to call it once but provide everything
|
|
1713
|
-
you need from that agent.
|
|
1714
|
-
`;
|
|
1715
|
-
|
|
1716
1637
|
// src/qstash/headers.ts
|
|
1717
1638
|
var WorkflowHeaders = class {
|
|
1718
1639
|
userHeaders;
|
|
@@ -1721,14 +1642,15 @@ var WorkflowHeaders = class {
|
|
|
1721
1642
|
initHeaderValue;
|
|
1722
1643
|
stepInfo;
|
|
1723
1644
|
headers;
|
|
1724
|
-
|
|
1645
|
+
/**
|
|
1646
|
+
* @param params workflow header parameters
|
|
1647
|
+
*/
|
|
1725
1648
|
constructor({
|
|
1726
1649
|
userHeaders,
|
|
1727
1650
|
workflowConfig,
|
|
1728
1651
|
invokeCount,
|
|
1729
1652
|
initHeaderValue,
|
|
1730
|
-
stepInfo
|
|
1731
|
-
keepTriggerConfig
|
|
1653
|
+
stepInfo
|
|
1732
1654
|
}) {
|
|
1733
1655
|
this.userHeaders = userHeaders;
|
|
1734
1656
|
this.workflowConfig = workflowConfig;
|
|
@@ -1740,7 +1662,6 @@ var WorkflowHeaders = class {
|
|
|
1740
1662
|
workflowHeaders: {},
|
|
1741
1663
|
failureHeaders: {}
|
|
1742
1664
|
};
|
|
1743
|
-
this.keepTriggerConfig = keepTriggerConfig;
|
|
1744
1665
|
}
|
|
1745
1666
|
getHeaders() {
|
|
1746
1667
|
this.addBaseHeaders();
|
|
@@ -1759,10 +1680,9 @@ var WorkflowHeaders = class {
|
|
|
1759
1680
|
[WORKFLOW_INIT_HEADER]: this.initHeaderValue,
|
|
1760
1681
|
[WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
|
|
1761
1682
|
[WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
|
|
1762
|
-
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger
|
|
1683
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1763
1684
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1764
|
-
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1765
|
-
...this.workflowConfig.telemetry && this.stepInfo?.lazyStep instanceof LazyCallStep && this.stepInfo.lazyStep.headers[AGENT_NAME_HEADER] ? { [TELEMETRY_HEADER_AGENT]: "true" } : {}
|
|
1685
|
+
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1766
1686
|
};
|
|
1767
1687
|
if (this.stepInfo?.lazyStep.stepType !== "Call") {
|
|
1768
1688
|
this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
@@ -1830,12 +1750,12 @@ var WorkflowHeaders = class {
|
|
|
1830
1750
|
}
|
|
1831
1751
|
this.headers.workflowHeaders["Failure-Callback"] = this.workflowConfig.failureUrl;
|
|
1832
1752
|
this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
|
|
1833
|
-
this.headers.failureHeaders[`Forward
|
|
1753
|
+
this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_CALLBACK_HEADER}`] = "true";
|
|
1834
1754
|
this.headers.failureHeaders["Workflow-Runid"] = this.workflowConfig.workflowRunId;
|
|
1835
1755
|
this.headers.failureHeaders["Workflow-Init"] = "false";
|
|
1836
1756
|
this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
|
|
1837
1757
|
this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
|
|
1838
|
-
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger";
|
|
1758
|
+
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
1839
1759
|
if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
|
|
1840
1760
|
this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
|
|
1841
1761
|
}
|
|
@@ -1905,14 +1825,13 @@ var submitParallelSteps = async ({
|
|
|
1905
1825
|
initialStepCount,
|
|
1906
1826
|
invokeCount,
|
|
1907
1827
|
telemetry,
|
|
1908
|
-
|
|
1828
|
+
dispatchDebug
|
|
1909
1829
|
}) => {
|
|
1910
1830
|
const planSteps = steps.map(
|
|
1911
1831
|
(step, index) => step.getPlanStep(steps.length, initialStepCount + index)
|
|
1912
1832
|
);
|
|
1913
|
-
await
|
|
1914
|
-
|
|
1915
|
-
steps: planSteps
|
|
1833
|
+
await dispatchDebug("onInfo", {
|
|
1834
|
+
info: `Submitting ${planSteps.length} parallel steps.`
|
|
1916
1835
|
});
|
|
1917
1836
|
const result = await context.qstashClient.batch(
|
|
1918
1837
|
planSteps.map((planStep) => {
|
|
@@ -1921,10 +1840,6 @@ var submitParallelSteps = async ({
|
|
|
1921
1840
|
workflowConfig: {
|
|
1922
1841
|
workflowRunId: context.workflowRunId,
|
|
1923
1842
|
workflowUrl: context.url,
|
|
1924
|
-
failureUrl: context.failureUrl,
|
|
1925
|
-
retries: context.retries,
|
|
1926
|
-
retryDelay: context.retryDelay,
|
|
1927
|
-
flowControl: context.flowControl,
|
|
1928
1843
|
telemetry
|
|
1929
1844
|
},
|
|
1930
1845
|
userHeaders: context.headers,
|
|
@@ -1940,13 +1855,11 @@ var submitParallelSteps = async ({
|
|
|
1940
1855
|
};
|
|
1941
1856
|
})
|
|
1942
1857
|
);
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
})
|
|
1949
|
-
});
|
|
1858
|
+
if (result && result.length > 0) {
|
|
1859
|
+
await dispatchDebug("onInfo", {
|
|
1860
|
+
info: `Submitted ${planSteps.length} parallel steps. messageIds: ${result.filter((r) => r).map((r) => r.messageId).join(", ")}.`
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1950
1863
|
throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
|
|
1951
1864
|
};
|
|
1952
1865
|
var submitSingleStep = async ({
|
|
@@ -1956,14 +1869,13 @@ var submitSingleStep = async ({
|
|
|
1956
1869
|
invokeCount,
|
|
1957
1870
|
concurrency,
|
|
1958
1871
|
telemetry,
|
|
1959
|
-
|
|
1872
|
+
dispatchDebug,
|
|
1873
|
+
dispatchLifecycle
|
|
1960
1874
|
}) => {
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
fromRequest: false,
|
|
1964
|
-
step: resultStep,
|
|
1965
|
-
stepCount: stepId
|
|
1875
|
+
await dispatchLifecycle("beforeExecution", {
|
|
1876
|
+
stepName: lazyStep.stepName
|
|
1966
1877
|
});
|
|
1878
|
+
const resultStep = await lazyStep.getResultStep(concurrency, stepId);
|
|
1967
1879
|
const { headers } = lazyStep.getHeaders({
|
|
1968
1880
|
context,
|
|
1969
1881
|
step: resultStep,
|
|
@@ -1977,10 +1889,6 @@ var submitSingleStep = async ({
|
|
|
1977
1889
|
invokeCount,
|
|
1978
1890
|
telemetry
|
|
1979
1891
|
});
|
|
1980
|
-
await debug?.log("SUBMIT", "SUBMIT_STEP", {
|
|
1981
|
-
length: 1,
|
|
1982
|
-
steps: [resultStep]
|
|
1983
|
-
});
|
|
1984
1892
|
const submitResult = await lazyStep.submitStep({
|
|
1985
1893
|
context,
|
|
1986
1894
|
body,
|
|
@@ -1990,13 +1898,11 @@ var submitSingleStep = async ({
|
|
|
1990
1898
|
step: resultStep,
|
|
1991
1899
|
telemetry
|
|
1992
1900
|
});
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
})
|
|
1999
|
-
});
|
|
1901
|
+
if (submitResult && submitResult[0]) {
|
|
1902
|
+
await dispatchDebug("onInfo", {
|
|
1903
|
+
info: `Submitted step "${resultStep.stepName}" with messageId: ${submitResult[0].messageId}.`
|
|
1904
|
+
});
|
|
1905
|
+
}
|
|
2000
1906
|
return resultStep;
|
|
2001
1907
|
};
|
|
2002
1908
|
|
|
@@ -2005,21 +1911,31 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2005
1911
|
context;
|
|
2006
1912
|
promises = /* @__PURE__ */ new WeakMap();
|
|
2007
1913
|
activeLazyStepList;
|
|
2008
|
-
debug;
|
|
2009
1914
|
nonPlanStepCount;
|
|
2010
1915
|
steps;
|
|
2011
1916
|
indexInCurrentList = 0;
|
|
2012
1917
|
invokeCount;
|
|
2013
1918
|
telemetry;
|
|
1919
|
+
dispatchDebug;
|
|
1920
|
+
dispatchLifecycle;
|
|
2014
1921
|
stepCount = 0;
|
|
2015
1922
|
planStepCount = 0;
|
|
2016
1923
|
executingStep = false;
|
|
2017
|
-
|
|
1924
|
+
/**
|
|
1925
|
+
* @param context workflow context
|
|
1926
|
+
* @param steps list of steps
|
|
1927
|
+
* @param dispatchDebug debug event dispatcher
|
|
1928
|
+
* @param dispatchLifecycle lifecycle event dispatcher
|
|
1929
|
+
* @param telemetry optional telemetry information
|
|
1930
|
+
* @param invokeCount optional invoke count
|
|
1931
|
+
*/
|
|
1932
|
+
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount) {
|
|
2018
1933
|
this.context = context;
|
|
2019
1934
|
this.steps = steps;
|
|
1935
|
+
this.dispatchDebug = dispatchDebug;
|
|
1936
|
+
this.dispatchLifecycle = dispatchLifecycle;
|
|
2020
1937
|
this.telemetry = telemetry;
|
|
2021
1938
|
this.invokeCount = invokeCount ?? 0;
|
|
2022
|
-
this.debug = debug;
|
|
2023
1939
|
this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
|
|
2024
1940
|
}
|
|
2025
1941
|
/**
|
|
@@ -2087,7 +2003,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2087
2003
|
/**
|
|
2088
2004
|
* Executes a step:
|
|
2089
2005
|
* - If the step result is available in the steps, returns the result
|
|
2090
|
-
* - If the result is not
|
|
2006
|
+
* - If the result is not available, runs the function
|
|
2091
2007
|
* - Sends the result to QStash
|
|
2092
2008
|
*
|
|
2093
2009
|
* @param lazyStep lazy step to execute
|
|
@@ -2097,12 +2013,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2097
2013
|
if (this.stepCount < this.nonPlanStepCount) {
|
|
2098
2014
|
const step = this.steps[this.stepCount + this.planStepCount];
|
|
2099
2015
|
validateStep(lazyStep, step);
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2016
|
+
const parsedOut = lazyStep.parseOut(step);
|
|
2017
|
+
const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
|
|
2018
|
+
if (isLastMemoized) {
|
|
2019
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2020
|
+
stepName: lazyStep.stepName,
|
|
2021
|
+
result: parsedOut
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
return parsedOut;
|
|
2106
2025
|
}
|
|
2107
2026
|
const resultStep = await submitSingleStep({
|
|
2108
2027
|
context: this.context,
|
|
@@ -2111,15 +2030,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2111
2030
|
invokeCount: this.invokeCount,
|
|
2112
2031
|
concurrency: 1,
|
|
2113
2032
|
telemetry: this.telemetry,
|
|
2114
|
-
|
|
2033
|
+
dispatchDebug: this.dispatchDebug,
|
|
2034
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2115
2035
|
});
|
|
2116
2036
|
throw new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2117
2037
|
}
|
|
2118
2038
|
/**
|
|
2119
2039
|
* Runs steps in parallel.
|
|
2120
2040
|
*
|
|
2121
|
-
* @param
|
|
2122
|
-
* @param stepFunctions list of async functions to run in parallel
|
|
2041
|
+
* @param parallelSteps list of lazy steps to run in parallel
|
|
2123
2042
|
* @returns results of the functions run in parallel
|
|
2124
2043
|
*/
|
|
2125
2044
|
async runParallel(parallelSteps) {
|
|
@@ -2132,12 +2051,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2132
2051
|
`Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
|
|
2133
2052
|
);
|
|
2134
2053
|
}
|
|
2135
|
-
await this.
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2054
|
+
await this.dispatchDebug("onInfo", {
|
|
2055
|
+
info: `Executing parallel steps with: ` + JSON.stringify({
|
|
2056
|
+
parallelCallState,
|
|
2057
|
+
initialStepCount,
|
|
2058
|
+
plannedParallelStepCount,
|
|
2059
|
+
stepCount: this.stepCount,
|
|
2060
|
+
planStepCount: this.planStepCount
|
|
2061
|
+
})
|
|
2141
2062
|
});
|
|
2142
2063
|
switch (parallelCallState) {
|
|
2143
2064
|
case "first": {
|
|
@@ -2147,7 +2068,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2147
2068
|
initialStepCount,
|
|
2148
2069
|
invokeCount: this.invokeCount,
|
|
2149
2070
|
telemetry: this.telemetry,
|
|
2150
|
-
|
|
2071
|
+
dispatchDebug: this.dispatchDebug
|
|
2151
2072
|
});
|
|
2152
2073
|
break;
|
|
2153
2074
|
}
|
|
@@ -2169,7 +2090,8 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2169
2090
|
invokeCount: this.invokeCount,
|
|
2170
2091
|
concurrency: parallelSteps.length,
|
|
2171
2092
|
telemetry: this.telemetry,
|
|
2172
|
-
|
|
2093
|
+
dispatchDebug: this.dispatchDebug,
|
|
2094
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2173
2095
|
});
|
|
2174
2096
|
throw new WorkflowAbort(parallelStep.stepName, resultStep);
|
|
2175
2097
|
} catch (error) {
|
|
@@ -2183,11 +2105,32 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2183
2105
|
break;
|
|
2184
2106
|
}
|
|
2185
2107
|
case "discard": {
|
|
2108
|
+
const resultStep = this.steps.at(-1);
|
|
2109
|
+
const lazyStep = parallelSteps.find(
|
|
2110
|
+
(planStep, index) => resultStep.stepId - index === initialStepCount
|
|
2111
|
+
);
|
|
2112
|
+
if (lazyStep) {
|
|
2113
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2114
|
+
stepName: lazyStep.stepName,
|
|
2115
|
+
result: lazyStep.parseOut(resultStep)
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2186
2118
|
throw new WorkflowAbort("discarded parallel");
|
|
2187
2119
|
}
|
|
2188
2120
|
case "last": {
|
|
2189
2121
|
const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
|
|
2190
2122
|
validateParallelSteps(parallelSteps, parallelResultSteps);
|
|
2123
|
+
const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
|
|
2124
|
+
if (isLastMemoized) {
|
|
2125
|
+
const resultStep = this.steps.at(-1);
|
|
2126
|
+
const lazyStep = parallelSteps.find(
|
|
2127
|
+
(planStep, index) => resultStep.stepId - index === initialStepCount
|
|
2128
|
+
);
|
|
2129
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2130
|
+
stepName: lazyStep.stepName,
|
|
2131
|
+
result: lazyStep.parseOut(resultStep)
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2191
2134
|
return parallelResultSteps.map(
|
|
2192
2135
|
(step, index) => parallelSteps[index].parseOut(step)
|
|
2193
2136
|
);
|
|
@@ -2331,15 +2274,18 @@ var getProviderInfo = (api) => {
|
|
|
2331
2274
|
// src/context/api/base.ts
|
|
2332
2275
|
var BaseWorkflowApi = class {
|
|
2333
2276
|
context;
|
|
2277
|
+
/**
|
|
2278
|
+
* @param context workflow context
|
|
2279
|
+
*/
|
|
2334
2280
|
constructor({ context }) {
|
|
2335
2281
|
this.context = context;
|
|
2336
2282
|
}
|
|
2337
2283
|
/**
|
|
2338
2284
|
* context.call which uses a QStash API
|
|
2339
2285
|
*
|
|
2340
|
-
* @param stepName
|
|
2341
|
-
* @param settings
|
|
2342
|
-
* @returns
|
|
2286
|
+
* @param stepName name of the step
|
|
2287
|
+
* @param settings call settings including api configuration
|
|
2288
|
+
* @returns call response
|
|
2343
2289
|
*/
|
|
2344
2290
|
async callApi(stepName, settings) {
|
|
2345
2291
|
const { url, appendHeaders, method } = getProviderInfo(settings.api);
|
|
@@ -2347,7 +2293,7 @@ var BaseWorkflowApi = class {
|
|
|
2347
2293
|
return await this.context.call(stepName, {
|
|
2348
2294
|
url,
|
|
2349
2295
|
method: userMethod ?? method,
|
|
2350
|
-
body,
|
|
2296
|
+
body: typeof body === "string" ? body : JSON.stringify(body),
|
|
2351
2297
|
headers: {
|
|
2352
2298
|
...appendHeaders,
|
|
2353
2299
|
...headers
|
|
@@ -2424,315 +2370,135 @@ var WorkflowApi = class extends BaseWorkflowApi {
|
|
|
2424
2370
|
}
|
|
2425
2371
|
};
|
|
2426
2372
|
|
|
2427
|
-
// src/
|
|
2428
|
-
var
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
var import_ai = require("ai");
|
|
2432
|
-
var fetchWithContextCall = async (context, agentCallParams, ...params) => {
|
|
2433
|
-
const [input, init] = params;
|
|
2434
|
-
try {
|
|
2435
|
-
const headers = init?.headers ? Object.fromEntries(new Headers(init.headers).entries()) : {};
|
|
2436
|
-
const body = init?.body ? JSON.parse(init.body) : void 0;
|
|
2437
|
-
const agentName = headers[AGENT_NAME_HEADER];
|
|
2438
|
-
const stepName = agentName ? `Call Agent ${agentName}` : "Call Agent";
|
|
2439
|
-
const responseInfo = await context.call(stepName, {
|
|
2440
|
-
url: input.toString(),
|
|
2441
|
-
method: init?.method,
|
|
2442
|
-
headers,
|
|
2443
|
-
body,
|
|
2444
|
-
timeout: agentCallParams?.timeout,
|
|
2445
|
-
retries: agentCallParams?.retries,
|
|
2446
|
-
retryDelay: agentCallParams?.retryDelay,
|
|
2447
|
-
flowControl: agentCallParams?.flowControl
|
|
2448
|
-
});
|
|
2449
|
-
const responseHeaders = new Headers(
|
|
2450
|
-
Object.entries(responseInfo.header).reduce(
|
|
2451
|
-
(acc, [key, values]) => {
|
|
2452
|
-
acc[key] = values.join(", ");
|
|
2453
|
-
return acc;
|
|
2454
|
-
},
|
|
2455
|
-
{}
|
|
2456
|
-
)
|
|
2457
|
-
);
|
|
2458
|
-
return new Response(JSON.stringify(responseInfo.body), {
|
|
2459
|
-
status: responseInfo.status,
|
|
2460
|
-
headers: responseHeaders
|
|
2461
|
-
});
|
|
2462
|
-
} catch (error) {
|
|
2463
|
-
if (error instanceof Error && isInstanceOf(error, WorkflowAbort)) {
|
|
2464
|
-
throw error;
|
|
2465
|
-
} else {
|
|
2466
|
-
console.error("Error in fetch implementation:", error);
|
|
2467
|
-
throw error;
|
|
2468
|
-
}
|
|
2373
|
+
// src/serve/serve-many.ts
|
|
2374
|
+
var getNewUrlFromWorkflowId = (url, workflowId) => {
|
|
2375
|
+
if (!workflowId) {
|
|
2376
|
+
throw new WorkflowError("You can only call workflow which has a workflowId");
|
|
2469
2377
|
}
|
|
2378
|
+
return url.replace(/[^/]+$/, workflowId);
|
|
2470
2379
|
};
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
agentCallParams
|
|
2476
|
-
}) => {
|
|
2477
|
-
return provider({
|
|
2478
|
-
fetch: (...params) => fetchWithContextCall(context, agentCallParams, ...params),
|
|
2479
|
-
...providerParams
|
|
2480
|
-
});
|
|
2481
|
-
};
|
|
2482
|
-
var wrapTools = ({
|
|
2483
|
-
context,
|
|
2484
|
-
tools
|
|
2485
|
-
}) => {
|
|
2486
|
-
return Object.fromEntries(
|
|
2487
|
-
Object.entries(tools).map((toolInfo) => {
|
|
2488
|
-
const [toolName, tool3] = toolInfo;
|
|
2489
|
-
const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
|
|
2490
|
-
const aiSDKTool = convertToAISDKTool(tool3);
|
|
2491
|
-
const execute = aiSDKTool.execute;
|
|
2492
|
-
if (execute && executeAsStep) {
|
|
2493
|
-
const wrappedExecute = (...params) => {
|
|
2494
|
-
return context.run(`Run tool ${toolName}`, () => execute(...params));
|
|
2495
|
-
};
|
|
2496
|
-
aiSDKTool.execute = wrappedExecute;
|
|
2497
|
-
}
|
|
2498
|
-
return [toolName, aiSDKTool];
|
|
2499
|
-
})
|
|
2500
|
-
);
|
|
2501
|
-
};
|
|
2502
|
-
var convertToAISDKTool = (tool3) => {
|
|
2503
|
-
const isLangchainTool = "invoke" in tool3;
|
|
2504
|
-
return isLangchainTool ? convertLangchainTool(tool3) : tool3;
|
|
2380
|
+
|
|
2381
|
+
// src/middleware/default-callbacks.ts
|
|
2382
|
+
var onErrorWithConsole = async ({ workflowRunId, error }) => {
|
|
2383
|
+
console.error(` [Upstash Workflow]: Error in workflow run ${workflowRunId}: ` + error);
|
|
2505
2384
|
};
|
|
2506
|
-
var
|
|
2507
|
-
|
|
2508
|
-
description: langchainTool.description,
|
|
2509
|
-
parameters: langchainTool.schema,
|
|
2510
|
-
execute: async (...param) => langchainTool.invoke(...param)
|
|
2511
|
-
});
|
|
2385
|
+
var onWarningWithConsole = async ({ workflowRunId, warning }) => {
|
|
2386
|
+
console.warn(` [Upstash Workflow]: Warning in workflow run ${workflowRunId}: ` + warning);
|
|
2512
2387
|
};
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
// src/serve/utils.ts
|
|
2519
|
-
var isDisabledWorkflowContext = (context) => {
|
|
2520
|
-
return "disabled" in context;
|
|
2388
|
+
var onInfoWithConsole = async ({
|
|
2389
|
+
workflowRunId,
|
|
2390
|
+
info
|
|
2391
|
+
}) => {
|
|
2392
|
+
console.info(` [Upstash Workflow]: Info in workflow run ${workflowRunId}: ` + info);
|
|
2521
2393
|
};
|
|
2522
2394
|
|
|
2523
|
-
// src/
|
|
2524
|
-
var
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
maxSteps;
|
|
2528
|
-
background;
|
|
2529
|
-
model;
|
|
2530
|
-
temparature;
|
|
2395
|
+
// src/middleware/manager.ts
|
|
2396
|
+
var MiddlewareManager = class {
|
|
2397
|
+
middlewares;
|
|
2398
|
+
workflowRunId;
|
|
2531
2399
|
context;
|
|
2532
|
-
constructor({ tools, maxSteps, background, name, model, temparature = 0.1 }, context) {
|
|
2533
|
-
this.name = name;
|
|
2534
|
-
this.tools = tools ?? {};
|
|
2535
|
-
this.maxSteps = maxSteps;
|
|
2536
|
-
this.background = background;
|
|
2537
|
-
this.model = model;
|
|
2538
|
-
this.temparature = temparature;
|
|
2539
|
-
this.context = context;
|
|
2540
|
-
}
|
|
2541
2400
|
/**
|
|
2542
|
-
*
|
|
2543
|
-
*
|
|
2544
|
-
* @param prompt task to assign to the agent
|
|
2545
|
-
* @returns Response as `{ text: string }`
|
|
2401
|
+
* @param middlewares list of workflow middlewares
|
|
2546
2402
|
*/
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
if (isDisabledWorkflowContext(this.context)) {
|
|
2550
|
-
await this.context.sleep("abort", 0);
|
|
2551
|
-
}
|
|
2552
|
-
const result = await (0, import_ai2.generateText)({
|
|
2553
|
-
model: this.model,
|
|
2554
|
-
tools: this.tools,
|
|
2555
|
-
maxSteps: this.maxSteps,
|
|
2556
|
-
system: this.background,
|
|
2557
|
-
prompt,
|
|
2558
|
-
headers: {
|
|
2559
|
-
[AGENT_NAME_HEADER]: this.name
|
|
2560
|
-
},
|
|
2561
|
-
temperature: this.temparature
|
|
2562
|
-
});
|
|
2563
|
-
return { text: result.text };
|
|
2564
|
-
} catch (error) {
|
|
2565
|
-
if (isInstanceOf(error, import_ai2.ToolExecutionError)) {
|
|
2566
|
-
if (error.cause instanceof Error && isInstanceOf(error.cause, WorkflowAbort)) {
|
|
2567
|
-
throw error.cause;
|
|
2568
|
-
} else if (isInstanceOf(error.cause, import_ai2.ToolExecutionError) && isInstanceOf(error.cause.cause, WorkflowAbort)) {
|
|
2569
|
-
throw error.cause.cause;
|
|
2570
|
-
} else {
|
|
2571
|
-
throw error;
|
|
2572
|
-
}
|
|
2573
|
-
} else {
|
|
2574
|
-
throw error;
|
|
2575
|
-
}
|
|
2576
|
-
}
|
|
2403
|
+
constructor(middlewares = []) {
|
|
2404
|
+
this.middlewares = middlewares;
|
|
2577
2405
|
}
|
|
2578
2406
|
/**
|
|
2579
|
-
*
|
|
2407
|
+
* Assign workflow run ID - will be passed to debug events
|
|
2580
2408
|
*
|
|
2581
|
-
* @
|
|
2409
|
+
* @param workflowRunId workflow run id to assign
|
|
2582
2410
|
*/
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
return (0, import_ai2.tool)({
|
|
2586
|
-
parameters: import_zod.z.object({ prompt: import_zod.z.string() }),
|
|
2587
|
-
execute: async ({ prompt }) => {
|
|
2588
|
-
return await this.call({ prompt });
|
|
2589
|
-
},
|
|
2590
|
-
description: `An AI Agent with the following background: ${this.background}Has access to the following tools: ${toolDescriptions}`
|
|
2591
|
-
});
|
|
2411
|
+
assignWorkflowRunId(workflowRunId) {
|
|
2412
|
+
this.workflowRunId = workflowRunId;
|
|
2592
2413
|
}
|
|
2593
|
-
};
|
|
2594
|
-
var ManagerAgent = class extends Agent {
|
|
2595
|
-
agents;
|
|
2596
2414
|
/**
|
|
2597
|
-
*
|
|
2598
|
-
* given task
|
|
2415
|
+
* Assign context - required for lifecycle events
|
|
2599
2416
|
*
|
|
2600
|
-
*
|
|
2601
|
-
*
|
|
2602
|
-
* @param
|
|
2603
|
-
* @param agents: List of agents available to the agent
|
|
2604
|
-
* @param maxSteps number of times the manager agent can call the LLM at most.
|
|
2605
|
-
* If the agent abruptly stops execution after calling other agents, you may
|
|
2606
|
-
* need to increase maxSteps
|
|
2417
|
+
* also assigns workflowRunId from context
|
|
2418
|
+
*
|
|
2419
|
+
* @param context workflow context to assign
|
|
2607
2420
|
*/
|
|
2608
|
-
|
|
2609
|
-
agents,
|
|
2610
|
-
background = MANAGER_AGENT_PROMPT,
|
|
2611
|
-
model,
|
|
2612
|
-
maxSteps,
|
|
2613
|
-
name = "manager llm"
|
|
2614
|
-
}, context) {
|
|
2615
|
-
super(
|
|
2616
|
-
{
|
|
2617
|
-
background,
|
|
2618
|
-
maxSteps,
|
|
2619
|
-
tools: Object.fromEntries(agents.map((agent) => [agent.name, agent.asTool()])),
|
|
2620
|
-
name,
|
|
2621
|
-
model
|
|
2622
|
-
},
|
|
2623
|
-
context
|
|
2624
|
-
);
|
|
2625
|
-
this.agents = agents;
|
|
2626
|
-
}
|
|
2627
|
-
};
|
|
2628
|
-
|
|
2629
|
-
// src/agents/task.ts
|
|
2630
|
-
var Task = class {
|
|
2631
|
-
context;
|
|
2632
|
-
taskParameters;
|
|
2633
|
-
constructor({
|
|
2634
|
-
context,
|
|
2635
|
-
taskParameters
|
|
2636
|
-
}) {
|
|
2421
|
+
assignContext(context) {
|
|
2637
2422
|
this.context = context;
|
|
2638
|
-
this.
|
|
2423
|
+
this.workflowRunId = context.workflowRunId;
|
|
2639
2424
|
}
|
|
2640
2425
|
/**
|
|
2641
|
-
*
|
|
2426
|
+
* Internal method to execute middlewares with common error handling logic
|
|
2642
2427
|
*
|
|
2643
|
-
* @
|
|
2428
|
+
* @param event event name to dispatch
|
|
2429
|
+
* @param params event parameters
|
|
2644
2430
|
*/
|
|
2645
|
-
async
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2431
|
+
async executeMiddlewares(event, params) {
|
|
2432
|
+
await Promise.all(this.middlewares.map((m) => m.ensureInit()));
|
|
2433
|
+
await Promise.all(
|
|
2434
|
+
this.middlewares.map(async (middleware) => {
|
|
2435
|
+
const callback = middleware.getCallback(event);
|
|
2436
|
+
if (callback) {
|
|
2437
|
+
try {
|
|
2438
|
+
await callback(params);
|
|
2439
|
+
} catch (error) {
|
|
2440
|
+
try {
|
|
2441
|
+
const onErrorCallback = middleware.getCallback("onError") ?? onErrorWithConsole;
|
|
2442
|
+
await onErrorCallback({
|
|
2443
|
+
workflowRunId: this.workflowRunId,
|
|
2444
|
+
error
|
|
2445
|
+
});
|
|
2446
|
+
} catch (onErrorError) {
|
|
2447
|
+
console.error(
|
|
2448
|
+
`Failed while executing "onError" of middleware "${middleware.name}", falling back to logging the error to console. Error: ${onErrorError}`
|
|
2449
|
+
);
|
|
2450
|
+
onErrorWithConsole({
|
|
2451
|
+
workflowRunId: this.workflowRunId,
|
|
2452
|
+
error
|
|
2453
|
+
});
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
})
|
|
2458
|
+
);
|
|
2459
|
+
if (event === "onError") {
|
|
2460
|
+
onErrorWithConsole({
|
|
2461
|
+
workflowRunId: this.workflowRunId,
|
|
2462
|
+
...params
|
|
2463
|
+
});
|
|
2464
|
+
} else if (event === "onWarning") {
|
|
2465
|
+
onWarningWithConsole({
|
|
2466
|
+
workflowRunId: this.workflowRunId,
|
|
2467
|
+
...params
|
|
2651
2468
|
});
|
|
2652
|
-
return { text: result.text };
|
|
2653
|
-
} else {
|
|
2654
|
-
const { agents, maxSteps, model, background } = otherParams;
|
|
2655
|
-
const managerAgent = new ManagerAgent(
|
|
2656
|
-
{
|
|
2657
|
-
model,
|
|
2658
|
-
maxSteps,
|
|
2659
|
-
agents,
|
|
2660
|
-
name: "Manager LLM",
|
|
2661
|
-
background
|
|
2662
|
-
},
|
|
2663
|
-
this.context
|
|
2664
|
-
);
|
|
2665
|
-
const result = await managerAgent.call({ prompt });
|
|
2666
|
-
return { text: result.text };
|
|
2667
2469
|
}
|
|
2668
2470
|
}
|
|
2669
|
-
};
|
|
2670
|
-
|
|
2671
|
-
// src/agents/index.ts
|
|
2672
|
-
var WorkflowAgents = class {
|
|
2673
|
-
context;
|
|
2674
|
-
constructor({ context }) {
|
|
2675
|
-
this.context = context;
|
|
2676
|
-
}
|
|
2677
2471
|
/**
|
|
2678
|
-
*
|
|
2472
|
+
* Dispatch a debug event (onError, onWarning, onInfo)
|
|
2679
2473
|
*
|
|
2680
|
-
*
|
|
2681
|
-
*
|
|
2682
|
-
* model,
|
|
2683
|
-
* name: 'academic',
|
|
2684
|
-
* maxSteps: 2,
|
|
2685
|
-
* tools: {
|
|
2686
|
-
* wikiTool: new WikipediaQueryRun({
|
|
2687
|
-
* topKResults: 1,
|
|
2688
|
-
* maxDocContentLength: 500,
|
|
2689
|
-
* })
|
|
2690
|
-
* },
|
|
2691
|
-
* background:
|
|
2692
|
-
* 'You are researcher agent with access to Wikipedia. ' +
|
|
2693
|
-
* 'Utilize Wikipedia as much as possible for correct information',
|
|
2694
|
-
* });
|
|
2695
|
-
* ```
|
|
2696
|
-
*
|
|
2697
|
-
* @param params agent parameters
|
|
2698
|
-
* @returns
|
|
2474
|
+
* @param event debug event name
|
|
2475
|
+
* @param params event parameters
|
|
2699
2476
|
*/
|
|
2700
|
-
|
|
2701
|
-
const
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
},
|
|
2707
|
-
this.context
|
|
2708
|
-
);
|
|
2709
|
-
}
|
|
2710
|
-
task(taskParameters) {
|
|
2711
|
-
return new Task({ context: this.context, taskParameters });
|
|
2477
|
+
async dispatchDebug(event, params) {
|
|
2478
|
+
const paramsWithRunId = {
|
|
2479
|
+
...params,
|
|
2480
|
+
workflowRunId: this.workflowRunId
|
|
2481
|
+
};
|
|
2482
|
+
await this.executeMiddlewares(event, paramsWithRunId);
|
|
2712
2483
|
}
|
|
2713
2484
|
/**
|
|
2714
|
-
*
|
|
2485
|
+
* Dispatch a lifecycle event (beforeExecution, afterExecution, runStarted, runCompleted)
|
|
2486
|
+
*
|
|
2487
|
+
* @param event lifecycle event name
|
|
2488
|
+
* @param params event parameters
|
|
2715
2489
|
*/
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
AISDKModel = createWorkflowModel;
|
|
2728
|
-
};
|
|
2729
|
-
|
|
2730
|
-
// src/serve/serve-many.ts
|
|
2731
|
-
var getNewUrlFromWorkflowId = (url, workflowId) => {
|
|
2732
|
-
if (!workflowId) {
|
|
2733
|
-
throw new WorkflowError("You can only call workflow which has a workflowId");
|
|
2490
|
+
async dispatchLifecycle(event, params) {
|
|
2491
|
+
if (!this.context) {
|
|
2492
|
+
throw new WorkflowError(
|
|
2493
|
+
`Something went wrong while calling middlewares. Lifecycle event "${event}" was called before assignContext.`
|
|
2494
|
+
);
|
|
2495
|
+
}
|
|
2496
|
+
const paramsWithContext = {
|
|
2497
|
+
...params,
|
|
2498
|
+
context: this.context
|
|
2499
|
+
};
|
|
2500
|
+
await this.executeMiddlewares(event, paramsWithContext);
|
|
2734
2501
|
}
|
|
2735
|
-
return url.replace(/[^/]+$/, workflowId);
|
|
2736
2502
|
};
|
|
2737
2503
|
|
|
2738
2504
|
// src/context/context.ts
|
|
@@ -2779,25 +2545,6 @@ var WorkflowContext = class {
|
|
|
2779
2545
|
* ```
|
|
2780
2546
|
*/
|
|
2781
2547
|
url;
|
|
2782
|
-
/**
|
|
2783
|
-
* URL to call in case of workflow failure with QStash failure callback
|
|
2784
|
-
*
|
|
2785
|
-
* https://upstash.com/docs/qstash/features/callbacks#what-is-a-failure-callback
|
|
2786
|
-
*
|
|
2787
|
-
* Can be overwritten by passing a `failureUrl` parameter in `serve`:
|
|
2788
|
-
*
|
|
2789
|
-
* ```ts
|
|
2790
|
-
* export const POST = serve(
|
|
2791
|
-
* async (context) => {
|
|
2792
|
-
* ...
|
|
2793
|
-
* },
|
|
2794
|
-
* {
|
|
2795
|
-
* failureUrl: "new-url-value"
|
|
2796
|
-
* }
|
|
2797
|
-
* )
|
|
2798
|
-
* ```
|
|
2799
|
-
*/
|
|
2800
|
-
failureUrl;
|
|
2801
2548
|
/**
|
|
2802
2549
|
* Payload of the request which started the workflow.
|
|
2803
2550
|
*
|
|
@@ -2853,46 +2600,6 @@ var WorkflowContext = class {
|
|
|
2853
2600
|
* Default value is set to `process.env`.
|
|
2854
2601
|
*/
|
|
2855
2602
|
env;
|
|
2856
|
-
/**
|
|
2857
|
-
* Number of retries
|
|
2858
|
-
*/
|
|
2859
|
-
retries;
|
|
2860
|
-
/**
|
|
2861
|
-
* Delay between retries.
|
|
2862
|
-
*
|
|
2863
|
-
* By default, the `retryDelay` is exponential backoff.
|
|
2864
|
-
* More details can be found in: https://upstash.com/docs/qstash/features/retry.
|
|
2865
|
-
*
|
|
2866
|
-
* The `retryDelay` option allows you to customize the delay (in milliseconds) between retry attempts when message delivery fails.
|
|
2867
|
-
*
|
|
2868
|
-
* You can use mathematical expressions and the following built-in functions to calculate the delay dynamically.
|
|
2869
|
-
* The special variable `retried` represents the current retry attempt count (starting from 0).
|
|
2870
|
-
*
|
|
2871
|
-
* Supported functions:
|
|
2872
|
-
* - `pow`
|
|
2873
|
-
* - `sqrt`
|
|
2874
|
-
* - `abs`
|
|
2875
|
-
* - `exp`
|
|
2876
|
-
* - `floor`
|
|
2877
|
-
* - `ceil`
|
|
2878
|
-
* - `round`
|
|
2879
|
-
* - `min`
|
|
2880
|
-
* - `max`
|
|
2881
|
-
*
|
|
2882
|
-
* Examples of valid `retryDelay` values:
|
|
2883
|
-
* ```ts
|
|
2884
|
-
* 1000 // 1 second
|
|
2885
|
-
* 1000 * (1 + retried) // 1 second multiplied by the current retry attempt
|
|
2886
|
-
* pow(2, retried) // 2 to the power of the current retry attempt
|
|
2887
|
-
* max(10, pow(2, retried)) // The greater of 10 or 2^retried
|
|
2888
|
-
* ```
|
|
2889
|
-
*/
|
|
2890
|
-
retryDelay;
|
|
2891
|
-
/**
|
|
2892
|
-
* Settings for controlling the number of active requests
|
|
2893
|
-
* and number of requests per second with the same key.
|
|
2894
|
-
*/
|
|
2895
|
-
flowControl;
|
|
2896
2603
|
/**
|
|
2897
2604
|
* Label to apply to the workflow run.
|
|
2898
2605
|
*
|
|
@@ -2915,30 +2622,31 @@ var WorkflowContext = class {
|
|
|
2915
2622
|
headers,
|
|
2916
2623
|
steps,
|
|
2917
2624
|
url,
|
|
2918
|
-
failureUrl,
|
|
2919
|
-
debug,
|
|
2920
2625
|
initialPayload,
|
|
2921
2626
|
env,
|
|
2922
|
-
retries,
|
|
2923
|
-
retryDelay,
|
|
2924
2627
|
telemetry,
|
|
2925
2628
|
invokeCount,
|
|
2926
|
-
|
|
2927
|
-
|
|
2629
|
+
label,
|
|
2630
|
+
middlewareManager
|
|
2928
2631
|
}) {
|
|
2929
2632
|
this.qstashClient = qstashClient;
|
|
2930
2633
|
this.workflowRunId = workflowRunId;
|
|
2931
2634
|
this.steps = steps;
|
|
2932
2635
|
this.url = url;
|
|
2933
|
-
this.failureUrl = failureUrl;
|
|
2934
2636
|
this.headers = headers;
|
|
2935
2637
|
this.requestPayload = initialPayload;
|
|
2936
2638
|
this.env = env ?? {};
|
|
2937
|
-
this.retries = retries ?? DEFAULT_RETRIES;
|
|
2938
|
-
this.retryDelay = retryDelay;
|
|
2939
|
-
this.flowControl = flowControl;
|
|
2940
2639
|
this.label = label;
|
|
2941
|
-
|
|
2640
|
+
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
2641
|
+
middlewareManagerInstance.assignContext(this);
|
|
2642
|
+
this.executor = new AutoExecutor(
|
|
2643
|
+
this,
|
|
2644
|
+
this.steps,
|
|
2645
|
+
middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
|
|
2646
|
+
middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
|
|
2647
|
+
telemetry,
|
|
2648
|
+
invokeCount
|
|
2649
|
+
);
|
|
2942
2650
|
}
|
|
2943
2651
|
/**
|
|
2944
2652
|
* Executes a workflow step
|
|
@@ -2968,7 +2676,7 @@ var WorkflowContext = class {
|
|
|
2968
2676
|
* @returns result of the step function
|
|
2969
2677
|
*/
|
|
2970
2678
|
async run(stepName, stepFunction) {
|
|
2971
|
-
const wrappedStepFunction = () => this.executor.wrapStep(stepName, stepFunction);
|
|
2679
|
+
const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
|
|
2972
2680
|
return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
|
|
2973
2681
|
}
|
|
2974
2682
|
/**
|
|
@@ -3011,44 +2719,32 @@ var WorkflowContext = class {
|
|
|
3011
2719
|
let callStep;
|
|
3012
2720
|
if ("workflow" in settings) {
|
|
3013
2721
|
const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
|
|
3014
|
-
|
|
3015
|
-
|
|
2722
|
+
const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
|
|
2723
|
+
callStep = new LazyCallStep({
|
|
2724
|
+
context: this,
|
|
3016
2725
|
stepName,
|
|
3017
2726
|
url,
|
|
3018
|
-
"POST",
|
|
3019
|
-
|
|
3020
|
-
settings.headers || {},
|
|
3021
|
-
settings.retries || 0,
|
|
3022
|
-
settings.retryDelay,
|
|
3023
|
-
settings.timeout,
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
);
|
|
2727
|
+
method: "POST",
|
|
2728
|
+
body: stringBody,
|
|
2729
|
+
headers: settings.headers || {},
|
|
2730
|
+
retries: settings.retries || 0,
|
|
2731
|
+
retryDelay: settings.retryDelay,
|
|
2732
|
+
timeout: settings.timeout,
|
|
2733
|
+
flowControl: settings.flowControl
|
|
2734
|
+
});
|
|
3027
2735
|
} else {
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
method = "GET",
|
|
3031
|
-
body,
|
|
3032
|
-
headers = {},
|
|
3033
|
-
retries = 0,
|
|
3034
|
-
retryDelay,
|
|
3035
|
-
timeout,
|
|
3036
|
-
flowControl,
|
|
3037
|
-
stringifyBody = true
|
|
3038
|
-
} = settings;
|
|
3039
|
-
callStep = new LazyCallStep(
|
|
3040
|
-
this,
|
|
2736
|
+
callStep = new LazyCallStep({
|
|
2737
|
+
context: this,
|
|
3041
2738
|
stepName,
|
|
3042
|
-
url,
|
|
3043
|
-
method,
|
|
3044
|
-
body,
|
|
3045
|
-
headers,
|
|
3046
|
-
retries,
|
|
3047
|
-
retryDelay,
|
|
3048
|
-
timeout,
|
|
3049
|
-
flowControl
|
|
3050
|
-
|
|
3051
|
-
);
|
|
2739
|
+
url: settings.url,
|
|
2740
|
+
method: settings.method ?? "GET",
|
|
2741
|
+
body: settings.body,
|
|
2742
|
+
headers: settings.headers ?? {},
|
|
2743
|
+
retries: settings.retries ?? 0,
|
|
2744
|
+
retryDelay: settings.retryDelay,
|
|
2745
|
+
timeout: settings.timeout,
|
|
2746
|
+
flowControl: settings.flowControl
|
|
2747
|
+
});
|
|
3052
2748
|
}
|
|
3053
2749
|
return await this.addStep(callStep);
|
|
3054
2750
|
}
|
|
@@ -3133,11 +2829,11 @@ var WorkflowContext = class {
|
|
|
3133
2829
|
/**
|
|
3134
2830
|
* Cancel the current workflow run
|
|
3135
2831
|
*
|
|
3136
|
-
* Will throw
|
|
2832
|
+
* Will throw WorkflowCancelAbort to stop workflow execution.
|
|
3137
2833
|
* Shouldn't be inside try/catch.
|
|
3138
2834
|
*/
|
|
3139
2835
|
async cancel() {
|
|
3140
|
-
throw new
|
|
2836
|
+
throw new WorkflowCancelAbort();
|
|
3141
2837
|
}
|
|
3142
2838
|
/**
|
|
3143
2839
|
* Adds steps to the executor. Needed so that it can be overwritten in
|
|
@@ -3151,60 +2847,6 @@ var WorkflowContext = class {
|
|
|
3151
2847
|
context: this
|
|
3152
2848
|
});
|
|
3153
2849
|
}
|
|
3154
|
-
get agents() {
|
|
3155
|
-
return new WorkflowAgents({
|
|
3156
|
-
context: this
|
|
3157
|
-
});
|
|
3158
|
-
}
|
|
3159
|
-
};
|
|
3160
|
-
|
|
3161
|
-
// src/logger.ts
|
|
3162
|
-
var LOG_LEVELS = ["DEBUG", "INFO", "SUBMIT", "WARN", "ERROR"];
|
|
3163
|
-
var WorkflowLogger = class _WorkflowLogger {
|
|
3164
|
-
logs = [];
|
|
3165
|
-
options;
|
|
3166
|
-
workflowRunId = void 0;
|
|
3167
|
-
constructor(options) {
|
|
3168
|
-
this.options = options;
|
|
3169
|
-
}
|
|
3170
|
-
async log(level, eventType, details) {
|
|
3171
|
-
if (this.shouldLog(level)) {
|
|
3172
|
-
const timestamp = Date.now();
|
|
3173
|
-
const logEntry = {
|
|
3174
|
-
timestamp,
|
|
3175
|
-
workflowRunId: this.workflowRunId ?? "",
|
|
3176
|
-
logLevel: level,
|
|
3177
|
-
eventType,
|
|
3178
|
-
details
|
|
3179
|
-
};
|
|
3180
|
-
this.logs.push(logEntry);
|
|
3181
|
-
if (this.options.logOutput === "console") {
|
|
3182
|
-
this.writeToConsole(logEntry);
|
|
3183
|
-
}
|
|
3184
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3185
|
-
}
|
|
3186
|
-
}
|
|
3187
|
-
setWorkflowRunId(workflowRunId) {
|
|
3188
|
-
this.workflowRunId = workflowRunId;
|
|
3189
|
-
}
|
|
3190
|
-
writeToConsole(logEntry) {
|
|
3191
|
-
const JSON_SPACING = 2;
|
|
3192
|
-
const logMethod = logEntry.logLevel === "ERROR" ? console.error : logEntry.logLevel === "WARN" ? console.warn : console.log;
|
|
3193
|
-
logMethod(JSON.stringify(logEntry, void 0, JSON_SPACING));
|
|
3194
|
-
}
|
|
3195
|
-
shouldLog(level) {
|
|
3196
|
-
return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
|
|
3197
|
-
}
|
|
3198
|
-
static getLogger(verbose) {
|
|
3199
|
-
if (typeof verbose === "object") {
|
|
3200
|
-
return verbose;
|
|
3201
|
-
} else {
|
|
3202
|
-
return verbose ? new _WorkflowLogger({
|
|
3203
|
-
logLevel: "INFO",
|
|
3204
|
-
logOutput: "console"
|
|
3205
|
-
}) : void 0;
|
|
3206
|
-
}
|
|
3207
|
-
}
|
|
3208
2850
|
};
|
|
3209
2851
|
|
|
3210
2852
|
// src/serve/authorization.ts
|
|
@@ -3213,19 +2855,19 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3213
2855
|
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
3214
2856
|
disabled = true;
|
|
3215
2857
|
/**
|
|
3216
|
-
* overwrite the WorkflowContext.addStep method to always raise
|
|
2858
|
+
* overwrite the WorkflowContext.addStep method to always raise WorkflowAuthError
|
|
3217
2859
|
* error in order to stop the execution whenever we encounter a step.
|
|
3218
2860
|
*
|
|
3219
2861
|
* @param _step
|
|
3220
2862
|
*/
|
|
3221
2863
|
async addStep(_step) {
|
|
3222
|
-
throw new
|
|
2864
|
+
throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
|
|
3223
2865
|
}
|
|
3224
2866
|
/**
|
|
3225
|
-
* overwrite cancel method to throw
|
|
2867
|
+
* overwrite cancel method to throw WorkflowAuthError with the disabledMessage
|
|
3226
2868
|
*/
|
|
3227
2869
|
async cancel() {
|
|
3228
|
-
throw new
|
|
2870
|
+
throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
|
|
3229
2871
|
}
|
|
3230
2872
|
/**
|
|
3231
2873
|
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
@@ -3248,18 +2890,14 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3248
2890
|
headers: context.headers,
|
|
3249
2891
|
steps: [],
|
|
3250
2892
|
url: context.url,
|
|
3251
|
-
failureUrl: context.failureUrl,
|
|
3252
2893
|
initialPayload: context.requestPayload,
|
|
3253
2894
|
env: context.env,
|
|
3254
|
-
retries: context.retries,
|
|
3255
|
-
retryDelay: context.retryDelay,
|
|
3256
|
-
flowControl: context.flowControl,
|
|
3257
2895
|
label: context.label
|
|
3258
2896
|
});
|
|
3259
2897
|
try {
|
|
3260
2898
|
await routeFunction(disabledContext);
|
|
3261
2899
|
} catch (error) {
|
|
3262
|
-
if (isInstanceOf(error,
|
|
2900
|
+
if (isInstanceOf(error, WorkflowAuthError) && error.stepName === this.disabledMessage || isInstanceOf(error, WorkflowNonRetryableError) || isInstanceOf(error, WorkflowRetryAfterError)) {
|
|
3263
2901
|
return ok("step-found");
|
|
3264
2902
|
}
|
|
3265
2903
|
console.warn(
|
|
@@ -3319,7 +2957,7 @@ var deduplicateSteps = (steps) => {
|
|
|
3319
2957
|
}
|
|
3320
2958
|
return deduplicatedSteps;
|
|
3321
2959
|
};
|
|
3322
|
-
var checkIfLastOneIsDuplicate = async (steps,
|
|
2960
|
+
var checkIfLastOneIsDuplicate = async (steps, dispatchDebug) => {
|
|
3323
2961
|
if (steps.length < 2) {
|
|
3324
2962
|
return false;
|
|
3325
2963
|
}
|
|
@@ -3330,14 +2968,41 @@ var checkIfLastOneIsDuplicate = async (steps, debug) => {
|
|
|
3330
2968
|
const step = steps[index];
|
|
3331
2969
|
if (step.stepId === lastStepId && step.targetStep === lastTargetStepId) {
|
|
3332
2970
|
const message = `Upstash Workflow: The step '${step.stepName}' with id '${step.stepId}' has run twice during workflow execution. Rest of the workflow will continue running as usual.`;
|
|
3333
|
-
await
|
|
3334
|
-
|
|
2971
|
+
await dispatchDebug?.("onWarning", {
|
|
2972
|
+
warning: message
|
|
2973
|
+
});
|
|
3335
2974
|
return true;
|
|
3336
2975
|
}
|
|
3337
2976
|
}
|
|
3338
2977
|
return false;
|
|
3339
2978
|
};
|
|
3340
2979
|
var validateRequest = (request) => {
|
|
2980
|
+
if (request.headers.get(WORKFLOW_UNKOWN_SDK_VERSION_HEADER)) {
|
|
2981
|
+
const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
|
|
2982
|
+
if (!workflowRunId2) {
|
|
2983
|
+
throw new WorkflowError(
|
|
2984
|
+
"Couldn't get workflow id from header when handling unknown sdk request"
|
|
2985
|
+
);
|
|
2986
|
+
}
|
|
2987
|
+
return {
|
|
2988
|
+
unknownSdk: true,
|
|
2989
|
+
isFirstInvocation: true,
|
|
2990
|
+
workflowRunId: workflowRunId2
|
|
2991
|
+
};
|
|
2992
|
+
}
|
|
2993
|
+
if (request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
|
|
2994
|
+
const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
|
|
2995
|
+
if (!workflowRunId2) {
|
|
2996
|
+
throw new WorkflowError(
|
|
2997
|
+
"Couldn't get workflow id from header when handling failure callback request"
|
|
2998
|
+
);
|
|
2999
|
+
}
|
|
3000
|
+
return {
|
|
3001
|
+
unknownSdk: false,
|
|
3002
|
+
isFirstInvocation: true,
|
|
3003
|
+
workflowRunId: workflowRunId2
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3341
3006
|
const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
|
|
3342
3007
|
const isFirstInvocation = !versionHeader;
|
|
3343
3008
|
if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
|
|
@@ -3351,11 +3016,20 @@ var validateRequest = (request) => {
|
|
|
3351
3016
|
}
|
|
3352
3017
|
return {
|
|
3353
3018
|
isFirstInvocation,
|
|
3354
|
-
workflowRunId
|
|
3019
|
+
workflowRunId,
|
|
3020
|
+
unknownSdk: false
|
|
3355
3021
|
};
|
|
3356
3022
|
};
|
|
3357
|
-
var parseRequest = async (
|
|
3358
|
-
|
|
3023
|
+
var parseRequest = async ({
|
|
3024
|
+
requestPayload,
|
|
3025
|
+
isFirstInvocation,
|
|
3026
|
+
unknownSdk,
|
|
3027
|
+
workflowRunId,
|
|
3028
|
+
requester,
|
|
3029
|
+
messageId,
|
|
3030
|
+
dispatchDebug
|
|
3031
|
+
}) => {
|
|
3032
|
+
if (isFirstInvocation && !unknownSdk) {
|
|
3359
3033
|
return {
|
|
3360
3034
|
rawInitialPayload: requestPayload ?? "",
|
|
3361
3035
|
steps: [],
|
|
@@ -3365,16 +3039,14 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
3365
3039
|
} else {
|
|
3366
3040
|
let rawSteps;
|
|
3367
3041
|
if (!requestPayload) {
|
|
3368
|
-
await
|
|
3369
|
-
"
|
|
3370
|
-
|
|
3371
|
-
"request payload is empty, steps will be fetched from QStash."
|
|
3372
|
-
);
|
|
3042
|
+
await dispatchDebug?.("onInfo", {
|
|
3043
|
+
info: "request payload is empty, steps will be fetched from QStash."
|
|
3044
|
+
});
|
|
3373
3045
|
const { steps: fetchedSteps, workflowRunEnded } = await getSteps(
|
|
3374
3046
|
requester,
|
|
3375
3047
|
workflowRunId,
|
|
3376
3048
|
messageId,
|
|
3377
|
-
|
|
3049
|
+
dispatchDebug
|
|
3378
3050
|
);
|
|
3379
3051
|
if (workflowRunEnded) {
|
|
3380
3052
|
return {
|
|
@@ -3389,7 +3061,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
3389
3061
|
rawSteps = JSON.parse(requestPayload);
|
|
3390
3062
|
}
|
|
3391
3063
|
const { rawInitialPayload, steps } = processRawSteps(rawSteps);
|
|
3392
|
-
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps,
|
|
3064
|
+
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, dispatchDebug);
|
|
3393
3065
|
const deduplicatedSteps = deduplicateSteps(steps);
|
|
3394
3066
|
return {
|
|
3395
3067
|
rawInitialPayload,
|
|
@@ -3399,16 +3071,21 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
3399
3071
|
};
|
|
3400
3072
|
}
|
|
3401
3073
|
};
|
|
3402
|
-
var handleFailure = async (
|
|
3403
|
-
|
|
3074
|
+
var handleFailure = async ({
|
|
3075
|
+
request,
|
|
3076
|
+
requestPayload,
|
|
3077
|
+
qstashClient,
|
|
3078
|
+
initialPayloadParser,
|
|
3079
|
+
routeFunction,
|
|
3080
|
+
failureFunction,
|
|
3081
|
+
env,
|
|
3082
|
+
dispatchDebug
|
|
3083
|
+
}) => {
|
|
3084
|
+
if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true" && !request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
|
|
3404
3085
|
return ok({ result: "not-failure-callback" });
|
|
3405
3086
|
}
|
|
3406
3087
|
if (!failureFunction) {
|
|
3407
|
-
return
|
|
3408
|
-
new WorkflowError(
|
|
3409
|
-
"Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."
|
|
3410
|
-
)
|
|
3411
|
-
);
|
|
3088
|
+
return ok({ result: "failure-function-undefined" });
|
|
3412
3089
|
}
|
|
3413
3090
|
try {
|
|
3414
3091
|
const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
|
|
@@ -3436,23 +3113,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
3436
3113
|
headers: userHeaders,
|
|
3437
3114
|
steps: [],
|
|
3438
3115
|
url,
|
|
3439
|
-
failureUrl: url,
|
|
3440
|
-
debug,
|
|
3441
3116
|
env,
|
|
3442
|
-
retries,
|
|
3443
|
-
retryDelay,
|
|
3444
|
-
flowControl,
|
|
3445
3117
|
telemetry: void 0,
|
|
3446
3118
|
// not going to make requests in authentication check
|
|
3447
|
-
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0
|
|
3119
|
+
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3120
|
+
middlewareManager: void 0
|
|
3448
3121
|
});
|
|
3449
3122
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3450
3123
|
routeFunction,
|
|
3451
3124
|
workflowContext
|
|
3452
3125
|
);
|
|
3453
3126
|
if (authCheck.isErr()) {
|
|
3454
|
-
await
|
|
3455
|
-
|
|
3127
|
+
await dispatchDebug?.("onError", {
|
|
3128
|
+
error: authCheck.error
|
|
3129
|
+
});
|
|
3130
|
+
return err(authCheck.error);
|
|
3456
3131
|
} else if (authCheck.value === "run-ended") {
|
|
3457
3132
|
return err(new WorkflowError("Not authorized to run the failure function."));
|
|
3458
3133
|
}
|
|
@@ -3463,74 +3138,309 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
3463
3138
|
failHeaders: header,
|
|
3464
3139
|
failStack
|
|
3465
3140
|
});
|
|
3466
|
-
return ok({ result: "
|
|
3141
|
+
return ok({ result: "failure-function-executed", response: failureResponse });
|
|
3467
3142
|
} catch (error) {
|
|
3468
3143
|
return err(error);
|
|
3469
3144
|
}
|
|
3470
3145
|
};
|
|
3471
3146
|
|
|
3472
|
-
// src/serve/
|
|
3147
|
+
// src/serve/multi-region/handlers.ts
|
|
3473
3148
|
var import_qstash10 = require("@upstash/qstash");
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3149
|
+
|
|
3150
|
+
// src/serve/multi-region/utils.ts
|
|
3151
|
+
var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
|
|
3152
|
+
var getRegionFromEnvironment = (environment) => {
|
|
3153
|
+
const region = environment.QSTASH_REGION;
|
|
3154
|
+
return normalizeRegionHeader(region);
|
|
3155
|
+
};
|
|
3156
|
+
function readEnvironmentVariables(environmentVariables, environment, region) {
|
|
3157
|
+
const result = {};
|
|
3158
|
+
for (const variable of environmentVariables) {
|
|
3159
|
+
const key = region ? `${region}_${variable}` : variable;
|
|
3160
|
+
result[variable] = environment[key];
|
|
3161
|
+
}
|
|
3162
|
+
return result;
|
|
3163
|
+
}
|
|
3164
|
+
function readClientEnvironmentVariables(environment, region) {
|
|
3165
|
+
return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
|
|
3166
|
+
}
|
|
3167
|
+
function readReceiverEnvironmentVariables(environment, region) {
|
|
3168
|
+
return readEnvironmentVariables(
|
|
3169
|
+
["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
|
|
3170
|
+
environment,
|
|
3171
|
+
region
|
|
3479
3172
|
);
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3173
|
+
}
|
|
3174
|
+
function normalizeRegionHeader(region) {
|
|
3175
|
+
if (!region) {
|
|
3176
|
+
return void 0;
|
|
3177
|
+
}
|
|
3178
|
+
region = region.replaceAll("-", "_").toUpperCase();
|
|
3179
|
+
if (VALID_REGIONS.includes(region)) {
|
|
3180
|
+
return region;
|
|
3181
|
+
}
|
|
3182
|
+
console.warn(
|
|
3183
|
+
`[Upstash Workflow] Invalid UPSTASH-REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
|
|
3184
|
+
", "
|
|
3185
|
+
)}.`
|
|
3186
|
+
);
|
|
3187
|
+
return void 0;
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
// src/serve/multi-region/handlers.ts
|
|
3191
|
+
var getHandlersForRequest = (qstashHandlers, regionHeader, isFirstInvocation) => {
|
|
3192
|
+
if (qstashHandlers.mode === "single-region") {
|
|
3193
|
+
return qstashHandlers.handlers;
|
|
3194
|
+
}
|
|
3195
|
+
let targetRegion;
|
|
3196
|
+
if (isFirstInvocation) {
|
|
3197
|
+
targetRegion = qstashHandlers.defaultRegion;
|
|
3198
|
+
} else {
|
|
3199
|
+
const normalizedRegion = regionHeader ? normalizeRegionHeader(regionHeader) : void 0;
|
|
3200
|
+
targetRegion = normalizedRegion ?? qstashHandlers.defaultRegion;
|
|
3201
|
+
}
|
|
3202
|
+
const handler = qstashHandlers.handlers[targetRegion];
|
|
3203
|
+
if (!handler) {
|
|
3204
|
+
console.warn(
|
|
3205
|
+
`[Upstash Workflow] No handler found for region "${targetRegion}". Falling back to default region.`
|
|
3206
|
+
);
|
|
3207
|
+
return qstashHandlers.handlers[qstashHandlers.defaultRegion];
|
|
3208
|
+
}
|
|
3209
|
+
return handler;
|
|
3210
|
+
};
|
|
3211
|
+
var createRegionalHandler = (environment, receiverConfig, region, clientOptions) => {
|
|
3212
|
+
const clientEnv = readClientEnvironmentVariables(environment, region);
|
|
3213
|
+
const client = new import_qstash10.Client({
|
|
3214
|
+
...clientOptions,
|
|
3215
|
+
baseUrl: clientEnv.QSTASH_URL,
|
|
3216
|
+
token: clientEnv.QSTASH_TOKEN
|
|
3217
|
+
});
|
|
3218
|
+
const receiver = getReceiver(environment, receiverConfig, region);
|
|
3219
|
+
return { client, receiver };
|
|
3220
|
+
};
|
|
3221
|
+
var shouldUseMultiRegionMode = (environment, qstashClientOption) => {
|
|
3222
|
+
const hasRegionEnv = Boolean(getRegionFromEnvironment(environment));
|
|
3223
|
+
if (hasRegionEnv && (!qstashClientOption || !("http" in qstashClientOption))) {
|
|
3224
|
+
return {
|
|
3225
|
+
isMultiRegion: true,
|
|
3226
|
+
defaultRegion: getRegionFromEnvironment(environment),
|
|
3227
|
+
clientOptions: qstashClientOption
|
|
3228
|
+
};
|
|
3229
|
+
} else {
|
|
3230
|
+
return { isMultiRegion: false };
|
|
3231
|
+
}
|
|
3232
|
+
};
|
|
3233
|
+
var getQStashHandlers = ({
|
|
3234
|
+
environment,
|
|
3235
|
+
qstashClientOption,
|
|
3236
|
+
receiverConfig
|
|
3237
|
+
}) => {
|
|
3238
|
+
const multiRegion = shouldUseMultiRegionMode(environment, qstashClientOption);
|
|
3239
|
+
if (multiRegion.isMultiRegion) {
|
|
3240
|
+
const regions = ["US_EAST_1", "EU_CENTRAL_1"];
|
|
3241
|
+
const handlers = {};
|
|
3242
|
+
for (const region of regions) {
|
|
3243
|
+
try {
|
|
3244
|
+
handlers[region] = createRegionalHandler(
|
|
3245
|
+
environment,
|
|
3246
|
+
receiverConfig,
|
|
3247
|
+
region,
|
|
3248
|
+
multiRegion.clientOptions
|
|
3525
3249
|
);
|
|
3250
|
+
} catch (error) {
|
|
3251
|
+
console.warn(`[Upstash Workflow] Failed to create handler for region ${region}:`, error);
|
|
3526
3252
|
}
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3253
|
+
}
|
|
3254
|
+
return {
|
|
3255
|
+
mode: "multi-region",
|
|
3256
|
+
handlers,
|
|
3257
|
+
defaultRegion: multiRegion.defaultRegion
|
|
3258
|
+
};
|
|
3259
|
+
} else {
|
|
3260
|
+
return {
|
|
3261
|
+
mode: "single-region",
|
|
3262
|
+
handlers: {
|
|
3263
|
+
client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash10.Client({
|
|
3264
|
+
...qstashClientOption,
|
|
3265
|
+
baseUrl: environment.QSTASH_URL,
|
|
3266
|
+
token: environment.QSTASH_TOKEN
|
|
3267
|
+
}),
|
|
3268
|
+
receiver: getReceiver(environment, receiverConfig)
|
|
3269
|
+
}
|
|
3270
|
+
};
|
|
3271
|
+
}
|
|
3272
|
+
};
|
|
3273
|
+
var getReceiver = (environment, receiverConfig, region) => {
|
|
3274
|
+
if (typeof receiverConfig === "string") {
|
|
3275
|
+
if (receiverConfig === "set-to-undefined") {
|
|
3276
|
+
return void 0;
|
|
3277
|
+
}
|
|
3278
|
+
const receiverEnv = readReceiverEnvironmentVariables(environment, region);
|
|
3279
|
+
return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash10.Receiver({
|
|
3280
|
+
currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
|
|
3281
|
+
nextSigningKey: receiverEnv.QSTASH_NEXT_SIGNING_KEY
|
|
3282
|
+
}) : void 0;
|
|
3283
|
+
} else {
|
|
3284
|
+
return receiverConfig;
|
|
3285
|
+
}
|
|
3286
|
+
};
|
|
3287
|
+
var getQStashHandlerOptions = (...params) => {
|
|
3288
|
+
const handlers = getQStashHandlers(...params);
|
|
3289
|
+
return {
|
|
3290
|
+
qstashHandlers: handlers,
|
|
3291
|
+
defaultReceiver: handlers.mode === "single-region" ? handlers.handlers.receiver : handlers.handlers[handlers.defaultRegion].receiver,
|
|
3292
|
+
defaultClient: handlers.mode === "single-region" ? handlers.handlers.client : handlers.handlers[handlers.defaultRegion].client
|
|
3293
|
+
};
|
|
3294
|
+
};
|
|
3295
|
+
|
|
3296
|
+
// src/middleware/middleware.ts
|
|
3297
|
+
var WorkflowMiddleware = class {
|
|
3298
|
+
name;
|
|
3299
|
+
initCallbacks;
|
|
3300
|
+
/**
|
|
3301
|
+
* Callback functions
|
|
3302
|
+
*
|
|
3303
|
+
* Initially set to undefined, will be populated after init is called
|
|
3304
|
+
*/
|
|
3305
|
+
middlewareCallbacks = void 0;
|
|
3306
|
+
constructor(parameters) {
|
|
3307
|
+
this.name = parameters.name;
|
|
3308
|
+
if ("init" in parameters) {
|
|
3309
|
+
this.initCallbacks = parameters.init;
|
|
3310
|
+
} else {
|
|
3311
|
+
this.middlewareCallbacks = parameters.callbacks;
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
async ensureInit() {
|
|
3315
|
+
if (!this.middlewareCallbacks) {
|
|
3316
|
+
if (!this.initCallbacks) {
|
|
3317
|
+
throw new WorkflowError(`Middleware "${this.name}" has no callbacks or init defined.`);
|
|
3318
|
+
}
|
|
3319
|
+
this.middlewareCallbacks = await this.initCallbacks();
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
/**
|
|
3323
|
+
* Gets a callback function by name.
|
|
3324
|
+
*
|
|
3325
|
+
* @param callback name of the callback to retrieve
|
|
3326
|
+
*/
|
|
3327
|
+
getCallback(callback) {
|
|
3328
|
+
return this.middlewareCallbacks?.[callback];
|
|
3329
|
+
}
|
|
3330
|
+
};
|
|
3331
|
+
|
|
3332
|
+
// src/middleware/logging.ts
|
|
3333
|
+
var loggingMiddleware = new WorkflowMiddleware({
|
|
3334
|
+
name: "logging",
|
|
3335
|
+
callbacks: {
|
|
3336
|
+
afterExecution(params) {
|
|
3337
|
+
const { context, ...rest } = params;
|
|
3338
|
+
console.log(" [Upstash Workflow]: Step executed:", {
|
|
3339
|
+
workflowRunId: context.workflowRunId,
|
|
3340
|
+
...rest
|
|
3341
|
+
});
|
|
3342
|
+
},
|
|
3343
|
+
beforeExecution(params) {
|
|
3344
|
+
const { context, ...rest } = params;
|
|
3345
|
+
console.log(" [Upstash Workflow]: Step execution started:", {
|
|
3346
|
+
workflowRunId: context.workflowRunId,
|
|
3347
|
+
...rest
|
|
3348
|
+
});
|
|
3349
|
+
},
|
|
3350
|
+
runStarted(params) {
|
|
3351
|
+
const { context, ...rest } = params;
|
|
3352
|
+
console.log(" [Upstash Workflow]: Workflow run started:", {
|
|
3353
|
+
workflowRunId: context.workflowRunId,
|
|
3354
|
+
...rest
|
|
3355
|
+
});
|
|
3356
|
+
},
|
|
3357
|
+
runCompleted(params) {
|
|
3358
|
+
const { context, ...rest } = params;
|
|
3359
|
+
console.log(" [Upstash Workflow]: Workflow run completed:", {
|
|
3360
|
+
workflowRunId: context.workflowRunId,
|
|
3361
|
+
...rest
|
|
3532
3362
|
});
|
|
3533
3363
|
},
|
|
3364
|
+
onError: onErrorWithConsole,
|
|
3365
|
+
onWarning: onWarningWithConsole,
|
|
3366
|
+
onInfo: onInfoWithConsole
|
|
3367
|
+
}
|
|
3368
|
+
});
|
|
3369
|
+
|
|
3370
|
+
// src/serve/options.ts
|
|
3371
|
+
var createResponseData = (workflowRunId, detailedFinishCondition) => {
|
|
3372
|
+
const baseHeaders = {
|
|
3373
|
+
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
3374
|
+
"Upstash-workflow-sdk": VERSION
|
|
3375
|
+
};
|
|
3376
|
+
if (detailedFinishCondition?.condition === "auth-fail") {
|
|
3377
|
+
return {
|
|
3378
|
+
text: JSON.stringify({
|
|
3379
|
+
message: AUTH_FAIL_MESSAGE,
|
|
3380
|
+
workflowRunId
|
|
3381
|
+
}),
|
|
3382
|
+
status: 400,
|
|
3383
|
+
headers: baseHeaders
|
|
3384
|
+
};
|
|
3385
|
+
} else if (detailedFinishCondition?.condition === "non-retryable-error") {
|
|
3386
|
+
return {
|
|
3387
|
+
text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
|
|
3388
|
+
status: 489,
|
|
3389
|
+
headers: {
|
|
3390
|
+
...baseHeaders,
|
|
3391
|
+
"Upstash-NonRetryable-Error": "true"
|
|
3392
|
+
}
|
|
3393
|
+
};
|
|
3394
|
+
} else if (detailedFinishCondition?.condition === "retry-after-error") {
|
|
3395
|
+
return {
|
|
3396
|
+
text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
|
|
3397
|
+
status: 429,
|
|
3398
|
+
headers: {
|
|
3399
|
+
...baseHeaders,
|
|
3400
|
+
"Retry-After": detailedFinishCondition.result.retryAfter.toString()
|
|
3401
|
+
}
|
|
3402
|
+
};
|
|
3403
|
+
} else if (detailedFinishCondition?.condition === "failure-callback-executed") {
|
|
3404
|
+
return {
|
|
3405
|
+
text: JSON.stringify({ result: detailedFinishCondition.result ?? void 0 }),
|
|
3406
|
+
status: 200,
|
|
3407
|
+
headers: baseHeaders
|
|
3408
|
+
};
|
|
3409
|
+
} else if (detailedFinishCondition?.condition === "failure-callback-undefined") {
|
|
3410
|
+
return {
|
|
3411
|
+
text: JSON.stringify({
|
|
3412
|
+
workflowRunId,
|
|
3413
|
+
finishCondition: detailedFinishCondition.condition
|
|
3414
|
+
}),
|
|
3415
|
+
status: 200,
|
|
3416
|
+
headers: {
|
|
3417
|
+
...baseHeaders,
|
|
3418
|
+
"Upstash-Workflow-Failure-Callback-Notfound": "true"
|
|
3419
|
+
}
|
|
3420
|
+
};
|
|
3421
|
+
}
|
|
3422
|
+
return {
|
|
3423
|
+
text: JSON.stringify({
|
|
3424
|
+
workflowRunId,
|
|
3425
|
+
finishCondition: detailedFinishCondition.condition
|
|
3426
|
+
}),
|
|
3427
|
+
status: 200,
|
|
3428
|
+
headers: baseHeaders
|
|
3429
|
+
};
|
|
3430
|
+
};
|
|
3431
|
+
var processOptions = (options, internalOptions) => {
|
|
3432
|
+
const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
|
|
3433
|
+
const {
|
|
3434
|
+
qstashHandlers,
|
|
3435
|
+
defaultClient: qstashClient,
|
|
3436
|
+
defaultReceiver: receiver
|
|
3437
|
+
} = getQStashHandlerOptions({
|
|
3438
|
+
environment,
|
|
3439
|
+
qstashClientOption: options?.qstashClient,
|
|
3440
|
+
receiverConfig: options && "receiver" in options ? options.receiver ? options.receiver : "set-to-undefined" : "not-set"
|
|
3441
|
+
});
|
|
3442
|
+
return {
|
|
3443
|
+
qstashClient,
|
|
3534
3444
|
initialPayloadParser: (initialRequest) => {
|
|
3535
3445
|
if (!initialRequest) {
|
|
3536
3446
|
return void 0;
|
|
@@ -3545,35 +3455,38 @@ var processOptions = (options) => {
|
|
|
3545
3455
|
throw error;
|
|
3546
3456
|
}
|
|
3547
3457
|
},
|
|
3548
|
-
receiver
|
|
3549
|
-
currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
|
|
3550
|
-
nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
|
|
3551
|
-
}) : void 0,
|
|
3458
|
+
receiver,
|
|
3552
3459
|
baseUrl: environment.UPSTASH_WORKFLOW_URL,
|
|
3553
3460
|
env: environment,
|
|
3554
|
-
retries: DEFAULT_RETRIES,
|
|
3555
|
-
useJSONContent: false,
|
|
3556
3461
|
disableTelemetry: false,
|
|
3557
|
-
|
|
3558
|
-
|
|
3462
|
+
...options,
|
|
3463
|
+
// merge middlewares
|
|
3464
|
+
middlewares: [options?.middlewares ?? [], options?.verbose ? [loggingMiddleware] : []].flat(),
|
|
3465
|
+
internal: {
|
|
3466
|
+
generateResponse: internalOptions?.generateResponse ?? ((responseData) => {
|
|
3467
|
+
return new Response(responseData.text, {
|
|
3468
|
+
status: responseData.status,
|
|
3469
|
+
headers: responseData.headers
|
|
3470
|
+
});
|
|
3471
|
+
}),
|
|
3472
|
+
useJSONContent: internalOptions?.useJSONContent ?? false,
|
|
3473
|
+
qstashHandlers
|
|
3474
|
+
}
|
|
3559
3475
|
};
|
|
3560
3476
|
};
|
|
3561
|
-
var determineUrls = async (request, url, baseUrl,
|
|
3477
|
+
var determineUrls = async (request, url, baseUrl, dispatchDebug) => {
|
|
3562
3478
|
const initialWorkflowUrl = url ?? request.url;
|
|
3563
3479
|
const workflowUrl = baseUrl ? initialWorkflowUrl.replace(/^(https?:\/\/[^/]+)(\/.*)?$/, (_, matchedBaseUrl, path) => {
|
|
3564
3480
|
return baseUrl + (path || "");
|
|
3565
3481
|
}) : initialWorkflowUrl;
|
|
3566
3482
|
if (workflowUrl !== initialWorkflowUrl) {
|
|
3567
|
-
await
|
|
3568
|
-
|
|
3569
|
-
originalURL: initialWorkflowUrl,
|
|
3570
|
-
updatedURL: workflowUrl
|
|
3483
|
+
await dispatchDebug("onInfo", {
|
|
3484
|
+
info: `The workflow URL's base URL has been replaced with the provided baseUrl. Original URL: ${initialWorkflowUrl}, New URL: ${workflowUrl}`
|
|
3571
3485
|
});
|
|
3572
3486
|
}
|
|
3573
|
-
const workflowFailureUrl = failureFunction ? workflowUrl : failureUrl;
|
|
3574
3487
|
if (workflowUrl.includes("localhost")) {
|
|
3575
|
-
await
|
|
3576
|
-
|
|
3488
|
+
await dispatchDebug("onInfo", {
|
|
3489
|
+
info: `Workflow URL contains localhost. This can happen in local development, but shouldn't happen in production unless you have a route which contains localhost. Received: ${workflowUrl}`
|
|
3577
3490
|
});
|
|
3578
3491
|
}
|
|
3579
3492
|
if (!(workflowUrl.startsWith("http://") || workflowUrl.startsWith("https://"))) {
|
|
@@ -3582,205 +3495,224 @@ var determineUrls = async (request, url, baseUrl, failureFunction, failureUrl, d
|
|
|
3582
3495
|
);
|
|
3583
3496
|
}
|
|
3584
3497
|
return {
|
|
3585
|
-
workflowUrl
|
|
3586
|
-
workflowFailureUrl
|
|
3498
|
+
workflowUrl
|
|
3587
3499
|
};
|
|
3588
3500
|
};
|
|
3589
3501
|
var AUTH_FAIL_MESSAGE = `Failed to authenticate Workflow request. If this is unexpected, see the caveat https://upstash.com/docs/workflow/basics/caveats#avoid-non-deterministic-code-outside-context-run`;
|
|
3590
3502
|
|
|
3591
3503
|
// src/serve/index.ts
|
|
3592
|
-
var serveBase = (routeFunction, telemetry, options) => {
|
|
3504
|
+
var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
3593
3505
|
const {
|
|
3594
|
-
qstashClient,
|
|
3595
|
-
onStepFinish,
|
|
3596
3506
|
initialPayloadParser,
|
|
3597
3507
|
url,
|
|
3598
|
-
verbose,
|
|
3599
|
-
receiver,
|
|
3600
|
-
failureUrl,
|
|
3601
3508
|
failureFunction,
|
|
3602
3509
|
baseUrl,
|
|
3603
3510
|
env,
|
|
3604
|
-
retries,
|
|
3605
|
-
retryDelay,
|
|
3606
|
-
useJSONContent,
|
|
3607
3511
|
disableTelemetry,
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
} = processOptions(options);
|
|
3512
|
+
middlewares,
|
|
3513
|
+
internal
|
|
3514
|
+
} = processOptions(options, internalOptions);
|
|
3611
3515
|
telemetry = disableTelemetry ? void 0 : telemetry;
|
|
3612
|
-
const
|
|
3613
|
-
const handler = async (request) => {
|
|
3614
|
-
await
|
|
3615
|
-
|
|
3516
|
+
const { generateResponse: responseGenerator, useJSONContent } = internal;
|
|
3517
|
+
const handler = async (request, middlewareManager) => {
|
|
3518
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3519
|
+
info: `Received request for workflow execution.`
|
|
3520
|
+
});
|
|
3521
|
+
const { workflowUrl } = await determineUrls(
|
|
3616
3522
|
request,
|
|
3617
3523
|
url,
|
|
3618
3524
|
baseUrl,
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3525
|
+
middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3526
|
+
);
|
|
3527
|
+
const { isFirstInvocation, workflowRunId, unknownSdk } = validateRequest(request);
|
|
3528
|
+
const regionHeader = request.headers.get("upstash-region");
|
|
3529
|
+
const { client: regionalClient, receiver: regionalReceiver } = getHandlersForRequest(
|
|
3530
|
+
internal.qstashHandlers,
|
|
3531
|
+
regionHeader,
|
|
3532
|
+
isFirstInvocation
|
|
3622
3533
|
);
|
|
3623
3534
|
const requestPayload = await getPayload(request) ?? "";
|
|
3624
|
-
await verifyRequest(requestPayload, request.headers.get("upstash-signature"),
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3535
|
+
await verifyRequest(requestPayload, request.headers.get("upstash-signature"), regionalReceiver);
|
|
3536
|
+
middlewareManager.assignWorkflowRunId(workflowRunId);
|
|
3537
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3538
|
+
info: `Run id identified. isFirstInvocation: ${isFirstInvocation}, unknownSdk: ${unknownSdk}`
|
|
3539
|
+
});
|
|
3540
|
+
const { rawInitialPayload, steps, isLastDuplicate, workflowRunEnded } = await parseRequest({
|
|
3628
3541
|
requestPayload,
|
|
3629
3542
|
isFirstInvocation,
|
|
3543
|
+
unknownSdk,
|
|
3630
3544
|
workflowRunId,
|
|
3631
|
-
|
|
3632
|
-
request.headers.get("upstash-message-id"),
|
|
3633
|
-
|
|
3634
|
-
);
|
|
3545
|
+
requester: regionalClient.http,
|
|
3546
|
+
messageId: request.headers.get("upstash-message-id"),
|
|
3547
|
+
dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3548
|
+
});
|
|
3635
3549
|
if (workflowRunEnded) {
|
|
3636
|
-
return
|
|
3637
|
-
|
|
3638
|
-
|
|
3550
|
+
return responseGenerator(
|
|
3551
|
+
createResponseData(workflowRunId, {
|
|
3552
|
+
condition: "workflow-already-ended"
|
|
3553
|
+
})
|
|
3554
|
+
);
|
|
3639
3555
|
}
|
|
3640
3556
|
if (isLastDuplicate) {
|
|
3641
|
-
return
|
|
3642
|
-
|
|
3643
|
-
|
|
3557
|
+
return responseGenerator(
|
|
3558
|
+
createResponseData(workflowRunId, {
|
|
3559
|
+
condition: "duplicate-step"
|
|
3560
|
+
})
|
|
3561
|
+
);
|
|
3644
3562
|
}
|
|
3645
|
-
const failureCheck = await handleFailure(
|
|
3563
|
+
const failureCheck = await handleFailure({
|
|
3646
3564
|
request,
|
|
3647
3565
|
requestPayload,
|
|
3648
|
-
qstashClient,
|
|
3566
|
+
qstashClient: regionalClient,
|
|
3649
3567
|
initialPayloadParser,
|
|
3650
3568
|
routeFunction,
|
|
3651
3569
|
failureFunction,
|
|
3652
3570
|
env,
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
flowControl,
|
|
3656
|
-
debug
|
|
3657
|
-
);
|
|
3571
|
+
dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3572
|
+
});
|
|
3658
3573
|
if (failureCheck.isErr()) {
|
|
3659
3574
|
throw failureCheck.error;
|
|
3660
|
-
} else if (failureCheck.value.result === "
|
|
3661
|
-
await
|
|
3662
|
-
|
|
3663
|
-
condition: "failure-callback",
|
|
3664
|
-
result: failureCheck.value.response
|
|
3575
|
+
} else if (failureCheck.value.result === "failure-function-executed") {
|
|
3576
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3577
|
+
info: `Handled failure callback.`
|
|
3665
3578
|
});
|
|
3579
|
+
return responseGenerator(
|
|
3580
|
+
createResponseData(workflowRunId, {
|
|
3581
|
+
condition: "failure-callback-executed",
|
|
3582
|
+
result: failureCheck.value.response
|
|
3583
|
+
})
|
|
3584
|
+
);
|
|
3585
|
+
} else if (failureCheck.value.result === "failure-function-undefined") {
|
|
3586
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3587
|
+
info: `Failure callback invoked but no failure function defined.`
|
|
3588
|
+
});
|
|
3589
|
+
return responseGenerator(
|
|
3590
|
+
createResponseData(workflowRunId, {
|
|
3591
|
+
condition: "failure-callback-undefined"
|
|
3592
|
+
})
|
|
3593
|
+
);
|
|
3666
3594
|
}
|
|
3667
3595
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
3668
3596
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
3669
3597
|
const workflowContext = new WorkflowContext({
|
|
3670
|
-
qstashClient,
|
|
3598
|
+
qstashClient: regionalClient,
|
|
3671
3599
|
workflowRunId,
|
|
3672
3600
|
initialPayload: initialPayloadParser(rawInitialPayload),
|
|
3673
3601
|
headers: recreateUserHeaders(request.headers),
|
|
3674
3602
|
steps,
|
|
3675
3603
|
url: workflowUrl,
|
|
3676
|
-
failureUrl: workflowFailureUrl,
|
|
3677
|
-
debug,
|
|
3678
3604
|
env,
|
|
3679
|
-
retries,
|
|
3680
|
-
retryDelay,
|
|
3681
3605
|
telemetry,
|
|
3682
3606
|
invokeCount,
|
|
3683
|
-
|
|
3684
|
-
|
|
3607
|
+
label,
|
|
3608
|
+
middlewareManager
|
|
3685
3609
|
});
|
|
3686
3610
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3687
3611
|
routeFunction,
|
|
3688
3612
|
workflowContext
|
|
3689
3613
|
);
|
|
3690
3614
|
if (authCheck.isErr()) {
|
|
3691
|
-
await debug?.log("ERROR", "ERROR", { error: authCheck.error.message });
|
|
3692
3615
|
throw authCheck.error;
|
|
3693
3616
|
} else if (authCheck.value === "run-ended") {
|
|
3694
|
-
await
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3617
|
+
await middlewareManager.dispatchDebug("onError", {
|
|
3618
|
+
error: new Error(AUTH_FAIL_MESSAGE)
|
|
3619
|
+
});
|
|
3620
|
+
return responseGenerator(
|
|
3621
|
+
createResponseData(isFirstInvocation ? "no-workflow-id" : workflowContext.workflowRunId, {
|
|
3622
|
+
condition: "auth-fail"
|
|
3623
|
+
})
|
|
3699
3624
|
);
|
|
3700
3625
|
}
|
|
3701
3626
|
const callReturnCheck = await handleThirdPartyCallResult({
|
|
3702
3627
|
request,
|
|
3703
3628
|
requestPayload: rawInitialPayload,
|
|
3704
|
-
client:
|
|
3629
|
+
client: regionalClient,
|
|
3705
3630
|
workflowUrl,
|
|
3706
|
-
failureUrl: workflowFailureUrl,
|
|
3707
|
-
retries,
|
|
3708
|
-
retryDelay,
|
|
3709
|
-
flowControl,
|
|
3710
3631
|
telemetry,
|
|
3711
|
-
|
|
3632
|
+
middlewareManager
|
|
3712
3633
|
});
|
|
3713
3634
|
if (callReturnCheck.isErr()) {
|
|
3714
|
-
await debug?.log("ERROR", "SUBMIT_THIRD_PARTY_RESULT", {
|
|
3715
|
-
error: callReturnCheck.error.message
|
|
3716
|
-
});
|
|
3717
3635
|
throw callReturnCheck.error;
|
|
3718
3636
|
} else if (callReturnCheck.value === "continue-workflow") {
|
|
3719
3637
|
const result = isFirstInvocation ? await triggerFirstInvocation({
|
|
3720
3638
|
workflowContext,
|
|
3721
3639
|
useJSONContent,
|
|
3722
3640
|
telemetry,
|
|
3723
|
-
|
|
3724
|
-
|
|
3641
|
+
invokeCount,
|
|
3642
|
+
middlewareManager,
|
|
3643
|
+
unknownSdk
|
|
3725
3644
|
}) : await triggerRouteFunction({
|
|
3726
|
-
onStep: async () =>
|
|
3645
|
+
onStep: async () => {
|
|
3646
|
+
if (steps.length === 1) {
|
|
3647
|
+
await middlewareManager.dispatchLifecycle("runStarted", {});
|
|
3648
|
+
}
|
|
3649
|
+
return await routeFunction(workflowContext);
|
|
3650
|
+
},
|
|
3727
3651
|
onCleanup: async (result2) => {
|
|
3728
|
-
await
|
|
3652
|
+
await middlewareManager.dispatchLifecycle("runCompleted", {
|
|
3653
|
+
result: result2
|
|
3654
|
+
});
|
|
3655
|
+
await triggerWorkflowDelete(
|
|
3656
|
+
workflowContext,
|
|
3657
|
+
result2,
|
|
3658
|
+
false,
|
|
3659
|
+
middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3660
|
+
);
|
|
3729
3661
|
},
|
|
3730
3662
|
onCancel: async () => {
|
|
3731
3663
|
await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
|
|
3732
3664
|
},
|
|
3733
|
-
|
|
3665
|
+
middlewareManager
|
|
3734
3666
|
});
|
|
3735
3667
|
if (result.isOk() && isInstanceOf(result.value, WorkflowNonRetryableError)) {
|
|
3736
|
-
return
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3668
|
+
return responseGenerator(
|
|
3669
|
+
createResponseData(workflowRunId, {
|
|
3670
|
+
condition: "non-retryable-error",
|
|
3671
|
+
result: result.value
|
|
3672
|
+
})
|
|
3673
|
+
);
|
|
3740
3674
|
}
|
|
3741
3675
|
if (result.isOk() && isInstanceOf(result.value, WorkflowRetryAfterError)) {
|
|
3742
|
-
return
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3676
|
+
return responseGenerator(
|
|
3677
|
+
createResponseData(workflowRunId, {
|
|
3678
|
+
condition: "retry-after-error",
|
|
3679
|
+
result: result.value
|
|
3680
|
+
})
|
|
3681
|
+
);
|
|
3746
3682
|
}
|
|
3747
3683
|
if (result.isErr()) {
|
|
3748
|
-
await debug?.log("ERROR", "ERROR", { error: result.error.message });
|
|
3749
3684
|
throw result.error;
|
|
3750
3685
|
}
|
|
3751
|
-
await
|
|
3752
|
-
|
|
3753
|
-
condition: "success"
|
|
3686
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3687
|
+
info: `Workflow endpoint execution completed successfully.`
|
|
3754
3688
|
});
|
|
3689
|
+
return responseGenerator(
|
|
3690
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3691
|
+
condition: "success"
|
|
3692
|
+
})
|
|
3693
|
+
);
|
|
3755
3694
|
} else if (callReturnCheck.value === "workflow-ended") {
|
|
3756
|
-
return
|
|
3757
|
-
|
|
3758
|
-
|
|
3695
|
+
return responseGenerator(
|
|
3696
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3697
|
+
condition: "workflow-already-ended"
|
|
3698
|
+
})
|
|
3699
|
+
);
|
|
3759
3700
|
}
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3701
|
+
return responseGenerator(
|
|
3702
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3703
|
+
condition: "fromCallback"
|
|
3704
|
+
})
|
|
3705
|
+
);
|
|
3764
3706
|
};
|
|
3765
3707
|
const safeHandler = async (request) => {
|
|
3708
|
+
const middlewareManager = new MiddlewareManager(middlewares);
|
|
3766
3709
|
try {
|
|
3767
|
-
return await handler(request);
|
|
3710
|
+
return await handler(request, middlewareManager);
|
|
3768
3711
|
} catch (error) {
|
|
3769
3712
|
const formattedError = formatWorkflowError(error);
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
}
|
|
3773
|
-
const formattedOnErrorError = formatWorkflowError(onErrorError);
|
|
3774
|
-
const errorMessage = `Error while running onError callback: '${formattedOnErrorError.message}'.
|
|
3775
|
-
Original error: '${formattedError.message}'`;
|
|
3776
|
-
console.error(errorMessage);
|
|
3777
|
-
return new Response(JSON.stringify({ error: errorMessage }), {
|
|
3778
|
-
status: 500,
|
|
3779
|
-
headers: {
|
|
3780
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3781
|
-
}
|
|
3782
|
-
});
|
|
3783
|
-
}
|
|
3713
|
+
await middlewareManager.dispatchDebug("onError", {
|
|
3714
|
+
error: isInstanceOf(error, Error) ? error : new Error(formattedError.message)
|
|
3715
|
+
});
|
|
3784
3716
|
return new Response(JSON.stringify(formattedError), {
|
|
3785
3717
|
status: 500,
|
|
3786
3718
|
headers: {
|
|
@@ -3806,7 +3738,11 @@ var serve = (routeFunction, options) => {
|
|
|
3806
3738
|
status: 405
|
|
3807
3739
|
});
|
|
3808
3740
|
}
|
|
3809
|
-
const { handler: serveHandler } = serveBase(
|
|
3741
|
+
const { handler: serveHandler } = serveBase(
|
|
3742
|
+
routeFunction,
|
|
3743
|
+
telemetry,
|
|
3744
|
+
options
|
|
3745
|
+
);
|
|
3810
3746
|
return await serveHandler(event.request);
|
|
3811
3747
|
};
|
|
3812
3748
|
return { POST: handler };
|