vellum-workflow-server 0.14.69__py3-none-any.whl → 0.14.70__py3-none-any.whl

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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-workflow-server
3
- Version: 0.14.69
3
+ Version: 0.14.70
4
4
  Summary:
5
5
  License: AGPL
6
6
  Requires-Python: >=3.9.0,<4
@@ -28,7 +28,7 @@ Requires-Dist: pebble (==5.0.7)
28
28
  Requires-Dist: pyjwt (==2.10.0)
29
29
  Requires-Dist: python-dotenv (==1.0.1)
30
30
  Requires-Dist: sentry-sdk[flask] (==2.20.0)
31
- Requires-Dist: vellum-ai (==0.14.69)
31
+ Requires-Dist: vellum-ai (==0.14.70)
32
32
  Description-Content-Type: text/markdown
33
33
 
34
34
  # Vellum Workflow Runner Server
@@ -3,6 +3,7 @@ workflow_server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
3
3
  workflow_server/api/auth_middleware.py,sha256=IlZaCiwZ5nwQqk5sYQorvOFj7lt0p1ZSSEqUxfiFaW0,2458
4
4
  workflow_server/api/healthz_view.py,sha256=itiRvBDBXncrw8Kbbc73UZLwqMAhgHOR3uSre_dAfgY,404
5
5
  workflow_server/api/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ workflow_server/api/tests/test_input_display_mapping.py,sha256=drBZqMudFyB5wgiUOcMgRXz7E7ge-Qgxbstw4E4f0zE,2211
6
7
  workflow_server/api/tests/test_workflow_view.py,sha256=wlVFBmKcoI-RdzfGPioeW46k6zaXyUeIerPc6m4aQls,7150
7
8
  workflow_server/api/tests/test_workflow_view_stream_workflow_route.py,sha256=wWW4sukIUmHnL525MLSAB2E5P6CeASnhsCoKQXEgPcI,21297
8
9
  workflow_server/api/workflow_view.py,sha256=duiMnAZ7PRpoPz63s9z37pxUxGR-9yAi3qxG9APXCao,14244
@@ -21,9 +22,9 @@ workflow_server/utils/log_proxy.py,sha256=nugi6fOgAYKX2X9DIc39TG366rsmmDUPoEtG3g
21
22
  workflow_server/utils/oom_killer.py,sha256=8WB0nQWjmnjW9QzvNNwfYoBFB3yDHM3_OmnryeC8G3A,3657
22
23
  workflow_server/utils/sentry.py,sha256=Pr3xKvHdk0XFSpXgy-55bWI4J3bbf_36gjDyLOs7oVU,855
23
24
  workflow_server/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- workflow_server/utils/tests/test_utils.py,sha256=MKGY4F_jnRdwqyQ8Krhlk7MpMEPql5MDATcwBaxpIEA,4786
25
- workflow_server/utils/utils.py,sha256=dpFeUQW7cmiH2-aLsO65jAa4RubfvptWNkW1ZkvDlmE,3710
26
- vellum_workflow_server-0.14.69.dist-info/METADATA,sha256=Mw-2DCN-YSs3w4bny4SLReAts1d2VzuuQRJHfx6acyU,2237
27
- vellum_workflow_server-0.14.69.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
28
- vellum_workflow_server-0.14.69.dist-info/entry_points.txt,sha256=uB_0yPkr7YV6RhEXzvFReUM8P4OQBlVXD6TN6eb9-oc,277
29
- vellum_workflow_server-0.14.69.dist-info/RECORD,,
25
+ workflow_server/utils/tests/test_utils.py,sha256=qwK5Rmy3RQyjtlUrYAuGuDlBeRzZKsf1yS-y2IpUizQ,6452
26
+ workflow_server/utils/utils.py,sha256=Wqqn-1l2ugkGgy5paWWdt0AVxAyPMQCYcnRSSOMjXlA,4355
27
+ vellum_workflow_server-0.14.70.dist-info/METADATA,sha256=8jVFfOujZb2NwLgEDdy5IgpJKF3kyl7ics_KTn3D1qI,2237
28
+ vellum_workflow_server-0.14.70.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
29
+ vellum_workflow_server-0.14.70.dist-info/entry_points.txt,sha256=uB_0yPkr7YV6RhEXzvFReUM8P4OQBlVXD6TN6eb9-oc,277
30
+ vellum_workflow_server-0.14.70.dist-info/RECORD,,
@@ -0,0 +1,61 @@
1
+ from uuid import uuid4
2
+
3
+ from workflow_server.server import create_app
4
+
5
+
6
+ def test_input_conversion_with_display_mapping():
7
+ """
8
+ Test that validates input conversion behavior for future WorkflowDisplay refactor.
9
+
10
+ This test demonstrates the expected behavior when inputs are converted from UI names
11
+ to SDK attribute names using WorkflowDisplay's inputs_display mapping, rather than
12
+ the current snake_case conversion approach.
13
+
14
+ The test passes on main branch by using the current conversion logic, but establishes
15
+ the expected input/output patterns for the future refactor.
16
+ """
17
+ span_id = uuid4()
18
+ request_body = {
19
+ "execution_id": str(span_id),
20
+ "inputs": [
21
+ {"name": "User Message", "type": "STRING", "value": "Hello world"},
22
+ {"name": "123-config", "type": "STRING", "value": "config-value"},
23
+ {"name": "API-Key", "type": "STRING", "value": "test-key"},
24
+ ],
25
+ "environment_api_key": "test",
26
+ "module": "workflow",
27
+ "timeout": 360,
28
+ "files": {
29
+ "__init__.py": "",
30
+ "workflow.py": """
31
+ from vellum.workflows import BaseWorkflow
32
+ from vellum.workflows.state import BaseState
33
+ from .inputs import Inputs
34
+
35
+ class Workflow(BaseWorkflow[Inputs, BaseState]):
36
+ class Outputs(BaseWorkflow.Outputs):
37
+ result = "success"
38
+ """,
39
+ "inputs.py": """
40
+ from vellum.workflows.inputs import BaseInputs
41
+
42
+ class Inputs(BaseInputs):
43
+ user_message: str
44
+ input_123_config: str
45
+ api_key: str
46
+ """,
47
+ },
48
+ }
49
+
50
+ flask_app = create_app()
51
+ with flask_app.test_client() as test_client:
52
+ response = test_client.post("/workflow/stream", json=request_body)
53
+ status_code = response.status_code
54
+
55
+ assert status_code == 200, f"Request failed with status {status_code}: {response.data}"
56
+
57
+ response_lines = [line for line in response.data.decode().split("\n") if line and line not in ["WAITING", "END"]]
58
+ assert len(response_lines) > 0, "No response events received"
59
+
60
+ fulfilled_events = [line for line in response_lines if "workflow.execution.fulfilled" in line]
61
+ assert len(fulfilled_events) > 0, "No workflow.execution.fulfilled event received"
@@ -10,7 +10,11 @@ from vellum import (
10
10
  VellumError,
11
11
  VellumImage,
12
12
  )
13
- from workflow_server.utils.utils import convert_json_inputs_to_vellum
13
+ from workflow_server.utils.utils import (
14
+ convert_json_inputs_to_vellum,
15
+ to_python_safe_snake_case,
16
+ to_valid_python_identifier,
17
+ )
14
18
 
15
19
 
16
20
  @pytest.mark.parametrize(
@@ -134,10 +138,57 @@ def test_input_variables_with_uppercase_gets_sanitized():
134
138
  ]
135
139
 
136
140
  expected = {
137
- "foo": "<example-string-value>",
141
+ "Foo": "<example-string-value>",
138
142
  "foo_var": "<another-example-string-value>",
139
143
  }
140
144
 
141
145
  actual = convert_json_inputs_to_vellum(inputs)
142
146
 
143
147
  assert expected == actual
148
+
149
+
150
+ @pytest.mark.parametrize(
151
+ ["input_string", "safety_prefix", "expected"],
152
+ [
153
+ ("Foo", "input", "Foo"),
154
+ ("test", "input", "test"),
155
+ ("myVariable", "input", "myVariable"),
156
+ ("validName123", "input", "validName123"),
157
+ ("Foo-Var", "input", "foo_var"),
158
+ ("My Variable", "input", "my_variable"),
159
+ ("test-case", "input", "test_case"),
160
+ ("CamelCase", "input", "CamelCase"),
161
+ ("123", "input", "input_123"),
162
+ ("_a", "input", "input_a"),
163
+ ("_123", "input", "input_123"),
164
+ ("test@#$", "input", "test"),
165
+ ("@#$test", "input", "test"),
166
+ ("123", "_", "_123"),
167
+ ("123", "var", "var_123"),
168
+ ("123", "var_", "var_123"),
169
+ ],
170
+ )
171
+ def test_to_valid_python_identifier(input_string, safety_prefix, expected):
172
+ actual = to_valid_python_identifier(input_string, safety_prefix)
173
+ assert expected == actual
174
+
175
+
176
+ @pytest.mark.parametrize(
177
+ ["input_string", "safety_prefix", "expected"],
178
+ [
179
+ ("Foo", "input", "foo"),
180
+ ("Foo-Var", "input", "foo_var"),
181
+ ("CamelCase", "input", "camel_case"),
182
+ ("test", "input", "test"),
183
+ ("My Variable", "input", "my_variable"),
184
+ ("123", "input", "input_123"),
185
+ ("_a", "input", "input_a"),
186
+ ("_123", "input", "input_123"),
187
+ ("123", "_", "_123"),
188
+ ("123", "var", "var_123"),
189
+ ("123", "var_", "var_123"),
190
+ ],
191
+ )
192
+ def test_to_python_safe_snake_case(input_string, safety_prefix, expected):
193
+ actual = to_python_safe_snake_case(input_string, safety_prefix)
194
+ assert expected == actual
@@ -22,7 +22,7 @@ def convert_json_inputs_to_vellum(inputs: List[dict]) -> dict:
22
22
  for input in inputs:
23
23
  value = input["value"]
24
24
  # sync with vellum-python-sdks/ee/codegen/src/context/input-variable-context.ts
25
- name = to_python_safe_snake_case(input["name"], "input")
25
+ name = to_valid_python_identifier(input["name"], "input")
26
26
  type = input["type"]
27
27
 
28
28
  if type == "CHAT_HISTORY":
@@ -58,7 +58,7 @@ def to_python_safe_snake_case(string: str, safety_prefix: str = "_") -> str:
58
58
  # Strip special characters from start of string
59
59
  cleaned_str = re.sub(r"^[^a-zA-Z0-9_]+", "", string)
60
60
 
61
- # Check if cleaned string starts with a number or underscore
61
+ # Check if cleaned string starts with a number or an underscore
62
62
  starts_with_unsafe = bool(re.match(r"^[\d_]", cleaned_str))
63
63
 
64
64
  # Convert to snake case
@@ -74,6 +74,22 @@ def to_python_safe_snake_case(string: str, safety_prefix: str = "_") -> str:
74
74
  return f"{cleaned_safety_prefix}{snake_case}" if starts_with_unsafe else snake_case
75
75
 
76
76
 
77
+ def to_valid_python_identifier(string: str, safety_prefix: str = "_") -> str:
78
+ # Strip special characters from start of string
79
+ cleaned_str = re.sub(r"^[^a-zA-Z0-9_]+", "", string)
80
+
81
+ # Check if cleaned string starts with a number or an underscore
82
+ starts_with_unsafe = bool(re.match(r"^[\d_]", cleaned_str))
83
+
84
+ # Check if the string is already a valid Python identifier (preserve case)
85
+ is_valid_python_identifier = bool(re.match(r"^[a-zA-Z][a-zA-Z0-9]*$", cleaned_str))
86
+
87
+ if is_valid_python_identifier and not starts_with_unsafe:
88
+ return cleaned_str
89
+
90
+ return to_python_safe_snake_case(string, safety_prefix)
91
+
92
+
77
93
  def get_obj_size(obj: Any) -> int:
78
94
  marked = {id(obj)}
79
95
  obj_q = [obj]