vellum-workflow-server 0.14.72.post3__tar.gz → 0.14.72.post4__tar.gz

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.

Potentially problematic release.


This version of vellum-workflow-server might be problematic. Click here for more details.

Files changed (31) hide show
  1. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/PKG-INFO +1 -1
  2. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/pyproject.toml +1 -1
  3. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/tests/test_workflow_view_stream_workflow_route.py +1 -0
  4. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/workflow_view.py +28 -13
  5. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/code_exec_runner.py +2 -1
  6. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/events.py +1 -0
  7. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/executor.py +54 -27
  8. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/README.md +0 -0
  9. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/__init__.py +0 -0
  10. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/__init__.py +0 -0
  11. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/auth_middleware.py +0 -0
  12. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/healthz_view.py +0 -0
  13. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/tests/__init__.py +0 -0
  14. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/tests/test_input_display_mapping.py +0 -0
  15. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/tests/test_workflow_view.py +0 -0
  16. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/config.py +0 -0
  17. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/__init__.py +0 -0
  18. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/cancel_workflow.py +0 -0
  19. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/workflow_executor_context.py +0 -0
  20. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/server.py +0 -0
  21. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/start.py +0 -0
  22. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/__init__.py +0 -0
  23. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/exit_handler.py +0 -0
  24. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/log_proxy.py +0 -0
  25. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/oom_killer.py +0 -0
  26. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/sentry.py +0 -0
  27. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/system_utils.py +0 -0
  28. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/tests/__init__.py +0 -0
  29. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/tests/test_system_utils.py +0 -0
  30. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/tests/test_utils.py +0 -0
  31. {vellum_workflow_server-0.14.72.post3 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-workflow-server
3
- Version: 0.14.72.post3
3
+ Version: 0.14.72.post4
4
4
  Summary:
5
5
  License: AGPL
6
6
  Requires-Python: >=3.9.0,<4
@@ -3,7 +3,7 @@ name = "vellum-workflow-server"
3
3
 
4
4
  [tool.poetry]
5
5
  name = "vellum-workflow-server"
6
- version = "0.14.72.post3"
6
+ version = "0.14.72.post4"
7
7
  description = ""
8
8
  readme = "README.md"
9
9
  authors = []
@@ -83,6 +83,7 @@ class Workflow(BaseWorkflow):
83
83
  # AND the version headers are present
84
84
  assert "X-Vellum-SDK-Version" in response.headers
85
85
  assert "X-Vellum-Server-Version" in response.headers
86
+ assert "X-Vellum-Workflow-Span-Id" in response.headers
86
87
 
87
88
 
88
89
  def test_stream_workflow_route__happy_path(both_stream_types):
@@ -23,6 +23,7 @@ from vellum.workflows.nodes import BaseNode
23
23
  from vellum.workflows.utils.names import pascal_to_title_case
24
24
  from workflow_server.config import MEMORY_LIMIT_MB
25
25
  from workflow_server.core.events import (
26
+ SPAN_ID_EVENT,
26
27
  STREAM_FINISHED_EVENT,
27
28
  VEMBDA_EXECUTION_FULFILLED_EVENT_NAME,
28
29
  VembdaExecutionFulfilledBody,
@@ -84,7 +85,12 @@ def stream_workflow_route() -> Response:
84
85
  parent=None,
85
86
  )
86
87
 
87
- process_output_queue: Queue[dict] = Queue()
88
+ process_output_queue: Queue[Union[str, dict]] = Queue()
89
+
90
+ headers = {
91
+ "X-Vellum-SDK-Version": vembda_initiated_event.body.sdk_version,
92
+ "X-Vellum-Server-Version": vembda_initiated_event.body.server_version,
93
+ }
88
94
 
89
95
  # We can exceed the concurrency count currently with long running workflows due to a knative issue. So here
90
96
  # if we detect a memory problem just exit us early
@@ -100,10 +106,7 @@ def stream_workflow_route() -> Response:
100
106
  ),
101
107
  status=200,
102
108
  content_type="application/x-ndjson",
103
- headers={
104
- "X-Vellum-SDK-Version": vembda_initiated_event.body.sdk_version,
105
- "X-Vellum-Server-Version": vembda_initiated_event.body.server_version,
106
- },
109
+ headers=headers,
107
110
  )
108
111
 
109
112
  try:
@@ -130,6 +133,19 @@ def stream_workflow_route() -> Response:
130
133
  )
131
134
  process_output_queue.put(vembda_fulfilled_event.model_dump(mode="json"))
132
135
 
136
+ first_item = process_output_queue.get(timeout=0.1)
137
+ if isinstance(first_item, str) and first_item.startswith(SPAN_ID_EVENT):
138
+ span_id = first_item.split(":")[1]
139
+ headers["X-Vellum-Workflow-Span-Id"] = span_id
140
+ else:
141
+ logger.error("Workflow stream did not start with span id event")
142
+ return Response(
143
+ json.dumps({"detail": "Internal Server Error"}),
144
+ status=500,
145
+ content_type="application/json",
146
+ headers=headers,
147
+ )
148
+
133
149
  def process_events(queue: Queue) -> Iterator[Union[str, dict]]:
134
150
  event: Union[str, dict]
135
151
  loops = 0
@@ -267,10 +283,7 @@ def stream_workflow_route() -> Response:
267
283
  stream_with_context(generator()),
268
284
  status=200,
269
285
  content_type="application/x-ndjson",
270
- headers={
271
- "X-Vellum-SDK-Version": vembda_initiated_event.body.sdk_version,
272
- "X-Vellum-Server-Version": vembda_initiated_event.body.server_version,
273
- },
286
+ headers=headers,
274
287
  )
275
288
  return resp
276
289
 
@@ -347,14 +360,16 @@ def stream_node_route() -> Response:
347
360
  yield "\n"
348
361
  yield json.dumps(row)
349
362
 
363
+ headers = {
364
+ "X-Vellum-SDK-Version": vembda_initiated_event.body.sdk_version,
365
+ "X-Vellum-Server-Version": vembda_initiated_event.body.server_version,
366
+ }
367
+
350
368
  resp = Response(
351
369
  stream_with_context(generator()),
352
370
  status=200,
353
371
  content_type="application/x-ndjson",
354
- headers={
355
- "X-Vellum-SDK-Version": vembda_initiated_event.body.sdk_version,
356
- "X-Vellum-Server-Version": vembda_initiated_event.body.server_version,
357
- },
372
+ headers=headers,
358
373
  )
359
374
  return resp
360
375
 
@@ -53,7 +53,8 @@ def run_code_exec_stream() -> None:
53
53
  print(f"{_EVENT_LINE}{initiated_event}") # noqa: T201
54
54
 
55
55
  try:
56
- for line in stream_workflow(context, disable_redirect=True):
56
+ stream_iterator, span_id = stream_workflow(context, disable_redirect=True)
57
+ for line in stream_iterator:
57
58
  print(f"{_EVENT_LINE}{json.dumps(line)}") # noqa: T201
58
59
  except WorkflowInitializationException as e:
59
60
  fulfilled_event = VembdaExecutionFulfilledEvent(
@@ -8,6 +8,7 @@ from vellum.workflows.nodes import BaseNode
8
8
  VEMBDA_EXECUTION_INITIATED_EVENT_NAME = "vembda.execution.initiated"
9
9
  VEMBDA_EXECUTION_FULFILLED_EVENT_NAME = "vembda.execution.fulfilled"
10
10
  STREAM_FINISHED_EVENT = "STREAM_FINISHED"
11
+ SPAN_ID_EVENT = "SPAN_ID_EVENT"
11
12
 
12
13
 
13
14
  class VembdaExecutionFulfilledBody(UniversalBaseModel):
@@ -11,7 +11,7 @@ import sys
11
11
  from threading import Event as ThreadingEvent
12
12
  import time
13
13
  from traceback import format_exc
14
- from uuid import uuid4
14
+ from uuid import UUID, uuid4
15
15
  from typing import Any, Callable, Generator, Iterator, Optional, Tuple, Type
16
16
 
17
17
  from pebble import concurrent
@@ -35,6 +35,7 @@ from vellum.workflows.workflows.event_filters import all_workflow_event_filter
35
35
  from workflow_server.config import IS_VPC, VELLUM_API_URL_HOST, VELLUM_API_URL_PORT
36
36
  from workflow_server.core.cancel_workflow import CancelWorkflowWatcherThread
37
37
  from workflow_server.core.events import (
38
+ SPAN_ID_EVENT,
38
39
  STREAM_FINISHED_EVENT,
39
40
  VembdaExecutionFulfilledBody,
40
41
  VembdaExecutionFulfilledEvent,
@@ -86,11 +87,20 @@ def _stream_node_wrapper(executor_context: NodeExecutorContext, queue: Queue) ->
86
87
 
87
88
 
88
89
  def _stream_workflow_wrapper(executor_context: WorkflowExecutorContext, queue: Queue) -> None:
90
+ span_id_emitted = False
89
91
  try:
90
- for event in stream_workflow(executor_context=executor_context):
92
+ stream_iterator, span_id = stream_workflow(executor_context=executor_context)
93
+
94
+ queue.put(f"{SPAN_ID_EVENT}:{span_id}")
95
+ span_id_emitted = True
96
+
97
+ for event in stream_iterator:
91
98
  queue.put(json.dumps(event))
92
99
 
93
100
  except WorkflowInitializationException as e:
101
+ if not span_id_emitted:
102
+ queue.put(f"{SPAN_ID_EVENT}:{uuid4()}")
103
+
94
104
  queue.put(
95
105
  VembdaExecutionFulfilledEvent(
96
106
  id=uuid4(),
@@ -106,6 +116,9 @@ def _stream_workflow_wrapper(executor_context: WorkflowExecutorContext, queue: Q
106
116
  ).model_dump(mode="json")
107
117
  )
108
118
  except Exception as e:
119
+ if not span_id_emitted:
120
+ queue.put(f"{SPAN_ID_EVENT}:{uuid4()}")
121
+
109
122
  sentry_sdk.set_tag("vellum_trace_id", str(executor_context.trace_id))
110
123
  logger.exception(e)
111
124
  queue.put(
@@ -161,7 +174,7 @@ def stream_workflow_process_timeout(
161
174
  def stream_workflow(
162
175
  executor_context: WorkflowExecutorContext,
163
176
  disable_redirect: bool = True,
164
- ) -> Iterator[dict]:
177
+ ) -> tuple[Iterator[dict], UUID]:
165
178
  workflow, namespace = _gather_workflow(executor_context)
166
179
  workflow_inputs = _get_workflow_inputs(executor_context)
167
180
  display_context = _gather_display_context(workflow, namespace)
@@ -179,30 +192,38 @@ def stream_workflow(
179
192
  workflow.__class__,
180
193
  )
181
194
 
182
- def call_workflow() -> Generator[dict[str, Any], Any, None]:
183
- cancel_watcher_kill_switch = ThreadingEvent()
184
- cancel_signal = ThreadingEvent()
185
- cancel_watcher = CancelWorkflowWatcherThread(
186
- kill_switch=cancel_watcher_kill_switch,
187
- execution_id=executor_context.execution_id,
188
- timeout_seconds=executor_context.timeout,
189
- vembda_public_url=executor_context.vembda_public_url,
195
+ cancel_watcher_kill_switch = ThreadingEvent()
196
+ cancel_signal = ThreadingEvent()
197
+ cancel_watcher = CancelWorkflowWatcherThread(
198
+ kill_switch=cancel_watcher_kill_switch,
199
+ execution_id=executor_context.execution_id,
200
+ timeout_seconds=executor_context.timeout,
201
+ vembda_public_url=executor_context.vembda_public_url,
202
+ cancel_signal=cancel_signal,
203
+ )
204
+
205
+ try:
206
+ if executor_context.vembda_public_url:
207
+ cancel_watcher.start()
208
+ except Exception:
209
+ logger.exception("Failed to start cancel watcher")
210
+
211
+ try:
212
+ stream = workflow.stream(
213
+ inputs=workflow_inputs,
214
+ state=workflow_state,
215
+ node_output_mocks=node_output_mocks,
216
+ event_filter=all_workflow_event_filter,
190
217
  cancel_signal=cancel_signal,
218
+ entrypoint_nodes=[run_from_node] if run_from_node else None,
191
219
  )
220
+ except Exception:
221
+ cancel_watcher_kill_switch.set()
222
+ logger.exception("Failed to generate Workflow Stream")
223
+ raise
192
224
 
225
+ def call_workflow() -> Generator[dict[str, Any], Any, None]:
193
226
  try:
194
- if executor_context.vembda_public_url:
195
- cancel_watcher.start()
196
-
197
- stream = workflow.stream(
198
- inputs=workflow_inputs,
199
- state=workflow_state,
200
- node_output_mocks=node_output_mocks,
201
- event_filter=all_workflow_event_filter,
202
- cancel_signal=cancel_signal,
203
- entrypoint_nodes=[run_from_node] if run_from_node else None,
204
- )
205
-
206
227
  first = True
207
228
  for event in stream:
208
229
  if first:
@@ -222,13 +243,19 @@ def stream_workflow(
222
243
  event=event,
223
244
  executor_context=executor_context,
224
245
  )
246
+ except Exception as e:
247
+ logger.exception("Failed to generate event from Workflow Stream")
248
+ raise e
225
249
  finally:
226
250
  cancel_watcher_kill_switch.set()
227
251
 
228
- return _call_stream(
229
- executor_context=executor_context,
230
- stream_generator=call_workflow,
231
- disable_redirect=disable_redirect,
252
+ return (
253
+ _call_stream(
254
+ executor_context=executor_context,
255
+ stream_generator=call_workflow,
256
+ disable_redirect=disable_redirect,
257
+ ),
258
+ stream.span_id,
232
259
  )
233
260
 
234
261