File size: 10,009 Bytes
61d360d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
import os
import subprocess
import sys
import time
import webbrowser
import requests
import signal
import psutil

def main():
    """Run the Debluring Application by starting both API and Streamlit app"""
    # Change to the directory of this script
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    
    print("\n🚀 Starting Debluring Application...\n")
    
    # Define URLs
    api_url = "http://localhost:8001"
    streamlit_url = "http://localhost:8501"
    
    # Function to start or restart the API server
    def start_api_server():
        nonlocal api_process
        # Kill any existing uvicorn processes that might be hanging
        for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
            try:
                if 'uvicorn' in str(proc.info['cmdline']).lower() and '8001' in str(proc.info['cmdline']):
                    print(f"Killing existing uvicorn process (PID: {proc.info['pid']})")
                    psutil.Process(proc.info['pid']).kill()
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                pass
                
        print("Starting API server...")
        return subprocess.Popen(
            [sys.executable, "api.py"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            bufsize=1,
            universal_newlines=True
        )
    
    # Function to check API health
    def check_api_health(timeout=2):
        try:
            response = requests.get(f"{api_url}/status", timeout=timeout)
            return response.status_code == 200
        except:
            return False
    
    # Start the API server
    api_process = start_api_server()
    
    # Wait for API to actually be available
    print("Waiting for API to start", end="")
    api_ready = False
    for _ in range(15):  # Try for 15 seconds
        if check_api_health():
            api_ready = True
            print("\n✅ API server is running")
            break
        print(".", end="", flush=True)
        time.sleep(1)
        
    if not api_ready:
        print("\n⚠️ API server might not be fully ready, but continuing anyway")
    
    # Function to start or restart the Streamlit app
    def start_streamlit_app():
        nonlocal streamlit_process
        # Kill any existing streamlit processes that might be hanging
        for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
            try:
                if 'streamlit' in str(proc.info['cmdline']).lower() and 'run' in str(proc.info['cmdline']):
                    print(f"Killing existing streamlit process (PID: {proc.info['pid']})")
                    psutil.Process(proc.info['pid']).kill()
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                pass
                
        print("Starting Streamlit web interface...")
        return subprocess.Popen(
            [sys.executable, "-m", "streamlit", "run", "app.py"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            bufsize=1,
            universal_newlines=True
        )
    
    # Start the Streamlit app
    streamlit_process = start_streamlit_app()
    
    # Give Streamlit a moment to start
    time.sleep(3)
    print("✅ Streamlit web interface started")
    
    # Open web browser
    try:
        print("Opening web interface in your browser...")
        webbrowser.open(streamlit_url)
    except Exception as e:
        print(f"Failed to open browser. Please open this URL manually: {streamlit_url}")
    
    # Print URLs
    print("\n📋 Application URLs:")
    print(f"   - Web Interface: {streamlit_url}")
    print(f"   - API: {api_url}")
    
    # Set up a more graceful exit handler
    def handle_exit(signum, frame):
        print("\n👋 Shutting down gracefully...")
        shutdown_services(api_process, streamlit_process)
        sys.exit(0)
        
    signal.signal(signal.SIGINT, handle_exit)
    
    print("\n⌨️  Press Ctrl+C to stop the application...")
    print("📊 Monitoring services for stability...\n")
    
    # Track request counts to detect stalled processes
    request_count = 0
    last_check_time = time.time()
    consecutive_failures = 0
    
    try:
        # Keep the script running until interrupted
        while True:
            # Check for crashed processes
            api_status = api_process.poll()
            streamlit_status = streamlit_process.poll()
            
            if api_status is not None:
                print(f"⚠️ API server stopped unexpectedly (exit code: {api_status})")
                # Restart the API server
                api_process = start_api_server()
                time.sleep(5)  # Give it time to start
                
            if streamlit_status is not None:
                print(f"⚠️ Streamlit app stopped unexpectedly (exit code: {streamlit_status})")
                # Restart Streamlit
                streamlit_process = start_streamlit_app()
                time.sleep(5)  # Give it time to start
            
            # More frequent health checks (every 10 seconds)
            if time.time() - last_check_time > 10:
                request_count += 1
                # Check API health
                if check_api_health():
                    print(f"✅ Health check #{request_count}: API is responsive")
                    consecutive_failures = 0  # Reset failure counter on success
                else:
                    consecutive_failures += 1
                    print(f"⚠️ Health check #{request_count}: API not responding (failure #{consecutive_failures})")
                    
                    # If we have 3 consecutive failures, restart the API
                    if consecutive_failures >= 3:
                        print("❌ API has been unresponsive for too long. Restarting...")
                        # Force terminate the API process
                        if api_process and api_process.poll() is None:
                            api_process.terminate()
                            time.sleep(1)
                            if api_process.poll() is None:
                                if sys.platform != 'win32':
                                    os.kill(api_process.pid, signal.SIGKILL)
                                else:
                                    subprocess.call(['taskkill', '/F', '/T', '/PID', str(api_process.pid)])
                        
                        # Start a new API process
                        api_process = start_api_server()
                        time.sleep(5)  # Give it time to start
                        consecutive_failures = 0  # Reset failure counter
                
                last_check_time = time.time()
            
            # Check memory usage of processes
            try:
                api_proc = psutil.Process(api_process.pid)
                api_memory = api_proc.memory_info().rss / (1024 * 1024)  # Convert to MB
                
                # If API is using too much memory (>500MB), restart it
                if api_memory > 500:
                    print(f"⚠️ API server using excessive memory: {api_memory:.2f} MB. Restarting...")
                    # Restart the API
                    if api_process and api_process.poll() is None:
                        api_process.terminate()
                        time.sleep(1)
                    api_process = start_api_server()
                    time.sleep(5)
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                pass
                
            # Sleep for a bit before checking again
            time.sleep(2)
            
    except KeyboardInterrupt:
        # Handle termination via Ctrl+C
        print("\n👋 Shutting down...")
    except Exception as e:
        # Handle any other exceptions
        print(f"\n❌ Error: {str(e)}")
    finally:
        shutdown_services(api_process, streamlit_process)

def shutdown_services(api_process, streamlit_process):
    """Safely shut down all running services"""
    # Clean up processes
    if api_process and api_process.poll() is None:
        print("Stopping API server...")
        try:
            api_process.terminate()
            # Give it a moment to terminate gracefully
            time.sleep(1)
            # Force kill if still running
            if api_process.poll() is None:
                if sys.platform != 'win32':
                    os.kill(api_process.pid, signal.SIGKILL)
                else:
                    subprocess.call(['taskkill', '/F', '/T', '/PID', str(api_process.pid)])
        except:
            print("Could not terminate API server gracefully")
    
    if streamlit_process and streamlit_process.poll() is None:
        print("Stopping Streamlit app...")
        try:
            streamlit_process.terminate()
            # Give it a moment to terminate gracefully
            time.sleep(1)
            # Force kill if still running
            if streamlit_process.poll() is None:
                if sys.platform != 'win32':
                    os.kill(streamlit_process.pid, signal.SIGKILL)
                else:
                    subprocess.call(['taskkill', '/F', '/T', '/PID', str(streamlit_process.pid)])
        except:
            print("Could not terminate Streamlit app gracefully")
    
    # Also kill any related processes that might still be hanging
    for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
        try:
            cmdline = str(proc.info['cmdline']).lower()
            if ('uvicorn' in cmdline and '8001' in cmdline) or ('streamlit' in cmdline and 'run' in cmdline):
                print(f"Killing leftover process: {proc.info['pid']}")
                psutil.Process(proc.info['pid']).kill()
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
            pass
    
    print("\n✅ Application stopped")

if __name__ == "__main__":
    main()