Spaces:
Running
Running
File size: 3,568 Bytes
ac787df 30b1aef ac787df e8cd7a9 b95fe6f ac787df 7ac3a44 ac787df 30b1aef b95fe6f ac787df e8cd7a9 b95fe6f 7ac3a44 e8cd7a9 ac787df b95fe6f ac787df b95fe6f e8cd7a9 30b1aef e8cd7a9 ac787df b95fe6f 30b1aef ac787df b95fe6f ac787df 30b1aef b95fe6f ac787df | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | import uuid
import os
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.database import get_db
from app.auth import get_current_user
from app.models.user import User
from app.models.chat import ChatMessage
from app.models.evaluation import EvaluationResult
from app.models.metrics import Metrics
from app.services.analysis import RetrievalAnalyzer
router = APIRouter(prefix="/api/analysis", tags=["analysis"])
@router.get("/{message_id}")
async def analyze_message(message_id: uuid.UUID, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
# 1. Fetch the message natively mapped
stmt_msg = select(ChatMessage).where(ChatMessage.id == message_id, ChatMessage.user_id == current_user.id)
msg = (await db.execute(stmt_msg)).scalars().first()
if not msg:
raise HTTPException(status_code=404, detail="Message not found")
if msg.role != "assistant":
raise HTTPException(status_code=400, detail="Analysis is logically only available for assistant messages")
# 2. Fetch associated metrics (if any timer payloads caught mapped traces securely)
stmt = select(Metrics).where(Metrics.message_id == message_id)
metrics_record = (await db.execute(stmt)).scalars().first()
eval_stmt = (
select(EvaluationResult)
.where(EvaluationResult.message_id == message_id)
.order_by(EvaluationResult.created_at.desc())
)
eval_record = (await db.execute(eval_stmt)).scalars().first()
# 3. Parse retrieved chunks and instantly leverage our abstract Analyzer class seamlessly
chunks_data = msg.retrieved_chunks or []
confidence_threshold = float(os.getenv("ANALYSIS_CONFIDENCE_THRESHOLD", "0.5"))
analyzer = RetrievalAnalyzer(chunks_data, confidence_threshold=confidence_threshold)
summary = analyzer.summary_stats()
# Construct structured insight payload natively mapped
return {
"message_id": message_id,
"total_chunks_retrieved": summary["total_chunks_retrieved"],
"avg_similarity": summary["avg_similarity"],
"warning_flag": summary["warning_flag"],
"confidence_threshold": summary["confidence_threshold"],
"score_distribution": summary["score_distribution"],
"chunk_diversity": summary["chunk_diversity"],
"top_contributors": summary["top_contributors"],
"ranked_chunks": summary["ranked_chunks"],
"summary_stats": summary,
"evaluation": {
"faithfulness": eval_record.faithfulness if eval_record else None,
"answer_relevancy": eval_record.answer_relevancy if eval_record else None,
"context_precision": eval_record.context_precision if eval_record else None,
"context_recall": eval_record.context_recall if eval_record else None,
} if eval_record else None,
"timing_breakdown": {
"chunking_time_ms": metrics_record.chunking_time_ms if metrics_record else None,
"embedding_time_ms": metrics_record.embedding_time_ms if metrics_record else None,
"retrieval_time_ms": metrics_record.retrieval_time_ms if metrics_record else None,
"reranking_time_ms": metrics_record.reranking_time_ms if metrics_record else None,
"llm_time_ms": metrics_record.llm_time_ms if metrics_record else None,
"total_time_ms": metrics_record.total_time_ms if metrics_record else None,
} if metrics_record else None
}
|