hjsu
Browse files- Dockerfile +2 -1
- audio_utils.py +24 -0
- fastapi_app.py +22 -21
- requirements.txt +4 -1
    	
        Dockerfile
    CHANGED
    
    | @@ -2,4 +2,5 @@ FROM python:3.10-slim | |
| 2 | 
             
            COPY . /app
         | 
| 3 | 
             
            WORKDIR /app
         | 
| 4 | 
             
            RUN pip install -r requirements.txt
         | 
| 5 | 
            -
             | 
|  | 
|  | |
| 2 | 
             
            COPY . /app
         | 
| 3 | 
             
            WORKDIR /app
         | 
| 4 | 
             
            RUN pip install -r requirements.txt
         | 
| 5 | 
            +
            RUN apt-get update && apt-get install -y ffmpeg
         | 
| 6 | 
            +
            CMD ["uvicorn", "fastapi_app:app", "--host", "0.0.0.0", "--port", "7860"]
         | 
    	
        audio_utils.py
    ADDED
    
    | @@ -0,0 +1,24 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import numpy as np
         | 
| 2 | 
            +
            import noisereduce as nr
         | 
| 3 | 
            +
            import soundfile as sf
         | 
| 4 | 
            +
            from pydub import AudioSegment
         | 
| 5 | 
            +
            import io
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            def preprocess_audio(file_bytes):
         | 
| 8 | 
            +
                # Charger l'audio
         | 
| 9 | 
            +
                audio = AudioSegment.from_file(io.BytesIO(file_bytes))
         | 
| 10 | 
            +
                # Convertir en mono, 16kHz
         | 
| 11 | 
            +
                audio = audio.set_channels(1).set_frame_rate(16000)
         | 
| 12 | 
            +
                # Exporter en wav bytes
         | 
| 13 | 
            +
                wav_io = io.BytesIO()
         | 
| 14 | 
            +
                audio.export(wav_io, format='wav')
         | 
| 15 | 
            +
                wav_io.seek(0)
         | 
| 16 | 
            +
                # Charger en numpy
         | 
| 17 | 
            +
                y, sr = sf.read(wav_io)
         | 
| 18 | 
            +
                # Réduction de bruit
         | 
| 19 | 
            +
                y_denoised = nr.reduce_noise(y=y, sr=sr)
         | 
| 20 | 
            +
                # Sauvegarder en wav bytes
         | 
| 21 | 
            +
                out_io = io.BytesIO()
         | 
| 22 | 
            +
                sf.write(out_io, y_denoised, sr, format='WAV')
         | 
| 23 | 
            +
                out_io.seek(0)
         | 
| 24 | 
            +
                return out_io 
         | 
    	
        fastapi_app.py
    CHANGED
    
    | @@ -2,6 +2,8 @@ | |
| 2 | 
             
            """
         | 
| 3 | 
             
            Medical AI Assistant - FastAPI Only Version
         | 
| 4 | 
             
            Simplified endpoints for backend integration with Swagger UI
         | 
|  | |
|  | |
| 5 | 
             
            """
         | 
| 6 |  | 
| 7 | 
             
            from fastapi import FastAPI, HTTPException, File, UploadFile, BackgroundTasks
         | 
| @@ -62,15 +64,15 @@ async def load_models(): | |
| 62 |  | 
| 63 | 
             
            @asynccontextmanager
         | 
| 64 | 
             
            async def lifespan(app: FastAPI):
         | 
| 65 | 
            -
                """Application lifespan management"""
         | 
| 66 | 
             
                try:
         | 
| 67 | 
             
                    await load_models()
         | 
| 68 | 
            -
                     | 
| 69 | 
             
                except Exception as e:
         | 
| 70 | 
             
                    logger.error(f"❌ Error during startup: {str(e)}", exc_info=True)
         | 
| 71 | 
            -
                    raise
         | 
| 72 | 
            -
                 | 
| 73 | 
            -
             | 
| 74 |  | 
| 75 | 
             
            # Custom OpenAPI schema
         | 
| 76 | 
             
            def custom_openapi():
         | 
| @@ -594,21 +596,20 @@ async def validation_exception_handler(request, exc): | |
| 594 | 
             
                    }
         | 
| 595 | 
             
                )
         | 
| 596 |  | 
| 597 | 
            -
            #  | 
| 598 | 
             
            # STARTUP MESSAGE
         | 
| 599 | 
            -
            #  | 
| 600 |  | 
| 601 | 
            -
             | 
| 602 | 
            -
             | 
| 603 | 
            -
             | 
| 604 | 
            -
             | 
| 605 | 
            -
             | 
| 606 | 
            -
             | 
| 607 | 
            -
             | 
| 608 | 
            -
             | 
| 609 | 
            -
             | 
| 610 | 
            -
             | 
| 611 | 
            -
             | 
| 612 | 
            -
             | 
| 613 | 
            -
             | 
| 614 | 
            -
                )
         | 
|  | |
| 2 | 
             
            """
         | 
| 3 | 
             
            Medical AI Assistant - FastAPI Only Version
         | 
| 4 | 
             
            Simplified endpoints for backend integration with Swagger UI
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            This file is Hugging Face Spaces compatible: the FastAPI app is exposed as 'app' at the module level.
         | 
| 7 | 
             
            """
         | 
| 8 |  | 
| 9 | 
             
            from fastapi import FastAPI, HTTPException, File, UploadFile, BackgroundTasks
         | 
|  | |
| 64 |  | 
| 65 | 
             
            @asynccontextmanager
         | 
| 66 | 
             
            async def lifespan(app: FastAPI):
         | 
| 67 | 
            +
                """Application lifespan management (robust for Hugging Face Spaces)"""
         | 
| 68 | 
             
                try:
         | 
| 69 | 
             
                    await load_models()
         | 
| 70 | 
            +
                    logger.info("✅ Models loaded in lifespan.")
         | 
| 71 | 
             
                except Exception as e:
         | 
| 72 | 
             
                    logger.error(f"❌ Error during startup: {str(e)}", exc_info=True)
         | 
| 73 | 
            +
                    # Do not raise, just log. App will start but endpoints will return 503 if models are missing.
         | 
| 74 | 
            +
                yield
         | 
| 75 | 
            +
                logger.info("🔄 Shutting down...")
         | 
| 76 |  | 
| 77 | 
             
            # Custom OpenAPI schema
         | 
| 78 | 
             
            def custom_openapi():
         | 
|  | |
| 596 | 
             
                    }
         | 
| 597 | 
             
                )
         | 
| 598 |  | 
| 599 | 
            +
            # =========================================================================
         | 
| 600 | 
             
            # STARTUP MESSAGE
         | 
| 601 | 
            +
            # =========================================================================
         | 
| 602 |  | 
| 603 | 
            +
            # The following block is removed for Hugging Face Spaces compatibility:
         | 
| 604 | 
            +
            # if __name__ == "__main__":
         | 
| 605 | 
            +
            #     import uvicorn
         | 
| 606 | 
            +
            #     print("🩺 Starting Medical AI Assistant API...")
         | 
| 607 | 
            +
            #     print("📚 Documentation available at: http://localhost:8000/docs")
         | 
| 608 | 
            +
            #     print("🔄 Alternative docs at: http://localhost:8000/redoc")
         | 
| 609 | 
            +
            #     uvicorn.run(
         | 
| 610 | 
            +
            #         app,
         | 
| 611 | 
            +
            #         host="0.0.0.0",
         | 
| 612 | 
            +
            #         port=8000,
         | 
| 613 | 
            +
            #         log_level="info",
         | 
| 614 | 
            +
            #         reload=False
         | 
| 615 | 
            +
            #     )
         | 
|  | 
    	
        requirements.txt
    CHANGED
    
    | @@ -36,4 +36,7 @@ pytest==7.4.3 | |
| 36 | 
             
            pytest-asyncio==0.21.1
         | 
| 37 |  | 
| 38 | 
             
            # Optional: For production deployment
         | 
| 39 | 
            -
            gunicorn==21.2.0
         | 
|  | |
|  | |
|  | 
|  | |
| 36 | 
             
            pytest-asyncio==0.21.1
         | 
| 37 |  | 
| 38 | 
             
            # Optional: For production deployment
         | 
| 39 | 
            +
            gunicorn==21.2.0
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            noisereduce==3.0.1
         | 
| 42 | 
            +
            pydub==0.25.1
         |