yukee1992 commited on
Commit
b02bbf6
ยท
verified ยท
1 Parent(s): 57cc4ee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +281 -5
app.py CHANGED
@@ -78,6 +78,22 @@ class JobStatusResponse(BaseModel):
78
  created_at: float
79
  updated_at: float
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  # HIGH-QUALITY MODEL SELECTION - ANIME FOCUSED & WORKING
82
  MODEL_CHOICES = {
83
  "dreamshaper-8": "lykon/dreamshaper-8", # Great all-rounder
@@ -97,6 +113,103 @@ current_model_name = None
97
  current_pipe = None
98
  model_lock = threading.Lock()
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  def load_model(model_name="dreamshaper-8"):
101
  """Thread-safe model loading with HIGH-QUALITY settings and better error handling"""
102
  global model_cache, current_model_name, current_pipe
@@ -732,6 +845,62 @@ async def api_health():
732
  "oci_api_connected": OCI_API_BASE_URL
733
  }
734
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
735
  @app.get("/api/local-images")
736
  async def get_local_images():
737
  """API endpoint to get locally saved test images"""
@@ -793,12 +962,30 @@ def create_gradio_interface():
793
  lines=2
794
  )
795
 
 
 
 
 
 
 
 
796
  def update_storage_info():
797
  info = get_local_storage_info()
798
  if "error" not in info:
799
  return f"๐Ÿ“ Local Storage: {info['total_files']} images, {info['total_size_mb']} MB used"
800
  return "๐Ÿ“ Local Storage: Unable to calculate"
801
 
 
 
 
 
 
 
 
 
 
 
 
802
  with gr.Row():
803
  with gr.Column(scale=1):
804
  gr.Markdown("### ๐ŸŽฏ Quality Settings")
@@ -828,12 +1015,25 @@ def create_gradio_interface():
828
  delete_btn = gr.Button("๐Ÿ—‘๏ธ Delete This Image", variant="stop")
829
  delete_status = gr.Textbox(label="Delete Status", interactive=False, lines=2)
830
 
 
 
 
 
 
 
 
 
831
  gr.Markdown("### ๐Ÿ“š API Usage for n8n")
832
  gr.Markdown("""
833
  **For complete storybooks (OCI bucket):**
834
  - Endpoint: `POST /api/generate-storybook`
835
  - Input: `story_title`, `scenes[]`, `characters[]`
836
  - Output: Uses pure prompts only from your script
 
 
 
 
 
837
  """)
838
 
839
  with gr.Column(scale=2):
@@ -888,6 +1088,32 @@ def create_gradio_interface():
888
  return f"โœ… Deleted {deleted_count} images", updated_files
889
  except Exception as e:
890
  return f"โŒ Error: {str(e)}", refresh_local_images()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
891
 
892
  # Connect buttons to functions
893
  generate_btn.click(
@@ -900,6 +1126,9 @@ def create_gradio_interface():
900
  ).then(
901
  fn=update_storage_info,
902
  outputs=storage_info
 
 
 
903
  )
904
 
905
  delete_btn.click(
@@ -909,6 +1138,9 @@ def create_gradio_interface():
909
  ).then(
910
  fn=update_storage_info,
911
  outputs=storage_info
 
 
 
912
  )
913
 
914
  refresh_btn.click(
@@ -917,6 +1149,9 @@ def create_gradio_interface():
917
  ).then(
918
  fn=update_storage_info,
919
  outputs=storage_info
 
 
 
920
  )
921
 
922
  clear_all_btn.click(
@@ -925,11 +1160,32 @@ def create_gradio_interface():
925
  ).then(
926
  fn=update_storage_info,
927
  outputs=storage_info
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
928
  )
929
 
930
  # Initialize on load
931
  demo.load(fn=refresh_local_images, outputs=file_gallery)
932
  demo.load(fn=update_storage_info, outputs=storage_info)
 
933
 
934
  return demo
935
 
@@ -945,11 +1201,15 @@ async def root():
945
  "health_check": "GET /api/health",
946
  "generate_storybook": "POST /api/generate-storybook",
947
  "check_job_status": "GET /api/job-status/{job_id}",
948
- "local_images": "GET /api/local-images"
 
 
 
949
  },
950
  "features": {
951
  "pure_prompts": "โœ… Enabled - No automatic enhancements",
952
- "n8n_integration": "โœ… Enabled"
 
953
  },
954
  "web_interface": "GET /ui"
955
  }
@@ -961,6 +1221,7 @@ async def test_endpoint():
961
  "status": "success",
962
  "message": "API with pure prompts is working correctly",
963
  "pure_prompts": "โœ… Enabled - Using exact prompts from Telegram",
 
964
  "timestamp": datetime.now().isoformat()
965
  }
966
 
@@ -980,6 +1241,7 @@ if __name__ == "__main__":
980
  print("๐Ÿ“š API endpoints available at: /api/*")
981
  print("๐ŸŽจ Web interface available at: /ui")
982
  print("๐Ÿ“ PURE PROMPTS enabled - no automatic enhancements")
 
983
 
984
  # Mount Gradio without reassigning app
985
  gr.mount_gradio_app(app, demo, path="/ui")
@@ -997,6 +1259,7 @@ if __name__ == "__main__":
997
  print("๐Ÿ“š API endpoints: http://localhost:8000/api/*")
998
  print("๐ŸŽจ Web interface: http://localhost:7860/ui")
999
  print("๐Ÿ“ PURE PROMPTS enabled - no automatic enhancements")
 
1000
 
1001
  def run_fastapi():
1002
  """Run FastAPI on port 8000 for API calls"""
@@ -1010,6 +1273,19 @@ if __name__ == "__main__":
1010
 
1011
  def run_gradio():
1012
  """Run Gradio on port 7860 for web interface"""
1013
- demo.launch(
1014
- server_name="0.0.0.0",
1015
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  created_at: float
79
  updated_at: float
80
 
81
+ class MemoryClearanceRequest(BaseModel):
82
+ clear_models: bool = True
83
+ clear_jobs: bool = False
84
+ clear_local_images: bool = False
85
+ force_gc: bool = True
86
+
87
+ class MemoryStatusResponse(BaseModel):
88
+ memory_used_mb: float
89
+ memory_percent: float
90
+ models_loaded: int
91
+ active_jobs: int
92
+ local_images_count: int
93
+ gpu_memory_allocated_mb: Optional[float] = None
94
+ gpu_memory_cached_mb: Optional[float] = None
95
+ status: str
96
+
97
  # HIGH-QUALITY MODEL SELECTION - ANIME FOCUSED & WORKING
98
  MODEL_CHOICES = {
99
  "dreamshaper-8": "lykon/dreamshaper-8", # Great all-rounder
 
113
  current_pipe = None
114
  model_lock = threading.Lock()
115
 
116
+ # MEMORY MANAGEMENT FUNCTIONS
117
+ def get_memory_usage():
118
+ """Get current memory usage statistics"""
119
+ process = psutil.Process()
120
+ memory_info = process.memory_info()
121
+ memory_used_mb = memory_info.rss / (1024 * 1024)
122
+ memory_percent = process.memory_percent()
123
+
124
+ # GPU memory if available
125
+ gpu_memory_allocated_mb = None
126
+ gpu_memory_cached_mb = None
127
+
128
+ if torch.cuda.is_available():
129
+ gpu_memory_allocated_mb = torch.cuda.memory_allocated() / (1024 * 1024)
130
+ gpu_memory_cached_mb = torch.cuda.memory_reserved() / (1024 * 1024)
131
+
132
+ return {
133
+ "memory_used_mb": round(memory_used_mb, 2),
134
+ "memory_percent": round(memory_percent, 2),
135
+ "gpu_memory_allocated_mb": round(gpu_memory_allocated_mb, 2) if gpu_memory_allocated_mb else None,
136
+ "gpu_memory_cached_mb": round(gpu_memory_cached_mb, 2) if gpu_memory_cached_mb else None,
137
+ "models_loaded": len(model_cache),
138
+ "active_jobs": len(job_storage),
139
+ "local_images_count": len(refresh_local_images())
140
+ }
141
+
142
+ def clear_memory(clear_models=True, clear_jobs=False, clear_local_images=False, force_gc=True):
143
+ """Clear memory by unloading models and cleaning up resources"""
144
+ results = []
145
+
146
+ # Clear model cache
147
+ if clear_models:
148
+ with model_lock:
149
+ models_cleared = len(model_cache)
150
+ for model_name, pipe in model_cache.items():
151
+ try:
152
+ # Move to CPU first if it's on GPU
153
+ if hasattr(pipe, 'to'):
154
+ pipe.to('cpu')
155
+
156
+ # Delete the pipeline
157
+ del pipe
158
+ results.append(f"Unloaded model: {model_name}")
159
+ except Exception as e:
160
+ results.append(f"Error unloading {model_name}: {str(e)}")
161
+
162
+ model_cache.clear()
163
+ global current_pipe, current_model_name
164
+ current_pipe = None
165
+ current_model_name = None
166
+ results.append(f"Cleared {models_cleared} models from cache")
167
+
168
+ # Clear completed jobs
169
+ if clear_jobs:
170
+ jobs_to_clear = []
171
+ for job_id, job_data in job_storage.items():
172
+ if job_data["status"] in [JobStatus.COMPLETED, JobStatus.FAILED]:
173
+ jobs_to_clear.append(job_id)
174
+
175
+ for job_id in jobs_to_clear:
176
+ del job_storage[job_id]
177
+ results.append(f"Cleared job: {job_id}")
178
+
179
+ results.append(f"Cleared {len(jobs_to_clear)} completed/failed jobs")
180
+
181
+ # Clear local images
182
+ if clear_local_images:
183
+ try:
184
+ storage_info = get_local_storage_info()
185
+ deleted_count = 0
186
+ if "images" in storage_info:
187
+ for image_info in storage_info["images"]:
188
+ success, _ = delete_local_image(image_info["path"])
189
+ if success:
190
+ deleted_count += 1
191
+ results.append(f"Deleted {deleted_count} local images")
192
+ except Exception as e:
193
+ results.append(f"Error clearing local images: {str(e)}")
194
+
195
+ # Force garbage collection
196
+ if force_gc:
197
+ gc.collect()
198
+ if torch.cuda.is_available():
199
+ torch.cuda.empty_cache()
200
+ torch.cuda.synchronize()
201
+ results.append("GPU cache cleared")
202
+ results.append("Garbage collection forced")
203
+
204
+ # Get memory status after cleanup
205
+ memory_status = get_memory_usage()
206
+
207
+ return {
208
+ "status": "success",
209
+ "actions_performed": results,
210
+ "memory_after_cleanup": memory_status
211
+ }
212
+
213
  def load_model(model_name="dreamshaper-8"):
214
  """Thread-safe model loading with HIGH-QUALITY settings and better error handling"""
215
  global model_cache, current_model_name, current_pipe
 
845
  "oci_api_connected": OCI_API_BASE_URL
846
  }
847
 
848
+ # NEW MEMORY MANAGEMENT ENDPOINTS
849
+ @app.get("/api/memory-status")
850
+ async def get_memory_status():
851
+ """Get current memory usage and system status"""
852
+ memory_info = get_memory_usage()
853
+ return MemoryStatusResponse(
854
+ memory_used_mb=memory_info["memory_used_mb"],
855
+ memory_percent=memory_info["memory_percent"],
856
+ models_loaded=memory_info["models_loaded"],
857
+ active_jobs=memory_info["active_jobs"],
858
+ local_images_count=memory_info["local_images_count"],
859
+ gpu_memory_allocated_mb=memory_info["gpu_memory_allocated_mb"],
860
+ gpu_memory_cached_mb=memory_info["gpu_memory_cached_mb"],
861
+ status="healthy"
862
+ )
863
+
864
+ @app.post("/api/clear-memory")
865
+ async def clear_memory_endpoint(request: MemoryClearanceRequest):
866
+ """Clear memory by unloading models and cleaning up resources"""
867
+ try:
868
+ result = clear_memory(
869
+ clear_models=request.clear_models,
870
+ clear_jobs=request.clear_jobs,
871
+ clear_local_images=request.clear_local_images,
872
+ force_gc=request.force_gc
873
+ )
874
+
875
+ return {
876
+ "status": "success",
877
+ "message": "Memory clearance completed",
878
+ "details": result
879
+ }
880
+
881
+ except Exception as e:
882
+ raise HTTPException(status_code=500, detail=f"Memory clearance failed: {str(e)}")
883
+
884
+ @app.post("/api/auto-cleanup")
885
+ async def auto_cleanup():
886
+ """Automatic cleanup - clears completed jobs and forces GC"""
887
+ try:
888
+ result = clear_memory(
889
+ clear_models=False, # Don't clear models by default
890
+ clear_jobs=True, # Clear completed jobs
891
+ clear_local_images=False, # Don't clear images by default
892
+ force_gc=True # Force garbage collection
893
+ )
894
+
895
+ return {
896
+ "status": "success",
897
+ "message": "Automatic cleanup completed",
898
+ "details": result
899
+ }
900
+
901
+ except Exception as e:
902
+ raise HTTPException(status_code=500, detail=f"Auto cleanup failed: {str(e)}")
903
+
904
  @app.get("/api/local-images")
905
  async def get_local_images():
906
  """API endpoint to get locally saved test images"""
 
962
  lines=2
963
  )
964
 
965
+ # Memory status display
966
+ memory_status = gr.Textbox(
967
+ label="๐Ÿง  Memory Status",
968
+ interactive=False,
969
+ lines=3
970
+ )
971
+
972
  def update_storage_info():
973
  info = get_local_storage_info()
974
  if "error" not in info:
975
  return f"๐Ÿ“ Local Storage: {info['total_files']} images, {info['total_size_mb']} MB used"
976
  return "๐Ÿ“ Local Storage: Unable to calculate"
977
 
978
+ def update_memory_status():
979
+ memory_info = get_memory_usage()
980
+ status_text = f"๐Ÿง  Memory Usage: {memory_info['memory_used_mb']} MB ({memory_info['memory_percent']}%)\n"
981
+ status_text += f"๐Ÿ“ฆ Models Loaded: {memory_info['models_loaded']}\n"
982
+ status_text += f"โšก Active Jobs: {memory_info['active_jobs']}"
983
+
984
+ if memory_info['gpu_memory_allocated_mb']:
985
+ status_text += f"\n๐ŸŽฎ GPU Memory: {memory_info['gpu_memory_allocated_mb']} MB allocated"
986
+
987
+ return status_text
988
+
989
  with gr.Row():
990
  with gr.Column(scale=1):
991
  gr.Markdown("### ๐ŸŽฏ Quality Settings")
 
1015
  delete_btn = gr.Button("๐Ÿ—‘๏ธ Delete This Image", variant="stop")
1016
  delete_status = gr.Textbox(label="Delete Status", interactive=False, lines=2)
1017
 
1018
+ # Memory management section
1019
+ gr.Markdown("### ๐Ÿง  Memory Management")
1020
+ with gr.Row():
1021
+ auto_cleanup_btn = gr.Button("๐Ÿ”„ Auto Cleanup", size="sm")
1022
+ clear_models_btn = gr.Button("๐Ÿ—‘๏ธ Clear Models", variant="stop", size="sm")
1023
+
1024
+ memory_clear_status = gr.Textbox(label="Memory Clear Status", interactive=False, lines=2)
1025
+
1026
  gr.Markdown("### ๐Ÿ“š API Usage for n8n")
1027
  gr.Markdown("""
1028
  **For complete storybooks (OCI bucket):**
1029
  - Endpoint: `POST /api/generate-storybook`
1030
  - Input: `story_title`, `scenes[]`, `characters[]`
1031
  - Output: Uses pure prompts only from your script
1032
+
1033
+ **Memory Management APIs:**
1034
+ - `GET /api/memory-status` - Check memory usage
1035
+ - `POST /api/clear-memory` - Clear memory
1036
+ - `POST /api/auto-cleanup` - Auto cleanup jobs
1037
  """)
1038
 
1039
  with gr.Column(scale=2):
 
1088
  return f"โœ… Deleted {deleted_count} images", updated_files
1089
  except Exception as e:
1090
  return f"โŒ Error: {str(e)}", refresh_local_images()
1091
+
1092
+ def perform_auto_cleanup():
1093
+ """Perform automatic cleanup"""
1094
+ try:
1095
+ result = clear_memory(
1096
+ clear_models=False,
1097
+ clear_jobs=True,
1098
+ clear_local_images=False,
1099
+ force_gc=True
1100
+ )
1101
+ return f"โœ… Auto cleanup completed: {len(result['actions_performed'])} actions"
1102
+ except Exception as e:
1103
+ return f"โŒ Auto cleanup failed: {str(e)}"
1104
+
1105
+ def clear_models():
1106
+ """Clear all loaded models"""
1107
+ try:
1108
+ result = clear_memory(
1109
+ clear_models=True,
1110
+ clear_jobs=False,
1111
+ clear_local_images=False,
1112
+ force_gc=True
1113
+ )
1114
+ return f"โœ… Models cleared: {len(result['actions_performed'])} actions"
1115
+ except Exception as e:
1116
+ return f"โŒ Model clearance failed: {str(e)}"
1117
 
1118
  # Connect buttons to functions
1119
  generate_btn.click(
 
1126
  ).then(
1127
  fn=update_storage_info,
1128
  outputs=storage_info
1129
+ ).then(
1130
+ fn=update_memory_status,
1131
+ outputs=memory_status
1132
  )
1133
 
1134
  delete_btn.click(
 
1138
  ).then(
1139
  fn=update_storage_info,
1140
  outputs=storage_info
1141
+ ).then(
1142
+ fn=update_memory_status,
1143
+ outputs=memory_status
1144
  )
1145
 
1146
  refresh_btn.click(
 
1149
  ).then(
1150
  fn=update_storage_info,
1151
  outputs=storage_info
1152
+ ).then(
1153
+ fn=update_memory_status,
1154
+ outputs=memory_status
1155
  )
1156
 
1157
  clear_all_btn.click(
 
1160
  ).then(
1161
  fn=update_storage_info,
1162
  outputs=storage_info
1163
+ ).then(
1164
+ fn=update_memory_status,
1165
+ outputs=memory_status
1166
+ )
1167
+
1168
+ # Memory management buttons
1169
+ auto_cleanup_btn.click(
1170
+ fn=perform_auto_cleanup,
1171
+ outputs=memory_clear_status
1172
+ ).then(
1173
+ fn=update_memory_status,
1174
+ outputs=memory_status
1175
+ )
1176
+
1177
+ clear_models_btn.click(
1178
+ fn=clear_models,
1179
+ outputs=memory_clear_status
1180
+ ).then(
1181
+ fn=update_memory_status,
1182
+ outputs=memory_status
1183
  )
1184
 
1185
  # Initialize on load
1186
  demo.load(fn=refresh_local_images, outputs=file_gallery)
1187
  demo.load(fn=update_storage_info, outputs=storage_info)
1188
+ demo.load(fn=update_memory_status, outputs=memory_status)
1189
 
1190
  return demo
1191
 
 
1201
  "health_check": "GET /api/health",
1202
  "generate_storybook": "POST /api/generate-storybook",
1203
  "check_job_status": "GET /api/job-status/{job_id}",
1204
+ "local_images": "GET /api/local-images",
1205
+ "memory_status": "GET /api/memory-status",
1206
+ "clear_memory": "POST /api/clear-memory",
1207
+ "auto_cleanup": "POST /api/auto-cleanup"
1208
  },
1209
  "features": {
1210
  "pure_prompts": "โœ… Enabled - No automatic enhancements",
1211
+ "n8n_integration": "โœ… Enabled",
1212
+ "memory_management": "โœ… Enabled"
1213
  },
1214
  "web_interface": "GET /ui"
1215
  }
 
1221
  "status": "success",
1222
  "message": "API with pure prompts is working correctly",
1223
  "pure_prompts": "โœ… Enabled - Using exact prompts from Telegram",
1224
+ "memory_management": "โœ… Enabled - Memory clearance available",
1225
  "timestamp": datetime.now().isoformat()
1226
  }
1227
 
 
1241
  print("๐Ÿ“š API endpoints available at: /api/*")
1242
  print("๐ŸŽจ Web interface available at: /ui")
1243
  print("๐Ÿ“ PURE PROMPTS enabled - no automatic enhancements")
1244
+ print("๐Ÿง  MEMORY MANAGEMENT enabled - automatic cleanup available")
1245
 
1246
  # Mount Gradio without reassigning app
1247
  gr.mount_gradio_app(app, demo, path="/ui")
 
1259
  print("๐Ÿ“š API endpoints: http://localhost:8000/api/*")
1260
  print("๐ŸŽจ Web interface: http://localhost:7860/ui")
1261
  print("๐Ÿ“ PURE PROMPTS enabled - no automatic enhancements")
1262
+ print("๐Ÿง  MEMORY MANAGEMENT enabled - automatic cleanup available")
1263
 
1264
  def run_fastapi():
1265
  """Run FastAPI on port 8000 for API calls"""
 
1273
 
1274
  def run_gradio():
1275
  """Run Gradio on port 7860 for web interface"""
1276
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
1277
+
1278
+ # Run both servers in separate threads
1279
+ import threading
1280
+ fastapi_thread = threading.Thread(target=run_fastapi, daemon=True)
1281
+ gradio_thread = threading.Thread(target=run_gradio, daemon=True)
1282
+
1283
+ fastapi_thread.start()
1284
+ gradio_thread.start()
1285
+
1286
+ try:
1287
+ # Keep main thread alive
1288
+ while True:
1289
+ time.sleep(1)
1290
+ except KeyboardInterrupt:
1291
+ print("๐Ÿ›‘ Shutting down servers...")