yukee1992 commited on
Commit
4fb0aa7
Β·
verified Β·
1 Parent(s): d609359

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -41
app.py CHANGED
@@ -15,8 +15,10 @@ from pydantic import BaseModel
15
  import requests
16
 
17
  print("=" * 60)
18
- print("πŸš€ STARTING STATUS TRACKER (with Gradio wrapper)")
19
  print("=" * 60)
 
 
20
 
21
  # =============================================
22
  # CREATE FASTAPI APP
@@ -50,7 +52,7 @@ class RegisterRequest(BaseModel):
50
  class StatusUpdate(BaseModel):
51
  project_id: str
52
  service_type: str
53
- status: str
54
  file_urls: Optional[List[str]] = None
55
  error: Optional[str] = None
56
 
@@ -77,7 +79,8 @@ def load_service_configs():
77
  for config in configs:
78
  services_config[config["type"]] = ServiceConfig(**config)
79
  print(f"βœ… Loaded {len(configs)} service configurations")
80
- except:
 
81
  services_config["tts"] = ServiceConfig(name="TTS", type="tts", timeout_minutes=30)
82
  services_config["image"] = ServiceConfig(name="Image", type="image", timeout_minutes=30)
83
  print("βœ… Using default service configurations")
@@ -90,18 +93,37 @@ load_service_configs()
90
 
91
  @app.get("/health")
92
  async def health():
93
- return {"status": "healthy", "active_projects": len(projects), "timestamp": datetime.now().isoformat()}
 
 
 
 
94
 
95
  @app.get("/test")
96
  async def test():
97
- return {"message": "API is working!"}
98
 
99
  @app.post("/api/register")
100
  async def register_project(request: RegisterRequest):
101
  try:
102
  project_id = request.project_id or str(uuid.uuid4())[:8]
 
 
 
 
 
 
 
 
 
103
  projects[project_id] = Project(project_id, request.n8n_webhook, request.services)
104
- return {"status": "registered", "project_id": project_id, "webhook": request.n8n_webhook, "services": request.services}
 
 
 
 
 
 
105
  except Exception as e:
106
  return JSONResponse(status_code=500, content={"error": str(e)})
107
 
@@ -112,32 +134,52 @@ async def update_status(update: StatusUpdate):
112
  return JSONResponse(status_code=404, content={"error": "Project not found"})
113
 
114
  project = projects[update.project_id]
 
 
 
 
 
115
  project.services[update.service_type]["status"] = update.status
116
  if update.file_urls:
117
  project.services[update.service_type]["file_urls"] = update.file_urls
118
 
 
119
  all_completed = all(s["status"] == "completed" for s in project.services.values())
120
 
121
  if all_completed and not project.completed_at:
122
  project.completed_at = datetime.now()
 
 
123
  final_data = {
124
  "project_id": update.project_id,
125
  "status": "completed",
126
  "completed_at": project.completed_at.isoformat(),
127
  "services": {}
128
  }
 
129
  for s_type, s_data in project.services.items():
130
  final_data["services"][s_type] = {
131
  "file_urls": s_data.get("file_urls", []),
132
  "file_count": len(s_data.get("file_urls", []))
133
  }
134
 
 
135
  try:
136
- requests.post(project.n8n_webhook, json=final_data, timeout=10)
137
- except:
138
- pass
 
 
 
 
 
 
139
 
140
- return {"status": "updated", "all_completed": all_completed}
 
 
 
 
141
  except Exception as e:
142
  return JSONResponse(status_code=500, content={"error": str(e)})
143
 
@@ -145,6 +187,7 @@ async def update_status(update: StatusUpdate):
145
  async def get_status(project_id: str):
146
  if project_id not in projects:
147
  raise HTTPException(status_code=404, detail="Project not found")
 
148
  project = projects[project_id]
149
  return {
150
  "project_id": project_id,
@@ -158,7 +201,9 @@ async def get_status(project_id: str):
158
  async def root():
159
  return {
160
  "name": "Status Tracker API",
 
161
  "status": "running",
 
162
  "endpoints": {
163
  "health": "GET /health",
164
  "test": "GET /test",
@@ -166,47 +211,116 @@ async def root():
166
  "update": "POST /api/update",
167
  "status": "GET /api/status/{project_id}"
168
  },
169
- "active_projects": len(projects)
 
 
170
  }
171
 
172
  # =============================================
173
- # GRADIO WRAPPER (REQUIRED FOR HUGGING FACE)
174
  # =============================================
175
 
176
- def create_gradio_interface():
177
- with gr.Blocks(title="Status Tracker") as demo:
178
- gr.Markdown("# πŸ”„ Status Tracker API")
179
- gr.Markdown("This space provides API endpoints for tracking generation status across multiple services.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
- with gr.Row():
182
- with gr.Column():
183
- gr.Markdown("### πŸ“Š Current Status")
184
- project_count = gr.Number(label="Active Projects", value=len(projects))
185
- refresh_btn = gr.Button("Refresh")
186
-
187
- with gr.Column():
188
- gr.Markdown("### πŸ“š API Endpoints")
189
- gr.Markdown("""
190
- - `GET /health` - Health check
191
- - `GET /test` - Test endpoint
192
- - `POST /api/register` - Register project
193
- - `POST /api/update` - Update status
194
- - `GET /api/status/{{project_id}}` - Get project status
195
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
- refresh_btn.click(
198
- fn=lambda: len(projects),
199
- outputs=[project_count]
 
200
  )
201
 
202
- return demo
203
-
204
- # Create Gradio interface
205
- demo = create_gradio_interface()
 
 
 
 
206
 
207
- # Mount FastAPI app to Gradio
208
  app = gr.mount_gradio_app(app, demo, path="/")
209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  # =============================================
211
  # RUN
212
  # =============================================
@@ -214,9 +328,8 @@ if __name__ == "__main__":
214
  print("\n" + "=" * 60)
215
  print("βœ… Status Tracker ready!")
216
  print(f"πŸ“Š Active projects: {len(projects)}")
 
217
  print("🌐 Server running on port 7860")
218
  print("=" * 60)
219
 
220
- # Import uvicorn here to avoid circular imports
221
- import uvicorn
222
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
15
  import requests
16
 
17
  print("=" * 60)
18
+ print("πŸš€ STARTING STATUS TRACKER (Gradio 6.9.0)")
19
  print("=" * 60)
20
+ print(f"Python version: {sys.version}")
21
+ print(f"Gradio version: {gr.__version__}")
22
 
23
  # =============================================
24
  # CREATE FASTAPI APP
 
52
  class StatusUpdate(BaseModel):
53
  project_id: str
54
  service_type: str
55
+ status: str # 'processing', 'completed', 'failed'
56
  file_urls: Optional[List[str]] = None
57
  error: Optional[str] = None
58
 
 
79
  for config in configs:
80
  services_config[config["type"]] = ServiceConfig(**config)
81
  print(f"βœ… Loaded {len(configs)} service configurations")
82
+ except Exception as e:
83
+ print(f"⚠️ Using default configs: {e}")
84
  services_config["tts"] = ServiceConfig(name="TTS", type="tts", timeout_minutes=30)
85
  services_config["image"] = ServiceConfig(name="Image", type="image", timeout_minutes=30)
86
  print("βœ… Using default service configurations")
 
93
 
94
  @app.get("/health")
95
  async def health():
96
+ return {
97
+ "status": "healthy",
98
+ "active_projects": len(projects),
99
+ "timestamp": datetime.now().isoformat()
100
+ }
101
 
102
  @app.get("/test")
103
  async def test():
104
+ return {"message": "API is working!", "timestamp": datetime.now().isoformat()}
105
 
106
  @app.post("/api/register")
107
  async def register_project(request: RegisterRequest):
108
  try:
109
  project_id = request.project_id or str(uuid.uuid4())[:8]
110
+
111
+ # Validate services
112
+ for service in request.services:
113
+ if service not in services_config:
114
+ return JSONResponse(
115
+ status_code=400,
116
+ content={"error": f"Unknown service: {service}", "available": list(services_config.keys())}
117
+ )
118
+
119
  projects[project_id] = Project(project_id, request.n8n_webhook, request.services)
120
+
121
+ return {
122
+ "status": "registered",
123
+ "project_id": project_id,
124
+ "webhook": request.n8n_webhook,
125
+ "services": request.services
126
+ }
127
  except Exception as e:
128
  return JSONResponse(status_code=500, content={"error": str(e)})
129
 
 
134
  return JSONResponse(status_code=404, content={"error": "Project not found"})
135
 
136
  project = projects[update.project_id]
137
+
138
+ if update.service_type not in project.services:
139
+ return JSONResponse(status_code=400, content={"error": f"Service {update.service_type} not in project"})
140
+
141
+ # Update service status
142
  project.services[update.service_type]["status"] = update.status
143
  if update.file_urls:
144
  project.services[update.service_type]["file_urls"] = update.file_urls
145
 
146
+ # Check if all services are complete
147
  all_completed = all(s["status"] == "completed" for s in project.services.values())
148
 
149
  if all_completed and not project.completed_at:
150
  project.completed_at = datetime.now()
151
+
152
+ # Prepare final data
153
  final_data = {
154
  "project_id": update.project_id,
155
  "status": "completed",
156
  "completed_at": project.completed_at.isoformat(),
157
  "services": {}
158
  }
159
+
160
  for s_type, s_data in project.services.items():
161
  final_data["services"][s_type] = {
162
  "file_urls": s_data.get("file_urls", []),
163
  "file_count": len(s_data.get("file_urls", []))
164
  }
165
 
166
+ # Send webhook to n8n
167
  try:
168
+ response = requests.post(
169
+ project.n8n_webhook,
170
+ json=final_data,
171
+ timeout=10,
172
+ headers={"Content-Type": "application/json"}
173
+ )
174
+ print(f"πŸ“€ Webhook sent: {response.status_code}")
175
+ except Exception as e:
176
+ print(f"⚠️ Webhook failed: {e}")
177
 
178
+ return {
179
+ "status": "updated",
180
+ "all_completed": all_completed,
181
+ "project_id": update.project_id
182
+ }
183
  except Exception as e:
184
  return JSONResponse(status_code=500, content={"error": str(e)})
185
 
 
187
  async def get_status(project_id: str):
188
  if project_id not in projects:
189
  raise HTTPException(status_code=404, detail="Project not found")
190
+
191
  project = projects[project_id]
192
  return {
193
  "project_id": project_id,
 
201
  async def root():
202
  return {
203
  "name": "Status Tracker API",
204
+ "version": "1.0.0",
205
  "status": "running",
206
+ "gradio_version": gr.__version__,
207
  "endpoints": {
208
  "health": "GET /health",
209
  "test": "GET /test",
 
211
  "update": "POST /api/update",
212
  "status": "GET /api/status/{project_id}"
213
  },
214
+ "active_projects": len(projects),
215
+ "configured_services": list(services_config.keys()),
216
+ "timestamp": datetime.now().isoformat()
217
  }
218
 
219
  # =============================================
220
+ # GRADIO INTERFACE (for Gradio 6.9.0)
221
  # =============================================
222
 
223
+ # Simple function to get project count
224
+ def get_project_count():
225
+ return len(projects)
226
+
227
+ def get_service_list():
228
+ return ", ".join(services_config.keys())
229
+
230
+ # Create Gradio interface
231
+ with gr.Blocks(title="Status Tracker", theme=gr.themes.Soft()) as demo:
232
+ gr.Markdown("# πŸ”„ Status Tracker API")
233
+ gr.Markdown("Centralized service for tracking generation status across multiple Hugging Face Spaces.")
234
+
235
+ with gr.Row():
236
+ with gr.Column(scale=1):
237
+ gr.Markdown("### πŸ“Š Current Status")
238
+ project_count = gr.Number(label="Active Projects", value=get_project_count, every=5)
239
+ services_display = gr.Textbox(label="Configured Services", value=get_service_list, interactive=False)
240
+ refresh_btn = gr.Button("πŸ”„ Refresh")
241
 
242
+ with gr.Column(scale=2):
243
+ gr.Markdown("### πŸ“š API Documentation")
244
+ gr.Markdown("""
245
+ | Endpoint | Method | Description |
246
+ |----------|--------|-------------|
247
+ | `/health` | GET | Health check |
248
+ | `/test` | GET | Test endpoint |
249
+ | `/api/register` | POST | Register a new project |
250
+ | `/api/update` | POST | Update service status |
251
+ | `/api/status/{project_id}` | GET | Get project status |
252
+ """)
253
+
254
+ with gr.Row():
255
+ gr.Markdown("### πŸš€ Quick Test")
256
+
257
+ with gr.Column():
258
+ test_project_id = gr.Textbox(label="Project ID", placeholder="test-123")
259
+ test_service = gr.Dropdown(label="Service Type", choices=list(services_config.keys()), value="tts")
260
+ test_status = gr.Dropdown(label="Status", choices=["processing", "completed", "failed"], value="completed")
261
+ test_btn = gr.Button("Test Status Update")
262
+ test_output = gr.JSON(label="Response")
263
+
264
+ def test_update(project_id, service, status):
265
+ test_data = {
266
+ "project_id": project_id,
267
+ "service_type": service,
268
+ "status": status,
269
+ "file_urls": ["https://example.com/test.mp3"] if service == "tts" else ["https://example.com/test.png"]
270
+ }
271
+ try:
272
+ response = requests.post(
273
+ f"https://{os.environ.get('SPACE_ID', 'localhost')}.hf.space/api/update",
274
+ json=test_data,
275
+ timeout=5
276
+ )
277
+ return response.json()
278
+ except:
279
+ return {"error": "Could not send test update"}
280
 
281
+ test_btn.click(
282
+ fn=test_update,
283
+ inputs=[test_project_id, test_service, test_status],
284
+ outputs=[test_output]
285
  )
286
 
287
+ gr.Markdown("---")
288
+ gr.Markdown("### πŸ”§ Configuration")
289
+ gr.Markdown(f"**Services configured:** {', '.join(services_config.keys())}")
290
+
291
+ refresh_btn.click(
292
+ fn=lambda: (len(projects), ", ".join(services_config.keys())),
293
+ outputs=[project_count, services_display]
294
+ )
295
 
296
+ # Mount FastAPI app to Gradio (for Gradio 6.9.0)
297
  app = gr.mount_gradio_app(app, demo, path="/")
298
 
299
+ # =============================================
300
+ # BACKGROUND CLEANUP TASK
301
+ # =============================================
302
+
303
+ def cleanup_old_projects():
304
+ while True:
305
+ time.sleep(300) # Every 5 minutes
306
+ now = datetime.now()
307
+ to_delete = []
308
+
309
+ for pid, project in projects.items():
310
+ # Remove completed projects after 1 hour
311
+ if project.completed_at and (now - project.completed_at).total_seconds() > 3600:
312
+ to_delete.append(pid)
313
+ # Remove stuck projects after 2 hours
314
+ elif (now - project.created_at).total_seconds() > 7200:
315
+ to_delete.append(pid)
316
+
317
+ for pid in to_delete:
318
+ del projects[pid]
319
+ print(f"🧹 Cleaned up project: {pid}")
320
+
321
+ cleanup_thread = threading.Thread(target=cleanup_old_projects, daemon=True)
322
+ cleanup_thread.start()
323
+
324
  # =============================================
325
  # RUN
326
  # =============================================
 
328
  print("\n" + "=" * 60)
329
  print("βœ… Status Tracker ready!")
330
  print(f"πŸ“Š Active projects: {len(projects)}")
331
+ print(f"πŸ”§ Services: {list(services_config.keys())}")
332
  print("🌐 Server running on port 7860")
333
  print("=" * 60)
334
 
 
 
335
  uvicorn.run(app, host="0.0.0.0", port=7860)