File size: 4,965 Bytes
411a994
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import logging
from fastapi import FastAPI, Request, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette import status

from app.utils.config import settings
from app.utils.security import get_current_user_optional
from app.routers import users, chat, audio, images
from app.routers import cameroon_data
from app.routers import ai


def create_app() -> FastAPI:
    # Configure logging
    settings.configure_logging()
    logger = logging.getLogger(__name__)
    
    app = FastAPI(
        title="Carehelp API",
        description="Backend IA médical pour le Cameroun (Carehelp)",
        version="1.0.0",
        contact={
            "name": "Carehelp",
        },
    )

    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.CORS_ALLOW_ORIGINS,
        allow_credentials=True,
        allow_methods=["*"]
        ,
        allow_headers=["*"]
        ,
    )

    # Routers
    app.include_router(users.router, prefix="/api", tags=["users"]) 
    app.include_router(chat.router, prefix="/api", tags=["chat"]) 
    app.include_router(audio.router, prefix="/api", tags=["audio"]) 
    app.include_router(images.router, prefix="/api", tags=["images"]) 
    app.include_router(cameroon_data.router, prefix="/api/cameroon-data", tags=["cameroon-data"]) 
    app.include_router(ai.router, prefix="/api", tags=["ai"]) 

    # Log all registered routes for debugging
    try:
        route_paths = sorted([getattr(r, 'path', str(r)) for r in app.routes])
        logging.getLogger(__name__).info(f"Registered routes: {route_paths}")
    except Exception as e:
        logging.getLogger(__name__).error(f"Failed to list routes: {e}")

    @app.get("/health")
    def healthcheck():
        return {"status": "ok"}

    # Debug: return list of routes
    @app.get("/routes")
    def list_routes():
        return {"routes": [
            {
                "path": getattr(r, 'path', str(r)),
                "name": getattr(r, 'name', None),
                "methods": list(getattr(r, 'methods', []) or [])
            }
            for r in app.routes
        ]}

    # No database initialization; Supabase will manage schema

    @app.post("/gateway")
    async def gateway(request: Request, current_user=Depends(get_current_user_optional)):
        """
        Endpoint unique pour le frontend.
        - Mode actions: {"action": "chat|transcribe|analyze-image|analyze-multimodal|translate", "payload": {...}}
        - Mode unifié (recommandé): {"payload": {text?, image?, images?, audio?, audios?, files?, file_names?, location?, want_stats?, agent_mode?}}
          Si "action" est omis ou vaut l'un de {"auto", "unified", "ai"}, la requête est routée automatiquement selon les champs fournis.
        """
        body = await request.json()
        action = body.get("action")
        payload = body.get("payload", {})
        
        # Mode unifié: pas d'action explicitement fournie ou action spéciale
        if not action or str(action).lower() in {"auto", "unified", "ai"}:
            try:
                req = ai.AIRequest(
                    text=payload.get("text"),
                    image=payload.get("image"),
                    images=payload.get("images"),
                    audio=payload.get("audio"),
                    audios=payload.get("audios"),
                    want_stats=payload.get("want_stats", False),
                    location=payload.get("location"),
                    files=payload.get("files"),
                    file_names=payload.get("file_names"),
                    agent_mode=payload.get("agent_mode"),
                )
            except Exception as e:
                return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content={"detail": f"Payload invalide pour le mode unifié: {e}"})
            # Déléguer au point d'entrée IA unifié (gère texte, image(s), audio(s), fichiers)
            return await ai.ai_endpoint(req)

        # Proxifier vers les endpoints internes
        if action == "chat":
            return await chat.handle_chat_via_gateway(payload, current_user)
        if action == "transcribe":
            return await audio.handle_transcription_via_gateway(payload, current_user)
        if action == "analyze-image":
            return await images.handle_analyze_image_via_gateway(payload, current_user)
        if action == "analyze-multimodal":
            return await images.handle_analyze_multimodal_via_gateway(payload, current_user)
        if action == "translate":
            return await chat.handle_translate_via_gateway(payload, current_user)

        return JSONResponse(
            status_code=status.HTTP_400_BAD_REQUEST,
            content={"detail": "Action inconnue"},
        )

    return app


app = create_app()

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="0.0.0.0", port=int(settings.PORT), reload=True)