This commit is contained in:
Timothy Jaeryang Baek
2026-06-16 23:24:27 +02:00
parent 56ae99e96a
commit 40c09167cd
5 changed files with 112 additions and 37 deletions

View File

@@ -957,6 +957,20 @@ class ChatTable:
all_chats = result.scalars().all()
return [ChatModel.model_validate(chat) for chat in all_chats]
async def get_chat_metas_by_chat_ids(
self,
chat_ids: list[str],
include_archived: bool = False,
db: AsyncSession | None = None,
) -> list[dict]:
async with get_async_db_context(db) as session:
stmt = select(Chat.meta).filter(Chat.id.in_(chat_ids))
if not include_archived:
stmt = stmt.filter_by(archived=False)
result = await session.execute(stmt)
return [meta for meta in result.scalars().all() if isinstance(meta, dict)]
# retrieve conversation
async def get_chat_by_id(
self,
@@ -1389,7 +1403,6 @@ class ChatTable:
for chat in all_chats
]
async def get_chats_by_folder_ids_and_user_id(
self, folder_ids: list[str], user_id: str, db: AsyncSession | None = None
) -> list[ChatModel]:

View File

@@ -133,6 +133,12 @@ class ModelHistoryEntry(BaseModel):
lost: int
class ModelHistoryCounts(BaseModel):
date: str
won: int = 0
lost: int = 0
class ModelHistoryResponse(BaseModel):
model_id: str
history: list[ModelHistoryEntry]
@@ -375,6 +381,45 @@ class FeedbackTable:
return result
async def get_model_feedback_counts_by_day(
self,
model_id: str,
start_date: Optional[int] = None,
db: Optional[AsyncSession] = None,
) -> list[ModelHistoryCounts]:
"""Get aggregated feedback counts per day for a model, preserving all matching days."""
from collections import defaultdict
from datetime import datetime
async with get_async_db_context(db) as db:
stmt = select(Feedback.created_at, Feedback.data).filter(Feedback.data['model_id'].as_string() == model_id)
if start_date is not None:
stmt = stmt.filter(Feedback.created_at >= start_date)
result = await db.execute(stmt.order_by(Feedback.created_at.asc()))
rows = result.all()
daily_counts = defaultdict(lambda: {'won': 0, 'lost': 0})
for created_at, data in rows:
if not data:
continue
rating_str = str(data.get('rating', ''))
if rating_str not in ('1', '-1'):
continue
date_str = datetime.fromtimestamp(created_at).strftime('%Y-%m-%d')
if rating_str == '1':
daily_counts[date_str]['won'] += 1
else:
daily_counts[date_str]['lost'] += 1
return [
ModelHistoryCounts(date=date_str, won=counts['won'], lost=counts['lost'])
for date_str, counts in sorted(daily_counts.items())
]
async def get_feedbacks_by_type(self, type: str, db: Optional[AsyncSession] = None) -> list[FeedbackModel]:
async with get_async_db_context(db) as db:
result = await db.execute(select(Feedback).filter_by(type=type).order_by(Feedback.updated_at.desc()))

View File

@@ -367,6 +367,12 @@ async def get_model_overview(
):
"""Get model overview with feedback history and chat tags."""
# Calculate start date for history
now = datetime.now()
start_dt = None
if days > 0:
start_dt = now - timedelta(days=days)
# Get chat IDs that used this model
chat_ids = await ChatMessages.get_chat_ids_by_model_id(
model_id=model_id,
@@ -377,31 +383,18 @@ async def get_model_overview(
db=db,
)
# Get feedback history per day
history_counts: dict[str, dict] = defaultdict(lambda: {'won': 0, 'lost': 0})
# Calculate start date for history
now = datetime.now()
start_dt = None
if days > 0:
start_dt = now - timedelta(days=days)
for chat_id in chat_ids:
feedbacks = await Feedbacks.get_feedbacks_by_chat_id(chat_id, db=db)
for fb in feedbacks:
if fb.data and 'rating' in fb.data:
rating = fb.data['rating']
fb_date = datetime.fromtimestamp(fb.created_at)
# Filter by date range
if start_dt and fb_date < start_dt:
continue
date_str = fb_date.strftime('%Y-%m-%d')
if rating == 1:
history_counts[date_str]['won'] += 1
elif rating == -1:
history_counts[date_str]['lost'] += 1
history_rows = await Feedbacks.get_model_feedback_counts_by_day(
model_id=model_id,
start_date=int(start_dt.timestamp()) if start_dt else None,
db=db,
)
history_counts = {
entry.date: {
'won': entry.won,
'lost': entry.lost,
}
for entry in history_rows
}
# Fill in missing days
history = []
@@ -430,10 +423,14 @@ async def get_model_overview(
# Get chat tags
tag_counts: dict[str, int] = defaultdict(int)
for chat_id in chat_ids:
chat = await Chats.get_chat_by_id(chat_id, db=db)
if chat and chat.meta:
for tag in chat.meta.get('tags', []):
if chat_ids:
chat_metas = await Chats.get_chat_metas_by_chat_ids(
chat_ids,
include_archived=True,
db=db,
)
for meta in chat_metas:
for tag in meta.get('tags', []):
tag_counts[tag] += 1
# Sort by count and take top 10

View File

@@ -368,7 +368,11 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None)
models_dict = {model['id']: model for model in models}
if isinstance(request.app.state.MODELS, RedisDict):
request.app.state.MODELS.set(models_dict)
try:
request.app.state.MODELS.set(models_dict)
except Exception as e:
log.warning(f'Failed to update Redis model cache, using in-process cache: {e}')
request.app.state.MODELS = models_dict
else:
request.app.state.MODELS = models_dict

View File

@@ -46,8 +46,9 @@
try {
const result = await getLeaderboard(localStorage.token, searchQuery);
const statsMap = new Map((result?.entries ?? []).map((e) => [e.model_id, e]));
const modelMap = new Map(($models ?? []).map((m) => [m.id, m]));
rankedModels = $models
const activeModels = $models
.filter((m) => m?.owned_by !== 'arena' && !m?.info?.meta?.hidden)
.map((model) => {
const s = statsMap.get(model.id);
@@ -61,12 +62,27 @@
},
top_tags: s?.top_tags ?? []
};
})
.sort((a, b) => {
if (a.rating === '-') return 1;
if (b.rating === '-') return -1;
return b.rating - a.rating;
});
const evaluatedModels = (result?.entries ?? [])
.filter((entry) => !modelMap.has(entry.model_id))
.map((entry) => ({
id: entry.model_id,
name: entry.model_id,
rating: entry.rating,
stats: {
count: entry.won + entry.lost,
won: entry.won.toString(),
lost: entry.lost.toString()
},
top_tags: entry.top_tags ?? []
}));
rankedModels = [...activeModels, ...evaluatedModels].sort((a, b) => {
if (a.rating === '-') return 1;
if (b.rating === '-') return -1;
return b.rating - a.rating;
});
} catch (err) {
console.error('Leaderboard load failed:', err);
}