mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
feat: add embedding support in statistics page, including data handling and UI updates
This commit is contained in:
@@ -20,6 +20,7 @@ class StatsRoutes:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.lora_scanner = None
|
self.lora_scanner = None
|
||||||
self.checkpoint_scanner = None
|
self.checkpoint_scanner = None
|
||||||
|
self.embedding_scanner = None
|
||||||
self.usage_stats = None
|
self.usage_stats = None
|
||||||
self.template_env = jinja2.Environment(
|
self.template_env = jinja2.Environment(
|
||||||
loader=jinja2.FileSystemLoader(config.templates_path),
|
loader=jinja2.FileSystemLoader(config.templates_path),
|
||||||
@@ -30,6 +31,7 @@ class StatsRoutes:
|
|||||||
"""Initialize services from ServiceRegistry"""
|
"""Initialize services from ServiceRegistry"""
|
||||||
self.lora_scanner = await ServiceRegistry.get_lora_scanner()
|
self.lora_scanner = await ServiceRegistry.get_lora_scanner()
|
||||||
self.checkpoint_scanner = await ServiceRegistry.get_checkpoint_scanner()
|
self.checkpoint_scanner = await ServiceRegistry.get_checkpoint_scanner()
|
||||||
|
self.embedding_scanner = await ServiceRegistry.get_embedding_scanner()
|
||||||
self.usage_stats = UsageStats()
|
self.usage_stats = UsageStats()
|
||||||
|
|
||||||
async def handle_stats_page(self, request: web.Request) -> web.Response:
|
async def handle_stats_page(self, request: web.Request) -> web.Response:
|
||||||
@@ -49,7 +51,12 @@ class StatsRoutes:
|
|||||||
(hasattr(self.checkpoint_scanner, '_is_initializing') and self.checkpoint_scanner._is_initializing)
|
(hasattr(self.checkpoint_scanner, '_is_initializing') and self.checkpoint_scanner._is_initializing)
|
||||||
)
|
)
|
||||||
|
|
||||||
is_initializing = lora_initializing or checkpoint_initializing
|
embedding_initializing = (
|
||||||
|
self.embedding_scanner._cache is None or
|
||||||
|
(hasattr(self.embedding_scanner, 'is_initializing') and self.embedding_scanner.is_initializing())
|
||||||
|
)
|
||||||
|
|
||||||
|
is_initializing = lora_initializing or checkpoint_initializing or embedding_initializing
|
||||||
|
|
||||||
template = self.template_env.get_template('statistics.html')
|
template = self.template_env.get_template('statistics.html')
|
||||||
rendered = template.render(
|
rendered = template.render(
|
||||||
@@ -85,21 +92,29 @@ class StatsRoutes:
|
|||||||
checkpoint_count = len(checkpoint_cache.raw_data)
|
checkpoint_count = len(checkpoint_cache.raw_data)
|
||||||
checkpoint_size = sum(cp.get('size', 0) for cp in checkpoint_cache.raw_data)
|
checkpoint_size = sum(cp.get('size', 0) for cp in checkpoint_cache.raw_data)
|
||||||
|
|
||||||
|
# Get Embedding statistics
|
||||||
|
embedding_cache = await self.embedding_scanner.get_cached_data()
|
||||||
|
embedding_count = len(embedding_cache.raw_data)
|
||||||
|
embedding_size = sum(emb.get('size', 0) for emb in embedding_cache.raw_data)
|
||||||
|
|
||||||
# Get usage statistics
|
# Get usage statistics
|
||||||
usage_data = await self.usage_stats.get_stats()
|
usage_data = await self.usage_stats.get_stats()
|
||||||
|
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
'success': True,
|
'success': True,
|
||||||
'data': {
|
'data': {
|
||||||
'total_models': lora_count + checkpoint_count,
|
'total_models': lora_count + checkpoint_count + embedding_count,
|
||||||
'lora_count': lora_count,
|
'lora_count': lora_count,
|
||||||
'checkpoint_count': checkpoint_count,
|
'checkpoint_count': checkpoint_count,
|
||||||
'total_size': lora_size + checkpoint_size,
|
'embedding_count': embedding_count,
|
||||||
|
'total_size': lora_size + checkpoint_size + embedding_size,
|
||||||
'lora_size': lora_size,
|
'lora_size': lora_size,
|
||||||
'checkpoint_size': checkpoint_size,
|
'checkpoint_size': checkpoint_size,
|
||||||
|
'embedding_size': embedding_size,
|
||||||
'total_generations': usage_data.get('total_executions', 0),
|
'total_generations': usage_data.get('total_executions', 0),
|
||||||
'unused_loras': self._count_unused_models(lora_cache.raw_data, usage_data.get('loras', {})),
|
'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_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', {}))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -121,14 +136,17 @@ class StatsRoutes:
|
|||||||
# Get model data for enrichment
|
# Get model data for enrichment
|
||||||
lora_cache = await self.lora_scanner.get_cached_data()
|
lora_cache = await self.lora_scanner.get_cached_data()
|
||||||
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
||||||
|
embedding_cache = await self.embedding_scanner.get_cached_data()
|
||||||
|
|
||||||
# Create hash to model mapping
|
# Create hash to model mapping
|
||||||
lora_map = {lora['sha256']: lora for lora in lora_cache.raw_data}
|
lora_map = {lora['sha256']: lora for lora in lora_cache.raw_data}
|
||||||
checkpoint_map = {cp['sha256']: cp for cp in checkpoint_cache.raw_data}
|
checkpoint_map = {cp['sha256']: cp for cp in checkpoint_cache.raw_data}
|
||||||
|
embedding_map = {emb['sha256']: emb for emb in embedding_cache.raw_data}
|
||||||
|
|
||||||
# Prepare top used models
|
# Prepare top used models
|
||||||
top_loras = self._get_top_used_models(usage_data.get('loras', {}), lora_map, 10)
|
top_loras = self._get_top_used_models(usage_data.get('loras', {}), lora_map, 10)
|
||||||
top_checkpoints = self._get_top_used_models(usage_data.get('checkpoints', {}), checkpoint_map, 10)
|
top_checkpoints = self._get_top_used_models(usage_data.get('checkpoints', {}), checkpoint_map, 10)
|
||||||
|
top_embeddings = self._get_top_used_models(usage_data.get('embeddings', {}), embedding_map, 10)
|
||||||
|
|
||||||
# Prepare usage timeline (last 30 days)
|
# Prepare usage timeline (last 30 days)
|
||||||
timeline = self._get_usage_timeline(usage_data, 30)
|
timeline = self._get_usage_timeline(usage_data, 30)
|
||||||
@@ -138,6 +156,7 @@ class StatsRoutes:
|
|||||||
'data': {
|
'data': {
|
||||||
'top_loras': top_loras,
|
'top_loras': top_loras,
|
||||||
'top_checkpoints': top_checkpoints,
|
'top_checkpoints': top_checkpoints,
|
||||||
|
'top_embeddings': top_embeddings,
|
||||||
'usage_timeline': timeline,
|
'usage_timeline': timeline,
|
||||||
'total_executions': usage_data.get('total_executions', 0)
|
'total_executions': usage_data.get('total_executions', 0)
|
||||||
}
|
}
|
||||||
@@ -158,16 +177,19 @@ class StatsRoutes:
|
|||||||
# Get model data
|
# Get model data
|
||||||
lora_cache = await self.lora_scanner.get_cached_data()
|
lora_cache = await self.lora_scanner.get_cached_data()
|
||||||
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
||||||
|
embedding_cache = await self.embedding_scanner.get_cached_data()
|
||||||
|
|
||||||
# Count by base model
|
# Count by base model
|
||||||
lora_base_models = Counter(lora.get('base_model', 'Unknown') for lora in lora_cache.raw_data)
|
lora_base_models = Counter(lora.get('base_model', 'Unknown') for lora in lora_cache.raw_data)
|
||||||
checkpoint_base_models = Counter(cp.get('base_model', 'Unknown') for cp in checkpoint_cache.raw_data)
|
checkpoint_base_models = Counter(cp.get('base_model', 'Unknown') for cp in checkpoint_cache.raw_data)
|
||||||
|
embedding_base_models = Counter(emb.get('base_model', 'Unknown') for emb in embedding_cache.raw_data)
|
||||||
|
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
'success': True,
|
'success': True,
|
||||||
'data': {
|
'data': {
|
||||||
'loras': dict(lora_base_models),
|
'loras': dict(lora_base_models),
|
||||||
'checkpoints': dict(checkpoint_base_models)
|
'checkpoints': dict(checkpoint_base_models),
|
||||||
|
'embeddings': dict(embedding_base_models)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -186,6 +208,7 @@ class StatsRoutes:
|
|||||||
# Get model data
|
# Get model data
|
||||||
lora_cache = await self.lora_scanner.get_cached_data()
|
lora_cache = await self.lora_scanner.get_cached_data()
|
||||||
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
||||||
|
embedding_cache = await self.embedding_scanner.get_cached_data()
|
||||||
|
|
||||||
# Count tag frequencies
|
# Count tag frequencies
|
||||||
all_tags = []
|
all_tags = []
|
||||||
@@ -193,6 +216,8 @@ class StatsRoutes:
|
|||||||
all_tags.extend(lora.get('tags', []))
|
all_tags.extend(lora.get('tags', []))
|
||||||
for cp in checkpoint_cache.raw_data:
|
for cp in checkpoint_cache.raw_data:
|
||||||
all_tags.extend(cp.get('tags', []))
|
all_tags.extend(cp.get('tags', []))
|
||||||
|
for emb in embedding_cache.raw_data:
|
||||||
|
all_tags.extend(emb.get('tags', []))
|
||||||
|
|
||||||
tag_counts = Counter(all_tags)
|
tag_counts = Counter(all_tags)
|
||||||
|
|
||||||
@@ -225,6 +250,7 @@ class StatsRoutes:
|
|||||||
# Get model data
|
# Get model data
|
||||||
lora_cache = await self.lora_scanner.get_cached_data()
|
lora_cache = await self.lora_scanner.get_cached_data()
|
||||||
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
||||||
|
embedding_cache = await self.embedding_scanner.get_cached_data()
|
||||||
|
|
||||||
# Create models with usage data
|
# Create models with usage data
|
||||||
lora_storage = []
|
lora_storage = []
|
||||||
@@ -255,15 +281,31 @@ class StatsRoutes:
|
|||||||
'base_model': cp.get('base_model', 'Unknown')
|
'base_model': cp.get('base_model', 'Unknown')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
embedding_storage = []
|
||||||
|
for emb in embedding_cache.raw_data:
|
||||||
|
usage_count = 0
|
||||||
|
if emb['sha256'] in usage_data.get('embeddings', {}):
|
||||||
|
usage_count = usage_data['embeddings'][emb['sha256']].get('total', 0)
|
||||||
|
|
||||||
|
embedding_storage.append({
|
||||||
|
'name': emb['model_name'],
|
||||||
|
'size': emb.get('size', 0),
|
||||||
|
'usage_count': usage_count,
|
||||||
|
'folder': emb.get('folder', ''),
|
||||||
|
'base_model': emb.get('base_model', 'Unknown')
|
||||||
|
})
|
||||||
|
|
||||||
# Sort by size
|
# Sort by size
|
||||||
lora_storage.sort(key=lambda x: x['size'], reverse=True)
|
lora_storage.sort(key=lambda x: x['size'], reverse=True)
|
||||||
checkpoint_storage.sort(key=lambda x: x['size'], reverse=True)
|
checkpoint_storage.sort(key=lambda x: x['size'], reverse=True)
|
||||||
|
embedding_storage.sort(key=lambda x: x['size'], reverse=True)
|
||||||
|
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
'success': True,
|
'success': True,
|
||||||
'data': {
|
'data': {
|
||||||
'loras': lora_storage[:20], # Top 20 by size
|
'loras': lora_storage[:20], # Top 20 by size
|
||||||
'checkpoints': checkpoint_storage[:20]
|
'checkpoints': checkpoint_storage[:20],
|
||||||
|
'embeddings': embedding_storage[:20]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -285,15 +327,18 @@ class StatsRoutes:
|
|||||||
# Get model data
|
# Get model data
|
||||||
lora_cache = await self.lora_scanner.get_cached_data()
|
lora_cache = await self.lora_scanner.get_cached_data()
|
||||||
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
checkpoint_cache = await self.checkpoint_scanner.get_cached_data()
|
||||||
|
embedding_cache = await self.embedding_scanner.get_cached_data()
|
||||||
|
|
||||||
insights = []
|
insights = []
|
||||||
|
|
||||||
# Calculate unused models
|
# Calculate unused models
|
||||||
unused_loras = self._count_unused_models(lora_cache.raw_data, usage_data.get('loras', {}))
|
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_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', {}))
|
||||||
|
|
||||||
total_loras = len(lora_cache.raw_data)
|
total_loras = len(lora_cache.raw_data)
|
||||||
total_checkpoints = len(checkpoint_cache.raw_data)
|
total_checkpoints = len(checkpoint_cache.raw_data)
|
||||||
|
total_embeddings = len(embedding_cache.raw_data)
|
||||||
|
|
||||||
if total_loras > 0:
|
if total_loras > 0:
|
||||||
unused_lora_percent = (unused_loras / total_loras) * 100
|
unused_lora_percent = (unused_loras / total_loras) * 100
|
||||||
@@ -315,9 +360,20 @@ class StatsRoutes:
|
|||||||
'suggestion': 'Review and consider removing checkpoints you no longer need.'
|
'suggestion': 'Review and consider removing checkpoints you no longer need.'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if total_embeddings > 0:
|
||||||
|
unused_embedding_percent = (unused_embeddings / total_embeddings) * 100
|
||||||
|
if unused_embedding_percent > 50:
|
||||||
|
insights.append({
|
||||||
|
'type': 'warning',
|
||||||
|
'title': 'High Number of Unused Embeddings',
|
||||||
|
'description': f'{unused_embedding_percent:.1f}% of your embeddings ({unused_embeddings}/{total_embeddings}) have never been used.',
|
||||||
|
'suggestion': 'Consider organizing or archiving unused embeddings to optimize your collection.'
|
||||||
|
})
|
||||||
|
|
||||||
# Storage insights
|
# Storage insights
|
||||||
total_size = sum(lora.get('size', 0) for lora in lora_cache.raw_data) + \
|
total_size = sum(lora.get('size', 0) for lora in lora_cache.raw_data) + \
|
||||||
sum(cp.get('size', 0) for cp in checkpoint_cache.raw_data)
|
sum(cp.get('size', 0) for cp in checkpoint_cache.raw_data) + \
|
||||||
|
sum(emb.get('size', 0) for emb in embedding_cache.raw_data)
|
||||||
|
|
||||||
if total_size > 100 * 1024 * 1024 * 1024: # 100GB
|
if total_size > 100 * 1024 * 1024 * 1024: # 100GB
|
||||||
insights.append({
|
insights.append({
|
||||||
@@ -390,6 +446,7 @@ class StatsRoutes:
|
|||||||
|
|
||||||
lora_usage = 0
|
lora_usage = 0
|
||||||
checkpoint_usage = 0
|
checkpoint_usage = 0
|
||||||
|
embedding_usage = 0
|
||||||
|
|
||||||
# Count usage for this date
|
# Count usage for this date
|
||||||
for model_usage in usage_data.get('loras', {}).values():
|
for model_usage in usage_data.get('loras', {}).values():
|
||||||
@@ -400,11 +457,16 @@ class StatsRoutes:
|
|||||||
if isinstance(model_usage, dict) and 'history' in model_usage:
|
if isinstance(model_usage, dict) and 'history' in model_usage:
|
||||||
checkpoint_usage += model_usage['history'].get(date_str, 0)
|
checkpoint_usage += model_usage['history'].get(date_str, 0)
|
||||||
|
|
||||||
|
for model_usage in usage_data.get('embeddings', {}).values():
|
||||||
|
if isinstance(model_usage, dict) and 'history' in model_usage:
|
||||||
|
embedding_usage += model_usage['history'].get(date_str, 0)
|
||||||
|
|
||||||
timeline.append({
|
timeline.append({
|
||||||
'date': date_str,
|
'date': date_str,
|
||||||
'lora_usage': lora_usage,
|
'lora_usage': lora_usage,
|
||||||
'checkpoint_usage': checkpoint_usage,
|
'checkpoint_usage': checkpoint_usage,
|
||||||
'total_usage': lora_usage + checkpoint_usage
|
'embedding_usage': embedding_usage,
|
||||||
|
'total_usage': lora_usage + checkpoint_usage + embedding_usage
|
||||||
})
|
})
|
||||||
|
|
||||||
return list(reversed(timeline)) # Oldest to newest
|
return list(reversed(timeline)) # Oldest to newest
|
||||||
|
|||||||
@@ -56,6 +56,24 @@
|
|||||||
color: var(--lora-error);
|
color: var(--lora-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update color scheme to include embeddings */
|
||||||
|
:root {
|
||||||
|
--embedding-color: oklch(68% 0.28 120); /* Green for embeddings */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update metric cards and chart colors to support embeddings */
|
||||||
|
.metric-card.embedding .metric-icon {
|
||||||
|
color: var(--embedding-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-item.embedding {
|
||||||
|
border-left: 3px solid var(--embedding-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-item.embedding:hover {
|
||||||
|
border-color: var(--embedding-color);
|
||||||
|
}
|
||||||
|
|
||||||
/* Dashboard Content */
|
/* Dashboard Content */
|
||||||
.dashboard-content {
|
.dashboard-content {
|
||||||
background: var(--card-bg);
|
background: var(--card-bg);
|
||||||
|
|||||||
@@ -150,6 +150,12 @@ class StatisticsManager {
|
|||||||
value: this.data.collection.checkpoint_count,
|
value: this.data.collection.checkpoint_count,
|
||||||
label: 'Checkpoints',
|
label: 'Checkpoints',
|
||||||
format: 'number'
|
format: 'number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'fas fa-code',
|
||||||
|
value: this.data.collection.embedding_count,
|
||||||
|
label: 'Embeddings',
|
||||||
|
format: 'number'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -195,7 +201,9 @@ class StatisticsManager {
|
|||||||
if (!this.data.collection) return 0;
|
if (!this.data.collection) return 0;
|
||||||
|
|
||||||
const totalModels = this.data.collection.total_models;
|
const totalModels = this.data.collection.total_models;
|
||||||
const unusedModels = this.data.collection.unused_loras + this.data.collection.unused_checkpoints;
|
const unusedModels = this.data.collection.unused_loras +
|
||||||
|
this.data.collection.unused_checkpoints +
|
||||||
|
this.data.collection.unused_embeddings;
|
||||||
const usedModels = totalModels - unusedModels;
|
const usedModels = totalModels - unusedModels;
|
||||||
|
|
||||||
return totalModels > 0 ? (usedModels / totalModels) * 100 : 0;
|
return totalModels > 0 ? (usedModels / totalModels) * 100 : 0;
|
||||||
@@ -233,12 +241,17 @@ class StatisticsManager {
|
|||||||
if (!ctx || !this.data.collection) return;
|
if (!ctx || !this.data.collection) return;
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
labels: ['LoRAs', 'Checkpoints'],
|
labels: ['LoRAs', 'Checkpoints', 'Embeddings'],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: [this.data.collection.lora_count, this.data.collection.checkpoint_count],
|
data: [
|
||||||
|
this.data.collection.lora_count,
|
||||||
|
this.data.collection.checkpoint_count,
|
||||||
|
this.data.collection.embedding_count
|
||||||
|
],
|
||||||
backgroundColor: [
|
backgroundColor: [
|
||||||
'oklch(68% 0.28 256)',
|
'oklch(68% 0.28 256)',
|
||||||
'oklch(68% 0.28 200)'
|
'oklch(68% 0.28 200)',
|
||||||
|
'oklch(68% 0.28 120)'
|
||||||
],
|
],
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--border-color')
|
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--border-color')
|
||||||
@@ -266,8 +279,13 @@ class StatisticsManager {
|
|||||||
|
|
||||||
const loraData = this.data.baseModels.loras;
|
const loraData = this.data.baseModels.loras;
|
||||||
const checkpointData = this.data.baseModels.checkpoints;
|
const checkpointData = this.data.baseModels.checkpoints;
|
||||||
|
const embeddingData = this.data.baseModels.embeddings;
|
||||||
|
|
||||||
const allModels = new Set([...Object.keys(loraData), ...Object.keys(checkpointData)]);
|
const allModels = new Set([
|
||||||
|
...Object.keys(loraData),
|
||||||
|
...Object.keys(checkpointData),
|
||||||
|
...Object.keys(embeddingData)
|
||||||
|
]);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
labels: Array.from(allModels),
|
labels: Array.from(allModels),
|
||||||
@@ -281,6 +299,11 @@ class StatisticsManager {
|
|||||||
label: 'Checkpoints',
|
label: 'Checkpoints',
|
||||||
data: Array.from(allModels).map(model => checkpointData[model] || 0),
|
data: Array.from(allModels).map(model => checkpointData[model] || 0),
|
||||||
backgroundColor: 'oklch(68% 0.28 200 / 0.7)'
|
backgroundColor: 'oklch(68% 0.28 200 / 0.7)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Embeddings',
|
||||||
|
data: Array.from(allModels).map(model => embeddingData[model] || 0),
|
||||||
|
backgroundColor: 'oklch(68% 0.28 120 / 0.7)'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -325,6 +348,13 @@ class StatisticsManager {
|
|||||||
borderColor: 'oklch(68% 0.28 200)',
|
borderColor: 'oklch(68% 0.28 200)',
|
||||||
backgroundColor: 'oklch(68% 0.28 200 / 0.1)',
|
backgroundColor: 'oklch(68% 0.28 200 / 0.1)',
|
||||||
fill: true
|
fill: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Embedding Usage',
|
||||||
|
data: timeline.map(item => item.embedding_usage),
|
||||||
|
borderColor: 'oklch(68% 0.28 120)',
|
||||||
|
backgroundColor: 'oklch(68% 0.28 120 / 0.1)',
|
||||||
|
fill: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -365,11 +395,13 @@ class StatisticsManager {
|
|||||||
|
|
||||||
const topLoras = this.data.usage.top_loras || [];
|
const topLoras = this.data.usage.top_loras || [];
|
||||||
const topCheckpoints = this.data.usage.top_checkpoints || [];
|
const topCheckpoints = this.data.usage.top_checkpoints || [];
|
||||||
|
const topEmbeddings = this.data.usage.top_embeddings || [];
|
||||||
|
|
||||||
// Combine and sort all models by usage
|
// Combine and sort all models by usage
|
||||||
const allModels = [
|
const allModels = [
|
||||||
...topLoras.map(m => ({ ...m, type: 'LoRA' })),
|
...topLoras.map(m => ({ ...m, type: 'LoRA' })),
|
||||||
...topCheckpoints.map(m => ({ ...m, type: 'Checkpoint' }))
|
...topCheckpoints.map(m => ({ ...m, type: 'Checkpoint' })),
|
||||||
|
...topEmbeddings.map(m => ({ ...m, type: 'Embedding' }))
|
||||||
].sort((a, b) => b.usage_count - a.usage_count).slice(0, 10);
|
].sort((a, b) => b.usage_count - a.usage_count).slice(0, 10);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
@@ -377,9 +409,14 @@ class StatisticsManager {
|
|||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Usage Count',
|
label: 'Usage Count',
|
||||||
data: allModels.map(model => model.usage_count),
|
data: allModels.map(model => model.usage_count),
|
||||||
backgroundColor: allModels.map(model =>
|
backgroundColor: allModels.map(model => {
|
||||||
model.type === 'LoRA' ? 'oklch(68% 0.28 256)' : 'oklch(68% 0.28 200)'
|
switch(model.type) {
|
||||||
)
|
case 'LoRA': return 'oklch(68% 0.28 256)';
|
||||||
|
case 'Checkpoint': return 'oklch(68% 0.28 200)';
|
||||||
|
case 'Embedding': return 'oklch(68% 0.28 120)';
|
||||||
|
default: return 'oklch(68% 0.28 256)';
|
||||||
|
}
|
||||||
|
})
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -404,12 +441,17 @@ class StatisticsManager {
|
|||||||
if (!ctx || !this.data.collection) return;
|
if (!ctx || !this.data.collection) return;
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
labels: ['LoRAs', 'Checkpoints'],
|
labels: ['LoRAs', 'Checkpoints', 'Embeddings'],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: [this.data.collection.lora_size, this.data.collection.checkpoint_size],
|
data: [
|
||||||
|
this.data.collection.lora_size,
|
||||||
|
this.data.collection.checkpoint_size,
|
||||||
|
this.data.collection.embedding_size
|
||||||
|
],
|
||||||
backgroundColor: [
|
backgroundColor: [
|
||||||
'oklch(68% 0.28 256)',
|
'oklch(68% 0.28 256)',
|
||||||
'oklch(68% 0.28 200)'
|
'oklch(68% 0.28 200)',
|
||||||
|
'oklch(68% 0.28 120)'
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
@@ -443,10 +485,12 @@ class StatisticsManager {
|
|||||||
|
|
||||||
const loraData = this.data.storage.loras || [];
|
const loraData = this.data.storage.loras || [];
|
||||||
const checkpointData = this.data.storage.checkpoints || [];
|
const checkpointData = this.data.storage.checkpoints || [];
|
||||||
|
const embeddingData = this.data.storage.embeddings || [];
|
||||||
|
|
||||||
const allData = [
|
const allData = [
|
||||||
...loraData.map(item => ({ ...item, type: 'LoRA' })),
|
...loraData.map(item => ({ ...item, type: 'LoRA' })),
|
||||||
...checkpointData.map(item => ({ ...item, type: 'Checkpoint' }))
|
...checkpointData.map(item => ({ ...item, type: 'Checkpoint' })),
|
||||||
|
...embeddingData.map(item => ({ ...item, type: 'Embedding' }))
|
||||||
];
|
];
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
@@ -458,9 +502,14 @@ class StatisticsManager {
|
|||||||
name: item.name,
|
name: item.name,
|
||||||
type: item.type
|
type: item.type
|
||||||
})),
|
})),
|
||||||
backgroundColor: allData.map(item =>
|
backgroundColor: allData.map(item => {
|
||||||
item.type === 'LoRA' ? 'oklch(68% 0.28 256 / 0.6)' : 'oklch(68% 0.28 200 / 0.6)'
|
switch(item.type) {
|
||||||
)
|
case 'LoRA': return 'oklch(68% 0.28 256 / 0.6)';
|
||||||
|
case 'Checkpoint': return 'oklch(68% 0.28 200 / 0.6)';
|
||||||
|
case 'Embedding': return 'oklch(68% 0.28 120 / 0.6)';
|
||||||
|
default: return 'oklch(68% 0.28 256 / 0.6)';
|
||||||
|
}
|
||||||
|
})
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -502,6 +551,7 @@ class StatisticsManager {
|
|||||||
renderTopModelsLists() {
|
renderTopModelsLists() {
|
||||||
this.renderTopLorasList();
|
this.renderTopLorasList();
|
||||||
this.renderTopCheckpointsList();
|
this.renderTopCheckpointsList();
|
||||||
|
this.renderTopEmbeddingsList();
|
||||||
this.renderLargestModelsList();
|
this.renderLargestModelsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,17 +605,44 @@ class StatisticsManager {
|
|||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderTopEmbeddingsList() {
|
||||||
|
const container = document.getElementById('topEmbeddingsList');
|
||||||
|
if (!container || !this.data.usage?.top_embeddings) return;
|
||||||
|
|
||||||
|
const topEmbeddings = this.data.usage.top_embeddings;
|
||||||
|
|
||||||
|
if (topEmbeddings.length === 0) {
|
||||||
|
container.innerHTML = '<div class="loading-placeholder">No usage data available</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = topEmbeddings.map(embedding => `
|
||||||
|
<div class="model-item">
|
||||||
|
<img src="${embedding.preview_url || '/loras_static/images/no-preview.png'}"
|
||||||
|
alt="${embedding.name}" class="model-preview"
|
||||||
|
onerror="this.src='/loras_static/images/no-preview.png'">
|
||||||
|
<div class="model-info">
|
||||||
|
<div class="model-name" title="${embedding.name}">${embedding.name}</div>
|
||||||
|
<div class="model-meta">${embedding.base_model} • ${embedding.folder}</div>
|
||||||
|
</div>
|
||||||
|
<div class="model-usage">${embedding.usage_count}</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
renderLargestModelsList() {
|
renderLargestModelsList() {
|
||||||
const container = document.getElementById('largestModelsList');
|
const container = document.getElementById('largestModelsList');
|
||||||
if (!container || !this.data.storage) return;
|
if (!container || !this.data.storage) return;
|
||||||
|
|
||||||
const loraModels = this.data.storage.loras || [];
|
const loraModels = this.data.storage.loras || [];
|
||||||
const checkpointModels = this.data.storage.checkpoints || [];
|
const checkpointModels = this.data.storage.checkpoints || [];
|
||||||
|
const embeddingModels = this.data.storage.embeddings || [];
|
||||||
|
|
||||||
// Combine and sort by size
|
// Combine and sort by size
|
||||||
const allModels = [
|
const allModels = [
|
||||||
...loraModels.map(m => ({ ...m, type: 'LoRA' })),
|
...loraModels.map(m => ({ ...m, type: 'LoRA' })),
|
||||||
...checkpointModels.map(m => ({ ...m, type: 'Checkpoint' }))
|
...checkpointModels.map(m => ({ ...m, type: 'Checkpoint' })),
|
||||||
|
...embeddingModels.map(m => ({ ...m, type: 'Embedding' }))
|
||||||
].sort((a, b) => b.size - a.size).slice(0, 10);
|
].sort((a, b) => b.size - a.size).slice(0, 10);
|
||||||
|
|
||||||
if (allModels.length === 0) {
|
if (allModels.length === 0) {
|
||||||
|
|||||||
@@ -98,6 +98,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Top Used Embeddings -->
|
||||||
|
<div class="list-container">
|
||||||
|
<h3><i class="fas fa-code"></i> Most Used Embeddings</h3>
|
||||||
|
<div class="model-list" id="topEmbeddingsList">
|
||||||
|
<!-- List will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Usage Distribution Chart -->
|
<!-- Usage Distribution Chart -->
|
||||||
<div class="chart-container full-width">
|
<div class="chart-container full-width">
|
||||||
<h3><i class="fas fa-chart-bar"></i> Usage Distribution</h3>
|
<h3><i class="fas fa-chart-bar"></i> Usage Distribution</h3>
|
||||||
|
|||||||
Reference in New Issue
Block a user