Skip to content

Commit 2b943ba

Browse files
committed
chore: add HF Space demo scaffold and push-to-hub utility (Gradio app, requirements, README); run pre-commit formatting
1 parent 9fc3a53 commit 2b943ba

File tree

4 files changed

+216
-0
lines changed

4 files changed

+216
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Med vLLM Demo (Config-first)
2+
3+
This Space demonstrates loading `MedicalModelConfig` from the Hub and provides a minimal UI for NER / Classification / Generation.
4+
5+
- Loads config from: `Junaidi-AI/med-vllm`
6+
- Uses placeholders for inference so the UI is responsive without heavy models.
7+
- Extend by wiring real inference pipelines or adapters later.
8+
9+
Run locally:
10+
11+
```bash
12+
pip install -r requirements.txt
13+
python app.py
14+
```
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import os
2+
import gradio as gr
3+
4+
from medvllm.medical.config.models.medical_config import MedicalModelConfig
5+
6+
HF_REPO_ID = os.getenv("MEDVLLM_DEMO_REPO", "Junaidi-AI/med-vllm")
7+
8+
9+
def load_config(repo_id: str):
10+
try:
11+
cfg = MedicalModelConfig.from_pretrained(repo_id)
12+
return cfg
13+
except Exception as e:
14+
raise gr.Error(f"Failed to load config from {repo_id}: {e}")
15+
16+
17+
CFG = load_config(HF_REPO_ID)
18+
19+
20+
def run_task(task_type: str, text: str):
21+
# This demo focuses on configuration-driven UX.
22+
# Inference backends can be wired later (e.g., adapters / pipelines).
23+
if not text.strip():
24+
return "", "Please enter text"
25+
26+
if task_type == "ner":
27+
# Placeholder: echo entities based on naive regex matches for demo purposes only.
28+
import re
29+
30+
entities = []
31+
for ent in CFG.medical_entity_types:
32+
# naive token contains match
33+
if re.search(rf"\b{re.escape(ent)}\b", text, flags=re.IGNORECASE):
34+
entities.append({"text": ent, "label": ent.upper(), "start": 0, "end": 0})
35+
highlighted = text
36+
for ent in entities:
37+
highlighted = highlighted.replace(ent["text"], f"[{ent['text']}:{ent['label']}]")
38+
return highlighted, str({"entities": entities})
39+
40+
elif task_type == "classification":
41+
# Placeholder: pick first label if present
42+
labels = CFG.classification_labels or ["positive", "negative"]
43+
return labels[0], f"labels={labels}"
44+
45+
else: # generation
46+
# Placeholder: echo text with a note
47+
return text + "\n\n[generated: demo placeholder]", "generation demo"
48+
49+
50+
def build_ui():
51+
with gr.Blocks(title="Med vLLM Demo (Config-first)") as demo:
52+
gr.Markdown(
53+
"""
54+
# Med vLLM Demo (Config-first)
55+
This Space loads `MedicalModelConfig` from the Hub and demonstrates a simple, config-driven UI.
56+
57+
- Repo: {repo}
58+
- Task: {task}
59+
- Model: {model}
60+
- Version: {ver}
61+
""".format(repo=HF_REPO_ID, task=CFG.task_type, model=CFG.model, ver=CFG.config_version)
62+
)
63+
with gr.Row():
64+
task = gr.Radio(
65+
choices=["ner", "classification", "generation"],
66+
value=CFG.task_type,
67+
label="Task",
68+
)
69+
text = gr.Textbox(label="Input Text", lines=6, placeholder="Enter clinical text...")
70+
with gr.Row():
71+
primary = gr.Button("Run")
72+
with gr.Row():
73+
out_primary = gr.Textbox(label="Primary Output")
74+
out_secondary = gr.Textbox(label="Details")
75+
76+
def _on_click(t, x):
77+
return run_task(t, x)
78+
79+
primary.click(_on_click, inputs=[task, text], outputs=[out_primary, out_secondary])
80+
return demo
81+
82+
83+
if __name__ == "__main__":
84+
demo = build_ui()
85+
demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", 7860)))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
gradio>=4.0.0
2+
medvllm

scripts/hf/push_checkpoints.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Utility to push a local folder (checkpoints, adapters, or artifacts) to the Hugging Face Hub.
4+
5+
Examples:
6+
python scripts/hf/push_checkpoints.py \
7+
--repo-id Junaidi-AI/med-vllm \
8+
--local-path outputs/ner-checkpoint \
9+
--path-in-repo checkpoints/ner-2025-09-23 \
10+
--commit-message "Add NER checkpoint"
11+
12+
If you need to create a PR instead of pushing to main directly, add --create-pr.
13+
To rely on credentials saved via `hf auth login`, do not set HF_TOKEN or unset it for this run.
14+
15+
env -u HF_TOKEN python scripts/hf/push_checkpoints.py ...
16+
"""
17+
18+
from __future__ import annotations
19+
20+
import argparse
21+
import os
22+
import sys
23+
from pathlib import Path
24+
25+
from huggingface_hub import HfApi
26+
27+
28+
def parse_args() -> argparse.Namespace:
29+
p = argparse.ArgumentParser(description="Push a local folder to the Hugging Face Hub")
30+
p.add_argument("--repo-id", required=True, help="Target repo id, e.g. org/name or user/name")
31+
p.add_argument(
32+
"--local-path",
33+
required=True,
34+
help="Local file or folder to upload (can be a single file or a directory)",
35+
)
36+
p.add_argument(
37+
"--path-in-repo",
38+
default=None,
39+
help="Destination path in repo (defaults to same name as local-path)",
40+
)
41+
p.add_argument(
42+
"--repo-type",
43+
default="model",
44+
choices=["model", "dataset", "space"],
45+
help="Repo type on the Hub",
46+
)
47+
p.add_argument(
48+
"--commit-message",
49+
default="Upload artifacts",
50+
help="Commit title / first line",
51+
)
52+
p.add_argument(
53+
"--commit-description",
54+
default=None,
55+
help="Optional commit description (body)",
56+
)
57+
p.add_argument(
58+
"--create-pr",
59+
action="store_true",
60+
help="Upload changes as a Pull Request instead of pushing to the default branch",
61+
)
62+
p.add_argument(
63+
"--revision",
64+
default=None,
65+
help="Optional branch name or refs/pr/X reference to push against",
66+
)
67+
return p.parse_args()
68+
69+
70+
def main() -> int:
71+
args = parse_args()
72+
73+
local_path = Path(args.local_path)
74+
if not local_path.exists():
75+
print(f"[ERROR] Local path does not exist: {local_path}")
76+
return 2
77+
78+
# Respect existing saved credential; avoid env HF_TOKEN overriding by allowing caller to unset it.
79+
api = HfApi()
80+
81+
# Use HfApi.upload_folder for directories, or upload_file for single files
82+
try:
83+
if local_path.is_dir():
84+
commit = api.upload_folder(
85+
repo_id=args.repo_id,
86+
repo_type=args.repo_type,
87+
folder_path=str(local_path),
88+
path_in_repo=(args.path_in_repo or local_path.name),
89+
commit_message=args.commit_message,
90+
commit_description=args.commit_description,
91+
create_pr=args.create_pr,
92+
revision=args.revision,
93+
)
94+
else:
95+
# Single file
96+
commit = api.upload_file(
97+
repo_id=args.repo_id,
98+
repo_type=args.repo_type,
99+
path_or_fileobj=str(local_path),
100+
path_in_repo=(args.path_in_repo or local_path.name),
101+
commit_message=args.commit_message,
102+
commit_description=args.commit_description,
103+
create_pr=args.create_pr,
104+
revision=args.revision,
105+
)
106+
except Exception as e:
107+
print(f"[ERROR] Upload failed: {e}")
108+
return 3
109+
110+
print(commit)
111+
return 0
112+
113+
114+
if __name__ == "__main__":
115+
raise SystemExit(main())

0 commit comments

Comments
 (0)