File size: 5,007 Bytes
9b4e272
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
"""
Baby Cry Analysis - Flask API for Hugging Face Spaces
Exposes REST endpoint for audio-based baby cry classification.
Wrapped with Gradio to keep the Space alive.
"""

from flask import Flask, request, jsonify
import tempfile
import os
import logging
import threading

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Import inference module (model loads at import time)
from inference import analyze_cry, preload_model

# Preload model at startup
preload_model()

app = Flask(__name__)

# Maximum file size: 16MB
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024

# Allowed audio extensions
ALLOWED_EXTENSIONS = {'wav', 'mp3', 'ogg', 'flac', 'm4a', 'webm'}


def allowed_file(filename: str) -> bool:
    """Check if file extension is allowed."""
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route("/health", methods=["GET"])
def health():
    """Health check endpoint."""
    return jsonify({"status": "healthy", "service": "baby-cry-ai"})


@app.route("/analyze-cry", methods=["POST"])
def analyze():
    """
    Analyze baby cry audio file.
    
    Expects multipart/form-data with 'audio' file field.
    
    Returns:
        JSON with cry analysis results:
        - cry_detected: boolean
        - top_reason: string (hunger, belly_pain, tired, discomfort, burping)
        - scores: object with confidence scores per label
        - disclaimer: legal disclaimer string
    """
    # Validate request has audio file
    if "audio" not in request.files:
        return jsonify({
            "error": "No audio file provided",
            "message": "Please upload an audio file with key 'audio'"
        }), 400
    
    audio = request.files["audio"]
    
    # Validate filename exists
    if audio.filename == '':
        return jsonify({
            "error": "Empty filename",
            "message": "No file selected"
        }), 400
    
    # Validate file extension
    if not allowed_file(audio.filename):
        return jsonify({
            "error": "Invalid file type",
            "message": f"Allowed types: {', '.join(ALLOWED_EXTENSIONS)}"
        }), 400
    
    # Get original extension for temp file
    ext = audio.filename.rsplit('.', 1)[1].lower()
    
    try:
        # Save to temporary file for processing
        with tempfile.NamedTemporaryFile(delete=False, suffix=f".{ext}") as tmp:
            audio.save(tmp.name)
            temp_path = tmp.name
        
        logger.info(f"Processing audio file: {temp_path}")
        
        # Run inference
        result = analyze_cry(temp_path)
        
        # Add disclaimer
        result["disclaimer"] = "AI-generated suggestion, not a medical diagnosis. Please consult a healthcare professional for medical advice."
        
        logger.info(f"Analysis successful: {result['top_reason']}")
        return jsonify(result)
    
    except Exception as e:
        logger.error(f"Analysis failed: {str(e)}", exc_info=True)
        return jsonify({
            "error": "Analysis failed",
            "message": str(e)
        }), 500
    
    finally:
        # Clean up temp file
        if 'temp_path' in locals() and os.path.exists(temp_path):
            os.unlink(temp_path)


@app.errorhandler(413)
def too_large(e):
    """Handle file too large error."""
    return jsonify({
        "error": "File too large",
        "message": "Maximum file size is 16MB"
    }), 413


def run_flask():
    """Run Flask app in background thread."""
    app.run(host="0.0.0.0", port=7860, debug=False, use_reloader=False)


# Start Flask in background thread
flask_thread = threading.Thread(target=run_flask, daemon=True)
flask_thread.start()

# Gradio UI to keep the Space alive
import gradio as gr

with gr.Blocks() as demo:
    gr.Markdown("""
    # 👶 Baby Cry Analysis API
    
    This Space hosts a REST API for analyzing baby cries using machine learning.
    
    ## 🔗 API Endpoints
    
    ### Health Check
    ```
    GET /health
    ```
    
    ### Analyze Cry
    ```
    POST /analyze-cry
    Content-Type: multipart/form-data
    Body: audio=<file>
    ```
    
    **Supported formats:** WAV, MP3, OGG, FLAC, M4A, WebM
    
    ## 📡 Example Usage
    
    ```python
    import requests
    
    with open("baby_cry.wav", "rb") as f:
        response = requests.post(
            "https://YOUR-SPACE.hf.space/analyze-cry",
            files={"audio": f}
        )
    print(response.json())
    ```
    
    ## 📊 Response Format
    
    ```json
    {
      "cry_detected": true,
      "top_reason": "hunger",
      "scores": {
        "hunger": 0.45,
        "belly_pain": 0.20,
        "tired": 0.15,
        "discomfort": 0.12,
        "burping": 0.08
      },
      "disclaimer": "AI-generated suggestion..."
    }
    ```
    
    ---
    ⚠️ *This is an AI-generated suggestion, not a medical diagnosis.*
    """)

demo.launch(server_name="0.0.0.0", server_port=7861)