vectorplasticity's picture
Update via HuggingFace Tool
c43ff0f verified
Raw
History Blame Contribute Delete
12.5 kB
"""
Datasets API Router - Explore and select datasets from HuggingFace Hub
"""
from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel, Field
from typing import Optional, Dict, List, Any
from datetime import datetime
import logging
import httpx
from app.config import settings
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/datasets", tags=["Datasets"])
class DatasetInfo(BaseModel):
"""Dataset information."""
id: str
author: str
dataset_name: str
description: Optional[str]
downloads: int = 0
likes: int = 0
private: bool = False
tags: List[str] = []
created_at: Optional[datetime]
last_modified: Optional[datetime]
class DatasetSearchResult(BaseModel):
"""Search result dataset."""
id: str
author: str
dataset_name: str
downloads: int
likes: int
class DatasetRecommendation(BaseModel):
"""Dataset recommendation for a task."""
dataset_id: str
reason: str
size: str
format: str
class DatasetSchema(BaseModel):
"""Dataset schema information."""
dataset_id: str
splits: List[str]
columns: List[str]
num_rows: Dict[str, int]
features: Dict[str, str]
HF_API_URL = "https://huggingface.co/api"
@router.get("/search", response_model=List[DatasetSearchResult])
async def search_datasets(
query: str = Query(..., min_length=1, description="Search query"),
task: Optional[str] = Query(None, description="Filter by task"),
language: Optional[str] = Query(None, description="Filter by language"),
author: Optional[str] = Query(None, description="Filter by author"),
limit: int = Query(20, ge=1, le=100)
):
"""Search HuggingFace Hub for datasets."""
async with httpx.AsyncClient() as client:
params = {
"search": query,
"limit": limit,
"full": "false"
}
if task:
params["task_categories"] = task
if language:
params["language"] = language
if author:
params["author"] = author
try:
response = await client.get(
f"{HF_API_URL}/datasets",
params=params,
timeout=30.0
)
response.raise_for_status()
data = response.json()
return [
DatasetSearchResult(
id=d.get("id", ""),
author=d.get("author", ""),
dataset_name=d.get("id", "").split("/")[-1] if "/" in d.get("id", "") else d.get("id", ""),
downloads=d.get("downloads", 0),
likes=d.get("likes", 0)
)
for d in data
]
except Exception as e:
logger.error(f"Dataset search failed: {e}")
raise HTTPException(status_code=500, detail=f"Search failed: {str(e)}")
@router.get("/info/{dataset_id:path}", response_model=DatasetInfo)
async def get_dataset_info(dataset_id: str):
"""Get detailed information about a dataset."""
async with httpx.AsyncClient() as client:
try:
response = await client.get(
f"{HF_API_URL}/datasets/{dataset_id}",
timeout=30.0
)
response.raise_for_status()
data = response.json()
return DatasetInfo(
id=data.get("id", ""),
author=data.get("author", ""),
dataset_name=data.get("id", "").split("/")[-1] if "/" in data.get("id", "") else data.get("id", ""),
description=data.get("cardData", {}).get("description", ""),
downloads=data.get("downloads", 0),
likes=data.get("likes", 0),
private=data.get("private", False),
tags=data.get("tags", []),
created_at=datetime.fromisoformat(data["createdAt"].replace("Z", "+00:00")) if data.get("createdAt") else None,
last_modified=datetime.fromisoformat(data["lastModified"].replace("Z", "+00:00")) if data.get("lastModified") else None
)
except httpx.HTTPStatusError as e:
if e.response.status_code == 404:
raise HTTPException(status_code=404, detail="Dataset not found")
raise HTTPException(status_code=e.response.status_code, detail=f"API error: {e}")
except Exception as e:
logger.error(f"Failed to get dataset info: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.get("/schema/{dataset_id:path}")
async def get_dataset_schema(dataset_id: str):
"""Get dataset schema including splits, columns, and row counts."""
try:
from datasets import get_dataset_infos
infos = get_dataset_infos(dataset_id)
if not infos:
raise HTTPException(status_code=404, detail="Dataset not found or inaccessible")
info = list(infos.values())[0]
splits = list(info.splits.keys()) if info.splits else []
columns = list(info.features.keys()) if info.features else []
num_rows = {k: v.num_examples for k, v in info.splits.items()} if info.splits else {}
features = {k: str(v) for k, v in info.features.items()} if info.features else {}
return {
"dataset_id": dataset_id,
"splits": splits,
"columns": columns,
"num_rows": num_rows,
"features": features
}
except Exception as e:
logger.error(f"Failed to get dataset schema: {e}")
return {
"dataset_id": dataset_id,
"splits": [],
"columns": [],
"num_rows": {},
"features": {},
"error": str(e)
}
@router.get("/recommend/{task_type}", response_model=List[DatasetRecommendation])
async def get_dataset_recommendations(task_type: str):
"""Get dataset recommendations for a specific task."""
recommendations = {
"causal-lm": [
DatasetRecommendation(dataset_id="tiiuae/falcon-refinedweb", reason="Large-scale web data for language modeling", size="350GB+", format="Raw text"),
DatasetRecommendation(dataset_id="OpenAssistant/oasst1", reason="Human conversations for instruction tuning", size="~10M tokens", format="Conversations"),
DatasetRecommendation(dataset_id="HuggingFaceH4/ultrachat_200k", reason="High-quality dialogues", size="200k conversations", format="Conversations"),
DatasetRecommendation(dataset_id="teknium/openhermes", reason="Diverse instruction data", size="1M+ samples", format="Instructions"),
],
"seq2seq": [
DatasetRecommendation(dataset_id="cfilt/iitb-english-hindi", reason="Translation dataset", size="~1.5M pairs", format="Parallel text"),
DatasetRecommendation(dataset_id="wmt/wmt14-en-de", reason="WMT translation benchmark", size="~4.5M pairs", format="Parallel text"),
],
"summarization": [
DatasetRecommendation(dataset_id="cnn_dailymail", reason="News summarization standard", size="~300k articles", format="Article-Summary"),
DatasetRecommendation(dataset_id="xsum", reason="BBC article summaries", size="~22k articles", format="Article-Summary"),
DatasetRecommendation(dataset_id="samsum", reason="Dialogue summarization", size="~16k dialogues", format="Dialogue-Summary"),
],
"token-classification": [
DatasetRecommendation(dataset_id="conll2003", reason="Classic NER benchmark", size="~22k sentences", format="IOB tags"),
DatasetRecommendation(dataset_id="ontonotes", reason="Multi-domain NER", size="~80k sentences", format="IOB tags"),
DatasetRecommendation(dataset_id="wikiann", reason="Cross-lingual NER", size="Varies by language", format="IOB tags"),
],
"sequence-classification": [
DatasetRecommendation(dataset_id="imdb", reason="Movie reviews for sentiment", size="50k reviews", format="Text-Label"),
DatasetRecommendation(dataset_id="yelp_polarity", reason="Yelp reviews", size="560k reviews", format="Text-Label"),
DatasetRecommendation(dataset_id="ag_news", reason="News topic classification", size="~127k articles", format="Text-Label"),
],
"question-answering": [
DatasetRecommendation(dataset_id="squad", reason="Reading comprehension QA", size="~100k questions", format="Context-QA"),
DatasetRecommendation(dataset_id="squad_v2", reason="SQuAD with unanswerable", size="~150k questions", format="Context-QA"),
DatasetRecommendation(dataset_id="natural_questions", reason="Real user questions", size="~300k questions", format="Document-QA"),
],
"translation": [
DatasetRecommendation(dataset_id="wmt/wmt14-en-de", reason="English-German", size="~4.5M pairs", format="Parallel"),
DatasetRecommendation(dataset_id="wmt/wmt19-ru-en", reason="Russian-English", size="~4M pairs", format="Parallel"),
],
"masked-lm": [
DatasetRecommendation(dataset_id="bookcorpus", reason="Books dataset for MLM", size="~1B words", format="Raw text"),
DatasetRecommendation(dataset_id="wikitext", reason="Wikipedia text", size="Varies", format="Raw text"),
],
}
result = recommendations.get(task_type, [])
if not result:
result = recommendations.get("causal-lm", [])
return result[:5]
@router.get("/popular")
async def get_popular_datasets(
task: Optional[str] = None,
limit: int = Query(10, ge=1, le=50)
):
"""Get most popular datasets, optionally filtered by task."""
async with httpx.AsyncClient() as client:
params = {
"sort": "downloads",
"direction": "-1",
"limit": limit
}
if task:
params["task_categories"] = task
try:
response = await client.get(
f"{HF_API_URL}/datasets",
params=params,
timeout=30.0
)
response.raise_for_status()
return response.json()
except Exception as e:
logger.error(f"Failed to get popular datasets: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.get("/preview/{dataset_id:path}")
async def preview_dataset(
dataset_id: str,
split: str = Query("train", description="Dataset split to preview"),
rows: int = Query(10, ge=1, le=100)
):
"""Preview first N rows of a dataset."""
try:
from datasets import load_dataset
ds = load_dataset(dataset_id, split=split, streaming=True)
data = []
for i, row in enumerate(ds):
if i >= rows:
break
data.append(row)
return {
"dataset_id": dataset_id,
"split": split,
"rows": data,
"columns": list(data[0].keys()) if data else []
}
except Exception as e:
logger.error(f"Failed to preview dataset: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.get("/configs/{dataset_id:path}")
async def get_dataset_configs(dataset_id: str):
"""Get available configurations for a dataset."""
try:
from datasets import get_dataset_config_names
configs = get_dataset_config_names(dataset_id)
return {
"dataset_id": dataset_id,
"configs": configs
}
except Exception as e:
logger.error(f"Failed to get configs: {e}")
return {
"dataset_id": dataset_id,
"configs": [],
"error": str(e)
}
@router.get("/splits/{dataset_id:path}")
async def get_dataset_splits(
dataset_id: str,
config: Optional[str] = None
):
"""Get available splits for a dataset."""
try:
from datasets import get_dataset_split_names
splits = get_dataset_split_names(dataset_id, config_name=config)
return {
"dataset_id": dataset_id,
"config": config,
"splits": splits
}
except Exception as e:
logger.error(f"Failed to get splits: {e}")
return {
"dataset_id": dataset_id,
"config": config,
"splits": [],
"error": str(e)
}