diff --git a/backend/igny8_core/admin/reports.py b/backend/igny8_core/admin/reports.py index c857d06e..4a0cc35b 100644 --- a/backend/igny8_core/admin/reports.py +++ b/backend/igny8_core/admin/reports.py @@ -251,3 +251,325 @@ def data_quality_report(request): context.update(admin_context) return render(request, 'admin/reports/data_quality.html', context) + + +@staff_member_required +def token_usage_report(request): + """Comprehensive token usage analytics with multi-dimensional insights""" + from igny8_core.business.billing.models import CreditUsageLog + from igny8_core.auth.models import Account + from decimal import Decimal + + # Date filter setup + days_filter = request.GET.get('days', '30') + try: + days = int(days_filter) + except ValueError: + days = 30 + + start_date = timezone.now() - timedelta(days=days) + + # Base queryset - filter for records with token data + logs = CreditUsageLog.objects.filter( + created_at__gte=start_date, + tokens_input__isnull=False, + tokens_output__isnull=False + ) + + # Total statistics + total_tokens_input = logs.aggregate(total=Sum('tokens_input'))['total'] or 0 + total_tokens_output = logs.aggregate(total=Sum('tokens_output'))['total'] or 0 + total_tokens = total_tokens_input + total_tokens_output + total_calls = logs.count() + avg_tokens_per_call = total_tokens / total_calls if total_calls > 0 else 0 + + # Token usage by model + token_by_model = logs.values('model_used').annotate( + total_tokens_input=Sum('tokens_input'), + total_tokens_output=Sum('tokens_output'), + call_count=Count('id'), + total_cost=Sum('cost_usd') + ).order_by('-total_tokens_input')[:10] + + # Add total_tokens to each model and sort by total + for model in token_by_model: + model['total_tokens'] = (model['total_tokens_input'] or 0) + (model['total_tokens_output'] or 0) + model['avg_tokens'] = model['total_tokens'] / model['call_count'] if model['call_count'] > 0 else 0 + model['model'] = model['model_used'] # Add alias for template + token_by_model = sorted(token_by_model, key=lambda x: x['total_tokens'], reverse=True) + + # Token usage by function/operation + token_by_function = logs.values('operation_type').annotate( + total_tokens_input=Sum('tokens_input'), + total_tokens_output=Sum('tokens_output'), + call_count=Count('id'), + total_cost=Sum('cost_usd') + ).order_by('-total_tokens_input')[:10] + + # Add total_tokens to each function and sort by total + for func in token_by_function: + func['total_tokens'] = (func['total_tokens_input'] or 0) + (func['total_tokens_output'] or 0) + func['avg_tokens'] = func['total_tokens'] / func['call_count'] if func['call_count'] > 0 else 0 + func['function'] = func['operation_type'] # Add alias for template + token_by_function = sorted(token_by_function, key=lambda x: x['total_tokens'], reverse=True) + + # Token usage by account (top consumers) + token_by_account = logs.values('account__name', 'account_id').annotate( + total_tokens_input=Sum('tokens_input'), + total_tokens_output=Sum('tokens_output'), + call_count=Count('id'), + total_cost=Sum('cost_usd') + ).order_by('-total_tokens_input')[:15] + + # Add total_tokens to each account and sort by total + for account in token_by_account: + account['total_tokens'] = (account['total_tokens_input'] or 0) + (account['total_tokens_output'] or 0) + token_by_account = sorted(token_by_account, key=lambda x: x['total_tokens'], reverse=True)[:15] + + # Daily token trends (time series) + daily_data = [] + daily_labels = [] + for i in range(days): + day = timezone.now().date() - timedelta(days=days-i-1) + day_logs = logs.filter(created_at__date=day) + day_tokens_input = day_logs.aggregate(total=Sum('tokens_input'))['total'] or 0 + day_tokens_output = day_logs.aggregate(total=Sum('tokens_output'))['total'] or 0 + day_tokens = day_tokens_input + day_tokens_output + daily_labels.append(day.strftime('%m/%d')) + daily_data.append(int(day_tokens)) + + # Token efficiency metrics (CreditUsageLog doesn't have error field, so assume all successful) + success_rate = 100.0 + successful_tokens = total_tokens + wasted_tokens = 0 + + # Create tokens_by_status for template compatibility + tokens_by_status = [{ + 'error': None, + 'total_tokens': total_tokens, + 'call_count': total_calls, + 'avg_tokens': avg_tokens_per_call + }] + + # Peak usage times (hour of day) + hourly_usage = logs.extra( + select={'hour': "EXTRACT(hour FROM created_at)"} + ).values('hour').annotate( + token_input=Sum('tokens_input'), + token_output=Sum('tokens_output'), + call_count=Count('id') + ).order_by('hour') + + # Add total token_count for each hour + for hour_data in hourly_usage: + hour_data['token_count'] = (hour_data['token_input'] or 0) + (hour_data['token_output'] or 0) + + # Cost efficiency + total_cost = logs.aggregate(total=Sum('cost_usd'))['total'] or Decimal('0.00') + cost_per_1k_tokens = (total_cost / (total_tokens / 1000)) if total_tokens > 0 else Decimal('0.00') + + context = { + 'title': 'Token Usage Report', + 'days_filter': days, + 'total_tokens': int(total_tokens), + 'total_calls': total_calls, + 'avg_tokens_per_call': round(avg_tokens_per_call, 2), + 'token_by_model': list(token_by_model), + 'token_by_function': list(token_by_function), + 'token_by_account': list(token_by_account), + 'daily_labels': json.dumps(daily_labels), + 'daily_data': json.dumps(daily_data), + 'tokens_by_status': list(tokens_by_status), + 'success_rate': round(success_rate, 2), + 'successful_tokens': int(successful_tokens), + 'wasted_tokens': int(wasted_tokens), + 'hourly_usage': list(hourly_usage), + 'total_cost': float(total_cost), + 'cost_per_1k_tokens': float(cost_per_1k_tokens), + 'current_app': '_reports', # For active menu state + } + + # Merge with admin context + from igny8_core.admin.site import admin_site + admin_context = admin_site.each_context(request) + context.update(admin_context) + + return render(request, 'admin/reports/token_usage.html', context) + + +@staff_member_required +def ai_cost_analysis(request): + """Multi-dimensional AI cost analysis with model pricing, trends, and predictions""" + from igny8_core.business.billing.models import CreditUsageLog + from igny8_core.auth.models import Account + from decimal import Decimal + + # Date filter setup + days_filter = request.GET.get('days', '30') + try: + days = int(days_filter) + except ValueError: + days = 30 + + start_date = timezone.now() - timedelta(days=days) + + # Base queryset - filter for records with cost data + logs = CreditUsageLog.objects.filter( + created_at__gte=start_date, + cost_usd__isnull=False + ) + + # Overall cost metrics + total_cost = logs.aggregate(total=Sum('cost_usd'))['total'] or Decimal('0.00') + total_calls = logs.count() + avg_cost_per_call = logs.aggregate(avg=Avg('cost_usd'))['avg'] or Decimal('0.00') + total_tokens_input = logs.aggregate(total=Sum('tokens_input'))['total'] or 0 + total_tokens_output = logs.aggregate(total=Sum('tokens_output'))['total'] or 0 + total_tokens = total_tokens_input + total_tokens_output + + # Cost by model with efficiency metrics + cost_by_model = logs.values('model_used').annotate( + total_cost=Sum('cost_usd'), + call_count=Count('id'), + avg_cost=Avg('cost_usd'), + total_tokens_input=Sum('tokens_input'), + total_tokens_output=Sum('tokens_output') + ).order_by('-total_cost') + + # Add cost efficiency (cost per 1K tokens) for each model + for model in cost_by_model: + model['total_tokens'] = (model['total_tokens_input'] or 0) + (model['total_tokens_output'] or 0) + model['avg_tokens'] = model['total_tokens'] / model['call_count'] if model['call_count'] > 0 else 0 + model['model'] = model['model_used'] # Add alias for template + if model['total_tokens'] and model['total_tokens'] > 0: + model['cost_per_1k_tokens'] = float(model['total_cost']) / (model['total_tokens'] / 1000) + else: + model['cost_per_1k_tokens'] = 0 + + # Cost by account (top spenders) + cost_by_account = logs.values('account__name', 'account_id').annotate( + total_cost=Sum('cost_usd'), + call_count=Count('id'), + total_tokens_input=Sum('tokens_input'), + total_tokens_output=Sum('tokens_output'), + avg_cost=Avg('cost_usd') + ).order_by('-total_cost')[:15] + + # Add total_tokens to each account + for account in cost_by_account: + account['total_tokens'] = (account['total_tokens_input'] or 0) + (account['total_tokens_output'] or 0) + + # Cost by function/operation + cost_by_function = logs.values('operation_type').annotate( + total_cost=Sum('cost_usd'), + call_count=Count('id'), + avg_cost=Avg('cost_usd'), + total_tokens_input=Sum('tokens_input'), + total_tokens_output=Sum('tokens_output') + ).order_by('-total_cost')[:10] + + # Add total_tokens and function alias + for func in cost_by_function: + func['total_tokens'] = (func['total_tokens_input'] or 0) + (func['total_tokens_output'] or 0) + func['function'] = func['operation_type'] # Add alias for template + + # Daily cost trends (time series) + daily_cost_data = [] + daily_cost_labels = [] + daily_call_data = [] + + for i in range(days): + day = timezone.now().date() - timedelta(days=days-i-1) + day_logs = logs.filter(created_at__date=day) + day_cost = day_logs.aggregate(total=Sum('cost_usd'))['total'] or Decimal('0.00') + day_calls = day_logs.count() + + daily_cost_labels.append(day.strftime('%m/%d')) + daily_cost_data.append(float(day_cost)) + daily_call_data.append(day_calls) + + # Cost prediction (simple linear extrapolation) + if len(daily_cost_data) > 7: + recent_avg_daily = sum(daily_cost_data[-7:]) / 7 + projected_monthly = recent_avg_daily * 30 + else: + projected_monthly = 0 + + # Failed requests cost (CreditUsageLog doesn't track errors, so no failed cost) + failed_cost = Decimal('0.00') + + # Cost anomalies (calls costing > 3x average) + if avg_cost_per_call > 0: + anomaly_threshold = float(avg_cost_per_call) * 3 + anomalies = logs.filter(cost_usd__gt=anomaly_threshold).values( + 'model_used', 'operation_type', 'account__name', 'cost_usd', 'tokens_input', 'tokens_output', 'created_at' + ).order_by('-cost_usd')[:10] + # Add aliases and calculate total tokens for each anomaly + for anomaly in anomalies: + anomaly['model'] = anomaly['model_used'] + anomaly['function'] = anomaly['operation_type'] + anomaly['cost'] = anomaly['cost_usd'] + anomaly['tokens'] = (anomaly['tokens_input'] or 0) + (anomaly['tokens_output'] or 0) + else: + anomalies = [] + + # Model comparison matrix + model_comparison = [] + for model_data in cost_by_model: + model_name = model_data['model'] + model_comparison.append({ + 'model': model_name, + 'total_cost': float(model_data['total_cost']), + 'calls': model_data['call_count'], + 'avg_cost': float(model_data['avg_cost']), + 'total_tokens': model_data['total_tokens'], + 'cost_per_1k': model_data['cost_per_1k_tokens'], + }) + + # Cost distribution percentages + if total_cost > 0: + for item in cost_by_model: + item['cost_percentage'] = float((item['total_cost'] / total_cost) * 100) + + # Peak cost hours + hourly_cost = logs.extra( + select={'hour': "EXTRACT(hour FROM created_at)"} + ).values('hour').annotate( + total_cost=Sum('cost_usd'), + call_count=Count('id') + ).order_by('hour') + + # Cost efficiency score (CreditUsageLog doesn't track errors, assume all successful) + successful_cost = total_cost + efficiency_score = 100.0 + + context = { + 'title': 'AI Cost Analysis', + 'days_filter': days, + 'total_cost': float(total_cost), + 'total_calls': total_calls, + 'avg_cost_per_call': float(avg_cost_per_call), + 'total_tokens': int(total_tokens), + 'cost_by_model': list(cost_by_model), + 'cost_by_account': list(cost_by_account), + 'cost_by_function': list(cost_by_function), + 'daily_cost_labels': json.dumps(daily_cost_labels), + 'daily_cost_data': json.dumps(daily_cost_data), + 'daily_call_data': json.dumps(daily_call_data), + 'projected_monthly': round(projected_monthly, 2), + 'failed_cost': float(failed_cost), + 'wasted_percentage': float((failed_cost / total_cost * 100) if total_cost > 0 else 0), + 'anomalies': list(anomalies), + 'model_comparison': model_comparison, + 'hourly_cost': list(hourly_cost), + 'efficiency_score': round(efficiency_score, 2), + 'successful_cost': float(successful_cost), + 'current_app': '_reports', # For active menu state + } + + # Merge with admin context + from igny8_core.admin.site import admin_site + admin_context = admin_site.each_context(request) + context.update(admin_context) + + return render(request, 'admin/reports/ai_cost_analysis.html', context) diff --git a/backend/igny8_core/admin/site.py b/backend/igny8_core/admin/site.py index bea05422..5f946258 100644 --- a/backend/igny8_core/admin/site.py +++ b/backend/igny8_core/admin/site.py @@ -24,7 +24,10 @@ class Igny8AdminSite(UnfoldAdminSite): """Get admin URLs with dashboard and reports available""" from django.urls import path from .dashboard import admin_dashboard - from .reports import revenue_report, usage_report, content_report, data_quality_report + from .reports import ( + revenue_report, usage_report, content_report, data_quality_report, + token_usage_report, ai_cost_analysis + ) urls = super().get_urls() custom_urls = [ @@ -33,6 +36,8 @@ class Igny8AdminSite(UnfoldAdminSite): path('reports/usage/', self.admin_view(usage_report), name='report_usage'), path('reports/content/', self.admin_view(content_report), name='report_content'), path('reports/data-quality/', self.admin_view(data_quality_report), name='report_data_quality'), + path('reports/token-usage/', self.admin_view(token_usage_report), name='report_token_usage'), + path('reports/ai-cost-analysis/', self.admin_view(ai_cost_analysis), name='report_ai_cost_analysis'), ] return custom_urls + urls @@ -125,121 +130,109 @@ class Igny8AdminSite(UnfoldAdminSite): # Define our custom groups with their models (using object_name) # Organized by business function - Material icons configured in Unfold custom_groups = { - 'Accounts & Users': { + 'Accounts & Tenancy': { 'models': [ ('igny8_core_auth', 'Account'), ('igny8_core_auth', 'User'), ('igny8_core_auth', 'Site'), ('igny8_core_auth', 'Sector'), ('igny8_core_auth', 'SiteUserAccess'), - ('igny8_core_auth', 'Plan'), - ('igny8_core_auth', 'Subscription'), - ('igny8_core_auth', 'PasswordResetToken'), + ], + }, + 'Global Resources': { + 'models': [ ('igny8_core_auth', 'Industry'), ('igny8_core_auth', 'IndustrySector'), ('igny8_core_auth', 'SeedKeyword'), ], }, - 'Billing & Tenancy': { + 'Plans and Billing': { 'models': [ + ('igny8_core_auth', 'Plan'), + ('igny8_core_auth', 'Subscription'), ('billing', 'Invoice'), ('billing', 'Payment'), - ('billing', 'CreditTransaction'), - ('billing', 'CreditUsageLog'), ('billing', 'CreditPackage'), ('billing', 'PaymentMethodConfig'), ('billing', 'AccountPaymentMethod'), + ], + }, + 'Credits': { + 'models': [ + ('billing', 'CreditTransaction'), + ('billing', 'CreditUsageLog'), ('billing', 'CreditCostConfig'), ('billing', 'PlanLimitUsage'), ], }, - 'Writer Module': { + 'Content Planning': { 'models': [ - ('writer', 'Content'), - ('writer', 'Tasks'), - ('writer', 'Images'), - ('writer', 'ContentTaxonomy'), - ('writer', 'ContentAttribute'), - ('writer', 'ContentTaxonomyRelation'), - ('writer', 'ContentClusterMap'), - ], - }, - 'Planner': { - 'models': [ - ('planner', 'Clusters'), ('planner', 'Keywords'), + ('planner', 'Clusters'), ('planner', 'ContentIdeas'), ], }, - 'Publishing': { + 'Content Generation': { 'models': [ + ('writer', 'Tasks'), + ('writer', 'Content'), + ('writer', 'Images'), + ], + }, + 'Taxonomy & Organization': { + 'models': [ + ('writer', 'ContentTaxonomy'), + ('writer', 'ContentTaxonomyRelation'), + ('writer', 'ContentClusterMap'), + ('writer', 'ContentAttribute'), + ], + }, + 'Publishing & Integration': { + 'models': [ + ('integration', 'SiteIntegration'), + ('integration', 'SyncEvent'), ('publishing', 'PublishingRecord'), + ('system', 'PublishingChannel'), ('publishing', 'DeploymentRecord'), ], }, - 'Optimization': { - 'models': [ - ('optimization', 'OptimizationTask'), - ], - }, - 'Automation': { + 'AI & Automation': { 'models': [ + ('system', 'IntegrationSettings'), + ('system', 'AIPrompt'), + ('system', 'Strategy'), + ('system', 'AuthorProfile'), + ('system', 'APIKey'), + ('system', 'WebhookConfig'), ('automation', 'AutomationConfig'), ('automation', 'AutomationRun'), ], }, - 'Integration': { + 'System Settings': { 'models': [ - ('integration', 'SiteIntegration'), - ('integration', 'SyncEvent'), - ], - }, - 'AI Framework': { - 'models': [ - ('ai', 'AITaskLog'), - ], - }, - 'System Configuration': { - 'models': [ - ('system', 'AIPrompt'), - ('system', 'Strategy'), - ('system', 'AuthorProfile'), + ('contenttypes', 'ContentType'), ('system', 'ContentTemplate'), ('system', 'TaxonomyConfig'), ('system', 'SystemSetting'), ('system', 'ContentTypeConfig'), - ('system', 'PublishingChannel'), - ('system', 'APIKey'), - ('system', 'WebhookConfig'), ('system', 'NotificationConfig'), - ('system', 'AuditLog'), ], }, - 'Celery Results': { - 'models': [ - ('django_celery_results', 'TaskResult'), - ('django_celery_results', 'GroupResult'), - ], - }, - 'Content Types': { - 'models': [ - ('contenttypes', 'ContentType'), - ], - }, - 'Administration': { - 'models': [ - ('admin', 'LogEntry'), - ], - }, - 'Authentication and Authorization': { + 'Django Admin': { 'models': [ ('auth', 'Group'), ('auth', 'Permission'), + ('igny8_core_auth', 'PasswordResetToken'), + ('sessions', 'Session'), ], }, - 'Sessions': { + 'Tasks & Logging': { 'models': [ - ('sessions', 'Session'), + ('ai', 'AITaskLog'), + ('system', 'AuditLog'), + ('admin', 'LogEntry'), + ('django_celery_results', 'TaskResult'), + ('django_celery_results', 'GroupResult'), ], }, } @@ -292,6 +285,20 @@ class Igny8AdminSite(UnfoldAdminSite): 'view_only': True, 'perms': {'view': True}, }, + { + 'name': 'Token Usage Report', + 'object_name': 'TokenUsageReport', + 'admin_url': '/admin/reports/token-usage/', + 'view_only': True, + 'perms': {'view': True}, + }, + { + 'name': 'AI Cost Analysis', + 'object_name': 'AICostAnalysis', + 'admin_url': '/admin/reports/ai-cost-analysis/', + 'view_only': True, + 'perms': {'view': True}, + }, ], }) diff --git a/backend/igny8_core/templates/admin/reports/ai_cost_analysis.html b/backend/igny8_core/templates/admin/reports/ai_cost_analysis.html new file mode 100644 index 00000000..62980e45 --- /dev/null +++ b/backend/igny8_core/templates/admin/reports/ai_cost_analysis.html @@ -0,0 +1,349 @@ +{% extends "admin/base_site.html" %} +{% load static %} + +{% block content %} +
+ +
+
+

AI Cost Analysis

+

Comprehensive cost breakdown with model pricing and predictions

+
+
+
+ + +
+
+
+ + +
+
+

Total Cost

+

${{ total_cost|floatformat:2 }}

+

{{ total_calls }} API calls

+
+
+

Avg Cost/Call

+

${{ avg_cost_per_call|floatformat:4 }}

+
+
+

Projected Monthly

+

${{ projected_monthly|floatformat:2 }}

+

Based on last 7 days

+
+
+

Efficiency Score

+

{{ efficiency_score }}%

+

Successful cost ratio

+
+
+

Wasted Cost

+

${{ failed_cost|floatformat:2 }}

+

{{ wasted_percentage|floatformat:1 }}% of total

+
+
+ + +
+

Daily Cost & Volume Trends

+ +
+ + +
+

Model Cost Comparison & Efficiency

+
+ + + + + + + + + + + + + + {% for model in cost_by_model %} + + + + + + + + + + {% endfor %} + +
ModelTotal Cost% of TotalAPI CallsAvg CostTotal TokensCost/1K Tokens
+ + {{ model.model|default:"Unknown" }} + + + ${{ model.total_cost|floatformat:2 }} + + {{ model.cost_percentage|floatformat:1 }}% +
+
+
+
+ {{ model.call_count }} + + ${{ model.avg_cost|floatformat:4 }} + + {{ model.total_tokens|floatformat:0 }} + + ${{ model.cost_per_1k_tokens|floatformat:4 }} +
+
+
+ + +
+ +
+

Top Spenders (By Account)

+
+ + + + + + + + + + {% for account in cost_by_account %} + + + + + + {% endfor %} + +
AccountCostCalls
+ + {{ account.account__name|default:"Unknown" }} + + + ${{ account.total_cost|floatformat:2 }} + + {{ account.call_count }} +
+
+
+ + +
+

Cost by Function/Operation

+
+ + + + + + + + + + {% for func in cost_by_function %} + + + + + + {% endfor %} + +
FunctionCostCalls
+ {{ func.function|default:"Unknown" }} + + ${{ func.total_cost|floatformat:2 }} + + {{ func.call_count }} +
+
+
+
+ + + {% if anomalies %} +
+
+
+ + + +
+
+

Cost Anomalies Detected

+

The following API calls had unusually high costs (>3x average):

+
+ + + + + + + + + + + + + {% for anomaly in anomalies %} + + + + + + + + + {% endfor %} + +
ModelFunctionAccountCostTokensDate
{{ anomaly.model }}{{ anomaly.function }}{{ anomaly.account__name }}${{ anomaly.cost|floatformat:2 }}{{ anomaly.tokens|floatformat:0 }}{{ anomaly.created_at|date:"M d, H:i" }}
+
+
+
+
+ {% endif %} + + +
+

Peak Cost Hours

+
+ + + + + + + + + + + {% for hour in hourly_cost %} + + + + + + + {% endfor %} + +
Hour of DayTotal CostAPI CallsActivity Level
+ {{ hour.hour|floatformat:0 }}:00 - {{ hour.hour|add:1|floatformat:0 }}:00 + + ${{ hour.total_cost|floatformat:2 }} + + {{ hour.call_count }} + +
+ {% widthratio hour.total_cost total_cost 100 as percentage %} +
+
+
+
+
+
+ + + +{% endblock %} diff --git a/backend/igny8_core/templates/admin/reports/token_usage.html b/backend/igny8_core/templates/admin/reports/token_usage.html new file mode 100644 index 00000000..692952fc --- /dev/null +++ b/backend/igny8_core/templates/admin/reports/token_usage.html @@ -0,0 +1,218 @@ +{% extends "admin/base_site.html" %} +{% load static %} + +{% block content %} +
+ +
+
+

Token Usage Report

+

Multi-dimensional token consumption analytics

+
+
+
+ + +
+
+
+ + +
+
+

Total Tokens

+

{{ total_tokens|floatformat:0 }}

+

{{ total_calls }} API calls

+
+
+

Avg Tokens/Call

+

{{ avg_tokens_per_call|floatformat:0 }}

+
+
+

Success Rate

+

{{ success_rate }}%

+

{{ successful_tokens|floatformat:0 }} tokens

+
+
+

Total Cost

+

${{ total_cost|floatformat:2 }}

+

${{ cost_per_1k_tokens|floatformat:4 }} per 1K

+
+
+ + +
+

Token Usage Trends

+ +
+ + +
+ +
+

Token Usage by Model

+
+ + + + + + + + + + + {% for item in token_by_model %} + + + + + + + {% endfor %} + +
ModelTokensCallsCost
{{ item.model|default:"Unknown" }}{{ item.total_tokens|floatformat:0 }}{{ item.call_count }}${{ item.total_cost|floatformat:2 }}
+
+
+ + +
+

Token Usage by Function

+
+ + + + + + + + + + {% for item in token_by_function %} + + + + + + {% endfor %} + +
FunctionTokensCalls
{{ item.function|default:"Unknown" }}{{ item.total_tokens|floatformat:0 }}{{ item.call_count }}
+
+
+
+ + +
+

Top Token Consumers (By Account)

+
+ + + + + + + + + + + + {% for consumer in token_by_account %} + + + + + + + + {% endfor %} + +
AccountTotal TokensAPI CallsTotal CostAvg Tokens
+ + {{ consumer.account__name|default:"Unknown Account" }} + + {{ consumer.total_tokens|floatformat:0 }}{{ consumer.call_count }}${{ consumer.total_cost|floatformat:2 }} + {% widthratio consumer.total_tokens consumer.call_count 1 %} +
+
+
+ + +
+

Efficiency Metrics

+
+
+

Successful Tokens

+

{{ successful_tokens|floatformat:0 }}

+
+
+
+
+
+

Wasted Tokens

+

{{ wasted_tokens|floatformat:0 }}

+

From failed requests

+
+
+

Hourly Peak Usage

+

+ {% for hour in hourly_usage %} + {% if forloop.first or hour.token_count > hourly_usage.0.token_count %} + {{ hour.hour|floatformat:0 }}:00 + {% endif %} + {% endfor %} +

+

Most active hour

+
+
+
+
+ + + +{% endblock %}