@upstash/workflow 0.2.12 → 0.2.13

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.
@@ -42,11 +42,12 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
42
42
  var DEFAULT_CONTENT_TYPE = "application/json";
43
43
  var NO_CONCURRENCY = 1;
44
44
  var DEFAULT_RETRIES = 3;
45
- var VERSION = "v0.2.7";
45
+ var VERSION = "v0.2.13";
46
46
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
47
47
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
48
48
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
49
49
  var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
50
+ var TELEMETRY_HEADER_AGENT = "Upstash-Telemetry-Agent";
50
51
 
51
52
  // src/error.ts
52
53
  import { QstashError } from "@upstash/qstash";
@@ -101,6 +102,271 @@ var StepTypes = [
101
102
  "Invoke"
102
103
  ];
103
104
 
105
+ // src/agents/adapters.ts
106
+ import { tool } from "ai";
107
+
108
+ // src/agents/constants.ts
109
+ var AGENT_NAME_HEADER = "upstash-agent-name";
110
+ var MANAGER_AGENT_PROMPT = `You are an agent orchestrating other AI Agents.
111
+
112
+ These other agents have tools available to them.
113
+
114
+ Given a prompt, utilize these agents to address requests.
115
+
116
+ 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.
117
+
118
+ Avoid calling the same agent twice in one turn. Instead, prefer to call it once but provide everything
119
+ you need from that agent.
120
+ `;
121
+
122
+ // src/agents/adapters.ts
123
+ var fetchWithContextCall = async (context, agentCallParams, ...params) => {
124
+ const [input, init] = params;
125
+ try {
126
+ const headers = init?.headers ? Object.fromEntries(new Headers(init.headers).entries()) : {};
127
+ const body = init?.body ? JSON.parse(init.body) : void 0;
128
+ const agentName = headers[AGENT_NAME_HEADER];
129
+ const stepName = agentName ? `Call Agent ${agentName}` : "Call Agent";
130
+ const responseInfo = await context.call(stepName, {
131
+ url: input.toString(),
132
+ method: init?.method,
133
+ headers,
134
+ body,
135
+ timeout: agentCallParams?.timeout,
136
+ retries: agentCallParams?.retries,
137
+ flowControl: agentCallParams?.flowControl
138
+ });
139
+ const responseHeaders = new Headers(
140
+ Object.entries(responseInfo.header).reduce(
141
+ (acc, [key, values]) => {
142
+ acc[key] = values.join(", ");
143
+ return acc;
144
+ },
145
+ {}
146
+ )
147
+ );
148
+ return new Response(JSON.stringify(responseInfo.body), {
149
+ status: responseInfo.status,
150
+ headers: responseHeaders
151
+ });
152
+ } catch (error) {
153
+ if (error instanceof Error && error.name === "WorkflowAbort") {
154
+ throw error;
155
+ } else {
156
+ console.error("Error in fetch implementation:", error);
157
+ throw error;
158
+ }
159
+ }
160
+ };
161
+ var createWorkflowModel = ({
162
+ context,
163
+ provider,
164
+ providerParams,
165
+ agentCallParams
166
+ }) => {
167
+ return provider({
168
+ fetch: (...params) => fetchWithContextCall(context, agentCallParams, ...params),
169
+ ...providerParams
170
+ });
171
+ };
172
+ var wrapTools = ({
173
+ context,
174
+ tools
175
+ }) => {
176
+ return Object.fromEntries(
177
+ Object.entries(tools).map((toolInfo) => {
178
+ const [toolName, tool3] = toolInfo;
179
+ const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
180
+ const aiSDKTool = convertToAISDKTool(tool3);
181
+ const execute = aiSDKTool.execute;
182
+ if (execute && executeAsStep) {
183
+ const wrappedExecute = (...params) => {
184
+ return context.run(`Run tool ${toolName}`, () => execute(...params));
185
+ };
186
+ aiSDKTool.execute = wrappedExecute;
187
+ }
188
+ return [toolName, aiSDKTool];
189
+ })
190
+ );
191
+ };
192
+ var convertToAISDKTool = (tool3) => {
193
+ const isLangchainTool = "invoke" in tool3;
194
+ return isLangchainTool ? convertLangchainTool(tool3) : tool3;
195
+ };
196
+ var convertLangchainTool = (langchainTool) => {
197
+ return tool({
198
+ description: langchainTool.description,
199
+ parameters: langchainTool.schema,
200
+ execute: async (...param) => langchainTool.invoke(...param)
201
+ });
202
+ };
203
+ var WorkflowTool = class {
204
+ /**
205
+ * description of the tool
206
+ */
207
+ description;
208
+ /**
209
+ * schema of the tool
210
+ */
211
+ schema;
212
+ /**
213
+ * function to invoke the tool
214
+ */
215
+ invoke;
216
+ /**
217
+ * whether the invoke method of the tool is to be wrapped with `context.run`
218
+ */
219
+ executeAsStep;
220
+ /**
221
+ *
222
+ * @param description description of the tool
223
+ * @param schema schema of the tool
224
+ * @param invoke function to invoke the tool
225
+ * @param executeAsStep whether the invoke method of the tool is to be wrapped with `context.run`
226
+ */
227
+ constructor(params) {
228
+ this.description = params.description;
229
+ this.schema = params.schema;
230
+ this.invoke = params.invoke;
231
+ this.executeAsStep = params.executeAsStep ?? true;
232
+ }
233
+ };
234
+
235
+ // src/serve/serve-many.ts
236
+ var getWorkflowId = (url) => {
237
+ const components = url.split("/");
238
+ const lastComponent = components[components.length - 1];
239
+ return lastComponent.split("?")[0];
240
+ };
241
+ var serveManyBase = ({
242
+ workflows,
243
+ getUrl,
244
+ serveMethod,
245
+ options
246
+ }) => {
247
+ const workflowIds = [];
248
+ const workflowMap = Object.fromEntries(
249
+ Object.entries(workflows).map((workflow) => {
250
+ const workflowId = workflow[0];
251
+ if (workflowIds.includes(workflowId)) {
252
+ throw new WorkflowError(
253
+ `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
254
+ );
255
+ }
256
+ if (workflowId.includes("/")) {
257
+ throw new WorkflowError(
258
+ `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
259
+ );
260
+ }
261
+ workflowIds.push(workflowId);
262
+ workflow[1].workflowId = workflowId;
263
+ workflow[1].options = {
264
+ ...options,
265
+ ...workflow[1].options
266
+ };
267
+ const params = [workflow[1].routeFunction, workflow[1].options];
268
+ const handler = serveMethod(...params);
269
+ return [workflowId, handler];
270
+ })
271
+ );
272
+ return {
273
+ handler: async (...params) => {
274
+ const url = getUrl(...params);
275
+ const pickedWorkflowId = getWorkflowId(url);
276
+ if (!pickedWorkflowId) {
277
+ return new Response(
278
+ `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
279
+ {
280
+ status: 404
281
+ }
282
+ );
283
+ }
284
+ const workflow = workflowMap[pickedWorkflowId];
285
+ if (!workflow) {
286
+ return new Response(
287
+ `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
288
+ {
289
+ status: 404
290
+ }
291
+ );
292
+ }
293
+ return await workflow(...params);
294
+ }
295
+ };
296
+ };
297
+ var getNewUrlFromWorkflowId = (url, workflowId) => {
298
+ if (!workflowId) {
299
+ throw new WorkflowError("You can only call workflow which has a workflowId");
300
+ }
301
+ return url.replace(/[^/]+$/, workflowId);
302
+ };
303
+
304
+ // src/context/auto-executor.ts
305
+ import { QstashError as QstashError5 } from "@upstash/qstash";
306
+
307
+ // src/qstash/headers.ts
308
+ import { QstashError as QstashError4 } from "@upstash/qstash";
309
+
310
+ // src/client/utils.ts
311
+ import { QstashError as QstashError2 } from "@upstash/qstash";
312
+ var makeNotifyRequest = async (requester, eventId, eventData) => {
313
+ const result = await requester.request({
314
+ path: ["v2", "notify", eventId],
315
+ method: "POST",
316
+ body: typeof eventData === "string" ? eventData : JSON.stringify(eventData)
317
+ });
318
+ return result;
319
+ };
320
+ var makeGetWaitersRequest = async (requester, eventId) => {
321
+ const result = await requester.request({
322
+ path: ["v2", "waiters", eventId],
323
+ method: "GET"
324
+ });
325
+ return result;
326
+ };
327
+ var makeCancelRequest = async (requester, workflowRunId) => {
328
+ await requester.request({
329
+ path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
330
+ method: "DELETE",
331
+ parseResponseAsJson: false
332
+ });
333
+ return true;
334
+ };
335
+ var getSteps = async (requester, workflowRunId, messageId, debug) => {
336
+ try {
337
+ const steps = await requester.request({
338
+ path: ["v2", "workflows", "runs", workflowRunId],
339
+ parseResponseAsJson: true
340
+ });
341
+ if (!messageId) {
342
+ await debug?.log("INFO", "ENDPOINT_START", {
343
+ message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
344
+ });
345
+ return { steps, workflowRunEnded: false };
346
+ } else {
347
+ const index = steps.findIndex((item) => item.messageId === messageId);
348
+ if (index === -1) {
349
+ return { steps: [], workflowRunEnded: false };
350
+ }
351
+ const filteredSteps = steps.slice(0, index + 1);
352
+ await debug?.log("INFO", "ENDPOINT_START", {
353
+ message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
354
+ });
355
+ return { steps: filteredSteps, workflowRunEnded: false };
356
+ }
357
+ } catch (error) {
358
+ if (error instanceof QstashError2 && error.status === 404) {
359
+ await debug?.log("WARN", "ENDPOINT_START", {
360
+ message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
361
+ error
362
+ });
363
+ return { steps: void 0, workflowRunEnded: true };
364
+ } else {
365
+ throw error;
366
+ }
367
+ }
368
+ };
369
+
104
370
  // src/utils.ts
105
371
  var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
106
372
  var NANOID_LENGTH = 21;
@@ -543,85 +809,27 @@ var fromThrowable = Result.fromThrowable;
543
809
 
544
810
  // src/workflow-requests.ts
545
811
  import { QstashError as QstashError3 } from "@upstash/qstash";
546
-
547
- // src/client/utils.ts
548
- import { QstashError as QstashError2 } from "@upstash/qstash";
549
- var makeNotifyRequest = async (requester, eventId, eventData) => {
550
- const result = await requester.request({
551
- path: ["v2", "notify", eventId],
552
- method: "POST",
553
- body: typeof eventData === "string" ? eventData : JSON.stringify(eventData)
554
- });
555
- return result;
556
- };
557
- var makeGetWaitersRequest = async (requester, eventId) => {
558
- const result = await requester.request({
559
- path: ["v2", "waiters", eventId],
560
- method: "GET"
561
- });
562
- return result;
563
- };
564
- var makeCancelRequest = async (requester, workflowRunId) => {
565
- await requester.request({
566
- path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
567
- method: "DELETE",
568
- parseResponseAsJson: false
569
- });
570
- return true;
571
- };
572
- var getSteps = async (requester, workflowRunId, messageId, debug) => {
573
- try {
574
- const steps = await requester.request({
575
- path: ["v2", "workflows", "runs", workflowRunId],
576
- parseResponseAsJson: true
577
- });
578
- if (!messageId) {
579
- await debug?.log("INFO", "ENDPOINT_START", {
580
- message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
581
- });
582
- return { steps, workflowRunEnded: false };
583
- } else {
584
- const index = steps.findIndex((item) => item.messageId === messageId);
585
- if (index === -1) {
586
- return { steps: [], workflowRunEnded: false };
587
- }
588
- const filteredSteps = steps.slice(0, index + 1);
589
- await debug?.log("INFO", "ENDPOINT_START", {
590
- message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
591
- });
592
- return { steps: filteredSteps, workflowRunEnded: false };
593
- }
594
- } catch (error) {
595
- if (error instanceof QstashError2 && error.status === 404) {
596
- await debug?.log("WARN", "ENDPOINT_START", {
597
- message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
598
- error
599
- });
600
- return { steps: void 0, workflowRunEnded: true };
601
- } else {
602
- throw error;
603
- }
604
- }
605
- };
606
-
607
- // src/workflow-requests.ts
608
812
  var triggerFirstInvocation = async ({
609
813
  workflowContext,
610
814
  useJSONContent,
611
815
  telemetry,
612
816
  debug,
613
- invokeCount
817
+ invokeCount,
818
+ delay
614
819
  }) => {
615
820
  const { headers } = getHeaders({
616
821
  initHeaderValue: "true",
617
- workflowRunId: workflowContext.workflowRunId,
618
- workflowUrl: workflowContext.url,
619
- userHeaders: workflowContext.headers,
620
- failureUrl: workflowContext.failureUrl,
621
- retries: workflowContext.retries,
622
- telemetry,
623
- invokeCount,
624
- flowControl: workflowContext.flowControl
822
+ workflowConfig: {
823
+ workflowRunId: workflowContext.workflowRunId,
824
+ workflowUrl: workflowContext.url,
825
+ failureUrl: workflowContext.failureUrl,
826
+ retries: workflowContext.retries,
827
+ telemetry,
828
+ flowControl: workflowContext.flowControl,
829
+ useJSONContent: useJSONContent ?? false
830
+ },
831
+ invokeCount: invokeCount ?? 0,
832
+ userHeaders: workflowContext.headers
625
833
  });
626
834
  if (workflowContext.headers.get("content-type")) {
627
835
  headers["content-type"] = workflowContext.headers.get("content-type");
@@ -635,7 +843,8 @@ var triggerFirstInvocation = async ({
635
843
  headers,
636
844
  method: "POST",
637
845
  body,
638
- url: workflowContext.url
846
+ url: workflowContext.url,
847
+ delay
639
848
  });
640
849
  if (result.deduplicated) {
641
850
  await debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
@@ -791,14 +1000,16 @@ ${atob(callbackMessage.body ?? "")}`
791
1000
  const userHeaders = recreateUserHeaders(request.headers);
792
1001
  const { headers: requestHeaders } = getHeaders({
793
1002
  initHeaderValue: "false",
794
- workflowRunId,
795
- workflowUrl,
1003
+ workflowConfig: {
1004
+ workflowRunId,
1005
+ workflowUrl,
1006
+ failureUrl,
1007
+ retries,
1008
+ telemetry,
1009
+ flowControl
1010
+ },
796
1011
  userHeaders,
797
- failureUrl,
798
- retries,
799
- telemetry,
800
- invokeCount: Number(invokeCount),
801
- flowControl
1012
+ invokeCount: Number(invokeCount)
802
1013
  });
803
1014
  const callResponse = {
804
1015
  status: callbackMessage.status,
@@ -844,154 +1055,6 @@ var getTelemetryHeaders = (telemetry) => {
844
1055
  [TELEMETRY_HEADER_RUNTIME]: telemetry.runtime ?? "unknown"
845
1056
  };
846
1057
  };
847
- var getHeaders = ({
848
- initHeaderValue,
849
- workflowRunId,
850
- workflowUrl,
851
- userHeaders,
852
- failureUrl,
853
- retries,
854
- step,
855
- callRetries,
856
- callTimeout,
857
- telemetry,
858
- invokeCount,
859
- flowControl,
860
- callFlowControl
861
- }) => {
862
- const callHeaders = new Headers(step?.callHeaders);
863
- const contentType = (callHeaders.get("content-type") ? callHeaders.get("content-type") : userHeaders?.get("Content-Type") ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
864
- const baseHeaders = {
865
- [WORKFLOW_INIT_HEADER]: initHeaderValue,
866
- [WORKFLOW_ID_HEADER]: workflowRunId,
867
- [WORKFLOW_URL_HEADER]: workflowUrl,
868
- [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
869
- [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
870
- "content-type": contentType,
871
- ...telemetry ? getTelemetryHeaders(telemetry) : {}
872
- };
873
- if (invokeCount !== void 0 && !step?.callUrl) {
874
- baseHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount.toString();
875
- }
876
- if (!step?.callUrl) {
877
- baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
878
- }
879
- if (callTimeout) {
880
- baseHeaders[`Upstash-Timeout`] = callTimeout.toString();
881
- }
882
- if (failureUrl) {
883
- baseHeaders[`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
884
- baseHeaders[`Upstash-Failure-Callback-Forward-Upstash-Workflow-Failure-Callback`] = "true";
885
- baseHeaders["Upstash-Failure-Callback-Workflow-Runid"] = workflowRunId;
886
- baseHeaders["Upstash-Failure-Callback-Workflow-Init"] = "false";
887
- baseHeaders["Upstash-Failure-Callback-Workflow-Url"] = workflowUrl;
888
- baseHeaders["Upstash-Failure-Callback-Workflow-Calltype"] = "failureCall";
889
- if (retries !== void 0) {
890
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
891
- }
892
- if (flowControl) {
893
- const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
894
- baseHeaders["Upstash-Failure-Callback-Flow-Control-Key"] = flowControlKey;
895
- baseHeaders["Upstash-Failure-Callback-Flow-Control-Value"] = flowControlValue;
896
- }
897
- if (!step?.callUrl) {
898
- baseHeaders["Upstash-Failure-Callback"] = failureUrl;
899
- }
900
- }
901
- if (step?.callUrl) {
902
- baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
903
- baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
904
- if (retries !== void 0) {
905
- baseHeaders["Upstash-Callback-Retries"] = retries.toString();
906
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
907
- }
908
- if (callFlowControl) {
909
- const { flowControlKey, flowControlValue } = prepareFlowControl(callFlowControl);
910
- baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
911
- baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
912
- }
913
- if (flowControl) {
914
- const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
915
- baseHeaders["Upstash-Callback-Flow-Control-Key"] = flowControlKey;
916
- baseHeaders["Upstash-Callback-Flow-Control-Value"] = flowControlValue;
917
- }
918
- } else {
919
- if (flowControl) {
920
- const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
921
- baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
922
- baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
923
- }
924
- if (retries !== void 0) {
925
- baseHeaders["Upstash-Retries"] = retries.toString();
926
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
927
- }
928
- }
929
- if (userHeaders) {
930
- for (const header of userHeaders.keys()) {
931
- if (step?.callHeaders) {
932
- baseHeaders[`Upstash-Callback-Forward-${header}`] = userHeaders.get(header);
933
- } else {
934
- baseHeaders[`Upstash-Forward-${header}`] = userHeaders.get(header);
935
- }
936
- baseHeaders[`Upstash-Failure-Callback-Forward-${header}`] = userHeaders.get(header);
937
- }
938
- }
939
- if (step?.callHeaders) {
940
- const forwardedHeaders = Object.fromEntries(
941
- Object.entries(step.callHeaders).map(([header, value]) => [
942
- `Upstash-Forward-${header}`,
943
- value
944
- ])
945
- );
946
- return {
947
- headers: {
948
- ...baseHeaders,
949
- ...forwardedHeaders,
950
- "Upstash-Callback": workflowUrl,
951
- "Upstash-Callback-Workflow-RunId": workflowRunId,
952
- "Upstash-Callback-Workflow-CallType": "fromCallback",
953
- "Upstash-Callback-Workflow-Init": "false",
954
- "Upstash-Callback-Workflow-Url": workflowUrl,
955
- "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
956
- "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
957
- "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
958
- "Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
959
- "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
960
- "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
961
- "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
962
- [`Upstash-Callback-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`]: (invokeCount ?? 0).toString(),
963
- "Upstash-Workflow-CallType": "toCallback"
964
- }
965
- };
966
- }
967
- if (step?.waitEventId) {
968
- return {
969
- headers: {
970
- ...baseHeaders,
971
- "Upstash-Workflow-CallType": "step"
972
- },
973
- timeoutHeaders: {
974
- // to include user headers:
975
- ...Object.fromEntries(
976
- Object.entries(baseHeaders).map(([header, value]) => [header, [value]])
977
- ),
978
- // to include telemetry headers:
979
- ...telemetry ? Object.fromEntries(
980
- Object.entries(getTelemetryHeaders(telemetry)).map(([header, value]) => [
981
- header,
982
- [value]
983
- ])
984
- ) : {},
985
- // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
986
- "Upstash-Workflow-Runid": [workflowRunId],
987
- [WORKFLOW_INIT_HEADER]: ["false"],
988
- [WORKFLOW_URL_HEADER]: [workflowUrl],
989
- "Upstash-Workflow-CallType": ["step"]
990
- }
991
- };
992
- }
993
- return { headers: baseHeaders };
994
- };
995
1058
  var verifyRequest = async (body, signature, verifier) => {
996
1059
  if (!verifier) {
997
1060
  return;
@@ -1010,286 +1073,11 @@ var verifyRequest = async (body, signature, verifier) => {
1010
1073
  } catch (error) {
1011
1074
  throw new WorkflowError(
1012
1075
  `Failed to verify that the Workflow request comes from QStash: ${error}
1013
-
1014
- If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
1015
-
1016
- If you want to disable QStash Verification, you should clear env variables QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY`
1017
- );
1018
- }
1019
- };
1020
- var prepareFlowControl = (flowControl) => {
1021
- const parallelism = flowControl.parallelism?.toString();
1022
- const rate = flowControl.ratePerSecond?.toString();
1023
- const controlValue = [
1024
- parallelism ? `parallelism=${parallelism}` : void 0,
1025
- rate ? `rate=${rate}` : void 0
1026
- ].filter(Boolean);
1027
- if (controlValue.length === 0) {
1028
- throw new QstashError3("Provide at least one of parallelism or ratePerSecond for flowControl");
1029
- }
1030
- return {
1031
- flowControlKey: flowControl.key,
1032
- flowControlValue: controlValue.join(", ")
1033
- };
1034
- };
1035
-
1036
- // src/serve/serve-many.ts
1037
- var getWorkflowId = (url) => {
1038
- const components = url.split("/");
1039
- const lastComponent = components[components.length - 1];
1040
- return lastComponent.split("?")[0];
1041
- };
1042
- var serveManyBase = ({
1043
- workflows,
1044
- getUrl,
1045
- serveMethod,
1046
- options
1047
- }) => {
1048
- const workflowIds = [];
1049
- const workflowMap = Object.fromEntries(
1050
- Object.entries(workflows).map((workflow) => {
1051
- const workflowId = workflow[0];
1052
- if (workflowIds.includes(workflowId)) {
1053
- throw new WorkflowError(
1054
- `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
1055
- );
1056
- }
1057
- if (workflowId.includes("/")) {
1058
- throw new WorkflowError(
1059
- `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
1060
- );
1061
- }
1062
- workflowIds.push(workflowId);
1063
- workflow[1].workflowId = workflowId;
1064
- workflow[1].options = {
1065
- ...options,
1066
- ...workflow[1].options
1067
- };
1068
- const params = [workflow[1].routeFunction, workflow[1].options];
1069
- const handler = serveMethod(...params);
1070
- return [workflowId, handler];
1071
- })
1072
- );
1073
- return {
1074
- handler: async (...params) => {
1075
- const url = getUrl(...params);
1076
- const pickedWorkflowId = getWorkflowId(url);
1077
- if (!pickedWorkflowId) {
1078
- return new Response(
1079
- `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
1080
- {
1081
- status: 404
1082
- }
1083
- );
1084
- }
1085
- const workflow = workflowMap[pickedWorkflowId];
1086
- if (!workflow) {
1087
- return new Response(
1088
- `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
1089
- {
1090
- status: 404
1091
- }
1092
- );
1093
- }
1094
- return await workflow(...params);
1095
- }
1096
- };
1097
- };
1098
- var invokeWorkflow = async ({
1099
- settings,
1100
- invokeStep,
1101
- context,
1102
- invokeCount,
1103
- telemetry
1104
- }) => {
1105
- const {
1106
- body,
1107
- workflow,
1108
- headers = {},
1109
- workflowRunId = getWorkflowRunId(),
1110
- retries,
1111
- flowControl
1112
- } = settings;
1113
- const { workflowId } = workflow;
1114
- const {
1115
- retries: workflowRetries,
1116
- failureFunction,
1117
- failureUrl,
1118
- useJSONContent,
1119
- flowControl: workflowFlowControl
1120
- } = workflow.options;
1121
- if (!workflowId) {
1122
- throw new WorkflowError("You can only invoke workflow which has a workflowId");
1123
- }
1124
- const { headers: invokerHeaders } = getHeaders({
1125
- initHeaderValue: "false",
1126
- workflowRunId: context.workflowRunId,
1127
- workflowUrl: context.url,
1128
- userHeaders: context.headers,
1129
- failureUrl: context.failureUrl,
1130
- retries: context.retries,
1131
- telemetry,
1132
- invokeCount,
1133
- flowControl: context.flowControl
1134
- });
1135
- invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1136
- const newUrl = context.url.replace(/[^/]+$/, workflowId);
1137
- const { headers: triggerHeaders } = getHeaders({
1138
- initHeaderValue: "true",
1139
- workflowRunId,
1140
- workflowUrl: newUrl,
1141
- userHeaders: new Headers(headers),
1142
- retries: retries ?? workflowRetries,
1143
- telemetry,
1144
- failureUrl: failureFunction ? newUrl : failureUrl,
1145
- invokeCount: invokeCount + 1,
1146
- flowControl: flowControl ?? workflowFlowControl
1147
- });
1148
- triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1149
- if (useJSONContent) {
1150
- triggerHeaders["content-type"] = "application/json";
1151
- }
1152
- const request = {
1153
- body: JSON.stringify(body),
1154
- headers: Object.fromEntries(
1155
- Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1156
- ),
1157
- workflowRunId: context.workflowRunId,
1158
- workflowUrl: context.url,
1159
- step: invokeStep
1160
- };
1161
- await context.qstashClient.publish({
1162
- headers: triggerHeaders,
1163
- method: "POST",
1164
- body: JSON.stringify(request),
1165
- url: newUrl
1166
- });
1167
- };
1168
-
1169
- // src/agents/adapters.ts
1170
- import { createOpenAI } from "@ai-sdk/openai";
1171
- import { tool } from "ai";
1172
-
1173
- // src/agents/constants.ts
1174
- var AGENT_NAME_HEADER = "upstash-agent-name";
1175
- var MANAGER_AGENT_PROMPT = `You are an agent orchestrating other AI Agents.
1176
-
1177
- These other agents have tools available to them.
1178
-
1179
- Given a prompt, utilize these agents to address requests.
1180
-
1181
- 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.
1182
-
1183
- Avoid calling the same agent twice in one turn. Instead, prefer to call it once but provide everything
1184
- you need from that agent.
1185
- `;
1186
-
1187
- // src/agents/adapters.ts
1188
- var fetchWithContextCall = async (context, ...params) => {
1189
- const [input, init] = params;
1190
- try {
1191
- const headers = init?.headers ? Object.fromEntries(new Headers(init.headers).entries()) : {};
1192
- const body = init?.body ? JSON.parse(init.body) : void 0;
1193
- const agentName = headers[AGENT_NAME_HEADER];
1194
- const stepName = agentName ? `Call Agent ${agentName}` : "Call Agent";
1195
- const responseInfo = await context.call(stepName, {
1196
- url: input.toString(),
1197
- method: init?.method,
1198
- headers,
1199
- body
1200
- });
1201
- const responseHeaders = new Headers(
1202
- Object.entries(responseInfo.header).reduce(
1203
- (acc, [key, values]) => {
1204
- acc[key] = values.join(", ");
1205
- return acc;
1206
- },
1207
- {}
1208
- )
1209
- );
1210
- return new Response(JSON.stringify(responseInfo.body), {
1211
- status: responseInfo.status,
1212
- headers: responseHeaders
1213
- });
1214
- } catch (error) {
1215
- if (error instanceof Error && error.name === "WorkflowAbort") {
1216
- throw error;
1217
- } else {
1218
- console.error("Error in fetch implementation:", error);
1219
- throw error;
1220
- }
1221
- }
1222
- };
1223
- var createWorkflowModel = ({
1224
- context,
1225
- provider,
1226
- providerParams
1227
- }) => {
1228
- return provider({
1229
- fetch: (...params) => fetchWithContextCall(context, ...params),
1230
- ...providerParams
1231
- });
1232
- };
1233
- var wrapTools = ({
1234
- context,
1235
- tools
1236
- }) => {
1237
- return Object.fromEntries(
1238
- Object.entries(tools).map((toolInfo) => {
1239
- const [toolName, tool3] = toolInfo;
1240
- const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
1241
- const aiSDKTool = convertToAISDKTool(tool3);
1242
- const execute = aiSDKTool.execute;
1243
- if (execute && executeAsStep) {
1244
- const wrappedExecute = (...params) => {
1245
- return context.run(`Run tool ${toolName}`, () => execute(...params));
1246
- };
1247
- aiSDKTool.execute = wrappedExecute;
1248
- }
1249
- return [toolName, aiSDKTool];
1250
- })
1251
- );
1252
- };
1253
- var convertToAISDKTool = (tool3) => {
1254
- const isLangchainTool = "invoke" in tool3;
1255
- return isLangchainTool ? convertLangchainTool(tool3) : tool3;
1256
- };
1257
- var convertLangchainTool = (langchainTool) => {
1258
- return tool({
1259
- description: langchainTool.description,
1260
- parameters: langchainTool.schema,
1261
- execute: async (...param) => langchainTool.invoke(...param)
1262
- });
1263
- };
1264
- var WorkflowTool = class {
1265
- /**
1266
- * description of the tool
1267
- */
1268
- description;
1269
- /**
1270
- * schema of the tool
1271
- */
1272
- schema;
1273
- /**
1274
- * function to invoke the tool
1275
- */
1276
- invoke;
1277
- /**
1278
- * whether the invoke method of the tool is to be wrapped with `context.run`
1279
- */
1280
- executeAsStep;
1281
- /**
1282
- *
1283
- * @param description description of the tool
1284
- * @param schema schema of the tool
1285
- * @param invoke function to invoke the tool
1286
- * @param executeAsStep whether the invoke method of the tool is to be wrapped with `context.run`
1287
- */
1288
- constructor(params) {
1289
- this.description = params.description;
1290
- this.schema = params.schema;
1291
- this.invoke = params.invoke;
1292
- this.executeAsStep = params.executeAsStep ?? true;
1076
+
1077
+ If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
1078
+
1079
+ If you want to disable QStash Verification, you should clear env variables QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY`
1080
+ );
1293
1081
  }
1294
1082
  };
1295
1083
 
@@ -1302,6 +1090,11 @@ var BaseLazyStep = class _BaseLazyStep {
1302
1090
  "A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
1303
1091
  );
1304
1092
  }
1093
+ if (typeof stepName !== "string") {
1094
+ console.warn(
1095
+ "Workflow Warning: A workflow step name must be a string. In a future release, this will throw an error."
1096
+ );
1097
+ }
1305
1098
  this.stepName = stepName;
1306
1099
  }
1307
1100
  /**
@@ -1351,6 +1144,40 @@ var BaseLazyStep = class _BaseLazyStep {
1351
1144
  return stepOut;
1352
1145
  }
1353
1146
  }
1147
+ getBody({ step }) {
1148
+ step.out = JSON.stringify(step.out);
1149
+ return JSON.stringify(step);
1150
+ }
1151
+ getHeaders({ context, telemetry, invokeCount, step }) {
1152
+ return getHeaders({
1153
+ initHeaderValue: "false",
1154
+ workflowConfig: {
1155
+ workflowRunId: context.workflowRunId,
1156
+ workflowUrl: context.url,
1157
+ failureUrl: context.failureUrl,
1158
+ retries: context.retries,
1159
+ useJSONContent: false,
1160
+ telemetry,
1161
+ flowControl: context.flowControl
1162
+ },
1163
+ userHeaders: context.headers,
1164
+ invokeCount,
1165
+ stepInfo: {
1166
+ step,
1167
+ lazyStep: this
1168
+ }
1169
+ });
1170
+ }
1171
+ async submitStep({ context, body, headers }) {
1172
+ return await context.qstashClient.batch([
1173
+ {
1174
+ body,
1175
+ headers,
1176
+ method: "POST",
1177
+ url: context.url
1178
+ }
1179
+ ]);
1180
+ }
1354
1181
  };
1355
1182
  var LazyFunctionStep = class extends BaseLazyStep {
1356
1183
  stepFunction;
@@ -1410,6 +1237,17 @@ var LazySleepStep = class extends BaseLazyStep {
1410
1237
  concurrent
1411
1238
  });
1412
1239
  }
1240
+ async submitStep({ context, body, headers, isParallel }) {
1241
+ return await context.qstashClient.batch([
1242
+ {
1243
+ body,
1244
+ headers,
1245
+ method: "POST",
1246
+ url: context.url,
1247
+ delay: isParallel ? void 0 : this.sleep
1248
+ }
1249
+ ]);
1250
+ }
1413
1251
  };
1414
1252
  var LazySleepUntilStep = class extends BaseLazyStep {
1415
1253
  sleepUntil;
@@ -1441,6 +1279,17 @@ var LazySleepUntilStep = class extends BaseLazyStep {
1441
1279
  safeParseOut() {
1442
1280
  return void 0;
1443
1281
  }
1282
+ async submitStep({ context, body, headers, isParallel }) {
1283
+ return await context.qstashClient.batch([
1284
+ {
1285
+ body,
1286
+ headers,
1287
+ method: "POST",
1288
+ url: context.url,
1289
+ notBefore: isParallel ? void 0 : this.sleepUntil
1290
+ }
1291
+ ]);
1292
+ }
1444
1293
  };
1445
1294
  var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1446
1295
  url;
@@ -1524,6 +1373,58 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1524
1373
  }
1525
1374
  return false;
1526
1375
  };
1376
+ getBody({ step }) {
1377
+ if (!step.callUrl) {
1378
+ throw new WorkflowError("Incompatible step received in LazyCallStep.getBody");
1379
+ }
1380
+ return JSON.stringify(step.callBody);
1381
+ }
1382
+ getHeaders({ context, telemetry, invokeCount, step }) {
1383
+ const { headers, contentType } = super.getHeaders({ context, telemetry, invokeCount, step });
1384
+ headers["Upstash-Retries"] = this.retries.toString();
1385
+ headers[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
1386
+ if (this.flowControl) {
1387
+ const { flowControlKey, flowControlValue } = prepareFlowControl(this.flowControl);
1388
+ headers["Upstash-Flow-Control-Key"] = flowControlKey;
1389
+ headers["Upstash-Flow-Control-Value"] = flowControlValue;
1390
+ }
1391
+ if (this.timeout) {
1392
+ headers["Upstash-Timeout"] = this.timeout.toString();
1393
+ }
1394
+ const forwardedHeaders = Object.fromEntries(
1395
+ Object.entries(this.headers).map(([header, value]) => [`Upstash-Forward-${header}`, value])
1396
+ );
1397
+ return {
1398
+ headers: {
1399
+ ...headers,
1400
+ ...forwardedHeaders,
1401
+ "Upstash-Callback": context.url,
1402
+ "Upstash-Callback-Workflow-RunId": context.workflowRunId,
1403
+ "Upstash-Callback-Workflow-CallType": "fromCallback",
1404
+ "Upstash-Callback-Workflow-Init": "false",
1405
+ "Upstash-Callback-Workflow-Url": context.url,
1406
+ "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
1407
+ "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
1408
+ "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
1409
+ "Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
1410
+ "Upstash-Callback-Forward-Upstash-Workflow-StepType": this.stepType,
1411
+ "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
1412
+ "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
1413
+ "Upstash-Workflow-CallType": "toCallback"
1414
+ },
1415
+ contentType
1416
+ };
1417
+ }
1418
+ async submitStep({ context, headers }) {
1419
+ return await context.qstashClient.batch([
1420
+ {
1421
+ headers,
1422
+ body: JSON.stringify(this.body),
1423
+ method: this.method,
1424
+ url: this.url
1425
+ }
1426
+ ]);
1427
+ }
1527
1428
  };
1528
1429
  var LazyWaitForEventStep = class extends BaseLazyStep {
1529
1430
  eventId;
@@ -1563,6 +1464,57 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
1563
1464
  eventData: BaseLazyStep.tryParsing(result.eventData)
1564
1465
  };
1565
1466
  }
1467
+ getHeaders({ context, telemetry, invokeCount, step }) {
1468
+ const headers = super.getHeaders({ context, telemetry, invokeCount, step });
1469
+ headers.headers["Upstash-Workflow-CallType"] = "step";
1470
+ return headers;
1471
+ }
1472
+ getBody({ context, step, headers, telemetry }) {
1473
+ if (!step.waitEventId) {
1474
+ throw new WorkflowError("Incompatible step received in LazyWaitForEventStep.getBody");
1475
+ }
1476
+ const timeoutHeaders = {
1477
+ // to include user headers:
1478
+ ...Object.fromEntries(Object.entries(headers).map(([header, value]) => [header, [value]])),
1479
+ // to include telemetry headers:
1480
+ ...telemetry ? Object.fromEntries(
1481
+ Object.entries(getTelemetryHeaders(telemetry)).map(([header, value]) => [
1482
+ header,
1483
+ [value]
1484
+ ])
1485
+ ) : {},
1486
+ // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
1487
+ "Upstash-Workflow-Runid": [context.workflowRunId],
1488
+ [WORKFLOW_INIT_HEADER]: ["false"],
1489
+ [WORKFLOW_URL_HEADER]: [context.url],
1490
+ "Upstash-Workflow-CallType": ["step"]
1491
+ };
1492
+ const waitBody = {
1493
+ url: context.url,
1494
+ timeout: step.timeout,
1495
+ timeoutBody: void 0,
1496
+ timeoutUrl: context.url,
1497
+ timeoutHeaders,
1498
+ step: {
1499
+ stepId: step.stepId,
1500
+ stepType: "Wait",
1501
+ stepName: step.stepName,
1502
+ concurrent: step.concurrent,
1503
+ targetStep: step.targetStep
1504
+ }
1505
+ };
1506
+ return JSON.stringify(waitBody);
1507
+ }
1508
+ async submitStep({ context, body, headers }) {
1509
+ const result = await context.qstashClient.http.request({
1510
+ path: ["v2", "wait", this.eventId],
1511
+ body,
1512
+ headers,
1513
+ method: "POST",
1514
+ parseResponseAsJson: false
1515
+ });
1516
+ return [result];
1517
+ }
1566
1518
  };
1567
1519
  var LazyNotifyStep = class extends LazyFunctionStep {
1568
1520
  stepType = "Notify";
@@ -1588,6 +1540,10 @@ var LazyInvokeStep = class extends BaseLazyStep {
1588
1540
  stepType = "Invoke";
1589
1541
  params;
1590
1542
  allowUndefinedOut = false;
1543
+ /**
1544
+ * workflow id of the invoked workflow
1545
+ */
1546
+ workflowId;
1591
1547
  constructor(stepName, {
1592
1548
  workflow,
1593
1549
  body,
@@ -1605,6 +1561,11 @@ var LazyInvokeStep = class extends BaseLazyStep {
1605
1561
  retries,
1606
1562
  flowControl
1607
1563
  };
1564
+ const { workflowId } = workflow;
1565
+ if (!workflowId) {
1566
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
1567
+ }
1568
+ this.workflowId = workflowId;
1608
1569
  }
1609
1570
  getPlanStep(concurrent, targetStep) {
1610
1571
  return {
@@ -1634,10 +1595,348 @@ var LazyInvokeStep = class extends BaseLazyStep {
1634
1595
  body: BaseLazyStep.tryParsing(result.body)
1635
1596
  };
1636
1597
  }
1598
+ getBody({ context, step, telemetry, invokeCount }) {
1599
+ const { headers: invokerHeaders } = getHeaders({
1600
+ initHeaderValue: "false",
1601
+ workflowConfig: {
1602
+ workflowRunId: context.workflowRunId,
1603
+ workflowUrl: context.url,
1604
+ failureUrl: context.failureUrl,
1605
+ retries: context.retries,
1606
+ telemetry,
1607
+ flowControl: context.flowControl,
1608
+ useJSONContent: false
1609
+ },
1610
+ userHeaders: context.headers,
1611
+ invokeCount
1612
+ });
1613
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1614
+ const request = {
1615
+ body: JSON.stringify(this.params.body),
1616
+ headers: Object.fromEntries(
1617
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1618
+ ),
1619
+ workflowRunId: context.workflowRunId,
1620
+ workflowUrl: context.url,
1621
+ step
1622
+ };
1623
+ return JSON.stringify(request);
1624
+ }
1625
+ getHeaders({ context, telemetry, invokeCount }) {
1626
+ const {
1627
+ workflow,
1628
+ headers = {},
1629
+ workflowRunId = getWorkflowRunId(),
1630
+ retries,
1631
+ flowControl
1632
+ } = this.params;
1633
+ const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
1634
+ const {
1635
+ retries: workflowRetries,
1636
+ failureFunction,
1637
+ failureUrl,
1638
+ useJSONContent,
1639
+ flowControl: workflowFlowControl
1640
+ } = workflow.options;
1641
+ const { headers: triggerHeaders, contentType } = getHeaders({
1642
+ initHeaderValue: "true",
1643
+ workflowConfig: {
1644
+ workflowRunId,
1645
+ workflowUrl: newUrl,
1646
+ retries: retries ?? workflowRetries,
1647
+ telemetry,
1648
+ failureUrl: failureFunction ? newUrl : failureUrl,
1649
+ flowControl: flowControl ?? workflowFlowControl,
1650
+ useJSONContent: useJSONContent ?? false
1651
+ },
1652
+ invokeCount: invokeCount + 1,
1653
+ userHeaders: new Headers(headers)
1654
+ });
1655
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1656
+ return { headers: triggerHeaders, contentType };
1657
+ }
1658
+ async submitStep({ context, body, headers }) {
1659
+ const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
1660
+ const result = await context.qstashClient.publish({
1661
+ headers,
1662
+ method: "POST",
1663
+ body,
1664
+ url: newUrl
1665
+ });
1666
+ return [result];
1667
+ }
1668
+ };
1669
+
1670
+ // src/qstash/headers.ts
1671
+ var WorkflowHeaders = class {
1672
+ userHeaders;
1673
+ workflowConfig;
1674
+ invokeCount;
1675
+ initHeaderValue;
1676
+ stepInfo;
1677
+ headers;
1678
+ constructor({
1679
+ userHeaders,
1680
+ workflowConfig,
1681
+ invokeCount,
1682
+ initHeaderValue,
1683
+ stepInfo
1684
+ }) {
1685
+ this.userHeaders = userHeaders;
1686
+ this.workflowConfig = workflowConfig;
1687
+ this.invokeCount = invokeCount;
1688
+ this.initHeaderValue = initHeaderValue;
1689
+ this.stepInfo = stepInfo;
1690
+ this.headers = {
1691
+ rawHeaders: {},
1692
+ workflowHeaders: {},
1693
+ failureHeaders: {}
1694
+ };
1695
+ }
1696
+ getHeaders() {
1697
+ this.addBaseHeaders();
1698
+ this.addRetries();
1699
+ this.addFlowControl();
1700
+ this.addUserHeaders();
1701
+ this.addInvokeCount();
1702
+ this.addFailureUrl();
1703
+ const contentType = this.addContentType();
1704
+ return this.prefixHeaders(contentType);
1705
+ }
1706
+ addBaseHeaders() {
1707
+ this.headers.rawHeaders = {
1708
+ ...this.headers.rawHeaders,
1709
+ [WORKFLOW_INIT_HEADER]: this.initHeaderValue,
1710
+ [WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
1711
+ [WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
1712
+ [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
1713
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1714
+ ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {},
1715
+ ...this.workflowConfig.telemetry && this.stepInfo?.lazyStep instanceof LazyCallStep && this.stepInfo.lazyStep.headers[AGENT_NAME_HEADER] ? { [TELEMETRY_HEADER_AGENT]: "true" } : {}
1716
+ };
1717
+ if (this.stepInfo?.lazyStep.stepType !== "Call") {
1718
+ this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1719
+ }
1720
+ }
1721
+ addInvokeCount() {
1722
+ if (this.invokeCount === void 0 || this.invokeCount === 0) {
1723
+ return;
1724
+ }
1725
+ const invokeCount = this.invokeCount.toString();
1726
+ this.headers.workflowHeaders[`Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount;
1727
+ if (this.workflowConfig.failureUrl) {
1728
+ this.headers.failureHeaders[`Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount;
1729
+ }
1730
+ if (this.stepInfo?.lazyStep instanceof LazyCallStep) {
1731
+ this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount;
1732
+ }
1733
+ }
1734
+ addRetries() {
1735
+ if (this.workflowConfig.retries === void 0 || this.workflowConfig.retries === DEFAULT_RETRIES) {
1736
+ return;
1737
+ }
1738
+ const retries = this.workflowConfig.retries.toString();
1739
+ this.headers.workflowHeaders["Retries"] = retries;
1740
+ if (this.workflowConfig.failureUrl) {
1741
+ this.headers.failureHeaders["Retries"] = retries;
1742
+ }
1743
+ }
1744
+ addFlowControl() {
1745
+ if (!this.workflowConfig.flowControl) {
1746
+ return;
1747
+ }
1748
+ const { flowControlKey, flowControlValue } = prepareFlowControl(
1749
+ this.workflowConfig.flowControl
1750
+ );
1751
+ this.headers.workflowHeaders["Flow-Control-Key"] = flowControlKey;
1752
+ this.headers.workflowHeaders["Flow-Control-Value"] = flowControlValue;
1753
+ if (this.workflowConfig.failureUrl) {
1754
+ this.headers.failureHeaders["Flow-Control-Key"] = flowControlKey;
1755
+ this.headers.failureHeaders["Flow-Control-Value"] = flowControlValue;
1756
+ }
1757
+ }
1758
+ addUserHeaders() {
1759
+ for (const [key, value] of this.userHeaders.entries()) {
1760
+ const forwardKey = `Forward-${key}`;
1761
+ this.headers.workflowHeaders[forwardKey] = value;
1762
+ if (this.workflowConfig.failureUrl) {
1763
+ this.headers.failureHeaders[forwardKey] = value;
1764
+ }
1765
+ }
1766
+ }
1767
+ addFailureUrl() {
1768
+ if (!this.workflowConfig.failureUrl) {
1769
+ return;
1770
+ }
1771
+ this.headers.workflowHeaders["Failure-Callback"] = this.workflowConfig.failureUrl;
1772
+ this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
1773
+ this.headers.failureHeaders[`Forward-Upstash-Workflow-Failure-Callback`] = "true";
1774
+ this.headers.failureHeaders["Workflow-Runid"] = this.workflowConfig.workflowRunId;
1775
+ this.headers.failureHeaders["Workflow-Init"] = "false";
1776
+ this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
1777
+ this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
1778
+ this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody";
1779
+ if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
1780
+ this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
1781
+ }
1782
+ }
1783
+ addContentType() {
1784
+ if (this.workflowConfig.useJSONContent) {
1785
+ this.headers.rawHeaders["content-type"] = "application/json";
1786
+ return "application/json";
1787
+ }
1788
+ const callHeaders = new Headers(
1789
+ this.stepInfo?.lazyStep instanceof LazyCallStep ? this.stepInfo.lazyStep.headers : {}
1790
+ );
1791
+ const contentType = (callHeaders.get("content-type") ? callHeaders.get("content-type") : this.userHeaders?.get("Content-Type") ? this.userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1792
+ this.headers.rawHeaders["content-type"] = contentType;
1793
+ return contentType;
1794
+ }
1795
+ prefixHeaders(contentType) {
1796
+ const { rawHeaders, workflowHeaders, failureHeaders } = this.headers;
1797
+ const isCall = this.stepInfo?.lazyStep.stepType === "Call";
1798
+ return {
1799
+ headers: {
1800
+ ...rawHeaders,
1801
+ ...addPrefixToHeaders(workflowHeaders, isCall ? "Upstash-Callback-" : "Upstash-"),
1802
+ ...addPrefixToHeaders(failureHeaders, "Upstash-Failure-Callback-"),
1803
+ ...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
1804
+ },
1805
+ contentType
1806
+ };
1807
+ }
1808
+ };
1809
+ function addPrefixToHeaders(headers, prefix) {
1810
+ const prefixedHeaders = {};
1811
+ for (const [key, value] of Object.entries(headers)) {
1812
+ prefixedHeaders[`${prefix}${key}`] = value;
1813
+ }
1814
+ return prefixedHeaders;
1815
+ }
1816
+ var prepareFlowControl = (flowControl) => {
1817
+ const parallelism = flowControl.parallelism?.toString();
1818
+ const rate = (flowControl.rate ?? flowControl.ratePerSecond)?.toString();
1819
+ const period = typeof flowControl.period === "number" ? `${flowControl.period}s` : flowControl.period;
1820
+ const controlValue = [
1821
+ parallelism ? `parallelism=${parallelism}` : void 0,
1822
+ rate ? `rate=${rate}` : void 0,
1823
+ period ? `period=${period}` : void 0
1824
+ ].filter(Boolean);
1825
+ if (controlValue.length === 0) {
1826
+ throw new QstashError4("Provide at least one of parallelism or ratePerSecond for flowControl");
1827
+ }
1828
+ return {
1829
+ flowControlKey: flowControl.key,
1830
+ flowControlValue: controlValue.join(", ")
1831
+ };
1832
+ };
1833
+ var getHeaders = (params) => {
1834
+ const workflowHeaders = new WorkflowHeaders(params);
1835
+ return workflowHeaders.getHeaders();
1836
+ };
1837
+
1838
+ // src/qstash/submit-steps.ts
1839
+ var submitParallelSteps = async ({
1840
+ context,
1841
+ steps,
1842
+ initialStepCount,
1843
+ invokeCount,
1844
+ telemetry,
1845
+ debug
1846
+ }) => {
1847
+ const planSteps = steps.map(
1848
+ (step, index) => step.getPlanStep(steps.length, initialStepCount + index)
1849
+ );
1850
+ await debug?.log("SUBMIT", "SUBMIT_STEP", {
1851
+ length: planSteps.length,
1852
+ steps: planSteps
1853
+ });
1854
+ const result = await context.qstashClient.batch(
1855
+ planSteps.map((planStep) => {
1856
+ const { headers } = getHeaders({
1857
+ initHeaderValue: "false",
1858
+ workflowConfig: {
1859
+ workflowRunId: context.workflowRunId,
1860
+ workflowUrl: context.url,
1861
+ failureUrl: context.failureUrl,
1862
+ retries: context.retries,
1863
+ flowControl: context.flowControl,
1864
+ telemetry
1865
+ },
1866
+ userHeaders: context.headers,
1867
+ invokeCount
1868
+ });
1869
+ return {
1870
+ headers,
1871
+ method: "POST",
1872
+ url: context.url,
1873
+ body: JSON.stringify(planStep),
1874
+ notBefore: planStep.sleepUntil,
1875
+ delay: planStep.sleepFor
1876
+ };
1877
+ })
1878
+ );
1879
+ await debug?.log("INFO", "SUBMIT_STEP", {
1880
+ messageIds: result.map((message) => {
1881
+ return {
1882
+ message: message.messageId
1883
+ };
1884
+ })
1885
+ });
1886
+ throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
1887
+ };
1888
+ var submitSingleStep = async ({
1889
+ context,
1890
+ lazyStep,
1891
+ stepId,
1892
+ invokeCount,
1893
+ concurrency,
1894
+ telemetry,
1895
+ debug
1896
+ }) => {
1897
+ const resultStep = await lazyStep.getResultStep(concurrency, stepId);
1898
+ await debug?.log("INFO", "RUN_SINGLE", {
1899
+ fromRequest: false,
1900
+ step: resultStep,
1901
+ stepCount: stepId
1902
+ });
1903
+ const { headers } = lazyStep.getHeaders({
1904
+ context,
1905
+ step: resultStep,
1906
+ invokeCount,
1907
+ telemetry
1908
+ });
1909
+ const body = lazyStep.getBody({
1910
+ context,
1911
+ step: resultStep,
1912
+ headers,
1913
+ invokeCount,
1914
+ telemetry
1915
+ });
1916
+ await debug?.log("SUBMIT", "SUBMIT_STEP", {
1917
+ length: 1,
1918
+ steps: [resultStep]
1919
+ });
1920
+ const submitResult = await lazyStep.submitStep({
1921
+ context,
1922
+ body,
1923
+ headers,
1924
+ isParallel: concurrency !== NO_CONCURRENCY,
1925
+ invokeCount,
1926
+ step: resultStep,
1927
+ telemetry
1928
+ });
1929
+ await debug?.log("INFO", "SUBMIT_STEP", {
1930
+ messageIds: submitResult.map((message) => {
1931
+ return {
1932
+ message: message.messageId
1933
+ };
1934
+ })
1935
+ });
1936
+ return resultStep;
1637
1937
  };
1638
1938
 
1639
1939
  // src/context/auto-executor.ts
1640
- import { QstashError as QstashError4 } from "@upstash/qstash";
1641
1940
  var AutoExecutor = class _AutoExecutor {
1642
1941
  context;
1643
1942
  promises = /* @__PURE__ */ new WeakMap();
@@ -1741,14 +2040,16 @@ var AutoExecutor = class _AutoExecutor {
1741
2040
  });
1742
2041
  return lazyStep.parseOut(step.out);
1743
2042
  }
1744
- const resultStep = await lazyStep.getResultStep(NO_CONCURRENCY, this.stepCount);
1745
- await this.debug?.log("INFO", "RUN_SINGLE", {
1746
- fromRequest: false,
1747
- step: resultStep,
1748
- stepCount: this.stepCount
2043
+ const resultStep = await submitSingleStep({
2044
+ context: this.context,
2045
+ lazyStep,
2046
+ stepId: this.stepCount,
2047
+ invokeCount: this.invokeCount,
2048
+ concurrency: 1,
2049
+ telemetry: this.telemetry,
2050
+ debug: this.debug
1749
2051
  });
1750
- await this.submitStepsToQStash([resultStep], [lazyStep]);
1751
- return resultStep.out;
2052
+ throw new WorkflowAbort(lazyStep.stepName, resultStep);
1752
2053
  }
1753
2054
  /**
1754
2055
  * Runs steps in parallel.
@@ -1776,10 +2077,14 @@ var AutoExecutor = class _AutoExecutor {
1776
2077
  });
1777
2078
  switch (parallelCallState) {
1778
2079
  case "first": {
1779
- const planSteps = parallelSteps.map(
1780
- (parallelStep, index) => parallelStep.getPlanStep(parallelSteps.length, initialStepCount + index)
1781
- );
1782
- await this.submitStepsToQStash(planSteps, parallelSteps);
2080
+ await submitParallelSteps({
2081
+ context: this.context,
2082
+ steps: parallelSteps,
2083
+ initialStepCount,
2084
+ invokeCount: this.invokeCount,
2085
+ telemetry: this.telemetry,
2086
+ debug: this.debug
2087
+ });
1783
2088
  break;
1784
2089
  }
1785
2090
  case "partial": {
@@ -1793,13 +2098,18 @@ var AutoExecutor = class _AutoExecutor {
1793
2098
  validateStep(parallelSteps[stepIndex], planStep);
1794
2099
  try {
1795
2100
  const parallelStep = parallelSteps[stepIndex];
1796
- const resultStep = await parallelStep.getResultStep(
1797
- parallelSteps.length,
1798
- planStep.targetStep
1799
- );
1800
- await this.submitStepsToQStash([resultStep], [parallelStep]);
2101
+ const resultStep = await submitSingleStep({
2102
+ context: this.context,
2103
+ lazyStep: parallelStep,
2104
+ stepId: planStep.targetStep,
2105
+ invokeCount: this.invokeCount,
2106
+ concurrency: parallelSteps.length,
2107
+ telemetry: this.telemetry,
2108
+ debug: this.debug
2109
+ });
2110
+ throw new WorkflowAbort(parallelStep.stepName, resultStep);
1801
2111
  } catch (error) {
1802
- if (error instanceof WorkflowAbort || error instanceof QstashError4 && error.status === 400) {
2112
+ if (error instanceof WorkflowAbort || error instanceof QstashError5 && error.status === 400) {
1803
2113
  throw error;
1804
2114
  }
1805
2115
  throw new WorkflowError(
@@ -1855,128 +2165,6 @@ var AutoExecutor = class _AutoExecutor {
1855
2165
  return "discard";
1856
2166
  }
1857
2167
  }
1858
- /**
1859
- * sends the steps to QStash as batch
1860
- *
1861
- * @param steps steps to send
1862
- */
1863
- async submitStepsToQStash(steps, lazySteps) {
1864
- if (steps.length === 0) {
1865
- throw new WorkflowError(
1866
- `Unable to submit steps to QStash. Provided list is empty. Current step: ${this.stepCount}`
1867
- );
1868
- }
1869
- await this.debug?.log("SUBMIT", "SUBMIT_STEP", {
1870
- length: steps.length,
1871
- steps
1872
- });
1873
- if (steps[0].waitEventId && steps.length === 1) {
1874
- const waitStep = steps[0];
1875
- const { headers, timeoutHeaders } = getHeaders({
1876
- initHeaderValue: "false",
1877
- workflowRunId: this.context.workflowRunId,
1878
- workflowUrl: this.context.url,
1879
- userHeaders: this.context.headers,
1880
- step: waitStep,
1881
- failureUrl: this.context.failureUrl,
1882
- retries: this.context.retries,
1883
- telemetry: this.telemetry,
1884
- invokeCount: this.invokeCount,
1885
- flowControl: this.context.flowControl
1886
- });
1887
- const waitBody = {
1888
- url: this.context.url,
1889
- timeout: waitStep.timeout,
1890
- timeoutBody: void 0,
1891
- timeoutUrl: this.context.url,
1892
- timeoutHeaders,
1893
- step: {
1894
- stepId: waitStep.stepId,
1895
- stepType: "Wait",
1896
- stepName: waitStep.stepName,
1897
- concurrent: waitStep.concurrent,
1898
- targetStep: waitStep.targetStep
1899
- }
1900
- };
1901
- await this.context.qstashClient.http.request({
1902
- path: ["v2", "wait", waitStep.waitEventId],
1903
- body: JSON.stringify(waitBody),
1904
- headers,
1905
- method: "POST",
1906
- parseResponseAsJson: false
1907
- });
1908
- throw new WorkflowAbort(waitStep.stepName, waitStep);
1909
- }
1910
- if (steps.length === 1 && lazySteps[0] instanceof LazyInvokeStep) {
1911
- const invokeStep = steps[0];
1912
- const lazyInvokeStep = lazySteps[0];
1913
- await invokeWorkflow({
1914
- settings: lazyInvokeStep.params,
1915
- invokeStep,
1916
- context: this.context,
1917
- invokeCount: this.invokeCount,
1918
- telemetry: this.telemetry
1919
- });
1920
- throw new WorkflowAbort(invokeStep.stepName, invokeStep);
1921
- }
1922
- const result = await this.context.qstashClient.batch(
1923
- steps.map((singleStep, index) => {
1924
- const lazyStep = lazySteps[index];
1925
- const { headers } = getHeaders({
1926
- initHeaderValue: "false",
1927
- workflowRunId: this.context.workflowRunId,
1928
- workflowUrl: this.context.url,
1929
- userHeaders: this.context.headers,
1930
- step: singleStep,
1931
- failureUrl: this.context.failureUrl,
1932
- retries: this.context.retries,
1933
- callRetries: lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0,
1934
- callTimeout: lazyStep instanceof LazyCallStep ? lazyStep.timeout : void 0,
1935
- telemetry: this.telemetry,
1936
- invokeCount: this.invokeCount,
1937
- flowControl: this.context.flowControl,
1938
- callFlowControl: lazyStep instanceof LazyCallStep ? lazyStep.flowControl : void 0
1939
- });
1940
- const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
1941
- singleStep.out = JSON.stringify(singleStep.out);
1942
- return singleStep.callUrl && lazyStep instanceof LazyCallStep ? (
1943
- // if the step is a third party call, we call the third party
1944
- // url (singleStep.callUrl) and pass information about the workflow
1945
- // in the headers (handled in getHeaders). QStash makes the request
1946
- // to callUrl and returns the result to Workflow endpoint.
1947
- // handleThirdPartyCallResult method sends the result of the third
1948
- // party call to QStash.
1949
- {
1950
- headers,
1951
- method: singleStep.callMethod,
1952
- body: JSON.stringify(singleStep.callBody),
1953
- url: singleStep.callUrl
1954
- }
1955
- ) : (
1956
- // if the step is not a third party call, we use workflow
1957
- // endpoint (context.url) as URL when calling QStash. QStash
1958
- // calls us back with the updated steps list.
1959
- {
1960
- headers,
1961
- method: "POST",
1962
- body: JSON.stringify(singleStep),
1963
- url: this.context.url,
1964
- notBefore: willWait ? singleStep.sleepUntil : void 0,
1965
- delay: willWait ? singleStep.sleepFor : void 0
1966
- }
1967
- );
1968
- })
1969
- );
1970
- const _result = result;
1971
- await this.debug?.log("INFO", "SUBMIT_STEP", {
1972
- messageIds: _result.map((message) => {
1973
- return {
1974
- message: message.messageId
1975
- };
1976
- })
1977
- });
1978
- throw new WorkflowAbort(steps[0].stepName, steps[0]);
1979
- }
1980
2168
  /**
1981
2169
  * Get the promise by executing the lazt steps list. If there is a single
1982
2170
  * step, we call `runSingle`. Otherwise `runParallel` is called.
@@ -2173,7 +2361,7 @@ var WorkflowApi = class extends BaseWorkflowApi {
2173
2361
  };
2174
2362
 
2175
2363
  // src/agents/index.ts
2176
- import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
2364
+ import { createOpenAI } from "@ai-sdk/openai";
2177
2365
 
2178
2366
  // src/agents/agent.ts
2179
2367
  import { z } from "zod";
@@ -2379,11 +2567,12 @@ var WorkflowAgents = class {
2379
2567
  */
2380
2568
  openai(...params) {
2381
2569
  const [model, settings] = params;
2382
- const { baseURL, apiKey, ...otherSettings } = settings ?? {};
2570
+ const { baseURL, apiKey, callSettings, ...otherSettings } = settings ?? {};
2383
2571
  const openaiModel = this.AISDKModel({
2384
2572
  context: this.context,
2385
- provider: createOpenAI2,
2386
- providerParams: { baseURL, apiKey, compatibility: "strict" }
2573
+ provider: createOpenAI,
2574
+ providerParams: { baseURL, apiKey, compatibility: "strict" },
2575
+ agentCallParams: callSettings
2387
2576
  });
2388
2577
  return openaiModel(model, otherSettings);
2389
2578
  }
@@ -2611,60 +2800,42 @@ var WorkflowContext = class {
2611
2800
  }
2612
2801
  await this.addStep(new LazySleepUntilStep(stepName, time));
2613
2802
  }
2614
- /**
2615
- * Makes a third party call through QStash in order to make a
2616
- * network call without consuming any runtime.
2617
- *
2618
- * ```ts
2619
- * const { status, body } = await context.call<string>(
2620
- * "post call step",
2621
- * {
2622
- * url: "https://www.some-endpoint.com/api",
2623
- * method: "POST",
2624
- * body: "my-payload"
2625
- * }
2626
- * );
2627
- * ```
2628
- *
2629
- * tries to parse the result of the request as JSON. If it's
2630
- * not a JSON which can be parsed, simply returns the response
2631
- * body as it is.
2632
- *
2633
- * @param stepName
2634
- * @param url url to call
2635
- * @param method call method. "GET" by default.
2636
- * @param body call body
2637
- * @param headers call headers
2638
- * @param retries number of call retries. 0 by default
2639
- * @param timeout max duration to wait for the endpoint to respond. in seconds.
2640
- * @returns call result as {
2641
- * status: number;
2642
- * body: unknown;
2643
- * header: Record<string, string[]>
2644
- * }
2645
- */
2646
2803
  async call(stepName, settings) {
2647
- const {
2648
- url,
2649
- method = "GET",
2650
- body: requestBody,
2651
- headers = {},
2652
- retries = 0,
2653
- timeout,
2654
- flowControl
2655
- } = settings;
2656
- return await this.addStep(
2657
- new LazyCallStep(
2804
+ let callStep;
2805
+ if ("workflow" in settings) {
2806
+ const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
2807
+ callStep = new LazyCallStep(
2808
+ stepName,
2809
+ url,
2810
+ "POST",
2811
+ settings.body,
2812
+ settings.headers || {},
2813
+ settings.retries || 0,
2814
+ settings.timeout,
2815
+ settings.flowControl ?? settings.workflow.options.flowControl
2816
+ );
2817
+ } else {
2818
+ const {
2819
+ url,
2820
+ method = "GET",
2821
+ body,
2822
+ headers = {},
2823
+ retries = 0,
2824
+ timeout,
2825
+ flowControl
2826
+ } = settings;
2827
+ callStep = new LazyCallStep(
2658
2828
  stepName,
2659
2829
  url,
2660
2830
  method,
2661
- requestBody,
2831
+ body,
2662
2832
  headers,
2663
2833
  retries,
2664
2834
  timeout,
2665
2835
  flowControl
2666
- )
2667
- );
2836
+ );
2837
+ }
2838
+ return await this.addStep(callStep);
2668
2839
  }
2669
2840
  /**
2670
2841
  * Pauses workflow execution until a specific event occurs or a timeout is reached.
@@ -3332,8 +3503,8 @@ export {
3332
3503
  getWorkflowRunId,
3333
3504
  StepTypes,
3334
3505
  triggerFirstInvocation,
3335
- serveManyBase,
3336
3506
  WorkflowTool,
3507
+ serveManyBase,
3337
3508
  WorkflowContext,
3338
3509
  WorkflowLogger,
3339
3510
  serveBase,