from fastapi import FastAPI, Depends, HTTPException, BackgroundTasks, File, UploadFile, Form, Query from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles from pydantic import BaseModel from sqlalchemy.orm import Session from typing import List, Optional, Dict, Any import os import shutil import json from datetime import datetime from app.db.database import get_db, engine from app.db.models import Base, ChatHistory, Document from app.agent import create_agent from app import document_processor from app import document_generator # Create tables if they don't exist Base.metadata.create_all(bind=engine) # Initialize FastAPI app app = FastAPI( title="Endüstri Chatbot API", description="Endüstriyel maliyet hesaplama için chatbot API", version="0.1.0" ) # Add CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], # In production, replace with specific origins allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Define request and response models class ChatRequest(BaseModel): message: str class ChatResponse(BaseModel): response: str class HealthResponse(BaseModel): status: str model_name: str class DocumentResponse(BaseModel): id: int filename: str content_type: str file_size: int document_type: Optional[str] = None template_type: Optional[str] = None uploaded_at: datetime analyzed_at: Optional[datetime] = None analysis_summary: Optional[str] = None class DocumentAnalysisResponse(BaseModel): id: int filename: str analysis_result: Dict[str, Any] analyzed_at: datetime class DocumentGenerateRequest(BaseModel): document_type: str # 'word' veya 'excel' template_type: str # 'maliyet_raporu', 'teklif', vs. data: Dict[str, Any] # Doküman içeriği için veriler class DocumentGenerateResponse(BaseModel): id: int filename: str document_type: str template_type: str file_path: str file_size: int uploaded_at: datetime # Save chat history to database def save_chat_history(db: Session, user_input: str, assistant_response: str): chat_entry = ChatHistory( user_input=user_input, assistant_response=assistant_response ) db.add(chat_entry) db.commit() # Health check endpoint @app.get("/health", response_model=HealthResponse) def health_check(): return { "status": "ok", "model_name": os.getenv("MODEL_NAME", "bigscience/bloomz-560m") } # Chat endpoint @app.post("/chat", response_model=ChatResponse) async def chat(request: ChatRequest, background_tasks: BackgroundTasks, db: Session = Depends(get_db)): try: # Create agent agent_executor = create_agent(db) # Run agent result = agent_executor.invoke({"input": request.message}) response = result["output"] # Save chat history in background background_tasks.add_task(save_chat_history, db, request.message, response) return {"response": response} except Exception as e: raise HTTPException(status_code=500, detail=f"Error processing request: {str(e)}") # Get chat history endpoint @app.get("/chat/history", response_model=List[dict]) def get_chat_history(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)): history = db.query(ChatHistory).order_by(ChatHistory.created_at.desc()).offset(skip).limit(limit).all() return [{ "id": entry.id, "user_input": entry.user_input, "assistant_response": entry.assistant_response, "created_at": entry.created_at } for entry in history] # Belge yükleme endpoint'i @app.post("/documents/upload", response_model=DocumentResponse) async def upload_document( file: UploadFile = File(...), analyze: bool = Form(False), db: Session = Depends(get_db) ): # Desteklenen dosya türlerini kontrol et if file.content_type not in document_processor.SUPPORTED_CONTENT_TYPES: raise HTTPException( status_code=400, detail=f"Desteklenmeyen dosya türü: {file.content_type}. Desteklenen türler: {list(document_processor.SUPPORTED_CONTENT_TYPES.keys())}" ) try: # Dosyayı kaydet filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{file.filename}" file_path = document_processor.save_uploaded_file(file, filename) file_size = os.path.getsize(file_path) # Metin çıkar ve analiz et content_text = None analysis_result = None if analyze: content_text = document_processor.process_document(file_path, file.content_type) if content_text: analysis_result = document_processor.analyze_document_content(content_text, db) # Veritabanına kaydet document = document_processor.save_document_to_db( db, filename, file.content_type, file_path, file_size, content_text, analysis_result ) # Yanıt oluştur response = { "id": document.id, "filename": document.filename, "content_type": document.content_type, "file_size": document.file_size, "uploaded_at": document.uploaded_at, "analyzed_at": document.analyzed_at } if analysis_result: response["analysis_summary"] = "Belge analiz edildi. /documents/{id}/analysis endpoint'inden sonuçları görüntüleyebilirsiniz." return response except Exception as e: raise HTTPException(status_code=500, detail=f"Belge yükleme hatası: {str(e)}") # Belge analiz endpoint'i @app.post("/documents/{document_id}/analyze", response_model=DocumentAnalysisResponse) async def analyze_document( document_id: int, db: Session = Depends(get_db) ): # Belgeyi veritabanından al document = db.query(Document).filter(Document.id == document_id).first() if not document: raise HTTPException(status_code=404, detail=f"Belge bulunamadı: {document_id}") try: # Eğer metin çıkarılmamışsa, çıkar if not document.content_text: document.content_text = document_processor.process_document(document.file_path, document.content_type) db.commit() # Metni analiz et analysis_result = document_processor.analyze_document_content(document.content_text, db) # Analiz sonucunu güncelle document.analysis_result = analysis_result document.analyzed_at = datetime.now() db.commit() # Yanıt oluştur return { "id": document.id, "filename": document.filename, "analysis_result": json.loads(document.analysis_result), "analyzed_at": document.analyzed_at } except Exception as e: raise HTTPException(status_code=500, detail=f"Belge analiz hatası: {str(e)}") # Belge listesi endpoint'i @app.get("/documents", response_model=List[DocumentResponse]) async def list_documents( skip: int = 0, limit: int = 10, db: Session = Depends(get_db) ): documents = db.query(Document).order_by(Document.uploaded_at.desc()).offset(skip).limit(limit).all() return [ { "id": doc.id, "filename": doc.filename, "content_type": doc.content_type, "file_size": doc.file_size, "uploaded_at": doc.uploaded_at, "analyzed_at": doc.analyzed_at, "analysis_summary": "Analiz sonuçları mevcut" if doc.analysis_result else None } for doc in documents ] # Belge detayı endpoint'i @app.get("/documents/{document_id}", response_model=DocumentResponse) async def get_document( document_id: int, db: Session = Depends(get_db) ): document = db.query(Document).filter(Document.id == document_id).first() if not document: raise HTTPException(status_code=404, detail=f"Belge bulunamadı: {document_id}") return { "id": document.id, "filename": document.filename, "content_type": document.content_type, "file_size": document.file_size, "uploaded_at": document.uploaded_at, "analyzed_at": document.analyzed_at, "analysis_summary": "Analiz sonuçları mevcut" if document.analysis_result else None } # Belge analiz sonucu endpoint'i @app.get("/documents/{document_id}/analysis", response_model=DocumentAnalysisResponse) async def get_document_analysis( document_id: int, db: Session = Depends(get_db) ): document = db.query(Document).filter(Document.id == document_id).first() if not document: raise HTTPException(status_code=404, detail=f"Belge bulunamadı: {document_id}") if not document.analysis_result: raise HTTPException(status_code=404, detail=f"Belge henüz analiz edilmemiş: {document_id}") return { "id": document.id, "filename": document.filename, "analysis_result": json.loads(document.analysis_result), "analyzed_at": document.analyzed_at } # Root endpoint @app.get("/") def read_root(): return {"message": "Endüstri Chatbot API'ye Hoş Geldiniz! /docs adresinden API dokümantasyonuna ulaşabilirsiniz."} # Statik dosyaları sunmak için app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads") # Doküman oluşturma endpoint'i @app.post("/documents/generate", response_model=DocumentGenerateResponse) async def generate_document( request: DocumentGenerateRequest, db: Session = Depends(get_db) ): try: # Doküman türüne göre oluştur if request.document_type.lower() == 'word': file_path = document_generator.create_word_document(request.data, request.template_type) elif request.document_type.lower() == 'excel': file_path = document_generator.create_excel_document(request.data, request.template_type) else: raise HTTPException(status_code=400, detail=f"Desteklenmeyen doküman türü: {request.document_type}. Desteklenen türler: word, excel") # Dosya boyutunu al file_size = os.path.getsize(file_path) # Dosya adını al filename = os.path.basename(file_path) # İçerik türünü belirle content_type = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" if request.document_type.lower() == 'word' else "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" # Veritabanına kaydet document = document_generator.save_document_to_db( db, filename, content_type, file_path, file_size, request.document_type.lower(), request.template_type ) # Yanıt oluştur return { "id": document.id, "filename": document.filename, "document_type": document.document_type, "template_type": document.template_type, "file_path": document.file_path, "file_size": document.file_size, "uploaded_at": document.uploaded_at } except Exception as e: raise HTTPException(status_code=500, detail=f"Doküman oluşturma hatası: {str(e)}") # Doküman şablonları endpoint'i @app.get("/documents/templates") async def get_document_templates(): return { "templates": [ { "id": "maliyet_raporu", "name": "Maliyet Raporu", "description": "İşçilik ve malzeme maliyetlerini içeren detaylı maliyet raporu", "supported_formats": ["word", "excel"] }, { "id": "teklif", "name": "Teklif", "description": "Müşteriye sunulacak resmi teklif dokümanı", "supported_formats": ["word", "excel"] } ] } # Run with: uvicorn app.main:app --reload if __name__ == "__main__": import uvicorn uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)