-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcost_service.py
More file actions
119 lines (106 loc) · 3.63 KB
/
Copy pathcost_service.py
File metadata and controls
119 lines (106 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import json
import os
from decimal import Decimal, InvalidOperation
from functools import lru_cache
from typing import Any
class CostService:
"""Estimate request exposure from operator-supplied model pricing."""
ENV_NAME = "MODEL_PRICING_USD_PER_MILLION"
@staticmethod
def _decimal(value: Any) -> Decimal | None:
try:
result = Decimal(str(value))
except (InvalidOperation, TypeError, ValueError):
return None
if not result.is_finite() or result < 0:
return None
return result
@staticmethod
def _token_count(value: Any) -> int:
try:
return max(0, int(value or 0))
except (TypeError, ValueError, OverflowError):
return 0
@classmethod
@lru_cache(maxsize=8)
def _parse_pricing_table(
cls,
raw_pricing: str,
) -> dict[str, dict[str, Decimal]]:
if not raw_pricing:
return {}
try:
payload = json.loads(raw_pricing)
except json.JSONDecodeError:
return {}
if not isinstance(payload, dict):
return {}
pricing: dict[str, dict[str, Decimal]] = {}
for model_id, raw_entry in payload.items():
if not isinstance(model_id, str) or not isinstance(raw_entry, dict):
continue
input_price = cls._decimal(
raw_entry.get(
"input",
raw_entry.get("input_cost_per_million"),
)
)
output_price = cls._decimal(
raw_entry.get(
"output",
raw_entry.get("output_cost_per_million"),
)
)
if input_price is None or output_price is None:
continue
pricing[model_id.strip().lower()] = {
"input": input_price,
"output": output_price,
}
return pricing
@classmethod
def pricing_table(cls) -> dict[str, dict[str, Decimal]]:
raw_pricing = os.environ.get(cls.ENV_NAME, "").strip()
return cls._parse_pricing_table(raw_pricing)
@classmethod
def pricing_for(
cls,
model_id: str | None,
provider: str | None = None,
) -> dict[str, Decimal] | None:
if not model_id:
return None
normalized_model = str(model_id).strip().lower()
normalized_provider = str(provider or "").strip().lower()
if ":" in normalized_model:
normalized_provider = normalized_model.split(":", 1)[0]
elif normalized_provider:
normalized_model = f"{normalized_provider}:{normalized_model}"
pricing = cls.pricing_table()
for candidate in (
normalized_model,
f"{normalized_provider}:*" if normalized_provider else "",
"*",
):
if candidate and candidate in pricing:
return pricing[candidate]
return None
@classmethod
def estimate(
cls,
model_id: str | None,
input_tokens: int | None,
output_tokens: int | None,
*,
provider: str | None = None,
) -> float | None:
prices = cls.pricing_for(model_id, provider=provider)
if prices is None:
return None
safe_input_tokens = cls._token_count(input_tokens)
safe_output_tokens = cls._token_count(output_tokens)
total = (
Decimal(safe_input_tokens) * prices["input"]
+ Decimal(safe_output_tokens) * prices["output"]
) / Decimal(1_000_000)
return float(total.quantize(Decimal("0.0000000001")))