VenkatesaPerumal commited on
Commit
955ef08
Β·
1 Parent(s): f35f643

intial commit

Browse files
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .env
__pycache__/app.cpython-313.pyc ADDED
Binary file (21.9 kB). View file
 
__pycache__/mcp_server.cpython-313.pyc ADDED
Binary file (6.75 kB). View file
 
app.py ADDED
@@ -0,0 +1,477 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ----------------------------------------------------------------------------------
2
+ # 1. Agent Configuration
3
+ # ----------------------------------------------------------------------------------
4
+
5
+ import os
6
+ import re
7
+ import json
8
+ from datetime import datetime
9
+ from typing import List, Dict, Any, Optional, Literal
10
+
11
+ from fastapi import FastAPI, Request, BackgroundTasks
12
+ from fastapi.middleware.cors import CORSMiddleware
13
+ import gradio as gr
14
+ import uvicorn
15
+ from pydantic import BaseModel
16
+ from fastapi.responses import RedirectResponse
17
+ from huggingface_hub.inference._mcp.agent import Agent
18
+ from dotenv import load_dotenv
19
+ load_dotenv()
20
+
21
+ HF_TOKEN=os.getenv("HF_TOKEN")
22
+ WEBHOOK_SECRET=os.getenv("WEBHOOK_SECRET")
23
+ HF_MODEL=os.getenv("HF_MODEL","HuggingFaceH4/zephyr-7b-beta")
24
+ DEFAULT_PROVIDER:Literal['hf-inference']="hf-inference"
25
+ HF_PROVIDER=os.getenv("HF_PROVIDER")
26
+ agent_instance: Optional[Agent]=None
27
+ tag_operations_store:List[Dict[str,Any]]=[]
28
+
29
+ RECOGNIZED_TAGS = {
30
+ "pytorch",
31
+ "tensorflow",
32
+ "jax",
33
+ "transformers",
34
+ "diffusers",
35
+ "text-generation",
36
+ "text-classification",
37
+ "question-answering",
38
+ "text-to-image",
39
+ "image-classification",
40
+ "object-detection",
41
+ " ",
42
+ "fill-mask",
43
+ "token-classification",
44
+ "translation",
45
+ "summarization",
46
+ "feature-extraction",
47
+ "sentence-similarity",
48
+ "zero-shot-classification",
49
+ "image-to-text",
50
+ "automatic-speech-recognition",
51
+ "audio-classification",
52
+ "voice-activity-detection",
53
+ "depth-estimation",
54
+ "image-segmentation",
55
+ "video-classification",
56
+ "reinforcement-learning",
57
+ "tabular-classification",
58
+ "tabular-regression",
59
+ "time-series-forecasting",
60
+ "graph-ml",
61
+ "robotics",
62
+ "computer-vision",
63
+ "nlp",
64
+ "cv",
65
+ "multimodal",
66
+ }
67
+
68
+
69
+ class WebhookEvent(BaseModel):
70
+ event: Dict[str, str]
71
+ comment: Dict[str, Any]
72
+ discussion: Dict[str, Any]
73
+ repo: Dict[str, str]
74
+
75
+
76
+ app = FastAPI(title="HF Tagging Bot")
77
+ app.add_middleware(CORSMiddleware, allow_origins=["*"])
78
+
79
+
80
+ async def get_agent():
81
+ """Get or create Agent instance"""
82
+ print("πŸ€– get_agent() called...")
83
+ global agent_instance
84
+ if agent_instance is None and HF_TOKEN:
85
+ print("πŸ”§ Creating new Agent instance...")
86
+ print(f"πŸ”‘ HF_TOKEN present: {bool(HF_TOKEN)}")
87
+ print(f"πŸ€– Model: {HF_MODEL}")
88
+ print(f"πŸ”— Provider: {DEFAULT_PROVIDER}")
89
+
90
+ try:
91
+ agent_instance = Agent(
92
+ model=HF_MODEL,
93
+ provider=DEFAULT_PROVIDER,
94
+ api_key=HF_TOKEN,
95
+ servers=[
96
+ {
97
+ "type": "stdio",
98
+ "command": "python",
99
+ "args": ["mcp_server.py"],
100
+ "cwd": ".",
101
+ "env": {"HF_TOKEN": HF_TOKEN} if HF_TOKEN else {},
102
+ }
103
+ ],
104
+ )
105
+ print("βœ… Agent instance created successfully")
106
+ print("πŸ”§ Loading tools...")
107
+ await agent_instance.load_tools()
108
+ print("βœ… Tools loaded successfully")
109
+ except Exception as e:
110
+ print(f"❌ Error creating/loading agent: {str(e)}")
111
+ agent_instance = None
112
+ elif agent_instance is None:
113
+ print("❌ No HF_TOKEN available, cannot create agent")
114
+ else:
115
+ print("βœ… Using existing agent instance")
116
+
117
+ return agent_instance
118
+
119
+
120
+ def extract_tags_from_text(text: str) -> List[str]:
121
+ """Extract potential tags from discussion text"""
122
+ text_lower = text.lower()
123
+ explicit_tags = []
124
+ tag_pattern = r"tags?:\s*([a-zA-Z0-9-_,\s]+)"
125
+ matches = re.findall(tag_pattern, text_lower)
126
+ for match in matches:
127
+ tags = [tag.strip() for tag in match.split(",")]
128
+ explicit_tags.extend(tags)
129
+ hashtag_pattern = r"#([a-zA-Z0-9-_]+)"
130
+ hashtag_matches = re.findall(hashtag_pattern, text_lower)
131
+ explicit_tags.extend(hashtag_matches)
132
+
133
+ mentioned_tags = []
134
+ for tag in RECOGNIZED_TAGS:
135
+ if tag in text_lower:
136
+ mentioned_tags.append(tag)
137
+
138
+ all_tags = list(set(explicit_tags + mentioned_tags))
139
+
140
+ valid_tags = []
141
+ for tag in all_tags:
142
+ if tag in RECOGNIZED_TAGS or tag in explicit_tags:
143
+ valid_tags.append(tag)
144
+
145
+ return valid_tags
146
+
147
+
148
+ async def process_webhook_comment(webhook_data: Dict[str, Any]):
149
+ """Process webhook to detect and add tags"""
150
+ print("🏷️ Starting process_webhook_comment...")
151
+
152
+ try:
153
+ comment_content = webhook_data["comment"]["content"]
154
+ discussion_title = webhook_data["discussion"]["title"]
155
+ repo_name = webhook_data["repo"]["name"]
156
+ discussion_num = webhook_data["discussion"]["num"]
157
+ comment_author = webhook_data["comment"]["author"].get("id", "unknown")
158
+
159
+ print(f"πŸ“ Comment content: {comment_content}")
160
+ print(f"πŸ“° Discussion title: {discussion_title}")
161
+ print(f"πŸ“¦ Repository: {repo_name}")
162
+
163
+ comment_tags = extract_tags_from_text(comment_content)
164
+ title_tags = extract_tags_from_text(discussion_title)
165
+ all_tags = list(set(comment_tags + title_tags))
166
+
167
+ print(f"πŸ” Comment tags found: {comment_tags}")
168
+ print(f"πŸ” Title tags found: {title_tags}")
169
+ print(f"🏷️ All unique tags: {all_tags}")
170
+
171
+ result_messages = []
172
+
173
+ if not all_tags:
174
+ msg = "No recognizable tags found in the discussion."
175
+ print(f"❌ {msg}")
176
+ result_messages.append(msg)
177
+ else:
178
+ print("πŸ€– Getting agent instance...")
179
+ agent = await get_agent()
180
+ if not agent:
181
+ msg = "Error: Agent not configured (missing HF_TOKEN please check)"
182
+ print(f"❌ {msg}")
183
+ result_messages.append(msg)
184
+ else:
185
+ print("βœ… Agent instance obtained successfully")
186
+
187
+ try:
188
+ user_prompt = f"""
189
+ I need to add the following tags to the repository '{repo_name}': {", ".join(all_tags)}
190
+ For each tag, please:
191
+ 1. Check if the tag already exists on the repository using get_current_tags
192
+ 2. If the tag doesn't exist, add it using add_new_tag
193
+ 3. Provide a summary of what was done for each tag
194
+ Please process all {len(all_tags)} tags: {", ".join(all_tags)}
195
+ """
196
+
197
+ print("πŸ’¬ Sending comprehensive prompt to agent...")
198
+ print(f"πŸ“ Prompt: {user_prompt}")
199
+
200
+ conversation_result = []
201
+
202
+ try:
203
+ async for item in agent.run(user_prompt):
204
+ item_str = str(item)
205
+ conversation_result.append(item_str)
206
+
207
+ if (
208
+ "tool_call" in item_str.lower()
209
+ or "function" in item_str.lower()
210
+ ):
211
+ print(f"πŸ”§ Agent using tools: {item_str[:200]}...")
212
+ elif "content" in item_str and len(item_str) < 500:
213
+ print(f"πŸ’­ Agent response: {item_str}")
214
+
215
+ full_response = " ".join(conversation_result)
216
+ print(f"πŸ“‹ Agent conversation completed successfully")
217
+
218
+ for tag in all_tags:
219
+ tag_mentioned = tag.lower() in full_response.lower()
220
+
221
+ if (
222
+ "already exists" in full_response.lower()
223
+ and tag_mentioned
224
+ ):
225
+ msg = f"Tag '{tag}': Already exists"
226
+ elif (
227
+ "pr" in full_response.lower()
228
+ or "pull request" in full_response.lower()
229
+ ):
230
+ if tag_mentioned:
231
+ msg = f"Tag '{tag}': PR created successfully"
232
+ else:
233
+ msg = (
234
+ f"Tag '{tag}': Processed "
235
+ "(PR may have been created)"
236
+ )
237
+ elif "success" in full_response.lower() and tag_mentioned:
238
+ msg = f"Tag '{tag}': Successfully processed"
239
+ elif "error" in full_response.lower() and tag_mentioned:
240
+ msg = f"Tag '{tag}': Error during processing"
241
+ else:
242
+ msg = f"Tag '{tag}': Processed by agent"
243
+
244
+ print(f"βœ… Result for tag '{tag}': {msg}")
245
+ result_messages.append(msg)
246
+
247
+ except Exception as agent_error:
248
+ print(f"⚠️ Agent streaming failed: {str(agent_error)}")
249
+ print("πŸ”„ Falling back to direct MCP tool calls...")
250
+
251
+ try:
252
+ import sys
253
+ import importlib.util
254
+
255
+ spec = importlib.util.spec_from_file_location(
256
+ "mcp_server", "./mcp_server.py"
257
+ )
258
+ mcp_module = importlib.util.module_from_spec(spec)
259
+ spec.loader.exec_module(mcp_module)
260
+ for tag in all_tags:
261
+ try:
262
+ print(
263
+ f"πŸ”§ Directly calling get_current_tags for '{tag}'"
264
+ )
265
+ current_tags_result = mcp_module.get_current_tags(
266
+ repo_name
267
+ )
268
+ print(
269
+ f"πŸ“„ Current tags result: {current_tags_result}"
270
+ )
271
+
272
+ import json
273
+
274
+ tags_data = json.loads(current_tags_result)
275
+
276
+ if tags_data.get("status") == "success":
277
+ current_tags = tags_data.get("current_tags", [])
278
+ if tag in current_tags:
279
+ msg = f"Tag '{tag}': Already exists"
280
+ print(f"βœ… {msg}")
281
+ else:
282
+ print(
283
+ f"πŸ”§ Directly calling add_new_tag for '{tag}'"
284
+ )
285
+ add_result = mcp_module.add_new_tag(
286
+ repo_name, tag
287
+ )
288
+ print(f"πŸ“„ Add tag result: {add_result}")
289
+
290
+ add_data = json.loads(add_result)
291
+ if add_data.get("status") == "success":
292
+ pr_url = add_data.get("pr_url", "")
293
+ msg = f"Tag '{tag}': PR created - {pr_url}"
294
+ elif (
295
+ add_data.get("status")
296
+ == "already_exists"
297
+ ):
298
+ msg = f"Tag '{tag}': Already exists"
299
+ else:
300
+ msg = f"Tag '{tag}': {add_data.get('message', 'Processed')}"
301
+ print(f"βœ… {msg}")
302
+ else:
303
+ error_msg = tags_data.get(
304
+ "error", "Unknown error"
305
+ )
306
+ msg = f"Tag '{tag}': Error - {error_msg}"
307
+ print(f"❌ {msg}")
308
+
309
+ result_messages.append(msg)
310
+
311
+ except Exception as direct_error:
312
+ error_msg = f"Tag '{tag}': Direct call error - {str(direct_error)}"
313
+ print(f"❌ {error_msg}")
314
+ result_messages.append(error_msg)
315
+ except Exception as fallback_error:
316
+ error_msg = f"Fallback approach failed: {str(fallback_error)}"
317
+ print(f"❌ {error_msg}")
318
+ result_messages.append(error_msg)
319
+ except Exception as e:
320
+ error_msg = f"Error during agent processing {str(e)}"
321
+ print(f"❌ {error_msg}")
322
+ result_messages.append(error_msg)
323
+ base_url="https://huggingface.co"
324
+ discussion_url=f"{base_url}/{repo_name}/discussion/{discussion_num}"
325
+ interaction = {
326
+ "timestamp": datetime.now().isoformat(),
327
+ "repo": repo_name,
328
+ "discussion_title": discussion_title,
329
+ "discussion_num": discussion_num,
330
+ "discussion_url": discussion_url,
331
+ "original_comment": comment_content,
332
+ "comment_author": comment_author,
333
+ "detected_tags": all_tags,
334
+ "results": result_messages,
335
+ }
336
+ tag_operations_store.append(interaction)
337
+ final_result="|".join(result_messages)
338
+ print(f"πŸ’Ύ Stored interaction and returning result: {final_result}")
339
+ return final_result
340
+
341
+ except Exception as e:
342
+ error_msg = f"❌ Fatal error in process_webhook_comment: {str(e)}"
343
+ print(error_msg)
344
+ return error_msg
345
+
346
+
347
+ @app.get("/")
348
+ async def root():
349
+ """Root endpoint with basic information"""
350
+ return {
351
+ "name":"HF Tagging Bot",
352
+ "status":"running",
353
+ "description":"Webhook listener for automatic model tagging",
354
+ "endpoints":{
355
+ "webhook":"/webhook",
356
+ "health":"/health",
357
+ "operations":"/operations"
358
+ }
359
+ }
360
+
361
+
362
+ @app.get("/health")
363
+ async def health_check():
364
+ """Health check endpoint for monitoring"""
365
+ agent=await get_agent()
366
+ return {
367
+ "status":"healthy",
368
+ "timestamp":datetime.now().isoformat(),
369
+ "components":{
370
+ "webhook_secret":"configured" if WEBHOOK_SECRET else "missing",
371
+ "hf_token":"configured" if HF_TOKEN else "missing",
372
+ "mcp_agent":"ready" if agent else "not ready"
373
+ }
374
+ }
375
+
376
+ @app.get("/operations")
377
+ async def get_operations():
378
+ """Get recent tag operations for monitoring"""
379
+ recent_ops=tag_operations_store[-50:] if tag_operations_store else []
380
+ return {
381
+ "total_operations":len(tag_operations_store),
382
+ "recent_operations":recent_ops
383
+ }
384
+
385
+
386
+
387
+ @app.post("/Webhook")
388
+ async def webhook_handler(request:Request, background_tasks:BackgroundTasks):
389
+ """
390
+ Handle incoming webhooks from Hugging Face Hub
391
+ Following the pattern from: https://raw.githubusercontent.com/huggingface/hub-docs/refs/heads/main/docs/hub/webhooks-guide-discussion-bot.md
392
+ """
393
+ print("πŸ”” Webhook received!")
394
+ webhook_secret=request.headers.get("X-webhook-Secret")
395
+ if webhook_secret!=WEBHOOK_SECRET:
396
+ print("❌ Invalid webhook secret")
397
+ return {"error":"incorrect secret"}
398
+ payload=await request.json()
399
+ print(f"πŸ“₯ Received webhook payload: {json.dumps(payload, indent=2)}")
400
+ event=payload.get("event",{})
401
+ scope=event.get("score")
402
+ action=event.get("action")
403
+
404
+ print(f"πŸ” Event details - scope: {scope}, action: {action}")
405
+ scope_check = scope == "discussion"
406
+ action_check = action == "create"
407
+ not_pr = not payload["discussion"]["isPullRequest"]
408
+ scope_check = scope_check and not_pr
409
+ print(f"βœ… not_pr: {not_pr}")
410
+ print(f"βœ… scope_check: {scope_check}")
411
+ print(f"βœ… action_check: {action_check}")
412
+
413
+ if scope_check and action_check:
414
+ required_fields=['comment','discussion','repo']
415
+ missing_fields=[field for field in required_fields if field not in payload]
416
+
417
+ if missing_fields:
418
+ error_msg = f"Missing required fields: {missing_fields}"
419
+ print(f"❌ {error_msg}")
420
+ return {"error": error_msg}
421
+ print(f"πŸš€ Processing webhook for repo: {payload['repo']['name']}")
422
+ background_tasks.add_task(process_webhook_comment,payload)
423
+ return {"status":"processing"}
424
+ print(f"⏭️ Ignoring webhook - scope: {scope}, action: {action}")
425
+ return {"status": "ignored"}
426
+
427
+ @app.post("/simulate_webhook")
428
+ async def simulate_webhook(repo_name:str,discussion_title:str,comment_content:str)->str:
429
+ """Simulate webhook for testing purposes"""
430
+ if not all([repo_name,discussion_title,comment_content]):
431
+ return "please fill in all fields"
432
+ mock_payload={
433
+ "event":{"action":"create","scope":"discussion.comment"},
434
+ "comment":{"content":comment_content,"author":{"id":"test-user"},"id":"mock-comment-id","hidden":False},
435
+ "discussion":{"title":discussion_title,"num":len(tag_operations_store)+1,"id":"mock-comment-id","status":"open","isPullRequest":False},
436
+ "repo":{"name":repo_name,"type":"model","private":False}
437
+ }
438
+ response=await process_webhook_comment(mock_payload)
439
+ return f"βœ… Processed! Results: {response}"
440
+
441
+ def create_gradio_app():
442
+ """Create Gradio interface"""
443
+ with gr.Blocks(title="HF Tagging Bot", theme=gr.themes.Soft()) as demo:
444
+ gr.Markdown("# 🏷️ HF Tagging Bot Dashboard")
445
+ gr.Markdown("*Automatically adds tags to models when mentioned in discussions*")
446
+
447
+ gr.Markdown("""
448
+ ## How it works:
449
+ - Monitors HuggingFace Hub discussions
450
+ - Detects tag mentions in comments (e.g., "tag: pytorch",
451
+ "#transformers")
452
+ - Automatically adds recognized tags to the model repository
453
+ - Supports common ML tags like: pytorch, tensorflow,
454
+ text-generation, etc.
455
+ """)
456
+ with gr.Column():
457
+ sim_repo=gr.Textbox(label="Repository",value="burtenshaw/play-mcp-repo-bot",placeholder="username/model-name")
458
+ sim_title= gr.Textbox(label="Discussion Title",value="Add pytorch tag",placeholder="Discussion title")
459
+ sim_comment=gr.Textbox(label="comment",lines=3,value="This model should have tags: pytorch, text-generation",placeholder="Comment mentioning tags ...")
460
+ sim_btn=gr.Button("🏷️ Test Tag Detection")
461
+ with gr.Column():
462
+ sim_result=gr.Textbox(label="Result",lines=8)
463
+ sim_btn.click(fn=simulate_webhook,inputs=[sim_repo,sim_title,sim_comment],outputs=sim_result)
464
+ gr.Markdown(f"""## Recognized Tags: {",".join(sorted(RECOGNIZED_TAGS))}""")
465
+ return demo
466
+ gradio_app=create_gradio_app()
467
+ app=gr.mount_gradio_app(app,gradio_app,path="/gradio")
468
+
469
+ @app.get("/")
470
+ async def root_direct():
471
+ return RedirectResponse(url="/gradio")
472
+
473
+ if __name__=="__main__":
474
+ print("πŸš€ Starting HF Tagging Bot...")
475
+ print("πŸ“Š Dashboard: http://localhost:7860/gradio")
476
+ print("πŸ”— Webhook: http://localhost:7860/webhook")
477
+ uvicorn.run("app:app",host="0.0.0.0",port=7860,reload=True)
mcp_server.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --------------------------------------------------------------------------------------
2
+ # 1. Imports and Configuration
3
+ # ---------------------------------------------------------------------------------------
4
+
5
+ import os
6
+ import json
7
+ from fastmcp import FastMCP
8
+ from huggingface_hub import HfApi, model_info, ModelCard, ModelCardData
9
+ from huggingface_hub.utils import HfHubHTTPError
10
+ from huggingface_hub import CommitOperationAdd
11
+ import traceback
12
+ from dotenv import load_dotenv
13
+ load_dotenv()
14
+
15
+
16
+ HF_TOKEN=os.getenv("HF_TOKEN")
17
+ hf_api=HfApi(token=HF_TOKEN) if HF_TOKEN else None
18
+ mcp=FastMCP("hf-tagging-bot")
19
+
20
+ # -------------------------------------------------------------------------------
21
+ # 2. Get Current Tags Tool
22
+ # -------------------------------------------------------------------------------
23
+
24
+ #@mcp.tool(name="get_current_tags")
25
+ def get_current_tags(repo_id:str)->str:
26
+ """Get Current tags from a HuggingFace model repository"""
27
+ print(f"πŸ”§ get_current_tags called with repo_id: {repo_id}")
28
+ if not hf_api:
29
+ error_result={"error":"HF token not configured"}
30
+ json_str=json.dumps(error_result)
31
+ print(f"❌ No HF API token-returning:{json_str}")
32
+ return json_str
33
+
34
+ try:
35
+ print(f"πŸ“‘Fetching model info for: {repo_id}")
36
+ info=model_info(repo_id=repo_id,token=HF_TOKEN)
37
+ current_tags= info.tags if info.tags else []
38
+ print(f"🏷️ Found {len(current_tags)} tags: {current_tags}")
39
+
40
+ result={
41
+ "status":"success",
42
+ "repo_id":repo_id,
43
+ "current_tags":current_tags,
44
+ "count":len(current_tags)
45
+ }
46
+ json_str=json.dumps(result)
47
+ print(f"βœ… get_current_tags returning : {json_str}")
48
+ return json_str
49
+ except Exception as e:
50
+ print(f"❌ Error in get_current_tags: {str(e)}")
51
+ error_result={
52
+ "status":"success",
53
+ "repo_id":repo_id,
54
+ "error":str(e)
55
+ }
56
+ json_str=json.dumps(error_result)
57
+ print(f"❌ get_current_tags error returning : {json_str}")
58
+ return json_str
59
+
60
+ # --------------------------------------------------------------------------------------------------------
61
+ # 3. 3. Add New Tag Tool
62
+ # --------------------------------------------------------------------------------------------------------
63
+
64
+ #@mcp.tool(name="add_new_tag")
65
+ def add_new_tag(repo_id:str,new_tag:str)->str:
66
+ """Add a new tag to a HuggingFace model repository via PR"""
67
+ print(f"πŸ”§ add_new_tag called with repo_id: {repo_id}, new_tag:{new_tag}")
68
+ if not hf_api:
69
+ error_result={"error":"HF token not configured"}
70
+ json_str=json.dumps(error_result)
71
+ print(f"❌ No HF API token-returning:{json_str}")
72
+ return json_str
73
+ try:
74
+ print(f"πŸ“‘Fetching model info for: {repo_id}")
75
+ info=model_info(repo_id=repo_id,token=HF_TOKEN)
76
+ current_tags= info.tags if info.tags else []
77
+ print(f"🏷️ Found {len(current_tags)} tags: {current_tags}")
78
+
79
+ if new_tag in current_tags:
80
+ print(f"⚠️ Tag '{new_tag}' already exists in {current_tags}")
81
+ result={
82
+ "status":"already_exists",
83
+ "repo_id":repo_id,
84
+ "tag":new_tag,
85
+ "message":f"Tag '{new_tag}' already exists"
86
+ }
87
+ json_str=json.dumps(result)
88
+ print(f"🏷️ add_new_tag (already exists) returning : {json_str}")
89
+ return json_str
90
+ updated_tags=current_tags+[new_tag]
91
+ print(f"πŸ†• will update tags from {current_tags} to {updated_tags}")
92
+ try:
93
+ print(f"πŸ“„ loading existing model card...")
94
+ card=ModelCard.load(repo_id,token=HF_TOKEN)
95
+ if not hasattr(card,"data") or card.data is None:
96
+ card.data=ModelCardData()
97
+ except HfHubHTTPError:
98
+ print(f"πŸ“„ creating new model card (none exists)")
99
+ card=ModelCard("")
100
+ card.data=ModelCardData()
101
+ card_dict=card.data.to_dict()
102
+ card_dict['tags']=updated_tags
103
+ card.data=ModelCardData(**card_dict)
104
+ pr_title=f"Add '{new_tag}' tag"
105
+ pr_description=f"""
106
+ ## Add tag: {new_tag}
107
+ This PR adds the '{new_tag}' tag to the model repository.
108
+ **changes:**
109
+ - Added '{new_tag}' to model tags
110
+ - Updated from {len(current_tags)} to {len(updated_tags)} tags
111
+ **current tags:** {",".join(current_tags) if current_tags else "None"}
112
+ **New tags:** {",".join(updated_tags)}
113
+ πŸ€–This is a pull request created by the HuggingFace Hub Tagging Bot.
114
+ """
115
+ print(f"πŸš€ creating PR with title: {pr_title}")
116
+ commit_info=hf_api.create_commit(
117
+ repo_id=repo_id,
118
+ operations=[CommitOperationAdd(path_in_repo="README.md",path_or_fileobj=str(card).encode("utf-8"))],
119
+ commit_message=pr_title,commit_description=pr_description,token=HF_TOKEN,create_pr=True
120
+ )
121
+ pr_url_attr=commit_info.pr_url
122
+ pr_url=pr_url_attr if hasattr(commit_info,"prl_url") else str(commit_info)
123
+ print(f"βœ… PR created successfully! URL: {pr_url}")
124
+ result={
125
+ "status":"success",
126
+ "repo_id":repo_id,
127
+ "tag":new_tag,
128
+ "prl_url":pr_url,
129
+ "previous_tags":current_tags,
130
+ "new_tags":updated_tags,
131
+ "message":f"created PR to add tag '{new_tag}'"
132
+ }
133
+ json_str=json.dumps(result)
134
+ print(f"βœ… add_new_tag success returning: {json_str}")
135
+ return json_str
136
+ except Exception as e:
137
+ print(f"❌ Error in add_new_tag: {str(e)}")
138
+ print(f"❌ Error type: {type(e)}")
139
+ print(f"❌ Traceback: {traceback.format_exc()}")
140
+ error_result={
141
+ "status":"error",
142
+ "repo_id":repo_id,
143
+ "tag":new_tag,
144
+ "error":str(e)
145
+ }
146
+ json_str=json.dumps(error_result)
147
+ print(f"❌ add_new_tag error returning: {json_str}")
148
+ return json_str
149
+ if __name__ == "__main__":
150
+ mcp.run()
pyproject.toml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "mcp-course-unit3-example"
3
+ version = "0.1.0"
4
+ description = "FastAPI and Gradio app for Hugging Face Hub discussion webhooks"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "fastapi>=0.104.0",
9
+ "uvicorn[standard]>=0.24.0",
10
+ "gradio>=4.0.0",
11
+ "huggingface-hub[mcp]>=0.32.0",
12
+ "pydantic>=2.0.0",
13
+ "python-multipart>=0.0.6",
14
+ "requests>=2.31.0",
15
+ "python-dotenv>=1.0.0",
16
+ "fastmcp>=2.0.0",
17
+ ]
18
+
19
+ [build-system]
20
+ requires = ["hatchling"]
21
+ build-backend = "hatchling.build"
22
+
23
+ [tool.hatch.build.targets.wheel]
24
+ packages = ["src"]
requirements.txt ADDED
Binary file (294 Bytes). View file
 
uv.lock ADDED
The diff for this file is too large to render. See raw diff