luminarycloud 0.17.0__py3-none-any.whl → 0.18.0__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 (54) hide show
  1. luminarycloud/_client/client.py +3 -0
  2. luminarycloud/_helpers/_create_geometry.py +134 -32
  3. luminarycloud/_helpers/cond.py +0 -1
  4. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +146 -123
  5. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +81 -8
  6. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.py +34 -0
  7. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.pyi +12 -0
  8. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.py +8 -8
  9. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.pyi +12 -7
  10. luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2.py +246 -0
  11. luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2.pyi +420 -0
  12. luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2_grpc.py +240 -0
  13. luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2_grpc.pyi +90 -0
  14. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.py +54 -3
  15. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.pyi +92 -1
  16. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2_grpc.py +132 -0
  17. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2_grpc.pyi +40 -0
  18. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.py +48 -26
  19. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.pyi +30 -2
  20. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.py +36 -0
  21. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.pyi +18 -0
  22. luminarycloud/_proto/client/simulation_pb2.py +261 -251
  23. luminarycloud/_proto/client/simulation_pb2.pyi +35 -2
  24. luminarycloud/_proto/frontend/output/output_pb2.py +24 -24
  25. luminarycloud/_proto/frontend/output/output_pb2.pyi +6 -3
  26. luminarycloud/_proto/geometry/geometry_pb2.py +63 -63
  27. luminarycloud/_proto/geometry/geometry_pb2.pyi +14 -4
  28. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.py +10 -10
  29. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.pyi +12 -7
  30. luminarycloud/enum/geometry_status.py +15 -8
  31. luminarycloud/enum/pipeline_job_status.py +23 -0
  32. luminarycloud/feature_modification.py +3 -1
  33. luminarycloud/geometry.py +12 -0
  34. luminarycloud/geometry_version.py +23 -0
  35. luminarycloud/params/enum/_enum_wrappers.py +29 -0
  36. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/fan_curve_inlet_.py +1 -1
  37. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mach_inlet_.py +5 -1
  38. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mass_flow_inlet_.py +5 -1
  39. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/total_pressure_inlet_.py +5 -1
  40. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/velocity_magnitude_inlet_.py +5 -1
  41. luminarycloud/physics_ai/inference.py +46 -30
  42. luminarycloud/pipelines/__init__.py +7 -0
  43. luminarycloud/pipelines/api.py +213 -0
  44. luminarycloud/project.py +65 -5
  45. luminarycloud/simulation_template.py +15 -6
  46. luminarycloud/vis/__init__.py +4 -0
  47. luminarycloud/vis/interactive_inference.py +153 -0
  48. luminarycloud/vis/interactive_scene.py +35 -16
  49. luminarycloud/vis/primitives.py +9 -0
  50. luminarycloud/vis/visualization.py +6 -0
  51. luminarycloud/volume_selection.py +3 -2
  52. {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.0.dist-info}/METADATA +18 -18
  53. {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.0.dist-info}/RECORD +54 -47
  54. {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.0.dist-info}/WHEEL +0 -0
luminarycloud/geometry.py CHANGED
@@ -29,6 +29,7 @@ from .named_variable_set import NamedVariableSet, get_named_variable_set
29
29
 
30
30
  if TYPE_CHECKING:
31
31
  from .project import Project
32
+ from .geometry_version import GeometryVersion
32
33
 
33
34
 
34
35
  @ProtoWrapper(geometrypb.Geometry)
@@ -289,6 +290,17 @@ class Geometry(ProtoWrapperBase):
289
290
  )
290
291
  )
291
292
 
293
+ def latest_version(self) -> GeometryVersion:
294
+ """
295
+ Get the latest version of the geometry.
296
+ """
297
+ from .geometry_version import get_geometry_version
298
+
299
+ req = geometrypb.GetGeometryRequest(geometry_id=self.id)
300
+ res_geo: geometrypb.GetGeometryResponse = get_default_client().GetGeometry(req)
301
+ geometry_version_id = res_geo.geometry.last_version_id
302
+ return get_geometry_version(geometry_version_id)
303
+
292
304
  def check(self) -> tuple[bool, list[str]]:
293
305
  """
294
306
  Check the geometry for any issues that may prevent meshing.
@@ -87,6 +87,29 @@ class GeometryVersion(ProtoWrapperBase):
87
87
  ]
88
88
  return surfaces, volumes
89
89
 
90
+ def copy_to_new_geometry(self, name: str = "") -> Geometry:
91
+ """
92
+ Copy this GeometryVersion and create a new Geometry containing only that newly copied version.
93
+
94
+ Parameters
95
+ ----------
96
+ name : str, optional
97
+ The name of the new Geometry. If not provided, a default name will be used.
98
+
99
+ Returns
100
+ -------
101
+ Geometry
102
+ The new Geometry containing only the newly copied GeometryVersion.
103
+ """
104
+ req = geometrypb.CopyGeometryFromVersionRequest(
105
+ geometry_version_id=self.id,
106
+ name=name,
107
+ )
108
+ res: geometrypb.CopyGeometryFromVersionResponse = (
109
+ get_default_client().CopyGeometryFromVersion(req)
110
+ )
111
+ return Geometry(res.geometry)
112
+
90
113
  def _list_features(
91
114
  self,
92
115
  ) -> list[gpb.Feature]:
@@ -1776,6 +1776,9 @@ class DirectionSpecification(_IntEnum):
1776
1776
  Impose a flow direction normal to the inlet boundary toward the interior of the domain.
1777
1777
  DIRECTION_VECTOR
1778
1778
  Specify a vector for the inlet flow direction.
1779
+ CYLINDRICAL_DIRECTIONS
1780
+ Specify the inlet flow direction in a cylindrical coordinate system whose main axis
1781
+ is defined by the z axis of the inlet frame.
1779
1782
 
1780
1783
 
1781
1784
  Examples
@@ -1783,11 +1786,37 @@ class DirectionSpecification(_IntEnum):
1783
1786
  >>> from luminarycloud.params.enum import DirectionSpecification
1784
1787
  >>> DirectionSpecification.NORMAL_TO_BOUNDARY
1785
1788
  >>> DirectionSpecification.DIRECTION_VECTOR
1789
+ >>> DirectionSpecification.CYLINDRICAL_DIRECTIONS
1786
1790
  """
1787
1791
 
1788
1792
  INVALID = _clientpb.INVALID_DIRECTION_SPECIFICATION
1789
1793
  NORMAL_TO_BOUNDARY = _clientpb.NORMAL_TO_BOUNDARY
1790
1794
  DIRECTION_VECTOR = _clientpb.DIRECTION_VECTOR
1795
+ CYLINDRICAL_DIRECTIONS = _clientpb.CYLINDRICAL_DIRECTIONS
1796
+
1797
+
1798
+ class DirectionFrame(_IntEnum):
1799
+ """
1800
+ Frame used to define the flow direction.
1801
+
1802
+ Attributes
1803
+ ----------
1804
+ DIRECTION_FRAME_GLOBAL
1805
+ The direction vectors are defined by the global frame.
1806
+ DIRECTION_FRAME_BOUNDARY
1807
+ The direction vectors are defined by the frame to which the boundary surfaces are attached.
1808
+
1809
+
1810
+ Examples
1811
+ --------
1812
+ >>> from luminarycloud.params.enum import DirectionFrame
1813
+ >>> DirectionFrame.DIRECTION_FRAME_GLOBAL
1814
+ >>> DirectionFrame.DIRECTION_FRAME_BOUNDARY
1815
+ """
1816
+
1817
+ INVALID = _clientpb.INVALID_DIRECTION_FRAME
1818
+ DIRECTION_FRAME_GLOBAL = _clientpb.DIRECTION_FRAME_GLOBAL
1819
+ DIRECTION_FRAME_BOUNDARY = _clientpb.DIRECTION_FRAME_BOUNDARY
1791
1820
 
1792
1821
 
1793
1822
  class OutletPressureConstraint(_IntEnum):
@@ -32,7 +32,7 @@ class FanCurveInlet(Inlet):
32
32
  )
33
33
  "Method of defining the flow direction at the inlet."
34
34
  direction: Vector3 = field(default_factory=lambda: Vector3(1.0, 0.0, 0.0))
35
- "Vector (x,y,z) defining the flow direction. Automatically scaled to a unit vector internally."
35
+ "Vector (x,y,z) or (ρ,φ,z) defining the flow direction. Automatically scaled to a unit vector internally."
36
36
  fan_curve_table: RectilinearTable | None = None
37
37
  "Correlation between fan pressure rise and volume flow rate."
38
38
  head_loss_coefficient: LcFloat = 0.0
@@ -33,8 +33,10 @@ class MachInlet(Inlet):
33
33
  enum.DirectionSpecification.NORMAL_TO_BOUNDARY
34
34
  )
35
35
  "Method of defining the flow direction at the inlet."
36
+ direction_frame: enum.DirectionFrame = enum.DirectionFrame.DIRECTION_FRAME_GLOBAL
37
+ "Frame used to define the flow direction."
36
38
  direction: Vector3 = field(default_factory=lambda: Vector3(1.0, 0.0, 0.0))
37
- "Vector (x,y,z) defining the flow direction. Automatically scaled to a unit vector internally."
39
+ "Vector (x,y,z) or (ρ,φ,z) defining the flow direction. Automatically scaled to a unit vector internally."
38
40
 
39
41
  def _to_proto(self) -> clientpb.BoundaryConditionsFluid:
40
42
  _proto = super()._to_proto()
@@ -42,6 +44,7 @@ class MachInlet(Inlet):
42
44
  _proto.farfield_mach_number.CopyFrom(_to_ad_proto(self.mach_number))
43
45
  _proto.farfield_pressure.CopyFrom(_to_ad_proto(self.pressure))
44
46
  _proto.direction_specification = self.direction_specification.value
47
+ _proto.direction_frame = self.direction_frame.value
45
48
  _proto.flow_direction.CopyFrom(self.direction._to_ad_proto())
46
49
  return _proto
47
50
 
@@ -51,5 +54,6 @@ class MachInlet(Inlet):
51
54
  self.mach_number = _from_ad_proto(proto.farfield_mach_number)
52
55
  self.pressure = _from_ad_proto(proto.farfield_pressure)
53
56
  self.direction_specification = enum.DirectionSpecification(proto.direction_specification)
57
+ self.direction_frame = enum.DirectionFrame(proto.direction_frame)
54
58
  self.direction._from_ad_proto(proto.flow_direction)
55
59
  return None
@@ -31,14 +31,17 @@ class MassFlowInlet(Inlet):
31
31
  enum.DirectionSpecification.NORMAL_TO_BOUNDARY
32
32
  )
33
33
  "Method of defining the flow direction at the inlet."
34
+ direction_frame: enum.DirectionFrame = enum.DirectionFrame.DIRECTION_FRAME_GLOBAL
35
+ "Frame used to define the flow direction."
34
36
  direction: Vector3 = field(default_factory=lambda: Vector3(1.0, 0.0, 0.0))
35
- "Vector (x,y,z) defining the flow direction. Automatically scaled to a unit vector internally."
37
+ "Vector (x,y,z) or (ρ,φ,z) defining the flow direction. Automatically scaled to a unit vector internally."
36
38
 
37
39
  def _to_proto(self) -> clientpb.BoundaryConditionsFluid:
38
40
  _proto = super()._to_proto()
39
41
  _proto.inlet_momentum = clientpb.InletMomentum.MASS_FLOW_INLET
40
42
  _proto.mass_flow_rate.CopyFrom(_to_ad_proto(self.mass_flow_rate))
41
43
  _proto.direction_specification = self.direction_specification.value
44
+ _proto.direction_frame = self.direction_frame.value
42
45
  _proto.flow_direction.CopyFrom(self.direction._to_ad_proto())
43
46
  return _proto
44
47
 
@@ -47,5 +50,6 @@ class MassFlowInlet(Inlet):
47
50
  assert proto.inlet_momentum == clientpb.InletMomentum.MASS_FLOW_INLET
48
51
  self.mass_flow_rate = _from_ad_proto(proto.mass_flow_rate)
49
52
  self.direction_specification = enum.DirectionSpecification(proto.direction_specification)
53
+ self.direction_frame = enum.DirectionFrame(proto.direction_frame)
50
54
  self.direction._from_ad_proto(proto.flow_direction)
51
55
  return None
@@ -33,8 +33,10 @@ class TotalPressureInlet(Inlet):
33
33
  enum.DirectionSpecification.NORMAL_TO_BOUNDARY
34
34
  )
35
35
  "Method of defining the flow direction at the inlet."
36
+ direction_frame: enum.DirectionFrame = enum.DirectionFrame.DIRECTION_FRAME_GLOBAL
37
+ "Frame used to define the flow direction."
36
38
  direction: Vector3 = field(default_factory=lambda: Vector3(1.0, 0.0, 0.0))
37
- "Vector (x,y,z) defining the flow direction. Automatically scaled to a unit vector internally."
39
+ "Vector (x,y,z) or (ρ,φ,z) defining the flow direction. Automatically scaled to a unit vector internally."
38
40
 
39
41
  def _to_proto(self) -> clientpb.BoundaryConditionsFluid:
40
42
  _proto = super()._to_proto()
@@ -42,6 +44,7 @@ class TotalPressureInlet(Inlet):
42
44
  _proto.total_pressure.CopyFrom(_to_ad_proto(self.total_pressure))
43
45
  _proto.total_pressure_col.value = self.total_pressure_column_index
44
46
  _proto.direction_specification = self.direction_specification.value
47
+ _proto.direction_frame = self.direction_frame.value
45
48
  _proto.flow_direction.CopyFrom(self.direction._to_ad_proto())
46
49
  return _proto
47
50
 
@@ -51,5 +54,6 @@ class TotalPressureInlet(Inlet):
51
54
  self.total_pressure = _from_ad_proto(proto.total_pressure)
52
55
  self.total_pressure_column_index = proto.total_pressure_col.value
53
56
  self.direction_specification = enum.DirectionSpecification(proto.direction_specification)
57
+ self.direction_frame = enum.DirectionFrame(proto.direction_frame)
54
58
  self.direction._from_ad_proto(proto.flow_direction)
55
59
  return None
@@ -33,8 +33,10 @@ class VelocityMagnitudeInlet(Inlet):
33
33
  enum.DirectionSpecification.NORMAL_TO_BOUNDARY
34
34
  )
35
35
  "Method of defining the flow direction at the inlet."
36
+ direction_frame: enum.DirectionFrame = enum.DirectionFrame.DIRECTION_FRAME_GLOBAL
37
+ "Frame used to define the flow direction."
36
38
  direction: Vector3 = field(default_factory=lambda: Vector3(1.0, 0.0, 0.0))
37
- "Vector (x,y,z) defining the flow direction. Automatically scaled to a unit vector internally."
39
+ "Vector (x,y,z) or (ρ,φ,z) defining the flow direction. Automatically scaled to a unit vector internally."
38
40
  pressure: LcFloat = 101325
39
41
  "Static pressure at the boundary relative to the material reference pressure."
40
42
 
@@ -44,6 +46,7 @@ class VelocityMagnitudeInlet(Inlet):
44
46
  _proto.inlet_velocity_magnitude.CopyFrom(_to_ad_proto(self.velocity))
45
47
  _proto.inlet_velocity_magnitude_col.value = self.velocity_column_index
46
48
  _proto.direction_specification = self.direction_specification.value
49
+ _proto.direction_frame = self.direction_frame.value
47
50
  _proto.flow_direction.CopyFrom(self.direction._to_ad_proto())
48
51
  _proto.farfield_pressure.CopyFrom(_to_ad_proto(self.pressure))
49
52
  return _proto
@@ -54,6 +57,7 @@ class VelocityMagnitudeInlet(Inlet):
54
57
  self.velocity = _from_ad_proto(proto.inlet_velocity_magnitude)
55
58
  self.velocity_column_index = proto.inlet_velocity_magnitude_col.value
56
59
  self.direction_specification = enum.DirectionSpecification(proto.direction_specification)
60
+ self.direction_frame = enum.DirectionFrame(proto.direction_frame)
57
61
  self.direction._from_ad_proto(proto.flow_direction)
58
62
  self.pressure = _from_ad_proto(proto.farfield_pressure)
59
63
  return None
@@ -1,8 +1,8 @@
1
1
  # File: python/sdk/luminarycloud/inference/inference.py
2
2
  # Copyright 2025 Luminary Cloud, Inc. All Rights Reserved.
3
3
  from datetime import datetime
4
- from typing import Any, Callable
5
- from json import loads as json_loads
4
+ from typing import Any, Callable, Dict, Optional
5
+ from json import loads as json_loads, dumps as json_dumps
6
6
  from dataclasses import dataclass
7
7
  import base64
8
8
  import os
@@ -48,7 +48,11 @@ class ExtAeroInferenceResult:
48
48
 
49
49
 
50
50
  def external_aero_inference(
51
- project: Project, stl_file: str, checkpoint_file: str, config_name: str, stencil_size: int
51
+ project: Project,
52
+ stl_file: str,
53
+ artifact_url: str,
54
+ parameters: Optional[Dict[str, Any]] = None,
55
+ stencil_size: int = 1,
52
56
  ) -> ExtAeroInferenceResult:
53
57
  """Performs an inference job returning external aerodynamic results.
54
58
  Parameters
@@ -57,12 +61,12 @@ def external_aero_inference(
57
61
  The project to which the inference files will be added.
58
62
  stl_file : str
59
63
  Fullpath the STL file to be used for inference.
60
- checkpoint_file : str
61
- Fullpath of the model to be used for inference.
62
- config_name :str
63
- Name of the configuration to be used for inference.
64
- stencil_size :int
65
- Size of the stencil to be used for inference.
64
+ artifact_url : str
65
+ Fullpath of the model artifact directory to be used for inference.
66
+ parameters : Dict[str, Any], optional
67
+ Dictionary of parameters to be passed to the inference service (e.g., alpha, beta, etc.).
68
+ stencil_size : int, optional
69
+ Size of the stencil to be used for inference. Defaults to 1.
66
70
 
67
71
  Returns
68
72
  ExtAeroInferenceResult
@@ -71,12 +75,16 @@ def external_aero_inference(
71
75
  warning:: This feature is experimental and may change or be removed without notice.
72
76
  """
73
77
 
74
- result = perform_inference(project, stl_file, checkpoint_file, config_name, stencil_size)
78
+ result = perform_inference(project, stl_file, artifact_url, parameters, stencil_size)
75
79
  return ExtAeroInferenceResult(result)
76
80
 
77
81
 
78
82
  def perform_inference(
79
- project: Project, stl_file: str, checkpoint_file: str, config_name: str, stencil_size: int
83
+ project: Project,
84
+ stl_file: str,
85
+ artifact_url: str,
86
+ parameters: Optional[Dict[str, Any]] = None,
87
+ stencil_size: int = 1,
80
88
  ) -> dict[str, Any]:
81
89
  """Creates an inference service job.
82
90
  Parameters
@@ -85,12 +93,12 @@ def perform_inference(
85
93
  The project to which the inference files will be added.
86
94
  stl_file : str
87
95
  Fullpath the STL file to be used for inference.
88
- checkpoint_file : str
89
- Fullpath of the model to be used for inference.
90
- config_name :str
91
- Name of the configuration to be used for inference.
92
- stencil_size :int
93
- Size of the stencil to be used for inference.
96
+ artifact_url : str
97
+ Fullpath of the model artifact directory to be used for inference.
98
+ parameters : Dict[str, Any], optional
99
+ Dictionary of parameters to be passed to the inference service (e.g., alpha, beta, etc.).
100
+ stencil_size : int, optional
101
+ Size of the stencil to be used for inference. Defaults to 1.
94
102
 
95
103
 
96
104
  Returns
@@ -123,9 +131,8 @@ def perform_inference(
123
131
  return download_file
124
132
 
125
133
  stl_url = upload_if_file(stl_file)
126
- check_url = upload_if_file(checkpoint_file)
127
134
 
128
- raw = start_inference_job(project, stl_url, check_url, config_name, stencil_size)
135
+ raw = start_inference_job(project, stl_url, artifact_url, parameters, stencil_size)
129
136
  currated: dict[str, Any] = {}
130
137
  for k, v in raw.items():
131
138
  if isinstance(v, str) and v.startswith("https://"):
@@ -144,9 +151,10 @@ def perform_inference(
144
151
  def start_inference_job(
145
152
  project: Project,
146
153
  stl_url: str,
147
- checkpoint_url: str,
148
- config_name: str,
149
- stencil_size: int,
154
+ artifact_url: str,
155
+ parameters: Optional[Dict[str, Any]] = None,
156
+ stencil_size: int = 1,
157
+ write_visualization_data=False,
150
158
  ) -> dict[str, Any]:
151
159
  """Creates an inference service job.
152
160
  Parameters
@@ -155,12 +163,14 @@ def start_inference_job(
155
163
  Reference to a project.
156
164
  stl_url : str
157
165
  URL of the STL file to be used for inference.
158
- checkpoint_url : str
159
- URL of the model to be used for inference.
160
- config_name :str
161
- Name of the configuration to be used for inference.
162
- stencil_size :int
163
- Size of the stencil to be used for inference.
166
+ artifact_url : str
167
+ URL of the model artifact directory to be used for inference.
168
+ parameters : Dict[str, Any], optional
169
+ Dictionary of parameters to be passed to the inference service (e.g., alpha, beta, etc.).
170
+ stencil_size : int, optional
171
+ Size of the stencil to be used for inference. Defaults to 1.
172
+ write_visualization_data : bool, optional
173
+ Whether to write LC render data for visualization by Luminary.
164
174
 
165
175
 
166
176
  Returns
@@ -170,12 +180,18 @@ def start_inference_job(
170
180
  warning:: This feature is experimental and may change or be removed without notice.
171
181
  """
172
182
 
183
+ # Convert parameters dict to bytes if provided
184
+ parameters_bytes = b""
185
+ if parameters is not None:
186
+ parameters_bytes = json_dumps(parameters).encode("utf-8")
187
+
173
188
  req = inferencepb.CreateInferenceServiceJobRequest(
174
189
  stl_url=stl_url,
175
- checkpoint_url=checkpoint_url,
176
- config_name=config_name,
190
+ artifact_url=artifact_url,
191
+ parameters=parameters_bytes,
177
192
  stencil_size=stencil_size,
178
193
  project_id=project.id,
194
+ write_visualization_data=write_visualization_data,
179
195
  )
180
196
 
181
197
  res: inferencepb.CreateInferenceServiceJobResponse = (
@@ -33,3 +33,10 @@ from .arguments import (
33
33
  PipelineArgs as PipelineArgs,
34
34
  ArgNamedVariableSet as ArgNamedVariableSet,
35
35
  )
36
+
37
+ from .api import (
38
+ create_pipeline as create_pipeline,
39
+ list_pipelines as list_pipelines,
40
+ get_pipeline as get_pipeline,
41
+ create_pipeline_job as create_pipeline_job,
42
+ )
@@ -0,0 +1,213 @@
1
+ # Copyright 2023-2024 Luminary Cloud, Inc. All Rights Reserved.
2
+ from dataclasses import dataclass
3
+
4
+ from datetime import datetime
5
+
6
+ from luminarycloud._helpers import timestamp_to_datetime
7
+
8
+ from ..enum.pipeline_job_status import PipelineJobStatus
9
+ from ..pipelines import Pipeline, PipelineArgs
10
+ from .._client import get_default_client
11
+ from .._proto.api.v0.luminarycloud.pipelines import pipelines_pb2 as pipelinespb
12
+
13
+
14
+ @dataclass
15
+ class PipelineRecord:
16
+ id: str
17
+ name: str
18
+ description: str | None
19
+ definition_yaml: str
20
+ create_time: datetime
21
+ update_time: datetime
22
+
23
+ def pipeline(self) -> Pipeline:
24
+ return Pipeline._from_yaml(self.definition_yaml)
25
+
26
+ @classmethod
27
+ def from_proto(cls, proto: pipelinespb.Pipeline) -> "PipelineRecord":
28
+ return cls(
29
+ id=proto.id,
30
+ name=proto.name,
31
+ description=proto.description,
32
+ definition_yaml=proto.definition_yaml,
33
+ create_time=timestamp_to_datetime(proto.created_at),
34
+ update_time=timestamp_to_datetime(proto.updated_at),
35
+ )
36
+
37
+
38
+ @dataclass
39
+ class PipelineJobRecord:
40
+ id: str
41
+ pipeline_id: str
42
+ project_id: str
43
+ name: str
44
+ description: str | None
45
+ status: PipelineJobStatus
46
+ create_time: datetime
47
+ update_time: datetime
48
+ started_at: datetime | None
49
+ completed_at: datetime | None
50
+
51
+ @classmethod
52
+ def from_proto(cls, proto: pipelinespb.PipelineJob) -> "PipelineJobRecord":
53
+ return cls(
54
+ id=proto.id,
55
+ pipeline_id=proto.pipeline_id,
56
+ project_id=proto.project_id,
57
+ name=proto.name,
58
+ description=proto.description,
59
+ status=PipelineJobStatus(proto.status),
60
+ create_time=timestamp_to_datetime(proto.created_at),
61
+ update_time=timestamp_to_datetime(proto.updated_at),
62
+ started_at=(
63
+ timestamp_to_datetime(proto.started_at) if proto.HasField("started_at") else None
64
+ ),
65
+ completed_at=(
66
+ timestamp_to_datetime(proto.completed_at)
67
+ if proto.HasField("completed_at")
68
+ else None
69
+ ),
70
+ )
71
+
72
+
73
+ def create_pipeline(
74
+ name: str, pipeline: Pipeline | str, description: str | None = None
75
+ ) -> PipelineRecord:
76
+ """
77
+ Create a new pipeline.
78
+
79
+ Parameters
80
+ ----------
81
+ name : str
82
+ Name of the pipeline.
83
+ pipeline : Pipeline | str
84
+ The pipeline to create. Accepts a Pipeline object or a YAML-formatted pipeline definition.
85
+ description : str, optional
86
+ Description of the pipeline.
87
+ """
88
+ if isinstance(pipeline, Pipeline):
89
+ definition_yaml = pipeline.to_yaml()
90
+ else:
91
+ definition_yaml = pipeline
92
+ req = pipelinespb.CreatePipelineRequest(
93
+ name=name, definition_yaml=definition_yaml, description=description
94
+ )
95
+ res: pipelinespb.CreatePipelineResponse = get_default_client().CreatePipeline(req)
96
+ return PipelineRecord.from_proto(res.pipeline)
97
+
98
+
99
+ def list_pipelines() -> list[PipelineRecord]:
100
+ """
101
+ List all pipelines.
102
+ """
103
+ req = pipelinespb.ListPipelinesRequest()
104
+ res: pipelinespb.ListPipelinesResponse = get_default_client().ListPipelines(req)
105
+ return [PipelineRecord.from_proto(p) for p in res.pipelines]
106
+
107
+
108
+ def get_pipeline(id: str) -> PipelineRecord:
109
+ """
110
+ Get a pipeline by ID.
111
+
112
+ Parameters
113
+ ----------
114
+ id : str
115
+ ID of the pipeline to fetch.
116
+ """
117
+ req = pipelinespb.GetPipelineRequest(id=id)
118
+ res: pipelinespb.GetPipelineResponse = get_default_client().GetPipeline(req)
119
+ return PipelineRecord.from_proto(res.pipeline)
120
+
121
+
122
+ def create_pipeline_job(
123
+ pipeline_id: str, args: PipelineArgs, project_id: str, name: str, description: str | None = None
124
+ ) -> PipelineJobRecord:
125
+ """
126
+ Create a new pipeline job.
127
+
128
+ Parameters
129
+ ----------
130
+ pipeline_id : str
131
+ ID of the pipeline to invoke.
132
+ args : PipelineArgs
133
+ Arguments to pass to the pipeline.
134
+ project_id : str
135
+ ID of the project to run the pipeline job in.
136
+ name : str
137
+ Name of the pipeline job.
138
+ description : str, optional
139
+ Description of the pipeline job.
140
+ """
141
+
142
+ col_values = [[] for _ in args.params]
143
+ for row in args.rows:
144
+ for i, v in enumerate(row.row_values):
145
+ col_values[i].append(v)
146
+
147
+ cols = []
148
+
149
+ for i, param in enumerate(args.params):
150
+ if param._represented_type() == str:
151
+ cols.append(
152
+ pipelinespb.PipelineJobArgsColumn(
153
+ string_column=pipelinespb.PipelineJobArgsColumn.StringColumn(
154
+ name=param.name,
155
+ values=col_values[i],
156
+ )
157
+ )
158
+ )
159
+ elif param._represented_type() == int:
160
+ cols.append(
161
+ pipelinespb.PipelineJobArgsColumn(
162
+ int_column=pipelinespb.PipelineJobArgsColumn.IntColumn(
163
+ name=param.name,
164
+ values=col_values[i],
165
+ )
166
+ )
167
+ )
168
+ elif param._represented_type() == float:
169
+ cols.append(
170
+ pipelinespb.PipelineJobArgsColumn(
171
+ double_column=pipelinespb.PipelineJobArgsColumn.DoubleColumn(
172
+ name=param.name,
173
+ values=col_values[i],
174
+ )
175
+ )
176
+ )
177
+ elif param._represented_type() == bool:
178
+ cols.append(
179
+ pipelinespb.PipelineJobArgsColumn(
180
+ bool_column=pipelinespb.PipelineJobArgsColumn.BoolColumn(
181
+ name=param.name,
182
+ values=col_values[i],
183
+ )
184
+ )
185
+ )
186
+
187
+ req = pipelinespb.CreatePipelineJobRequest(
188
+ pipeline_id=pipeline_id,
189
+ args_columns=cols,
190
+ name=name,
191
+ description=description,
192
+ project_id=project_id,
193
+ )
194
+ res: pipelinespb.CreatePipelineJobResponse = get_default_client().CreatePipelineJob(req)
195
+ return PipelineJobRecord.from_proto(res.pipeline_job)
196
+
197
+
198
+ def get_pipeline_job(id: str) -> PipelineJobRecord:
199
+ """
200
+ Get a pipeline job by ID.
201
+ """
202
+ req = pipelinespb.GetPipelineJobRequest(id=id)
203
+ res: pipelinespb.GetPipelineJobResponse = get_default_client().GetPipelineJob(req)
204
+ return PipelineJobRecord.from_proto(res.pipeline_job)
205
+
206
+
207
+ def list_pipeline_jobs() -> list[PipelineJobRecord]:
208
+ """
209
+ List all pipeline jobs.
210
+ """
211
+ req = pipelinespb.ListPipelineJobsRequest()
212
+ res: pipelinespb.ListPipelineJobsResponse = get_default_client().ListPipelineJobs(req)
213
+ return [PipelineJobRecord.from_proto(p) for p in res.pipeline_jobs]