feat(costs): Stufe 2 — geschätzte Quellen (lokaler Strom, Cartesia, Notruf)
Lokales LLM: Call-Dauer gemessen -> Stromkosten je Job ((max-idle)*load*n/ 3.6e6*Preis(EUR/kWh)*usd_per_eur), Kategorie llm_local. Cartesia-TTS: Zeichen x cartesia_usd_per_char (tts). Notruf: pro Webhook-Alarm 0,20€*usd_per_eur (alert), nur Doku, nie Sperre. Helfer in costs.py; neue Settings (GPU-Watt/Last/ Strompreis/usd_per_eur/cartesia/alert). Statistik: "exakt vs. geschätzt" (Tooltip + Summenzeile, Kategorien tts/alert/llm_local). Tests: 318. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
66d6708f59
commit
d45e2c9ae3
8 changed files with 85 additions and 5 deletions
|
|
@ -1,8 +1,10 @@
|
|||
import time
|
||||
from collections.abc import AsyncIterator
|
||||
|
||||
import httpx
|
||||
|
||||
from app.core import clock
|
||||
from app.core.costs import add_cost, electricity_usd
|
||||
from app.providers.llm.base import (
|
||||
LLMProvider,
|
||||
lang_instruction,
|
||||
|
|
@ -88,6 +90,7 @@ class LocalOpenAICompatibleLLM(LLMProvider):
|
|||
session_id: str | None = None,
|
||||
language: str | None = None,
|
||||
) -> str:
|
||||
t0 = time.monotonic()
|
||||
async with httpx.AsyncClient(timeout=120) as client:
|
||||
response = await client.post(
|
||||
f"{self.base_url}/chat/completions",
|
||||
|
|
@ -96,7 +99,8 @@ class LocalOpenAICompatibleLLM(LLMProvider):
|
|||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
return data["choices"][0]["message"]["content"]
|
||||
add_cost(electricity_usd(time.monotonic() - t0), "llm_local")
|
||||
return data["choices"][0]["message"]["content"]
|
||||
|
||||
async def stream(
|
||||
self,
|
||||
|
|
@ -106,6 +110,7 @@ class LocalOpenAICompatibleLLM(LLMProvider):
|
|||
language: str | None = None,
|
||||
**kwargs, # z. B. on_tool_start — hier ignoriert (kein Tool-Calling)
|
||||
) -> AsyncIterator[str]:
|
||||
t0 = time.monotonic()
|
||||
async with httpx.AsyncClient(timeout=120) as client:
|
||||
async with client.stream(
|
||||
"POST",
|
||||
|
|
@ -123,3 +128,4 @@ class LocalOpenAICompatibleLLM(LLMProvider):
|
|||
delta = sse_delta(line)
|
||||
if delta:
|
||||
yield delta
|
||||
add_cost(electricity_usd(time.monotonic() - t0), "llm_local")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import httpx
|
||||
|
||||
from app.core.costs import add_cost, cartesia_usd
|
||||
from app.providers.tts.base import TTSProvider
|
||||
|
||||
_API_URL = "https://api.cartesia.ai/tts/bytes"
|
||||
|
|
@ -139,4 +140,5 @@ class CartesiaTTSProvider(TTSProvider):
|
|||
|
||||
if not response.content:
|
||||
raise RuntimeError("Cartesia TTS returned empty audio")
|
||||
add_cost(cartesia_usd(len(text.strip())), "tts") # geschätzt (Zeichen × Preis)
|
||||
return response.content
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue