recon + sqlglot validator + drill_down package; guard ReAct dimension picks against candidate list

This commit is contained in:
2026-06-03 07:15:02 -03:00
parent e124a8a7d9
commit 2dad62f7e7
38 changed files with 1954 additions and 596 deletions

View File

@@ -15,13 +15,13 @@ from api.analyses.registry import catalog
from api.llm import chat
from api.plan.types import Plan
from api.prompts import load, render
from api.tools.schema import load_schema
from api.recon import load_recon
logger = logging.getLogger("nvi.plan.planner")
def plan(question: str) -> Plan:
schema = load_schema()
schema = load_recon()
user = render(
"planner.user",
question=question,

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
from typing import Any, Optional
@dataclass
@@ -11,17 +11,30 @@ class PlanStep:
analysis: str
args: dict[str, Any] = field(default_factory=dict)
why: str = ""
# Optional sibling step to run when the primary errors or yields no
# finding. Lets the planner hedge between a speculative Analysis (e.g.
# `drill_down`) and a safer one (e.g. `direct_answer`).
fallback: Optional["PlanStep"] = None
@classmethod
def from_dict(cls, raw: dict[str, Any]) -> "PlanStep":
fb_raw = raw.get("fallback")
return cls(
analysis=raw["analysis"],
args=raw.get("args") or {},
why=raw.get("why", ""),
fallback=cls.from_dict(fb_raw) if fb_raw else None,
)
def to_dict(self) -> dict[str, Any]:
return {"analysis": self.analysis, "args": self.args, "why": self.why}
out: dict[str, Any] = {
"analysis": self.analysis,
"args": self.args,
"why": self.why,
}
if self.fallback is not None:
out["fallback"] = self.fallback.to_dict()
return out
@dataclass