abhinavvathadi's picture
Initial commit for DealSight Intelligence
76562dd
Raw
History Blame Contribute Delete
6.69 kB
"""Dataset registry, loaders, and schema validators.
Three kinds of Hugging Face datasets are supported:
- structured (`items_lite`/`items_full`) — the app, vector store, and
pricing pipelines all use this shape (title, category, price, summary).
- prompt (`items_prompts_lite`/`items_prompts_full`) — prompt/completion
pairs for prompt-based fine-tuning and prompt evaluation only.
- raw (`items_raw_lite`/`items_raw_full`) — raw source rows; supported
but not the default app input.
The validators here raise `DatasetSchemaError` when a dataset is fed into
the wrong pipeline so silent shape mismatches never reach the model.
"""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable, Literal
from dealsight_intelligence import config
from dealsight_intelligence.data.items import Item
DatasetPurpose = Literal["structured", "prompt", "raw"]
STRUCTURED_DATASETS = {
"lite": "abhinavvathadi/items_lite",
"full": "abhinavvathadi/items_full",
"ed_lite": "ed-donner/items_lite",
"ed_full": "ed-donner/items_full",
}
PROMPT_DATASETS = {
"lite": "abhinavvathadi/items_prompts_lite",
"full": "abhinavvathadi/items_prompts_full",
}
RAW_DATASETS = {
"lite": "abhinavvathadi/items_raw_lite",
"full": "abhinavvathadi/items_raw_full",
}
STRUCTURED_REQUIRED_FIELDS = {"title", "category", "price", "summary"}
PROMPT_REQUIRED_FIELDS = {"prompt", "completion"}
class DatasetSchemaError(ValueError):
"""Raised when a dataset source is used for the wrong pipeline."""
@dataclass(frozen=True)
class PromptExample:
prompt: str
completion: str
def resolve_dataset_source(
source: str | Path | None = None,
purpose: DatasetPurpose = "structured",
size: str = "lite",
) -> str:
if source:
return str(source)
if purpose == "structured":
return config.structured_dataset_source()
if purpose == "prompt":
return config.prompt_dataset_source()
return config.raw_dataset_source()
def load_dataset_anywhere(source: str | Path):
"""Load a Hugging Face dataset by repo ID or a local exported folder.
Tries `load_from_disk` first when the path exists, then falls back to
`load_dataset` so users can pass either an HF ID or a local copy.
"""
try:
from datasets import load_dataset, load_from_disk
except ImportError as exc:
raise RuntimeError("Install ML dependencies with: python -m pip install -e '.[ml]'") from exc
source_text = str(source)
source_path = Path(source_text).expanduser()
if source_path.exists():
try:
return load_from_disk(str(source_path))
except Exception as exc:
try:
return load_dataset(str(source_path))
except Exception as second_exc:
raise RuntimeError(
f"Could not load local dataset folder {source_path}. "
f"Tried load_from_disk and load_dataset. Errors: {exc}; {second_exc}"
) from second_exc
try:
return load_dataset(source_text)
except Exception as exc:
raise RuntimeError(f"Could not load Hugging Face dataset source {source_text}: {exc}") from exc
def load_structured_items(source: str | Path | None = None) -> tuple[list[Item], list[Item], list[Item]]:
dataset_source = resolve_dataset_source(source, purpose="structured")
dataset = load_dataset_anywhere(dataset_source)
_validate_dataset_dict(dataset, STRUCTURED_REQUIRED_FIELDS, dataset_source, "structured")
return (
[Item.from_mapping(row) for row in dataset["train"]],
[Item.from_mapping(row) for row in _optional_split(dataset, "validation")],
[Item.from_mapping(row) for row in dataset["test"]],
)
def load_prompt_examples(source: str | Path | None = None) -> dict[str, list[PromptExample]]:
dataset_source = resolve_dataset_source(source, purpose="prompt")
dataset = load_dataset_anywhere(dataset_source)
_validate_dataset_dict(dataset, PROMPT_REQUIRED_FIELDS, dataset_source, "prompt")
return {
split: [PromptExample(prompt=str(row["prompt"]), completion=str(row["completion"])) for row in dataset[split]]
for split in dataset.keys()
}
def validate_structured_items(items: Iterable[Item], source: str | Path) -> None:
missing_rows = []
for index, item in enumerate(items):
if not item.title or not item.category or item.price <= 0 or not item.summary:
missing_rows.append(index)
if len(missing_rows) >= 5:
break
if missing_rows:
raise DatasetSchemaError(
f"Structured dataset {source} has rows missing title/category/positive price/summary. "
f"Example bad row indexes: {missing_rows}. Use items_lite/items_full, not prompt or raw datasets."
)
def validate_prompt_examples(examples: Iterable[PromptExample], source: str | Path) -> None:
missing_rows = []
for index, example in enumerate(examples):
if not example.prompt or not example.completion:
missing_rows.append(index)
if len(missing_rows) >= 5:
break
if missing_rows:
raise DatasetSchemaError(
f"Prompt dataset {source} has rows missing prompt/completion. "
f"Example bad row indexes: {missing_rows}. Use items_prompts_lite/items_prompts_full."
)
def _validate_dataset_dict(dataset, required_fields: set[str], source: str, purpose: DatasetPurpose) -> None:
if "train" not in dataset or "test" not in dataset:
raise DatasetSchemaError(
f"{purpose.title()} dataset {source} must include at least train and test splits. "
f"Found splits: {list(dataset.keys())}."
)
fields = set(dataset["train"].column_names)
missing = sorted(required_fields - fields)
if missing:
expected = ", ".join(sorted(required_fields))
found = ", ".join(sorted(fields))
if purpose == "structured":
hint = "Use items_lite/items_full for vector stores and structured pricing/evaluation."
elif purpose == "prompt":
hint = "Use items_prompts_lite/items_prompts_full only for prompt training/eval."
else:
hint = "Raw datasets are not app-ready by default."
raise DatasetSchemaError(
f"{purpose.title()} dataset {source} has the wrong schema. "
f"Missing fields: {missing}. Expected fields include: {expected}. Found fields: {found}. {hint}"
)
def _optional_split(dataset, split: str):
return dataset[split] if split in dataset else []