from fastapi import FastAPI, File, UploadFile from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles import os from services.whisper_service import transcribeAudio from services.llm_service import analyzePainDescription from services.conversation_service import generateFollowUpQuestions from services.neuro_symbolic_service import analyze_pain_neuro_symbolic, get_system_info # Smart embedding service selection EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "biolord") # Options: "biolord", "openai" if EMBEDDING_MODEL == "biolord": print(f"[Main] Using BioLORD-2023-M embeddings (medical specialist)") from services.semantic_distance_service_biolord import precompute_dictionary_embeddings else: print(f"[Main] Using OpenAI embeddings (general purpose)") from services.semantic_distance_service_v2 import precompute_dictionary_embeddings from pydantic import BaseModel from typing import List, Dict class ConversationRequest(BaseModel): history: List[Dict] app = FastAPI( title = "Pain Report Platform", version = "0.2.0" # Updated to v0.2.0 with BioLORD support ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.mount("/images", StaticFiles(directory="images"), name="images") @app.get("/") async def root(): return { "message": "This is pain report platform backend API.", "status": "running" } @app.get("/health") async def healthCheck(): return { "status": "healthy", "message": "The API is healthy and running", "version": "0.1.3" } @app.post("/api/analyze-audio") async def analyze_audio(file: UploadFile = File(...)): #check file if not file.content_type.startswith("audio/"): return {"error": "Invalid file type.", "message": "Please upload an audio file."} #read file audioBytes = await file.read() try: transcription = transcribeAudio(audioBytes, language=None) analysis = analyzePainDescription(transcription["text"]) return { "status": "success", "message": "Audio file received", "size": len(audioBytes), "filename": file.filename, "trancription": transcription["text"], "language": transcription["language"], "analysis": analysis } except Exception as e: return { "status": "error", "message": str(e) } @app.post("/api/follow-up") async def getFollowUpQuestion(request: ConversationRequest): try: followUp = generateFollowUpQuestions(request.history) return { "status": "success", "followup": followUp } except Exception as e: return { "status": "error", "message": str(e) } @app.post("/api/analyze-text-neuro-symbolic") async def analyzeTextNeuroSymbolic(request: dict): """ Analyze text pain description using neuro-symbolic architecture. This is the new upgraded analysis endpoint that uses: - LLM for narrow-scope entity extraction only - Ontology mapping for multilingual medical terminology - Rule-based engine for deterministic clinical recommendations Returns structured pain data with complete explainability and reasoning chain. """ try: patient_text = request.get("text", "") if not patient_text: return { "status": "error", "message": "No text provided" } # Execute neuro-symbolic pipeline analysis = analyze_pain_neuro_symbolic(patient_text) return analysis except Exception as e: return { "status": "error", "message": str(e) } @app.post("/api/analyze-audio-neuro-symbolic") async def analyzeAudioNeuroSymbolic(file: UploadFile = File(...)): """ Analyze audio pain description using neuro-symbolic architecture. Combines: 1. Whisper transcription (audio → text) 2. Neuro-symbolic analysis (text → structured clinical data) Returns complete explainable report with reasoning chain. """ if not file.content_type.startswith("audio/"): return { "error": "Invalid file type.", "message": "Please upload an audio file." } audioBytes = await file.read() try: # Step 1: Transcribe audio transcription_result = transcribeAudio(audioBytes, language=None) original_transcription = transcription_result["text"] # Step 2: Neuro-symbolic analysis (includes normalization + ontology mapping) analysis = analyze_pain_neuro_symbolic(original_transcription) # Merge transcription info with analysis results # The analysis already contains transcription normalization in analysis["transcription"] return { "status": "success", "message": "Audio analyzed successfully using neuro-symbolic architecture", "size": len(audioBytes), "filename": file.filename, "whisper_language": transcription_result["language"], **analysis # Spread analysis results (includes transcription, structured_data, etc.) } except Exception as e: return { "status": "error", "message": str(e) } @app.get("/api/system-info") async def getSystemInfo(): """ Get information about the neuro-symbolic pain assessment system. Returns system configuration, capabilities, and limitations. Useful for documentation and debugging. """ try: info = get_system_info() return { "status": "success", "system_info": info } except Exception as e: return { "status": "error", "message": str(e) } @app.on_event("startup") async def startup_event(): print("[Startup] Precomputing dictionary embeddings...") precompute_dictionary_embeddings() print("[Startup] System ready!") if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)