Spaces:
Sleeping
Sleeping
| from fastapi import APIRouter, File, UploadFile, HTTPException | |
| from bson import ObjectId | |
| import random | |
| from ..schema import PredictionResponse, DiagnosticRecordSchema, OverrideRequest | |
| from ..database import upload_image_to_cloud, diagnostic_records | |
| from ..ml_model import run_diagnostic_inference | |
| # Create the router instance | |
| router = APIRouter( | |
| prefix='/api/v1', | |
| tags=['Diagnostics'] | |
| ) | |
| # Enforce strict typing on the response using response_model | |
| async def predict_xray(file: UploadFile = File(...)): | |
| """ | |
| Analyzes an uploaded X-ray, uploads to Cloudinary, saves to MongoDB, | |
| and returns strictly typed results. | |
| """ | |
| if not file.content_type.startswith('image/'): | |
| raise HTTPException(status_code=400, detail='Invalid file format. Please upload an image.') | |
| try: | |
| image_bytes = await file.read() | |
| # Select a model with given weights | |
| assigned_model = random.choices(['DenseNet121_v1', 'ResNet50_Challenger'], | |
| weights=[1, 0], # To train another model in the future | |
| k=1)[0] # For now, we only have DenseNet121_v1, so it gets 100% of the traffic. This is where A/B testing logic would go in the future. | |
| ai_result = run_diagnostic_inference(image_bytes) | |
| original_url = upload_image_to_cloud(image_bytes, folder_name='raw_xrays') | |
| heatmap_url = upload_image_to_cloud(ai_result['heatmap_bytes'], folder_name='heatmaps') | |
| # Use Pydantic to validate the data before saving to MongoDB | |
| record = DiagnosticRecordSchema( | |
| original_image_url=original_url, | |
| heatmap_image_url=heatmap_url, | |
| prediction=ai_result['prediction'], | |
| confidence_score=ai_result['confidence'], | |
| probabilities=ai_result['probabilities'], | |
| model_variant=assigned_model | |
| ) | |
| # model_dump() converts the Pydantic model safely to a dictionary for MongoDB | |
| result = await diagnostic_records.insert_one(record.model_dump()) | |
| # Return a dictionary that strictly matches PredictionResponse | |
| return { | |
| 'status': 'success', | |
| 'record_id': str(result.inserted_id), | |
| 'prediction': ai_result['prediction'], | |
| 'confidence': ai_result['confidence'], | |
| 'heatmap_url': heatmap_url, | |
| 'original_url': original_url | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f'Inference pipeline failed: {str(e)}') | |
| async def submit_physician_override(record_id: str, override_data: OverrideRequest): | |
| """ | |
| Updates a specific diagnostic record with the physician's feedback. | |
| """ | |
| try: | |
| # Update the MongoDB document matching the ID | |
| update_result = await diagnostic_records.update_one( | |
| {'_id': ObjectId(record_id)}, | |
| {'$set': { | |
| 'physician_override': override_data.physician_override, | |
| 'override_notes': override_data.notes | |
| }} | |
| ) | |
| if update_result.modified_count == 0: | |
| raise HTTPException(status_code=404, detail='Diagnostic record not found.') | |
| return {'status': 'success', 'message': 'Feedback recorded. A/B metrics updated.'} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f'Database update failed: {str(e)}') |