arxplorer / src /controller /smart_feed_controller.py
Subhadeep Mandal
Fresh deploy
54eb2ce
Raw
History Blame Contribute Delete
3.55 kB
from sqlalchemy import select, desc
from ..database.db import session_pool
from ..model.paper import Paper
from ..model.arxiv_catalog import ArxivCatalog
from ..schema.arxiv import ArxivEntry
from ..controller.catalog_controller import get_feed
from ..core.recommendation_engine.recommender import (
get_feed_recommendations,
rerank_results,
search_catalog,
)
from ..core.logger import SingletonLogger
logger = SingletonLogger().get_logger()
MAX_SEED_PAPERS = 5
def _qdrant_results_to_entries(items: list[dict]) -> list[ArxivEntry]:
entries: list[ArxivEntry] = []
for item in items:
authors_str = item.get("authors") or ""
authors = [a.strip() for a in authors_str.split(";") if a.strip()] if authors_str else []
categories_str = item.get("categories") or ""
categories = categories_str.split() if categories_str else []
entries.append(
ArxivEntry(
id=str(item.get("arxiv_id") or ""),
arxiv_id=item.get("arxiv_id"),
title=item.get("title") or "Untitled",
abstract=item.get("abstract") or "",
authors=authors,
categories=categories,
primary_category=item.get("primary_category"),
published=item.get("published_date"),
updated=None,
pdf_url=item.get("pdf_url"),
paper_url=item.get("paper_url"),
)
)
return entries
async def search_catalog_papers(
query: str,
start: int = 0,
limit: int = 20,
) -> list[ArxivEntry]:
try:
results = await search_catalog(query=query, offset=start, limit=limit)
return _qdrant_results_to_entries(results)
except Exception as exc:
logger.error("Catalog search failed: %s", exc)
raise
async def get_smart_feed(
user_id: int,
topics: list[str],
start: int = 0,
limit: int = 24,
) -> list[ArxivEntry]:
async with session_pool() as session:
saved_result = await session.execute(
select(Paper)
.where(Paper.user_id == user_id, Paper.arxiv_id.isnot(None))
.order_by(desc(Paper.created_at))
)
saved_papers = saved_result.scalars().all()
if not saved_papers:
return await get_feed(topics=topics, start=start, limit=limit)
saved_arxiv_ids = [p.arxiv_id for p in saved_papers if p.arxiv_id]
seed_papers = saved_papers[:MAX_SEED_PAPERS]
query_texts = [
f"{p.title} {(p.abstract or '')[:200]}" for p in seed_papers
]
async with session_pool() as session:
cat_result = await session.execute(
select(ArxivCatalog.primary_category)
.where(ArxivCatalog.arxiv_id.in_(saved_arxiv_ids))
.distinct()
)
user_saved_categories = [row[0] for row in cat_result.all() if row[0]]
try:
results = await get_feed_recommendations(
query_texts=query_texts,
category_filter=topics,
exclude_ids=saved_arxiv_ids,
offset=start,
limit=limit,
)
reranked = rerank_results(results, user_saved_categories)[:limit]
return _qdrant_results_to_entries(reranked)
except Exception as exc:
logger.warning("Qdrant unavailable, falling back to catalog feed: %s", exc)
return await get_feed(topics=topics, start=start, limit=limit)