vellum-ai 0.14.45__py3-none-any.whl → 0.14.47__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.
Files changed (162) hide show
  1. vellum/client/README.md +2 -2
  2. vellum/client/__init__.py +72 -6
  3. vellum/client/core/client_wrapper.py +1 -1
  4. vellum/client/core/file.py +13 -8
  5. vellum/client/core/http_client.py +26 -14
  6. vellum/client/core/pydantic_utilities.py +2 -2
  7. vellum/client/core/request_options.py +3 -0
  8. vellum/client/resources/ad_hoc/client.py +14 -2
  9. vellum/client/resources/container_images/client.py +6 -0
  10. vellum/client/resources/deployments/client.py +12 -0
  11. vellum/client/resources/document_indexes/client.py +18 -0
  12. vellum/client/resources/documents/client.py +6 -0
  13. vellum/client/resources/folder_entities/client.py +6 -0
  14. vellum/client/resources/metric_definitions/client.py +6 -0
  15. vellum/client/resources/prompts/client.py +6 -0
  16. vellum/client/resources/sandboxes/client.py +12 -0
  17. vellum/client/resources/test_suite_runs/client.py +6 -0
  18. vellum/client/resources/test_suites/client.py +2 -2
  19. vellum/client/resources/workflow_deployments/client.py +6 -0
  20. vellum/client/resources/workflow_sandboxes/client.py +6 -0
  21. vellum/client/resources/workflows/client.py +6 -4
  22. vellum/client/resources/workspace_secrets/client.py +6 -0
  23. vellum/client/types/api_request_parent_context.py +0 -6
  24. vellum/client/types/array_input.py +0 -5
  25. vellum/client/types/code_execution_node_array_result.py +0 -5
  26. vellum/client/types/code_execution_node_result.py +0 -5
  27. vellum/client/types/code_execution_node_result_data.py +0 -5
  28. vellum/client/types/code_executor_response.py +0 -5
  29. vellum/client/types/create_test_suite_test_case_request.py +0 -5
  30. vellum/client/types/deployment_history_item.py +0 -5
  31. vellum/client/types/deployment_read.py +0 -5
  32. vellum/client/types/execute_workflow_response.py +0 -5
  33. vellum/client/types/execution_array_vellum_value.py +0 -5
  34. vellum/client/types/external_test_case_execution.py +0 -5
  35. vellum/client/types/external_test_case_execution_request.py +0 -5
  36. vellum/client/types/fulfilled_execute_workflow_workflow_result_event.py +0 -7
  37. vellum/client/types/fulfilled_workflow_node_result_event.py +0 -5
  38. vellum/client/types/initiated_workflow_node_result_event.py +0 -5
  39. vellum/client/types/metadata_filter_config_request.py +0 -5
  40. vellum/client/types/metric_definition_execution.py +0 -5
  41. vellum/client/types/metric_definition_history_item.py +0 -5
  42. vellum/client/types/named_test_case_array_variable_value.py +0 -5
  43. vellum/client/types/named_test_case_array_variable_value_request.py +0 -7
  44. vellum/client/types/node_execution_fulfilled_event.py +0 -11
  45. vellum/client/types/node_execution_initiated_event.py +0 -11
  46. vellum/client/types/node_execution_paused_event.py +0 -11
  47. vellum/client/types/node_execution_rejected_event.py +0 -11
  48. vellum/client/types/node_execution_resumed_event.py +0 -11
  49. vellum/client/types/node_execution_span.py +0 -11
  50. vellum/client/types/node_execution_streaming_event.py +0 -11
  51. vellum/client/types/node_input_compiled_array_value.py +0 -5
  52. vellum/client/types/node_output_compiled_array_value.py +0 -5
  53. vellum/client/types/node_parent_context.py +0 -6
  54. vellum/client/types/paginated_slim_deployment_read_list.py +0 -5
  55. vellum/client/types/paginated_slim_workflow_deployment_list.py +0 -5
  56. vellum/client/types/paginated_test_suite_run_execution_list.py +0 -5
  57. vellum/client/types/paginated_test_suite_test_case_list.py +0 -5
  58. vellum/client/types/prompt_deployment_parent_context.py +0 -6
  59. vellum/client/types/prompt_exec_config.py +0 -6
  60. vellum/client/types/rejected_workflow_node_result_event.py +0 -5
  61. vellum/client/types/replace_test_suite_test_case_request.py +0 -5
  62. vellum/client/types/search_filters_request.py +0 -7
  63. vellum/client/types/search_request_options_request.py +0 -7
  64. vellum/client/types/slim_deployment_read.py +0 -5
  65. vellum/client/types/slim_workflow_deployment.py +0 -5
  66. vellum/client/types/slim_workflow_execution_read.py +0 -12
  67. vellum/client/types/span_link.py +0 -6
  68. vellum/client/types/streaming_workflow_node_result_event.py +0 -5
  69. vellum/client/types/templating_node_array_result.py +0 -5
  70. vellum/client/types/templating_node_result.py +0 -5
  71. vellum/client/types/templating_node_result_data.py +0 -5
  72. vellum/client/types/terminal_node_array_result.py +0 -5
  73. vellum/client/types/terminal_node_result.py +0 -5
  74. vellum/client/types/terminal_node_result_data.py +0 -5
  75. vellum/client/types/test_case_array_variable_value.py +0 -5
  76. vellum/client/types/test_suite_run_execution.py +0 -5
  77. vellum/client/types/test_suite_run_execution_array_output.py +0 -5
  78. vellum/client/types/test_suite_run_execution_metric_result.py +0 -5
  79. vellum/client/types/test_suite_run_external_exec_config.py +0 -5
  80. vellum/client/types/test_suite_run_external_exec_config_data.py +0 -5
  81. vellum/client/types/test_suite_run_external_exec_config_data_request.py +0 -7
  82. vellum/client/types/test_suite_run_external_exec_config_request.py +0 -7
  83. vellum/client/types/test_suite_run_metric_array_output.py +0 -5
  84. vellum/client/types/test_suite_run_read.py +0 -5
  85. vellum/client/types/test_suite_test_case.py +0 -5
  86. vellum/client/types/test_suite_test_case_create_bulk_operation_request.py +0 -7
  87. vellum/client/types/test_suite_test_case_replace_bulk_operation_request.py +0 -7
  88. vellum/client/types/test_suite_test_case_upsert_bulk_operation_request.py +0 -7
  89. vellum/client/types/upsert_test_suite_test_case_request.py +0 -5
  90. vellum/client/types/vellum_value_logical_condition_group_request.py +0 -3
  91. vellum/client/types/vellum_value_logical_condition_request.py +0 -5
  92. vellum/client/types/vellum_variable.py +0 -5
  93. vellum/client/types/workflow_deployment_event_executions_response.py +0 -26
  94. vellum/client/types/workflow_deployment_history_item.py +0 -5
  95. vellum/client/types/workflow_deployment_parent_context.py +0 -6
  96. vellum/client/types/workflow_deployment_read.py +0 -5
  97. vellum/client/types/workflow_deployment_release.py +0 -5
  98. vellum/client/types/workflow_deployment_release_workflow_version.py +0 -5
  99. vellum/client/types/workflow_event_execution_read.py +0 -12
  100. vellum/client/types/workflow_execution_actual.py +0 -5
  101. vellum/client/types/workflow_execution_fulfilled_event.py +0 -11
  102. vellum/client/types/workflow_execution_initiated_event.py +0 -11
  103. vellum/client/types/workflow_execution_node_result_event.py +0 -5
  104. vellum/client/types/workflow_execution_paused_event.py +0 -11
  105. vellum/client/types/workflow_execution_rejected_event.py +0 -11
  106. vellum/client/types/workflow_execution_resumed_event.py +0 -11
  107. vellum/client/types/workflow_execution_snapshotted_event.py +0 -13
  108. vellum/client/types/workflow_execution_span.py +0 -11
  109. vellum/client/types/workflow_execution_streaming_event.py +0 -11
  110. vellum/client/types/workflow_execution_view_online_eval_metric_result.py +0 -7
  111. vellum/client/types/workflow_execution_workflow_result_event.py +0 -5
  112. vellum/client/types/workflow_output_array.py +0 -5
  113. vellum/client/types/workflow_parent_context.py +0 -6
  114. vellum/client/types/workflow_result_event.py +0 -5
  115. vellum/client/types/workflow_result_event_output_data_array.py +0 -5
  116. vellum/client/types/workflow_sandbox_parent_context.py +0 -6
  117. vellum/workflows/nodes/bases/base.py +26 -6
  118. vellum/workflows/nodes/bases/tests/test_base_node.py +30 -0
  119. vellum/workflows/nodes/core/try_node/node.py +3 -6
  120. vellum/workflows/nodes/core/try_node/tests/test_node.py +0 -24
  121. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +8 -14
  122. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +112 -0
  123. vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -54
  124. vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +5 -6
  125. vellum/workflows/nodes/utils.py +8 -0
  126. vellum/workflows/types/code_execution_node_wrappers.py +69 -0
  127. vellum/workflows/vellum_client.py +19 -7
  128. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/METADATA +1 -1
  129. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/RECORD +162 -161
  130. vellum_cli/config.py +7 -2
  131. vellum_cli/push.py +5 -1
  132. vellum_cli/tests/test_push.py +192 -8
  133. vellum_ee/workflows/display/nodes/base_node_display.py +17 -6
  134. vellum_ee/workflows/display/nodes/get_node_display_class.py +4 -5
  135. vellum_ee/workflows/display/nodes/vellum/api_node.py +11 -0
  136. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +5 -0
  137. vellum_ee/workflows/display/nodes/vellum/error_node.py +22 -16
  138. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +2 -0
  139. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +46 -12
  140. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +2 -0
  141. vellum_ee/workflows/display/nodes/vellum/map_node.py +2 -0
  142. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +3 -5
  143. vellum_ee/workflows/display/nodes/vellum/search_node.py +8 -0
  144. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +2 -5
  145. vellum_ee/workflows/display/nodes/vellum/templating_node.py +2 -0
  146. vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +1 -1
  147. vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py +4 -0
  148. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_deployment_node.py +4 -3
  149. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +36 -2
  150. vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py +5 -4
  151. vellum_ee/workflows/display/nodes/vellum/tests/test_templating_node.py +1 -1
  152. vellum_ee/workflows/display/tests/test_base_workflow_display.py +44 -0
  153. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +2 -4
  154. vellum_ee/workflows/display/types.py +3 -0
  155. vellum_ee/workflows/display/utils/expressions.py +3 -3
  156. vellum_ee/workflows/display/utils/vellum.py +1 -3
  157. vellum_ee/workflows/display/workflows/base_workflow_display.py +10 -0
  158. vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +3 -0
  159. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +53 -0
  160. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/LICENSE +0 -0
  161. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/WHEEL +0 -0
  162. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/entry_points.txt +0 -0
@@ -409,6 +409,56 @@ def main(word: str) -> int:
409
409
  assert exc_info.value.message == "Expected an output of type 'int', but received 'str'"
410
410
 
411
411
 
412
+ def test_run_node__run_inline__incorrect_output_type_list():
413
+ """Confirm that CodeExecutionNodes raise an error if the output type is incorrect during inline execution."""
414
+
415
+ # GIVEN a node that subclasses CodeExecutionNode that returns a list but is defined to return an int
416
+ class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, int]):
417
+ code = """\
418
+ def main(output: list[str]) -> int:
419
+ return output.value
420
+ """
421
+ runtime = "PYTHON_3_11_6"
422
+
423
+ code_inputs = {
424
+ "output": ["hello", "world"],
425
+ }
426
+
427
+ # WHEN we run the node
428
+ node = ExampleCodeExecutionNode()
429
+ with pytest.raises(NodeException) as exc_info:
430
+ node.run()
431
+
432
+ # THEN the node should have produced the exception we expected
433
+ assert exc_info.value.message == "Expected an output of type 'int', but received 'list'"
434
+
435
+
436
+ def test_run_node__run_inline__incorrect_output_type_dict():
437
+ """Confirm that CodeExecutionNodes raise an error if the output type is incorrect during inline execution."""
438
+
439
+ # GIVEN a node that subclasses CodeExecutionNode that returns a dict but is defined to return an int
440
+ class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, int]):
441
+ code = """\
442
+ def main(output: dict[str, str]) -> int:
443
+ return output.value
444
+ """
445
+ runtime = "PYTHON_3_11_6"
446
+
447
+ code_inputs = {
448
+ "output": {
449
+ "foo": "bar",
450
+ }
451
+ }
452
+
453
+ # WHEN we run the node
454
+ node = ExampleCodeExecutionNode()
455
+ with pytest.raises(NodeException) as exc_info:
456
+ node.run()
457
+
458
+ # THEN the node should have produced the exception we expected
459
+ assert exc_info.value.message == "Expected an output of type 'int', but received 'dict'"
460
+
461
+
412
462
  def test_run_node__run_inline__valid_dict_to_pydantic():
413
463
  """Confirm that CodeExecutionNodes can convert a dict to a Pydantic model during inline execution."""
414
464
 
@@ -821,3 +871,65 @@ def main(arg1: list) -> str:
821
871
 
822
872
  # AND the result should be the correct output
823
873
  assert outputs == {"result": "bar", "log": ""}
874
+
875
+
876
+ def test_run_node__string_value_wrapper__get_attr():
877
+ # GIVEN a node that accesses the 'value' property of a string input
878
+ class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, str]):
879
+ code = """\
880
+ def main(text: str) -> str:
881
+ return text.value
882
+ """
883
+ code_inputs = {
884
+ "text": "hello",
885
+ }
886
+ runtime = "PYTHON_3_11_6"
887
+
888
+ # WHEN we run the node
889
+ node = ExampleCodeExecutionNode()
890
+ outputs = node.run()
891
+
892
+ # THEN the node should successfully access the string value through the .value property
893
+ assert outputs == {"result": "hello", "log": ""}
894
+
895
+
896
+ def test_run_node__string_value_wrapper__get_item():
897
+ # GIVEN a node that accesses the 'value' property of a string input
898
+ class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, str]):
899
+ code = """\
900
+ def main(text: str) -> str:
901
+ return text["value"]
902
+ """
903
+ code_inputs = {
904
+ "text": "hello",
905
+ }
906
+ runtime = "PYTHON_3_11_6"
907
+
908
+ # WHEN we run the node
909
+ node = ExampleCodeExecutionNode()
910
+ outputs = node.run()
911
+
912
+ # THEN the node should successfully access the string value through the .value property
913
+ assert outputs == {"result": "hello", "log": ""}
914
+
915
+
916
+ def test_run_node__string_value_wrapper__list_of_dicts():
917
+ # GIVEN a node that accesses the 'value' property of a string input
918
+ class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, Any]):
919
+ code = """\
920
+ def main(output: list[str]) -> list[str]:
921
+ results = []
922
+ for item in output:
923
+ results.append(item['value'])
924
+
925
+ return results
926
+ """
927
+ code_inputs = {"output": ['{"foo": "bar"}', '{"foo2": "bar2"}']}
928
+ runtime = "PYTHON_3_11_6"
929
+
930
+ # WHEN we run the node
931
+ node = ExampleCodeExecutionNode()
932
+ outputs = node.run()
933
+
934
+ # THEN the node should successfully access the string value
935
+ assert outputs == {"result": ['{"foo": "bar"}', '{"foo2": "bar2"}'], "log": ""}
@@ -8,6 +8,7 @@ from vellum.workflows.errors.types import WorkflowErrorCode
8
8
  from vellum.workflows.exceptions import NodeException
9
9
  from vellum.workflows.nodes.utils import cast_to_output_type
10
10
  from vellum.workflows.state.context import WorkflowContext
11
+ from vellum.workflows.types.code_execution_node_wrappers import ListWrapper, clean_for_dict_wrapper
11
12
  from vellum.workflows.types.core import EntityInputsInterface
12
13
 
13
14
 
@@ -35,58 +36,6 @@ def read_file_from_path(
35
36
  return None
36
37
 
37
38
 
38
- class ListWrapper(list):
39
- def __getitem__(self, key):
40
- item = super().__getitem__(key)
41
- if not isinstance(item, DictWrapper) and not isinstance(item, ListWrapper):
42
- self.__setitem__(key, _clean_for_dict_wrapper(item))
43
-
44
- return super().__getitem__(key)
45
-
46
-
47
- class DictWrapper(dict):
48
- """
49
- This wraps a dict object to make it behave basically the same as a standard javascript object
50
- and enables us to use vellum types here without a shared library since we don't actually
51
- typecheck things here.
52
- """
53
-
54
- def __getitem__(self, key):
55
- return self.__getattr__(key)
56
-
57
- def __getattr__(self, attr):
58
- if attr not in self:
59
- if attr == "value":
60
- # In order to be backwards compatible with legacy Workflows, which wrapped
61
- # several values as VellumValue objects, we use the "value" key to return itself
62
- return self
63
-
64
- raise AttributeError(f"dict has no key: '{attr}'")
65
-
66
- item = super().__getitem__(attr)
67
- if not isinstance(item, DictWrapper) and not isinstance(item, ListWrapper):
68
- self.__setattr__(attr, _clean_for_dict_wrapper(item))
69
-
70
- return super().__getitem__(attr)
71
-
72
- def __setattr__(self, name, value):
73
- self[name] = value
74
-
75
-
76
- def _clean_for_dict_wrapper(obj):
77
- if isinstance(obj, dict):
78
- wrapped = DictWrapper(obj)
79
- for key in wrapped:
80
- wrapped[key] = _clean_for_dict_wrapper(wrapped[key])
81
-
82
- return wrapped
83
-
84
- elif isinstance(obj, list):
85
- return ListWrapper(map(lambda item: _clean_for_dict_wrapper(item), obj))
86
-
87
- return obj
88
-
89
-
90
39
  def run_code_inline(
91
40
  code: str,
92
41
  inputs: EntityInputsInterface,
@@ -107,12 +56,12 @@ def run_code_inline(
107
56
  (
108
57
  item.model_dump()
109
58
  if isinstance(item, BaseModel)
110
- else _clean_for_dict_wrapper(item) if isinstance(item, (dict, list)) else item
59
+ else clean_for_dict_wrapper(item) if isinstance(item, (dict, list, str)) else item
111
60
  )
112
61
  for item in value
113
62
  ]
114
63
  )
115
- return _clean_for_dict_wrapper(value)
64
+ return clean_for_dict_wrapper(value)
116
65
 
117
66
  exec_globals = {
118
67
  "__arg__inputs": {name: wrap_value(value) for name, value in inputs.items()},
@@ -9,7 +9,6 @@ from vellum import (
9
9
  PromptOutput,
10
10
  StringVellumValue,
11
11
  )
12
- from vellum.workflows.constants import OMIT
13
12
  from vellum.workflows.inputs import BaseInputs
14
13
  from vellum.workflows.nodes import PromptDeploymentNode
15
14
  from vellum.workflows.state import BaseState
@@ -66,14 +65,14 @@ def test_text_prompt_deployment_node__basic(vellum_client):
66
65
 
67
66
  # AND we should have made the expected call to stream the prompt execution
68
67
  vellum_client.execute_prompt_stream.assert_called_once_with(
69
- expand_meta=OMIT,
70
- expand_raw=OMIT,
71
- external_id=OMIT,
68
+ expand_meta=None,
69
+ expand_raw=None,
70
+ external_id=None,
72
71
  inputs=[],
73
- metadata=OMIT,
72
+ metadata=None,
74
73
  prompt_deployment_id=None,
75
74
  prompt_deployment_name="my-deployment",
76
- raw_overrides=OMIT,
75
+ raw_overrides=None,
77
76
  release_tag="LATEST",
78
77
  request_options={
79
78
  "additional_body_parameters": {"execution_context": {"parent_context": None, "trace_id": mock.ANY}}
@@ -1,4 +1,5 @@
1
1
  from functools import cache
2
+ import inspect
2
3
  import json
3
4
  import sys
4
5
  from types import ModuleType
@@ -14,6 +15,7 @@ from vellum.workflows.nodes import BaseNode
14
15
  from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
15
16
  from vellum.workflows.ports.port import Port
16
17
  from vellum.workflows.state.base import BaseState
18
+ from vellum.workflows.types.code_execution_node_wrappers import DictWrapper, ListWrapper, StringValueWrapper
17
19
  from vellum.workflows.types.core import Json
18
20
  from vellum.workflows.types.generics import NodeType
19
21
 
@@ -176,6 +178,12 @@ def parse_type_from_str(result_as_str: str, output_type: Any) -> Any:
176
178
 
177
179
 
178
180
  def _get_type_name(obj: Any) -> str:
181
+ if inspect.isclass(obj) and issubclass(obj, StringValueWrapper):
182
+ return "str"
183
+ if inspect.isclass(obj) and issubclass(obj, ListWrapper):
184
+ return "list"
185
+ if inspect.isclass(obj) and issubclass(obj, DictWrapper):
186
+ return "dict"
179
187
  if isinstance(obj, type):
180
188
  return obj.__name__
181
189
 
@@ -0,0 +1,69 @@
1
+ class StringValueWrapper(str):
2
+ def __getitem__(self, key):
3
+ if key == "value":
4
+ return self
5
+ raise KeyError(key)
6
+
7
+ def __getattr__(self, attr):
8
+ if attr == "value":
9
+ return self
10
+ raise AttributeError(f"'str' object has no attribute '{attr}'")
11
+
12
+
13
+ class ListWrapper(list):
14
+ def __getitem__(self, key):
15
+ item = super().__getitem__(key)
16
+ if not isinstance(item, DictWrapper) and not isinstance(item, ListWrapper):
17
+ self.__setitem__(key, clean_for_dict_wrapper(item))
18
+
19
+ return super().__getitem__(key)
20
+
21
+ def __getattr__(self, attr):
22
+ if attr == "value":
23
+ return self
24
+ raise AttributeError(f"'list' object has no attribute '{attr}'")
25
+
26
+
27
+ class DictWrapper(dict):
28
+ """
29
+ This wraps a dict object to make it behave basically the same as a standard javascript object
30
+ and enables us to use vellum types here without a shared library since we don't actually
31
+ typecheck things here.
32
+ """
33
+
34
+ def __getitem__(self, key):
35
+ return self.__getattr__(key)
36
+
37
+ def __getattr__(self, attr):
38
+ if attr not in self:
39
+ if attr == "value":
40
+ # In order to be backwards compatible with legacy Workflows, which wrapped
41
+ # several values as VellumValue objects, we use the "value" key to return itself
42
+ return self
43
+
44
+ raise AttributeError(f"dict has no key: '{attr}'")
45
+
46
+ item = super().__getitem__(attr)
47
+ if not isinstance(item, DictWrapper) and not isinstance(item, ListWrapper):
48
+ self.__setattr__(attr, clean_for_dict_wrapper(item))
49
+
50
+ return super().__getitem__(attr)
51
+
52
+ def __setattr__(self, name, value):
53
+ self[name] = value
54
+
55
+
56
+ def clean_for_dict_wrapper(obj):
57
+ if isinstance(obj, dict):
58
+ wrapped = DictWrapper(obj)
59
+ for key in wrapped:
60
+ wrapped[key] = clean_for_dict_wrapper(wrapped[key])
61
+
62
+ return wrapped
63
+
64
+ elif isinstance(obj, list):
65
+ return ListWrapper(map(lambda item: clean_for_dict_wrapper(item), obj))
66
+ elif isinstance(obj, str):
67
+ return StringValueWrapper(obj)
68
+
69
+ return obj
@@ -1,22 +1,34 @@
1
1
  import os
2
- from typing import Optional
2
+ from typing import List, Optional
3
3
 
4
4
  from vellum import Vellum, VellumEnvironment
5
5
 
6
6
 
7
- def create_vellum_client(api_key: Optional[str] = None) -> Vellum:
7
+ def create_vellum_client(api_key: Optional[str] = None, api_url: Optional[str] = None) -> Vellum:
8
8
  if api_key is None:
9
9
  api_key = os.getenv("VELLUM_API_KEY", default="")
10
10
 
11
11
  return Vellum(
12
12
  api_key=api_key,
13
- environment=create_vellum_environment(),
13
+ environment=create_vellum_environment(api_url),
14
14
  )
15
15
 
16
16
 
17
- def create_vellum_environment() -> VellumEnvironment:
17
+ def create_vellum_environment(api_url: Optional[str] = None) -> VellumEnvironment:
18
18
  return VellumEnvironment(
19
- default=os.getenv("VELLUM_DEFAULT_API_URL", os.getenv("VELLUM_API_URL", "https://api.vellum.ai")),
20
- documents=os.getenv("VELLUM_DOCUMENTS_API_URL", os.getenv("VELLUM_API_URL", "https://documents.vellum.ai")),
21
- predict=os.getenv("VELLUM_PREDICT_API_URL", os.getenv("VELLUM_API_URL", "https://predict.vellum.ai")),
19
+ default=_resolve_env([api_url, "VELLUM_DEFAULT_API_URL", "VELLUM_API_URL"], "https://api.vellum.ai"),
20
+ documents=_resolve_env([api_url, "VELLUM_DOCUMENTS_API_URL", "VELLUM_API_URL"], "https://documents.vellum.ai"),
21
+ predict=_resolve_env([api_url, "VELLUM_PREDICT_API_URL", "VELLUM_API_URL"], "https://predict.vellum.ai"),
22
22
  )
23
+
24
+
25
+ def _resolve_env(names: List[Optional[str]], default: str = "") -> str:
26
+ for name in names:
27
+ if not name:
28
+ continue
29
+
30
+ value = os.getenv(name)
31
+ if value:
32
+ return value
33
+
34
+ return default
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.45
3
+ Version: 0.14.47
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0