Skip to content

Commit e524762

Browse files
feat(container-types): add OpenAPI container types service
Co-authored-by: imagene-shahar <imagene-shahar@users.noreply.github.com>
1 parent 7b0fb1a commit e524762

8 files changed

Lines changed: 130 additions & 0 deletions

File tree

datacrunch_compat/datacrunch/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
authentication,
77
balance,
88
constants,
9+
container_types,
910
containers,
1011
exceptions,
1112
helpers,
@@ -29,6 +30,7 @@
2930
'authentication',
3031
'balance',
3132
'constants',
33+
'container_types',
3234
'containers',
3335
'datacrunch',
3436
'exceptions',

datacrunch_compat/datacrunch/datacrunch.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from verda.authentication import AuthenticationService
66
from verda.balance import BalanceService
77
from verda.constants import Constants
8+
from verda.container_types import ContainerTypesService
89
from verda.containers import ContainersService
910
from verda.http_client import HTTPClient
1011
from verda.images import ImagesService
@@ -21,6 +22,7 @@
2122
'AuthenticationService',
2223
'BalanceService',
2324
'Constants',
25+
'ContainerTypesService',
2426
'ContainersService',
2527
'DataCrunchClient',
2628
'HTTPClient',
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Container Types
2+
===============
3+
4+
.. autoclass:: verda.container_types.ContainerTypesService
5+
:members:
6+
7+
.. autoclass:: verda.container_types.ContainerType
8+
:members:
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import responses # https://github.com/getsentry/responses
2+
3+
from verda.container_types import ContainerType, ContainerTypesService
4+
5+
CONTAINER_TYPE_ID = 'type-c0de-a5d2-4972-ae4e-d429115d055b'
6+
7+
8+
@responses.activate
9+
def test_container_types(http_client):
10+
endpoint = http_client._base_url + '/container-types?currency=eur'
11+
responses.add(
12+
responses.GET,
13+
endpoint,
14+
json=[
15+
{
16+
'id': CONTAINER_TYPE_ID,
17+
'model': 'H100',
18+
'name': 'H100 SXM5 80GB',
19+
'instance_type': '1H100.80S.22V',
20+
'cpu': {'description': '22 CPU', 'number_of_cores': 22},
21+
'gpu': {'description': '1x H100 SXM5 80GB', 'number_of_gpus': 1},
22+
'gpu_memory': {'description': '80GB GPU RAM', 'size_in_gigabytes': 80},
23+
'memory': {'description': '187GB RAM', 'size_in_gigabytes': 187},
24+
'serverless_price': '1.75',
25+
'serverless_spot_price': '0.87',
26+
'currency': 'eur',
27+
'manufacturer': 'NVIDIA',
28+
}
29+
],
30+
status=200,
31+
)
32+
33+
service = ContainerTypesService(http_client)
34+
35+
container_types = service.get(currency='eur')
36+
container_type = container_types[0]
37+
38+
assert isinstance(container_types, list)
39+
assert len(container_types) == 1
40+
assert isinstance(container_type, ContainerType)
41+
assert container_type.id == CONTAINER_TYPE_ID
42+
assert container_type.model == 'H100'
43+
assert container_type.name == 'H100 SXM5 80GB'
44+
assert container_type.instance_type == '1H100.80S.22V'
45+
assert container_type.serverless_price == 1.75
46+
assert container_type.serverless_spot_price == 0.87
47+
assert container_type.currency == 'eur'
48+
assert container_type.manufacturer == 'NVIDIA'
49+
assert responses.assert_call_count(endpoint, 1) is True

tests/unit_tests/test_client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717

1818
class TestVerdaClient:
19+
@responses.activate
1920
def test_client(self):
2021
# arrange - add response mock
2122
responses.add(responses.POST, BASE_URL + '/oauth2/token', json=response_json, status=200)
@@ -25,7 +26,9 @@ def test_client(self):
2526

2627
# assert
2728
assert client.constants.base_url == BASE_URL
29+
assert hasattr(client, 'container_types')
2830

31+
@responses.activate
2932
def test_client_with_default_base_url(self):
3033
# arrange - add response mock
3134
DEFAULT_BASE_URL = 'https://api.verda.com/v1'
@@ -42,6 +45,7 @@ def test_client_with_default_base_url(self):
4245
# assert
4346
assert client.constants.base_url == DEFAULT_BASE_URL
4447

48+
@responses.activate
4549
def test_invalid_client_credentials(self):
4650
# arrange - add response mock
4751
responses.add(

verda/_verda.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from verda.authentication import AuthenticationService
33
from verda.balance import BalanceService
44
from verda.clusters import ClustersService
5+
from verda.container_types import ContainerTypesService
56
from verda.constants import Constants
67
from verda.containers import ContainersService
78
from verda.http_client import HTTPClient
@@ -80,6 +81,9 @@ def __init__(
8081
self.containers: ContainersService = ContainersService(self._http_client, inference_key)
8182
"""Containers service. Deploy, manage, and monitor container deployments"""
8283

84+
self.container_types: ContainerTypesService = ContainerTypesService(self._http_client)
85+
"""Container types service. Get available serverless container SKUs"""
86+
8387
self.clusters: ClustersService = ClustersService(self._http_client)
8488
"""Clusters service. Create and manage compute clusters"""
8589

verda/container_types/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from verda.container_types._container_types import ContainerType, ContainerTypesService
2+
3+
__all__ = ['ContainerType', 'ContainerTypesService']
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from dataclasses import dataclass
2+
from typing import Literal
3+
4+
from dataclasses_json import dataclass_json
5+
6+
CONTAINER_TYPES_ENDPOINT = '/container-types'
7+
8+
Currency = Literal['usd', 'eur']
9+
10+
11+
@dataclass_json
12+
@dataclass
13+
class ContainerType:
14+
"""Container type returned by the public API."""
15+
16+
id: str
17+
model: str
18+
name: str
19+
instance_type: str
20+
cpu: dict
21+
gpu: dict
22+
gpu_memory: dict
23+
memory: dict
24+
serverless_price: float
25+
serverless_spot_price: float
26+
currency: Currency
27+
manufacturer: str
28+
29+
30+
class ContainerTypesService:
31+
"""Service for interacting with container types."""
32+
33+
def __init__(self, http_client) -> None:
34+
self._http_client = http_client
35+
36+
def get(self, currency: Currency = 'usd') -> list[ContainerType]:
37+
"""Return all available container types."""
38+
container_types = self._http_client.get(
39+
CONTAINER_TYPES_ENDPOINT,
40+
params={'currency': currency},
41+
).json()
42+
return [
43+
ContainerType(
44+
id=container_type['id'],
45+
model=container_type['model'],
46+
name=container_type['name'],
47+
instance_type=container_type['instance_type'],
48+
cpu=container_type['cpu'],
49+
gpu=container_type['gpu'],
50+
gpu_memory=container_type['gpu_memory'],
51+
memory=container_type['memory'],
52+
serverless_price=float(container_type['serverless_price']),
53+
serverless_spot_price=float(container_type['serverless_spot_price']),
54+
currency=container_type['currency'],
55+
manufacturer=container_type['manufacturer'],
56+
)
57+
for container_type in container_types
58+
]

0 commit comments

Comments
 (0)