Spaces:
Running
Running
✨ Submission handling and integration of results into results dataset added
Browse files- .gitignore +1 -0
- app.py +3 -1
- backend/api.py +47 -0
- backend/data_loader.py +21 -5
- backend/dataset_storage.py +66 -0
- backend/evaluator.py +52 -0
- backend/schema.py +63 -0
- backend/submission.py +70 -0
- frontend/content.py +39 -4
- frontend/layout.py +46 -23
- frontend/submission.py +36 -0
- raw_predictions.json +75 -0
.gitignore
CHANGED
|
@@ -12,3 +12,4 @@ eval-results/
|
|
| 12 |
eval-queue-bk/
|
| 13 |
eval-results-bk/
|
| 14 |
logs/
|
|
|
|
|
|
| 12 |
eval-queue-bk/
|
| 13 |
eval-results-bk/
|
| 14 |
logs/
|
| 15 |
+
debugging_and_dev/
|
app.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
| 1 |
from frontend.layout import create_main_interface
|
| 2 |
from frontend.leaderboard import refresh_leaderboard
|
|
|
|
| 3 |
|
| 4 |
# Create the main interface with callbacks
|
| 5 |
demo, leaderboard_table = create_main_interface(
|
| 6 |
-
refresh_callback=refresh_leaderboard
|
|
|
|
| 7 |
)
|
| 8 |
|
| 9 |
if __name__ == "__main__":
|
|
|
|
| 1 |
from frontend.layout import create_main_interface
|
| 2 |
from frontend.leaderboard import refresh_leaderboard
|
| 3 |
+
from frontend.submission import handle_submission
|
| 4 |
|
| 5 |
# Create the main interface with callbacks
|
| 6 |
demo, leaderboard_table = create_main_interface(
|
| 7 |
+
refresh_callback=refresh_leaderboard,
|
| 8 |
+
submit_callback=handle_submission
|
| 9 |
)
|
| 10 |
|
| 11 |
if __name__ == "__main__":
|
backend/api.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
from typing import Dict, Any
|
| 3 |
+
from .submission import process_submission
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
# Sample data - replace with actual data loading
|
| 7 |
+
SAMPLE_SMILES = ["CCO", "c1ccccc1", "CC(=O)Cl", "C1=CC=CN=C1"]
|
| 8 |
+
SAMPLE_LABELS = {
|
| 9 |
+
"CCO": {"NR-AR": 0.1, "NR-AR-LBD": 0.2},
|
| 10 |
+
"c1ccccc1": {"NR-AR": 0.3, "NR-AR-LBD": 0.4},
|
| 11 |
+
"CC(=O)Cl": {"NR-AR": 0.5, "NR-AR-LBD": 0.6},
|
| 12 |
+
"C1=CC=CN=C1": {"NR-AR": 0.7, "NR-AR-LBD": 0.8}
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
async def submit_model(
|
| 17 |
+
model_name: str,
|
| 18 |
+
hf_space_tag: str,
|
| 19 |
+
model_description: str,
|
| 20 |
+
publication_title: str = "",
|
| 21 |
+
publication_link: str = "",
|
| 22 |
+
model_size: str = "",
|
| 23 |
+
pretraining: str = "",
|
| 24 |
+
organization: str = ""
|
| 25 |
+
) -> Dict[str, Any]:
|
| 26 |
+
"""API endpoint for model submission."""
|
| 27 |
+
|
| 28 |
+
record = await process_submission(
|
| 29 |
+
model_name=model_name,
|
| 30 |
+
hf_space_tag=hf_space_tag,
|
| 31 |
+
model_description=model_description,
|
| 32 |
+
publication_title=publication_title,
|
| 33 |
+
publication_link=publication_link,
|
| 34 |
+
model_size=model_size,
|
| 35 |
+
pretraining=pretraining,
|
| 36 |
+
organization=organization,
|
| 37 |
+
smiles_list=SAMPLE_SMILES,
|
| 38 |
+
true_labels=SAMPLE_LABELS
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
return record
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def get_submission_status(submission_id: str) -> Dict[str, str]:
|
| 45 |
+
"""Get the status of a submission."""
|
| 46 |
+
# Placeholder - implement proper status tracking
|
| 47 |
+
return {"status": "completed", "message": "Submission processed successfully"}
|
backend/data_loader.py
CHANGED
|
@@ -28,19 +28,23 @@ def load_leaderboard_data() -> pd.DataFrame:
|
|
| 28 |
else:
|
| 29 |
raise ValueError("Dataset does not contain a 'test' split.")
|
| 30 |
|
| 31 |
-
# Convert to DataFrame
|
| 32 |
rows = []
|
| 33 |
for entry in results_data:
|
| 34 |
config = entry['config']
|
| 35 |
results = entry['results']
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
# Create a row with all the data
|
| 38 |
row = {
|
| 39 |
'Model': config['model_name'],
|
| 40 |
-
'Model Description': config['model_description'],
|
| 41 |
-
'Publication': config
|
| 42 |
-
'Parameters': config
|
| 43 |
-
'Date Added': str(config
|
| 44 |
'Overall Score': results['overall_score']['roc_auc']
|
| 45 |
}
|
| 46 |
|
|
@@ -52,6 +56,18 @@ def load_leaderboard_data() -> pd.DataFrame:
|
|
| 52 |
rows.append(row)
|
| 53 |
|
| 54 |
df = pd.DataFrame(rows)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
print(df)
|
| 56 |
print(f"Created DataFrame with shape: {df.shape}")
|
| 57 |
return df
|
|
|
|
| 28 |
else:
|
| 29 |
raise ValueError("Dataset does not contain a 'test' split.")
|
| 30 |
|
| 31 |
+
# Convert to DataFrame (new schema only)
|
| 32 |
rows = []
|
| 33 |
for entry in results_data:
|
| 34 |
config = entry['config']
|
| 35 |
results = entry['results']
|
| 36 |
|
| 37 |
+
# Only include approved entries
|
| 38 |
+
if not config.get('approved', False):
|
| 39 |
+
continue
|
| 40 |
+
|
| 41 |
# Create a row with all the data
|
| 42 |
row = {
|
| 43 |
'Model': config['model_name'],
|
| 44 |
+
'Model Description': config['model_description'],
|
| 45 |
+
'Publication': config.get('publication_title', ''),
|
| 46 |
+
'Parameters': config.get('model_size', ''),
|
| 47 |
+
'Date Added': str(config.get('date_approved', config.get('date_submitted', ''))).split()[0],
|
| 48 |
'Overall Score': results['overall_score']['roc_auc']
|
| 49 |
}
|
| 50 |
|
|
|
|
| 56 |
rows.append(row)
|
| 57 |
|
| 58 |
df = pd.DataFrame(rows)
|
| 59 |
+
|
| 60 |
+
# Handle empty dataset case
|
| 61 |
+
if df.empty:
|
| 62 |
+
print("No approved submissions found. Creating empty DataFrame with proper columns.")
|
| 63 |
+
# Create empty DataFrame with expected columns
|
| 64 |
+
columns = [
|
| 65 |
+
'Model', 'Model Description', 'Publication', 'Parameters', 'Date Added', 'Overall Score',
|
| 66 |
+
'NR-AR', 'NR-AR-LBD', 'NR-AhR', 'NR-Aromatase', 'NR-ER', 'NR-ER-LBD',
|
| 67 |
+
'NR-PPAR-gamma', 'SR-ARE', 'SR-ATAD5', 'SR-HSE', 'SR-MMP', 'SR-p53'
|
| 68 |
+
]
|
| 69 |
+
df = pd.DataFrame(columns=columns)
|
| 70 |
+
|
| 71 |
print(df)
|
| 72 |
print(f"Created DataFrame with shape: {df.shape}")
|
| 73 |
return df
|
backend/dataset_storage.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datasets import Dataset, load_dataset
|
| 2 |
+
from typing import Dict, Any, List
|
| 3 |
+
from config.settings import RESULTS_DATASET, HF_TOKEN
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def save_submission_to_dataset(submission_record: Dict[str, Any]) -> None:
|
| 7 |
+
"""Save a submission record to the HuggingFace dataset."""
|
| 8 |
+
|
| 9 |
+
# Load existing dataset or create new one
|
| 10 |
+
try:
|
| 11 |
+
dataset = load_dataset(RESULTS_DATASET, token=HF_TOKEN, split="test")
|
| 12 |
+
existing_data = list(dataset)
|
| 13 |
+
except:
|
| 14 |
+
# Dataset doesn't exist or is empty, start fresh
|
| 15 |
+
existing_data = []
|
| 16 |
+
|
| 17 |
+
# Add new submission
|
| 18 |
+
existing_data.append(submission_record)
|
| 19 |
+
|
| 20 |
+
# Create new dataset
|
| 21 |
+
new_dataset = Dataset.from_list(existing_data)
|
| 22 |
+
|
| 23 |
+
# Push to HuggingFace
|
| 24 |
+
new_dataset.push_to_hub(
|
| 25 |
+
RESULTS_DATASET,
|
| 26 |
+
token=HF_TOKEN,
|
| 27 |
+
split="test"
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def get_all_submissions(approved_only: bool = False) -> List[Dict[str, Any]]:
|
| 32 |
+
"""Get all submissions from the dataset."""
|
| 33 |
+
|
| 34 |
+
dataset = load_dataset(RESULTS_DATASET, token=HF_TOKEN, split="test")
|
| 35 |
+
submissions = list(dataset)
|
| 36 |
+
|
| 37 |
+
if approved_only:
|
| 38 |
+
submissions = [s for s in submissions if s["config"]["approved"]]
|
| 39 |
+
|
| 40 |
+
return submissions
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def update_submission_approval(model_name: str, hf_space_tag: str, approved: bool) -> None:
|
| 44 |
+
"""Update the approval status of a submission."""
|
| 45 |
+
|
| 46 |
+
# Load dataset
|
| 47 |
+
dataset = load_dataset(RESULTS_DATASET, token=HF_TOKEN, split="test")
|
| 48 |
+
data = list(dataset)
|
| 49 |
+
|
| 50 |
+
# Find and update the submission
|
| 51 |
+
for submission in data:
|
| 52 |
+
config = submission["config"]
|
| 53 |
+
if config["model_name"] == model_name and config["hf_space_tag"] == hf_space_tag:
|
| 54 |
+
config["approved"] = approved
|
| 55 |
+
if approved:
|
| 56 |
+
from datetime import datetime
|
| 57 |
+
config["date_approved"] = datetime.now().isoformat()
|
| 58 |
+
break
|
| 59 |
+
|
| 60 |
+
# Save updated dataset
|
| 61 |
+
updated_dataset = Dataset.from_list(data)
|
| 62 |
+
updated_dataset.push_to_hub(
|
| 63 |
+
RESULTS_DATASET,
|
| 64 |
+
token=HF_TOKEN,
|
| 65 |
+
split="test"
|
| 66 |
+
)
|
backend/evaluator.py
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import httpx
|
| 3 |
+
from typing import List, Dict, Any
|
| 4 |
+
|
| 5 |
+
BATCH_SIZE = 1000
|
| 6 |
+
TIMEOUT_S = 600
|
| 7 |
+
MAX_RETRIES = 3
|
| 8 |
+
RETRY_DELAY = 1
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def chunks(xs: List[str], n: int):
|
| 12 |
+
for i in range(0, len(xs), n):
|
| 13 |
+
yield xs[i:i+n]
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
async def fetch_metadata(client: httpx.AsyncClient, base_url: str) -> Dict[str, Any]:
|
| 17 |
+
for attempt in range(MAX_RETRIES):
|
| 18 |
+
r = await client.get(f"{base_url}/metadata", timeout=30)
|
| 19 |
+
r.raise_for_status()
|
| 20 |
+
return r.json()
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
async def call_predict(client: httpx.AsyncClient, base_url: str, smiles_batch: List[str]) -> Dict[str, Any]:
|
| 24 |
+
for attempt in range(MAX_RETRIES):
|
| 25 |
+
r = await client.post(
|
| 26 |
+
f"{base_url}/predict",
|
| 27 |
+
json={"smiles": smiles_batch},
|
| 28 |
+
timeout=TIMEOUT_S,
|
| 29 |
+
)
|
| 30 |
+
r.raise_for_status()
|
| 31 |
+
return r.json()
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
async def evaluate_model(hf_space_tag: str, smiles_list: List[str]) -> Dict[str, Any]:
|
| 35 |
+
# Convert username/space-name to username-space-name.hf.space
|
| 36 |
+
base_url = f"https://{hf_space_tag.replace('/', '-').replace('_', '-').lower()}.hf.space"
|
| 37 |
+
results = []
|
| 38 |
+
|
| 39 |
+
async with httpx.AsyncClient() as client:
|
| 40 |
+
meta = await fetch_metadata(client, base_url)
|
| 41 |
+
max_bs = min(meta.get("max_batch_size", BATCH_SIZE), BATCH_SIZE)
|
| 42 |
+
|
| 43 |
+
for batch in chunks(smiles_list, max_bs):
|
| 44 |
+
resp = await call_predict(client, base_url, batch)
|
| 45 |
+
predictions_dict = resp["predictions"]
|
| 46 |
+
for smiles in batch:
|
| 47 |
+
if smiles in predictions_dict:
|
| 48 |
+
results.append({"smiles": smiles, "raw_predictions": predictions_dict[smiles]})
|
| 49 |
+
else:
|
| 50 |
+
results.append({"smiles": smiles, "raw_predictions": {}, "error": "No prediction found"})
|
| 51 |
+
|
| 52 |
+
return {"results": results, "metadata": meta}
|
backend/schema.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime
|
| 2 |
+
from typing import Dict, List, Any, Optional
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def create_submission_record(
|
| 6 |
+
model_name: str,
|
| 7 |
+
hf_space_tag: str,
|
| 8 |
+
model_description: str,
|
| 9 |
+
publication_title: str,
|
| 10 |
+
publication_link: str,
|
| 11 |
+
model_size: str,
|
| 12 |
+
pretraining: str,
|
| 13 |
+
organization: str,
|
| 14 |
+
raw_predictions: List[Dict[str, Any]],
|
| 15 |
+
computed_metrics: Dict[str, Any],
|
| 16 |
+
status: str = "completed",
|
| 17 |
+
approved: bool = False
|
| 18 |
+
) -> Dict[str, Any]:
|
| 19 |
+
"""Create a standardized submission record for the HuggingFace dataset."""
|
| 20 |
+
|
| 21 |
+
now = datetime.now().isoformat()
|
| 22 |
+
|
| 23 |
+
return {
|
| 24 |
+
"config": {
|
| 25 |
+
"model_name": model_name,
|
| 26 |
+
"hf_space_tag": hf_space_tag,
|
| 27 |
+
"model_description": model_description,
|
| 28 |
+
"publication_title": publication_title,
|
| 29 |
+
"publication_link": publication_link,
|
| 30 |
+
"model_size": model_size,
|
| 31 |
+
"pretraining": pretraining,
|
| 32 |
+
"organization": organization,
|
| 33 |
+
"date_submitted": now,
|
| 34 |
+
"date_approved": None,
|
| 35 |
+
"status": status,
|
| 36 |
+
"approved": approved
|
| 37 |
+
},
|
| 38 |
+
"raw_predictions": raw_predictions,
|
| 39 |
+
"results": computed_metrics
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def get_dataset_schema() -> Dict[str, Any]:
|
| 44 |
+
"""Return the HuggingFace dataset schema."""
|
| 45 |
+
|
| 46 |
+
return {
|
| 47 |
+
"config": {
|
| 48 |
+
"model_name": "string",
|
| 49 |
+
"hf_space_tag": "string",
|
| 50 |
+
"model_description": "string",
|
| 51 |
+
"publication_title": "string",
|
| 52 |
+
"publication_link": "string",
|
| 53 |
+
"model_size": "string",
|
| 54 |
+
"pretraining": "string",
|
| 55 |
+
"organization": "string",
|
| 56 |
+
"date_submitted": "string",
|
| 57 |
+
"date_approved": "string",
|
| 58 |
+
"status": "string",
|
| 59 |
+
"approved": "bool"
|
| 60 |
+
},
|
| 61 |
+
"raw_predictions": "list",
|
| 62 |
+
"results": "dict"
|
| 63 |
+
}
|
backend/submission.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
from typing import Dict, Any, List
|
| 3 |
+
from .evaluator import evaluate_model
|
| 4 |
+
from .schema import create_submission_record
|
| 5 |
+
from .dataset_storage import save_submission_to_dataset
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
async def process_submission(
|
| 9 |
+
model_name: str,
|
| 10 |
+
hf_space_tag: str,
|
| 11 |
+
model_description: str,
|
| 12 |
+
publication_title: str,
|
| 13 |
+
publication_link: str,
|
| 14 |
+
model_size: str,
|
| 15 |
+
pretraining: str,
|
| 16 |
+
organization: str,
|
| 17 |
+
smiles_list: List[str],
|
| 18 |
+
true_labels: Dict[str, Dict[str, float]]
|
| 19 |
+
) -> Dict[str, Any]:
|
| 20 |
+
"""Process a complete submission from evaluation to metrics computation."""
|
| 21 |
+
|
| 22 |
+
# Step 1: Evaluate the model
|
| 23 |
+
evaluation_result = await evaluate_model(hf_space_tag, smiles_list)
|
| 24 |
+
|
| 25 |
+
# Step 2: Compute metrics
|
| 26 |
+
metrics = compute_metrics(evaluation_result["results"], true_labels)
|
| 27 |
+
|
| 28 |
+
# Step 3: Create the submission record
|
| 29 |
+
record = create_submission_record(
|
| 30 |
+
model_name=model_name,
|
| 31 |
+
hf_space_tag=hf_space_tag,
|
| 32 |
+
model_description=model_description,
|
| 33 |
+
publication_title=publication_title,
|
| 34 |
+
publication_link=publication_link,
|
| 35 |
+
model_size=model_size,
|
| 36 |
+
pretraining=pretraining,
|
| 37 |
+
organization=organization,
|
| 38 |
+
raw_predictions=evaluation_result["results"],
|
| 39 |
+
computed_metrics=metrics,
|
| 40 |
+
status="completed",
|
| 41 |
+
approved=False
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
# Step 4: Save to HuggingFace dataset
|
| 45 |
+
save_submission_to_dataset(record)
|
| 46 |
+
|
| 47 |
+
return record
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def compute_metrics(predictions: List[Dict[str, Any]], true_labels: Dict[str, Dict[str, float]]) -> Dict[str, Any]:
|
| 51 |
+
"""Compute evaluation metrics from predictions and true labels."""
|
| 52 |
+
|
| 53 |
+
# Simple placeholder - you'll want to implement proper ROC-AUC computation
|
| 54 |
+
task_metrics = {}
|
| 55 |
+
|
| 56 |
+
# Get all unique tasks from predictions
|
| 57 |
+
if predictions:
|
| 58 |
+
first_pred = predictions[0].get("raw_predictions", {})
|
| 59 |
+
tasks = list(first_pred.keys())
|
| 60 |
+
|
| 61 |
+
for task in tasks:
|
| 62 |
+
# Placeholder metric computation
|
| 63 |
+
task_metrics[task] = {"roc_auc": 0.75} # Replace with real computation
|
| 64 |
+
|
| 65 |
+
# Overall score (average of task scores)
|
| 66 |
+
if task_metrics:
|
| 67 |
+
overall_score = sum(m["roc_auc"] for m in task_metrics.values()) / len(task_metrics)
|
| 68 |
+
task_metrics["overall_score"] = {"roc_auc": overall_score}
|
| 69 |
+
|
| 70 |
+
return task_metrics
|
frontend/content.py
CHANGED
|
@@ -73,13 +73,48 @@ class SubmissionContent:
|
|
| 73 |
|
| 74 |
title = "🚀 Submit Your Model"
|
| 75 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
@staticmethod
|
| 77 |
def get_instructions_html() -> str:
|
| 78 |
"""Generate submission instructions HTML"""
|
| 79 |
-
return
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
"""
|
| 84 |
|
| 85 |
|
|
|
|
| 73 |
|
| 74 |
title = "🚀 Submit Your Model"
|
| 75 |
|
| 76 |
+
form_labels = {
|
| 77 |
+
"model_name": "Model Name *",
|
| 78 |
+
"hf_space_tag": "HuggingFace Space Tag *",
|
| 79 |
+
"model_description": "Model Description *",
|
| 80 |
+
"organization": "Organization",
|
| 81 |
+
"model_size": "Model Size",
|
| 82 |
+
"pretraining": "Pretraining Details",
|
| 83 |
+
"publication_title": "Publication Title",
|
| 84 |
+
"publication_link": "Publication Link"
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
form_placeholders = {
|
| 88 |
+
"model_name": "e.g., AwesomeTox",
|
| 89 |
+
"hf_space_tag": "e.g., username/model-name",
|
| 90 |
+
"model_description": "Brief description of your model architecture and approach...",
|
| 91 |
+
"organization": "e.g., University of Example",
|
| 92 |
+
"model_size": "e.g., 150M parameters",
|
| 93 |
+
"pretraining": "e.g., ChEMBL 29, ZINC-15",
|
| 94 |
+
"publication_title": "Title of associated paper",
|
| 95 |
+
"publication_link": "https://arxiv.org/abs/..."
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
form_info = {
|
| 99 |
+
"model_name": "A short, descriptive name for your model",
|
| 100 |
+
"hf_space_tag": "Your HuggingFace space in format: username/space-name",
|
| 101 |
+
"model_description": "Describe your model, methodology, and key features"
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
@staticmethod
|
| 105 |
def get_instructions_html() -> str:
|
| 106 |
"""Generate submission instructions HTML"""
|
| 107 |
+
return """
|
| 108 |
+
<div class="instructions-section">
|
| 109 |
+
<p>Submit your HuggingFace space for evaluation on the Tox21 benchmark.</p>
|
| 110 |
+
<p><strong>Requirements:</strong></p>
|
| 111 |
+
<ul>
|
| 112 |
+
<li>Your space must implement <code>/metadata</code> and <code>/predict</code> endpoints</li>
|
| 113 |
+
<li>The <code>/predict</code> endpoint should accept a JSON payload with <code>{"smiles": [list_of_smiles]}</code></li>
|
| 114 |
+
<li>Response should be <code>{"predictions": {smiles: {task: score, ...}, ...}}</code></li>
|
| 115 |
+
</ul>
|
| 116 |
+
<p><em>* Required fields</em></p>
|
| 117 |
+
</div>
|
| 118 |
"""
|
| 119 |
|
| 120 |
|
frontend/layout.py
CHANGED
|
@@ -66,46 +66,69 @@ def create_submission_tab(submit_callback: Callable = None) -> gr.TabItem:
|
|
| 66 |
# Instructions
|
| 67 |
instructions_html = gr.HTML(SubmissionContent.get_instructions_html())
|
| 68 |
|
| 69 |
-
#
|
| 70 |
-
"""
|
| 71 |
with gr.Group():
|
|
|
|
| 72 |
model_name = gr.Textbox(
|
| 73 |
label=SubmissionContent.form_labels["model_name"],
|
| 74 |
-
placeholder=SubmissionContent.form_placeholders["model_name"]
|
|
|
|
| 75 |
)
|
| 76 |
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
)
|
| 83 |
-
model_type = gr.Dropdown(
|
| 84 |
-
choices=SubmissionContent.model_types,
|
| 85 |
-
label=SubmissionContent.form_labels["model_type"]
|
| 86 |
-
)
|
| 87 |
-
precision = gr.Dropdown(
|
| 88 |
-
choices=SubmissionContent.precisions,
|
| 89 |
-
label=SubmissionContent.form_labels["precision"],
|
| 90 |
-
value="float16"
|
| 91 |
-
)
|
| 92 |
|
| 93 |
-
|
| 94 |
-
label=SubmissionContent.form_labels["
|
| 95 |
-
placeholder=SubmissionContent.form_placeholders["
|
|
|
|
| 96 |
lines=3
|
| 97 |
)
|
| 98 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
submit_btn = gr.Button("Submit Model", variant="primary")
|
| 100 |
result_msg = gr.HTML()
|
| 101 |
|
| 102 |
if submit_callback:
|
| 103 |
submit_btn.click(
|
| 104 |
fn=submit_callback,
|
| 105 |
-
inputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
outputs=result_msg
|
| 107 |
)
|
| 108 |
-
"""
|
| 109 |
|
| 110 |
return tab
|
| 111 |
|
|
|
|
| 66 |
# Instructions
|
| 67 |
instructions_html = gr.HTML(SubmissionContent.get_instructions_html())
|
| 68 |
|
| 69 |
+
# Submission form
|
|
|
|
| 70 |
with gr.Group():
|
| 71 |
+
# Required fields
|
| 72 |
model_name = gr.Textbox(
|
| 73 |
label=SubmissionContent.form_labels["model_name"],
|
| 74 |
+
placeholder=SubmissionContent.form_placeholders["model_name"],
|
| 75 |
+
info=SubmissionContent.form_info["model_name"]
|
| 76 |
)
|
| 77 |
|
| 78 |
+
hf_space_tag = gr.Textbox(
|
| 79 |
+
label=SubmissionContent.form_labels["hf_space_tag"],
|
| 80 |
+
placeholder=SubmissionContent.form_placeholders["hf_space_tag"],
|
| 81 |
+
info=SubmissionContent.form_info["hf_space_tag"]
|
| 82 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
+
model_description = gr.Textbox(
|
| 85 |
+
label=SubmissionContent.form_labels["model_description"],
|
| 86 |
+
placeholder=SubmissionContent.form_placeholders["model_description"],
|
| 87 |
+
info=SubmissionContent.form_info["model_description"],
|
| 88 |
lines=3
|
| 89 |
)
|
| 90 |
|
| 91 |
+
# Optional fields in accordion
|
| 92 |
+
with gr.Accordion("Additional Information (Optional)", open=False):
|
| 93 |
+
organization = gr.Textbox(
|
| 94 |
+
label=SubmissionContent.form_labels["organization"],
|
| 95 |
+
placeholder=SubmissionContent.form_placeholders["organization"]
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
with gr.Row():
|
| 99 |
+
model_size = gr.Textbox(
|
| 100 |
+
label=SubmissionContent.form_labels["model_size"],
|
| 101 |
+
placeholder=SubmissionContent.form_placeholders["model_size"]
|
| 102 |
+
)
|
| 103 |
+
pretraining = gr.Textbox(
|
| 104 |
+
label=SubmissionContent.form_labels["pretraining"],
|
| 105 |
+
placeholder=SubmissionContent.form_placeholders["pretraining"]
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
publication_title = gr.Textbox(
|
| 109 |
+
label=SubmissionContent.form_labels["publication_title"],
|
| 110 |
+
placeholder=SubmissionContent.form_placeholders["publication_title"]
|
| 111 |
+
)
|
| 112 |
+
|
| 113 |
+
publication_link = gr.Textbox(
|
| 114 |
+
label=SubmissionContent.form_labels["publication_link"],
|
| 115 |
+
placeholder=SubmissionContent.form_placeholders["publication_link"]
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
# Submit button and result
|
| 119 |
submit_btn = gr.Button("Submit Model", variant="primary")
|
| 120 |
result_msg = gr.HTML()
|
| 121 |
|
| 122 |
if submit_callback:
|
| 123 |
submit_btn.click(
|
| 124 |
fn=submit_callback,
|
| 125 |
+
inputs=[
|
| 126 |
+
model_name, hf_space_tag, model_description,
|
| 127 |
+
organization, model_size, pretraining,
|
| 128 |
+
publication_title, publication_link
|
| 129 |
+
],
|
| 130 |
outputs=result_msg
|
| 131 |
)
|
|
|
|
| 132 |
|
| 133 |
return tab
|
| 134 |
|
frontend/submission.py
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import gradio as gr
|
| 3 |
+
from backend.api import submit_model
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def handle_submission(
|
| 7 |
+
model_name, hf_space_tag, model_description,
|
| 8 |
+
organization, model_size, pretraining,
|
| 9 |
+
publication_title, publication_link
|
| 10 |
+
):
|
| 11 |
+
"""Handle model submission from the form."""
|
| 12 |
+
|
| 13 |
+
# Basic validation
|
| 14 |
+
if not model_name or not hf_space_tag or not model_description:
|
| 15 |
+
return "<div style='color: red;'>L Error: Please fill in all required fields (*)</div>"
|
| 16 |
+
|
| 17 |
+
if "/" not in hf_space_tag:
|
| 18 |
+
return "<div style='color: red;'>L Error: HuggingFace space tag should be in format 'username/space-name'</div>"
|
| 19 |
+
|
| 20 |
+
# Process submission
|
| 21 |
+
try:
|
| 22 |
+
result = asyncio.run(submit_model(
|
| 23 |
+
model_name=model_name,
|
| 24 |
+
hf_space_tag=hf_space_tag,
|
| 25 |
+
model_description=model_description,
|
| 26 |
+
organization=organization or "",
|
| 27 |
+
model_size=model_size or "",
|
| 28 |
+
pretraining=pretraining or "",
|
| 29 |
+
publication_title=publication_title or "",
|
| 30 |
+
publication_link=publication_link or ""
|
| 31 |
+
))
|
| 32 |
+
|
| 33 |
+
return "<div style='color: green;'> Success! Your model has been submitted for evaluation. Results pending approval.</div>"
|
| 34 |
+
|
| 35 |
+
except Exception as e:
|
| 36 |
+
return f"<div style='color: red;'>L Error: {str(e)}</div>"
|
raw_predictions.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"participant": "RandomClassifiers",
|
| 4 |
+
"results": [
|
| 5 |
+
{
|
| 6 |
+
"smiles": "CCO",
|
| 7 |
+
"raw_predictions": {
|
| 8 |
+
"NR-AR": 0.3917455619722391,
|
| 9 |
+
"NR-AR-LBD": 0.26008200862052433,
|
| 10 |
+
"NR-AhR": 0.2101509144439867,
|
| 11 |
+
"NR-Aromatase": 0.2630385047554743,
|
| 12 |
+
"NR-ER": 0.7002048747849454,
|
| 13 |
+
"NR-ER-LBD": 0.15377240592596264,
|
| 14 |
+
"NR-PPAR-gamma": 0.1353658553753373,
|
| 15 |
+
"SR-ARE": 0.2797094295476972,
|
| 16 |
+
"SR-ATAD5": 0.8406408791404627,
|
| 17 |
+
"SR-HSE": 0.9222532746585554,
|
| 18 |
+
"SR-MMP": 0.7170385993040889,
|
| 19 |
+
"SR-p53": 0.19909124452286608
|
| 20 |
+
}
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"smiles": "c1ccccc1",
|
| 24 |
+
"raw_predictions": {
|
| 25 |
+
"NR-AR": 0.6967615243164884,
|
| 26 |
+
"NR-AR-LBD": 0.5429334657310607,
|
| 27 |
+
"NR-AhR": 0.8359421675060189,
|
| 28 |
+
"NR-Aromatase": 0.21863713829746834,
|
| 29 |
+
"NR-ER": 0.1384164897724609,
|
| 30 |
+
"NR-ER-LBD": 0.0567773996289892,
|
| 31 |
+
"NR-PPAR-gamma": 0.884469677268269,
|
| 32 |
+
"SR-ARE": 0.9507655336335464,
|
| 33 |
+
"SR-ATAD5": 0.861007589725987,
|
| 34 |
+
"SR-HSE": 0.9886291113232994,
|
| 35 |
+
"SR-MMP": 0.6429608178936979,
|
| 36 |
+
"SR-p53": 0.3907555716000315
|
| 37 |
+
}
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
"smiles": "CC(=O)Cl",
|
| 41 |
+
"raw_predictions": {
|
| 42 |
+
"NR-AR": 0.7653254677767931,
|
| 43 |
+
"NR-AR-LBD": 0.47398075844041376,
|
| 44 |
+
"NR-AhR": 0.0364769256514208,
|
| 45 |
+
"NR-Aromatase": 0.6524090139294707,
|
| 46 |
+
"NR-ER": 0.6020335714581855,
|
| 47 |
+
"NR-ER-LBD": 0.4920265461047544,
|
| 48 |
+
"NR-PPAR-gamma": 0.2534192957556971,
|
| 49 |
+
"SR-ARE": 0.177969901329171,
|
| 50 |
+
"SR-ATAD5": 0.09011487142758401,
|
| 51 |
+
"SR-HSE": 0.9120474797445164,
|
| 52 |
+
"SR-MMP": 0.7510410188649429,
|
| 53 |
+
"SR-p53": 0.7914294317144421
|
| 54 |
+
}
|
| 55 |
+
},
|
| 56 |
+
{
|
| 57 |
+
"smiles": "C1=CC=CN=C1",
|
| 58 |
+
"raw_predictions": {
|
| 59 |
+
"NR-AR": 0.5975434925069701,
|
| 60 |
+
"NR-AR-LBD": 0.2177384387523179,
|
| 61 |
+
"NR-AhR": 0.5091539966886376,
|
| 62 |
+
"NR-Aromatase": 0.5279716247658367,
|
| 63 |
+
"NR-ER": 0.37037003396449375,
|
| 64 |
+
"NR-ER-LBD": 0.042919826736678934,
|
| 65 |
+
"NR-PPAR-gamma": 0.6182258605984476,
|
| 66 |
+
"SR-ARE": 0.9229999554560413,
|
| 67 |
+
"SR-ATAD5": 0.1531615980022496,
|
| 68 |
+
"SR-HSE": 0.08691301573299948,
|
| 69 |
+
"SR-MMP": 0.21204819276958253,
|
| 70 |
+
"SR-p53": 0.08065030781214522
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
]
|
| 74 |
+
}
|
| 75 |
+
]
|