Spaces:
Running
Running
File size: 10,196 Bytes
9ce984a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
from __future__ import annotations
import argparse
import os
from typing import Any, Dict, List
import gradio as gr
from mcp_server.tools.list_items import list_items as tool_list_items
from mcp_server.tools.semantic_search import semantic_search as tool_semantic_search
from mcp_server.tools.get_code import get_code as tool_get_code
def create_gradio_blocks() -> gr.Blocks:
"""
Build a Gradio UI that, when launched with mcp_server=True, exposes a remote MCP server
at: http://<host>:<port>/gradio_api/mcp/sse
Tools exposed (via function signatures and docstrings):
- list_items()
- semantic_search(problem_markdown: str)
- get_code(path: str)
Polished UI/UX:
- Themed interfaces, custom CSS, clear titles and descriptions
- Curated examples for Semantic Search and Get Code
- Helpful hero/guide text on the List Items tab
"""
# Lightweight custom CSS for a more polished look
custom_css = """
:root {
--radius-md: 12px;
--shadow-md: 0 6px 24px rgba(0,0,0,.08);
--color-accent: #3B82F6; /* Blue 500 */
--color-accent-hover: #2563EB; /* Blue 600 */
--color-accent-soft: rgba(59,130,246,.15);
--link-text-color: #3B82F6;
}
.gradio-container { max-width: 1120px !important; margin: 0 auto; }
/* Buttons and controls -> blue accent */
.gr-button {
border-radius: 12px;
box-shadow: var(--shadow-md);
background: var(--color-accent) !important;
color: #fff !important;
border: 1px solid transparent !important;
}
.gr-button:hover { background: var(--color-accent-hover) !important; }
.gr-button:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* Tabs -> blue accent on active/hover */
.gr-tabs .tab-nav button[aria-selected="true"] {
border-bottom: 2px solid var(--color-accent) !important;
color: var(--color-accent) !important;
}
.gr-tabs .tab-nav button:hover { color: var(--color-accent) !important; }
/* Examples (chips/buttons) */
.gr-examples button, .examples button {
border-color: var(--color-accent) !important;
color: var(--color-accent) !important;
background: transparent !important;
}
.gr-examples button:hover, .examples button:hover {
background: var(--color-accent-soft) !important;
}
.gr-textbox textarea {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
h1, .prose h1 {
background: linear-gradient(90deg, #60A5FA, #22D3EE, #1D4ED8);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
a, .prose a { color: var(--link-text-color) !important; }
.prose p, .prose li { font-size: 15px; line-height: 1.65; }
"""
def list_items() -> List[Dict[str, Any]]:
"""
List all knowledge base items.
Returns:
A JSON-serializable list of items, each with:
id (str): '<category>/<filename>.py'
category (str)
filename (str)
path (str): 'knowledge_base/<category>/<filename>.py'
summary (str): Docstring or first non-empty line
"""
return tool_list_items()
def semantic_search(problem_markdown: str) -> Dict[str, Any]:
"""
Semantic search over the knowledge base.
Args:
problem_markdown: Markdown text describing the task/problem.
Returns:
{
"best_match": {
"id": str,
"category": str,
"filename": str,
"path": str,
"summary": str
},
"score": float # cosine similarity in [-1, 1]
}
"""
return tool_semantic_search(problem_markdown)
def get_code(path: str) -> str:
"""
Return the full Python source code for a KB file.
Args:
path: Either 'knowledge_base/<category>/<file>.py' or '<category>/<file>.py'
Returns:
UTF-8 Python source as a string.
"""
return tool_get_code(path)
# Curated examples for a smoother first-run UX
search_examples = [
"I want to fine-tune a transformer for sentiment classification.",
"Train a GNN on citation networks for node classification.",
"Image generation with GANs; how to stabilize training?",
]
code_examples = [
"knowledge_base/nlp/text_classification_with_transformer.py",
"knowledge_base/graph/gnn_citations.py",
"knowledge_base/generative/dcgan_overriding_train_step.py",
]
hero_md = """
# β‘οΈ ML Starter: Your ML Launchpad
## **Starting an ML project and overwhelmed by where to begin?**
## **LLMs not specialized enough for your domain?**
## **Need real, reusable code instead of vague suggestions?**
### **Describe your problem β get the top-ranked match β pull the exact code file.**
---
### π₯ Why you'll love it
- π― **Problem-to-code in one flow** β search semantically, explore context, and download source.
- π§ **Domain-tuned knowledge** β embeddings built over curated ML projects across vision, NLP, audio, structured data, and more.
- π€ **Automation ready** β the same tools power IDEs/agents via MCP over SSE.
### π What you can do
- π **Browse Items** β scan the entire library with instant summaries.
- π **Semantic Search** β paste your challenge and get the closest-fit recipe plus similarity score.
- π» **Get Code** β drop in the path and copy the full Python implementation.
### π Under the hood
- Sentence-transformer embeddings + cosine similarity for precise retrieval.
- Rich metadata (id, category, path, summary) for fast filtering.
- Remote MCP endpoint at `/gradio_api/mcp/sse` exposing `list_items()`, `semantic_search()`, `get_code()`.
### β± Quickstart
1. Head to βπ Semantic Searchβ, describe what you're building, and submit.
2. Copy the suggested path from the results.
3. Open βπ» Get Codeβ, paste the path, and grab the exact source.
4. Want the big picture first? Start with βπ Browse Itemsβ.
### π‘ Power tip
Run locally or on Spaces, then connect any MCP-compatible client to orchestrate the same workflow programmatically.
"""
search_article = """
π§ How to use
1) Describe your task with as much signal as possible (dataset, modality, constraints, target metric).
2) Click Submit or pick an example. We compute embeddings and retrieve the closest KB match.
3) Copy the 'path' value and open it in the βπ» Get Codeβ tab to view the full source.
π§ Notes
- Markdown is supported. Bullet points and short snippets help a lot.
- Similarity uses cosine distance on L2βnormalized sentence-transformer embeddings.
"""
code_article = """
Paste a valid knowledge base path to fetch the full Python source.
π Examples
- knowledge_base/nlp/text_classification_with_transformer.py
- nlp/text_classification_with_transformer.py
π‘ Tips
- Accepts both absolute KB paths and '<category>/<file>.py'.
- The code block is copy-friendly for quick reuse.
"""
list_ui = gr.Interface(
fn=list_items,
inputs=None,
outputs=gr.JSON(label="π¦ Items (JSON)"),
title="π Browse Items",
description="Explore every ML Starter KB entry β id, category, path, and summary.",
article="",
)
search_ui = gr.Interface(
fn=semantic_search,
inputs=gr.Textbox(
lines=10,
label="βοΈ Describe your problem (Markdown supported)",
placeholder="e.g., Fine-tune a transformer for sentiment classification on IMDB (dataset, goal, constraints)"
),
outputs=gr.JSON(label="π Best match + similarity score"),
title="π Semantic Search",
description="Paste your task. We compute embeddings and return the closest KB recipe with a score.",
examples=search_examples,
article=search_article,
)
code_ui = gr.Interface(
fn=get_code,
inputs=gr.Textbox(
lines=1,
label="π KB file path",
placeholder="knowledge_base/nlp/text_classification_with_transformer.py"
),
outputs=gr.Code(label="π§© Python source", language="python"),
title="π» Get Code",
description="Paste a KB path and copy the exact source into your project.",
examples=code_examples,
article=code_article,
)
# Compose top-level layout: explanation on top, tabs below
with gr.Blocks() as blocks:
gr.HTML(f"<style>{custom_css}</style>")
gr.Markdown(hero_md)
with gr.Tabs():
with gr.Tab("π List Items"):
list_ui.render()
with gr.Tab("π Semantic Search"):
search_ui.render()
with gr.Tab("π» Get Code"):
code_ui.render()
return blocks
def main() -> None:
"""
Entry point: Launch Gradio UI and expose remote MCP over SSE at /gradio_api/mcp/sse
"""
parser = argparse.ArgumentParser(description="ML Starter MCP Server (Gradio Remote Only)")
parser.add_argument("--host", default="127.0.0.1", help="Host for Gradio")
parser.add_argument("--port", type=int, default=7860, help="Port for Gradio")
args = parser.parse_args()
# Derive host/port from environment for Hugging Face Spaces and containers
env_host = os.getenv("GRADIO_SERVER_NAME") or os.getenv("HOST") or args.host
env_port_str = os.getenv("GRADIO_SERVER_PORT") or os.getenv("PORT")
env_port = int(env_port_str) if env_port_str and env_port_str.isdigit() else args.port
# If running on HF Spaces, bind to 0.0.0.0 unless explicitly overridden
if os.getenv("SPACE_ID") and env_host in ("127.0.0.1", "localhost"):
env_host = "0.0.0.0"
blocks = create_gradio_blocks()
blocks.launch(server_name=env_host, server_port=env_port, mcp_server=True)
if __name__ == "__main__":
main() |