ishaq101's picture
/fix v2 chat stream api update to analyses_messages (#7)
0066161
Raw
History Blame
4.41 kB
"""Deterministic routing gate β€” policy check over the router's intent.
After the LLM router picks an intent, the gate checks it against the per-analysis
Analysis State and returns the **effective** intent: allow as-is, or redirect. No
LLM, no I/O in `gate()` itself.
Only one rule has teeth in v1: an analytical request (`structured_flow`) requires a
validated problem statement (`problem_validated is True`); otherwise it is
redirected to `problem_statement` so the user defines the goal first. Everything
else passes through. `generate_report` is not a router intent (button / report
API), so it is not gated here.
`AnalysisState` is the locked 8-field contract (mirrors the `analysis_states` DB
table). `get_analysis_state` reads the real per-analysis row via `AnalysisStateStore`
(#9, landed); it fails closed to a not-validated stub on a missing row or read error.
See `ORCHESTRATOR_REWORK_PLAN.md` Β§4.
"""
from __future__ import annotations
from datetime import UTC, datetime
from pydantic import BaseModel, Field
from src.agents.orchestration import Intent
from src.middlewares.logging import get_logger
logger = get_logger("gate")
class AnalysisState(BaseModel):
"""Per-analysis state the Help skill + report layer read every turn.
Field names mirror the dedorch `analyses` table so the DB read swaps in without
touching readers. The goal is the user-entered `objective` + `business_questions`
(set at onboarding by Go); the old `problem_statement`/`problem_validated` gate fields
were dropped (dedorch #3 / KM-652). `report_id` is null until a report exists.
"""
id: str
analysis_title: str
objective: str = ""
business_questions: list[str] = Field(default_factory=list)
user_id: str
report_id: str | None = None
created_at: datetime
updated_at: datetime
def gate(intent: Intent, state: AnalysisState) -> Intent:
"""Return the effective intent (NEUTERED 2026-06-24 β€” passes everything through).
The `problem_validated` gate was removed: analysis is no longer gated on a validated
problem statement (the goal is now two user-entered fields, `objective` +
`business_questions`, captured at onboarding with no agent validation). Kept as a
no-op seam so gating can be restored without re-threading call sites.
"""
# Pre-2026-06-24 policy: redirect analytical requests until the goal was validated.
# if intent == "structured_flow" and not state.problem_validated:
# logger.info(
# "gate redirect",
# requested=intent,
# effective="problem_statement",
# reason="problem_not_validated",
# )
# return "problem_statement"
return intent
def stub_analysis_state() -> AnalysisState:
"""Hardcoded Analysis State for never-throw fallbacks / tests.
Shared fixture so the gate seam, the Help skill, and tests all exercise the same
shape when a real row is missing or a read fails.
"""
now = datetime.now(UTC)
return AnalysisState(
id="stub-analysis",
analysis_title="Stub analysis",
objective="",
business_questions=[],
user_id="stub-user",
report_id=None,
created_at=now,
updated_at=now,
)
async def get_analysis_state(analysis_id: str) -> AnalysisState:
"""Load the Analysis State for an analysis (shared id with the chat room).
Reads the `analysis_states` row via `AnalysisStateStore`. Never-throw seam: a
missing row (e.g. a legacy room created before this table) or a read failure
degrades to a **not-validated** stub, so the gate fails closed (β†’ steer to
`problem_statement`) rather than running ungated analysis. The store import is
lazy so this module stays import-safe without a DB.
"""
try:
from src.agents.state_store import AnalysisStateStore
state = await AnalysisStateStore().get(analysis_id)
except Exception as exc: # noqa: BLE001 β€” never-throw; fail closed to not-validated
logger.warning(
"get_analysis_state read failed β€” default not-validated",
analysis_id=analysis_id,
error=str(exc),
)
return stub_analysis_state()
if state is None:
logger.debug("analysis_state missing β€” default not-validated", analysis_id=analysis_id)
return stub_analysis_state()
return state