[KM-652] refactor: remove problem_statement skill + problem_validated gate
Browse files2026-06-24 pivot: the analysis goal is now two user-entered fields (objective + business_questions) captured at onboarding with no agent validation, so the problem_statement skill and the problem_validated gate are removed (comment, don't delete).
- Router: drop problem_statement from the Intent literal + intent_router.md (6->5 intents); comment the dispatch branch in chat_handler; remove /problem-statement from list_tools. problem_statement.py kept intact but unwired.
- Gate: gate() neutered to a no-op; call site commented in chat_handler; enable_gate marked deprecated. report_floor drops the problem_validated precondition -> floor is now >=1 completed analysis only.
- Docs: REPO_STATUS.md + DEV_PLAN.md updated.
- Tests (local, gitignored): test_gate / test_chat_handler / test_readiness updated. Full suite 284 passed, 7 skipped; ruff clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- DEV_PLAN.md +3 -2
- REPO_STATUS.md +27 -24
- src/agents/chat_handler.py +42 -35
- src/agents/gate.py +14 -11
- src/agents/handlers/problem_statement.py +4 -0
- src/agents/orchestration.py +7 -5
- src/agents/report/readiness.py +15 -11
- src/api/v1/tools.py +10 -7
- src/config/prompts/intent_router.md +5 -17
- src/config/settings.py +4 -5
|
@@ -82,8 +82,8 @@ Status legend: β¬ not started Β· π in progress Β· β
done Β· β blocked Β·
|
|
| 82 |
|
| 83 |
| # | Task | Owner | Status | Note |
|
| 84 |
|---|---|---|---|---|
|
| 85 |
-
| 1 | Comment out `problem_statement` skill **+ `Intent` literal + router prompt + gate redirect target**; remove `/problem-statement` from `list_tools` | Rifqi |
|
| 86 |
-
| 2 | Drop `problem_validated`: gate
|
| 87 |
| 3 | dedorch `analysis` migration: drop `problem_statement`/`problem_validated`, add `objective` + `business_questions`, rename `owner_id`β`user_id` | Harry | β¬ | Blocks #4 |
|
| 88 |
| 4 | Update Python `analysis` model + `state_store` + `analysis.py` to match #3; `owner_id`β`user_id` | Rifqi/Sofhia | β after #3 | getattr-tolerant so deploy order isn't a hard blocker |
|
| 89 |
| 5 | Report generator β `objective`+`business_questions`, "generated by {user}", formal outline | Sofhia | π pushed (KM-674) | Goal-shape done. Open: author name source (#19); background/EDA/insights mapping |
|
|
@@ -101,6 +101,7 @@ Status legend: β¬ not started Β· π in progress Β· β
done Β· β blocked Β·
|
|
| 101 |
| 17 | Reconcile report contract with records-based: remove/flag `chat_history` | Sofhia/Rifqi | β¬ new | Resolves #5 β #6 contradiction |
|
| 102 |
| 18 | Confirm Go owns ALL analysis-state writes + both creation gates; make Python `state_store.ensure` read-only | Rifqi + Harry | β¬ new | Extends #7 to the full decision #4 (G5) |
|
| 103 |
| 19 | Decide report author display-name source (`users.fullname` vs Go-passed name) | Sofhia | β¬ new | Raw `user_id` in a formal report looks unprofessional |
|
|
|
|
| 104 |
|
| 105 |
## 5. Critical path & sequencing
|
| 106 |
|
|
|
|
| 82 |
|
| 83 |
| # | Task | Owner | Status | Note |
|
| 84 |
|---|---|---|---|---|
|
| 85 |
+
| 1 | Comment out `problem_statement` skill **+ `Intent` literal + router prompt + gate redirect target**; remove `/problem-statement` from `list_tools` | Rifqi | β
| Done 2026-06-25 (one commit w/ #2). Unwired in `orchestration.py`, `intent_router.md`, `chat_handler.py`, `tools.py`; `problem_statement.py` kept intact |
|
| 86 |
+
| 2 | Drop `problem_validated`: gate neutered; `is_report_ready`/`report_floor` β **β₯1 completed analysis** only, no-LLM | Rifqi | β
| Done 2026-06-25. `gate.py` no-op, gate call site commented in `chat_handler.py`, `report_floor` drops the goal check. Tests updated (`test_gate`/`test_chat_handler`/`test_readiness`). Suite: **284 passed, 7 skipped**; ruff clean |
|
| 87 |
| 3 | dedorch `analysis` migration: drop `problem_statement`/`problem_validated`, add `objective` + `business_questions`, rename `owner_id`β`user_id` | Harry | β¬ | Blocks #4 |
|
| 88 |
| 4 | Update Python `analysis` model + `state_store` + `analysis.py` to match #3; `owner_id`β`user_id` | Rifqi/Sofhia | β after #3 | getattr-tolerant so deploy order isn't a hard blocker |
|
| 89 |
| 5 | Report generator β `objective`+`business_questions`, "generated by {user}", formal outline | Sofhia | π pushed (KM-674) | Goal-shape done. Open: author name source (#19); background/EDA/insights mapping |
|
|
|
|
| 101 |
| 17 | Reconcile report contract with records-based: remove/flag `chat_history` | Sofhia/Rifqi | β¬ new | Resolves #5 β #6 contradiction |
|
| 102 |
| 18 | Confirm Go owns ALL analysis-state writes + both creation gates; make Python `state_store.ensure` read-only | Rifqi + Harry | β¬ new | Extends #7 to the full decision #4 (G5) |
|
| 103 |
| 19 | Decide report author display-name source (`users.fullname` vs Go-passed name) | Sofhia | β¬ new | Raw `user_id` in a formal report looks unprofessional |
|
| 104 |
+
| 20 | **Help handoff:** update `handlers/help.py` + `help.md` β drop the `problem_validated` tier + `define_problem_statement` action (the skill it points at is gone as of #1) | Sofhia | β¬ new | Created by #1/#2 (2026-06-25). Help currently still steers users to a removed skill |
|
| 105 |
|
| 106 |
## 5. Critical path & sequencing
|
| 107 |
|
|
@@ -1,8 +1,8 @@
|
|
| 1 |
# Data Eyond β Python Agentic Service: Current Status
|
| 2 |
|
| 3 |
**Audience:** teammates onboarding onto the Python repo (`Agentic-Service-Data-Eyond-Catalog`).
|
| 4 |
-
**Scope:** what the code does **right now** (branch `pr/
|
| 5 |
-
**Snapshot date:** 2026-06-
|
| 6 |
|
| 7 |
> This file is grounded in the source, not the older design docs. Where the two
|
| 8 |
> disagree, the code wins β see [Β§11 Doc-vs-code](#11-where-the-older-docs-are-stale).
|
|
@@ -68,19 +68,21 @@ POST /chat/stream { user_id, room_id, message }
|
|
| 68 |
ββ ChatHandler.handle:
|
| 69 |
1. classify β RouterDecision [1 GPT-4o call]
|
| 70 |
2. ensure analysis-state row (get-or-create, idempotent)
|
| 71 |
-
3.
|
| 72 |
-
4. emit `intent` (internal; gates caching), then dispatch:
|
| 73 |
chat β ChatbotAgent β SSE
|
| 74 |
help β HelpAgent (state + history + readiness) β SSE
|
| 75 |
check β check_data/check_knowledge tool β rendered table [no LLM]
|
| 76 |
-
problem_statement β PS draft + validate β write state
|
| 77 |
unstructured_flow β DocumentRetriever (PGVector RAG) β ChatbotAgent β SSE
|
| 78 |
structured_flow β CatalogReader β (slow path | QueryService) β SSE
|
| 79 |
-
|
| 80 |
```
|
| 81 |
|
| 82 |
Only the `chat` intent is cached (stateless). Messages persist on `done`.
|
| 83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
---
|
| 85 |
|
| 86 |
## 5. Report lifecycle
|
|
@@ -90,7 +92,7 @@ The report is a **dedicated API, not a chat route** (`src/api/v1/report.py`):
|
|
| 90 |
```
|
| 91 |
POST /report?analysis_id&user_id
|
| 92 |
ββ load analysis state; enforce the report FLOOR
|
| 93 |
-
β (
|
| 94 |
ββ ReportGenerator.generate (src/agents/report/generator.py):
|
| 95 |
β read persisted AnalysisRecords (list_for_analysis)
|
| 96 |
β deterministically assemble findings / caveats / open-questions /
|
|
@@ -115,9 +117,8 @@ Two facts to internalise:
|
|
| 115 |
|
| 116 |
## 6. Feature list (what's built)
|
| 117 |
|
| 118 |
-
- **
|
| 119 |
-
- **
|
| 120 |
-
- **Skills:** `help` (LLM, state-aware next-step guidance), `check` (no-LLM data/document inventory), `problem_statement` (LLM drafter + completeness validator).
|
| 121 |
- **Slow analytical path:** Planner β TaskRunner β Assembler (static plan, degrade-and-continue, 3 LLM calls fixed).
|
| 122 |
- **Structured query engine:** catalog-driven JSON IR β deterministic SQL/pandas compiler β read-only executor, with **single-level FK joins** (DB sources only).
|
| 123 |
- **Unstructured RAG** over PGVector.
|
|
@@ -132,7 +133,7 @@ Two facts to internalise:
|
|
| 132 |
|
| 133 |
| Endpoint | Purpose | Caller |
|
| 134 |
|---|---|---|
|
| 135 |
-
| `POST /chat/stream` | Main chat SSE (router β
|
| 136 |
| `DELETE /chat/cache` Β· `/chat/cache/room/{id}` Β· `/retrieval/cache/{user_id}` | Cache management | internal / ops |
|
| 137 |
| `POST /analysis/create` Β· `GET /analysis` Β· `GET /analysis/{id}` | Analysis-session CRUD (state + room + bindings created atomically) | intended FE β Go |
|
| 138 |
| `POST /report` Β· `GET /report/{id}` Β· `GET /report/{id}/{ver}` | Report generate / list / fetch | FE β Go (report button) |
|
|
@@ -171,16 +172,18 @@ Joins are single-level equi-joins to a related table **in the same source**, FK-
|
|
| 171 |
|
| 172 |
### Router β `src/agents/orchestration.py`
|
| 173 |
One GPT-4o structured-output call β `RouterDecision{intent, rewritten_query, confidence}`,
|
| 174 |
-
`intent β {chat, help,
|
|
|
|
| 175 |
*handler* classifier: `structured_flow` = slow path, `unstructured_flow` = fast RAG; the
|
| 176 |
data-modality mix on the slow path is the Planner's job. Prompt: `src/config/prompts/intent_router.md`.
|
| 177 |
|
| 178 |
### Gate β `src/agents/gate.py`
|
| 179 |
-
|
| 180 |
-
`
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
|
|
|
| 184 |
|
| 185 |
### Skills β `src/agents/handlers/`
|
| 186 |
- `help.py` β LLM (streamed). A consistency guard derives the *allowed* actions from state
|
|
@@ -188,9 +191,8 @@ only when `ENABLE_GATE` and `analysis_id` are present.
|
|
| 188 |
isn't validated or there's nothing to report. Consumes a deterministic readiness signal.
|
| 189 |
- `check.py` β **no LLM.** Keyword cues route to `check_data`, `check_knowledge`, or both
|
| 190 |
(helicopter view, concurrent). Renders tool tables to markdown.
|
| 191 |
-
- `problem_statement.py` β
|
| 192 |
-
|
| 193 |
-
valid draft, writes `problem_statement` + `problem_validated=True`.
|
| 194 |
|
| 195 |
### Slow path β `src/agents/slow_path/` + `src/agents/planner/`
|
| 196 |
- **Planner** (`planner/service.py`) β 1 LLM call β `TaskList` (DAG of tool-call chains). 8-check
|
|
@@ -231,8 +233,9 @@ real `analytics_registry()`.
|
|
| 231 |
### Report β `src/agents/report/`
|
| 232 |
`generator.py` reads records, deterministically assembles structured fields, 1 LLM call for the
|
| 233 |
executive summary; `store.py` versions under an advisory lock and persists markdown to dedorch
|
| 234 |
-
`reports`; `readiness.py` defines the **report floor** (
|
| 235 |
-
shared by the report API and the Help
|
|
|
|
| 236 |
|
| 237 |
### Observability β Langfuse
|
| 238 |
The endpoint's `ChatHandler` runs with `enable_tracing=True`. One trace per request groups
|
|
@@ -247,7 +250,7 @@ only.
|
|
| 247 |
| Flag | Where | Default | Effect |
|
| 248 |
|---|---|---|---|
|
| 249 |
| `ENABLE_SLOW_PATH` | `settings.enable_slow_path` | **off** | Route `structured_flow` through Planner/TaskRunner/Assembler (vs single-query `QueryService`). Records persist only on the slow path β reports require this on. |
|
| 250 |
-
| `ENABLE_GATE` | `settings.enable_gate` | **off** |
|
| 251 |
| `SKIP_INIT_DB` | env, `main.py` | off | Skip `create_all` on startup β the dedorch cutover switch (Go owns dedorch migrations). |
|
| 252 |
| `enable_tracing` | hardcoded `True` in `chat.py` | on (endpoint) | Langfuse tracing. |
|
| 253 |
|
|
@@ -260,7 +263,7 @@ copies disagree with the current code on:
|
|
| 260 |
|
| 261 |
| Topic | Old docs | Current code |
|
| 262 |
|---|---|---|
|
| 263 |
-
| Router | 3-way `source_hint` (chat/unstructured/structured) | Flat **
|
| 264 |
| Joins in IR | "single-table only; deferred" | **Single-level FK-backed joins** (DB sources only) |
|
| 265 |
| Analysis / report / gate / slow path | "Phase 2 spine only" | All built and present |
|
| 266 |
| `analysis_id` | open question | resolved: **`analysis_id == room_id`** |
|
|
|
|
| 1 |
# Data Eyond β Python Agentic Service: Current Status
|
| 2 |
|
| 3 |
**Audience:** teammates onboarding onto the Python repo (`Agentic-Service-Data-Eyond-Catalog`).
|
| 4 |
+
**Scope:** what the code does **right now** (branch `pr/4`, ticket KM-652). Describes current state only β no roadmap or to-dos.
|
| 5 |
+
**Snapshot date:** 2026-06-25.
|
| 6 |
|
| 7 |
> This file is grounded in the source, not the older design docs. Where the two
|
| 8 |
> disagree, the code wins β see [Β§11 Doc-vs-code](#11-where-the-older-docs-are-stale).
|
|
|
|
| 68 |
ββ ChatHandler.handle:
|
| 69 |
1. classify β RouterDecision [1 GPT-4o call]
|
| 70 |
2. ensure analysis-state row (get-or-create, idempotent)
|
| 71 |
+
3. emit `intent` (internal; gates caching), then dispatch:
|
|
|
|
| 72 |
chat β ChatbotAgent β SSE
|
| 73 |
help β HelpAgent (state + history + readiness) β SSE
|
| 74 |
check β check_data/check_knowledge tool β rendered table [no LLM]
|
|
|
|
| 75 |
unstructured_flow β DocumentRetriever (PGVector RAG) β ChatbotAgent β SSE
|
| 76 |
structured_flow β CatalogReader β (slow path | QueryService) β SSE
|
| 77 |
+
4. SSE events: intent (internal), sources, chunk, status, done | error
|
| 78 |
```
|
| 79 |
|
| 80 |
Only the `chat` intent is cached (stateless). Messages persist on `done`.
|
| 81 |
|
| 82 |
+
> The router emits **5 intents** now. The `problem_statement` skill and the `problem_validated`
|
| 83 |
+
> gate were removed 2026-06-25 (KM-652) β the analysis goal is two user-entered fields
|
| 84 |
+
> (`objective` + `business_questions`) captured at onboarding, with no agent validation.
|
| 85 |
+
|
| 86 |
---
|
| 87 |
|
| 88 |
## 5. Report lifecycle
|
|
|
|
| 92 |
```
|
| 93 |
POST /report?analysis_id&user_id
|
| 94 |
ββ load analysis state; enforce the report FLOOR
|
| 95 |
+
β (β₯1 substantive analyze_* success) β else 409
|
| 96 |
ββ ReportGenerator.generate (src/agents/report/generator.py):
|
| 97 |
β read persisted AnalysisRecords (list_for_analysis)
|
| 98 |
β deterministically assemble findings / caveats / open-questions /
|
|
|
|
| 117 |
|
| 118 |
## 6. Feature list (what's built)
|
| 119 |
|
| 120 |
+
- **5-intent handler router** (`chat`/`help`/`check`/`unstructured_flow`/`structured_flow`) with history-aware query rewriting (EN/ID).
|
| 121 |
+
- **Skills:** `help` (LLM, state-aware next-step guidance), `check` (no-LLM data/document inventory). *(The `problem_statement` skill and the `problem_validated` gate were removed 2026-06-25 β KM-652; `gate.py` kept as a no-op seam, `problem_statement.py` kept but unwired.)*
|
|
|
|
| 122 |
- **Slow analytical path:** Planner β TaskRunner β Assembler (static plan, degrade-and-continue, 3 LLM calls fixed).
|
| 123 |
- **Structured query engine:** catalog-driven JSON IR β deterministic SQL/pandas compiler β read-only executor, with **single-level FK joins** (DB sources only).
|
| 124 |
- **Unstructured RAG** over PGVector.
|
|
|
|
| 133 |
|
| 134 |
| Endpoint | Purpose | Caller |
|
| 135 |
|---|---|---|
|
| 136 |
+
| `POST /chat/stream` | Main chat SSE (router β dispatch) | FE β Go β Python (the only FEβPython call today) |
|
| 137 |
| `DELETE /chat/cache` Β· `/chat/cache/room/{id}` Β· `/retrieval/cache/{user_id}` | Cache management | internal / ops |
|
| 138 |
| `POST /analysis/create` Β· `GET /analysis` Β· `GET /analysis/{id}` | Analysis-session CRUD (state + room + bindings created atomically) | intended FE β Go |
|
| 139 |
| `POST /report` Β· `GET /report/{id}` Β· `GET /report/{id}/{ver}` | Report generate / list / fetch | FE β Go (report button) |
|
|
|
|
| 172 |
|
| 173 |
### Router β `src/agents/orchestration.py`
|
| 174 |
One GPT-4o structured-output call β `RouterDecision{intent, rewritten_query, confidence}`,
|
| 175 |
+
`intent β {chat, help, check, unstructured_flow, structured_flow}` (`problem_statement` removed
|
| 176 |
+
2026-06-25). It's a
|
| 177 |
*handler* classifier: `structured_flow` = slow path, `unstructured_flow` = fast RAG; the
|
| 178 |
data-modality mix on the slow path is the Planner's job. Prompt: `src/config/prompts/intent_router.md`.
|
| 179 |
|
| 180 |
### Gate β `src/agents/gate.py`
|
| 181 |
+
**Neutered 2026-06-25 (KM-652):** `gate()` now passes every intent through unchanged β the
|
| 182 |
+
`problem_validated` redirect was removed (the goal is user-entered, no agent validation). The
|
| 183 |
+
function + `AnalysisState` contract are kept as a no-op seam; the call site in
|
| 184 |
+
`chat_handler.handle` is commented out. `AnalysisState` still carries (id, analysis_title,
|
| 185 |
+
problem_statement, problem_validated, owner_id, report_id, created_at, updated_at) until the
|
| 186 |
+
dedorch state migration (#3/#4) renames it.
|
| 187 |
|
| 188 |
### Skills β `src/agents/handlers/`
|
| 189 |
- `help.py` β LLM (streamed). A consistency guard derives the *allowed* actions from state
|
|
|
|
| 191 |
isn't validated or there's nothing to report. Consumes a deterministic readiness signal.
|
| 192 |
- `check.py` β **no LLM.** Keyword cues route to `check_data`, `check_knowledge`, or both
|
| 193 |
(helicopter view, concurrent). Renders tool tables to markdown.
|
| 194 |
+
- `problem_statement.py` β **unwired 2026-06-25** (no longer routed to; file kept intact). Was an
|
| 195 |
+
LLM drafter that validated a goal and wrote `problem_validated`.
|
|
|
|
| 196 |
|
| 197 |
### Slow path β `src/agents/slow_path/` + `src/agents/planner/`
|
| 198 |
- **Planner** (`planner/service.py`) β 1 LLM call β `TaskList` (DAG of tool-call chains). 8-check
|
|
|
|
| 233 |
### Report β `src/agents/report/`
|
| 234 |
`generator.py` reads records, deterministically assembles structured fields, 1 LLM call for the
|
| 235 |
executive summary; `store.py` versions under an advisory lock and persists markdown to dedorch
|
| 236 |
+
`reports`; `readiness.py` defines the **report floor** (β₯1 successful `analyze_*`; the
|
| 237 |
+
`problem_validated` precondition was dropped 2026-06-25) shared by the report API and the Help
|
| 238 |
+
readiness signal so the two can't disagree.
|
| 239 |
|
| 240 |
### Observability β Langfuse
|
| 241 |
The endpoint's `ChatHandler` runs with `enable_tracing=True`. One trace per request groups
|
|
|
|
| 250 |
| Flag | Where | Default | Effect |
|
| 251 |
|---|---|---|---|
|
| 252 |
| `ENABLE_SLOW_PATH` | `settings.enable_slow_path` | **off** | Route `structured_flow` through Planner/TaskRunner/Assembler (vs single-query `QueryService`). Records persist only on the slow path β reports require this on. |
|
| 253 |
+
| `ENABLE_GATE` | `settings.enable_gate` | **off** | **Deprecated 2026-06-25** β gate neutered; the flag has no effect. Kept to avoid `.env` churn. |
|
| 254 |
| `SKIP_INIT_DB` | env, `main.py` | off | Skip `create_all` on startup β the dedorch cutover switch (Go owns dedorch migrations). |
|
| 255 |
| `enable_tracing` | hardcoded `True` in `chat.py` | on (endpoint) | Langfuse tracing. |
|
| 256 |
|
|
|
|
| 263 |
|
| 264 |
| Topic | Old docs | Current code |
|
| 265 |
|---|---|---|
|
| 266 |
+
| Router | 3-way `source_hint` (chat/unstructured/structured) | Flat **5-intent** `RouterDecision` (was 6; `problem_statement` removed 2026-06-25) |
|
| 267 |
| Joins in IR | "single-table only; deferred" | **Single-level FK-backed joins** (DB sources only) |
|
| 268 |
| Analysis / report / gate / slow path | "Phase 2 spine only" | All built and present |
|
| 269 |
| `analysis_id` | open question | resolved: **`analysis_id == room_id`** |
|
|
@@ -9,8 +9,10 @@ End-to-end flow per user message:
|
|
| 9 |
- `unstructured_flow` β DocumentRetriever (RAG over PGVector) β
|
| 10 |
list[DocumentChunk].
|
| 11 |
- `check` β check_data / check_knowledge tool β rendered table.
|
| 12 |
-
- `problem_statement` β PS skill: draft + validate β write analysis state.
|
| 13 |
- `help` β Help skill: analysis state + history β streamed guidance.
|
|
|
|
|
|
|
|
|
|
| 14 |
3. `ChatbotAgent.astream` β yield text tokens.
|
| 15 |
4. Wrap each step into an SSE-style event dict so the API endpoint can
|
| 16 |
stream them as Server-Sent Events.
|
|
@@ -39,7 +41,9 @@ from src.retrieval.base import RetrievalResult
|
|
| 39 |
from .chatbot import ChatbotAgent, DocumentChunk
|
| 40 |
from .handlers.check import run_check
|
| 41 |
from .handlers.help import HelpAgent
|
| 42 |
-
|
|
|
|
|
|
|
| 43 |
from .orchestration import OrchestratorAgent
|
| 44 |
|
| 45 |
if TYPE_CHECKING:
|
|
@@ -114,8 +118,8 @@ class ChatHandler:
|
|
| 114 |
# `#10` data-source binding: scopes structured_flow's catalog to the sources
|
| 115 |
# the analysis is bound to. Injectable for tests; fail-open when absent.
|
| 116 |
self._binding_store = binding_store
|
| 117 |
-
# Deterministic gate
|
| 118 |
-
#
|
| 119 |
self._enable_gate = enable_gate
|
| 120 |
|
| 121 |
# ------------------------------------------------------------------
|
|
@@ -244,9 +248,8 @@ class ChatHandler:
|
|
| 244 |
|
| 245 |
intent = decision.intent
|
| 246 |
# ---- 1a. Ensure session state row (T-A) ----------------------
|
| 247 |
-
# Rooms created via /room/create have no `
|
| 248 |
-
# the
|
| 249 |
-
# no-op. Lazily get-or-create it (idempotent) so any session is gate-ready.
|
| 250 |
analysis_state: AnalysisState | None = None
|
| 251 |
if analysis_id:
|
| 252 |
try:
|
|
@@ -256,18 +259,20 @@ class ChatHandler:
|
|
| 256 |
"analysis state ensure failed", analysis_id=analysis_id, error=str(e)
|
| 257 |
)
|
| 258 |
|
| 259 |
-
# ---- 1b. Gate (
|
| 260 |
-
#
|
| 261 |
-
#
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
|
|
|
|
|
|
| 271 |
|
| 272 |
# The `intent` event is consumed by the endpoint (it gates response caching
|
| 273 |
# on the effective intent) and is NOT forwarded to the frontend. We emit the
|
|
@@ -337,22 +342,24 @@ class ChatHandler:
|
|
| 337 |
yield {"event": "chunk", "data": text}
|
| 338 |
yield {"event": "done", "data": ""}
|
| 339 |
return
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
|
|
|
|
|
|
| 356 |
elif intent == "help":
|
| 357 |
try:
|
| 358 |
state = analysis_state or await self._load_analysis_state(analysis_id)
|
|
|
|
| 9 |
- `unstructured_flow` β DocumentRetriever (RAG over PGVector) β
|
| 10 |
list[DocumentChunk].
|
| 11 |
- `check` β check_data / check_knowledge tool β rendered table.
|
|
|
|
| 12 |
- `help` β Help skill: analysis state + history β streamed guidance.
|
| 13 |
+
|
| 14 |
+
(`problem_statement` was removed 2026-06-24 β the goal is now user-entered
|
| 15 |
+
`objective` + `business_questions` captured at onboarding, with no agent skill.)
|
| 16 |
3. `ChatbotAgent.astream` β yield text tokens.
|
| 17 |
4. Wrap each step into an SSE-style event dict so the API endpoint can
|
| 18 |
stream them as Server-Sent Events.
|
|
|
|
| 41 |
from .chatbot import ChatbotAgent, DocumentChunk
|
| 42 |
from .handlers.check import run_check
|
| 43 |
from .handlers.help import HelpAgent
|
| 44 |
+
# `run_problem_statement` unwired 2026-06-24 (problem_statement removed from the router).
|
| 45 |
+
# `ProblemStatementAgent` kept β still referenced by the constructor + _get_ps_agent.
|
| 46 |
+
from .handlers.problem_statement import ProblemStatementAgent
|
| 47 |
from .orchestration import OrchestratorAgent
|
| 48 |
|
| 49 |
if TYPE_CHECKING:
|
|
|
|
| 118 |
# `#10` data-source binding: scopes structured_flow's catalog to the sources
|
| 119 |
# the analysis is bound to. Injectable for tests; fail-open when absent.
|
| 120 |
self._binding_store = binding_store
|
| 121 |
+
# Deterministic gate β DEPRECATED 2026-06-24 (problem_validated gate removed).
|
| 122 |
+
# Unused flag; the gate call site in handle() is commented out.
|
| 123 |
self._enable_gate = enable_gate
|
| 124 |
|
| 125 |
# ------------------------------------------------------------------
|
|
|
|
| 248 |
|
| 249 |
intent = decision.intent
|
| 250 |
# ---- 1a. Ensure session state row (T-A) ----------------------
|
| 251 |
+
# Rooms created via /room/create have no `analysis` row. Without one, Help and
|
| 252 |
+
# the report_id write-back silently no-op. Lazily get-or-create it (idempotent).
|
|
|
|
| 253 |
analysis_state: AnalysisState | None = None
|
| 254 |
if analysis_id:
|
| 255 |
try:
|
|
|
|
| 259 |
"analysis state ensure failed", analysis_id=analysis_id, error=str(e)
|
| 260 |
)
|
| 261 |
|
| 262 |
+
# ---- 1b. Gate (REMOVED 2026-06-24) ---------------------------
|
| 263 |
+
# The problem_validated gate was dropped: structured_flow is no longer
|
| 264 |
+
# redirected to problem_statement (the goal is now user-entered objective +
|
| 265 |
+
# business_questions, no agent validation). `gate()` is neutered to a no-op; the
|
| 266 |
+
# call site is left commented for restorability.
|
| 267 |
+
# if self._enable_gate and analysis_id:
|
| 268 |
+
# from .gate import gate, stub_analysis_state
|
| 269 |
+
#
|
| 270 |
+
# intent = gate(
|
| 271 |
+
# intent,
|
| 272 |
+
# analysis_state
|
| 273 |
+
# if analysis_state is not None
|
| 274 |
+
# else stub_analysis_state(problem_validated=False),
|
| 275 |
+
# )
|
| 276 |
|
| 277 |
# The `intent` event is consumed by the endpoint (it gates response caching
|
| 278 |
# on the effective intent) and is NOT forwarded to the frontend. We emit the
|
|
|
|
| 342 |
yield {"event": "chunk", "data": text}
|
| 343 |
yield {"event": "done", "data": ""}
|
| 344 |
return
|
| 345 |
+
# problem_statement dispatch removed 2026-06-24 (skill unwired; intent no longer
|
| 346 |
+
# emitted by the router). Branch kept commented for restorability.
|
| 347 |
+
# elif intent == "problem_statement":
|
| 348 |
+
# try:
|
| 349 |
+
# text = await run_problem_statement(
|
| 350 |
+
# message,
|
| 351 |
+
# analysis_id,
|
| 352 |
+
# agent=self._get_ps_agent(),
|
| 353 |
+
# store=self._get_state_store(),
|
| 354 |
+
# history=history,
|
| 355 |
+
# )
|
| 356 |
+
# except Exception as e:
|
| 357 |
+
# logger.error("problem_statement route failed", user_id=user_id, error=str(e))
|
| 358 |
+
# yield {"event": "error", "data": f"Problem statement failed: {e}"}
|
| 359 |
+
# return
|
| 360 |
+
# yield {"event": "chunk", "data": text}
|
| 361 |
+
# yield {"event": "done", "data": ""}
|
| 362 |
+
# return
|
| 363 |
elif intent == "help":
|
| 364 |
try:
|
| 365 |
state = analysis_state or await self._load_analysis_state(analysis_id)
|
|
@@ -47,19 +47,22 @@ class AnalysisState(BaseModel):
|
|
| 47 |
|
| 48 |
|
| 49 |
def gate(intent: Intent, state: AnalysisState) -> Intent:
|
| 50 |
-
"""Return the effective intent
|
| 51 |
|
| 52 |
-
`
|
| 53 |
-
|
|
|
|
|
|
|
| 54 |
"""
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
|
|
|
| 63 |
return intent
|
| 64 |
|
| 65 |
|
|
|
|
| 47 |
|
| 48 |
|
| 49 |
def gate(intent: Intent, state: AnalysisState) -> Intent:
|
| 50 |
+
"""Return the effective intent (NEUTERED 2026-06-24 β passes everything through).
|
| 51 |
|
| 52 |
+
The `problem_validated` gate was removed: analysis is no longer gated on a validated
|
| 53 |
+
problem statement (the goal is now two user-entered fields, `objective` +
|
| 54 |
+
`business_questions`, captured at onboarding with no agent validation). Kept as a
|
| 55 |
+
no-op seam so gating can be restored without re-threading call sites.
|
| 56 |
"""
|
| 57 |
+
# Pre-2026-06-24 policy: redirect analytical requests until the goal was validated.
|
| 58 |
+
# if intent == "structured_flow" and not state.problem_validated:
|
| 59 |
+
# logger.info(
|
| 60 |
+
# "gate redirect",
|
| 61 |
+
# requested=intent,
|
| 62 |
+
# effective="problem_statement",
|
| 63 |
+
# reason="problem_not_validated",
|
| 64 |
+
# )
|
| 65 |
+
# return "problem_statement"
|
| 66 |
return intent
|
| 67 |
|
| 68 |
|
|
@@ -1,3 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""Problem Statement skill β guide the user to a usable problem statement.
|
| 2 |
|
| 3 |
Routed by the orchestrator (intent `problem_statement`) and callable as a skill.
|
|
|
|
| 1 |
+
# UNWIRED 2026-06-24: the problem_statement skill is no longer routed to β it was removed
|
| 2 |
+
# from the 6-intent router and the gate (the goal is now user-entered objective +
|
| 3 |
+
# business_questions, no agent validation). File kept intact (comment, don't delete) so
|
| 4 |
+
# the skill can be restored if needed. See DEV_PLAN.md #1.
|
| 5 |
"""Problem Statement skill β guide the user to a usable problem statement.
|
| 6 |
|
| 7 |
Routed by the orchestrator (intent `problem_statement`) and callable as a skill.
|
|
@@ -32,7 +32,9 @@ logger = get_logger("orchestrator")
|
|
| 32 |
Intent = Literal[
|
| 33 |
"chat",
|
| 34 |
"help",
|
| 35 |
-
"problem_statement",
|
|
|
|
|
|
|
| 36 |
"check",
|
| 37 |
"unstructured_flow",
|
| 38 |
"structured_flow",
|
|
@@ -53,10 +55,10 @@ class RouterDecision(BaseModel):
|
|
| 53 |
...,
|
| 54 |
description=(
|
| 55 |
"Handler route for this message: 'chat' (conversational, no data), "
|
| 56 |
-
"'help' (what-to-do-next guidance), '
|
| 57 |
-
"
|
| 58 |
-
"
|
| 59 |
-
"
|
| 60 |
),
|
| 61 |
)
|
| 62 |
rewritten_query: str | None = Field(
|
|
|
|
| 32 |
Intent = Literal[
|
| 33 |
"chat",
|
| 34 |
"help",
|
| 35 |
+
# "problem_statement", # removed 2026-06-24 β the analysis goal is now two
|
| 36 |
+
# # user-entered fields (objective + business_questions),
|
| 37 |
+
# # captured at onboarding with no agent validation.
|
| 38 |
"check",
|
| 39 |
"unstructured_flow",
|
| 40 |
"structured_flow",
|
|
|
|
| 55 |
...,
|
| 56 |
description=(
|
| 57 |
"Handler route for this message: 'chat' (conversational, no data), "
|
| 58 |
+
"'help' (what-to-do-next guidance), 'check' (inventory: what "
|
| 59 |
+
"data/documents exist), 'unstructured_flow' (answer from documents, fast "
|
| 60 |
+
"RAG), or 'structured_flow' (analytical question over data, slow Planner "
|
| 61 |
+
"path)."
|
| 62 |
),
|
| 63 |
)
|
| 64 |
rewritten_query: str | None = Field(
|
|
@@ -7,8 +7,8 @@ not a judgement.
|
|
| 7 |
|
| 8 |
The rule mirrors what makes a real report non-empty and worth generating, so Help can
|
| 9 |
never suggest an action that would 409 or produce a duplicate:
|
| 10 |
-
1.
|
| 11 |
-
|
| 12 |
2. at least one **substantive** persisted `AnalysisRecord` β a record whose
|
| 13 |
*analysis* task succeeded. A failed run still persists a record WITH findings
|
| 14 |
(they narrate the failure), and data-access tasks (check_/retrieve_) succeed even
|
|
@@ -45,7 +45,7 @@ if TYPE_CHECKING:
|
|
| 45 |
logger = get_logger("report_readiness")
|
| 46 |
|
| 47 |
# Human-readable gaps surfaced to the user via Help (kept stable for the prompt).
|
| 48 |
-
_MISSING_PROBLEM
|
| 49 |
_MISSING_ANALYSIS = "at least one completed analysis"
|
| 50 |
_MISSING_DELTA = "a new analysis since the last report"
|
| 51 |
|
|
@@ -91,18 +91,22 @@ async def report_floor(
|
|
| 91 |
*,
|
| 92 |
record_store=None,
|
| 93 |
) -> tuple[list[str], list]:
|
| 94 |
-
"""The report **floor**:
|
| 95 |
|
| 96 |
Returns `(missing, substantive_records)`. This is the shared gate both the Help
|
| 97 |
readiness signal AND the report API enforce, so the button and Help can't drift
|
| 98 |
-
(T-D / T11).
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
a
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
"""
|
| 103 |
missing: list[str] = []
|
| 104 |
-
if not state.problem_validated:
|
| 105 |
-
missing.append(_MISSING_PROBLEM)
|
| 106 |
|
| 107 |
substantive: list = []
|
| 108 |
if analysis_id:
|
|
@@ -116,7 +120,7 @@ async def report_floor(
|
|
| 116 |
analysis_id=analysis_id,
|
| 117 |
error=str(exc),
|
| 118 |
)
|
| 119 |
-
return [
|
| 120 |
|
| 121 |
if not substantive:
|
| 122 |
missing.append(_MISSING_ANALYSIS)
|
|
|
|
| 7 |
|
| 8 |
The rule mirrors what makes a real report non-empty and worth generating, so Help can
|
| 9 |
never suggest an action that would 409 or produce a duplicate:
|
| 10 |
+
1. (removed 2026-06-24) a validated problem statement β the report no longer gates on
|
| 11 |
+
the goal (now user-entered `objective` + `business_questions`, no agent validation).
|
| 12 |
2. at least one **substantive** persisted `AnalysisRecord` β a record whose
|
| 13 |
*analysis* task succeeded. A failed run still persists a record WITH findings
|
| 14 |
(they narrate the failure), and data-access tasks (check_/retrieve_) succeed even
|
|
|
|
| 45 |
logger = get_logger("report_readiness")
|
| 46 |
|
| 47 |
# Human-readable gaps surfaced to the user via Help (kept stable for the prompt).
|
| 48 |
+
# _MISSING_PROBLEM retired 2026-06-24 β the report no longer gates on a validated goal.
|
| 49 |
_MISSING_ANALYSIS = "at least one completed analysis"
|
| 50 |
_MISSING_DELTA = "a new analysis since the last report"
|
| 51 |
|
|
|
|
| 91 |
*,
|
| 92 |
record_store=None,
|
| 93 |
) -> tuple[list[str], list]:
|
| 94 |
+
"""The report **floor**: β₯1 substantive analysis.
|
| 95 |
|
| 96 |
Returns `(missing, substantive_records)`. This is the shared gate both the Help
|
| 97 |
readiness signal AND the report API enforce, so the button and Help can't drift
|
| 98 |
+
(T-D / T11).
|
| 99 |
+
|
| 100 |
+
CHANGED 2026-06-24: the `problem_validated` precondition was dropped β analysis is no
|
| 101 |
+
longer gated on a validated goal (now user-entered `objective` + `business_questions`,
|
| 102 |
+
no agent validation), so the only floor is "is there anything worth reporting". The
|
| 103 |
+
delta-since-report check stays advisory and lives only in `is_report_ready`; the
|
| 104 |
+
report button is always allowed to cut a new version (decision 4A). Fails closed
|
| 105 |
+
(counts as missing analysis) on a record-store read error. `record_store` is
|
| 106 |
+
injectable for tests. `state` stays in the signature (callers + the `is_report_ready`
|
| 107 |
+
delta check use it).
|
| 108 |
"""
|
| 109 |
missing: list[str] = []
|
|
|
|
|
|
|
| 110 |
|
| 111 |
substantive: list = []
|
| 112 |
if analysis_id:
|
|
|
|
| 120 |
analysis_id=analysis_id,
|
| 121 |
error=str(exc),
|
| 122 |
)
|
| 123 |
+
return [_MISSING_ANALYSIS], []
|
| 124 |
|
| 125 |
if not substantive:
|
| 126 |
missing.append(_MISSING_ANALYSIS)
|
|
@@ -56,13 +56,16 @@ _COMMAND_CATALOG: list[CommandResponse] = [
|
|
| 56 |
type="skill",
|
| 57 |
description="Show what the assistant can do and guide your next step.",
|
| 58 |
),
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
|
|
|
|
|
|
|
|
|
| 66 |
CommandResponse(
|
| 67 |
command="/analyze-descriptive",
|
| 68 |
name="analyze_descriptive",
|
|
|
|
| 56 |
type="skill",
|
| 57 |
description="Show what the assistant can do and guide your next step.",
|
| 58 |
),
|
| 59 |
+
# /problem-statement removed 2026-06-24 β replaced by user-entered objective +
|
| 60 |
+
# business_questions captured at onboarding (no agent skill). Kept commented for
|
| 61 |
+
# restorability.
|
| 62 |
+
# CommandResponse(
|
| 63 |
+
# command="/problem-statement",
|
| 64 |
+
# name="problem_statement",
|
| 65 |
+
# type="skill",
|
| 66 |
+
# description="Define and validate your analysis goal (objective + metric) "
|
| 67 |
+
# "before exploring data.",
|
| 68 |
+
# ),
|
| 69 |
CommandResponse(
|
| 70 |
command="/analyze-descriptive",
|
| 71 |
name="analyze_descriptive",
|
|
@@ -7,7 +7,7 @@ Return three fields:
|
|
| 7 |
- **`intent`** β exactly one of:
|
| 8 |
- `chat` β conversational, no data needed: greetings, farewells, thanks, "how are you", "what can you do", small talk.
|
| 9 |
- `help` β the user wants to know **what to do next** or how the process works ("what's the next step?", "how do I start?", "what should I do now?").
|
| 10 |
-
- `problem_statement`
|
| 11 |
- `check` β the user wants an **inventory** of what they have: "what data do I have?", "what columns are in this table?", "what documents did I upload?", "describe my dataset". This is metadata/listing, not analysis.
|
| 12 |
- `unstructured_flow` β the user asks about a **topic, concept, feature, explanation, or factual knowledge** that may live in uploaded documents (PDF/DOCX/TXT). Pure document Q&A. The user need not mention a document.
|
| 13 |
- `structured_flow` β the user asks an **analytical question over their data**: counts, sums, top-N, filters, comparisons, trends, correlations, segments, share-of-total, joins across structured sources. This routes to the slow analytical path.
|
|
@@ -18,16 +18,14 @@ Return three fields:
|
|
| 18 |
|
| 19 |
1. Pure greeting / farewell / thanks / "what can you do" / compliment with no task β `chat`.
|
| 20 |
2. "What do I do next / how do I proceed / where do I start" β `help`.
|
| 21 |
-
3.
|
| 22 |
-
4.
|
| 23 |
-
5.
|
| 24 |
-
6. An analytical question answerable by computing over tabular/DB data (counts, sums, top-N, filters, comparisons, trends, correlations, segments) β `structured_flow`.
|
| 25 |
|
| 26 |
## Disambiguation (the boundaries that matter)
|
| 27 |
|
| 28 |
- **`check` vs `structured_flow`** β "what do I have / describe it" β `check`; "analyze / compute / trend / correlate / compare it" β `structured_flow`.
|
| 29 |
- **`unstructured_flow` vs `structured_flow`** β pure document/concept Q&A β `unstructured_flow`; anything needing computation over tabular/DB data β `structured_flow`. **When in doubt between "analytical AND also needs document context" β `structured_flow`** (the analytical path can pull document context itself). Only choose `unstructured_flow` for *pure* document questions with no computation.
|
| 30 |
-
- **`help` vs `problem_statement`** β "what's next?" β `help`; "here is my goal / let's define the objective" β `problem_statement`.
|
| 31 |
- **`chat` vs everything else** β only use `chat` when there is no task and no data question at all.
|
| 32 |
|
| 33 |
## Rewriting follow-ups
|
|
@@ -58,16 +56,6 @@ User: "Okay I uploaded my data, what do I do next?"
|
|
| 58 |
User: "How does this work? Where should I start?"
|
| 59 |
β intent="help", rewritten_query=null, confidence=0.9
|
| 60 |
|
| 61 |
-
User: "I want to reduce customer churn next quarter, target under 5%."
|
| 62 |
-
β intent="problem_statement",
|
| 63 |
-
rewritten_query="Define the analysis goal: reduce customer churn next quarter to under 5%.",
|
| 64 |
-
confidence=0.9
|
| 65 |
-
|
| 66 |
-
User: "My goal is to grow revenue in the north region."
|
| 67 |
-
β intent="problem_statement",
|
| 68 |
-
rewritten_query="Define the analysis goal: grow revenue in the north region.",
|
| 69 |
-
confidence=0.88
|
| 70 |
-
|
| 71 |
User: "What data do I have?"
|
| 72 |
β intent="check", rewritten_query="What data sources do I have?", confidence=0.95
|
| 73 |
|
|
@@ -113,7 +101,7 @@ User: "And in March?"
|
|
| 113 |
|
| 114 |
## Constraints
|
| 115 |
|
| 116 |
-
- Pick exactly one `intent`. Do not invent values outside the
|
| 117 |
- Prefer `unstructured_flow` over `structured_flow` only for pure knowledge/document questions; prefer `structured_flow` whenever computation over data is involved.
|
| 118 |
- Do not refuse β refusal happens later in guardrails. Just classify.
|
| 119 |
- One JSON object as output; no prose, no markdown.
|
|
|
|
| 7 |
- **`intent`** β exactly one of:
|
| 8 |
- `chat` β conversational, no data needed: greetings, farewells, thanks, "how are you", "what can you do", small talk.
|
| 9 |
- `help` β the user wants to know **what to do next** or how the process works ("what's the next step?", "how do I start?", "what should I do now?").
|
| 10 |
+
<!-- `problem_statement` intent removed 2026-06-24 β the analysis goal is now two user-entered fields (objective + business_questions) captured at onboarding, with no agent validation. -->
|
| 11 |
- `check` β the user wants an **inventory** of what they have: "what data do I have?", "what columns are in this table?", "what documents did I upload?", "describe my dataset". This is metadata/listing, not analysis.
|
| 12 |
- `unstructured_flow` β the user asks about a **topic, concept, feature, explanation, or factual knowledge** that may live in uploaded documents (PDF/DOCX/TXT). Pure document Q&A. The user need not mention a document.
|
| 13 |
- `structured_flow` β the user asks an **analytical question over their data**: counts, sums, top-N, filters, comparisons, trends, correlations, segments, share-of-total, joins across structured sources. This routes to the slow analytical path.
|
|
|
|
| 18 |
|
| 19 |
1. Pure greeting / farewell / thanks / "what can you do" / compliment with no task β `chat`.
|
| 20 |
2. "What do I do next / how do I proceed / where do I start" β `help`.
|
| 21 |
+
3. "What data / columns / tables / documents do I have", "describe my data", inventory or metadata requests β `check`.
|
| 22 |
+
4. A question answerable from document prose β a topic, concept, feature, explanation, summary, or factual knowledge, even without naming a document β `unstructured_flow`.
|
| 23 |
+
5. An analytical question answerable by computing over tabular/DB data (counts, sums, top-N, filters, comparisons, trends, correlations, segments) β `structured_flow`.
|
|
|
|
| 24 |
|
| 25 |
## Disambiguation (the boundaries that matter)
|
| 26 |
|
| 27 |
- **`check` vs `structured_flow`** β "what do I have / describe it" β `check`; "analyze / compute / trend / correlate / compare it" β `structured_flow`.
|
| 28 |
- **`unstructured_flow` vs `structured_flow`** β pure document/concept Q&A β `unstructured_flow`; anything needing computation over tabular/DB data β `structured_flow`. **When in doubt between "analytical AND also needs document context" β `structured_flow`** (the analytical path can pull document context itself). Only choose `unstructured_flow` for *pure* document questions with no computation.
|
|
|
|
| 29 |
- **`chat` vs everything else** β only use `chat` when there is no task and no data question at all.
|
| 30 |
|
| 31 |
## Rewriting follow-ups
|
|
|
|
| 56 |
User: "How does this work? Where should I start?"
|
| 57 |
β intent="help", rewritten_query=null, confidence=0.9
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
User: "What data do I have?"
|
| 60 |
β intent="check", rewritten_query="What data sources do I have?", confidence=0.95
|
| 61 |
|
|
|
|
| 101 |
|
| 102 |
## Constraints
|
| 103 |
|
| 104 |
+
- Pick exactly one `intent`. Do not invent values outside the five listed.
|
| 105 |
- Prefer `unstructured_flow` over `structured_flow` only for pure knowledge/document questions; prefer `structured_flow` whenever computation over data is involved.
|
| 106 |
- Do not refuse β refusal happens later in guardrails. Just classify.
|
| 107 |
- One JSON object as output; no prose, no markdown.
|
|
@@ -24,11 +24,10 @@ class Settings(BaseSettings):
|
|
| 24 |
# real source lands, so this stays opt-in.
|
| 25 |
enable_slow_path: bool = Field(alias="enable_slow_path", default=False)
|
| 26 |
|
| 27 |
-
#
|
| 28 |
-
#
|
| 29 |
-
#
|
| 30 |
-
#
|
| 31 |
-
# /analysis/create.
|
| 32 |
enable_gate: bool = Field(alias="enable_gate", default=False)
|
| 33 |
|
| 34 |
# Database
|
|
|
|
| 24 |
# real source lands, so this stays opt-in.
|
| 25 |
enable_slow_path: bool = Field(alias="enable_slow_path", default=False)
|
| 26 |
|
| 27 |
+
# DEPRECATED 2026-06-24: the problem_validated gate was removed (the goal is now
|
| 28 |
+
# user-entered objective + business_questions, no agent validation). This flag no
|
| 29 |
+
# longer has any effect β the gate call site in ChatHandler is commented out. Kept
|
| 30 |
+
# to avoid .env churn; remove once no environment references it.
|
|
|
|
| 31 |
enable_gate: bool = Field(alias="enable_gate", default=False)
|
| 32 |
|
| 33 |
# Database
|