eeshanyaj's picture
backend changes, added new features
bcbf1db
raw
history blame
50.1 kB
# """
# Chat API Endpoints (WITH AUTHENTICATION)
# RESTful API for the Banking RAG Chatbot
# NOW REQUIRES JWT TOKEN FOR ALL ENDPOINTS!
# Endpoints:
# - POST /chat - Send a message and get response (PROTECTED)
# - GET /chat/history/{conversation_id} - Get conversation history (PROTECTED)
# - POST /chat/conversation - Create new conversation (PROTECTED)
# - GET /chat/conversations - List user's conversations (PROTECTED)
# - DELETE /chat/conversation/{conversation_id} - Delete conversation (PROTECTED)
# - GET /chat/health - Health check (PUBLIC)
# """
# from fastapi import APIRouter, HTTPException, status, Depends
# from pydantic import BaseModel, Field
# from typing import List, Dict, Optional
# from datetime import datetime
# from app.services.chat_service import chat_service
# from app.db.repositories.conversation_repository import ConversationRepository
# from app.utils.dependencies import get_current_user # AUTH DEPENDENCY
# from app.models.user import TokenData # USER DATA FROM TOKEN
# # ============================================================================
# # CREATE ROUTER
# # ============================================================================
# router = APIRouter()
# # ============================================================================
# # DEPENDENCY: Get ConversationRepository instance
# # ============================================================================
# def get_conversation_repo() -> ConversationRepository:
# """
# Dependency that provides ConversationRepository instance.
# This ensures MongoDB is connected before repository is used.
# """
# return ConversationRepository()
# # ============================================================================
# # PYDANTIC MODELS (Request/Response schemas)
# # ============================================================================
# class ChatRequest(BaseModel):
# """Request model for chat endpoint"""
# query: str = Field(..., description="User query text", min_length=1, max_length=1000)
# conversation_id: Optional[str] = Field(None, description="Optional conversation ID")
# class Config:
# json_schema_extra = {
# "example": {
# "query": "What is my account balance?",
# "conversation_id": "conv-123"
# }
# }
# class ChatResponse(BaseModel):
# """Response model for chat endpoint"""
# response: str = Field(..., description="Generated response text")
# conversation_id: str = Field(..., description="Conversation ID")
# policy_action: str = Field(..., description="Policy decision: FETCH or NO_FETCH")
# policy_confidence: float = Field(..., description="Policy confidence score (0-1)")
# documents_retrieved: int = Field(..., description="Number of documents retrieved")
# top_doc_score: Optional[float] = Field(None, description="Best document similarity score")
# total_time_ms: float = Field(..., description="Total processing time in milliseconds")
# timestamp: str = Field(..., description="Response timestamp (ISO format)")
# class ConversationCreateResponse(BaseModel):
# """Response after creating a conversation"""
# conversation_id: str = Field(..., description="Created conversation ID")
# created_at: str = Field(..., description="Creation timestamp")
# class MessageModel(BaseModel):
# """Single message in conversation history"""
# role: str = Field(..., description="Message role: user or assistant")
# content: str = Field(..., description="Message content")
# timestamp: str = Field(..., description="Message timestamp")
# metadata: Optional[Dict] = Field(None, description="Optional metadata")
# class ConversationHistoryResponse(BaseModel):
# """Response containing conversation history"""
# conversation_id: str
# messages: List[MessageModel]
# message_count: int
# # ============================================================================
# # ENDPOINTS (ALL PROTECTED WITH JWT)
# # ============================================================================
# @router.post("/", response_model=ChatResponse, status_code=status.HTTP_200_OK)
# async def chat(
# request: ChatRequest,
# current_user: TokenData = Depends(get_current_user),
# repo: ConversationRepository = Depends(get_conversation_repo) # ← INJECT REPO
# ):
# """
# Main chat endpoint - Send a query and get a response.
# **REQUIRES AUTHENTICATION** - JWT token must be provided in Authorization header.
# """
# try:
# # Get user_id from token
# user_id = current_user.user_id
# # If no conversation_id provided, create a new conversation
# conversation_id = request.conversation_id
# if not conversation_id:
# conversation_id = await repo.create_conversation(user_id=user_id)
# else:
# # Verify user owns this conversation
# conversation = await repo.get_conversation(conversation_id)
# if not conversation:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail="Conversation not found"
# )
# if conversation["user_id"] != user_id:
# raise HTTPException(
# status_code=status.HTTP_403_FORBIDDEN,
# detail="Access denied - you don't own this conversation"
# )
# # Get conversation history
# history = await repo.get_conversation_history(
# conversation_id=conversation_id,
# max_messages=10
# )
# # Save user message
# await repo.add_message(
# conversation_id=conversation_id,
# message={
# 'role': 'user',
# 'content': request.query,
# 'timestamp': datetime.now()
# }
# )
# # Process query through RAG pipeline
# result = await chat_service.process_query(
# query=request.query,
# conversation_history=history,
# user_id=user_id
# )
# # Save assistant message
# await repo.add_message(
# conversation_id=conversation_id,
# message={
# 'role': 'assistant',
# 'content': result['response'],
# 'timestamp': datetime.now(),
# 'metadata': {
# 'policy_action': result['policy_action'],
# 'policy_confidence': result['policy_confidence'],
# 'documents_retrieved': result['documents_retrieved'],
# 'top_doc_score': result['top_doc_score']
# }
# }
# )
# # Log retrieval data for RL training
# await repo.log_retrieval({
# 'conversation_id': conversation_id,
# 'user_id': user_id,
# 'query': request.query,
# 'policy_action': result['policy_action'],
# 'policy_confidence': result['policy_confidence'],
# 'should_retrieve': result['should_retrieve'],
# 'documents_retrieved': result['documents_retrieved'],
# 'top_doc_score': result['top_doc_score'],
# 'response': result['response'],
# 'retrieval_time_ms': result['retrieval_time_ms'],
# 'generation_time_ms': result['generation_time_ms'],
# 'total_time_ms': result['total_time_ms'],
# 'retrieved_docs_metadata': result.get('retrieved_docs_metadata', []),
# 'timestamp': datetime.now()
# })
# # Return response
# return ChatResponse(
# response=result['response'],
# conversation_id=conversation_id,
# policy_action=result['policy_action'],
# policy_confidence=result['policy_confidence'],
# documents_retrieved=result['documents_retrieved'],
# top_doc_score=result['top_doc_score'],
# total_time_ms=result['total_time_ms'],
# timestamp=result['timestamp']
# )
# except HTTPException:
# raise
# except Exception as e:
# print(f"❌ Chat endpoint error: {e}")
# import traceback
# traceback.print_exc()
# raise HTTPException(
# status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# detail=f"Failed to process chat request: {str(e)}"
# )
# @router.post("/conversation", response_model=ConversationCreateResponse, status_code=status.HTTP_201_CREATED)
# async def create_conversation(
# current_user: TokenData = Depends(get_current_user),
# repo: ConversationRepository = Depends(get_conversation_repo)
# ):
# """Create a new conversation"""
# try:
# conversation_id = await repo.create_conversation(user_id=current_user.user_id)
# return ConversationCreateResponse(
# conversation_id=conversation_id,
# created_at=datetime.now().isoformat()
# )
# except Exception as e:
# raise HTTPException(
# status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# detail=f"Failed to create conversation: {str(e)}"
# )
# @router.get("/history/{conversation_id}", response_model=ConversationHistoryResponse)
# async def get_conversation_history(
# conversation_id: str,
# current_user: TokenData = Depends(get_current_user),
# repo: ConversationRepository = Depends(get_conversation_repo)
# ):
# """Get conversation history by ID"""
# try:
# conversation = await repo.get_conversation(conversation_id)
# if not conversation:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=f"Conversation {conversation_id} not found"
# )
# if conversation["user_id"] != current_user.user_id:
# raise HTTPException(
# status_code=status.HTTP_403_FORBIDDEN,
# detail="Access denied - you don't own this conversation"
# )
# messages = []
# for msg in conversation.get('messages', []):
# messages.append(MessageModel(
# role=msg['role'],
# content=msg['content'],
# timestamp=msg['timestamp'].isoformat() if isinstance(msg['timestamp'], datetime) else msg['timestamp'],
# metadata=msg.get('metadata')
# ))
# return ConversationHistoryResponse(
# conversation_id=conversation_id,
# messages=messages,
# message_count=len(messages)
# )
# except HTTPException:
# raise
# except Exception as e:
# raise HTTPException(
# status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# detail=f"Failed to fetch conversation history: {str(e)}"
# )
# @router.get("/conversations")
# async def list_user_conversations(
# limit: int = 10,
# skip: int = 0,
# current_user: TokenData = Depends(get_current_user),
# repo: ConversationRepository = Depends(get_conversation_repo)
# ):
# """List all conversations for the authenticated user"""
# try:
# conversations = await repo.get_user_conversations(
# user_id=current_user.user_id,
# limit=limit,
# skip=skip
# )
# return {
# "user_id": current_user.user_id,
# "user_email": current_user.email,
# "conversations": [
# {
# "conversation_id": conv['conversation_id'],
# "created_at": conv['created_at'].isoformat() if isinstance(conv['created_at'], datetime) else conv['created_at'],
# "updated_at": conv['updated_at'].isoformat() if isinstance(conv['updated_at'], datetime) else conv['updated_at'],
# "message_count": len(conv.get('messages', []))
# }
# for conv in conversations
# ],
# "total": len(conversations)
# }
# except Exception as e:
# raise HTTPException(
# status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# detail=f"Failed to fetch conversations: {str(e)}"
# )
# @router.delete("/conversation/{conversation_id}")
# async def delete_conversation(
# conversation_id: str,
# current_user: TokenData = Depends(get_current_user),
# repo: ConversationRepository = Depends(get_conversation_repo)
# ):
# """Delete a conversation"""
# try:
# conversation = await repo.get_conversation(conversation_id)
# if not conversation:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=f"Conversation {conversation_id} not found"
# )
# if conversation["user_id"] != current_user.user_id:
# raise HTTPException(
# status_code=status.HTTP_403_FORBIDDEN,
# detail="Access denied - you don't own this conversation"
# )
# success = await repo.delete_conversation(conversation_id)
# if success:
# return {
# "message": "Conversation deleted successfully",
# "conversation_id": conversation_id
# }
# else:
# raise HTTPException(
# status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# detail="Failed to delete conversation"
# )
# except HTTPException:
# raise
# except Exception as e:
# raise HTTPException(
# status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# detail=f"Failed to delete conversation: {str(e)}"
# )
# @router.get("/health")
# async def chat_health():
# """Health check for chat service (PUBLIC)"""
# try:
# health = await chat_service.health_check()
# return {
# "status": "healthy",
# "service": "chat",
# "components": health['components'],
# "timestamp": datetime.now().isoformat()
# }
# except Exception as e:
# return {
# "status": "unhealthy",
# "service": "chat",
# "error": str(e),
# "timestamp": datetime.now().isoformat()
# }
# # ============================================================================
# # """
# # Chat API Endpoints (WITH AUTHENTICATION)
# # RESTful API for the Banking RAG Chatbot
# # NOW REQUIRES JWT TOKEN FOR ALL ENDPOINTS!
# # Endpoints:
# # - POST /chat - Send a message and get response (PROTECTED)
# # - GET /chat/history/{conversation_id} - Get conversation history (PROTECTED)
# # - POST /chat/conversation - Create new conversation (PROTECTED)
# # - GET /chat/conversations - List user's conversations (PROTECTED)
# # - DELETE /chat/conversation/{conversation_id} - Delete conversation (PROTECTED)
# # - GET /chat/health - Health check (PUBLIC)
# # """
# # from fastapi import APIRouter, HTTPException, status, Depends
# # from pydantic import BaseModel, Field
# # from typing import List, Dict, Optional
# # from datetime import datetime
# # from app.services.chat_service import chat_service
# # from app.db.repositories.conversation_repository import ConversationRepository
# # from app.utils.dependencies import get_current_user # AUTH DEPENDENCY
# # from app.models.user import TokenData # USER DATA FROM TOKEN
# # # ============================================================================
# # # CREATE ROUTER
# # # ============================================================================
# # router = APIRouter()
# # # Initialize repository
# # conversation_repo = ConversationRepository()
# # # ============================================================================
# # # PYDANTIC MODELS (Request/Response schemas)
# # # ============================================================================
# # class ChatRequest(BaseModel):
# # """
# # Request model for chat endpoint.
# # NOTE: user_id is now extracted from JWT token, not from request body!
# # Example:
# # {
# # "query": "What is my account balance?",
# # "conversation_id": "abc-123"
# # }
# # """
# # query: str = Field(..., description="User query text", min_length=1, max_length=1000)
# # conversation_id: Optional[str] = Field(None, description="Optional conversation ID")
# # class Config:
# # json_schema_extra = {
# # "example": {
# # "query": "What is my account balance?",
# # "conversation_id": "conv-123"
# # }
# # }
# # class ChatResponse(BaseModel):
# # """
# # Response model for chat endpoint.
# # Contains the generated response plus metadata about the RAG pipeline.
# # """
# # response: str = Field(..., description="Generated response text")
# # conversation_id: str = Field(..., description="Conversation ID")
# # policy_action: str = Field(..., description="Policy decision: FETCH or NO_FETCH")
# # policy_confidence: float = Field(..., description="Policy confidence score (0-1)")
# # documents_retrieved: int = Field(..., description="Number of documents retrieved")
# # top_doc_score: Optional[float] = Field(None, description="Best document similarity score")
# # total_time_ms: float = Field(..., description="Total processing time in milliseconds")
# # timestamp: str = Field(..., description="Response timestamp (ISO format)")
# # class ConversationCreateRequest(BaseModel):
# # """Request to create a new conversation (no user_id needed - from token)"""
# # pass # Empty - user_id comes from JWT token
# # class ConversationCreateResponse(BaseModel):
# # """Response after creating a conversation"""
# # conversation_id: str = Field(..., description="Created conversation ID")
# # created_at: str = Field(..., description="Creation timestamp")
# # class MessageModel(BaseModel):
# # """Single message in conversation history"""
# # role: str = Field(..., description="Message role: user or assistant")
# # content: str = Field(..., description="Message content")
# # timestamp: str = Field(..., description="Message timestamp")
# # metadata: Optional[Dict] = Field(None, description="Optional metadata")
# # class ConversationHistoryResponse(BaseModel):
# # """Response containing conversation history"""
# # conversation_id: str
# # messages: List[MessageModel]
# # message_count: int
# # # ============================================================================
# # # ENDPOINTS (ALL PROTECTED WITH JWT)
# # # ============================================================================
# # @router.post("/", response_model=ChatResponse, status_code=status.HTTP_200_OK)
# # async def chat(
# # request: ChatRequest,
# # current_user: TokenData = Depends(get_current_user) # ← REQUIRES AUTH!
# # ):
# # """
# # Main chat endpoint - Send a query and get a response.
# # **REQUIRES AUTHENTICATION** - JWT token must be provided in Authorization header.
# # This endpoint:
# # 1. Extracts user_id from JWT token
# # 2. Processes the query through the RAG pipeline
# # 3. Saves messages to MongoDB
# # 4. Logs retrieval data for RL training
# # 5. Returns response with metadata
# # Args:
# # request: ChatRequest with query and optional conversation_id
# # current_user: Authenticated user data from JWT token
# # Returns:
# # ChatResponse: Generated response with metadata
# # Raises:
# # HTTPException: If processing fails or user not authenticated
# # """
# # try:
# # # Get user_id from token (NOT from request body!)
# # user_id = current_user.user_id
# # # If no conversation_id provided, create a new conversation
# # conversation_id = request.conversation_id
# # if not conversation_id:
# # conversation_id = await conversation_repo.create_conversation(
# # user_id=user_id
# # )
# # else:
# # # Verify user owns this conversation
# # conversation = await conversation_repo.get_conversation(conversation_id)
# # if not conversation:
# # raise HTTPException(
# # status_code=status.HTTP_404_NOT_FOUND,
# # detail="Conversation not found"
# # )
# # if conversation["user_id"] != user_id:
# # raise HTTPException(
# # status_code=status.HTTP_403_FORBIDDEN,
# # detail="Access denied - you don't own this conversation"
# # )
# # # Get conversation history
# # history = await conversation_repo.get_conversation_history(
# # conversation_id=conversation_id,
# # max_messages=10 # Last 5 turns (10 messages)
# # )
# # # Save user message to database
# # await conversation_repo.add_message(
# # conversation_id=conversation_id,
# # message={
# # 'role': 'user',
# # 'content': request.query,
# # 'timestamp': datetime.now()
# # }
# # )
# # # Process query through RAG pipeline
# # result = await chat_service.process_query(
# # query=request.query,
# # conversation_history=history,
# # user_id=user_id
# # )
# # # Save assistant message to database
# # await conversation_repo.add_message(
# # conversation_id=conversation_id,
# # message={
# # 'role': 'assistant',
# # 'content': result['response'],
# # 'timestamp': datetime.now(),
# # 'metadata': {
# # 'policy_action': result['policy_action'],
# # 'policy_confidence': result['policy_confidence'],
# # 'documents_retrieved': result['documents_retrieved'],
# # 'top_doc_score': result['top_doc_score']
# # }
# # }
# # )
# # # Log retrieval data for RL training
# # await conversation_repo.log_retrieval({
# # 'conversation_id': conversation_id,
# # 'user_id': user_id,
# # 'query': request.query,
# # 'policy_action': result['policy_action'],
# # 'policy_confidence': result['policy_confidence'],
# # 'should_retrieve': result['should_retrieve'],
# # 'documents_retrieved': result['documents_retrieved'],
# # 'top_doc_score': result['top_doc_score'],
# # 'response': result['response'],
# # 'retrieval_time_ms': result['retrieval_time_ms'],
# # 'generation_time_ms': result['generation_time_ms'],
# # 'total_time_ms': result['total_time_ms'],
# # 'retrieved_docs_metadata': result.get('retrieved_docs_metadata', []),
# # 'timestamp': datetime.now()
# # })
# # # Return response
# # return ChatResponse(
# # response=result['response'],
# # conversation_id=conversation_id,
# # policy_action=result['policy_action'],
# # policy_confidence=result['policy_confidence'],
# # documents_retrieved=result['documents_retrieved'],
# # top_doc_score=result['top_doc_score'],
# # total_time_ms=result['total_time_ms'],
# # timestamp=result['timestamp']
# # )
# # except HTTPException:
# # raise # Re-raise HTTP exceptions
# # except Exception as e:
# # print(f"❌ Chat endpoint error: {e}")
# # raise HTTPException(
# # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # detail=f"Failed to process chat request: {str(e)}"
# # )
# # @router.post("/conversation", response_model=ConversationCreateResponse, status_code=status.HTTP_201_CREATED)
# # async def create_conversation(
# # current_user: TokenData = Depends(get_current_user) # ← REQUIRES AUTH!
# # ):
# # """
# # Create a new conversation.
# # **REQUIRES AUTHENTICATION** - User ID is extracted from JWT token.
# # Args:
# # current_user: Authenticated user data from JWT token
# # Returns:
# # ConversationCreateResponse: Created conversation ID
# # """
# # try:
# # conversation_id = await conversation_repo.create_conversation(
# # user_id=current_user.user_id
# # )
# # return ConversationCreateResponse(
# # conversation_id=conversation_id,
# # created_at=datetime.now().isoformat()
# # )
# # except Exception as e:
# # raise HTTPException(
# # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # detail=f"Failed to create conversation: {str(e)}"
# # )
# # @router.get("/history/{conversation_id}", response_model=ConversationHistoryResponse)
# # async def get_conversation_history(
# # conversation_id: str,
# # current_user: TokenData = Depends(get_current_user) # ← REQUIRES AUTH!
# # ):
# # """
# # Get conversation history by ID.
# # **REQUIRES AUTHENTICATION** - User can only access their own conversations.
# # Args:
# # conversation_id: Conversation ID
# # current_user: Authenticated user data from JWT token
# # Returns:
# # ConversationHistoryResponse: List of messages
# # Raises:
# # HTTPException: If conversation not found or user doesn't own it
# # """
# # try:
# # # Get conversation
# # conversation = await conversation_repo.get_conversation(conversation_id)
# # if not conversation:
# # raise HTTPException(
# # status_code=status.HTTP_404_NOT_FOUND,
# # detail=f"Conversation {conversation_id} not found"
# # )
# # # Verify user owns this conversation
# # if conversation["user_id"] != current_user.user_id:
# # raise HTTPException(
# # status_code=status.HTTP_403_FORBIDDEN,
# # detail="Access denied - you don't own this conversation"
# # )
# # # Format messages
# # messages = []
# # for msg in conversation.get('messages', []):
# # messages.append(MessageModel(
# # role=msg['role'],
# # content=msg['content'],
# # timestamp=msg['timestamp'].isoformat() if isinstance(msg['timestamp'], datetime) else msg['timestamp'],
# # metadata=msg.get('metadata')
# # ))
# # return ConversationHistoryResponse(
# # conversation_id=conversation_id,
# # messages=messages,
# # message_count=len(messages)
# # )
# # except HTTPException:
# # raise
# # except Exception as e:
# # raise HTTPException(
# # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # detail=f"Failed to fetch conversation history: {str(e)}"
# # )
# # @router.get("/conversations")
# # async def list_user_conversations(
# # limit: int = 10,
# # skip: int = 0,
# # current_user: TokenData = Depends(get_current_user) # ← REQUIRES AUTH!
# # ):
# # """
# # List all conversations for the authenticated user.
# # **REQUIRES AUTHENTICATION** - User ID is extracted from JWT token.
# # Args:
# # limit: Maximum conversations to return (default: 10)
# # skip: Number to skip for pagination (default: 0)
# # current_user: Authenticated user data from JWT token
# # Returns:
# # dict: List of conversations for current user
# # """
# # try:
# # conversations = await conversation_repo.get_user_conversations(
# # user_id=current_user.user_id, # From JWT token!
# # limit=limit,
# # skip=skip
# # )
# # # Format response
# # return {
# # "user_id": current_user.user_id,
# # "user_email": current_user.email,
# # "conversations": [
# # {
# # "conversation_id": conv['conversation_id'],
# # "created_at": conv['created_at'].isoformat() if isinstance(conv['created_at'], datetime) else conv['created_at'],
# # "updated_at": conv['updated_at'].isoformat() if isinstance(conv['updated_at'], datetime) else conv['updated_at'],
# # "message_count": len(conv.get('messages', []))
# # }
# # for conv in conversations
# # ],
# # "total": len(conversations)
# # }
# # except Exception as e:
# # raise HTTPException(
# # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # detail=f"Failed to fetch conversations: {str(e)}"
# # )
# # @router.delete("/conversation/{conversation_id}")
# # async def delete_conversation(
# # conversation_id: str,
# # current_user: TokenData = Depends(get_current_user) # ← REQUIRES AUTH!
# # ):
# # """
# # Delete a conversation.
# # **REQUIRES AUTHENTICATION** - User can only delete their own conversations.
# # Args:
# # conversation_id: Conversation ID to delete
# # current_user: Authenticated user data from JWT token
# # Returns:
# # dict: Success message
# # Raises:
# # HTTPException: If conversation not found or user doesn't own it
# # """
# # try:
# # # Get conversation
# # conversation = await conversation_repo.get_conversation(conversation_id)
# # if not conversation:
# # raise HTTPException(
# # status_code=status.HTTP_404_NOT_FOUND,
# # detail=f"Conversation {conversation_id} not found"
# # )
# # # Verify user owns this conversation
# # if conversation["user_id"] != current_user.user_id:
# # raise HTTPException(
# # status_code=status.HTTP_403_FORBIDDEN,
# # detail="Access denied - you don't own this conversation"
# # )
# # # Delete conversation
# # success = await conversation_repo.delete_conversation(conversation_id)
# # if success:
# # return {
# # "message": "Conversation deleted successfully",
# # "conversation_id": conversation_id
# # }
# # else:
# # raise HTTPException(
# # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # detail="Failed to delete conversation"
# # )
# # except HTTPException:
# # raise
# # except Exception as e:
# # raise HTTPException(
# # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # detail=f"Failed to delete conversation: {str(e)}"
# # )
# # @router.get("/health")
# # async def chat_health():
# # """
# # Health check for chat service.
# # **PUBLIC ENDPOINT** - No authentication required.
# # Returns:
# # dict: Health status of chat service components
# # """
# # try:
# # health = await chat_service.health_check()
# # return {
# # "status": "healthy",
# # "service": "chat",
# # "components": health['components'],
# # "timestamp": datetime.now().isoformat()
# # }
# # except Exception as e:
# # return {
# # "status": "unhealthy",
# # "service": "chat",
# # "error": str(e),
# # "timestamp": datetime.now().isoformat()
# # }
# # # ============================================================================
# # # USAGE DOCUMENTATION
# # # ============================================================================
# # """
# # === API USAGE EXAMPLES (WITH AUTHENTICATION) ===
# # ALL ENDPOINTS (except /health) NOW REQUIRE JWT TOKEN IN AUTHORIZATION HEADER!
# # 1. Register user:
# # POST /api/v1/auth/register
# # Body: {
# # "email": "user@example.com",
# # "password": "SecurePass123",
# # "full_name": "John Doe"
# # }
# # Response: { "access_token": "eyJ...", "user": {...} }
# # 2. Login:
# # POST /api/v1/auth/login
# # Body: {
# # "email": "user@example.com",
# # "password": "SecurePass123"
# # }
# # Response: { "access_token": "eyJ...", "user": {...} }
# # 3. Send chat message (WITH TOKEN):
# # POST /api/v1/chat/
# # Headers: { "Authorization": "Bearer eyJ..." }
# # Body: {
# # "query": "What is my account balance?",
# # "conversation_id": "conv_abc" // optional
# # }
# # 4. Get conversation history (WITH TOKEN):
# # GET /api/v1/chat/history/conv_abc
# # Headers: { "Authorization": "Bearer eyJ..." }
# # 5. List conversations (WITH TOKEN):
# # GET /api/v1/chat/conversations?limit=10
# # Headers: { "Authorization": "Bearer eyJ..." }
# # === TESTING WITH CURL ===
# # # 1. Register
# # TOKEN=$(curl -X POST "http://localhost:8000/api/v1/auth/register" \
# # -H "Content-Type: application/json" \
# # -d '{"email":"test@test.com","password":"test123","full_name":"Test User"}' \
# # | jq -r '.access_token')
# # # 2. Send chat message with token
# # curl -X POST "http://localhost:8000/api/v1/chat/" \
# # -H "Content-Type: application/json" \
# # -H "Authorization: Bearer $TOKEN" \
# # -d '{"query": "What is my balance?"}'
# # """
# # # ============================================================================
# # # ======================================================================================================
# # # OLD CODE
# # # ======================================================================================================
# # # """
# # # Chat API Endpoints
# # # RESTful API for the Banking RAG Chatbot
# # # Endpoints:
# # # - POST /chat - Send a message and get response
# # # - GET /chat/history/{conversation_id} - Get conversation history
# # # - POST /chat/conversation - Create new conversation
# # # - GET /chat/conversations - List user's conversations
# # # - GET /chat/health - Health check for chat service
# # # """
# # # from fastapi import APIRouter, HTTPException, status
# # # from pydantic import BaseModel, Field
# # # from typing import List, Dict, Optional
# # # from datetime import datetime
# # # from app.services.chat_service import chat_service
# # # from app.db.repositories.conversation_repository import ConversationRepository
# # # # ============================================================================
# # # # CREATE ROUTER
# # # # ============================================================================
# # # router = APIRouter()
# # # # Initialize repository
# # # conversation_repo = ConversationRepository()
# # # # ============================================================================
# # # # PYDANTIC MODELS (Request/Response schemas)
# # # # ============================================================================
# # # class ChatRequest(BaseModel):
# # # """
# # # Request model for chat endpoint.
# # # Example:
# # # {
# # # "query": "What is my account balance?",
# # # "conversation_id": "abc-123",
# # # "user_id": "user_456"
# # # }
# # # """
# # # query: str = Field(..., description="User query text", min_length=1, max_length=1000)
# # # conversation_id: Optional[str] = Field(None, description="Optional conversation ID")
# # # user_id: str = Field(..., description="User ID")
# # # class Config:
# # # json_schema_extra = {
# # # "example": {
# # # "query": "What is my account balance?",
# # # "conversation_id": "conv-123",
# # # "user_id": "user-456"
# # # }
# # # }
# # # class ChatResponse(BaseModel):
# # # """
# # # Response model for chat endpoint.
# # # Contains the generated response plus metadata about the RAG pipeline.
# # # """
# # # response: str = Field(..., description="Generated response text")
# # # conversation_id: str = Field(..., description="Conversation ID")
# # # policy_action: str = Field(..., description="Policy decision: FETCH or NO_FETCH")
# # # policy_confidence: float = Field(..., description="Policy confidence score (0-1)")
# # # documents_retrieved: int = Field(..., description="Number of documents retrieved")
# # # top_doc_score: Optional[float] = Field(None, description="Best document similarity score")
# # # total_time_ms: float = Field(..., description="Total processing time in milliseconds")
# # # timestamp: str = Field(..., description="Response timestamp (ISO format)")
# # # class ConversationCreateRequest(BaseModel):
# # # """Request to create a new conversation"""
# # # user_id: str = Field(..., description="User ID")
# # # class ConversationCreateResponse(BaseModel):
# # # """Response after creating a conversation"""
# # # conversation_id: str = Field(..., description="Created conversation ID")
# # # created_at: str = Field(..., description="Creation timestamp")
# # # class MessageModel(BaseModel):
# # # """Single message in conversation history"""
# # # role: str = Field(..., description="Message role: user or assistant")
# # # content: str = Field(..., description="Message content")
# # # timestamp: str = Field(..., description="Message timestamp")
# # # metadata: Optional[Dict] = Field(None, description="Optional metadata")
# # # class ConversationHistoryResponse(BaseModel):
# # # """Response containing conversation history"""
# # # conversation_id: str
# # # messages: List[MessageModel]
# # # message_count: int
# # # # ============================================================================
# # # # ENDPOINTS
# # # # ============================================================================
# # # @router.post("/", response_model=ChatResponse, status_code=status.HTTP_200_OK)
# # # async def chat(request: ChatRequest):
# # # """
# # # Main chat endpoint - Send a query and get a response.
# # # This endpoint:
# # # 1. Processes the query through the RAG pipeline
# # # 2. Saves messages to MongoDB
# # # 3. Logs retrieval data for RL training
# # # 4. Returns response with metadata
# # # Args:
# # # request: ChatRequest with query, conversation_id, user_id
# # # Returns:
# # # ChatResponse: Generated response with metadata
# # # Raises:
# # # HTTPException: If processing fails
# # # """
# # # try:
# # # # If no conversation_id provided, create a new conversation
# # # conversation_id = request.conversation_id
# # # if not conversation_id:
# # # conversation_id = await conversation_repo.create_conversation(
# # # user_id=request.user_id
# # # )
# # # # Get conversation history
# # # history = await conversation_repo.get_conversation_history(
# # # conversation_id=conversation_id,
# # # max_messages=10 # Last 5 turns (10 messages)
# # # )
# # # # Save user message to database
# # # await conversation_repo.add_message(
# # # conversation_id=conversation_id,
# # # message={
# # # 'role': 'user',
# # # 'content': request.query,
# # # 'timestamp': datetime.now()
# # # }
# # # )
# # # # Process query through RAG pipeline
# # # result = await chat_service.process_query(
# # # query=request.query,
# # # conversation_history=history,
# # # user_id=request.user_id
# # # )
# # # # Save assistant message to database
# # # await conversation_repo.add_message(
# # # conversation_id=conversation_id,
# # # message={
# # # 'role': 'assistant',
# # # 'content': result['response'],
# # # 'timestamp': datetime.now(),
# # # 'metadata': {
# # # 'policy_action': result['policy_action'],
# # # 'policy_confidence': result['policy_confidence'],
# # # 'documents_retrieved': result['documents_retrieved'],
# # # 'top_doc_score': result['top_doc_score']
# # # }
# # # }
# # # )
# # # # Log retrieval data for RL training
# # # await conversation_repo.log_retrieval({
# # # 'conversation_id': conversation_id,
# # # 'user_id': request.user_id,
# # # 'query': request.query,
# # # 'policy_action': result['policy_action'],
# # # 'policy_confidence': result['policy_confidence'],
# # # 'should_retrieve': result['should_retrieve'],
# # # 'documents_retrieved': result['documents_retrieved'],
# # # 'top_doc_score': result['top_doc_score'],
# # # 'response': result['response'],
# # # 'retrieval_time_ms': result['retrieval_time_ms'],
# # # 'generation_time_ms': result['generation_time_ms'],
# # # 'total_time_ms': result['total_time_ms'],
# # # 'retrieved_docs_metadata': result.get('retrieved_docs_metadata', []),
# # # 'timestamp': datetime.now()
# # # })
# # # # Return response
# # # return ChatResponse(
# # # response=result['response'],
# # # conversation_id=conversation_id,
# # # policy_action=result['policy_action'],
# # # policy_confidence=result['policy_confidence'],
# # # documents_retrieved=result['documents_retrieved'],
# # # top_doc_score=result['top_doc_score'],
# # # total_time_ms=result['total_time_ms'],
# # # timestamp=result['timestamp']
# # # )
# # # except Exception as e:
# # # print(f"❌ Chat endpoint error: {e}")
# # # raise HTTPException(
# # # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # # detail=f"Failed to process chat request: {str(e)}"
# # # )
# # # @router.post("/conversation", response_model=ConversationCreateResponse, status_code=status.HTTP_201_CREATED)
# # # async def create_conversation(request: ConversationCreateRequest):
# # # """
# # # Create a new conversation.
# # # Args:
# # # request: ConversationCreateRequest with user_id
# # # Returns:
# # # ConversationCreateResponse: Created conversation ID
# # # """
# # # try:
# # # conversation_id = await conversation_repo.create_conversation(
# # # user_id=request.user_id
# # # )
# # # return ConversationCreateResponse(
# # # conversation_id=conversation_id,
# # # created_at=datetime.now().isoformat()
# # # )
# # # except Exception as e:
# # # raise HTTPException(
# # # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # # detail=f"Failed to create conversation: {str(e)}"
# # # )
# # # @router.get("/history/{conversation_id}", response_model=ConversationHistoryResponse)
# # # async def get_conversation_history(conversation_id: str):
# # # """
# # # Get conversation history by ID.
# # # Args:
# # # conversation_id: Conversation ID
# # # Returns:
# # # ConversationHistoryResponse: List of messages
# # # """
# # # try:
# # # # Get conversation
# # # conversation = await conversation_repo.get_conversation(conversation_id)
# # # if not conversation:
# # # raise HTTPException(
# # # status_code=status.HTTP_404_NOT_FOUND,
# # # detail=f"Conversation {conversation_id} not found"
# # # )
# # # # Format messages
# # # messages = []
# # # for msg in conversation.get('messages', []):
# # # messages.append(MessageModel(
# # # role=msg['role'],
# # # content=msg['content'],
# # # timestamp=msg['timestamp'].isoformat() if isinstance(msg['timestamp'], datetime) else msg['timestamp'],
# # # metadata=msg.get('metadata')
# # # ))
# # # return ConversationHistoryResponse(
# # # conversation_id=conversation_id,
# # # messages=messages,
# # # message_count=len(messages)
# # # )
# # # except HTTPException:
# # # raise
# # # except Exception as e:
# # # raise HTTPException(
# # # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # # detail=f"Failed to fetch conversation history: {str(e)}"
# # # )
# # # @router.get("/conversations")
# # # async def list_user_conversations(user_id: str, limit: int = 10, skip: int = 0):
# # # """
# # # List all conversations for a user.
# # # Args:
# # # user_id: User ID
# # # limit: Maximum conversations to return (default: 10)
# # # skip: Number to skip for pagination (default: 0)
# # # Returns:
# # # dict: List of conversations
# # # """
# # # try:
# # # conversations = await conversation_repo.get_user_conversations(
# # # user_id=user_id,
# # # limit=limit,
# # # skip=skip
# # # )
# # # # Format response
# # # return {
# # # "user_id": user_id,
# # # "conversations": [
# # # {
# # # "conversation_id": conv['conversation_id'],
# # # "created_at": conv['created_at'].isoformat() if isinstance(conv['created_at'], datetime) else conv['created_at'],
# # # "updated_at": conv['updated_at'].isoformat() if isinstance(conv['updated_at'], datetime) else conv['updated_at'],
# # # "message_count": len(conv.get('messages', []))
# # # }
# # # for conv in conversations
# # # ],
# # # "total": len(conversations)
# # # }
# # # except Exception as e:
# # # raise HTTPException(
# # # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# # # detail=f"Failed to fetch conversations: {str(e)}"
# # # )
# # # @router.get("/health")
# # # async def chat_health():
# # # """
# # # Health check for chat service.
# # # Returns:
# # # dict: Health status of chat service components
# # # """
# # # try:
# # # health = await chat_service.health_check()
# # # return {
# # # "status": "healthy",
# # # "service": "chat",
# # # "components": health['components'],
# # # "timestamp": datetime.now().isoformat()
# # # }
# # # except Exception as e:
# # # return {
# # # "status": "unhealthy",
# # # "service": "chat",
# # # "error": str(e),
# # # "timestamp": datetime.now().isoformat()
# # # }
# # # # ============================================================================
# # # # USAGE DOCUMENTATION
# # # # ============================================================================
# # # """
# # # === API USAGE EXAMPLES ===
# # # 1. Send a chat message:
# # # POST /api/v1/chat/
# # # Body: {
# # # "query": "What is my account balance?",
# # # "user_id": "user_123",
# # # "conversation_id": "conv_abc" // optional
# # # }
# # # 2. Create new conversation:
# # # POST /api/v1/chat/conversation
# # # Body: {
# # # "user_id": "user_123"
# # # }
# # # 3. Get conversation history:
# # # GET /api/v1/chat/history/conv_abc
# # # 4. List user's conversations:
# # # GET /api/v1/chat/conversations?user_id=user_123&limit=10&skip=0
# # # 5. Check health:
# # # GET /api/v1/chat/health
# # # === TESTING WITH CURL ===
# # # # Send chat message
# # # curl -X POST "http://localhost:8000/api/v1/chat/" \
# # # -H "Content-Type: application/json" \
# # # -d '{
# # # "query": "What is my balance?",
# # # "user_id": "user_123"
# # # }'
# # # # Get history
# # # curl "http://localhost:8000/api/v1/chat/history/conv_123"
# # # === TESTING WITH SWAGGER UI ===
# # # After starting the server, visit:
# # # http://localhost:8000/docs
# # # Interactive API documentation with "Try it out" buttons!
# # # """