Spaces:
Sleeping
Sleeping
| from typing import Dict, Any | |
| from pydantic import ValidationError | |
| import json | |
| from schema import ScenarioPlan, TaskPlan | |
| from llm_router import cohere_chat, open_fallback_chat | |
| _PARSER_PROMPT = """You are a Canadian healthcare analysis planner. | |
| Read the scenario and produce a JSON plan with tasks that exactly match the scenario requests. | |
| Each task must specify: title, format, data_key (if you can infer), and any needed steps: | |
| - filter: boolean expression using dataframe column names | |
| - derive: list of 'col = expression' for calculated columns | |
| - joins: list of objects {right_key, left_on, right_on, how} | |
| - group_by: list of columns | |
| - agg: list of aggregations like 'avg(x)', 'median(y)', 'p90(z)', 'count(*)' | |
| - pivot: {"index":[...], "columns":"...", "values":"..."} | |
| - sort_by, sort_dir, top | |
| - fields: columns to output in order | |
| - chart + encodings: when a chart is requested (x,y,color,column) | |
| - number_format: per-column display formats (e.g., {"wait":"0", "rate":"0.0%"}) | |
| If you cannot infer a field/column name, leave it as-is; the executor will attempt to resolve. | |
| Output STRICT JSON only with keys: tasks, notes. DO NOT include explanations outside JSON.""" | |
| def build_parser_prompt(scenario_text: str, dataset_catalog: Dict[str, list]) -> str: | |
| cat_lines = [] | |
| for k, cols in dataset_catalog.items(): | |
| cat_lines.append(f"- {k}: {', '.join(cols)}") | |
| catalog_str = "\n".join(cat_lines) if cat_lines else "- (no files uploaded yet)" | |
| return f"""{_PARSER_PROMPT} | |
| # Uploaded datasets and their columns | |
| {catalog_str} | |
| # Scenario | |
| {scenario_text} | |
| """ | |
| def parse_to_plan(scenario_text: str, dataset_catalog: Dict[str, list]) -> ScenarioPlan: | |
| prompt = build_parser_prompt(scenario_text, dataset_catalog) | |
| raw = cohere_chat(prompt) or open_fallback_chat(prompt) | |
| if not raw: | |
| return ScenarioPlan(tasks=[TaskPlan(title="Scenario Summary", format="narrative")], notes="LLM unavailable; minimal plan.") | |
| raw = raw.strip() | |
| start = raw.find("{"); end = raw.rfind("}") | |
| if start != -1 and end != -1: | |
| raw = raw[start:end+1] | |
| try: | |
| obj = json.loads(raw) | |
| return ScenarioPlan(**obj) | |
| except (json.JSONDecodeError, ValidationError): | |
| return ScenarioPlan(tasks=[TaskPlan(title="Scenario Summary", format="narrative")], notes="Plan validation failed, fallback.") | |