Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
-
# app.py -
|
2 |
import os
|
3 |
import re
|
4 |
import json
|
5 |
from datetime import datetime
|
6 |
-
from typing import List, Dict, Any
|
7 |
|
|
|
8 |
from fastapi import FastAPI, Request, BackgroundTasks
|
9 |
from fastapi.middleware.cors import CORSMiddleware
|
10 |
-
import gradio as gr
|
11 |
from pydantic import BaseModel
|
12 |
|
13 |
# Configuration - Use HF Spaces secrets
|
@@ -89,11 +89,10 @@ async def process_tags_directly(all_tags: List[str], repo_name: str) -> List[str
|
|
89 |
|
90 |
hf_api = HfApi(token=HF_TOKEN)
|
91 |
|
92 |
-
#
|
93 |
repo_type = None
|
94 |
repo_info = None
|
95 |
|
96 |
-
# Try different repository types
|
97 |
for repo_type_to_try in ["model", "dataset", "space"]:
|
98 |
try:
|
99 |
print(f"π Trying to access {repo_name} as {repo_type_to_try}...")
|
@@ -230,7 +229,7 @@ async def process_webhook_comment(webhook_data: Dict[str, Any]):
|
|
230 |
}
|
231 |
tag_operations_store.append(interaction)
|
232 |
|
233 |
-
# Keep only last 100 operations
|
234 |
if len(tag_operations_store) > 100:
|
235 |
tag_operations_store.pop(0)
|
236 |
|
@@ -241,47 +240,90 @@ async def process_webhook_comment(webhook_data: Dict[str, Any]):
|
|
241 |
print(f"β {error_msg}")
|
242 |
return error_msg
|
243 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
244 |
def create_gradio_interface():
|
245 |
"""Create Gradio interface for monitoring"""
|
246 |
-
with gr.Blocks(title="HF Tagging Bot", theme=gr.themes.Soft()) as
|
247 |
gr.Markdown("# π·οΈ HuggingFace Tagging Bot")
|
248 |
gr.Markdown("*Automatically adds tags to repositories when mentioned in discussions*")
|
249 |
|
250 |
-
with gr.Tab("π
|
251 |
gr.Markdown(f"""
|
252 |
-
## Bot
|
253 |
- π **HF Token**: {'β
Configured' if HF_TOKEN else 'β Missing'}
|
254 |
- π **Webhook Secret**: {'β
Configured' if WEBHOOK_SECRET else 'β Missing'}
|
255 |
- π **Operations Processed**: {len(tag_operations_store)}
|
256 |
|
257 |
-
##
|
258 |
-
1. **
|
259 |
-
-
|
260 |
-
-
|
261 |
-
-
|
|
|
262 |
|
263 |
-
2. **In discussions, mention tags
|
264 |
- "Please add tags: pytorch, transformers"
|
265 |
-
- "This needs #pytorch and #text-generation
|
266 |
- "tag: computer-vision"
|
267 |
|
268 |
-
3. **Bot will automatically**:
|
269 |
-
- Detect mentioned tags
|
270 |
-
- Create PRs to add missing tags
|
271 |
-
- Post results in logs
|
272 |
-
|
273 |
## Webhook Endpoint
|
274 |
-
`POST
|
|
|
|
|
|
|
275 |
""")
|
276 |
|
277 |
-
with gr.Tab("π
|
278 |
def get_recent_operations():
|
279 |
if not tag_operations_store:
|
280 |
-
return "No operations yet.
|
281 |
|
282 |
-
recent = tag_operations_store[-10:]
|
283 |
output = []
|
284 |
-
for op in reversed(recent):
|
285 |
output.append(f"""
|
286 |
**{op['repo']}** - {op['timestamp'][:19]}
|
287 |
- π€ Author: {op['comment_author']}
|
@@ -297,33 +339,26 @@ def create_gradio_interface():
|
|
297 |
interactive=False
|
298 |
)
|
299 |
|
300 |
-
refresh_btn = gr.Button("π Refresh")
|
301 |
refresh_btn.click(fn=get_recent_operations, outputs=operations_display)
|
302 |
|
303 |
-
with gr.Tab("π·οΈ
|
304 |
gr.Markdown(f"""
|
305 |
-
##
|
306 |
-
|
307 |
{', '.join(sorted(RECOGNIZED_TAGS))}
|
308 |
|
309 |
-
## Tag Detection
|
310 |
- **Explicit**: `tag: pytorch` or `tags: pytorch, transformers`
|
311 |
- **Hashtag**: `#pytorch #transformers`
|
312 |
-
- **Natural
|
313 |
-
|
314 |
-
## Examples:
|
315 |
-
- β
"This model uses pytorch and transformers"
|
316 |
-
- β
"Please add tags: computer-vision, object-detection"
|
317 |
-
- β
"Missing #pytorch #text-generation tags"
|
318 |
-
- β
"tag: nlp"
|
319 |
""")
|
320 |
-
|
321 |
-
with gr.Tab("π§ Test"):
|
322 |
gr.Markdown("### Test Tag Detection")
|
323 |
test_input = gr.Textbox(
|
324 |
label="Test Comment",
|
325 |
placeholder="Enter a comment to test tag detection...",
|
326 |
-
lines=3
|
|
|
327 |
)
|
328 |
test_output = gr.Textbox(
|
329 |
label="Detected Tags",
|
@@ -337,75 +372,23 @@ def create_gradio_interface():
|
|
337 |
return "Enter some text to test"
|
338 |
tags = extract_tags_from_text(text)
|
339 |
if tags:
|
340 |
-
return f"Found tags: {', '.join(tags)}"
|
341 |
else:
|
342 |
-
return "No tags detected"
|
343 |
|
344 |
test_btn.click(fn=test_tag_detection, inputs=test_input, outputs=test_output)
|
345 |
|
346 |
-
return
|
347 |
-
|
348 |
-
# Create FastAPI app for webhook handling
|
349 |
-
app = FastAPI(title="HF Tagging Bot API")
|
350 |
-
app.add_middleware(CORSMiddleware, allow_origins=["*"])
|
351 |
-
|
352 |
-
@app.post("/webhook")
|
353 |
-
async def webhook_handler(request: Request, background_tasks: BackgroundTasks):
|
354 |
-
"""Handle HF Hub webhooks"""
|
355 |
-
# Verify webhook secret if configured
|
356 |
-
if WEBHOOK_SECRET:
|
357 |
-
webhook_secret = request.headers.get("X-Webhook-Secret")
|
358 |
-
if webhook_secret != WEBHOOK_SECRET:
|
359 |
-
print("β Invalid webhook secret")
|
360 |
-
return {"error": "Invalid webhook secret"}
|
361 |
-
|
362 |
-
try:
|
363 |
-
payload = await request.json()
|
364 |
-
print(f"π₯ Received webhook: {payload.get('event', {})}")
|
365 |
-
|
366 |
-
event = payload.get("event", {})
|
367 |
-
scope = event.get("scope")
|
368 |
-
action = event.get("action")
|
369 |
-
|
370 |
-
# Only process discussion comment creation (not PRs)
|
371 |
-
if (scope == "discussion" and
|
372 |
-
action == "create" and
|
373 |
-
not payload.get("discussion", {}).get("isPullRequest", False)):
|
374 |
-
|
375 |
-
background_tasks.add_task(process_webhook_comment, payload)
|
376 |
-
return {"status": "processing"}
|
377 |
-
|
378 |
-
return {"status": "ignored"}
|
379 |
-
except Exception as e:
|
380 |
-
print(f"β Webhook error: {e}")
|
381 |
-
return {"error": str(e)}
|
382 |
-
|
383 |
-
# Health check endpoint
|
384 |
-
@app.get("/health")
|
385 |
-
async def health_check():
|
386 |
-
return {
|
387 |
-
"status": "healthy",
|
388 |
-
"hf_token_configured": bool(HF_TOKEN),
|
389 |
-
"webhook_secret_configured": bool(WEBHOOK_SECRET),
|
390 |
-
"operations_processed": len(tag_operations_store)
|
391 |
-
}
|
392 |
-
|
393 |
-
@app.get("/")
|
394 |
-
async def root():
|
395 |
-
return {"message": "HF Tagging Bot is running! Visit /gradio for the interface."}
|
396 |
|
397 |
-
# Create
|
398 |
-
|
399 |
-
app = gr.mount_gradio_app(app, gradio_app, path="/gradio")
|
400 |
|
401 |
-
#
|
402 |
-
demo =
|
403 |
|
|
|
404 |
if __name__ == "__main__":
|
405 |
-
|
406 |
-
import uvicorn
|
407 |
-
port = int(os.getenv("PORT", 7860))
|
408 |
-
print(f"π Starting HF Tagging Bot on port {port}")
|
409 |
print(f"π HF_TOKEN: {'β
Configured' if HF_TOKEN else 'β Missing'}")
|
410 |
print(f"π Webhook Secret: {'β
Configured' if WEBHOOK_SECRET else 'β Missing'}")
|
411 |
-
|
|
|
1 |
+
# app.py - HF Spaces compatible version
|
2 |
import os
|
3 |
import re
|
4 |
import json
|
5 |
from datetime import datetime
|
6 |
+
from typing import List, Dict, Any
|
7 |
|
8 |
+
import gradio as gr
|
9 |
from fastapi import FastAPI, Request, BackgroundTasks
|
10 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
11 |
from pydantic import BaseModel
|
12 |
|
13 |
# Configuration - Use HF Spaces secrets
|
|
|
89 |
|
90 |
hf_api = HfApi(token=HF_TOKEN)
|
91 |
|
92 |
+
# Determine repository type
|
93 |
repo_type = None
|
94 |
repo_info = None
|
95 |
|
|
|
96 |
for repo_type_to_try in ["model", "dataset", "space"]:
|
97 |
try:
|
98 |
print(f"π Trying to access {repo_name} as {repo_type_to_try}...")
|
|
|
229 |
}
|
230 |
tag_operations_store.append(interaction)
|
231 |
|
232 |
+
# Keep only last 100 operations
|
233 |
if len(tag_operations_store) > 100:
|
234 |
tag_operations_store.pop(0)
|
235 |
|
|
|
240 |
print(f"β {error_msg}")
|
241 |
return error_msg
|
242 |
|
243 |
+
# Create FastAPI app for webhook handling
|
244 |
+
fastapi_app = FastAPI(title="HF Tagging Bot API")
|
245 |
+
fastapi_app.add_middleware(CORSMiddleware, allow_origins=["*"])
|
246 |
+
|
247 |
+
@fastapi_app.post("/webhook")
|
248 |
+
async def webhook_handler(request: Request, background_tasks: BackgroundTasks):
|
249 |
+
"""Handle HF Hub webhooks"""
|
250 |
+
# Verify webhook secret if configured
|
251 |
+
if WEBHOOK_SECRET:
|
252 |
+
webhook_secret = request.headers.get("X-Webhook-Secret")
|
253 |
+
if webhook_secret != WEBHOOK_SECRET:
|
254 |
+
print("β Invalid webhook secret")
|
255 |
+
return {"error": "Invalid webhook secret"}
|
256 |
+
|
257 |
+
try:
|
258 |
+
payload = await request.json()
|
259 |
+
print(f"π₯ Received webhook: {payload.get('event', {})}")
|
260 |
+
|
261 |
+
event = payload.get("event", {})
|
262 |
+
scope = event.get("scope")
|
263 |
+
action = event.get("action")
|
264 |
+
|
265 |
+
# Only process discussion comment creation (not PRs)
|
266 |
+
if (scope == "discussion" and
|
267 |
+
action == "create" and
|
268 |
+
not payload.get("discussion", {}).get("isPullRequest", False)):
|
269 |
+
|
270 |
+
background_tasks.add_task(process_webhook_comment, payload)
|
271 |
+
return {"status": "processing"}
|
272 |
+
|
273 |
+
return {"status": "ignored"}
|
274 |
+
except Exception as e:
|
275 |
+
print(f"β Webhook error: {e}")
|
276 |
+
return {"error": str(e)}
|
277 |
+
|
278 |
+
@fastapi_app.get("/health")
|
279 |
+
async def health_check():
|
280 |
+
return {
|
281 |
+
"status": "healthy",
|
282 |
+
"hf_token_configured": bool(HF_TOKEN),
|
283 |
+
"webhook_secret_configured": bool(WEBHOOK_SECRET),
|
284 |
+
"operations_processed": len(tag_operations_store)
|
285 |
+
}
|
286 |
+
|
287 |
def create_gradio_interface():
|
288 |
"""Create Gradio interface for monitoring"""
|
289 |
+
with gr.Blocks(title="HF Tagging Bot", theme=gr.themes.Soft()) as interface:
|
290 |
gr.Markdown("# π·οΈ HuggingFace Tagging Bot")
|
291 |
gr.Markdown("*Automatically adds tags to repositories when mentioned in discussions*")
|
292 |
|
293 |
+
with gr.Tab("π Status"):
|
294 |
gr.Markdown(f"""
|
295 |
+
## Bot Configuration
|
296 |
- π **HF Token**: {'β
Configured' if HF_TOKEN else 'β Missing'}
|
297 |
- π **Webhook Secret**: {'β
Configured' if WEBHOOK_SECRET else 'β Missing'}
|
298 |
- π **Operations Processed**: {len(tag_operations_store)}
|
299 |
|
300 |
+
## Setup Instructions
|
301 |
+
1. **Add webhook to your repository**:
|
302 |
+
- Go to repository Settings β Webhooks
|
303 |
+
- Add webhook URL: `https://your-space-name.hf.space/webhook`
|
304 |
+
- Select "Discussion comments" events
|
305 |
+
- Add your webhook secret (optional)
|
306 |
|
307 |
+
2. **In discussions, mention tags**:
|
308 |
- "Please add tags: pytorch, transformers"
|
309 |
+
- "This needs #pytorch and #text-generation"
|
310 |
- "tag: computer-vision"
|
311 |
|
|
|
|
|
|
|
|
|
|
|
312 |
## Webhook Endpoint
|
313 |
+
`POST https://your-space-name.hf.space/webhook`
|
314 |
+
|
315 |
+
## Health Check
|
316 |
+
Visit: `https://your-space-name.hf.space/health`
|
317 |
""")
|
318 |
|
319 |
+
with gr.Tab("π Operations Log"):
|
320 |
def get_recent_operations():
|
321 |
if not tag_operations_store:
|
322 |
+
return "No operations yet. Configure webhooks and post comments with tags to see activity here."
|
323 |
|
324 |
+
recent = tag_operations_store[-10:]
|
325 |
output = []
|
326 |
+
for op in reversed(recent):
|
327 |
output.append(f"""
|
328 |
**{op['repo']}** - {op['timestamp'][:19]}
|
329 |
- π€ Author: {op['comment_author']}
|
|
|
339 |
interactive=False
|
340 |
)
|
341 |
|
342 |
+
refresh_btn = gr.Button("π Refresh Log")
|
343 |
refresh_btn.click(fn=get_recent_operations, outputs=operations_display)
|
344 |
|
345 |
+
with gr.Tab("π·οΈ Tags & Testing"):
|
346 |
gr.Markdown(f"""
|
347 |
+
## Supported Tags ({len(RECOGNIZED_TAGS)} total)
|
|
|
348 |
{', '.join(sorted(RECOGNIZED_TAGS))}
|
349 |
|
350 |
+
## Tag Detection Examples
|
351 |
- **Explicit**: `tag: pytorch` or `tags: pytorch, transformers`
|
352 |
- **Hashtag**: `#pytorch #transformers`
|
353 |
+
- **Natural**: "This model uses pytorch and transformers"
|
|
|
|
|
|
|
|
|
|
|
|
|
354 |
""")
|
355 |
+
|
|
|
356 |
gr.Markdown("### Test Tag Detection")
|
357 |
test_input = gr.Textbox(
|
358 |
label="Test Comment",
|
359 |
placeholder="Enter a comment to test tag detection...",
|
360 |
+
lines=3,
|
361 |
+
value="This model should have tags: pytorch, text-generation"
|
362 |
)
|
363 |
test_output = gr.Textbox(
|
364 |
label="Detected Tags",
|
|
|
372 |
return "Enter some text to test"
|
373 |
tags = extract_tags_from_text(text)
|
374 |
if tags:
|
375 |
+
return f"Found {len(tags)} tags: {', '.join(tags)}"
|
376 |
else:
|
377 |
+
return "No tags detected in this text"
|
378 |
|
379 |
test_btn.click(fn=test_tag_detection, inputs=test_input, outputs=test_output)
|
380 |
|
381 |
+
return interface
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
382 |
|
383 |
+
# Create the Gradio interface
|
384 |
+
demo = create_gradio_interface()
|
|
|
385 |
|
386 |
+
# Mount FastAPI to Gradio (for HF Spaces)
|
387 |
+
app = gr.mount_gradio_app(fastapi_app, demo, path="/")
|
388 |
|
389 |
+
# This is the main app that HF Spaces will use
|
390 |
if __name__ == "__main__":
|
391 |
+
print("π HF Tagging Bot - Gradio interface ready")
|
|
|
|
|
|
|
392 |
print(f"π HF_TOKEN: {'β
Configured' if HF_TOKEN else 'β Missing'}")
|
393 |
print(f"π Webhook Secret: {'β
Configured' if WEBHOOK_SECRET else 'β Missing'}")
|
394 |
+
demo.launch()
|