Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/11505.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `ResourceSlot.to_humanized()` to fall back to a guessed unit per slot instead of raising on missing slot type entries, preventing 500 errors from clobbering user-facing 4xx responses during error formatting.
19 changes: 11 additions & 8 deletions src/ai/backend/common/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1223,14 +1223,17 @@ def from_user_input(
return cls(data)

def to_humanized(self, slot_types: Mapping[str, Any]) -> Mapping[str, str]:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we change the parameter type to Mapping[SlotName, SlotTypes]?

try:
return {
k: type(self)._humanize_value(Decimal(v), slot_types[k])
for k, v in self.data.items()
if v is not None
}
except KeyError as e:
raise ValueError(f"Unknown slot type: {e.args[0]!r}") from e
def _resolve_unit(key: str) -> Any:
try:
return slot_types[key]
except KeyError:
return type(self)._guess_slot_type(key)
Comment thread
fregataa marked this conversation as resolved.

return {
k: type(self)._humanize_value(Decimal(v), _resolve_unit(k))
for k, v in self.data.items()
if v is not None
}
Comment on lines 1225 to +1236

@classmethod
def from_json(cls, obj: Mapping[str, Any]) -> ResourceSlot:
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/common/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,30 @@ def test_resource_slot_serialization() -> None:
assert r2["b"] == Decimal(0)


def test_resource_slot_to_humanized_falls_back_for_unknown_slot() -> None:
# Missing keys fall back to the guessed unit instead of raising.
r = ResourceSlot({
"cpu": "1",
"mem": str(Decimal(2 * (2**30))),
"cuda.device": "1",
"cuda.mem": str(Decimal(1 * (2**30))),
})
slot_types: dict[str, str] = {"cpu": "count", "mem": "bytes"}
humanized = r.to_humanized(slot_types)
assert humanized == {
"cpu": "1",
"mem": "2g",
"cuda.device": "1",
"cuda.mem": "1g",
}


def test_resource_slot_to_humanized_with_empty_slot_types() -> None:
r = ResourceSlot({"cuda.device": "2", "cuda.mem": str(Decimal(2**30))})
humanized = r.to_humanized({})
assert humanized == {"cuda.device": "2", "cuda.mem": "1g"}
Comment thread
fregataa marked this conversation as resolved.


def test_resource_slot_serialization_prevent_scientific_notation() -> None:
r1 = ResourceSlot({"a": "2E+1", "b": "200"})
assert r1.to_json()["a"] == "20"
Expand Down
Loading