kalibr 1.1.3a0__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
kalibr/pricing.py ADDED
@@ -0,0 +1,245 @@
1
+ """Centralized pricing data for all LLM vendors.
2
+
3
+ This module serves as the single source of truth for model pricing across
4
+ the entire Kalibr SDK. All cost adapters and instrumentation modules should
5
+ use this pricing data to ensure consistency.
6
+
7
+ All prices are in USD per 1 million tokens, matching the format used by
8
+ major LLM providers (OpenAI, Anthropic, etc.) on their pricing pages.
9
+
10
+ Version: 2026-01
11
+ Last Updated: January 2026
12
+ """
13
+
14
+ from typing import Dict, Optional, Tuple
15
+
16
+ # Pricing version for tracking updates
17
+ PRICING_VERSION = "2026-01"
18
+
19
+ # All prices in USD per 1M tokens
20
+ MODEL_PRICING: Dict[str, Dict[str, Dict[str, float]]] = {
21
+ "openai": {
22
+ # GPT-5 models (future-proofing)
23
+ "gpt-5": {"input": 5.00, "output": 15.00},
24
+ "gpt-5-turbo": {"input": 2.50, "output": 7.50},
25
+ # GPT-4 models
26
+ "gpt-4": {"input": 30.00, "output": 60.00},
27
+ "gpt-4-turbo": {"input": 10.00, "output": 30.00},
28
+ "gpt-4o": {"input": 2.50, "output": 10.00},
29
+ "gpt-4o-mini": {"input": 0.15, "output": 0.60},
30
+ # GPT-3.5 models
31
+ "gpt-3.5-turbo": {"input": 0.50, "output": 1.50},
32
+ "gpt-3.5-turbo-16k": {"input": 1.00, "output": 2.00},
33
+ },
34
+ "anthropic": {
35
+ # Claude 4 models (future-proofing)
36
+ "claude-4-opus": {"input": 15.00, "output": 75.00},
37
+ "claude-4-sonnet": {"input": 3.00, "output": 15.00},
38
+ # Claude 3.5/3.7 models (Sonnet 4 is actually Claude 3.7)
39
+ "claude-sonnet-4": {"input": 3.00, "output": 15.00},
40
+ "claude-3-7-sonnet": {"input": 3.00, "output": 15.00},
41
+ "claude-3-5-sonnet": {"input": 3.00, "output": 15.00},
42
+ # Claude 3 models
43
+ "claude-3-opus": {"input": 15.00, "output": 75.00},
44
+ "claude-3-sonnet": {"input": 3.00, "output": 15.00},
45
+ "claude-3-haiku": {"input": 0.25, "output": 1.25},
46
+ # Claude 2 models
47
+ "claude-2.1": {"input": 8.00, "output": 24.00},
48
+ "claude-2.0": {"input": 8.00, "output": 24.00},
49
+ "claude-instant-1.2": {"input": 0.80, "output": 2.40},
50
+ },
51
+ "google": {
52
+ # Gemini 2.5 models
53
+ "gemini-2.5-pro": {"input": 1.25, "output": 5.00},
54
+ "gemini-2.5-flash": {"input": 0.075, "output": 0.30},
55
+ # Gemini 2.0 models
56
+ "gemini-2.0-flash": {"input": 0.075, "output": 0.30},
57
+ "gemini-2.0-flash-thinking": {"input": 0.075, "output": 0.30},
58
+ # Gemini 1.5 models
59
+ "gemini-1.5-pro": {"input": 1.25, "output": 5.00},
60
+ "gemini-1.5-flash": {"input": 0.075, "output": 0.30},
61
+ "gemini-1.5-flash-8b": {"input": 0.0375, "output": 0.15},
62
+ # Gemini 1.0 models
63
+ "gemini-1.0-pro": {"input": 0.50, "output": 1.50},
64
+ "gemini-pro": {"input": 0.50, "output": 1.50}, # Alias
65
+ },
66
+ }
67
+
68
+ # Default fallback pricing per vendor (highest tier pricing for safety)
69
+ DEFAULT_PRICING: Dict[str, Dict[str, float]] = {
70
+ "openai": {"input": 30.00, "output": 60.00}, # GPT-4 pricing
71
+ "anthropic": {"input": 15.00, "output": 75.00}, # Claude 3 Opus pricing
72
+ "google": {"input": 1.25, "output": 5.00}, # Gemini 1.5 Pro pricing
73
+ }
74
+
75
+
76
+ def normalize_model_name(vendor: str, model_name: str) -> str:
77
+ """Normalize model name to match pricing table keys.
78
+
79
+ Handles version suffixes, date stamps, and common variations.
80
+
81
+ Args:
82
+ vendor: Vendor name (openai, anthropic, google)
83
+ model_name: Raw model name from API
84
+
85
+ Returns:
86
+ Normalized model name that matches pricing table, or original if no match
87
+
88
+ Example:
89
+ >>> normalize_model_name("openai", "gpt-4o-2024-05-13")
90
+ 'gpt-4o'
91
+ >>> normalize_model_name("anthropic", "claude-3-5-sonnet-20240620")
92
+ 'claude-3-5-sonnet'
93
+ """
94
+ vendor = vendor.lower()
95
+ model_lower = model_name.lower()
96
+
97
+ # Get vendor pricing table
98
+ vendor_models = MODEL_PRICING.get(vendor, {})
99
+
100
+ # Direct match
101
+ if model_lower in vendor_models:
102
+ return model_lower
103
+
104
+ # OpenAI fuzzy matching
105
+ if vendor == "openai":
106
+ # Remove date suffixes like -20240513
107
+ base_model = model_lower.split("-2")[0] if "-2" in model_lower else model_lower
108
+
109
+ # Try direct match on base
110
+ if base_model in vendor_models:
111
+ return base_model
112
+
113
+ # Fuzzy match in priority order
114
+ if "gpt-4o-mini" in model_lower:
115
+ return "gpt-4o-mini"
116
+ elif "gpt-4o" in model_lower:
117
+ return "gpt-4o"
118
+ elif "gpt-5-turbo" in model_lower:
119
+ return "gpt-5-turbo"
120
+ elif "gpt-5" in model_lower:
121
+ return "gpt-5"
122
+ elif "gpt-4-turbo" in model_lower:
123
+ return "gpt-4-turbo"
124
+ elif "gpt-4" in model_lower:
125
+ return "gpt-4"
126
+ elif "gpt-3.5-turbo-16k" in model_lower:
127
+ return "gpt-3.5-turbo-16k"
128
+ elif "gpt-3.5" in model_lower:
129
+ return "gpt-3.5-turbo"
130
+
131
+ # Anthropic fuzzy matching
132
+ elif vendor == "anthropic":
133
+ # Try fuzzy matching for versioned models
134
+ if "claude-3.5-sonnet" in model_lower or "claude-3-5-sonnet" in model_lower:
135
+ return "claude-3-5-sonnet"
136
+ elif "claude-sonnet-4" in model_lower or "sonnet-4" in model_lower:
137
+ return "claude-sonnet-4"
138
+ elif "claude-3-7-sonnet" in model_lower:
139
+ return "claude-3-7-sonnet"
140
+ elif "claude-4-opus" in model_lower:
141
+ return "claude-4-opus"
142
+ elif "claude-4-sonnet" in model_lower:
143
+ return "claude-4-sonnet"
144
+ elif "claude-3-opus" in model_lower:
145
+ return "claude-3-opus"
146
+ elif "claude-3-sonnet" in model_lower:
147
+ return "claude-3-sonnet"
148
+ elif "claude-3-haiku" in model_lower:
149
+ return "claude-3-haiku"
150
+ elif "claude-2.1" in model_lower:
151
+ return "claude-2.1"
152
+ elif "claude-2.0" in model_lower or "claude-2" in model_lower:
153
+ return "claude-2.0"
154
+ elif "claude-instant" in model_lower:
155
+ return "claude-instant-1.2"
156
+
157
+ # Google fuzzy matching
158
+ elif vendor == "google":
159
+ # Try fuzzy matching for versioned models
160
+ if "gemini-2.5-pro" in model_lower:
161
+ return "gemini-2.5-pro"
162
+ elif "gemini-2.5-flash" in model_lower:
163
+ return "gemini-2.5-flash"
164
+ elif "gemini-2.0-flash-thinking" in model_lower:
165
+ return "gemini-2.0-flash-thinking"
166
+ elif "gemini-2.0-flash" in model_lower:
167
+ return "gemini-2.0-flash"
168
+ elif "gemini-1.5-flash-8b" in model_lower:
169
+ return "gemini-1.5-flash-8b"
170
+ elif "gemini-1.5-flash" in model_lower:
171
+ return "gemini-1.5-flash"
172
+ elif "gemini-1.5-pro" in model_lower:
173
+ return "gemini-1.5-pro"
174
+ elif "gemini-1.0-pro" in model_lower or "gemini-pro" in model_lower:
175
+ return "gemini-pro"
176
+
177
+ # Return original if no match found
178
+ return model_lower
179
+
180
+
181
+ def get_pricing(
182
+ vendor: str, model_name: str
183
+ ) -> Tuple[Dict[str, float], str]:
184
+ """Get pricing for a specific vendor and model.
185
+
186
+ Args:
187
+ vendor: Vendor name (openai, anthropic, google)
188
+ model_name: Model identifier
189
+
190
+ Returns:
191
+ Tuple of (pricing dict with 'input' and 'output' keys in USD per 1M tokens,
192
+ normalized model name used)
193
+
194
+ Example:
195
+ >>> pricing, normalized = get_pricing("openai", "gpt-4o")
196
+ >>> print(pricing)
197
+ {'input': 2.50, 'output': 10.00}
198
+ >>> print(normalized)
199
+ 'gpt-4o'
200
+ """
201
+ vendor = vendor.lower()
202
+ normalized_model = normalize_model_name(vendor, model_name)
203
+
204
+ # Get vendor pricing table
205
+ vendor_models = MODEL_PRICING.get(vendor, {})
206
+
207
+ # Try to get pricing for normalized model
208
+ pricing = vendor_models.get(normalized_model)
209
+
210
+ # Fall back to default vendor pricing if not found
211
+ if pricing is None:
212
+ pricing = DEFAULT_PRICING.get(vendor, {"input": 20.00, "output": 60.00})
213
+
214
+ return pricing, normalized_model
215
+
216
+
217
+ def compute_cost(
218
+ vendor: str, model_name: str, input_tokens: int, output_tokens: int
219
+ ) -> float:
220
+ """Compute cost in USD for given vendor, model, and token counts.
221
+
222
+ This is a convenience function that combines pricing lookup and cost calculation.
223
+
224
+ Args:
225
+ vendor: Vendor name (openai, anthropic, google)
226
+ model_name: Model identifier
227
+ input_tokens: Number of input tokens
228
+ output_tokens: Number of output tokens
229
+
230
+ Returns:
231
+ Cost in USD (rounded to 6 decimal places)
232
+
233
+ Example:
234
+ >>> cost = compute_cost("openai", "gpt-4o", 1000, 500)
235
+ >>> print(f"${cost:.6f}")
236
+ $0.007500
237
+ """
238
+ pricing, _ = get_pricing(vendor, model_name)
239
+
240
+ # Calculate cost (pricing is per 1M tokens)
241
+ input_cost = (input_tokens / 1_000_000) * pricing["input"]
242
+ output_cost = (output_tokens / 1_000_000) * pricing["output"]
243
+
244
+ return round(input_cost + output_cost, 6)
245
+