Coverage for packages/python/voice_prices/data_snapshot.py: 97.54%

80 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2026-09-27 04:08 +0000

1from __future__ import annotations as _annotations 

2 

3import re 

4from dataclasses import dataclass, field 

5from datetime import datetime, timedelta, timezone 

6from functools import cache 

7from typing import Any 

8 

9from . import types 

10 

11__all__ = 'DataSnapshot', 'set_custom_snapshot' 

12 

13# snapshot set by UpdatePrices, or manually by the user 

14_custom_snapshot: DataSnapshot | None = None 

15 

16 

17def get_snapshot() -> DataSnapshot: 

18 if _custom_snapshot is not None: 

19 return _custom_snapshot 

20 return _bundled_snapshot() 

21 

22 

23@cache 

24def _bundled_snapshot() -> DataSnapshot: 

25 from .data import providers 

26 

27 return DataSnapshot(providers=providers, from_auto_update=False) 

28 

29 

30def set_custom_snapshot(snapshot: DataSnapshot | None): 

31 """Install a snapshot for every later price lookup in this process, or reset with None. 

32 

33 Process-wide by design: `get_snapshot` reads this module global, so one call changes what 

34 every subsequent `calc_price` sees. `UpdatePrices` calls it from a background daemon thread, 

35 which means a process that has started an updater can return a different price for two 

36 identical calls, and a test that has not reset can see downloaded data instead of the 

37 snapshot bundled with the installed version. 

38 

39 `set_custom_snapshot(None)` is the supported reset and restores the bundled data. Prefer 

40 `with UpdatePrices():`, whose `stop()` resets for you after joining the thread. 

41 """ 

42 global _custom_snapshot 

43 _custom_snapshot = snapshot 

44 

45 

46@dataclass 

47class DataSnapshot: 

48 providers: list[types.Provider] 

49 from_auto_update: bool 

50 _lookup_cache: dict[tuple[str | None, str | None, str], tuple[types.Provider, types.ModelInfo]] = field( 

51 default_factory=lambda: {} 

52 ) 

53 timestamp: datetime = field(default_factory=datetime.now) 

54 

55 def active(self, ttl: timedelta) -> bool: 

56 """Check if the snapshot is "active" (e.g. hasn't expired) based on a time to live.""" 

57 return self.timestamp + ttl > datetime.now() 

58 

59 def calc( 

60 self, 

61 usage: types.AbstractUsage, 

62 model_ref: str, 

63 provider_id: str | None, 

64 provider_api_url: str | None, 

65 genai_request_timestamp: datetime | None, 

66 ) -> types.PriceCalculation: 

67 """Calculate the price for the given usage.""" 

68 genai_request_timestamp = genai_request_timestamp or datetime.now(tz=timezone.utc) 

69 

70 provider, model = self.find_provider_model(model_ref, None, provider_id, provider_api_url) 

71 return model.calc_price( 

72 usage, 

73 provider, 

74 genai_request_timestamp=genai_request_timestamp, 

75 auto_update_timestamp=self.timestamp if self.from_auto_update else None, 

76 ) 

77 

78 def extract_usage( 

79 self, 

80 response_data: Any, 

81 provider_id: types.ProviderID | str | None = None, 

82 provider_api_url: str | None = None, 

83 api_flavor: str = 'default', 

84 ) -> types.ExtractedUsage: 

85 provider = self.find_provider(None, provider_id, provider_api_url) 

86 model_ref, usage = provider.extract_usage(response_data, api_flavor=api_flavor) 

87 if model_ref is not None: 

88 _, model = self.find_provider_model(model_ref, provider, None, None) 

89 else: 

90 model = None 

91 return types.ExtractedUsage(usage, model, provider, self.timestamp if self.from_auto_update else None) 

92 

93 def find_provider_model( 

94 self, 

95 model_ref: str, 

96 provider: types.Provider | None, 

97 provider_id: str | None, 

98 provider_api_url: str | None, 

99 ) -> tuple[types.Provider, types.ModelInfo]: 

100 """Find the provider and model for the given model reference and optional provider identifier.""" 

101 model_ref = model_ref.lower() 

102 

103 # Handle litellm provider_id by extracting actual provider from model name prefix 

104 if provider_id and provider_id.lower() == 'litellm' and '/' in model_ref: 

105 actual_provider_id, actual_model_ref = model_ref.split('/', 1) 

106 # Only use the extracted provider if it exists 

107 if actual_provider_id and find_provider_by_id(self.providers, actual_provider_id): 107 ↛ 111line 107 didn't jump to line 111 because the condition on line 107 was always true

108 provider_id = actual_provider_id 

109 model_ref = actual_model_ref 

110 

111 if provider: 

112 if provider_model := self._lookup_cache.get((provider.id, None, model_ref)): 

113 return provider_model 

114 else: 

115 if provider_model := self._lookup_cache.get((provider_id, provider_api_url, model_ref)): 

116 return provider_model 

117 

118 provider = self.find_provider(model_ref, provider_id, provider_api_url) 

119 

120 if model := provider.find_model(model_ref, all_providers=self.providers): 

121 self._lookup_cache[(provider_id, provider_api_url, model_ref)] = ret = provider, model 

122 return ret 

123 else: 

124 raise LookupError(f'Unable to find model with {model_ref=!r} in {provider.id}') 

125 

126 def find_provider( 

127 self, 

128 model_ref: str | None, 

129 provider_id: str | None, 

130 provider_api_url: str | None, 

131 ) -> types.Provider: 

132 if provider_id is not None: 

133 if provider := find_provider_by_id(self.providers, provider_id): 

134 return provider 

135 # Special case for litellm: fall back to model matching if provider not found 

136 if provider_id.lower() != 'litellm': 

137 raise LookupError(f'Unable to find provider {provider_id=!r}') 

138 

139 if provider_api_url is not None: 

140 for provider in self.providers: 

141 if re.match(provider.api_pattern, provider_api_url): 

142 return provider 

143 raise LookupError(f'Unable to find provider {provider_api_url=!r}') 

144 

145 if model_ref: 145 ↛ 150line 145 didn't jump to line 150 because the condition on line 145 was always true

146 for provider in self.providers: 

147 if provider.model_match is not None and provider.model_match.is_match(model_ref): 

148 return provider 

149 

150 raise LookupError(f'Unable to find provider with model matching {model_ref!r}') 

151 

152 

153def find_provider_by_id(providers: list[types.Provider], provider_id: str) -> types.Provider | None: 

154 """Find a provider by matching against provider_match logic. 

155 

156 Args: 

157 providers: List of available providers 

158 provider_id: The provider ID to match 

159 

160 Returns: 

161 The matching provider or None 

162 """ 

163 normalized_provider_id = provider_id.lower().strip() 

164 

165 for provider in providers: 

166 if provider.id == normalized_provider_id: 

167 return provider 

168 

169 for provider in providers: 

170 if provider.provider_match and provider.provider_match.is_match(normalized_provider_id): 

171 return provider 

172 

173 return None