Skip to content

Commit a18bc3f

Browse files
committed
Feat: new method for pricing aggregate
1 parent 0add192 commit a18bc3f

File tree

1 file changed

+103
-2
lines changed

1 file changed

+103
-2
lines changed

src/aleph/sdk/client/services/pricing.py

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from __future__ import annotations
2-
1+
import logging
2+
import math
33
from enum import Enum
44
from typing import TYPE_CHECKING, Dict, List, Optional, Union
55

@@ -12,6 +12,8 @@
1212

1313
from pydantic import BaseModel, RootModel
1414

15+
logger = logging.getLogger(__name__)
16+
1517

1618
class PricingEntity(str, Enum):
1719
STORAGE = "storage"
@@ -46,18 +48,117 @@ class ComputeUnit(BaseModel):
4648
disk_mib: int
4749

4850

51+
class TierComputedSpec(ComputeUnit):
52+
...
53+
gpu_model: Optional[str]
54+
vram: Optional[int]
55+
56+
4957
class Tier(BaseModel):
5058
id: str
5159
compute_units: int
5260
vram: Optional[int] = None
5361
model: Optional[str] = None
5462

63+
def extract_tier_id(self) -> str:
64+
return self.id.split("-", 1)[-1]
65+
5566

5667
class PricingPerEntity(BaseModel):
5768
price: Dict[str, Union[Price, Decimal]]
5869
compute_unit: Optional[ComputeUnit] = None
5970
tiers: Optional[List[Tier]] = None
6071

72+
def _get_nb_compute_units(
73+
self,
74+
vcpus: int = 1,
75+
memory_mib: int = 2048,
76+
) -> Optional[int]:
77+
if self.compute_unit:
78+
memory = math.ceil(memory_mib / self.compute_unit.memory_mib)
79+
nb_compute = vcpus if vcpus >= memory else memory
80+
return nb_compute
81+
return None
82+
83+
def get_closest_tier(
84+
self,
85+
vcpus: Optional[int] = None,
86+
memory_mib: Optional[int] = None,
87+
compute_unit: Optional[int] = None,
88+
):
89+
"""Get Closest tier for Program / Instance"""
90+
91+
# We Calculate Compute Unit requested based on vcpus and memory
92+
computed_cu = None
93+
if vcpus is not None and memory_mib is not None:
94+
computed_cu = self._get_nb_compute_units(vcpus=vcpus, memory_mib=memory_mib)
95+
elif vcpus is not None and self.compute_unit is not None:
96+
computed_cu = self._get_nb_compute_units(
97+
vcpus=vcpus, memory_mib=self.compute_unit.memory_mib
98+
)
99+
elif memory_mib is not None and self.compute_unit is not None:
100+
computed_cu = self._get_nb_compute_units(
101+
vcpus=self.compute_unit.vcpus, memory_mib=memory_mib
102+
)
103+
104+
# Case where Vcpus or memory is given but also a number of CU (case on aleph-client)
105+
cu: Optional[int] = None
106+
if computed_cu is not None and compute_unit is not None:
107+
if computed_cu != compute_unit:
108+
logger.warning(
109+
f"Mismatch in compute units: from CPU/RAM={computed_cu}, given={compute_unit}. "
110+
f"Choosing {max(computed_cu, compute_unit)}."
111+
)
112+
cu = max(computed_cu, compute_unit) # We trust the bigger trier
113+
else:
114+
cu = compute_unit if compute_unit is not None else computed_cu
115+
116+
# now tier found
117+
if cu is None:
118+
return None
119+
120+
# With CU available, choose the closest one
121+
candidates = self.tiers
122+
if candidates is None:
123+
return None
124+
125+
best_tier = min(
126+
candidates,
127+
key=lambda t: (abs(t.compute_units - cu), -t.compute_units),
128+
)
129+
return best_tier
130+
131+
def get_services_specs(
132+
self,
133+
tier: Tier,
134+
) -> TierComputedSpec:
135+
"""
136+
Calculate ammount of vram / cpu / disk | + gpu model / vram if it GPU instance
137+
"""
138+
if self.compute_unit is None:
139+
raise ValueError("ComputeUnit is required to get service specs")
140+
141+
cpu = tier.compute_units * self.compute_unit.vcpus
142+
memory_mib = tier.compute_units * self.compute_unit.memory_mib
143+
disk = (
144+
tier.compute_units * self.compute_unit.disk_mib
145+
) # Min value disk can be increased
146+
147+
# Gpu Specs
148+
gpu = None
149+
vram = None
150+
if tier.model and tier.vram:
151+
gpu = tier.model
152+
vram = tier.vram
153+
154+
return TierComputedSpec(
155+
vcpus=cpu,
156+
memory_mib=memory_mib,
157+
disk_mib=disk,
158+
gpu_model=gpu,
159+
vram=vram,
160+
)
161+
61162

62163
class PricingModel(RootModel[Dict[PricingEntity, PricingPerEntity]]):
63164
def __iter__(self):

0 commit comments

Comments
 (0)