File size: 8,225 Bytes
4abf821
 
5be262b
4abf821
e92f1ba
4abf821
 
 
61e63b0
4abf821
 
61e63b0
d61c890
61e63b0
4abf821
 
 
 
5be262b
 
 
 
 
 
 
 
 
 
4abf821
2aa5eb6
f2b649d
5be262b
 
 
4abf821
5be262b
4abf821
5be262b
 
 
 
 
 
 
 
 
 
 
 
4abf821
93dd654
 
5be262b
93dd654
 
5be262b
93dd654
d61c890
fee972c
93dd654
 
fee972c
 
5be262b
 
d61c890
fee972c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d61c890
 
fee972c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d61c890
 
 
 
 
 
 
 
 
5be262b
d61c890
 
5be262b
 
 
 
 
 
 
d61c890
93dd654
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e0bd111
5be262b
 
e0bd111
93dd654
 
 
5be262b
93dd654
 
 
 
 
4abf821
 
5be262b
 
 
d61c890
5be262b
d61c890
5be262b
 
d61c890
5be262b
 
 
 
d61c890
5be262b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e92f1ba
5be262b
 
 
 
 
 
 
4abf821
 
5be262b
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import os
import logging
from flask import Flask, jsonify
from flask_cors import CORS
import whisper
from dotenv import load_dotenv
from .agents.text_extractor import TextExtractorAgent
from .agents.phi_scrubber import PHIScrubberAgent
from .agents.phi_scrubber import MedicalTextUtils
from .agents.summarizer import SummarizerAgent
from .agents.medical_data_extractor import MedicalDataExtractorAgent
from .agents.medical_data_extractor import MedicalDocDataExtractorAgent
import torch


# Load environment variables
load_dotenv()

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler('/tmp/app.log')
    ]
)

app = Flask(__name__)
CORS(app)

# Configure upload directory
UPLOAD_DIR = '/data/uploads'
os.makedirs(UPLOAD_DIR, exist_ok=True)
app.config['UPLOAD_FOLDER'] = UPLOAD_DIR
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024  # 100 MB max file size

# Set cache directories
CACHE_DIRS = {
    'HF_HOME': '/tmp/huggingface',
    'TRANSFORMERS_CACHE': '/tmp/huggingface',
    'XDG_CACHE_HOME': '/tmp',
    'TORCH_HOME': '/tmp/torch',
    'WHISPER_CACHE': '/tmp/whisper'
}

for env_var, path in CACHE_DIRS.items():
    os.environ[env_var] = path
    os.makedirs(path, exist_ok=True)

# Model loaders
class LazyModelLoader:
    def __init__(self, model_name, model_type, fallback_model=None):
        self.model_name = model_name
        self.model_type = model_type
        self.fallback_model = fallback_model
        self._model = None
        self._tokenizer = None
        self._pipeline = None

    def load(self):
        from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM, AutoModelForSeq2SeqLM
        if self._pipeline is None:
            try:
                logging.info(f"Loading {self.model_name}...")
                
                # Clear GPU memory
                import torch
                torch.cuda.empty_cache()
                
                # Load tokenizer with proper error handling
                try:
                    self._tokenizer = AutoTokenizer.from_pretrained(
                        self.model_name,
                        trust_remote_code=True,
                        cache_dir=os.environ.get('TRANSFORMERS_CACHE', '/tmp/huggingface')
                    )
                except Exception as e:
                    logging.error(f"Failed to load tokenizer: {str(e)}")
                    if self.fallback_model:
                        logging.info(f"Trying fallback model tokenizer: {self.fallback_model}")
                        self._tokenizer = AutoTokenizer.from_pretrained(
                            self.fallback_model,
                            trust_remote_code=True,
                            cache_dir=os.environ.get('TRANSFORMERS_CACHE', '/tmp/huggingface')
                        )
                    else:
                        raise
                
                # Load model with memory optimizations
                try:
                    if self.model_type == "text-generation":
                        self._model = AutoModelForCausalLM.from_pretrained(
                            self.model_name,
                            trust_remote_code=True,
                            device_map="auto",
                            low_cpu_mem_usage=True,
                            torch_dtype=torch.float16,
                            cache_dir=os.environ.get('TRANSFORMERS_CACHE', '/tmp/huggingface')
                        )
                    else:
                        self._model = AutoModelForSeq2SeqLM.from_pretrained(
                            self.model_name,
                            trust_remote_code=True,
                            device_map="auto",
                            low_cpu_mem_usage=True,
                            torch_dtype=torch.float16,
                            cache_dir=os.environ.get('TRANSFORMERS_CACHE', '/tmp/huggingface')
                        )
                except Exception as e:
                    logging.error(f"Failed to load model: {str(e)}")
                    if self.fallback_model:
                        logging.info(f"Trying fallback model: {self.fallback_model}")
                        self._model = AutoModelForSeq2SeqLM.from_pretrained(
                            self.fallback_model,
                            trust_remote_code=True,
                            device_map="auto",
                            low_cpu_mem_usage=True,
                            torch_dtype=torch.float16,
                            cache_dir=os.environ.get('TRANSFORMERS_CACHE', '/tmp/huggingface')
                        )
                    else:
                        raise
                
                # Create pipeline after model is loaded
                self._pipeline = pipeline(
                    task=self.model_type,
                    model=self._model,
                    tokenizer=self._tokenizer,
                    device_map="auto"
                )
                
                logging.info(f"Successfully loaded {self.model_name}")
                return self._pipeline
                
            except Exception as e:
                if self.fallback_model:
                    logging.warning(f"Failed to load {self.model_name}, falling back to {self.fallback_model}")
                    self.model_name = self.fallback_model
                    return self.load()
                logging.error(f"Failed to load {self.model_name}: {str(e)}", exc_info=True)
                raise
        return self._pipeline

class WhisperModelLoader:
    _instance = None
    
    def __init__(self):
        self._model = None
        
    @staticmethod
    def get_instance():
        if WhisperModelLoader._instance is None:
            WhisperModelLoader._instance = WhisperModelLoader()
        return WhisperModelLoader._instance
    
    def load(self):
        if self._model is None:
            try:
                logging.info("Loading Whisper model...")
                self._model = whisper.load_model(
                    "tiny",  # Using tiny model for better memory usage
                    download_root=os.environ.get('WHISPER_CACHE', '/tmp/whisper')
                )
                logging.info("Whisper model loaded successfully")
            except Exception as e:
                logging.error(f"Failed to load Whisper model: {str(e)}", exc_info=True)
                raise
        return self._model
        
    def transcribe(self, audio_path):
        model = self.load()
        return model.transcribe(audio_path)

# Initialize agents
try:
    # Use smaller models for Hugging Face Spaces
    medalpaca_model_loader = LazyModelLoader(
        "facebook/bart-base",  # Start with a smaller model
        "text-generation",
        fallback_model="facebook/bart-large-cnn"
    )
    summarization_model_loader = LazyModelLoader(
        "facebook/bart-base",
        "summarization",
        fallback_model="facebook/bart-large-cnn"
    )
    
    # Initialize agents with lazy loading
    text_extractor_agent = TextExtractorAgent()
    phi_scrubber_agent = PHIScrubberAgent()
    medical_data_extractor_agent = MedicalDataExtractorAgent(medalpaca_model_loader)
    summarizer_agent = SummarizerAgent(summarization_model_loader)
    
    # Pass all agents and models to routes
    agents = {
        "text_extractor": text_extractor_agent,
        "phi_scrubber": phi_scrubber_agent,
        "summarizer": summarizer_agent,
        "medical_data_extractor": medical_data_extractor_agent,
        "whisper_model": WhisperModelLoader.get_instance()
    }
    
    from .api.routes import register_routes
    register_routes(app, agents)
    
except Exception as e:
    logging.error(f"Failed to initialize application: {str(e)}", exc_info=True)
    raise

@app.errorhandler(Exception)
def handle_error(error):
    logging.error(f"Unhandled error: {str(error)}", exc_info=True)
    return jsonify({
        "error": str(error),
        "status": "error"
    }), 500

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=7860, debug=False)