Dockerfile / core /validator.py
kkthakur's picture
Deploy Local Hybrid Engine
b336134
Raw
History Blame Contribute Delete
3.07 kB
"""
Pre-execution validator.
Checks that a resolved intent is compatible with the dataframe schema
before any data gets touched. This is the safety net that stops
"increase the name column" from ever reaching Polars.
"""
from __future__ import annotations
from typing import Optional
import polars as pl
from config import DATA_DIR
from core.column_registry import column_registry
# Operations that REQUIRE numeric dtype on the target column
NUMERIC_ONLY_OPS = {"increase", "decrease", "sum", "average", "min", "max"}
def get_schema(session_id: str) -> Optional[pl.Schema]:
"""Read just the Parquet metadata to get column dtypes β€” no data loaded."""
import os
from services.session_manager import session_manager
pq_path = session_manager.get_filepath(session_id)
if not os.path.exists(pq_path):
return None
try:
schema = pl.scan_parquet(pq_path).collect_schema()
return schema
except Exception:
return None
def validate_intent(session_id: str, intent: dict) -> Optional[str]:
"""Return an error string if the intent is invalid, else ``None``."""
schema = get_schema(session_id)
if schema is None:
return "Session ka data nahi mila β€” dobara upload karo"
col = intent.get("column")
op = intent.get("operation", "")
# ── Column existence (skip for operations that don't target a column) ──
no_column_ops = {"remove_duplicates"}
if op not in no_column_ops and col:
if col not in schema:
return f"'{col}' column nahi hai file mein"
# ── Numeric dtype check ────────────────────────────────────────────
if op in NUMERIC_ONLY_OPS and col:
dtype = schema[col]
if dtype not in (pl.Float64, pl.Int64, pl.Int32, pl.Float32, pl.UInt64, pl.UInt32):
return f"'{col}' numeric column nahi hai ({dtype}), ye operation apply nahi ho sakta"
# ── Filter: need condition + value ──────────────────────────────────
if op == "filter":
if intent.get("condition") is None or intent.get("filter_value") is None:
return "Filter ke liye condition aur value dono chahiye, jaise: 'salary > 50000 dikhao'"
# ── Find & Replace: need old + new ──────────────────────────────────
if op == "find_replace":
if not intent.get("old_value") or not intent.get("new_value"):
return "Find & Replace ke liye purana aur naya value dono chahiye, jaise: 'Active ko Inactive se badlo'"
# ── Rename: need new_name ───────────────────────────────────────────
if op == "rename_column":
if not intent.get("new_name"):
return "Rename ke liye naya naam chahiye, jaise: 'name ko full_name rename karo'"
return None # All good