Download backend/app/api/image.py from Rixhot/ZARAI: direct link, hf CLI and curl.
- Browser
- Download file 5.07 kB
-
https://huggingface.co/spaces/Rixhot/ZARAI/resolve/main/backend/app/api/image.py
- Command line
-
hf download hf://spaces/Rixhot/ZARAI/backend/app/api/image.py
-
curl -L -o image.py https://huggingface.co/spaces/Rixhot/ZARAI/resolve/main/backend/app/api/image.py
5.07 kB
| """ | |
| JARVIS β Image API Route | |
| POST /api/image/analyze β upload image + optional question β analysis + LLM response | |
| POST /api/image/caption β quick caption only | |
| """ | |
| import uuid | |
| from typing import Optional | |
| from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form | |
| from pydantic import BaseModel | |
| from loguru import logger | |
| from app.db.database import AsyncSession, get_db, create_session, get_session, add_message | |
| from app.pipelines.image_pipeline import ( | |
| analyze_image, | |
| generate_caption, | |
| load_image_from_bytes, | |
| ) | |
| from app.pipelines.llm_pipeline import build_chat_prompt, generate_response | |
| from app.memory.faiss_store import FAISSMemoryStore | |
| from app.core.config import settings | |
| router = APIRouter() | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| # Response schemas | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| class ImageAnalysisResponse(BaseModel): | |
| caption: str | |
| detailed_caption: str | |
| top_categories: list | |
| analysis: str | |
| llm_response: str | |
| session_id: str | |
| size: dict | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| # Routes | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| async def analyze_image_endpoint( | |
| file: UploadFile = File(...), | |
| question: Optional[str] = Form(None), | |
| session_id: Optional[str] = Form(None), | |
| db: AsyncSession = Depends(get_db), | |
| ): | |
| """ | |
| Analyze an uploaded image using BLIP + CLIP. | |
| Optionally answer a question about the image using the LLM. | |
| """ | |
| # Validate file type | |
| if not file.content_type.startswith("image/"): | |
| raise HTTPException(status_code=400, detail="File must be an image") | |
| # Read file | |
| contents = await file.read() | |
| if len(contents) > settings.MAX_UPLOAD_MB * 1024 * 1024: | |
| raise HTTPException(status_code=413, detail=f"Image too large (max {settings.MAX_UPLOAD_MB}MB)") | |
| # Load PIL image | |
| try: | |
| pil_image = load_image_from_bytes(contents) | |
| except Exception as e: | |
| raise HTTPException(status_code=400, detail=f"Invalid image: {str(e)}") | |
| # Run vision analysis | |
| try: | |
| vision_result = analyze_image(pil_image, user_question=question) | |
| except Exception as e: | |
| logger.error(f"Vision analysis error: {e}") | |
| raise HTTPException(status_code=500, detail=f"Vision analysis failed: {str(e)}") | |
| # Build LLM response | |
| image_caption = vision_result.get("caption", "") | |
| detailed = vision_result.get("detailed_caption", "") | |
| analysis = vision_result.get("analysis", "") | |
| user_question_text = question or "Describe this image in detail." | |
| prompt = build_chat_prompt( | |
| user_message=user_question_text, | |
| history=[], | |
| image_caption=f"{image_caption}. {detailed}", | |
| system_override=( | |
| "You are JARVIS, analyzing an image for the user. " | |
| "Use the provided image caption to give a thorough, helpful response. " | |
| "Be descriptive and insightful." | |
| ), | |
| ) | |
| llm_response = generate_response(prompt, max_new_tokens=300) | |
| # Session handling | |
| session_id = session_id or str(uuid.uuid4()) | |
| session = await get_session(db, session_id) | |
| if not session: | |
| await create_session(db, session_id, title=f"Image: {image_caption[:40]}") | |
| # Store in DB | |
| user_content = f"[IMAGE UPLOADED] {user_question_text}" | |
| await add_message(db, session_id, "user", user_content, modality="image") | |
| await add_message(db, session_id, "assistant", llm_response, modality="image") | |
| # Store in memory | |
| memory = FAISSMemoryStore.get_instance() | |
| memory.add( | |
| f"Image analysis: {image_caption}. {detailed}", | |
| session_id=session_id, | |
| metadata={"type": "image"}, | |
| ) | |
| return ImageAnalysisResponse( | |
| caption=image_caption, | |
| detailed_caption=detailed, | |
| top_categories=vision_result.get("top_categories", []), | |
| analysis=analysis, | |
| llm_response=llm_response, | |
| session_id=session_id, | |
| size=vision_result.get("size", {}), | |
| ) | |
| async def quick_caption(file: UploadFile = File(...)): | |
| """Quick image captioning β no LLM, just BLIP.""" | |
| if not file.content_type.startswith("image/"): | |
| raise HTTPException(status_code=400, detail="File must be an image") | |
| contents = await file.read() | |
| try: | |
| pil_image = load_image_from_bytes(contents) | |
| caption = generate_caption(pil_image) | |
| return {"caption": caption, "width": pil_image.width, "height": pil_image.height} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |