luminarycloud 0.21.2__py3-none-any.whl → 0.22.1__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.
- luminarycloud/__init__.py +3 -1
- luminarycloud/_client/authentication_plugin.py +49 -0
- luminarycloud/_client/client.py +38 -8
- luminarycloud/_client/http_client.py +1 -1
- luminarycloud/_client/retry_interceptor.py +64 -2
- luminarycloud/_feature_flag.py +22 -0
- luminarycloud/_helpers/_create_simulation.py +7 -2
- luminarycloud/_helpers/download.py +11 -0
- luminarycloud/_helpers/proto_decorator.py +13 -5
- luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2.py +55 -0
- luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2.pyi +52 -0
- luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2_grpc.py +72 -0
- luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2_grpc.pyi +35 -0
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +132 -132
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +36 -8
- luminarycloud/_proto/api/v0/luminarycloud/mesh/mesh_pb2.py +74 -73
- luminarycloud/_proto/api/v0/luminarycloud/mesh/mesh_pb2.pyi +8 -1
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +53 -23
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +54 -1
- luminarycloud/_proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference_pb2.py +195 -0
- luminarycloud/_proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference_pb2.pyi +361 -0
- luminarycloud/_proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference_pb2_grpc.py +172 -0
- luminarycloud/_proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference_pb2_grpc.pyi +66 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.py +97 -61
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.pyi +68 -3
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.py +34 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.py +33 -31
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.pyi +23 -2
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.py +88 -65
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.pyi +42 -0
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.py +34 -0
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/base/base_pb2.py +7 -6
- luminarycloud/_proto/base/base_pb2.pyi +4 -0
- luminarycloud/_proto/cad/shape_pb2.py +39 -19
- luminarycloud/_proto/cad/shape_pb2.pyi +86 -34
- luminarycloud/_proto/client/simulation_pb2.py +3 -3
- luminarycloud/_proto/geometry/geometry_pb2.py +77 -63
- luminarycloud/_proto/geometry/geometry_pb2.pyi +42 -3
- luminarycloud/_proto/hexmesh/hexmesh_pb2.py +22 -18
- luminarycloud/_proto/hexmesh/hexmesh_pb2.pyi +18 -2
- luminarycloud/_proto/physicsaiinferenceservice/physicsaiinferenceservice_pb2.py +30 -0
- luminarycloud/_proto/physicsaiinferenceservice/physicsaiinferenceservice_pb2.pyi +7 -0
- luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2.py +2 -2
- luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.py +34 -0
- luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.pyi +12 -0
- luminarycloud/enum/vis_enums.py +6 -0
- luminarycloud/feature_modification.py +32 -1
- luminarycloud/geometry.py +67 -7
- luminarycloud/geometry_version.py +4 -0
- luminarycloud/mesh.py +4 -0
- luminarycloud/meshing/mesh_generation_params.py +13 -14
- luminarycloud/meshing/sizing_strategy/sizing_strategies.py +1 -2
- luminarycloud/physics_ai/solution.py +4 -0
- luminarycloud/pipelines/api.py +99 -8
- luminarycloud/pipelines/core.py +12 -2
- luminarycloud/pipelines/stages.py +22 -9
- luminarycloud/project.py +5 -8
- luminarycloud/simulation.py +57 -0
- luminarycloud/types/vector3.py +1 -2
- luminarycloud/vis/data_extraction.py +7 -7
- luminarycloud/vis/interactive_report.py +163 -7
- luminarycloud/vis/report.py +113 -1
- luminarycloud/volume_selection.py +71 -7
- {luminarycloud-0.21.2.dist-info → luminarycloud-0.22.1.dist-info}/METADATA +1 -1
- {luminarycloud-0.21.2.dist-info → luminarycloud-0.22.1.dist-info}/RECORD +68 -58
- {luminarycloud-0.21.2.dist-info → luminarycloud-0.22.1.dist-info}/WHEEL +1 -1
- luminarycloud/pipeline_util/dictable.py +0 -27
luminarycloud/__init__.py
CHANGED
|
@@ -13,12 +13,14 @@ from . import (
|
|
|
13
13
|
types as types,
|
|
14
14
|
vis as vis,
|
|
15
15
|
)
|
|
16
|
-
|
|
17
16
|
from ._client import (
|
|
18
17
|
Client as Client,
|
|
19
18
|
get_default_client as get_default_client,
|
|
20
19
|
set_default_client as set_default_client,
|
|
21
20
|
)
|
|
21
|
+
from ._feature_flag import (
|
|
22
|
+
_get_feature_flags as _get_feature_flags,
|
|
23
|
+
)
|
|
22
24
|
from .geometry import (
|
|
23
25
|
get_geometry as get_geometry,
|
|
24
26
|
Geometry as Geometry,
|
|
@@ -38,3 +38,52 @@ class AuthenticationPlugin(grpc.AuthMetadataPlugin):
|
|
|
38
38
|
callback(metadata, None)
|
|
39
39
|
except Exception as err:
|
|
40
40
|
callback(None, err)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AuthInterceptor(
|
|
44
|
+
grpc.UnaryUnaryClientInterceptor,
|
|
45
|
+
grpc.UnaryStreamClientInterceptor,
|
|
46
|
+
grpc.StreamUnaryClientInterceptor,
|
|
47
|
+
grpc.StreamStreamClientInterceptor,
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
I need this as a workaround for container-to-host connections because I need to create a channel
|
|
51
|
+
that uses CallCredentials but doesn't use any ChannelCredentials. I.e. I need to authenticate
|
|
52
|
+
the requests, but I need the connection to be unencrypted. This is because the grpc server on
|
|
53
|
+
the native host isn't using SSL, so I can't use grpc.ssl_channel_credentials(), but it's also
|
|
54
|
+
not reachable on a loopback interface, so I can't use grpc.local_channel_credentials() either.
|
|
55
|
+
So I need to use a grpc.insecure_channel(), but you can't use CallCredentials with an insecure
|
|
56
|
+
channel. So the workaround is to use an interceptor instead of CallCredentials.
|
|
57
|
+
|
|
58
|
+
Also, I don't care about auth0, so I'm only supporting an API key.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, api_key: str):
|
|
62
|
+
self._api_key = api_key
|
|
63
|
+
|
|
64
|
+
def _augment(self, metadata):
|
|
65
|
+
return metadata + [("x-api-key", self._api_key)]
|
|
66
|
+
|
|
67
|
+
def intercept_unary_unary(self, continuation, client_call_details, request):
|
|
68
|
+
new_details = client_call_details._replace(
|
|
69
|
+
metadata=self._augment(client_call_details.metadata or [])
|
|
70
|
+
)
|
|
71
|
+
return continuation(new_details, request)
|
|
72
|
+
|
|
73
|
+
def intercept_unary_stream(self, continuation, client_call_details, request):
|
|
74
|
+
new_details = client_call_details._replace(
|
|
75
|
+
metadata=self._augment(client_call_details.metadata or [])
|
|
76
|
+
)
|
|
77
|
+
return continuation(new_details, request)
|
|
78
|
+
|
|
79
|
+
def intercept_stream_unary(self, continuation, client_call_details, request_iter):
|
|
80
|
+
new_details = client_call_details._replace(
|
|
81
|
+
metadata=self._augment(client_call_details.metadata or [])
|
|
82
|
+
)
|
|
83
|
+
return continuation(new_details, request_iter)
|
|
84
|
+
|
|
85
|
+
def intercept_stream_stream(self, continuation, client_call_details, request_iter):
|
|
86
|
+
new_details = client_call_details._replace(
|
|
87
|
+
metadata=self._augment(client_call_details.metadata or [])
|
|
88
|
+
)
|
|
89
|
+
return continuation(new_details, request_iter)
|
luminarycloud/_client/client.py
CHANGED
|
@@ -44,7 +44,10 @@ from .._proto.api.v0.luminarycloud.project_ui_state.project_ui_state_pb2_grpc im
|
|
|
44
44
|
from .._proto.api.v0.luminarycloud.solution.solution_pb2_grpc import SolutionServiceStub
|
|
45
45
|
from .._proto.api.v0.luminarycloud.upload.upload_pb2_grpc import UploadServiceStub
|
|
46
46
|
from .._proto.api.v0.luminarycloud.vis.vis_pb2_grpc import VisAPIServiceStub
|
|
47
|
-
from .
|
|
47
|
+
from .._proto.api.v0.luminarycloud.feature_flag.feature_flag_pb2_grpc import (
|
|
48
|
+
FeatureFlagServiceStub,
|
|
49
|
+
)
|
|
50
|
+
from .authentication_plugin import AuthenticationPlugin, AuthInterceptor
|
|
48
51
|
from .config import LC_DOMAIN, LC_API_KEY
|
|
49
52
|
from .logging_interceptor import LoggingInterceptor
|
|
50
53
|
from .retry_interceptor import RetryInterceptor
|
|
@@ -71,6 +74,7 @@ class Client(
|
|
|
71
74
|
InferenceServiceStub,
|
|
72
75
|
OnshapeServiceStub,
|
|
73
76
|
ProjectUIStateServiceStub,
|
|
77
|
+
FeatureFlagServiceStub,
|
|
74
78
|
):
|
|
75
79
|
"""
|
|
76
80
|
Creates a Luminary API client.
|
|
@@ -89,11 +93,20 @@ class Client(
|
|
|
89
93
|
The URL of the HTTP REST server. If not provided, it will default to the `target`.
|
|
90
94
|
localhost : bool
|
|
91
95
|
True if the API server is running locally.
|
|
96
|
+
insecure_grpc_channel : bool
|
|
97
|
+
True to use an unencrypted gRPC channel, even though requests are authenticated. There's no
|
|
98
|
+
legitimate reason to do this outside of a local development situation where the SDK is
|
|
99
|
+
running from a container and connecting to an API server that is running on the host.
|
|
92
100
|
grpc_channel_options : Optional[Iterable[tuple[str, str]]]
|
|
93
101
|
A list of gRPC channel args. The full list is available here:
|
|
94
102
|
https://github.com/grpc/grpc/blob/v1.46.x/include/grpc/impl/codegen/grpc_types.h
|
|
95
103
|
api_key : Optional[str]
|
|
96
104
|
The API key to use for authentication.
|
|
105
|
+
log_retries : bool
|
|
106
|
+
True to log each retriable error response. There are some errors the API server may return
|
|
107
|
+
that are known to be transient, and the client will always retry requests when it gets one
|
|
108
|
+
of them. By default, the client retries silently. Set this to True to log the error
|
|
109
|
+
responses (at INFO level) for the retriable errors.
|
|
97
110
|
**kwargs : dict, optional
|
|
98
111
|
Additional arguments are passed to Auth0Client. See _auth/auth.py.
|
|
99
112
|
|
|
@@ -112,9 +125,11 @@ class Client(
|
|
|
112
125
|
target: str = LC_DOMAIN,
|
|
113
126
|
http_target: str | None = None,
|
|
114
127
|
localhost: bool = False,
|
|
128
|
+
insecure_grpc_channel: bool = False,
|
|
115
129
|
grpc_channel_options: Optional[Iterable[tuple[str, Union[str, int]]]] = None,
|
|
116
130
|
channel_credentials: Optional[grpc.ChannelCredentials] = None,
|
|
117
131
|
api_key: Optional[str] = LC_API_KEY,
|
|
132
|
+
log_retries: bool = False,
|
|
118
133
|
**kwargs: Any,
|
|
119
134
|
):
|
|
120
135
|
self._target = target
|
|
@@ -135,7 +150,12 @@ class Client(
|
|
|
135
150
|
if grpc_channel_options:
|
|
136
151
|
grpc_channel_options_with_keep_alive.extend(grpc_channel_options)
|
|
137
152
|
self._channel = self._create_channel(
|
|
138
|
-
localhost,
|
|
153
|
+
localhost,
|
|
154
|
+
insecure_grpc_channel,
|
|
155
|
+
grpc_channel_options_with_keep_alive,
|
|
156
|
+
channel_credentials,
|
|
157
|
+
api_key,
|
|
158
|
+
log_retries,
|
|
139
159
|
)
|
|
140
160
|
self._context_tokens: list[Token] = []
|
|
141
161
|
self.__register_rpcs()
|
|
@@ -189,9 +209,11 @@ class Client(
|
|
|
189
209
|
def _create_channel(
|
|
190
210
|
self,
|
|
191
211
|
localhost: bool = False,
|
|
212
|
+
insecure: bool = False,
|
|
192
213
|
grpc_channel_options: Optional[Iterable[tuple[str, Union[str, int]]]] = None,
|
|
193
214
|
channel_credentials: Optional[grpc.ChannelCredentials] = None,
|
|
194
215
|
api_key: Optional[str] = None,
|
|
216
|
+
log_retries: bool = False,
|
|
195
217
|
) -> grpc.Channel:
|
|
196
218
|
if channel_credentials is None:
|
|
197
219
|
if localhost:
|
|
@@ -213,15 +235,22 @@ class Client(
|
|
|
213
235
|
call_creds = grpc.metadata_call_credentials(auth_plugin)
|
|
214
236
|
composite_creds = grpc.composite_channel_credentials(channel_credentials, call_creds)
|
|
215
237
|
options = grpc_channel_options and list(grpc_channel_options)
|
|
216
|
-
|
|
217
|
-
self._target,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
238
|
+
if insecure:
|
|
239
|
+
channel = grpc.insecure_channel(self._target, options=options)
|
|
240
|
+
channel = grpc.intercept_channel(
|
|
241
|
+
channel,
|
|
242
|
+
AuthInterceptor(api_key),
|
|
243
|
+
)
|
|
244
|
+
else:
|
|
245
|
+
channel = grpc.secure_channel(
|
|
246
|
+
self._target,
|
|
247
|
+
composite_creds,
|
|
248
|
+
options=options,
|
|
249
|
+
)
|
|
221
250
|
intercepted_channel = grpc.intercept_channel(
|
|
222
251
|
channel,
|
|
223
252
|
LoggingInterceptor(),
|
|
224
|
-
RetryInterceptor(),
|
|
253
|
+
RetryInterceptor(log_retries),
|
|
225
254
|
)
|
|
226
255
|
return add_instrumentation(
|
|
227
256
|
intercepted_channel,
|
|
@@ -248,6 +277,7 @@ class Client(
|
|
|
248
277
|
NamedVariableSetServiceStub.__init__(self, self._channel)
|
|
249
278
|
OnshapeServiceStub.__init__(self, self._channel)
|
|
250
279
|
ProjectUIStateServiceStub.__init__(self, self._channel)
|
|
280
|
+
FeatureFlagServiceStub.__init__(self, self._channel)
|
|
251
281
|
for name, value in self.__dict__.items():
|
|
252
282
|
if isinstance(value, grpc.UnaryUnaryMultiCallable):
|
|
253
283
|
setattr(self, name, rpc_error(value))
|
|
@@ -32,7 +32,7 @@ class HttpClient:
|
|
|
32
32
|
api_key: str | None = None,
|
|
33
33
|
auth0_client: Auth0Client | None = None,
|
|
34
34
|
*,
|
|
35
|
-
timeout: int =
|
|
35
|
+
timeout: int | None = 300,
|
|
36
36
|
retries: int = 3,
|
|
37
37
|
backoff_factor: float = 0.3,
|
|
38
38
|
retriable_status_codes: tuple = (500, 502, 503, 504, 429),
|
|
@@ -2,19 +2,68 @@
|
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from time import sleep
|
|
4
4
|
from typing import Any
|
|
5
|
+
import logging
|
|
5
6
|
|
|
6
7
|
import grpc
|
|
7
8
|
from grpc import (
|
|
8
9
|
ClientCallDetails,
|
|
9
10
|
UnaryUnaryClientInterceptor,
|
|
10
11
|
)
|
|
12
|
+
from grpc_status.rpc_status import GRPC_DETAILS_METADATA_KEY
|
|
11
13
|
|
|
12
14
|
from luminarycloud.exceptions import AuthenticationError
|
|
15
|
+
from luminarycloud._proto.base import base_pb2
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _is_rate_limited(call: grpc.Call) -> bool:
|
|
19
|
+
"""
|
|
20
|
+
Check if a gRPC call failed due to rate limiting.
|
|
21
|
+
|
|
22
|
+
Rate limit errors are identified with the SUBCODE_RATE_LIMITED subcode and UNAVAILABLE status.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
call: The gRPC call to check
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
True if the error is a rate limit error, False otherwise
|
|
29
|
+
"""
|
|
30
|
+
if call.code() != grpc.StatusCode.UNAVAILABLE:
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
# Get the trailing metadata which contains error details
|
|
35
|
+
# Metadata is a sequence of tuples (key, value)
|
|
36
|
+
for key, value in call.trailing_metadata() or []:
|
|
37
|
+
if key != GRPC_DETAILS_METADATA_KEY or not isinstance(value, bytes):
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
status = base_pb2.Status()
|
|
41
|
+
status.ParseFromString(value)
|
|
42
|
+
for any_detail in status.details:
|
|
43
|
+
if any_detail.Is(base_pb2.StatusPayload.DESCRIPTOR):
|
|
44
|
+
payload = base_pb2.StatusPayload()
|
|
45
|
+
any_detail.Unpack(payload)
|
|
46
|
+
if payload.subcode == base_pb2.SUBCODE_RATE_LIMITED:
|
|
47
|
+
return True
|
|
48
|
+
except Exception:
|
|
49
|
+
pass
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
logger = logging.getLogger(__name__)
|
|
13
54
|
|
|
14
55
|
|
|
15
56
|
class RetryInterceptor(UnaryUnaryClientInterceptor):
|
|
57
|
+
def __init__(self, log_retries: bool = False):
|
|
58
|
+
self.log_retries = log_retries
|
|
59
|
+
super().__init__()
|
|
60
|
+
|
|
16
61
|
"""
|
|
17
|
-
A retry interceptor that retries on
|
|
62
|
+
A retry interceptor that retries on rate limit errors and other retryable errors.
|
|
63
|
+
|
|
64
|
+
This interceptor handles:
|
|
65
|
+
1. Rate limit errors (UNAVAILABLE with SUBCODE_RATE_LIMITED) - always retried
|
|
66
|
+
2. [grpc.StatusCode.RESOURCE_EXHAUSTED, grpc.StatusCode.UNAVAILABLE] - retried
|
|
18
67
|
|
|
19
68
|
This is required because, while the retry policy for the gRPC client is configurable via
|
|
20
69
|
https://github.com/grpc/grpc-proto/blob/master/grpc/service_config/service_config.proto,
|
|
@@ -43,7 +92,10 @@ class RetryInterceptor(UnaryUnaryClientInterceptor):
|
|
|
43
92
|
n_max_retries = 20
|
|
44
93
|
max_retry_seconds = 20
|
|
45
94
|
backoffs = [min(i * 2, max_retry_seconds) for i in range(1, n_max_retries)]
|
|
46
|
-
|
|
95
|
+
backoff_index = 0
|
|
96
|
+
while True:
|
|
97
|
+
if backoff_index >= len(backoffs):
|
|
98
|
+
break
|
|
47
99
|
call = continuation(client_call_details, request)
|
|
48
100
|
if call.code() not in retryable_codes:
|
|
49
101
|
break
|
|
@@ -54,7 +106,17 @@ class RetryInterceptor(UnaryUnaryClientInterceptor):
|
|
|
54
106
|
details = call.details() or ""
|
|
55
107
|
if "InteractiveAuthException" in details:
|
|
56
108
|
break
|
|
109
|
+
backoff = backoffs[backoff_index]
|
|
110
|
+
if self.log_retries:
|
|
111
|
+
logger.info(
|
|
112
|
+
f"Retrying {client_call_details.method} in {backoff} seconds (last response: {call.code()}, {call.details()})"
|
|
113
|
+
)
|
|
57
114
|
sleep(backoff)
|
|
115
|
+
# Keep retrying rate-limited calls while increasing the backoff up to the max.
|
|
116
|
+
backoff_index += 1
|
|
117
|
+
if _is_rate_limited(call):
|
|
118
|
+
backoff_index = max(min(backoff_index, len(backoffs) - 2), 0)
|
|
119
|
+
|
|
58
120
|
try:
|
|
59
121
|
call.result()
|
|
60
122
|
except grpc.RpcError as e:
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright 2023-2025 Luminary Cloud, Inc. All Rights Reserved.
|
|
2
|
+
from google.protobuf import empty_pb2
|
|
3
|
+
|
|
4
|
+
from ._proto.api.v0.luminarycloud.feature_flag import feature_flag_pb2
|
|
5
|
+
from ._client import get_default_client
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _get_feature_flags() -> dict[int, str]:
|
|
9
|
+
"""Get enabled feature flags for the authenticated user.
|
|
10
|
+
|
|
11
|
+
Feature flags are used to control access to experimental and internal features.
|
|
12
|
+
Each flag is identified by an experiment ID and a feature flag name.
|
|
13
|
+
|
|
14
|
+
Returns
|
|
15
|
+
-------
|
|
16
|
+
dict[int, str]
|
|
17
|
+
A dictionary mapping experiment IDs (uint64) to feature flag names.
|
|
18
|
+
Returns an empty dictionary if no feature flags are enabled for the user.
|
|
19
|
+
"""
|
|
20
|
+
req = empty_pb2.Empty()
|
|
21
|
+
res = get_default_client().GetFeatureFlags(req)
|
|
22
|
+
return dict(res.enabled_feature_flags)
|
|
@@ -7,6 +7,9 @@ from .._proto.api.v0.luminarycloud.simulation.simulation_pb2 import (
|
|
|
7
7
|
SimulationOptions,
|
|
8
8
|
CreateSimulationRequest,
|
|
9
9
|
)
|
|
10
|
+
from .._proto.api.v0.luminarycloud.simulation_template.simulation_template_pb2 import (
|
|
11
|
+
SimulationTemplate,
|
|
12
|
+
)
|
|
10
13
|
from .._client import Client
|
|
11
14
|
from ..enum import GPUType
|
|
12
15
|
|
|
@@ -18,8 +21,9 @@ def create_simulation(
|
|
|
18
21
|
project_id: str,
|
|
19
22
|
mesh_id: str,
|
|
20
23
|
name: str,
|
|
21
|
-
simulation_template_id: str,
|
|
24
|
+
simulation_template_id: Optional[str] = None,
|
|
22
25
|
*,
|
|
26
|
+
simulation_template: Optional[SimulationTemplate] = None,
|
|
23
27
|
named_variable_set_version_id: Optional[str] = None,
|
|
24
28
|
description: str = "",
|
|
25
29
|
batch_processing: bool = True,
|
|
@@ -32,7 +36,8 @@ def create_simulation(
|
|
|
32
36
|
CreateSimulationRequest(
|
|
33
37
|
project_id=project_id,
|
|
34
38
|
mesh_id=mesh_id,
|
|
35
|
-
simulation_template_id=simulation_template_id,
|
|
39
|
+
simulation_template_id=simulation_template_id or "",
|
|
40
|
+
simulation_template=simulation_template,
|
|
36
41
|
named_variable_set_version_id=named_variable_set_version_id or "",
|
|
37
42
|
name=name,
|
|
38
43
|
description=description,
|
|
@@ -17,6 +17,7 @@ from .._proto.api.v0.luminarycloud.solution.solution_pb2 import (
|
|
|
17
17
|
)
|
|
18
18
|
from .._proto.api.v0.luminarycloud.physics_ai.physics_ai_pb2 import (
|
|
19
19
|
GetSolutionDataPhysicsAIRequest,
|
|
20
|
+
SurfaceGroup,
|
|
20
21
|
)
|
|
21
22
|
from .._client import Client
|
|
22
23
|
from ..enum.quantity_type import QuantityType
|
|
@@ -140,6 +141,7 @@ def download_solution_physics_ai(
|
|
|
140
141
|
process_volume: bool = False,
|
|
141
142
|
single_precision: bool = False,
|
|
142
143
|
internal_options: Optional[Dict[str, str]] = None,
|
|
144
|
+
export_surface_groups: Optional[Dict[str, List[str]]] = None,
|
|
143
145
|
) -> Optional[FileChunkStream]:
|
|
144
146
|
"""
|
|
145
147
|
Returns the download as a file-like object, or None if destination_url is provided.
|
|
@@ -165,6 +167,9 @@ def download_solution_physics_ai(
|
|
|
165
167
|
Whether to process volume meshes during physics AI processing.
|
|
166
168
|
single_precision: bool
|
|
167
169
|
If True, the solution will be downloaded in single precision.
|
|
170
|
+
export_surface_groups: Optional[Dict[str, List[str]]]
|
|
171
|
+
Dictionary mapping group names to lists of surface names.
|
|
172
|
+
Each group will be exported as an individual STL file.
|
|
168
173
|
|
|
169
174
|
Examples
|
|
170
175
|
--------
|
|
@@ -173,6 +178,11 @@ def download_solution_physics_ai(
|
|
|
173
178
|
... fp.write(dl.read())
|
|
174
179
|
"""
|
|
175
180
|
|
|
181
|
+
surface_groups_pb = []
|
|
182
|
+
if export_surface_groups:
|
|
183
|
+
for group_name, surfaces in export_surface_groups.items():
|
|
184
|
+
surface_groups_pb.append(SurfaceGroup(name=group_name, surfaces=surfaces))
|
|
185
|
+
|
|
176
186
|
request = GetSolutionDataPhysicsAIRequest(
|
|
177
187
|
solution_id=solution_id,
|
|
178
188
|
exclude_surfaces=exclude_surfaces or [],
|
|
@@ -186,6 +196,7 @@ def download_solution_physics_ai(
|
|
|
186
196
|
process_volume=process_volume,
|
|
187
197
|
single_precision=single_precision,
|
|
188
198
|
internal_options=internal_options or {},
|
|
199
|
+
export_surface_groups=surface_groups_pb,
|
|
189
200
|
)
|
|
190
201
|
response = client.GetSolutionDataPhysicsAI(request)
|
|
191
202
|
|
|
@@ -9,6 +9,9 @@ from typing import (
|
|
|
9
9
|
from google.protobuf.message import Message
|
|
10
10
|
|
|
11
11
|
from luminarycloud.types import Vector3
|
|
12
|
+
from luminarycloud.types.adfloat import _to_ad_proto
|
|
13
|
+
from luminarycloud.types.vector3 import _to_vector3_ad_proto
|
|
14
|
+
from .._proto.base.base_pb2 import AdFloatType, AdVector3
|
|
12
15
|
|
|
13
16
|
P = TypeVar("P", bound=Message)
|
|
14
17
|
C = TypeVar("C")
|
|
@@ -36,11 +39,16 @@ class proto_decorator(Generic[P]):
|
|
|
36
39
|
_type = type_hints.get(field.name, None)
|
|
37
40
|
if _type:
|
|
38
41
|
value = getattr(self, field.name)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
proto_value = getattr(proto, field.name)
|
|
43
|
+
if issubclass(_type, float) and isinstance(proto_value, AdFloatType):
|
|
44
|
+
proto_value.CopyFrom(_to_ad_proto(value))
|
|
45
|
+
elif issubclass(_type, Vector3):
|
|
46
|
+
if isinstance(proto_value, AdVector3):
|
|
47
|
+
proto_value.CopyFrom(_to_vector3_ad_proto((value.x, value.y, value.z)))
|
|
48
|
+
else:
|
|
49
|
+
proto_value.x = value.x
|
|
50
|
+
proto_value.y = value.y
|
|
51
|
+
proto_value.z = value.z
|
|
44
52
|
else:
|
|
45
53
|
setattr(proto, field.name, value)
|
|
46
54
|
return proto
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: proto/api/v0/luminarycloud/feature_flag/feature_flag.proto
|
|
4
|
+
"""Generated protocol buffer code."""
|
|
5
|
+
from google.protobuf import descriptor as _descriptor
|
|
6
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
7
|
+
from google.protobuf import message as _message
|
|
8
|
+
from google.protobuf import reflection as _reflection
|
|
9
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
10
|
+
# @@protoc_insertion_point(imports)
|
|
11
|
+
|
|
12
|
+
_sym_db = _symbol_database.Default()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2
|
|
16
|
+
from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n:proto/api/v0/luminarycloud/feature_flag/feature_flag.proto\x12\x30luminary.proto.api.v0.luminarycloud.feature_flag\x1a\x1cgoogle/api/annotations.proto\x1a\x1bgoogle/protobuf/empty.proto\"\xd9\x01\n\x17GetFeatureFlagsResponse\x12\x81\x01\n\x15\x65nabled_feature_flags\x18\x01 \x03(\x0b\x32\x62.luminary.proto.api.v0.luminarycloud.feature_flag.GetFeatureFlagsResponse.EnabledFeatureFlagsEntry\x1a:\n\x18\x45nabledFeatureFlagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x32\xa6\x01\n\x12\x46\x65\x61tureFlagService\x12\x8f\x01\n\x0fGetFeatureFlags\x12\x16.google.protobuf.Empty\x1aI.luminary.proto.api.v0.luminarycloud.feature_flag.GetFeatureFlagsResponse\"\x19\x82\xd3\xe4\x93\x02\x13\x12\x11/v0/feature_flagsB@Z>luminarycloud.com/core/proto/api/v0/luminarycloud/feature_flagb\x06proto3')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_GETFEATUREFLAGSRESPONSE = DESCRIPTOR.message_types_by_name['GetFeatureFlagsResponse']
|
|
24
|
+
_GETFEATUREFLAGSRESPONSE_ENABLEDFEATUREFLAGSENTRY = _GETFEATUREFLAGSRESPONSE.nested_types_by_name['EnabledFeatureFlagsEntry']
|
|
25
|
+
GetFeatureFlagsResponse = _reflection.GeneratedProtocolMessageType('GetFeatureFlagsResponse', (_message.Message,), {
|
|
26
|
+
|
|
27
|
+
'EnabledFeatureFlagsEntry' : _reflection.GeneratedProtocolMessageType('EnabledFeatureFlagsEntry', (_message.Message,), {
|
|
28
|
+
'DESCRIPTOR' : _GETFEATUREFLAGSRESPONSE_ENABLEDFEATUREFLAGSENTRY,
|
|
29
|
+
'__module__' : 'proto.api.v0.luminarycloud.feature_flag.feature_flag_pb2'
|
|
30
|
+
# @@protoc_insertion_point(class_scope:luminary.proto.api.v0.luminarycloud.feature_flag.GetFeatureFlagsResponse.EnabledFeatureFlagsEntry)
|
|
31
|
+
})
|
|
32
|
+
,
|
|
33
|
+
'DESCRIPTOR' : _GETFEATUREFLAGSRESPONSE,
|
|
34
|
+
'__module__' : 'proto.api.v0.luminarycloud.feature_flag.feature_flag_pb2'
|
|
35
|
+
# @@protoc_insertion_point(class_scope:luminary.proto.api.v0.luminarycloud.feature_flag.GetFeatureFlagsResponse)
|
|
36
|
+
})
|
|
37
|
+
_sym_db.RegisterMessage(GetFeatureFlagsResponse)
|
|
38
|
+
_sym_db.RegisterMessage(GetFeatureFlagsResponse.EnabledFeatureFlagsEntry)
|
|
39
|
+
|
|
40
|
+
_FEATUREFLAGSERVICE = DESCRIPTOR.services_by_name['FeatureFlagService']
|
|
41
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
42
|
+
|
|
43
|
+
DESCRIPTOR._options = None
|
|
44
|
+
DESCRIPTOR._serialized_options = b'Z>luminarycloud.com/core/proto/api/v0/luminarycloud/feature_flag'
|
|
45
|
+
_GETFEATUREFLAGSRESPONSE_ENABLEDFEATUREFLAGSENTRY._options = None
|
|
46
|
+
_GETFEATUREFLAGSRESPONSE_ENABLEDFEATUREFLAGSENTRY._serialized_options = b'8\001'
|
|
47
|
+
_FEATUREFLAGSERVICE.methods_by_name['GetFeatureFlags']._options = None
|
|
48
|
+
_FEATUREFLAGSERVICE.methods_by_name['GetFeatureFlags']._serialized_options = b'\202\323\344\223\002\023\022\021/v0/feature_flags'
|
|
49
|
+
_GETFEATUREFLAGSRESPONSE._serialized_start=172
|
|
50
|
+
_GETFEATUREFLAGSRESPONSE._serialized_end=389
|
|
51
|
+
_GETFEATUREFLAGSRESPONSE_ENABLEDFEATUREFLAGSENTRY._serialized_start=331
|
|
52
|
+
_GETFEATUREFLAGSRESPONSE_ENABLEDFEATUREFLAGSENTRY._serialized_end=389
|
|
53
|
+
_FEATUREFLAGSERVICE._serialized_start=392
|
|
54
|
+
_FEATUREFLAGSERVICE._serialized_end=558
|
|
55
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@generated by mypy-protobuf. Do not edit manually!
|
|
3
|
+
isort:skip_file
|
|
4
|
+
"""
|
|
5
|
+
import builtins
|
|
6
|
+
import collections.abc
|
|
7
|
+
import google.protobuf.descriptor
|
|
8
|
+
import google.protobuf.internal.containers
|
|
9
|
+
import google.protobuf.message
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
if sys.version_info >= (3, 8):
|
|
13
|
+
import typing as typing_extensions
|
|
14
|
+
else:
|
|
15
|
+
import typing_extensions
|
|
16
|
+
|
|
17
|
+
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
|
18
|
+
|
|
19
|
+
class GetFeatureFlagsResponse(google.protobuf.message.Message):
|
|
20
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
21
|
+
|
|
22
|
+
class EnabledFeatureFlagsEntry(google.protobuf.message.Message):
|
|
23
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
24
|
+
|
|
25
|
+
KEY_FIELD_NUMBER: builtins.int
|
|
26
|
+
VALUE_FIELD_NUMBER: builtins.int
|
|
27
|
+
key: builtins.int
|
|
28
|
+
value: builtins.str
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
*,
|
|
32
|
+
key: builtins.int = ...,
|
|
33
|
+
value: builtins.str = ...,
|
|
34
|
+
) -> None: ...
|
|
35
|
+
def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ...
|
|
36
|
+
|
|
37
|
+
ENABLED_FEATURE_FLAGS_FIELD_NUMBER: builtins.int
|
|
38
|
+
@property
|
|
39
|
+
def enabled_feature_flags(self) -> google.protobuf.internal.containers.ScalarMap[builtins.int, builtins.str]:
|
|
40
|
+
"""Map of enabled feature flags where:
|
|
41
|
+
- Key: experiment ID (uint64)
|
|
42
|
+
- Value: feature flag name (string)
|
|
43
|
+
Returns an empty map if no feature flags are enabled for the user.
|
|
44
|
+
"""
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
*,
|
|
48
|
+
enabled_feature_flags: collections.abc.Mapping[builtins.int, builtins.str] | None = ...,
|
|
49
|
+
) -> None: ...
|
|
50
|
+
def ClearField(self, field_name: typing_extensions.Literal["enabled_feature_flags", b"enabled_feature_flags"]) -> None: ...
|
|
51
|
+
|
|
52
|
+
global___GetFeatureFlagsResponse = GetFeatureFlagsResponse
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
|
2
|
+
"""Client and server classes corresponding to protobuf-defined services."""
|
|
3
|
+
import grpc
|
|
4
|
+
|
|
5
|
+
from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
|
|
6
|
+
from luminarycloud._proto.api.v0.luminarycloud.feature_flag import feature_flag_pb2 as proto_dot_api_dot_v0_dot_luminarycloud_dot_feature__flag_dot_feature__flag__pb2
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FeatureFlagServiceStub(object):
|
|
10
|
+
"""Manages feature flags for internal users.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, channel):
|
|
14
|
+
"""Constructor.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
channel: A grpc.Channel.
|
|
18
|
+
"""
|
|
19
|
+
self.GetFeatureFlags = channel.unary_unary(
|
|
20
|
+
'/luminary.proto.api.v0.luminarycloud.feature_flag.FeatureFlagService/GetFeatureFlags',
|
|
21
|
+
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
|
|
22
|
+
response_deserializer=proto_dot_api_dot_v0_dot_luminarycloud_dot_feature__flag_dot_feature__flag__pb2.GetFeatureFlagsResponse.FromString,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FeatureFlagServiceServicer(object):
|
|
27
|
+
"""Manages feature flags for internal users.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def GetFeatureFlags(self, request, context):
|
|
31
|
+
"""Gets all enabled feature flags for the authenticated user.
|
|
32
|
+
Feature flags are used to control access to experimental and internal features.
|
|
33
|
+
"""
|
|
34
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
35
|
+
context.set_details('Method not implemented!')
|
|
36
|
+
raise NotImplementedError('Method not implemented!')
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def add_FeatureFlagServiceServicer_to_server(servicer, server):
|
|
40
|
+
rpc_method_handlers = {
|
|
41
|
+
'GetFeatureFlags': grpc.unary_unary_rpc_method_handler(
|
|
42
|
+
servicer.GetFeatureFlags,
|
|
43
|
+
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
|
|
44
|
+
response_serializer=proto_dot_api_dot_v0_dot_luminarycloud_dot_feature__flag_dot_feature__flag__pb2.GetFeatureFlagsResponse.SerializeToString,
|
|
45
|
+
),
|
|
46
|
+
}
|
|
47
|
+
generic_handler = grpc.method_handlers_generic_handler(
|
|
48
|
+
'luminary.proto.api.v0.luminarycloud.feature_flag.FeatureFlagService', rpc_method_handlers)
|
|
49
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# This class is part of an EXPERIMENTAL API.
|
|
53
|
+
class FeatureFlagService(object):
|
|
54
|
+
"""Manages feature flags for internal users.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def GetFeatureFlags(request,
|
|
59
|
+
target,
|
|
60
|
+
options=(),
|
|
61
|
+
channel_credentials=None,
|
|
62
|
+
call_credentials=None,
|
|
63
|
+
insecure=False,
|
|
64
|
+
compression=None,
|
|
65
|
+
wait_for_ready=None,
|
|
66
|
+
timeout=None,
|
|
67
|
+
metadata=None):
|
|
68
|
+
return grpc.experimental.unary_unary(request, target, '/luminary.proto.api.v0.luminarycloud.feature_flag.FeatureFlagService/GetFeatureFlags',
|
|
69
|
+
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
|
|
70
|
+
proto_dot_api_dot_v0_dot_luminarycloud_dot_feature__flag_dot_feature__flag__pb2.GetFeatureFlagsResponse.FromString,
|
|
71
|
+
options, channel_credentials,
|
|
72
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@generated by mypy-protobuf. Do not edit manually!
|
|
3
|
+
isort:skip_file
|
|
4
|
+
"""
|
|
5
|
+
import abc
|
|
6
|
+
import google.protobuf.empty_pb2
|
|
7
|
+
import grpc
|
|
8
|
+
import luminarycloud._proto.api.v0.luminarycloud.feature_flag.feature_flag_pb2
|
|
9
|
+
|
|
10
|
+
class FeatureFlagServiceStub:
|
|
11
|
+
"""Manages feature flags for internal users."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, channel: grpc.Channel) -> None: ...
|
|
14
|
+
GetFeatureFlags: grpc.UnaryUnaryMultiCallable[
|
|
15
|
+
google.protobuf.empty_pb2.Empty,
|
|
16
|
+
luminarycloud._proto.api.v0.luminarycloud.feature_flag.feature_flag_pb2.GetFeatureFlagsResponse,
|
|
17
|
+
]
|
|
18
|
+
"""Gets all enabled feature flags for the authenticated user.
|
|
19
|
+
Feature flags are used to control access to experimental and internal features.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
class FeatureFlagServiceServicer(metaclass=abc.ABCMeta):
|
|
23
|
+
"""Manages feature flags for internal users."""
|
|
24
|
+
|
|
25
|
+
@abc.abstractmethod
|
|
26
|
+
def GetFeatureFlags(
|
|
27
|
+
self,
|
|
28
|
+
request: google.protobuf.empty_pb2.Empty,
|
|
29
|
+
context: grpc.ServicerContext,
|
|
30
|
+
) -> luminarycloud._proto.api.v0.luminarycloud.feature_flag.feature_flag_pb2.GetFeatureFlagsResponse:
|
|
31
|
+
"""Gets all enabled feature flags for the authenticated user.
|
|
32
|
+
Feature flags are used to control access to experimental and internal features.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def add_FeatureFlagServiceServicer_to_server(servicer: FeatureFlagServiceServicer, server: grpc.Server) -> None: ...
|