feat: Credits-System mit Verbrauchsanzeige im User-Dropdown
- DB-Migration: credits_total/credits_used/cost_per_credit auf licenses, token_usage_monthly Tabelle - Orchestrator: Monatliche Token-Aggregation + Credits-Abzug nach Refresh - Auth: Credits-Daten im /me Endpoint + Bugfix fehlende Klammer in get() - Frontend: Credits-Balken im User-Dropdown mit Farbwechsel Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -1316,6 +1316,41 @@ class AgentOrchestrator:
|
||||
f"${usage_acc.total_cost_usd:.4f} ({usage_acc.call_count} Calls)"
|
||||
)
|
||||
|
||||
# Credits-Tracking: Monatliche Aggregation + Credits abziehen
|
||||
if tenant_id and usage_acc.total_cost_usd > 0:
|
||||
year_month = datetime.now(TIMEZONE).strftime('%Y-%m')
|
||||
await db.execute("""
|
||||
INSERT INTO token_usage_monthly
|
||||
(organization_id, year_month, input_tokens, output_tokens,
|
||||
cache_creation_tokens, cache_read_tokens, total_cost_usd, api_calls, refresh_count)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)
|
||||
ON CONFLICT(organization_id, year_month) DO UPDATE SET
|
||||
input_tokens = input_tokens + excluded.input_tokens,
|
||||
output_tokens = output_tokens + excluded.output_tokens,
|
||||
cache_creation_tokens = cache_creation_tokens + excluded.cache_creation_tokens,
|
||||
cache_read_tokens = cache_read_tokens + excluded.cache_read_tokens,
|
||||
total_cost_usd = total_cost_usd + excluded.total_cost_usd,
|
||||
api_calls = api_calls + excluded.api_calls,
|
||||
refresh_count = refresh_count + 1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
""", (tenant_id, year_month,
|
||||
usage_acc.input_tokens, usage_acc.output_tokens,
|
||||
usage_acc.cache_creation_tokens, usage_acc.cache_read_tokens,
|
||||
round(usage_acc.total_cost_usd, 7), usage_acc.call_count))
|
||||
|
||||
# Credits auf Lizenz abziehen
|
||||
lic_cursor = await db.execute(
|
||||
"SELECT cost_per_credit FROM licenses WHERE organization_id = ? AND status = 'active' ORDER BY id DESC LIMIT 1",
|
||||
(tenant_id,))
|
||||
lic = await lic_cursor.fetchone()
|
||||
if lic and lic["cost_per_credit"] and lic["cost_per_credit"] > 0:
|
||||
credits_consumed = usage_acc.total_cost_usd / lic["cost_per_credit"]
|
||||
await db.execute(
|
||||
"UPDATE licenses SET credits_used = COALESCE(credits_used, 0) + ? WHERE organization_id = ? AND status = 'active'",
|
||||
(round(credits_consumed, 2), tenant_id))
|
||||
await db.commit()
|
||||
logger.info(f"Credits: {round(credits_consumed, 1) if lic and lic['cost_per_credit'] else 0} abgezogen für Tenant {tenant_id}")
|
||||
|
||||
# Quellen-Discovery im Background starten
|
||||
if unique_results:
|
||||
asyncio.create_task(_background_discover_sources(unique_results))
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren