feat: Implement the initial IPSExtractor project structure, including application logic, GUI, core utilities, and documentation.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
"""Utilidades para el cálculo de costos de tokens.
|
||||
|
||||
Define precios por modelo y funciones para estimar costos a partir del uso de tokens
|
||||
(prompt, completion, cached).
|
||||
"""
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
# Precios actualizados por 1M tokens (USD) - Enero 2026
|
||||
MODEL_PRICING = {
|
||||
"gpt-4.1": {
|
||||
"input": 2.00,
|
||||
"cached_input": 0.50,
|
||||
"output": 8.00,
|
||||
},
|
||||
"gpt-4.1-mini": {
|
||||
"input": 0.40,
|
||||
"cached_input": 0.10,
|
||||
"output": 1.60,
|
||||
},
|
||||
"gpt-4.1-nano": {
|
||||
"input": 0.10,
|
||||
"cached_input": 0.03,
|
||||
"output": 0.40,
|
||||
},
|
||||
"gpt-5.1": {
|
||||
"input": 1.25,
|
||||
"cached_input": 0.13,
|
||||
"output": 10.00,
|
||||
}
|
||||
}
|
||||
|
||||
# Legacy mapping o fase por defecto (si no se especifica modelo)
|
||||
PRICING = {
|
||||
"extract": MODEL_PRICING["gpt-4.1"],
|
||||
"normalize": MODEL_PRICING["gpt-4.1-mini"],
|
||||
}
|
||||
|
||||
|
||||
def compute_cost_from_usage(
|
||||
usage: Dict[str, Any] | None,
|
||||
phase: str = "extract",
|
||||
model: Optional[str] = None
|
||||
) -> Dict[str, Any] | None:
|
||||
"""Calcula el costo estimado basado en el uso de tokens, la fase o el modelo específico."""
|
||||
if not isinstance(usage, dict):
|
||||
return None
|
||||
|
||||
prompt_tokens = int(usage.get("prompt_tokens") or 0)
|
||||
completion_tokens = int(usage.get("completion_tokens") or 0)
|
||||
|
||||
cached_tokens = 0
|
||||
try:
|
||||
details = usage.get("prompt_tokens_details") or {}
|
||||
cached_tokens = int(details.get("cached_tokens") or 0)
|
||||
except Exception:
|
||||
cached_tokens = 0
|
||||
|
||||
non_cached_prompt = max(prompt_tokens - cached_tokens, 0)
|
||||
|
||||
# Determinar tarifas
|
||||
rates = None
|
||||
if model and model in MODEL_PRICING:
|
||||
rates = MODEL_PRICING[model]
|
||||
else:
|
||||
# Fallback a fase (backward compatibility)
|
||||
rates = PRICING.get(phase, PRICING["extract"])
|
||||
|
||||
rate_in = rates["input"]
|
||||
rate_cached = rates["cached_input"]
|
||||
rate_out = rates["output"]
|
||||
|
||||
cost_input = (non_cached_prompt / 1_000_000.0) * rate_in
|
||||
cost_cached = (cached_tokens / 1_000_000.0) * rate_cached
|
||||
cost_output = (completion_tokens / 1_000_000.0) * rate_out
|
||||
|
||||
return {
|
||||
"input_usd": round(cost_input, 6),
|
||||
"cached_input_usd": round(cost_cached, 6),
|
||||
"output_usd": round(cost_output, 6),
|
||||
"total_usd": round(cost_input + cost_cached + cost_output, 6),
|
||||
}
|
||||
|
||||
|
||||
def sum_costs(a: Dict[str, Any] | None, b: Dict[str, Any] | None) -> Dict[str, Any]:
|
||||
"""Suma dos objetos de costos."""
|
||||
def safe(x): return float(x) if x is not None else 0.0
|
||||
|
||||
input_usd = safe((a or {}).get("input_usd")) + safe((b or {}).get("input_usd"))
|
||||
cached_usd = safe((a or {}).get("cached_input_usd")) + safe((b or {}).get("cached_input_usd"))
|
||||
output_usd = safe((a or {}).get("output_usd")) + safe((b or {}).get("output_usd"))
|
||||
|
||||
total = {
|
||||
"input_usd": round(input_usd, 6),
|
||||
"cached_input_usd": round(cached_usd, 6),
|
||||
"output_usd": round(output_usd, 6),
|
||||
}
|
||||
total["total_usd"] = round(total["input_usd"] + total["cached_input_usd"] + total["output_usd"], 6)
|
||||
return total
|
||||
Reference in New Issue
Block a user