Spaces:
Running
Running
| from fastapi import FastAPI, UploadFile, File, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from ultralytics import YOLO | |
| from PIL import Image | |
| import io | |
| import uvicorn | |
| import base64 | |
| import cv2 | |
| import numpy as np | |
| from pydantic import BaseModel | |
| from fastapi.responses import StreamingResponse | |
| from src.chains.rag import generate_narrative | |
| from src.chains.chain import create_rag_chain | |
| app = FastAPI( | |
| title="ChiliCare API", | |
| description="API Pendeteksi Penyakit Daun Cabai dengan YOLOv11 dan LLM RAG", | |
| version="1.0.0" | |
| ) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| try: | |
| model = YOLO("model/bestv11.pt") | |
| except Exception as e: | |
| print(f"Error memuat model YOLO: {e}") | |
| model = None | |
| def read_root(): | |
| return {"message": "Selamat datang di ChiliCare API. Gunakan endpoint /detect untuk inferensi."} | |
| async def detect_disease(file: UploadFile = File(...)): | |
| if not model: | |
| raise HTTPException(status_code=500, detail="Model YOLO belum siap atau tidak ditemukan.") | |
| if not file.content_type.startswith("image/"): | |
| raise HTTPException(status_code=400, detail="File yang diunggah harus berupa gambar.") | |
| try: | |
| contents = await file.read() | |
| image = Image.open(io.BytesIO(contents)).convert("RGB") | |
| results = model(image) | |
| annotated_frame = results[0].plot() | |
| rgb_image = cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB) | |
| img_pil = Image.fromarray(rgb_image) | |
| buffered = io.BytesIO() | |
| img_pil.save(buffered, format="JPEG") | |
| img_base64 = base64.b64encode(buffered.getvalue()).decode("utf-8") | |
| detected_labels = {} | |
| for box in results[0].boxes: | |
| cls_id = int(box.cls[0]) | |
| confidence = float(box.conf[0]) | |
| label = model.names[cls_id] | |
| if label not in detected_labels or confidence > detected_labels[label]: | |
| detected_labels[label] = confidence | |
| detections = [] | |
| for label, confidence in detected_labels.items(): | |
| label_capitalized = label.capitalize() | |
| narrative = generate_narrative(label_capitalized) | |
| detections.append({ | |
| "class": label_capitalized, | |
| "confidence": round(confidence, 2), | |
| "narrative": narrative | |
| }) | |
| return { | |
| "status": "success", | |
| "filename": file.filename, | |
| "total_detections": len(detections), | |
| "results": detections, | |
| "image_base64": img_base64 | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Terjadi kesalahan: {str(e)}") | |
| class QuestionRequest(BaseModel): | |
| question: str | |
| async def ask_expert(request: QuestionRequest): | |
| try: | |
| rag_chain = create_rag_chain() | |
| # Buat fungsi generator untuk memecah respons menjadi potongan (chunks) | |
| def generate_response(): | |
| # rag_chain.stream() menggantikan rag_chain.invoke() | |
| for chunk in rag_chain.stream(request.question): | |
| # yield mengirimkan potongan teks ke klien saat itu juga | |
| yield chunk | |
| # Kembalikan StreamingResponse, bukan dictionary JSON | |
| return StreamingResponse( | |
| generate_response(), | |
| media_type="text/event-stream" | |
| ) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Terjadi kesalahan pada LLM: {str(e)}") | |
| if __name__ == "__main__": | |
| uvicorn.run("api:app", host="0.0.0.0", port=8000, reload=True) |