apple muncy commited on
Commit
fffd9dc
Β·
1 Parent(s): f9b1458

Signed-off-by: apple muncy <[email protected]>

Files changed (4) hide show
  1. Dockerfile +33 -0
  2. README.md +15 -8
  3. mcp_server.py +184 -0
  4. requirements.txt +77 -0
Dockerfile ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ git \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Copy project files
12
+ COPY pyproject.toml .
13
+ COPY server.py .
14
+ COPY mcp_server.py .
15
+ COPY env.example .
16
+ COPY README.md .
17
+
18
+ # Install Python dependencies
19
+ RUN pip install --no-cache-dir -e .
20
+
21
+ # Create a non-root user
22
+ RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
23
+ USER appuser
24
+
25
+ # Expose port
26
+ EXPOSE 8000
27
+
28
+ # Health check
29
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
30
+ CMD curl -f http://localhost:8000/ || exit 1
31
+
32
+ # Run the application
33
+ CMD ["python", "server.py"]
README.md CHANGED
@@ -1,14 +1,21 @@
1
  ---
2
- title: Unit31
3
- emoji: ⚑
4
- colorFrom: red
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 5.34.0
8
  app_file: app.py
9
  pinned: false
10
- license: gpl-3.0
11
- short_description: Test of Unit 3.1 client server werhooks
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: tag-a-repo bot
3
+ emoji: πŸ‘€
4
+ colorFrom: purple
5
+ colorTo: yellow
6
  sdk: gradio
7
+ sdk_version: 5.31.0
8
  app_file: app.py
9
  pinned: false
10
+ base_path: /gradio
 
11
  ---
12
 
13
+ # HF Tagging Bot
14
+
15
+ This is a bot that tags HuggingFace models when they are mentioned in discussions.
16
+
17
+ ## How it works
18
+
19
+ 1. The bot listens to discussions on the HuggingFace Hub
20
+ 2. When a discussion is created, the bot checks for tag mentions in the comment
21
+ 3. If a tag is mentioned, the bot adds the tag to the model repository via a PR
mcp_server.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simplified MCP Server for HuggingFace Hub Tagging Operations using FastMCP
4
+ """
5
+
6
+ import os
7
+ import json
8
+ from fastmcp import FastMCP
9
+ from huggingface_hub import HfApi, model_info, ModelCard, ModelCardData
10
+ from huggingface_hub.utils import HfHubHTTPError
11
+ from dotenv import load_dotenv
12
+
13
+ load_dotenv()
14
+
15
+ # Configuration
16
+ HF_TOKEN = os.getenv("HF_TOKEN")
17
+
18
+ # Initialize HF API client
19
+ hf_api = HfApi(token=HF_TOKEN) if HF_TOKEN else None
20
+
21
+ # Create the FastMCP server
22
+ mcp = FastMCP("hf-tagging-bot")
23
+
24
+
25
+ @mcp.tool()
26
+ def get_current_tags(repo_id: str) -> str:
27
+ """Get current tags from a HuggingFace model repository"""
28
+ print(f"πŸ”§ get_current_tags called with repo_id: {repo_id}")
29
+
30
+ if not hf_api:
31
+ error_result = {"error": "HF token not configured"}
32
+ json_str = json.dumps(error_result)
33
+ print(f"❌ No HF API token - returning: {json_str}")
34
+ return json_str
35
+
36
+ try:
37
+ print(f"πŸ“‘ Fetching model info for: {repo_id}")
38
+ info = model_info(repo_id=repo_id, token=HF_TOKEN)
39
+ current_tags = info.tags if info.tags else []
40
+ print(f"🏷️ Found {len(current_tags)} tags: {current_tags}")
41
+
42
+ result = {
43
+ "status": "success",
44
+ "repo_id": repo_id,
45
+ "current_tags": current_tags,
46
+ "count": len(current_tags),
47
+ }
48
+ json_str = json.dumps(result)
49
+ print(f"βœ… get_current_tags returning: {json_str}")
50
+ return json_str
51
+
52
+ except Exception as e:
53
+ print(f"❌ Error in get_current_tags: {str(e)}")
54
+ error_result = {"status": "error", "repo_id": repo_id, "error": str(e)}
55
+ json_str = json.dumps(error_result)
56
+ print(f"❌ get_current_tags error returning: {json_str}")
57
+ return json_str
58
+
59
+
60
+ @mcp.tool()
61
+ def add_new_tag(repo_id: str, new_tag: str) -> str:
62
+ """Add a new tag to a HuggingFace model repository via PR"""
63
+ print(f"πŸ”§ add_new_tag called with repo_id: {repo_id}, new_tag: {new_tag}")
64
+
65
+ if not hf_api:
66
+ error_result = {"error": "HF token not configured"}
67
+ json_str = json.dumps(error_result)
68
+ print(f"❌ No HF API token - returning: {json_str}")
69
+ return json_str
70
+
71
+ try:
72
+ # Get current model info and tags
73
+ print(f"πŸ“‘ Fetching current model info for: {repo_id}")
74
+ info = model_info(repo_id=repo_id, token=HF_TOKEN)
75
+ current_tags = info.tags if info.tags else []
76
+ print(f"🏷️ Current tags: {current_tags}")
77
+
78
+ # Check if tag already exists
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
+
91
+ # Add the new tag to existing tags
92
+ updated_tags = current_tags + [new_tag]
93
+ print(f"πŸ†• Will update tags from {current_tags} to {updated_tags}")
94
+
95
+ # Create model card content with updated tags
96
+ try:
97
+ # Load existing model card
98
+ print(f"πŸ“„ Loading existing model card...")
99
+ card = ModelCard.load(repo_id, token=HF_TOKEN)
100
+ if not hasattr(card, "data") or card.data is None:
101
+ card.data = ModelCardData()
102
+ except HfHubHTTPError:
103
+ # Create new model card if none exists
104
+ print(f"πŸ“„ Creating new model card (none exists)")
105
+ card = ModelCard("")
106
+ card.data = ModelCardData()
107
+
108
+ # Update tags - create new ModelCardData with updated tags
109
+ card_dict = card.data.to_dict()
110
+ card_dict["tags"] = updated_tags
111
+ card.data = ModelCardData(**card_dict)
112
+
113
+ # Create a pull request with the updated model card
114
+ pr_title = f"Add '{new_tag}' tag"
115
+ pr_description = f"""
116
+ ## Add tag: {new_tag}
117
+
118
+ This PR adds the `{new_tag}` tag to the model repository.
119
+
120
+ **Changes:**
121
+ - Added `{new_tag}` to model tags
122
+ - Updated from {len(current_tags)} to {len(updated_tags)} tags
123
+
124
+ **Current tags:** {", ".join(current_tags) if current_tags else "None"}
125
+ **New tags:** {", ".join(updated_tags)}
126
+ """
127
+
128
+ print(f"πŸš€ Creating PR with title: {pr_title}")
129
+
130
+ # Create commit with updated model card using CommitOperationAdd
131
+ from huggingface_hub import CommitOperationAdd
132
+
133
+ commit_info = hf_api.create_commit(
134
+ repo_id=repo_id,
135
+ operations=[
136
+ CommitOperationAdd(
137
+ path_in_repo="README.md", path_or_fileobj=str(card).encode("utf-8")
138
+ )
139
+ ],
140
+ commit_message=pr_title,
141
+ commit_description=pr_description,
142
+ token=HF_TOKEN,
143
+ create_pr=True,
144
+ )
145
+
146
+ # Extract PR URL from commit info
147
+ pr_url_attr = commit_info.pr_url
148
+ pr_url = pr_url_attr if hasattr(commit_info, "pr_url") else str(commit_info)
149
+
150
+ print(f"βœ… PR created successfully! URL: {pr_url}")
151
+
152
+ result = {
153
+ "status": "success",
154
+ "repo_id": repo_id,
155
+ "tag": new_tag,
156
+ "pr_url": pr_url,
157
+ "previous_tags": current_tags,
158
+ "new_tags": updated_tags,
159
+ "message": f"Created PR to add tag '{new_tag}'",
160
+ }
161
+ json_str = json.dumps(result)
162
+ print(f"βœ… add_new_tag success returning: {json_str}")
163
+ return json_str
164
+
165
+ except Exception as e:
166
+ print(f"❌ Error in add_new_tag: {str(e)}")
167
+ print(f"❌ Error type: {type(e)}")
168
+ import traceback
169
+
170
+ print(f"❌ Traceback: {traceback.format_exc()}")
171
+
172
+ error_result = {
173
+ "status": "error",
174
+ "repo_id": repo_id,
175
+ "tag": new_tag,
176
+ "error": str(e),
177
+ }
178
+ json_str = json.dumps(error_result)
179
+ print(f"❌ add_new_tag error returning: {json_str}")
180
+ return json_str
181
+
182
+
183
+ if __name__ == "__main__":
184
+ mcp.run()
requirements.txt ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file was autogenerated by uv via the following command:
2
+ # uv export --format requirements-txt --no-hashes
3
+ aiofiles==24.1.0
4
+ aiohappyeyeballs==2.6.1
5
+ aiohttp==3.12.2
6
+ aiosignal==1.3.2
7
+ annotated-types==0.7.0
8
+ anyio==4.9.0
9
+ attrs==25.3.0
10
+ audioop-lts==0.2.1 ; python_full_version >= '3.13'
11
+ certifi==2025.4.26
12
+ charset-normalizer==3.4.2
13
+ click==8.2.1
14
+ colorama==0.4.6 ; sys_platform == 'win32' or platform_system == 'Windows'
15
+ exceptiongroup==1.3.0
16
+ fastapi==0.115.12
17
+ fastmcp==2.5.1
18
+ ffmpy==0.5.0
19
+ filelock==3.18.0
20
+ frozenlist==1.6.0
21
+ fsspec==2025.5.1
22
+ gradio==5.31.0
23
+ gradio-client==1.10.1
24
+ groovy==0.1.2
25
+ h11==0.16.0
26
+ hf-xet==1.1.2 ; platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'
27
+ httpcore==1.0.9
28
+ httptools==0.6.4
29
+ httpx==0.28.1
30
+ httpx-sse==0.4.0
31
+ huggingface-hub==0.32.2
32
+ idna==3.10
33
+ jinja2==3.1.6
34
+ markdown-it-py==3.0.0
35
+ markupsafe==3.0.2
36
+ mcp==1.9.1
37
+ mdurl==0.1.2
38
+ multidict==6.4.4
39
+ numpy==2.2.6
40
+ openapi-pydantic==0.5.1
41
+ orjson==3.10.18
42
+ packaging==25.0
43
+ pandas==2.2.3
44
+ pillow==11.2.1
45
+ propcache==0.3.1
46
+ pydantic==2.11.5
47
+ pydantic-core==2.33.2
48
+ pydantic-settings==2.9.1
49
+ pydub==0.25.1
50
+ pygments==2.19.1
51
+ python-dateutil==2.9.0.post0
52
+ python-dotenv==1.1.0
53
+ python-multipart==0.0.20
54
+ pytz==2025.2
55
+ pyyaml==6.0.2
56
+ requests==2.32.3
57
+ rich==14.0.0
58
+ ruff==0.11.11 ; sys_platform != 'emscripten'
59
+ safehttpx==0.1.6
60
+ semantic-version==2.10.0
61
+ shellingham==1.5.4
62
+ six==1.17.0
63
+ sniffio==1.3.1
64
+ sse-starlette==2.3.5
65
+ starlette==0.46.2
66
+ tomlkit==0.13.2
67
+ tqdm==4.67.1
68
+ typer==0.16.0
69
+ typing-extensions==4.13.2
70
+ typing-inspection==0.4.1
71
+ tzdata==2025.2
72
+ urllib3==2.4.0
73
+ uvicorn==0.34.2
74
+ uvloop==0.21.0 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'
75
+ watchfiles==1.0.5
76
+ websockets==15.0.1
77
+ yarl==1.20.0