Download app/sql/sql_router.py from nvikou/corrective-rag-data-assistant: direct link, hf CLI and curl.
- Browser
- Download file 3.28 kB
-
https://huggingface.co/spaces/nvikou/corrective-rag-data-assistant/resolve/main/app/sql/sql_router.py
- Command line
-
hf download hf://spaces/nvikou/corrective-rag-data-assistant/app/sql/sql_router.py
-
curl -L -o sql_router.py https://huggingface.co/spaces/nvikou/corrective-rag-data-assistant/resolve/main/app/sql/sql_router.py
3.28 kB
| """Construction de requêtes SQL paramétrées (pas de SQL libre LLM).""" | |
| from __future__ import annotations | |
| from typing import Any, Literal | |
| from app.sql.executor import execute_readonly | |
| from app.sql.sql_validator import assert_whitelisted_identifiers | |
| AGG_SQL = { | |
| "sum": "SUM", | |
| "max": "MAX", | |
| "min": "MIN", | |
| "mean": "AVG", | |
| "avg": "AVG", | |
| "count": "COUNT", | |
| } | |
| def build_sales_query( | |
| metric: Literal["chiffre_affaires", "marge"] = "chiffre_affaires", | |
| aggregation: str = "sum", | |
| filters: dict[str, Any] | None = None, | |
| group_by: str | None = None, | |
| top_n: int | None = None, | |
| ) -> tuple[str, list[Any]]: | |
| """Construit un SELECT whitelisté sur la table sales.""" | |
| filters = filters or {} | |
| agg = AGG_SQL.get(aggregation.lower()) | |
| if agg is None: | |
| raise ValueError(f"Agrégation inconnue: {aggregation}") | |
| columns = [metric, *filters.keys()] | |
| if group_by: | |
| columns.append(group_by) | |
| assert_whitelisted_identifiers("sales", columns, agg) | |
| where_parts: list[str] = [] | |
| params: list[Any] = [] | |
| for key, value in filters.items(): | |
| where_parts.append(f"LOWER({key}) = LOWER(?)") | |
| params.append(value) | |
| where_sql = f" WHERE {' AND '.join(where_parts)}" if where_parts else "" | |
| if group_by: | |
| query = ( | |
| f"SELECT {group_by} AS label, {agg}({metric}) AS value " | |
| f"FROM sales{where_sql} " | |
| f"GROUP BY {group_by} " | |
| f"ORDER BY value DESC" | |
| ) | |
| if top_n: | |
| query += f" LIMIT {int(top_n)}" | |
| else: | |
| query = ( | |
| f"SELECT {agg}({metric}) AS value, COUNT(*) AS rows_considered " | |
| f"FROM sales{where_sql}" | |
| ) | |
| return query, params | |
| def run_sql_query( | |
| metric: Literal["chiffre_affaires", "marge"] = "chiffre_affaires", | |
| aggregation: str = "sum", | |
| filters: dict | None = None, | |
| group_by: str | None = None, | |
| top_n: int | None = None, | |
| ) -> dict: | |
| """Point d'entrée métier : construit + exécute + normalise le résultat.""" | |
| try: | |
| query, params = build_sales_query( | |
| metric=metric, | |
| aggregation=aggregation, | |
| filters=filters, | |
| group_by=group_by, | |
| top_n=top_n, | |
| ) | |
| except ValueError as exc: | |
| return {"error": str(exc)} | |
| raw = execute_readonly(query, params) | |
| if "error" in raw: | |
| return raw | |
| rows = raw.get("rows", []) | |
| if group_by: | |
| ranked = [ | |
| (r["label"], float(r["value"]) if r["value"] is not None else 0.0) | |
| for r in rows | |
| ] | |
| return { | |
| "metric": metric, | |
| "aggregation": aggregation, | |
| "group_by": group_by, | |
| "filters": filters or {}, | |
| "result": ranked, | |
| "sql": raw.get("query"), | |
| "duration_ms": raw.get("duration_ms"), | |
| } | |
| value = rows[0]["value"] if rows else 0 | |
| considered = rows[0].get("rows_considered", 0) if rows else 0 | |
| return { | |
| "metric": metric, | |
| "aggregation": aggregation, | |
| "filters": filters or {}, | |
| "result": float(value) if value is not None else 0.0, | |
| "rows_considered": considered, | |
| "sql": raw.get("query"), | |
| "duration_ms": raw.get("duration_ms"), | |
| } | |