evalstate HF Staff commited on
Commit
ddd502d
·
verified ·
1 Parent(s): 122ab90

Deploy hidden gems MCP server

Browse files
Files changed (4) hide show
  1. Dockerfile +3 -23
  2. README.md +80 -28
  3. hidden-gems.md +24 -44
  4. hidden_gems_tool.py +119 -176
Dockerfile CHANGED
@@ -1,38 +1,18 @@
1
  FROM python:3.13-slim
2
 
3
- # Install system dependencies required by fast-agent and HF Spaces
4
  RUN apt-get update && \
5
- apt-get install -y \
6
- bash \
7
- git git-lfs \
8
- wget curl procps \
9
- && rm -rf /var/lib/apt/lists/*
10
 
11
- # Install uv for fast, reliable package management
12
  COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
13
 
14
- # Set working directory
15
  WORKDIR /app
16
-
17
- # Install fast-agent-mcp from PyPI
18
  RUN uv pip install --system --no-cache fast-agent-mcp
19
 
20
- # Copy all files from the Space repository to /app
21
  COPY --link ./ /app
22
-
23
- # Ensure /app is owned by uid 1000 (required for HF Spaces)
24
  RUN chown -R 1000:1000 /app
25
-
26
- # Switch to non-root user
27
  USER 1000
28
 
29
- # Expose port 7860 (HF Spaces default)
30
  EXPOSE 7860
31
 
32
- # Run fast-agent serve with token passthrough
33
- CMD ["fast-agent", "serve", \
34
- "--card", "hidden-gems.md", \
35
- "--transport", "http", \
36
- "--instance-scope", "request", \
37
- "--host", "0.0.0.0", \
38
- "--port", "7860"]
 
1
  FROM python:3.13-slim
2
 
 
3
  RUN apt-get update && \
4
+ apt-get install -y bash git git-lfs wget curl procps && \
5
+ rm -rf /var/lib/apt/lists/*
 
 
 
6
 
 
7
  COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
8
 
 
9
  WORKDIR /app
 
 
10
  RUN uv pip install --system --no-cache fast-agent-mcp
11
 
 
12
  COPY --link ./ /app
 
 
13
  RUN chown -R 1000:1000 /app
 
 
14
  USER 1000
15
 
 
16
  EXPOSE 7860
17
 
18
+ CMD ["fast-agent", "serve", "--card", "hidden-gems.md", "--transport", "http", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
 
README.md CHANGED
@@ -1,43 +1,95 @@
1
  ---
2
- title: Hidden Gems Finder
3
- emoji: 💎
4
- colorFrom: purple
5
- colorTo: blue
6
  sdk: docker
7
  app_port: 7860
8
- license: mit
9
- short_description: Discover undervalued HF models with high engagement ratios
10
  ---
11
 
12
- # Hidden Gems Finder MCP Server
13
 
14
- An MCP server that helps you discover undervalued Hugging Face models. It finds models with high "likes per download" ratios - quality models that haven't blown up yet!
15
 
16
- ## What it does
17
 
18
- The Hidden Gems Finder analyzes models from the Hugging Face Hub and identifies those where:
19
- - The community engagement (likes) is high relative to downloads
20
- - Downloads are in the "sweet spot" (not too obscure, not too popular)
21
- - Model quality signals suggest undervalued potential
22
 
23
- ## Usage
 
24
 
25
- Connect to this MCP server to:
26
- - Find hidden gems across all model types
27
- - Search for gems in specific categories (text-generation, image-to-text, etc.)
28
- - Get detailed model information with gem scores
29
- - Export results as JSON
30
 
31
- ## Algorithm
32
 
33
- The tool calculates a "gem score" based on:
34
- 1. **Ratio** = likes per 1000 downloads (raw quality signal)
35
- 2. **Age balance** - excludes very new (<14 days) and very old (>2 years) models
36
- 3. **Download sweet spot** - boosts models with 100-10k downloads
37
- 4. **Minimum thresholds** - filters out low-engagement models
38
 
39
- Higher scores indicate models that are loved by the community but haven't gone mainstream yet.
40
 
41
- ## Configuration
 
 
 
 
 
42
 
43
- This Space uses token passthrough - clients authenticate with their own HF tokens.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Hidden Gems - Undervalued HF Models
 
 
 
3
  sdk: docker
4
  app_port: 7860
 
 
5
  ---
6
 
7
+ # 🔍 Hidden Gems Finder
8
 
9
+ An MCP Server that discovers undervalued Hugging Face models with high likes-to-downloads ratios quality models that haven't gone viral yet!
10
 
11
+ ## What are Hidden Gems?
12
 
13
+ The **Hidden Gem Score** = Likes / Downloads
 
 
 
14
 
15
+ - **High ratio** = Loved by the community, but not widely adopted (undervalued!)
16
+ - **Low ratio** = Popular but mainstream (already discovered)
17
 
18
+ This helps you find quality models before they blow up.
 
 
 
 
19
 
20
+ ## MCP Tools
21
 
22
+ ### `find_hidden_gems`
 
 
 
 
23
 
24
+ Search for undervalued models with various filters:
25
 
26
+ **Parameters:**
27
+ - `limit` (int): Models to fetch from API (default: 100)
28
+ - `min_downloads` (int): Filter out brand-new models (default: 100)
29
+ - `top` (int): Number of results to return (default: 20)
30
+ - `pipeline_tag` (str, optional): Filter by type like "text-generation", "image-to-text"
31
+ - `sort_by` (str): Sort by "ratio" (default), "likes", "downloads", or "trending"
32
 
33
+ **Example:** Find text generation gems
34
+ ```json
35
+ {
36
+ "limit": 200,
37
+ "min_downloads": 100,
38
+ "top": 10,
39
+ "pipeline_tag": "text-generation",
40
+ "sort_by": "ratio"
41
+ }
42
+ ```
43
+
44
+ ### `get_model_details`
45
+
46
+ Get detailed information about a specific model:
47
+
48
+ **Parameters:**
49
+ - `model_id` (str): The model ID (e.g., "microsoft/DialoGPT-medium")
50
+
51
+ **Example:**
52
+ ```json
53
+ {"model_id": "openbmb/MiniCPM-SALA"}
54
+ ```
55
+
56
+ ## Usage Examples
57
+
58
+ ### Find Top 20 Hidden Gems
59
+ ```
60
+ find_hidden_gems()
61
+ ```
62
+
63
+ ### Find Text Generation Gems
64
+ ```
65
+ find_hidden_gems(pipeline_tag="text-generation", top=15)
66
+ ```
67
+
68
+ ### Deep Search
69
+ ```
70
+ find_hidden_gems(limit=500, min_downloads=500, top=50, sort_by="likes")
71
+ ```
72
+
73
+ ### Check Specific Model
74
+ ```
75
+ get_model_details("xai-org/grok-1")
76
+ ```
77
+
78
+ ## Connecting to Claude Desktop
79
+
80
+ Add to your `claude_desktop_config.json`:
81
+
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "hidden-gems": {
86
+ "command": "npx",
87
+ "args": ["-y", "mcp-remote", "https://evalstate-hidden-gems.hf.space/mcp"]
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ## Environment Variables
94
+
95
+ - `HF_TOKEN`: Optional Hugging Face token for higher API rate limits
hidden-gems.md CHANGED
@@ -1,65 +1,45 @@
1
  ---
2
  type: agent
3
  name: hidden-gems
4
- default: true
5
- description: |
6
- Find undervalued Hugging Face models with high likes-to-downloads ratio.
7
- Discover hidden gems - quality models that haven't blown up yet.
8
  function_tools:
9
  - hidden_gems_tool.py:find_hidden_gems
10
  - hidden_gems_tool.py:get_model_details
11
- - hidden_gems_tool.py:list_model_types
12
- model: kimi
13
  ---
14
 
15
  # Hidden Gems Finder
16
 
17
- You are a specialized assistant that helps users discover undervalued Hugging Face models. You use the "hidden gems" algorithm to find models with high community engagement relative to their download count.
18
 
19
- ## What Makes a "Hidden Gem"
20
 
21
- A hidden gem is a model where:
22
- - **Likes are high** relative to downloads (quality indicator)
23
- - **Downloads are in the sweet spot** (100-10k) - not too obscure, not too popular
24
- - **Age is balanced** - not brand new, not ancient
25
- - **Community loves it** but it hasn't gone mainstream yet
26
 
27
- ## How to Help Users
28
 
29
- 1. **Find gems** - Use `find_hidden_gems` to search for undervalued models
30
- 2. **Filter by type** - Help users find gems in specific categories (text-generation, image-to-text, etc.)
31
- 3. **Get details** - Use `get_model_details` for deep dives on specific models
32
- 4. **Explain scores** - Interpret gem scores and ratios for users
33
 
34
- ## Gem Score Interpretation
 
 
35
 
36
- - **Ratio**: Likes per 1000 downloads (higher = more loved per download)
37
- - **Gem Score**: Adjusted ratio accounting for age, downloads, and timing
38
- - **Score > 100**: Exceptional quality signal
39
- - **Score 50-100**: Strong hidden gem
40
- - **Score 20-50**: Worth investigating
41
- - **Score < 20**: Decent but more mainstream
42
 
43
- ## Common Model Types
44
 
45
- - `text-generation` - LLMs and text completion models
46
- - `image-to-text` - Vision-language models, OCR
47
- - `text-classification` - Sentiment analysis, topic classification
48
- - `image-classification` - Vision models
49
- - `text-to-image` - Diffusion models, image generation
50
- - `text-to-speech` - TTS models
51
- - `automatic-speech-recognition` - ASR models
52
- - `audio-to-audio` - Audio processing models
53
 
54
  ## Response Style
55
 
56
- Be enthusiastic about discoveries! Present gems in a clear format:
57
-
58
- ```
59
- 💎 Hidden Gem: {model_id}
60
- Score: {gem_score} | Ratio: {ratio} likes/1k downloads
61
- Likes: {likes} | Downloads: {downloads}
62
- Type: {pipeline_tag} | Age: {age_days} days
63
- ```
64
-
65
- Help users understand WHY a model is a gem and what makes it special.
 
1
  ---
2
  type: agent
3
  name: hidden-gems
 
 
 
 
4
  function_tools:
5
  - hidden_gems_tool.py:find_hidden_gems
6
  - hidden_gems_tool.py:get_model_details
7
+ default: true
8
+ description: Discover undervalued Hugging Face models with high likes-to-downloads ratios - hidden gems that haven't gone viral yet
9
  ---
10
 
11
  # Hidden Gems Finder
12
 
13
+ You are a specialized tool for discovering undervalued Hugging Face models. Your purpose is to help users find "hidden gems" - high-quality models that have received significant community appreciation (likes) relative to their download numbers.
14
 
15
+ ## Capabilities
16
 
17
+ 1. **Search for hidden gems** across different categories:
18
+ - Filter by minimum downloads to avoid brand-new models
19
+ - Filter by pipeline tag (text-generation, image-to-image, etc.)
20
+ - Sort by ratio (default), likes, downloads, or trending score
 
21
 
22
+ 2. **Get detailed information** about specific models
23
 
24
+ ## The "Hidden Gem Score"
 
 
 
25
 
26
+ The key metric is **Likes / Downloads ratio**:
27
+ - High ratio = Model is loved by the community but not widely adopted yet
28
+ - Low ratio = Model is popular but mainstream
29
 
30
+ This helps identify quality models before they blow up!
 
 
 
 
 
31
 
32
+ ## Usage Guidelines
33
 
34
+ When users ask about hidden gems:
35
+ 1. Ask if they have a specific category/pipeline in mind
36
+ 2. Suggest appropriate filters based on their needs
37
+ 3. Present results in a clear, ranked format
38
+ 4. Highlight interesting findings with context
 
 
 
39
 
40
  ## Response Style
41
 
42
+ - Be enthusiastic about discoveries
43
+ - Explain why high-ratio models are valuable
44
+ - Suggest next steps (trying the model, checking the model card, etc.)
45
+ - Use data to back up recommendations
 
 
 
 
 
 
hidden_gems_tool.py CHANGED
@@ -1,205 +1,148 @@
1
- """
2
- Hidden Gems Finder Tool
3
 
4
- Finds Hugging Face models with high likes-to-downloads ratio - undervalued
5
- quality models that haven't blown up yet.
6
- """
7
-
8
- import json
9
  import os
10
- import urllib.request
11
- from datetime import datetime
12
- from typing import Any
13
-
14
- HF_API_BASE = "https://huggingface.co/api"
15
-
16
-
17
- def fetch_models(limit: int = 500, model_type: str | None = None) -> list[dict[str, Any]]:
18
- """Fetch models from Hugging Face API."""
19
- url = f"{HF_API_BASE}/models?limit={limit}"
20
- if model_type:
21
- url += f"&filter={model_type}"
22
-
23
- headers = {}
24
- if token := os.environ.get("HF_TOKEN"):
25
- headers["Authorization"] = f"Bearer {token}"
26
-
27
- req = urllib.request.Request(url, headers=headers)
28
- with urllib.request.urlopen(req, timeout=60) as response:
29
- return json.loads(response.read().decode("utf-8"))
30
-
31
-
32
- def parse_date(date_str: str) -> datetime:
33
- """Parse ISO date string."""
34
- return datetime.fromisoformat(date_str.replace("Z", "+00:00"))
35
-
36
-
37
- def calculate_gem_score(model: dict[str, Any]) -> dict[str, Any]:
38
- """Calculate a 'hidden gem' score based on likes-to-downloads ratio."""
39
- downloads = model.get("downloads", 0)
40
- likes = model.get("likes", 0)
41
- created_str = model.get("createdAt", "")
42
-
43
- if not downloads or not likes or not created_str:
44
- return {**model, "gem_score": 0.0, "ratio": 0.0}
45
-
46
- # Calculate raw ratio (likes per 1000 downloads)
47
- ratio = (likes / downloads) * 1000 if downloads > 0 else 0
48
-
49
- # Parse creation date
50
- created_at = parse_date(created_str)
51
- age_days = (datetime.now(datetime.now().astimezone().tzinfo) - created_at).days
52
-
53
- # Penalize very new models (< 14 days)
54
- newness_penalty = max(0, (14 - age_days) / 14) if age_days < 14 else 0
55
-
56
- # Penalize very old models (> 2 years)
57
- age_penalty = max(0, (age_days - 730) / 365) if age_days > 730 else 0
58
-
59
- # Penalize very low downloads (< 100)
60
- download_penalty = max(0, (100 - downloads) / 100) if downloads < 100 else 0
61
-
62
- # Boost for moderate downloads (100-10k)
63
- download_boost = 1.0
64
- if 100 <= downloads <= 10000:
65
- download_boost = 1.2
66
- elif downloads > 100000:
67
- download_boost = 0.7
68
-
69
- # Calculate adjusted gem score
70
- base_score = ratio * download_boost
71
- gem_score = base_score * (1 - newness_penalty) * (1 - age_penalty) * (1 - download_penalty)
72
-
73
- return {
74
- "id": model["id"],
75
- "likes": likes,
76
- "downloads": downloads,
77
- "ratio": round(ratio, 2),
78
- "gem_score": round(gem_score, 2),
79
- "pipeline_tag": model.get("pipeline_tag") or "unknown",
80
- "created_at": created_str,
81
- "age_days": age_days,
82
- "tags": model.get("tags", [])[:5],
83
- }
84
 
85
 
86
  def find_hidden_gems(
87
- limit: int = 500,
88
- top: int = 10,
89
- model_type: str | None = None,
90
- min_likes: int = 10,
91
- min_downloads: int = 50,
92
- max_downloads: int = 500000,
93
  ) -> str:
94
  """
95
- Find hidden gem models on Hugging Face Hub.
96
 
97
  Args:
98
- limit: Number of models to fetch from API (default: 500)
99
- top: Number of top gems to return (default: 10)
100
- model_type: Filter by model type (e.g., 'text-generation', 'image-to-text')
101
- min_likes: Minimum likes required (default: 10)
102
- min_downloads: Minimum downloads required (default: 50)
103
- max_downloads: Maximum downloads to exclude popular models (default: 500000)
104
 
105
  Returns:
106
- JSON string with list of hidden gem models, sorted by gem score
107
  """
 
 
 
 
 
 
 
 
 
 
 
108
  try:
109
- models = fetch_models(limit, model_type)
110
-
111
- # Calculate gem scores
112
- scored_models = [calculate_gem_score(m) for m in models]
113
-
114
- # Filter by criteria
115
- filtered = [
116
- m for m in scored_models
117
- if m["likes"] >= min_likes
118
- and m["downloads"] >= min_downloads
119
- and m["downloads"] <= max_downloads
120
- and m["gem_score"] > 0
121
- ]
122
 
123
- # Sort by gem score (descending)
124
- gems = sorted(filtered, key=lambda x: x["gem_score"], reverse=True)[:top]
 
 
125
 
126
- result = {
127
- "total_analyzed": len(models),
128
- "candidates": len(filtered),
129
- "gems_found": len(gems),
130
- "model_type_filter": model_type,
131
- "gems": gems,
132
- }
133
 
134
- return json.dumps(result, indent=2)
135
-
136
- except Exception as e:
137
- return json.dumps({"error": str(e)}, indent=2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
 
140
  def get_model_details(model_id: str) -> str:
141
  """
142
- Get detailed information about a specific model.
143
 
144
  Args:
145
- model_id: The model ID (e.g., 'microsoft/bitnet-b1.58-2B-4T')
146
 
147
  Returns:
148
- JSON string with model details
149
  """
 
 
 
 
 
 
 
150
  try:
151
- url = f"{HF_API_BASE}/models/{model_id}"
152
-
153
- headers = {}
154
- if token := os.environ.get("HF_TOKEN"):
155
- headers["Authorization"] = f"Bearer {token}"
156
-
157
- req = urllib.request.Request(url, headers=headers)
158
- with urllib.request.urlopen(req, timeout=30) as response:
159
  model = json.loads(response.read().decode("utf-8"))
160
-
161
- scored = calculate_gem_score(model)
162
-
163
- # Add extra details
164
- details = {
165
- **scored,
166
- "library_name": model.get("library_name"),
167
- "config": model.get("config", {}),
168
- "siblings_count": len(model.get("siblings", [])),
169
- "card_data": model.get("cardData", {}),
170
- }
171
-
172
- return json.dumps(details, indent=2)
173
-
174
  except Exception as e:
175
- return json.dumps({"error": str(e)}, indent=2)
176
-
177
-
178
- def list_model_types() -> str:
179
- """
180
- List common model types/pipeline tags available for filtering.
181
 
182
- Returns:
183
- JSON string with list of model types
184
- """
185
- common_types = [
186
- "text-generation",
187
- "text-classification",
188
- "image-to-text",
189
- "image-classification",
190
- "text-to-image",
191
- "text-to-speech",
192
- "automatic-speech-recognition",
193
- "question-answering",
194
- "summarization",
195
- "translation",
196
- "feature-extraction",
197
- "sentence-similarity",
198
- "token-classification",
199
- "zero-shot-classification",
200
- "audio-to-audio",
201
- "video-classification",
202
- "image-to-image",
203
- ]
204
-
205
- return json.dumps({"model_types": common_types}, indent=2)
 
1
+ """Hidden Gems Tool - Find undervalued Hugging Face models."""
 
2
 
 
 
 
 
 
3
  import os
4
+ import json
5
+ from typing import Optional
6
+ from urllib.request import urlopen, Request
7
+ from urllib.error import HTTPError
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
 
10
  def find_hidden_gems(
11
+ limit: int = 100,
12
+ min_downloads: int = 100,
13
+ top: int = 20,
14
+ pipeline_tag: Optional[str] = None,
15
+ sort_by: str = "ratio"
 
16
  ) -> str:
17
  """
18
+ Find hidden gem models with high likes-to-downloads ratios.
19
 
20
  Args:
21
+ limit: Number of models to fetch from API (default: 100)
22
+ min_downloads: Minimum downloads to filter out very new models (default: 100)
23
+ top: Number of top results to return (default: 20)
24
+ pipeline_tag: Filter by pipeline tag like "text-generation", "image-to-text" (optional)
25
+ sort_by: Sort results by "ratio" (default), "likes", "downloads", or "trending"
 
26
 
27
  Returns:
28
+ JSON string with list of hidden gem models ranked by score
29
  """
30
+ # Build API URL
31
+ api_url = f"https://huggingface.co/api/models?limit={limit}"
32
+ if pipeline_tag:
33
+ api_url += f"&pipeline_tag={pipeline_tag}"
34
+
35
+ # Fetch models
36
+ headers = {}
37
+ token = os.environ.get("HF_TOKEN")
38
+ if token:
39
+ headers["Authorization"] = f"Bearer {token}"
40
+
41
  try:
42
+ req = Request(api_url, headers=headers)
43
+ with urlopen(req, timeout=60) as response:
44
+ models = json.loads(response.read().decode("utf-8"))
45
+ except HTTPError as e:
46
+ return json.dumps({"error": f"API error: {e.code} - {e.reason}"})
47
+ except Exception as e:
48
+ return json.dumps({"error": f"Failed to fetch models: {str(e)}"})
49
+
50
+ # Calculate hidden gem scores
51
+ results = []
52
+ for model in models:
53
+ likes = model.get("likes")
54
+ downloads = model.get("downloads")
55
 
56
+ if likes is None or downloads is None:
57
+ continue
58
+ if downloads < min_downloads:
59
+ continue
60
 
61
+ ratio = likes / downloads if downloads > 0 else 0
 
 
 
 
 
 
62
 
63
+ results.append({
64
+ "id": model.get("id"),
65
+ "likes": likes,
66
+ "downloads": downloads,
67
+ "ratio": round(ratio, 6),
68
+ "pipeline_tag": model.get("pipeline_tag") or "unknown",
69
+ "library_name": model.get("library_name") or "unknown",
70
+ "createdAt": model.get("createdAt") or "unknown",
71
+ "trendingScore": model.get("trendingScore") or 0,
72
+ "tags": model.get("tags", [])
73
+ })
74
+
75
+ # Sort results
76
+ sort_key = {
77
+ "likes": "likes",
78
+ "downloads": "downloads",
79
+ "trending": "trendingScore"
80
+ }.get(sort_by, "ratio")
81
+
82
+ results.sort(key=lambda x: x[sort_key], reverse=True)
83
+
84
+ # Take top N
85
+ top_results = results[:top]
86
+
87
+ return json.dumps({
88
+ "count": len(results),
89
+ "showing": len(top_results),
90
+ "filters": {
91
+ "min_downloads": min_downloads,
92
+ "pipeline_tag": pipeline_tag,
93
+ "sort_by": sort_by
94
+ },
95
+ "gems": top_results
96
+ }, indent=2)
97
 
98
 
99
  def get_model_details(model_id: str) -> str:
100
  """
101
+ Get detailed information about a specific Hugging Face model.
102
 
103
  Args:
104
+ model_id: The model ID (e.g., "microsoft/DialoGPT-medium")
105
 
106
  Returns:
107
+ JSON string with detailed model information
108
  """
109
+ api_url = f"https://huggingface.co/api/models/{model_id}"
110
+
111
+ headers = {}
112
+ token = os.environ.get("HF_TOKEN")
113
+ if token:
114
+ headers["Authorization"] = f"Bearer {token}"
115
+
116
  try:
117
+ req = Request(api_url, headers=headers)
118
+ with urlopen(req, timeout=30) as response:
 
 
 
 
 
 
119
  model = json.loads(response.read().decode("utf-8"))
120
+ except HTTPError as e:
121
+ if e.code == 404:
122
+ return json.dumps({"error": f"Model '{model_id}' not found"})
123
+ return json.dumps({"error": f"API error: {e.code} - {e.reason}"})
 
 
 
 
 
 
 
 
 
 
124
  except Exception as e:
125
+ return json.dumps({"error": f"Failed to fetch model: {str(e)}"})
 
 
 
 
 
126
 
127
+ # Calculate hidden gem score
128
+ likes = model.get("likes", 0)
129
+ downloads = model.get("downloads", 0)
130
+ ratio = likes / downloads if downloads > 0 else 0
131
+
132
+ result = {
133
+ "id": model.get("id"),
134
+ "likes": likes,
135
+ "downloads": downloads,
136
+ "hidden_gem_score": round(ratio, 6),
137
+ "pipeline_tag": model.get("pipeline_tag") or "unknown",
138
+ "library_name": model.get("library_name") or "unknown",
139
+ "tags": model.get("tags", []),
140
+ "createdAt": model.get("createdAt"),
141
+ "lastModified": model.get("lastModified"),
142
+ "cardExists": model.get("cardExists", False),
143
+ "widgetData": model.get("widgetData", []),
144
+ "siblings": [s.get("rfilename") for s in model.get("siblings", [])[:10]],
145
+ "description": (model.get("cardData") or {}).get("tags", [])
146
+ }
147
+
148
+ return json.dumps(result, indent=2)