voice-rag / app /dataset_loader.py
Ganesh-0509's picture
Voice-enabled RAG system for ai4bharat/MSMARCO-XI
76c3290
Raw
History Blame Contribute Delete
6.18 kB
import hashlib
from typing import Any, Dict, List
from app.chunking import RawDoc, normalize_text
TEXT_KEYS = [
"text",
"passage_text",
"body",
"content",
"document",
"context",
"answer",
"is_selected",
]
TITLE_KEYS = [
"title",
"heading",
"url",
]
PASSAGE_FIELDS = [
"positive_passages",
"negative_passages",
"passages",
"contexts",
"documents",
"positive",
"negative",
]
def stable_doc_id(text: str, prefix: str = "doc") -> str:
h = hashlib.sha256(text.encode("utf-8")).hexdigest()[:32]
return f"{prefix}_{h}"
def get_first(obj: Dict, keys: List[str], default: str = "") -> str:
for k in keys:
val = obj.get(k)
if isinstance(val, str) and val.strip():
return val.strip()
return default
def dict_of_lists_to_items(obj: Dict) -> List[Dict]:
lengths = []
for v in obj.values():
if isinstance(v, list):
lengths.append(len(v))
if not lengths:
return [obj]
n = max(lengths)
items = []
for i in range(n):
item = {}
for k, v in obj.items():
if isinstance(v, list):
item[k] = v[i] if i < len(v) else None
else:
item[k] = v
items.append(item)
return items
def passage_items(value: Any) -> List[Dict]:
if value is None:
return []
if isinstance(value, str):
return [{"text": value}]
if isinstance(value, dict):
return dict_of_lists_to_items(value)
if isinstance(value, list):
out = []
for item in value:
if isinstance(item, str):
out.append({"text": item})
elif isinstance(item, dict):
out.append(item)
return out
return []
def row_to_docs_msmarco_xi(row: Dict, config_name: str, split: str, row_index: int) -> List[RawDoc]:
"""
Parser for the real ai4bharat/MSMARCO-XI parquet schema:
source_lang, target_lang, meta, Answer, query_id, query_type,
passages: {English_passages: [str], Translated_passages: [str], is_selected: [int]},
Eng_Query, Eng_Answer, query.
`is_selected[i] == 1` marks the passage MS MARCO judged relevant to the query;
we only attach the query to the qa_fused chunk for those, so irrelevant
(negative) passages don't get a misleadingly high-relevance fused chunk.
"""
docs: List[RawDoc] = []
query = row.get("query") or row.get("Eng_Query") or ""
query_id = row.get("query_id")
query_type = row.get("query_type") or ""
language = row.get("target_lang") or config_name or ""
passages = row.get("passages") or {}
translated = passages.get("Translated_passages") or []
is_selected = passages.get("is_selected") or []
for idx, text in enumerate(translated):
text = normalize_text(text or "")
if len(text) < 40:
continue
selected = idx < len(is_selected) and is_selected[idx] == 1
source_type = "positive_passages" if selected else "negative_passages"
docs.append(
RawDoc(
doc_id=f"{config_name}_{query_id}_{idx}",
text=text,
title="",
language=language,
source_type=source_type,
query=query if selected else "",
metadata={
"dataset_config": config_name,
"split": split,
"row_index": row_index,
"query_id": query_id,
"query_type": query_type,
"passage_index": idx,
"is_selected": selected,
},
)
)
return docs
def row_to_docs(row: Dict, config_name: str, split: str, row_index: int) -> List[RawDoc]:
if isinstance(row.get("passages"), dict) and "Translated_passages" in row["passages"]:
return row_to_docs_msmarco_xi(row, config_name, split, row_index)
docs: List[RawDoc] = []
query = (
row.get("query")
or row.get("question")
or row.get("query_text")
or ""
)
language = (
row.get("language")
or row.get("lang")
or config_name
or ""
)
for field in PASSAGE_FIELDS:
if field not in row:
continue
for j, item in enumerate(passage_items(row[field])):
text = get_first(item, TEXT_KEYS)
text = normalize_text(text)
if len(text) < 40:
continue
title = get_first(item, TITLE_KEYS)
pid = (
item.get("docid")
or item.get("doc_id")
or item.get("pid")
or stable_doc_id(text, prefix=f"{config_name}_{field}")
)
docs.append(
RawDoc(
doc_id=str(pid),
text=text,
title=title,
language=language,
source_type=field,
query=query,
metadata={
"dataset_config": config_name,
"split": split,
"row_index": row_index,
"passage_index": j,
},
)
)
# fallback for unusual/flat schemas
if not docs:
for key, value in row.items():
if isinstance(value, str) and len(value) > 120 and key not in {"query", "question", "query_id", "id"}:
docs.append(
RawDoc(
doc_id=stable_doc_id(value, prefix=f"{config_name}_{key}"),
text=value,
title="",
language=language,
source_type=key,
query=query,
metadata={
"dataset_config": config_name,
"split": split,
"row_index": row_index,
},
)
)
return docs