apac-ops-bot/backend/app/api/v1/endpoints/messages.py
SamoilenkoVadym f3f62fef24 Implement chat API endpoints (conversations, messages, tokens)
Conversation Endpoints (/api/v1/conversations):
- POST / - Create new conversation
- GET / - List user's conversations with pagination
- GET /{id} - Get conversation details
- PUT /{id} - Update conversation title
- POST /{id}/archive - Archive conversation
- DELETE /{id} - Delete conversation with cascade

Message Endpoints (/api/v1/conversations/{id}/messages):
- GET / - Get messages for conversation with pagination
- POST / - Send message and get AI response

Token Usage Endpoints (/api/v1/tokens):
- GET /usage - Get token usage summary with daily breakdown

Schemas:
- ConversationCreate/Update/Response
- ConversationListResponse for listing
- MessageCreate/Response
- SendMessageResponse with usage stats
- TokenUsageSummary with analytics

Features:
- Full permission checks (user ownership verification)
- Pagination support for all list endpoints
- Detailed error handling with appropriate HTTP codes
- Usage statistics tracking per message
- Cost calculation and reporting
- File search results in message metadata

Security:
- All endpoints require authentication
- User can only access their own conversations
- Proper 403/404 error handling
- Request validation with Pydantic

Router Updates:
- Connected all new endpoints to /api/v1
- Organized by resource (auth, conversations, messages, tokens)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-27 14:34:39 +00:00

131 lines
3.7 KiB
Python

"""
Message Endpoints
API endpoints for sending and retrieving messages
"""
import logging
from typing import List
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.services.chat_service import ChatService
from app.schemas.conversation import (
MessageCreate,
MessageResponse,
SendMessageResponse
)
from app.core.middleware import get_current_active_user
from app.models.user import User
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/conversations", tags=["Messages"])
@router.get("/{conversation_id}/messages", response_model=List[MessageResponse])
async def get_messages(
conversation_id: UUID,
skip: int = Query(0, ge=0, description="Number of messages to skip"),
limit: int = Query(100, ge=1, le=200, description="Maximum number of messages"),
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db)
):
"""
Get messages for a conversation
Args:
conversation_id: Conversation UUID
skip: Pagination offset
limit: Pagination limit
current_user: Current authenticated user
db: Database session
Returns:
List of messages
Raises:
HTTPException: If conversation not found or access denied
"""
chat_service = ChatService(db)
# Verify conversation exists and belongs to user
conversation = await chat_service.get_conversation(conversation_id)
if not conversation:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Conversation not found"
)
if UUID(conversation["user_id"]) != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Access denied"
)
messages = await chat_service.get_messages(
conversation_id=conversation_id,
skip=skip,
limit=limit
)
return [MessageResponse(**msg) for msg in messages]
@router.post("/{conversation_id}/messages", response_model=SendMessageResponse)
async def send_message(
conversation_id: UUID,
request: MessageCreate,
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db)
):
"""
Send a message and get AI response
Args:
conversation_id: Conversation UUID
request: Message content
current_user: Current authenticated user
db: Database session
Returns:
SendMessageResponse with user message, assistant response, and usage stats
Raises:
HTTPException: If conversation not found, access denied, or OpenAI error
"""
chat_service = ChatService(db)
try:
result = await chat_service.send_message(
user_id=current_user.id,
conversation_id=conversation_id,
message_content=request.content
)
logger.info(
f"Message exchange completed in conversation {conversation_id}. "
f"Tokens: {result['usage']['total_tokens']}, Cost: ${result['cost_usd']:.6f}"
)
return SendMessageResponse(**result)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(e)
)
except PermissionError as e:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=str(e)
)
except Exception as e:
logger.error(f"Error sending message: {e}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to process message. Please try again."
)