feat: Implement Cost Verification & Audit Engine (TASK-004) v1.3.2

This commit is contained in:
rin.kagamine committed 2026-06-26 21:16:23 -04:00
1 parent 2bd50db173
commit f42a3247dc
3 files changed
+116 -6

No files matched your search

+90 -6
View File
@@ -5,7 +5,7 @@ author_url: https://rin.captcouch.xyz
description: Check LiteLLM proxy health + auth status and get cost breakdowns with configurable timerange. Two functions, that's it.
required_open_webui_version: 0.4.0
requirements: requests
version: 1.2.2
version: 1.3.2
licence: MIT
"""
@@ -20,6 +20,56 @@ class Tools:
def __init__(self):
self.valves = self.Valves()
# TASK-004: Hardcore FinOps Audit Registry (Standard Tier Pricing)
MODEL_PRICING = {
"gemini/gemini-3.5-flash": {
"input_uncached": 1.50 / 1_000_000,
"input_cached": 0.15 / 1_000_000,
"output": 9.00 / 1_000_000,
},
"gemini/gemini-3-flash-preview": {
"input_uncached": 0.50 / 1_000_000,
"input_cached": 0.05 / 1_000_000,
"output": 3.00 / 1_000_000,
},
"gemini/gemini-2.5-flash": {
"input_uncached": 0.30 / 1_000_000,
"input_cached": 0.03 / 1_000_000,
"output": 2.50 / 1_000_000,
},
"gemini/gemini-3.1-flash-lite": {
"input_uncached": 0.25 / 1_000_000,
"input_cached": 0.025 / 1_000_000,
"output": 1.50 / 1_000_000,
},
"gemini/gemini-3.1-pro-preview": {
"input_uncached": 2.00 / 1_000_000,
"input_cached": 0.20 / 1_000_000,
"output": 12.00 / 1_000_000,
}
}
def _audit_cost(self, model: str, prompt: int, cached: int, completion: int, reported_cost: float) -> dict:
"""Audit reported cost against our pricing registry, factoring in context caching discounts."""
pricing = self.MODEL_PRICING.get(model)
if not pricing:
return {"audited": reported_cost, "delta": 0.0, "status": "unsupported", "display": ""}
# Subtract cached tokens from prompt before applying standard rate
uncached = max(0, prompt - cached)
calculated = (cached * pricing["input_cached"]) + (uncached * pricing["input_uncached"]) + (completion * pricing["output"])
delta = reported_cost - calculated
# Flag discrepancies greater than half a cent
if abs(delta) > 0.005:
status = "mismatch"
display = f" [calc: ${calculated:.4f} ⚠️]"
else:
status = "ok"
display = f" [calc: ${calculated:.4f} ✓]"
return {"audited": calculated, "delta": delta, "status": status, "display": display}
class Valves(BaseModel):
"""LiteLLM proxy connection settings."""
litellm_url: str = Field(
@@ -279,6 +329,7 @@ class Tools:
model_totals[model_name] = {
"spend": 0, "requests": 0, "prompt": 0,
"completion": 0, "total": 0, "failed": 0,
"cache_read": 0, "cache_created": 0
}
model_totals[model_name]["spend"] += m_metrics.get("spend", 0)
model_totals[model_name]["requests"] += m_metrics.get("successful_requests", 0)
@@ -286,6 +337,8 @@ class Tools:
model_totals[model_name]["prompt"] += m_metrics.get("prompt_tokens", 0)
model_totals[model_name]["completion"] += m_metrics.get("completion_tokens", 0)
model_totals[model_name]["total"] += m_metrics.get("total_tokens", 0)
model_totals[model_name]["cache_read"] += m_metrics.get("cache_read_input_tokens", 0)
model_totals[model_name]["cache_created"] += m_metrics.get("cache_creation_input_tokens", 0)
# Build per-date breakdown
date_lines = []
@@ -316,16 +369,32 @@ class Tools:
max_name = 24
if len(display) > max_name:
display = display[:max_name - 1] + "…"
# Build token string with split and cache indicator
p_tokens = mt["prompt"]
c_tokens = mt["completion"]
# Compact token numbers
p_str = f"{p_tokens / 1000:.1f}k" if p_tokens >= 1000 else str(p_tokens)
c_str = f"{c_tokens / 1000:.1f}k" if c_tokens >= 1000 else str(c_tokens)
token_display = f"{p_str}→{c_str}"
# Cache indicator
cache_info = ""
if mt["cache_read"] > 0:
hit_rate = (mt["cache_read"] / p_tokens * 100) if p_tokens > 0 else 0
cache_info = f" 💾{hit_rate:.0f}%"
model_lines.append(
f" {display:25s} ${mt['spend']:7.2f} | "
f"{mt['requests']:3d} ok, {mt['failed']:d} fail | "
f"{mt['total']:>10,} tokens"
f"{token_display:>10s}{cache_info}"
)
# Assemble report
lines = []
lines.append(f"LiteLLM Cost Report: {resolved_start} to {resolved_end}")
lines.append("=" * 60)
lines.append("=" * 70)
lines.append(f"Total Spend: ${total_spend:.2f}")
lines.append(f"Total Tokens: {total_tokens:,} ({total_prompt:,} prompt + {total_completion:,} completion)")
lines.append(f"Cache Hit Rate: {cache_str}")
@@ -441,8 +510,17 @@ class Tools:
completion_tokens = entry.get("completion_tokens", 0) or 0
total_tokens = entry.get("total_tokens", 0) or 0
# Cache
# Cache extraction (handles Anthropic/OpenAI top-level and Gemini/z.ai nested metadata structures)
cache_read = entry.get("cache_read_input_tokens", 0) or 0
if cache_read == 0:
metadata = entry.get("metadata", {}) or {}
usage_obj = metadata.get("usage_object", {}) or {}
if isinstance(usage_obj, dict):
cache_read = usage_obj.get("cache_read_input_tokens", 0) or 0
if cache_read == 0:
prompt_details = entry.get("prompt_tokens_details", {}) or {}
if isinstance(prompt_details, dict):
cache_read = prompt_details.get("cached_tokens", 0) or 0
# Spend
spend = entry.get("spend", 0) or 0
@@ -470,9 +548,15 @@ class Tools:
base += f" | {token_str}"
# Cost
# Cost & Audit
if show_cost and spend > 0:
base += f" | ${spend:.4f}"
audit = self._audit_cost(entry.get("model", ""), prompt_tokens, cache_read, completion_tokens, spend)
if audit["status"] == "mismatch":
base += f" | ${spend:.4f} (calc: ${audit['audited']:.4f} ⚠️)"
elif audit["status"] == "ok":
base += f" | ${spend:.4f} ✓"
else:
base += f" | ${spend:.4f}"
# Error info
if is_failed: