|
6 | 6 | the chat service logic. |
7 | 7 | """ |
8 | 8 |
|
9 | | -from fastapi import APIRouter, HTTPException, Response, status |
| 9 | +from typing import List, Optional |
| 10 | +from fastapi import APIRouter, HTTPException, Response, status, UploadFile, File, Form |
10 | 11 | from api.models.schemas import ( |
11 | 12 | ChatRequest, |
12 | 13 | ChatResponse, |
13 | 14 | SessionResponse, |
14 | | - DeleteResponse |
| 15 | + DeleteResponse, |
| 16 | + FileAttachment, |
| 17 | + SupportedExtensionsResponse |
15 | 18 | ) |
16 | 19 | from api.services.chat_service import get_chatbot_reply |
17 | 20 | from api.services.memory import ( |
18 | 21 | init_session, |
19 | 22 | delete_session, |
20 | 23 | session_exists |
21 | 24 | ) |
| 25 | +from api.services.file_service import ( |
| 26 | + process_uploaded_file, |
| 27 | + get_supported_extensions, |
| 28 | + FileProcessingError |
| 29 | +) |
22 | 30 |
|
23 | 31 | router = APIRouter() |
24 | 32 |
|
@@ -61,6 +69,90 @@ def chatbot_reply(session_id: str, request: ChatRequest): |
61 | 69 | return get_chatbot_reply(session_id, request.message) |
62 | 70 |
|
63 | 71 |
|
| 72 | +@router.post("/sessions/{session_id}/message/upload", response_model=ChatResponse) |
| 73 | +async def chatbot_reply_with_files( |
| 74 | + session_id: str, |
| 75 | + message: str = Form(...), |
| 76 | + files: Optional[List[UploadFile]] = File(None) |
| 77 | +): |
| 78 | + """ |
| 79 | + POST endpoint to handle chatbot replies with file uploads. |
| 80 | +
|
| 81 | + Receives a user message with optional file attachments and returns |
| 82 | + the assistant's reply. Files are processed and their content is |
| 83 | + included in the context for the LLM. |
| 84 | +
|
| 85 | + Supported file types: |
| 86 | + - Text files: .txt, .log, .md, .json, .xml, .yaml, .yml, code files |
| 87 | + - Image files: .png, .jpg, .jpeg, .gif, .webp, .bmp |
| 88 | +
|
| 89 | + Args: |
| 90 | + session_id (str): The ID of the session from the URL path. |
| 91 | + message (str): The user's message (form field). |
| 92 | + files (List[UploadFile]): Optional list of uploaded files. |
| 93 | +
|
| 94 | + Returns: |
| 95 | + ChatResponse: The chatbot's generated reply. |
| 96 | +
|
| 97 | + Raises: |
| 98 | + HTTPException: 404 if session not found, 400 if file processing fails, |
| 99 | + 422 if message is empty and no files provided. |
| 100 | + """ |
| 101 | + if not session_exists(session_id): |
| 102 | + raise HTTPException(status_code=404, detail="Session not found.") |
| 103 | + |
| 104 | + # Validate that at least message or files are provided |
| 105 | + has_message = message and message.strip() |
| 106 | + has_files = files and len(files) > 0 |
| 107 | + |
| 108 | + if not has_message and not has_files: |
| 109 | + raise HTTPException( |
| 110 | + status_code=422, |
| 111 | + detail="Either message or files must be provided." |
| 112 | + ) |
| 113 | + |
| 114 | + # Process uploaded files |
| 115 | + processed_files: List[FileAttachment] = [] |
| 116 | + |
| 117 | + if files: |
| 118 | + for upload_file in files: |
| 119 | + try: |
| 120 | + content = await upload_file.read() |
| 121 | + processed = process_uploaded_file( |
| 122 | + content, upload_file.filename or "unknown" |
| 123 | + ) |
| 124 | + processed_files.append(FileAttachment(**processed)) |
| 125 | + except FileProcessingError as e: |
| 126 | + raise HTTPException(status_code=400, detail=str(e)) from e |
| 127 | + except Exception as e: |
| 128 | + raise HTTPException( |
| 129 | + status_code=500, |
| 130 | + detail=f"Failed to process file: {type(e).__name__}" |
| 131 | + ) from e |
| 132 | + finally: |
| 133 | + await upload_file.close() |
| 134 | + |
| 135 | + # Use default message if only files provided |
| 136 | + final_message = message.strip() if has_message else "Please analyze the attached file(s)." |
| 137 | + |
| 138 | + return get_chatbot_reply( |
| 139 | + session_id, final_message, processed_files if processed_files else None |
| 140 | + ) |
| 141 | + |
| 142 | + |
| 143 | +@router.get("/files/supported-extensions", response_model=SupportedExtensionsResponse) |
| 144 | +def get_supported_file_extensions(): |
| 145 | + """ |
| 146 | + GET endpoint to retrieve supported file extensions for upload. |
| 147 | +
|
| 148 | + Returns: |
| 149 | + SupportedExtensionsResponse: Lists of supported text and image extensions, |
| 150 | + along with size limits. |
| 151 | + """ |
| 152 | + extensions = get_supported_extensions() |
| 153 | + return SupportedExtensionsResponse(**extensions) |
| 154 | + |
| 155 | + |
64 | 156 | @router.delete("/sessions/{session_id}", response_model=DeleteResponse) |
65 | 157 | def delete_chat(session_id: str): |
66 | 158 | """ |
|
0 commit comments