mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-06-19 00:42:05 -03:00
fix(stats): implement Model Types chart in Collection tab with correct type distribution
This commit is contained in:
@@ -11,6 +11,8 @@ from ..config import config
|
||||
from ..services.settings_manager import get_settings_manager
|
||||
from ..services.server_i18n import server_i18n
|
||||
from ..services.service_registry import ServiceRegistry
|
||||
from ..services.model_query import normalize_sub_type, resolve_sub_type
|
||||
from ..utils.constants import VALID_LORA_SUB_TYPES, VALID_CHECKPOINT_SUB_TYPES
|
||||
from ..utils.usage_stats import UsageStats
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -140,6 +142,21 @@ class StatsRoutes:
|
||||
# Get usage statistics
|
||||
usage_data = await self.usage_stats.get_stats()
|
||||
|
||||
# CivitAI model type distribution across all model types
|
||||
# Use the same logic as the filter panel: normalize_sub_type(resolve_sub_type(entry))
|
||||
# with sub-type validation per model type
|
||||
model_types_counter: Counter[str] = Counter()
|
||||
for entry in lora_cache.raw_data:
|
||||
ntype = normalize_sub_type(resolve_sub_type(entry))
|
||||
if ntype and ntype in VALID_LORA_SUB_TYPES:
|
||||
model_types_counter[ntype] += 1
|
||||
for entry in checkpoint_cache.raw_data:
|
||||
ntype = normalize_sub_type(resolve_sub_type(entry))
|
||||
if ntype and ntype in VALID_CHECKPOINT_SUB_TYPES:
|
||||
model_types_counter[ntype] += 1
|
||||
# Embeddings: always count as "embedding" regardless of CivitAI sub-type
|
||||
model_types_counter['embedding'] = len(embedding_cache.raw_data)
|
||||
|
||||
return web.json_response({
|
||||
'success': True,
|
||||
'data': {
|
||||
@@ -154,7 +171,8 @@ class StatsRoutes:
|
||||
'total_generations': usage_data.get('total_executions', 0),
|
||||
'unused_loras': self._count_unused_models(lora_cache.raw_data, usage_data.get('loras', {})),
|
||||
'unused_checkpoints': self._count_unused_models(checkpoint_cache.raw_data, usage_data.get('checkpoints', {})),
|
||||
'unused_embeddings': self._count_unused_models(embedding_cache.raw_data, usage_data.get('embeddings', {}))
|
||||
'unused_embeddings': self._count_unused_models(embedding_cache.raw_data, usage_data.get('embeddings', {})),
|
||||
'model_types_distribution': dict(model_types_counter.most_common())
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -240,6 +240,9 @@ export class StatisticsManager {
|
||||
|
||||
// Storage efficiency chart
|
||||
this.createStorageEfficiencyChart();
|
||||
|
||||
// Model types chart (Collection tab)
|
||||
this.createModelTypesChart();
|
||||
}
|
||||
|
||||
createCollectionPieChart() {
|
||||
@@ -554,6 +557,68 @@ export class StatisticsManager {
|
||||
});
|
||||
}
|
||||
|
||||
createModelTypesChart() {
|
||||
const ctx = document.getElementById('modelTypesChart');
|
||||
if (!ctx || !this.data.collection || !this.data.collection.model_types_distribution) return;
|
||||
|
||||
const distribution = this.data.collection.model_types_distribution;
|
||||
const typeDisplayNames = {
|
||||
lora: 'LoRA',
|
||||
locon: 'LyCORIS',
|
||||
dora: 'DoRA',
|
||||
checkpoint: 'Checkpoint',
|
||||
diffusion_model: 'Diffusion Model',
|
||||
embedding: 'Embeddings'
|
||||
};
|
||||
|
||||
const colorPalette = {
|
||||
lora: 'oklch(68% 0.28 256)',
|
||||
locon: 'oklch(68% 0.25 190)',
|
||||
dora: 'oklch(68% 0.25 330)',
|
||||
checkpoint: 'oklch(68% 0.28 45)',
|
||||
diffusion_model: 'oklch(68% 0.25 280)',
|
||||
embedding: 'oklch(68% 0.25 120)'
|
||||
};
|
||||
|
||||
const labels = Object.keys(distribution).map(k => typeDisplayNames[k] || k);
|
||||
const values = Object.values(distribution);
|
||||
const colors = Object.keys(distribution).map(k => colorPalette[k] || 'oklch(68% 0.15 0)');
|
||||
|
||||
const data = {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: values,
|
||||
backgroundColor: colors,
|
||||
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--border-color'),
|
||||
borderWidth: 2
|
||||
}]
|
||||
};
|
||||
|
||||
this.charts.modelTypes = new Chart(ctx, {
|
||||
type: 'doughnut',
|
||||
data: data,
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (context) => {
|
||||
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||
const value = context.parsed;
|
||||
const pct = ((value / total) * 100).toFixed(1);
|
||||
return ` ${context.label}: ${value} (${pct}%)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async initializeLists() {
|
||||
const listTypes = [
|
||||
{ type: 'lora', containerId: 'topLorasList' },
|
||||
|
||||
Reference in New Issue
Block a user