viettelcloud-aiplatform 0.3.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 (71) hide show
  1. viettelcloud/__init__.py +1 -0
  2. viettelcloud/aiplatform/__init__.py +15 -0
  3. viettelcloud/aiplatform/common/__init__.py +0 -0
  4. viettelcloud/aiplatform/common/constants.py +22 -0
  5. viettelcloud/aiplatform/common/types.py +28 -0
  6. viettelcloud/aiplatform/common/utils.py +40 -0
  7. viettelcloud/aiplatform/hub/OWNERS +14 -0
  8. viettelcloud/aiplatform/hub/__init__.py +25 -0
  9. viettelcloud/aiplatform/hub/api/__init__.py +13 -0
  10. viettelcloud/aiplatform/hub/api/_proxy_client.py +355 -0
  11. viettelcloud/aiplatform/hub/api/model_registry_client.py +561 -0
  12. viettelcloud/aiplatform/hub/api/model_registry_client_test.py +462 -0
  13. viettelcloud/aiplatform/optimizer/__init__.py +45 -0
  14. viettelcloud/aiplatform/optimizer/api/__init__.py +0 -0
  15. viettelcloud/aiplatform/optimizer/api/optimizer_client.py +248 -0
  16. viettelcloud/aiplatform/optimizer/backends/__init__.py +13 -0
  17. viettelcloud/aiplatform/optimizer/backends/base.py +77 -0
  18. viettelcloud/aiplatform/optimizer/backends/kubernetes/__init__.py +13 -0
  19. viettelcloud/aiplatform/optimizer/backends/kubernetes/backend.py +563 -0
  20. viettelcloud/aiplatform/optimizer/backends/kubernetes/utils.py +112 -0
  21. viettelcloud/aiplatform/optimizer/constants/__init__.py +13 -0
  22. viettelcloud/aiplatform/optimizer/constants/constants.py +59 -0
  23. viettelcloud/aiplatform/optimizer/types/__init__.py +13 -0
  24. viettelcloud/aiplatform/optimizer/types/algorithm_types.py +87 -0
  25. viettelcloud/aiplatform/optimizer/types/optimization_types.py +135 -0
  26. viettelcloud/aiplatform/optimizer/types/search_types.py +95 -0
  27. viettelcloud/aiplatform/py.typed +0 -0
  28. viettelcloud/aiplatform/trainer/__init__.py +82 -0
  29. viettelcloud/aiplatform/trainer/api/__init__.py +3 -0
  30. viettelcloud/aiplatform/trainer/api/trainer_client.py +277 -0
  31. viettelcloud/aiplatform/trainer/api/trainer_client_test.py +72 -0
  32. viettelcloud/aiplatform/trainer/backends/__init__.py +0 -0
  33. viettelcloud/aiplatform/trainer/backends/base.py +94 -0
  34. viettelcloud/aiplatform/trainer/backends/container/adapters/base.py +195 -0
  35. viettelcloud/aiplatform/trainer/backends/container/adapters/docker.py +231 -0
  36. viettelcloud/aiplatform/trainer/backends/container/adapters/podman.py +258 -0
  37. viettelcloud/aiplatform/trainer/backends/container/backend.py +668 -0
  38. viettelcloud/aiplatform/trainer/backends/container/backend_test.py +867 -0
  39. viettelcloud/aiplatform/trainer/backends/container/runtime_loader.py +631 -0
  40. viettelcloud/aiplatform/trainer/backends/container/runtime_loader_test.py +637 -0
  41. viettelcloud/aiplatform/trainer/backends/container/types.py +67 -0
  42. viettelcloud/aiplatform/trainer/backends/container/utils.py +213 -0
  43. viettelcloud/aiplatform/trainer/backends/kubernetes/__init__.py +0 -0
  44. viettelcloud/aiplatform/trainer/backends/kubernetes/backend.py +710 -0
  45. viettelcloud/aiplatform/trainer/backends/kubernetes/backend_test.py +1344 -0
  46. viettelcloud/aiplatform/trainer/backends/kubernetes/constants.py +15 -0
  47. viettelcloud/aiplatform/trainer/backends/kubernetes/utils.py +636 -0
  48. viettelcloud/aiplatform/trainer/backends/kubernetes/utils_test.py +582 -0
  49. viettelcloud/aiplatform/trainer/backends/localprocess/__init__.py +0 -0
  50. viettelcloud/aiplatform/trainer/backends/localprocess/backend.py +306 -0
  51. viettelcloud/aiplatform/trainer/backends/localprocess/backend_test.py +501 -0
  52. viettelcloud/aiplatform/trainer/backends/localprocess/constants.py +90 -0
  53. viettelcloud/aiplatform/trainer/backends/localprocess/job.py +184 -0
  54. viettelcloud/aiplatform/trainer/backends/localprocess/types.py +52 -0
  55. viettelcloud/aiplatform/trainer/backends/localprocess/utils.py +302 -0
  56. viettelcloud/aiplatform/trainer/constants/__init__.py +0 -0
  57. viettelcloud/aiplatform/trainer/constants/constants.py +179 -0
  58. viettelcloud/aiplatform/trainer/options/__init__.py +52 -0
  59. viettelcloud/aiplatform/trainer/options/common.py +55 -0
  60. viettelcloud/aiplatform/trainer/options/kubernetes.py +502 -0
  61. viettelcloud/aiplatform/trainer/options/kubernetes_test.py +259 -0
  62. viettelcloud/aiplatform/trainer/options/localprocess.py +20 -0
  63. viettelcloud/aiplatform/trainer/test/common.py +22 -0
  64. viettelcloud/aiplatform/trainer/types/__init__.py +0 -0
  65. viettelcloud/aiplatform/trainer/types/types.py +517 -0
  66. viettelcloud/aiplatform/trainer/types/types_test.py +115 -0
  67. viettelcloud_aiplatform-0.3.0.dist-info/METADATA +226 -0
  68. viettelcloud_aiplatform-0.3.0.dist-info/RECORD +71 -0
  69. viettelcloud_aiplatform-0.3.0.dist-info/WHEEL +4 -0
  70. viettelcloud_aiplatform-0.3.0.dist-info/licenses/LICENSE +201 -0
  71. viettelcloud_aiplatform-0.3.0.dist-info/licenses/NOTICE +36 -0
@@ -0,0 +1,259 @@
1
+ # Copyright 2025 The Kubeflow Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Unit tests for Kubernetes options."""
16
+
17
+ import pytest
18
+
19
+ from viettelcloud.aiplatform.trainer.backends.kubernetes.backend import KubernetesBackend
20
+ from viettelcloud.aiplatform.trainer.backends.localprocess.backend import LocalProcessBackend
21
+ from viettelcloud.aiplatform.trainer.options import (
22
+ Annotations,
23
+ ContainerOverride,
24
+ Labels,
25
+ Name,
26
+ PodTemplateOverride,
27
+ PodTemplateOverrides,
28
+ SpecAnnotations,
29
+ SpecLabels,
30
+ TrainerArgs,
31
+ TrainerCommand,
32
+ )
33
+
34
+
35
+ @pytest.fixture
36
+ def mock_kubernetes_backend():
37
+ """Mock Kubernetes backend for testing."""
38
+ from unittest.mock import Mock
39
+
40
+ backend = Mock(spec=KubernetesBackend)
41
+ backend.__class__ = KubernetesBackend
42
+ return backend
43
+
44
+
45
+ @pytest.fixture
46
+ def mock_localprocess_backend():
47
+ """Mock LocalProcess backend for testing."""
48
+ from unittest.mock import MagicMock
49
+
50
+ # Create a proper mock that isinstance checks will work with
51
+ backend = MagicMock(spec=LocalProcessBackend)
52
+ # Make type(backend).__name__ return the correct class name
53
+ type(backend).__name__ = "LocalProcessBackend"
54
+ return backend
55
+
56
+
57
+ class TestKubernetesOptionBackendValidation:
58
+ """Test that Kubernetes options validate backend compatibility."""
59
+
60
+ @pytest.mark.parametrize(
61
+ "option_class,option_args",
62
+ [
63
+ (Labels, {"app": "test", "version": "v1"}),
64
+ (Annotations, {"description": "test job"}),
65
+ (SpecLabels, {"app": "training"}),
66
+ (SpecAnnotations, {"prometheus.io/scrape": "true"}),
67
+ (TrainerCommand, ["python", "train.py"]),
68
+ (TrainerArgs, ["--epochs", "10"]),
69
+ ],
70
+ )
71
+ def test_kubernetes_options_reject_wrong_backend(
72
+ self, option_class, option_args, mock_localprocess_backend
73
+ ):
74
+ """Test Kubernetes-specific options reject non-Kubernetes backends."""
75
+ if option_class == TrainerCommand:
76
+ option = option_class(command=option_args)
77
+ elif option_class == TrainerArgs:
78
+ option = option_class(args=option_args)
79
+ else:
80
+ option = option_class(option_args)
81
+
82
+ job_spec = {}
83
+
84
+ with pytest.raises(ValueError) as exc_info:
85
+ option(job_spec, None, mock_localprocess_backend)
86
+
87
+ assert "not compatible with" in str(exc_info.value)
88
+ assert "LocalProcessBackend" in str(exc_info.value)
89
+
90
+ def test_pod_template_overrides_rejects_wrong_backend(self, mock_localprocess_backend):
91
+ """Test PodTemplateOverrides rejects non-Kubernetes backends."""
92
+ override = PodTemplateOverride(target_jobs=["node"])
93
+ option = PodTemplateOverrides(override)
94
+
95
+ job_spec = {}
96
+
97
+ with pytest.raises(ValueError) as exc_info:
98
+ option(job_spec, None, mock_localprocess_backend)
99
+
100
+ assert "not compatible with" in str(exc_info.value)
101
+
102
+
103
+ class TestKubernetesOptionApplication:
104
+ """Test Kubernetes option application behavior."""
105
+
106
+ @pytest.mark.parametrize(
107
+ "option_class,option_args,expected_spec",
108
+ [
109
+ (
110
+ Labels,
111
+ {"app": "test", "version": "v1"},
112
+ {"metadata": {"labels": {"app": "test", "version": "v1"}}},
113
+ ),
114
+ (
115
+ Annotations,
116
+ {"description": "test job"},
117
+ {"metadata": {"annotations": {"description": "test job"}}},
118
+ ),
119
+ (
120
+ SpecLabels,
121
+ {"app": "training", "version": "v1.0"},
122
+ {"spec": {"labels": {"app": "training", "version": "v1.0"}}},
123
+ ),
124
+ (
125
+ SpecAnnotations,
126
+ {"prometheus.io/scrape": "true"},
127
+ {"spec": {"annotations": {"prometheus.io/scrape": "true"}}},
128
+ ),
129
+ (Name, "custom-job-name", {"metadata": {"name": "custom-job-name"}}),
130
+ (
131
+ TrainerCommand,
132
+ ["python", "train.py"],
133
+ {"spec": {"trainer": {"command": ["python", "train.py"]}}},
134
+ ),
135
+ (
136
+ TrainerArgs,
137
+ ["--epochs", "10"],
138
+ {"spec": {"trainer": {"args": ["--epochs", "10"]}}},
139
+ ),
140
+ ],
141
+ )
142
+ def test_option_application(
143
+ self, option_class, option_args, expected_spec, mock_kubernetes_backend
144
+ ):
145
+ """Test each option applies correctly to job spec with Kubernetes backend."""
146
+ if option_class == TrainerCommand:
147
+ option = option_class(command=option_args)
148
+ elif option_class == TrainerArgs:
149
+ option = option_class(args=option_args)
150
+ else:
151
+ option = option_class(option_args)
152
+
153
+ job_spec = {}
154
+ option(job_spec, None, mock_kubernetes_backend)
155
+
156
+ assert job_spec == expected_spec
157
+
158
+
159
+ class TestTrainerOptionValidation:
160
+ """Test validation of trainer-specific options."""
161
+
162
+ @pytest.mark.parametrize(
163
+ "option_class,option_args,trainer_type,should_fail",
164
+ [
165
+ # Validation failures
166
+ (TrainerCommand, ["python", "train.py"], "CustomTrainer", True),
167
+ (TrainerArgs, ["--epochs", "10"], "CustomTrainer", True),
168
+ (TrainerCommand, ["python", "train.py"], "BuiltinTrainer", True),
169
+ (TrainerArgs, ["--epochs", "10"], "BuiltinTrainer", True),
170
+ # Successful applications
171
+ (TrainerCommand, ["python", "train.py"], "CustomTrainerContainer", False),
172
+ (TrainerArgs, ["--epochs", "10"], "CustomTrainerContainer", False),
173
+ ],
174
+ )
175
+ def test_trainer_option_validation(
176
+ self, option_class, option_args, trainer_type, should_fail, mock_kubernetes_backend
177
+ ):
178
+ """Test trainer option validation with different trainer types."""
179
+ from viettelcloud.aiplatform.trainer.types.types import (
180
+ BuiltinTrainer,
181
+ CustomTrainer,
182
+ CustomTrainerContainer,
183
+ TorchTuneConfig,
184
+ )
185
+
186
+ # Create appropriate trainer instance
187
+ if trainer_type == "CustomTrainer":
188
+
189
+ def dummy_func():
190
+ pass
191
+
192
+ trainer = CustomTrainer(func=dummy_func)
193
+ elif trainer_type == "BuiltinTrainer":
194
+ trainer = BuiltinTrainer(config=TorchTuneConfig())
195
+ else: # CustomTrainerContainer
196
+ trainer = CustomTrainerContainer(image="custom-image:latest")
197
+
198
+ # Create option
199
+ if option_class == TrainerCommand:
200
+ option = option_class(command=option_args)
201
+ else: # TrainerArgs
202
+ option = option_class(args=option_args)
203
+
204
+ job_spec = {}
205
+
206
+ if should_fail:
207
+ with pytest.raises(ValueError) as exc_info:
208
+ option(job_spec, trainer, mock_kubernetes_backend)
209
+ assert "TrainerCommand can only be used with CustomTrainerContainer" in str(
210
+ exc_info.value
211
+ ) or "TrainerArgs can only be used with CustomTrainerContainer" in str(exc_info.value)
212
+ else:
213
+ option(job_spec, trainer, mock_kubernetes_backend)
214
+ if option_class == TrainerCommand:
215
+ assert job_spec["spec"]["trainer"]["command"] == option_args
216
+ else:
217
+ assert job_spec["spec"]["trainer"]["args"] == option_args
218
+
219
+
220
+ class TestContainerOverride:
221
+ """Test ContainerOverride validation."""
222
+
223
+ @pytest.mark.parametrize(
224
+ "kwargs,expected_error",
225
+ [
226
+ ({"name": ""}, "Container name must be a non-empty string"),
227
+ (
228
+ {"name": "trainer", "env": [{"invalid": "structure"}]},
229
+ "Each env entry must have a 'name' key",
230
+ ),
231
+ (
232
+ {"name": "trainer", "volume_mounts": [{"name": "vol"}]},
233
+ "Each volume_mounts entry must have a 'mountPath' key",
234
+ ),
235
+ ],
236
+ )
237
+ def test_container_override_validation(self, kwargs, expected_error):
238
+ """Test ContainerOverride validates inputs correctly."""
239
+ with pytest.raises(ValueError) as exc_info:
240
+ ContainerOverride(**kwargs)
241
+ assert expected_error in str(exc_info.value)
242
+
243
+
244
+ class TestPodTemplateOverrides:
245
+ """Test PodTemplateOverrides functionality."""
246
+
247
+ def test_pod_template_overrides_basic(self, mock_kubernetes_backend):
248
+ """Test basic PodTemplateOverrides application."""
249
+
250
+ override = PodTemplateOverride(target_jobs=["node"])
251
+ option = PodTemplateOverrides(override)
252
+
253
+ job_spec = {}
254
+ option(job_spec, None, mock_kubernetes_backend)
255
+
256
+ assert "spec" in job_spec
257
+ assert "podTemplateOverrides" in job_spec["spec"]
258
+ assert len(job_spec["spec"]["podTemplateOverrides"]) == 1
259
+ assert job_spec["spec"]["podTemplateOverrides"][0]["targetJobs"] == [{"name": "node"}]
@@ -0,0 +1,20 @@
1
+ # Copyright 2025 The Kubeflow Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """LocalProcess-specific training options for the Kubeflow Trainer SDK.
16
+
17
+ TODO: Add LocalProcess options (ProcessTimeout, WorkingDirectory, etc.) in future iteration.
18
+ """
19
+
20
+ # TODO: Implement LocalProcess options using LocalProcessCompatible base class
@@ -0,0 +1,22 @@
1
+ # Shared test utilities and types for Kubeflow Trainer tests.
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Optional
5
+
6
+ # Common status constants
7
+ SUCCESS = "success"
8
+ FAILED = "Failed"
9
+ DEFAULT_NAMESPACE = "default"
10
+ TIMEOUT = "timeout"
11
+ RUNTIME = "runtime"
12
+
13
+
14
+ @dataclass
15
+ class TestCase:
16
+ name: str
17
+ expected_status: str = SUCCESS
18
+ config: dict[str, Any] = field(default_factory=dict)
19
+ expected_output: Optional[Any] = None
20
+ expected_error: Optional[type[Exception]] = None
21
+ # Prevent pytest from collecting this dataclass as a test
22
+ __test__ = False
File without changes