Coverage for src / lexigram / contracts / ai / governance / resource_unit.py: 0%

42 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-15 18:57 +0800

1"""Resource unit contracts for per-tenant resource quotas (LXF-001).""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass 

6from datetime import datetime, timedelta 

7from enum import Enum 

8 

9from lexigram.contracts.ai.governance.errors import GovernanceError 

10 

11 

12class ResourceWindowKind(str, Enum): 

13 """Time window strategy for resource tracking.""" 

14 

15 SLIDING = "sliding" 

16 CALENDAR = "calendar" 

17 INSTANTANEOUS = "instantaneous" 

18 

19 

20@dataclass(frozen=True, slots=True) 

21class ResourceUnit: 

22 """A measurable resource the governance system tracks per tenant.""" 

23 

24 name: str 

25 unit_kind: str 

26 window: timedelta | None = None 

27 window_kind: ResourceWindowKind = ResourceWindowKind.SLIDING 

28 

29 

30@dataclass(frozen=True, slots=True) 

31class ResourceQuota: 

32 """Per-tenant quota for a specific resource unit.""" 

33 

34 tenant_id: str 

35 unit: ResourceUnit 

36 limit: float 

37 soft_threshold_pct: float | None = None 

38 

39 

40@dataclass(frozen=True, slots=True) 

41class ResourceUsageSnapshot: 

42 """A point-in-time snapshot of resource usage.""" 

43 

44 tenant_id: str 

45 unit_name: str 

46 current: float 

47 limit: float 

48 window_resets_at: datetime | None = None 

49 

50 

51class ResourceUsageResult(str, Enum): 

52 """Result of a resource usage check.""" 

53 

54 APPROVED = "approved" 

55 SOFT_THRESHOLD_BREACH = "soft_threshold_breach" 

56 EXHAUSTED = "exhausted" 

57 

58 

59class ResourceExhaustedError(GovernanceError): 

60 """Raised when a resource quota is exhausted.""" 

61 

62 _code: str = "LEX_ERR_GOV_010" 

63 

64 def __init__( 

65 self, 

66 tenant_id: str, 

67 unit_name: str, 

68 limit: float, 

69 current: float, 

70 actor_id: str | None = None, 

71 ) -> None: 

72 self.tenant_id = tenant_id 

73 self.unit_name = unit_name 

74 self.limit = limit 

75 self.current = current 

76 self.actor_id = actor_id 

77 super().__init__( 

78 f"Resource quota exhausted: tenant={tenant_id}, unit={unit_name}, " 

79 f"{current}/{limit}" 

80 ) 

81 

82 

83__all__ = [ 

84 "ResourceExhaustedError", 

85 "ResourceQuota", 

86 "ResourceUnit", 

87 "ResourceUsageResult", 

88 "ResourceUsageSnapshot", 

89 "ResourceWindowKind", 

90]