vellum-workflow-server 0.14.72.post2__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.
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/PKG-INFO +1 -1
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/pyproject.toml +1 -1
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/tests/test_workflow_view_stream_workflow_route.py +1 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/workflow_view.py +30 -15
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/code_exec_runner.py +2 -1
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/events.py +1 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/executor.py +54 -27
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/README.md +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/__init__.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/__init__.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/auth_middleware.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/healthz_view.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/tests/__init__.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/tests/test_input_display_mapping.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/api/tests/test_workflow_view.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/config.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/__init__.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/cancel_workflow.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/core/workflow_executor_context.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/server.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/start.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/__init__.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/exit_handler.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/log_proxy.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/oom_killer.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/sentry.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/system_utils.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/tests/__init__.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/tests/test_system_utils.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/tests/test_utils.py +0 -0
- {vellum_workflow_server-0.14.72.post2 → vellum_workflow_server-0.14.72.post4}/src/workflow_server/utils/utils.py +0 -0
|
@@ -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
|
|
@@ -252,7 +268,7 @@ def stream_workflow_route() -> Response:
|
|
|
252
268
|
f"process count: {get_active_process_count()}"
|
|
253
269
|
)
|
|
254
270
|
except GeneratorExit:
|
|
255
|
-
app.logger.error("Client disconnected in the middle of the
|
|
271
|
+
app.logger.error("Client disconnected in the middle of the Workflow Stream")
|
|
256
272
|
return
|
|
257
273
|
finally:
|
|
258
274
|
try:
|
|
@@ -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
|
|
|
@@ -470,5 +485,5 @@ def startup_error_generator(
|
|
|
470
485
|
|
|
471
486
|
logger.error("Workflow stream could not start from resource constraints")
|
|
472
487
|
except GeneratorExit:
|
|
473
|
-
app.logger.error("Client disconnected in the middle of the
|
|
488
|
+
app.logger.error("Client disconnected in the middle of the Startup Error Stream")
|
|
474
489
|
return
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|