vectorplasticity commited on
Commit
352ecc9
·
verified ·
1 Parent(s): 40cb688

Fix router prefix - remove duplicate /api/datasets from router definition

Browse files
Files changed (1) hide show
  1. app/routers/datasets.py +101 -296
app/routers/datasets.py CHANGED
@@ -12,7 +12,7 @@ import httpx
12
  from app.config import settings
13
 
14
  logger = logging.getLogger(__name__)
15
- router = APIRouter(prefix="/api/datasets", tags=["Datasets"])
16
 
17
 
18
  class DatasetInfo(BaseModel):
@@ -25,47 +25,34 @@ class DatasetInfo(BaseModel):
25
  likes: int = 0
26
  private: bool = False
27
  tags: List[str] = []
28
- task_categories: List[str] = []
29
- size_categories: List[str] = []
30
- languages: List[str] = []
31
- licenses: List[str] = []
32
  created_at: Optional[datetime]
33
  last_modified: Optional[datetime]
34
 
35
 
36
  class DatasetSearchResult(BaseModel):
37
- """Search result model."""
38
  id: str
39
  author: str
40
  dataset_name: str
41
  downloads: int
42
  likes: int
43
- description: Optional[str]
44
 
45
 
46
- class DatasetColumn(BaseModel):
47
- """Dataset column information."""
48
- name: str
49
- dtype: str
50
- sample_values: List[Any] = []
51
-
 
52
 
53
  class DatasetSchema(BaseModel):
54
  """Dataset schema information."""
 
55
  splits: List[str]
56
- columns: List[DatasetColumn]
57
  num_rows: Dict[str, int]
58
- features: Dict[str, Any]
59
-
60
-
61
- class DatasetRecommendation(BaseModel):
62
- """Dataset recommendation for a task."""
63
- dataset_id: str
64
- name: str
65
- description: str
66
- size: str
67
- task_fit: str
68
- sample_count: int
69
 
70
 
71
  HF_API_URL = "https://huggingface.co/api"
@@ -74,7 +61,7 @@ HF_API_URL = "https://huggingface.co/api"
74
  @router.get("/search", response_model=List[DatasetSearchResult])
75
  async def search_datasets(
76
  query: str = Query(..., min_length=1, description="Search query"),
77
- task: Optional[str] = Query(None, description="Filter by task category"),
78
  language: Optional[str] = Query(None, description="Filter by language"),
79
  author: Optional[str] = Query(None, description="Filter by author"),
80
  limit: int = Query(20, ge=1, le=100)
@@ -109,8 +96,7 @@ async def search_datasets(
109
  author=d.get("author", ""),
110
  dataset_name=d.get("id", "").split("/")[-1] if "/" in d.get("id", "") else d.get("id", ""),
111
  downloads=d.get("downloads", 0),
112
- likes=d.get("likes", 0),
113
- description=d.get("description", "")[:200] if d.get("description") else None
114
  )
115
  for d in data
116
  ]
@@ -131,27 +117,15 @@ async def get_dataset_info(dataset_id: str):
131
  response.raise_for_status()
132
  data = response.json()
133
 
134
- # Extract metadata
135
- tags = data.get("tags", [])
136
-
137
- task_categories = [t.replace("task_categories:", "") for t in tags if t.startswith("task_categories:")]
138
- size_categories = [t.replace("size_categories:", "") for t in tags if t.startswith("size_categories:")]
139
- languages = [t.replace("language:", "") for t in tags if t.startswith("language:")]
140
- licenses = [t.replace("license:", "") for t in tags if t.startswith("license:")]
141
-
142
  return DatasetInfo(
143
  id=data.get("id", ""),
144
  author=data.get("author", ""),
145
  dataset_name=data.get("id", "").split("/")[-1] if "/" in data.get("id", "") else data.get("id", ""),
146
- description=data.get("description", "")[:1000] if data.get("description") else None,
147
  downloads=data.get("downloads", 0),
148
  likes=data.get("likes", 0),
149
  private=data.get("private", False),
150
- tags=tags,
151
- task_categories=task_categories,
152
- size_categories=size_categories,
153
- languages=languages,
154
- licenses=licenses,
155
  created_at=datetime.fromisoformat(data["createdAt"].replace("Z", "+00:00")) if data.get("createdAt") else None,
156
  last_modified=datetime.fromisoformat(data["lastModified"].replace("Z", "+00:00")) if data.get("lastModified") else None
157
  )
@@ -164,58 +138,40 @@ async def get_dataset_info(dataset_id: str):
164
  raise HTTPException(status_code=500, detail=str(e))
165
 
166
 
167
- @router.get("/schema/{dataset_id:path}", response_model=DatasetSchema)
168
- async def get_dataset_schema(
169
- dataset_id: str,
170
- config: Optional[str] = Query(None, description="Dataset config/subset"),
171
- split: str = Query("train", description="Dataset split to inspect")
172
- ):
173
- """Get schema and structure of a dataset."""
174
  try:
175
- from datasets import load_dataset_builder
176
 
177
- builder = load_dataset_builder(dataset_id, config, trust_remote_code=True)
178
- info = builder.info
 
179
 
180
- # Get splits
181
- splits = list(builder.builder_configs.keys()) if builder.builder_configs else ["train"]
182
- try:
183
- split_dict = builder.info.splits
184
- if split_dict:
185
- splits = list(split_dict.keys())
186
- except:
187
- pass
188
-
189
- # Get features/columns
190
- columns = []
191
- features = info.features
192
- if features:
193
- for name, feature in features.items():
194
- dtype = str(type(feature).__name__)
195
- columns.append(DatasetColumn(
196
- name=name,
197
- dtype=dtype,
198
- sample_values=[]
199
- ))
200
 
201
- # Get row counts
202
- num_rows = {}
203
- try:
204
- for split_name in splits:
205
- num_rows[split_name] = info.splits[split_name].num_examples if info.splits else 0
206
- except:
207
- num_rows = {"train": 0}
208
-
209
- return DatasetSchema(
210
- splits=splits,
211
- columns=columns,
212
- num_rows=num_rows,
213
- features={k: str(v) for k, v in features.items()} if features else {}
214
- )
215
 
 
 
 
 
 
 
 
216
  except Exception as e:
217
  logger.error(f"Failed to get dataset schema: {e}")
218
- raise HTTPException(status_code=500, detail=f"Could not load dataset: {str(e)}")
 
 
 
 
 
 
 
219
 
220
 
221
  @router.get("/recommend/{task_type}", response_model=List[DatasetRecommendation])
@@ -223,200 +179,50 @@ async def get_dataset_recommendations(task_type: str):
223
  """Get dataset recommendations for a specific task."""
224
  recommendations = {
225
  "causal-lm": [
226
- DatasetRecommendation(
227
- dataset_id="tiiuae/falcon-refinedweb",
228
- name="RefinedWeb",
229
- description="High-quality web data for language modeling",
230
- size="Large (500B+ tokens)",
231
- task_fit="Excellent for pretraining and continued pretraining",
232
- sample_count=500000000
233
- ),
234
- DatasetRecommendation(
235
- dataset_id="OpenAssistant/oasst1",
236
- name="OpenAssistant Conversations",
237
- description="Human-generated assistant conversations",
238
- size="Medium (84K conversations)",
239
- task_fit="Instruction tuning, chat models",
240
- sample_count=84000
241
- ),
242
- DatasetRecommendation(
243
- dataset_id="HuggingFaceH4/ultrachat_200k",
244
- name="UltraChat 200K",
245
- description="Large-scale instruction following data",
246
- size="Medium (200K examples)",
247
- task_fit="Instruction tuning",
248
- sample_count=200000
249
- ),
250
- DatasetRecommendation(
251
- dataset_id="teknium/openhermes",
252
- name="OpenHermes",
253
- description="High-quality synthetic data",
254
- size="Medium (240K examples)",
255
- task_fit="Instruction tuning, chat",
256
- sample_count=240000
257
- ),
258
  ],
259
  "seq2seq": [
260
- DatasetRecommendation(
261
- dataset_id="cfilt/iitb-english-hindi",
262
- name="IITB English-Hindi",
263
- description="English-Hindi parallel corpus",
264
- size="Large (1.5M pairs)",
265
- task_fit="Translation",
266
- sample_count=1500000
267
- ),
268
- DatasetRecommendation(
269
- dataset_id="wmt/wmt14-en-de",
270
- name="WMT14 English-German",
271
- description="WMT translation benchmark",
272
- size="Large (4.5M pairs)",
273
- task_fit="Machine translation",
274
- sample_count=4500000
275
- ),
276
  ],
277
  "summarization": [
278
- DatasetRecommendation(
279
- dataset_id="cnn_dailymail",
280
- name="CNN/DailyMail",
281
- description="News article summarization",
282
- size="Large (300K articles)",
283
- task_fit="News summarization",
284
- sample_count=300000
285
- ),
286
- DatasetRecommendation(
287
- dataset_id="xsum",
288
- name="XSum",
289
- description="BBC article summarization",
290
- size="Medium (220K articles)",
291
- task_fit="Abstractive summarization",
292
- sample_count=220000
293
- ),
294
- DatasetRecommendation(
295
- dataset_id="samsum",
296
- name="SAMSum",
297
- description="Dialogue summarization",
298
- size="Small (16K dialogues)",
299
- task_fit="Conversation summarization",
300
- sample_count=16000
301
- ),
302
  ],
303
  "token-classification": [
304
- DatasetRecommendation(
305
- dataset_id="conll2003",
306
- name="CoNLL-2003",
307
- description="Standard NER benchmark",
308
- size="Small (22K sentences)",
309
- task_fit="Named entity recognition",
310
- sample_count=22000
311
- ),
312
- DatasetRecommendation(
313
- dataset_id="wikiann",
314
- name="WikiANN",
315
- description="Cross-lingual NER",
316
- size="Large (20M+ tags)",
317
- task_fit="Multilingual NER",
318
- sample_count=20000000
319
- ),
320
- DatasetRecommendation(
321
- dataset_id="ontonotes",
322
- name="OntoNotes 5.0",
323
- description="Rich annotation including NER, POS",
324
- size="Medium (1.8M tokens)",
325
- task_fit="Comprehensive NER",
326
- sample_count=1800000
327
- ),
328
  ],
329
  "sequence-classification": [
330
- DatasetRecommendation(
331
- dataset_id="imdb",
332
- name="IMDB Reviews",
333
- description="Movie review sentiment",
334
- size="Medium (50K reviews)",
335
- task_fit="Binary sentiment classification",
336
- sample_count=50000
337
- ),
338
- DatasetRecommendation(
339
- dataset_id="yelp_review_full",
340
- name="Yelp Reviews",
341
- description="Business review sentiment",
342
- size="Large (650K reviews)",
343
- task_fit="Multi-class sentiment",
344
- sample_count=650000
345
- ),
346
- DatasetRecommendation(
347
- dataset_id="ag_news",
348
- name="AG News",
349
- description="News article categorization",
350
- size="Medium (120K articles)",
351
- task_fit="Topic classification",
352
- sample_count=120000
353
- ),
354
  ],
355
  "question-answering": [
356
- DatasetRecommendation(
357
- dataset_id="squad",
358
- name="SQuAD 1.1",
359
- description="Reading comprehension QA",
360
- size="Medium (100K questions)",
361
- task_fit="Extractive QA",
362
- sample_count=100000
363
- ),
364
- DatasetRecommendation(
365
- dataset_id="squad_v2",
366
- name="SQuAD 2.0",
367
- description="QA with unanswerable questions",
368
- size="Large (150K questions)",
369
- task_fit="Robust extractive QA",
370
- sample_count=150000
371
- ),
372
- DatasetRecommendation(
373
- dataset_id="hotpot_qa",
374
- name="HotpotQA",
375
- description="Multi-hop reasoning QA",
376
- size="Medium (113K questions)",
377
- task_fit="Multi-hop reasoning",
378
- sample_count=113000
379
- ),
380
  ],
381
  "translation": [
382
- DatasetRecommendation(
383
- dataset_id="opus100",
384
- name="OPUS-100",
385
- description="100 language pairs",
386
- size="Very Large (100M+ pairs)",
387
- task_fit="Multilingual translation",
388
- sample_count=100000000
389
- ),
390
- DatasetRecommendation(
391
- dataset_id="wmt19",
392
- name="WMT19",
393
- description="WMT19 translation benchmark",
394
- size="Large",
395
- task_fit="High-quality translation",
396
- sample_count=10000000
397
- ),
398
  ],
399
  "masked-lm": [
400
- DatasetRecommendation(
401
- dataset_id="bookcorpus",
402
- name="BookCorpus",
403
- description="Books for language modeling",
404
- size="Large (1B words)",
405
- task_fit="Pretraining",
406
- sample_count=1000000000
407
- ),
408
- DatasetRecommendation(
409
- dataset_id="wikipedia",
410
- name="Wikipedia",
411
- description="Wikipedia articles",
412
- size="Very Large",
413
- task_fit="Pretraining, knowledge",
414
- sample_count=6000000
415
- ),
416
  ],
417
  }
418
 
419
- result = recommendations.get(task_type, recommendations.get("causal-lm", []))
 
 
 
 
420
  return result[:5]
421
 
422
 
@@ -425,7 +231,7 @@ async def get_popular_datasets(
425
  task: Optional[str] = None,
426
  limit: int = Query(10, ge=1, le=50)
427
  ):
428
- """Get most popular datasets."""
429
  async with httpx.AsyncClient() as client:
430
  params = {
431
  "sort": "downloads",
@@ -452,72 +258,71 @@ async def get_popular_datasets(
452
  @router.get("/preview/{dataset_id:path}")
453
  async def preview_dataset(
454
  dataset_id: str,
455
- config: Optional[str] = Query(None),
456
- split: str = Query("train"),
457
- limit: int = Query(5, ge=1, le=20)
458
  ):
459
  """Preview first N rows of a dataset."""
460
  try:
461
  from datasets import load_dataset
462
 
463
- dataset = load_dataset(
464
- dataset_id,
465
- config,
466
- split=split,
467
- trust_remote_code=True
468
- )
469
-
470
- rows = dataset.select(range(min(limit, len(dataset))))
471
 
472
  return {
473
  "dataset_id": dataset_id,
474
- "config": config,
475
  "split": split,
476
- "total_rows": len(dataset),
477
- "columns": list(dataset.features.keys()),
478
- "rows": rows.to_list()
479
  }
480
-
481
  except Exception as e:
482
  logger.error(f"Failed to preview dataset: {e}")
483
- raise HTTPException(status_code=500, detail=f"Could not load dataset: {str(e)}")
484
 
485
 
486
  @router.get("/configs/{dataset_id:path}")
487
  async def get_dataset_configs(dataset_id: str):
488
- """Get available configurations/subsets of a dataset."""
489
  try:
490
  from datasets import get_dataset_config_names
491
 
492
- configs = get_dataset_config_names(dataset_id, trust_remote_code=True)
493
-
494
  return {
495
  "dataset_id": dataset_id,
496
  "configs": configs
497
  }
498
-
499
  except Exception as e:
500
- logger.error(f"Failed to get dataset configs: {e}")
501
- raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
502
 
503
 
504
  @router.get("/splits/{dataset_id:path}")
505
  async def get_dataset_splits(
506
  dataset_id: str,
507
- config: Optional[str] = Query(None)
508
  ):
509
- """Get available splits of a dataset."""
510
  try:
511
  from datasets import get_dataset_split_names
512
 
513
- splits = get_dataset_split_names(dataset_id, config, trust_remote_code=True)
514
-
515
  return {
516
  "dataset_id": dataset_id,
517
  "config": config,
518
- "splits": list(splits)
519
  }
520
-
521
  except Exception as e:
522
- logger.error(f"Failed to get dataset splits: {e}")
523
- raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
12
  from app.config import settings
13
 
14
  logger = logging.getLogger(__name__)
15
+ router = APIRouter(tags=["Datasets"])
16
 
17
 
18
  class DatasetInfo(BaseModel):
 
25
  likes: int = 0
26
  private: bool = False
27
  tags: List[str] = []
 
 
 
 
28
  created_at: Optional[datetime]
29
  last_modified: Optional[datetime]
30
 
31
 
32
  class DatasetSearchResult(BaseModel):
33
+ """Search result dataset."""
34
  id: str
35
  author: str
36
  dataset_name: str
37
  downloads: int
38
  likes: int
 
39
 
40
 
41
+ class DatasetRecommendation(BaseModel):
42
+ """Dataset recommendation for a task."""
43
+ dataset_id: str
44
+ reason: str
45
+ size: str
46
+ format: str
47
+
48
 
49
  class DatasetSchema(BaseModel):
50
  """Dataset schema information."""
51
+ dataset_id: str
52
  splits: List[str]
53
+ columns: List[str]
54
  num_rows: Dict[str, int]
55
+ features: Dict[str, str]
 
 
 
 
 
 
 
 
 
 
56
 
57
 
58
  HF_API_URL = "https://huggingface.co/api"
 
61
  @router.get("/search", response_model=List[DatasetSearchResult])
62
  async def search_datasets(
63
  query: str = Query(..., min_length=1, description="Search query"),
64
+ task: Optional[str] = Query(None, description="Filter by task"),
65
  language: Optional[str] = Query(None, description="Filter by language"),
66
  author: Optional[str] = Query(None, description="Filter by author"),
67
  limit: int = Query(20, ge=1, le=100)
 
96
  author=d.get("author", ""),
97
  dataset_name=d.get("id", "").split("/")[-1] if "/" in d.get("id", "") else d.get("id", ""),
98
  downloads=d.get("downloads", 0),
99
+ likes=d.get("likes", 0)
 
100
  )
101
  for d in data
102
  ]
 
117
  response.raise_for_status()
118
  data = response.json()
119
 
 
 
 
 
 
 
 
 
120
  return DatasetInfo(
121
  id=data.get("id", ""),
122
  author=data.get("author", ""),
123
  dataset_name=data.get("id", "").split("/")[-1] if "/" in data.get("id", "") else data.get("id", ""),
124
+ description=data.get("cardData", {}).get("description", ""),
125
  downloads=data.get("downloads", 0),
126
  likes=data.get("likes", 0),
127
  private=data.get("private", False),
128
+ tags=data.get("tags", []),
 
 
 
 
129
  created_at=datetime.fromisoformat(data["createdAt"].replace("Z", "+00:00")) if data.get("createdAt") else None,
130
  last_modified=datetime.fromisoformat(data["lastModified"].replace("Z", "+00:00")) if data.get("lastModified") else None
131
  )
 
138
  raise HTTPException(status_code=500, detail=str(e))
139
 
140
 
141
+ @router.get("/schema/{dataset_id:path}")
142
+ async def get_dataset_schema(dataset_id: str):
143
+ """Get dataset schema including splits, columns, and row counts."""
 
 
 
 
144
  try:
145
+ from datasets import get_dataset_infos
146
 
147
+ infos = get_dataset_infos(dataset_id)
148
+ if not infos:
149
+ raise HTTPException(status_code=404, detail="Dataset not found or inaccessible")
150
 
151
+ info = list(infos.values())[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
 
153
+ splits = list(info.splits.keys()) if info.splits else []
154
+ columns = list(info.features.keys()) if info.features else []
155
+ num_rows = {k: v.num_examples for k, v in info.splits.items()} if info.splits else {}
156
+ features = {k: str(v) for k, v in info.features.items()} if info.features else {}
 
 
 
 
 
 
 
 
 
 
157
 
158
+ return {
159
+ "dataset_id": dataset_id,
160
+ "splits": splits,
161
+ "columns": columns,
162
+ "num_rows": num_rows,
163
+ "features": features
164
+ }
165
  except Exception as e:
166
  logger.error(f"Failed to get dataset schema: {e}")
167
+ return {
168
+ "dataset_id": dataset_id,
169
+ "splits": [],
170
+ "columns": [],
171
+ "num_rows": {},
172
+ "features": {},
173
+ "error": str(e)
174
+ }
175
 
176
 
177
  @router.get("/recommend/{task_type}", response_model=List[DatasetRecommendation])
 
179
  """Get dataset recommendations for a specific task."""
180
  recommendations = {
181
  "causal-lm": [
182
+ DatasetRecommendation(dataset_id="tiiuae/falcon-refinedweb", reason="Large-scale web data for language modeling", size="350GB+", format="Raw text"),
183
+ DatasetRecommendation(dataset_id="OpenAssistant/oasst1", reason="Human conversations for instruction tuning", size="~10M tokens", format="Conversations"),
184
+ DatasetRecommendation(dataset_id="HuggingFaceH4/ultrachat_200k", reason="High-quality dialogues", size="200k conversations", format="Conversations"),
185
+ DatasetRecommendation(dataset_id="teknium/openhermes", reason="Diverse instruction data", size="1M+ samples", format="Instructions"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  ],
187
  "seq2seq": [
188
+ DatasetRecommendation(dataset_id="cfilt/iitb-english-hindi", reason="Translation dataset", size="~1.5M pairs", format="Parallel text"),
189
+ DatasetRecommendation(dataset_id="wmt/wmt14-en-de", reason="WMT translation benchmark", size="~4.5M pairs", format="Parallel text"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  ],
191
  "summarization": [
192
+ DatasetRecommendation(dataset_id="cnn_dailymail", reason="News summarization standard", size="~300k articles", format="Article-Summary"),
193
+ DatasetRecommendation(dataset_id="xsum", reason="BBC article summaries", size="~22k articles", format="Article-Summary"),
194
+ DatasetRecommendation(dataset_id="samsum", reason="Dialogue summarization", size="~16k dialogues", format="Dialogue-Summary"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  ],
196
  "token-classification": [
197
+ DatasetRecommendation(dataset_id="conll2003", reason="Classic NER benchmark", size="~22k sentences", format="IOB tags"),
198
+ DatasetRecommendation(dataset_id="ontonotes", reason="Multi-domain NER", size="~80k sentences", format="IOB tags"),
199
+ DatasetRecommendation(dataset_id="wikiann", reason="Cross-lingual NER", size="Varies by language", format="IOB tags"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  ],
201
  "sequence-classification": [
202
+ DatasetRecommendation(dataset_id="imdb", reason="Movie reviews for sentiment", size="50k reviews", format="Text-Label"),
203
+ DatasetRecommendation(dataset_id="yelp_polarity", reason="Yelp reviews", size="560k reviews", format="Text-Label"),
204
+ DatasetRecommendation(dataset_id="ag_news", reason="News topic classification", size="~127k articles", format="Text-Label"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  ],
206
  "question-answering": [
207
+ DatasetRecommendation(dataset_id="squad", reason="Reading comprehension QA", size="~100k questions", format="Context-QA"),
208
+ DatasetRecommendation(dataset_id="squad_v2", reason="SQuAD with unanswerable", size="~150k questions", format="Context-QA"),
209
+ DatasetRecommendation(dataset_id="natural_questions", reason="Real user questions", size="~300k questions", format="Document-QA"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  ],
211
  "translation": [
212
+ DatasetRecommendation(dataset_id="wmt/wmt14-en-de", reason="English-German", size="~4.5M pairs", format="Parallel"),
213
+ DatasetRecommendation(dataset_id="wmt/wmt19-ru-en", reason="Russian-English", size="~4M pairs", format="Parallel"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  ],
215
  "masked-lm": [
216
+ DatasetRecommendation(dataset_id="bookcorpus", reason="Books dataset for MLM", size="~1B words", format="Raw text"),
217
+ DatasetRecommendation(dataset_id="wikitext", reason="Wikipedia text", size="Varies", format="Raw text"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  ],
219
  }
220
 
221
+ result = recommendations.get(task_type, [])
222
+
223
+ if not result:
224
+ result = recommendations.get("causal-lm", [])
225
+
226
  return result[:5]
227
 
228
 
 
231
  task: Optional[str] = None,
232
  limit: int = Query(10, ge=1, le=50)
233
  ):
234
+ """Get most popular datasets, optionally filtered by task."""
235
  async with httpx.AsyncClient() as client:
236
  params = {
237
  "sort": "downloads",
 
258
  @router.get("/preview/{dataset_id:path}")
259
  async def preview_dataset(
260
  dataset_id: str,
261
+ split: str = Query("train", description="Dataset split to preview"),
262
+ rows: int = Query(10, ge=1, le=100)
 
263
  ):
264
  """Preview first N rows of a dataset."""
265
  try:
266
  from datasets import load_dataset
267
 
268
+ ds = load_dataset(dataset_id, split=split, streaming=True)
269
+ data = []
270
+ for i, row in enumerate(ds):
271
+ if i >= rows:
272
+ break
273
+ data.append(row)
 
 
274
 
275
  return {
276
  "dataset_id": dataset_id,
 
277
  "split": split,
278
+ "rows": data,
279
+ "columns": list(data[0].keys()) if data else []
 
280
  }
 
281
  except Exception as e:
282
  logger.error(f"Failed to preview dataset: {e}")
283
+ raise HTTPException(status_code=500, detail=str(e))
284
 
285
 
286
  @router.get("/configs/{dataset_id:path}")
287
  async def get_dataset_configs(dataset_id: str):
288
+ """Get available configurations for a dataset."""
289
  try:
290
  from datasets import get_dataset_config_names
291
 
292
+ configs = get_dataset_config_names(dataset_id)
 
293
  return {
294
  "dataset_id": dataset_id,
295
  "configs": configs
296
  }
 
297
  except Exception as e:
298
+ logger.error(f"Failed to get configs: {e}")
299
+ return {
300
+ "dataset_id": dataset_id,
301
+ "configs": [],
302
+ "error": str(e)
303
+ }
304
 
305
 
306
  @router.get("/splits/{dataset_id:path}")
307
  async def get_dataset_splits(
308
  dataset_id: str,
309
+ config: Optional[str] = None
310
  ):
311
+ """Get available splits for a dataset."""
312
  try:
313
  from datasets import get_dataset_split_names
314
 
315
+ splits = get_dataset_split_names(dataset_id, config_name=config)
 
316
  return {
317
  "dataset_id": dataset_id,
318
  "config": config,
319
+ "splits": splits
320
  }
 
321
  except Exception as e:
322
+ logger.error(f"Failed to get splits: {e}")
323
+ return {
324
+ "dataset_id": dataset_id,
325
+ "config": config,
326
+ "splits": [],
327
+ "error": str(e)
328
+ }