Rifqi Hafizuddin Claude Opus 4.8 commited on
Commit Β·
ce7be17
1
Parent(s): 0b2d678
[NOTICKET] docs: pr/5 sprint tracker + endpoint-restructure contract + REPO_STATUS banner
Browse filesDEV_PLAN $0 = pr/5 sprint table (P0 contract done; Phase 1 unwire done via KM-686 0b2d678; v2/regroup/observability pending). Header bumped to pr/5 / 2026-06-30.
Add API_ENDPOINTS_RESTRUCTURE.md: contract sent to Harry β chat -> /api/v2/chat/stream (analysis_id), skills regroup under /api/v1/tools/ (list/help/report), new /api/v1/observability (tentative).
REPO_STATUS: pr/5 direction banner (Phase 1 marked done) + 2026-06-29 cross-repo Go verification.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- API_ENDPOINTS_RESTRUCTURE.md +391 -0
- DEV_PLAN.md +41 -4
- REPO_STATUS.md +69 -19
API_ENDPOINTS_RESTRUCTURE.md
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Backend Agentic Service β API Endpoint Docs (endpoint restructure)
|
| 2 |
+
|
| 3 |
+
**Status:** contract draft for FE/Go integration (2026-06-30). Covers the AI-only surface after the
|
| 4 |
+
restructure. Sections marked **TENTATIVE** (observability) may still change β send feedback before we
|
| 5 |
+
lock them.
|
| 6 |
+
|
| 7 |
+
**What changed**
|
| 8 |
+
- **Only the chat pilot moves to `/api/v2`.** Everything else stays on `/api/v1`, regrouped under `/tools`.
|
| 9 |
+
- **Chat pilot (`/api/v2/chat/stream`) uses `analysis_id`, not `room_id`.**
|
| 10 |
+
- **Skills are grouped under `/api/v1/tools`:** `list` / `help` / `report`.
|
| 11 |
+
- **New:** `GET /api/v1/observability` β Responsible-AI provenance per chat answer.
|
| 12 |
+
- Python is **generative-AI only.** It never creates/updates an analysis, room, document, DB
|
| 13 |
+
client, or catalog β Go owns those. Python just receives `analysis_id`. Those v1 routers are
|
| 14 |
+
unwired from `main` + Swagger (not deleted).
|
| 15 |
+
|
| 16 |
+
**Open coordination questions (need a decision with Harry) β flagged inline as β οΈ:**
|
| 17 |
+
1. **`message_id` origin** β who mints the assistant turn id used to correlate stream β observability? (Recommend: Go mints it, passes it in the chat request, Python echoes on `done`.)
|
| 18 |
+
2. **Deterministic `/help` dispatch** β dedicated endpoint (recommended below) vs router classification.
|
| 19 |
+
3. **Observability storage** β single JSONB row per message (recommended) vs 3 normalized tables.
|
| 20 |
+
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
## 1. call_agent β `POST /api/v2/chat/stream`
|
| 24 |
+
|
| 25 |
+
The only FEβPython call in normal operation. Same as v1 except **`room_id` β `analysis_id`**, and
|
| 26 |
+
the `done` event now carries the assistant `message_id` for observability correlation.
|
| 27 |
+
|
| 28 |
+
**Request body** (`application/json`) β `ChatRequest`:
|
| 29 |
+
|
| 30 |
+
```json
|
| 31 |
+
{
|
| 32 |
+
"user_id": "u_1a2b3c",
|
| 33 |
+
"analysis_id": "an_42",
|
| 34 |
+
"message_id": "msg_88f1",
|
| 35 |
+
"message": "What were total sales by region last quarter?"
|
| 36 |
+
}
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
- `analysis_id` is the analysis-session id (replaces `room_id`). No auth header (handled by Go).
|
| 40 |
+
- β οΈ `message_id` (optional): the assistant turn id. **Recommended: Go mints it** alongside the
|
| 41 |
+
`analyses_messages` row and passes it here, so the FE can call `/api/v1/observability?message_id=...`
|
| 42 |
+
in parallel. If omitted, Python mints one and returns it on `done`.
|
| 43 |
+
|
| 44 |
+
**Response:** `text/event-stream`. Events arrive in this order:
|
| 45 |
+
|
| 46 |
+
| event | data | notes |
|
| 47 |
+
|---|---|---|
|
| 48 |
+
| `sources` | JSON array of `{document_id, filename, page_label}` | structured: one per executed table; unstructured: deduped doc/page; chat/help/error: `[]`. |
|
| 49 |
+
| `status` | text | slow-path only β progress pings ("Planningβ¦", "Running N stepsβ¦"). Safe to surface or ignore. |
|
| 50 |
+
| `chunk` | text fragment | concatenate in order to form the answer. |
|
| 51 |
+
| `done` | `{"message_id": "..."}` | **v2 change:** was empty; now returns the turn id for the observability lookup. |
|
| 52 |
+
| `error` | text | terminal error; stream stops after this. |
|
| 53 |
+
|
| 54 |
+
The internal `intent` event is consumed inside Python (gates caching) and **not** forwarded.
|
| 55 |
+
|
| 56 |
+
**Stream carries the answer text ONLY.** Planning / tool calls / sources detail are **not** in the
|
| 57 |
+
stream (it would slow it down) β fetch them from `/observability` (Β§7), called in parallel.
|
| 58 |
+
|
| 59 |
+
**Example β `structured_flow` answer** (raw SSE; blank line separates events):
|
| 60 |
+
|
| 61 |
+
```
|
| 62 |
+
event: sources
|
| 63 |
+
data: [{"document_id":"u_1a2b3c_orders","filename":"orders","page_label":null}]
|
| 64 |
+
|
| 65 |
+
event: status
|
| 66 |
+
data: Planning analysisβ¦
|
| 67 |
+
|
| 68 |
+
event: status
|
| 69 |
+
data: Running 3 stepsβ¦
|
| 70 |
+
|
| 71 |
+
event: chunk
|
| 72 |
+
data: Total sales by region last quarter:
|
| 73 |
+
|
| 74 |
+
event: chunk
|
| 75 |
+
data: Central led at $1.21M (38%), East $0.74M, West $0.55M (down 12% QoQ).
|
| 76 |
+
|
| 77 |
+
event: done
|
| 78 |
+
data: {"message_id":"msg_88f1"}
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
**Example β simple chat reply** (no status pings, empty sources):
|
| 82 |
+
|
| 83 |
+
```
|
| 84 |
+
event: sources
|
| 85 |
+
data: []
|
| 86 |
+
|
| 87 |
+
event: chunk
|
| 88 |
+
data: I'm your AI data analyst β connect a source or ask a question to get started.
|
| 89 |
+
|
| 90 |
+
event: done
|
| 91 |
+
data: {"message_id":"msg_12"}
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
Behavior unchanged from v1: 1h Redis response-cache on the stateless `chat` intent only;
|
| 95 |
+
greeting/farewell fast-path (canned, no LLM); LLM router classifies every message into one of 5
|
| 96 |
+
intents (`chat Β· help Β· check Β· unstructured_flow Β· structured_flow`); messages persist on `done`.
|
| 97 |
+
|
| 98 |
+
---
|
| 99 |
+
|
| 100 |
+
## 2. list_skills β `GET /api/v1/tools/list`
|
| 101 |
+
|
| 102 |
+
Static, deterministic, safe for Go to cache. (Was `GET /api/v1/tools`.)
|
| 103 |
+
|
| 104 |
+
**Request:** none.
|
| 105 |
+
|
| 106 |
+
**Response 200** (`ListToolsResponse`):
|
| 107 |
+
|
| 108 |
+
```json
|
| 109 |
+
{
|
| 110 |
+
"count": 2,
|
| 111 |
+
"tools": [
|
| 112 |
+
{ "command": "/help", "name": "help", "type": "skill",
|
| 113 |
+
"description": "Show what the assistant can do and guide your next step." },
|
| 114 |
+
{ "command": "/report", "name": "report", "type": "skill",
|
| 115 |
+
"description": "Generate a versioned analysis report (background, EDA, key findings, insights)." }
|
| 116 |
+
]
|
| 117 |
+
}
|
| 118 |
+
```
|
| 119 |
+
|
| 120 |
+
`CommandResponse = { command, name, type, description }`, `type β {skill, analytics, data_access}`.
|
| 121 |
+
Catalog is `/help` + `/report` only; the `analyze_*` / `check_*` / `retrieve_*` and retired
|
| 122 |
+
`/problem-statement` entries are commented out (kept for restorability), not deleted.
|
| 123 |
+
|
| 124 |
+
**FE behavior:** the `/` slash menu surfaces **`/help` only**. **Report is a right-side button, not
|
| 125 |
+
a slash command** (it fires only when an analysis is finished β saves tokens).
|
| 126 |
+
|
| 127 |
+
---
|
| 128 |
+
|
| 129 |
+
## 3. skill: help β `POST /api/v1/tools/help`
|
| 130 |
+
|
| 131 |
+
β οΈ **Proposed dedicated endpoint** (new in v2). In v1 there was no `/help` endpoint β help was reached
|
| 132 |
+
only by letting the LLM router classify a chat message. A dedicated endpoint makes `/help` dispatch
|
| 133 |
+
**deterministic** (no risk the router mis-classifies the slash command) and gives it a clean home in
|
| 134 |
+
the tools group. State-aware: reads analysis state + history to guide the next step.
|
| 135 |
+
|
| 136 |
+
> Alternative if we *don't* add this endpoint: FE keeps calling `POST /chat/stream` and trusts the
|
| 137 |
+
> router to classify the help intent. We recommend the dedicated endpoint β decision pending (open
|
| 138 |
+
> question #2).
|
| 139 |
+
|
| 140 |
+
**Request body** (`application/json`):
|
| 141 |
+
|
| 142 |
+
```json
|
| 143 |
+
{
|
| 144 |
+
"user_id": "u_1a2b3c",
|
| 145 |
+
"analysis_id": "an_42"
|
| 146 |
+
}
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
**Response:** `text/event-stream` β same SSE shape as chat, with `sources: []` and no `status`
|
| 150 |
+
pings (help never references documents). Streams a next-step guidance reply.
|
| 151 |
+
|
| 152 |
+
```
|
| 153 |
+
event: sources
|
| 154 |
+
data: []
|
| 155 |
+
|
| 156 |
+
event: chunk
|
| 157 |
+
data: Your goal is set β you can start exploring now. Try a question like "average order value by month", then I can generate a report.
|
| 158 |
+
|
| 159 |
+
event: done
|
| 160 |
+
data: {"message_id":"msg_h7"}
|
| 161 |
+
```
|
| 162 |
+
|
| 163 |
+
---
|
| 164 |
+
|
| 165 |
+
## 4. skill: report β `POST /api/v1/tools/report`
|
| 166 |
+
|
| 167 |
+
The "Generate Report" button. Same as v1, moved under `/tools`. Generate, persist, and return a new
|
| 168 |
+
report version. Currently renders **Markdown** (FE preview); PPT/PDF/infographic export is future work
|
| 169 |
+
(triggered on a download button, not here).
|
| 170 |
+
|
| 171 |
+
**Query params:** `analysis_id` (required), `user_id` (required). No request body.
|
| 172 |
+
|
| 173 |
+
```
|
| 174 |
+
POST /api/v1/tools/report?analysis_id=an_42&user_id=u_1a2b3c
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
| status | meaning |
|
| 178 |
+
|---|---|
|
| 179 |
+
| 201 | new version generated β `AnalysisReport` body. |
|
| 180 |
+
| 409 | floor not met β no recorded analyses yet for this session, nothing to report. |
|
| 181 |
+
| 500 | generation or persistence failed. |
|
| 182 |
+
|
| 183 |
+
**201 response** (`AnalysisReport`):
|
| 184 |
+
|
| 185 |
+
```json
|
| 186 |
+
{
|
| 187 |
+
"report_id": "8f3a2b1c9d4e4f6a8b0c1d2e3f4a5b6c",
|
| 188 |
+
"analysis_id": "an_42",
|
| 189 |
+
"user_id": "u_1a2b3c",
|
| 190 |
+
"version": 2,
|
| 191 |
+
"generated_at": "2026-06-30T09:14:33.512Z",
|
| 192 |
+
"problem_statement": {
|
| 193 |
+
"objective": "Understand which regions drive revenue and why Q1 dipped.",
|
| 194 |
+
"business_questions": [
|
| 195 |
+
"Which regions contribute most to total revenue?",
|
| 196 |
+
"Did any region decline quarter-over-quarter?"
|
| 197 |
+
]
|
| 198 |
+
},
|
| 199 |
+
"record_ids": ["rec_a1", "rec_b2"],
|
| 200 |
+
"executive_summary": "Revenue is concentrated in the Central region (38% of total). The West was the only region to contract, down 12% QoQ β the main driver of the Q1 dip.",
|
| 201 |
+
"findings": [
|
| 202 |
+
{ "text": "Central region contributed 38% of total revenue, the largest share.",
|
| 203 |
+
"record_ids": ["rec_a1"], "supporting_data": null },
|
| 204 |
+
{ "text": "West region revenue fell 12% quarter-over-quarter.",
|
| 205 |
+
"record_ids": ["rec_b2"], "supporting_data": null }
|
| 206 |
+
],
|
| 207 |
+
"caveats": [
|
| 208 |
+
{ "text": "March data for the East region was partially missing (~6% of rows).",
|
| 209 |
+
"record_ids": ["rec_b2"] }
|
| 210 |
+
],
|
| 211 |
+
"open_questions": [
|
| 212 |
+
{ "text": "What drove the West region's QoQ decline?", "record_ids": ["rec_b2"] }
|
| 213 |
+
],
|
| 214 |
+
"data_sources": [
|
| 215 |
+
{ "source_id": "src_sales_db", "name": "orders", "source_type": "postgres",
|
| 216 |
+
"detail": { "tables": ["orders"], "row_count": 48213,
|
| 217 |
+
"columns": ["region", "amount", "ordered_at"] } }
|
| 218 |
+
],
|
| 219 |
+
"method_steps": [
|
| 220 |
+
{ "task_id": "t1", "stage": "data_understanding", "objective": "Inventory the sales source",
|
| 221 |
+
"status": "success", "tools_used": ["check_data"] },
|
| 222 |
+
{ "task_id": "t2", "stage": "modeling", "objective": "Aggregate revenue by region",
|
| 223 |
+
"status": "success", "tools_used": ["analyze_aggregate"] }
|
| 224 |
+
],
|
| 225 |
+
"rendered_markdown": "# Analysis Report\n\n*Generated 2026-06-30 by u_1a2b3c Β· 2 analyses Β· 1 source(s)*\n\n## Objective\nUnderstand which regions drive revenueβ¦\n\n## Key Findings\n1. Central region contributed 38%β¦"
|
| 226 |
+
}
|
| 227 |
+
```
|
| 228 |
+
|
| 229 |
+
**409 response** (floor not met β the demo's most common error):
|
| 230 |
+
|
| 231 |
+
```json
|
| 232 |
+
{ "detail": "Not ready to generate a report β still needs at least one completed analysis." }
|
| 233 |
+
```
|
| 234 |
+
|
| 235 |
+
β οΈ **Precondition:** `AnalysisRecord`s persist only on the slow path, so reports require
|
| 236 |
+
`ENABLE_SLOW_PATH=true` on the Python deployment and β₯1 prior `structured_flow` question in the
|
| 237 |
+
session. With slow path off, `POST` 409s by design.
|
| 238 |
+
|
| 239 |
+
---
|
| 240 |
+
|
| 241 |
+
## 5. report versions β `GET /api/v1/tools/report/{analysis_id}` and `/{analysis_id}/{version}`
|
| 242 |
+
|
| 243 |
+
List a session's report versions (oldest-first). Returns `[ReportVersionEntry]`; `[]` if none.
|
| 244 |
+
|
| 245 |
+
```json
|
| 246 |
+
[
|
| 247 |
+
{ "report_id": "1b2c3d4eβ¦", "version": 1, "generated_at": "2026-06-24T15:02:11Z", "record_count": 1 },
|
| 248 |
+
{ "report_id": "8f3a2b1cβ¦", "version": 2, "generated_at": "2026-06-25T09:14:33Z", "record_count": 2 }
|
| 249 |
+
]
|
| 250 |
+
```
|
| 251 |
+
|
| 252 |
+
`GET /api/v1/tools/report/{analysis_id}/{version}` β one `AnalysisReport` (same shape as the POST
|
| 253 |
+
201 body); 404 if that version doesn't exist:
|
| 254 |
+
|
| 255 |
+
```json
|
| 256 |
+
{ "detail": "No report v3 for analysis 'an_42'." }
|
| 257 |
+
```
|
| 258 |
+
|
| 259 |
+
---
|
| 260 |
+
|
| 261 |
+
## 6. Unwired in v2 (mounted in v1, OFF in v2)
|
| 262 |
+
|
| 263 |
+
Commented out of `main` + Swagger, **files kept**. Go owns these; Python is generative-only:
|
| 264 |
+
`POST /analysis/create` + analysis CRUD Β· `room` Β· `db_client` Β· `document` Β· `data_catalog` Β·
|
| 265 |
+
`users`/login. Re-mounting is a one-line `include_router` if ever needed.
|
| 266 |
+
|
| 267 |
+
---
|
| 268 |
+
|
| 269 |
+
## 7. observability β `GET /api/v1/observability` **(NEW Β· TENTATIVE)**
|
| 270 |
+
|
| 271 |
+
Responsible-AI provenance for **one chat answer**. Separate endpoint, called **in parallel with the
|
| 272 |
+
stream** β never embedded in it. The FE renders it as a collapsed dropdown the user can expand
|
| 273 |
+
(planning / tool calls / sources), Claude/Codex-style.
|
| 274 |
+
|
| 275 |
+
**Design (recommended):** one endpoint returns one merged object, backed by **one JSONB row per
|
| 276 |
+
message** written by an accumulating "scratchpad" decorator inside the chat agent and flushed on
|
| 277 |
+
`done`. The 3 facets (`planning` / `tool_calls` / `sources`) are **logical sections of the JSON**,
|
| 278 |
+
not separate tables β so the shape can evolve without a dedorch migration each time. (Storage is open
|
| 279 |
+
question #3.)
|
| 280 |
+
|
| 281 |
+
**Query params:** `analysis_id` (required), `message_id` (required).
|
| 282 |
+
|
| 283 |
+
```
|
| 284 |
+
GET /api/v1/observability?analysis_id=an_42&message_id=msg_88f1
|
| 285 |
+
```
|
| 286 |
+
|
| 287 |
+
**Timing:** the row is written when the turn finishes, so call this **after** the stream's `done`
|
| 288 |
+
event (or poll until 200). "Parallel" = a separate call the FE fires alongside the stream, not data
|
| 289 |
+
embedded in the stream.
|
| 290 |
+
|
| 291 |
+
**Field rules (by intent):**
|
| 292 |
+
- `planning` β present **only when the planner ran** (slow path); `null` otherwise.
|
| 293 |
+
- `tool_calls` β every tool invoked, with input + output. `[]` for pure chat / greeting / help.
|
| 294 |
+
- `sources` β **required for retrieve flows** (`structured_flow`, `unstructured_flow`). **Empty for
|
| 295 |
+
greeting / `chat` / `help`** (they don't reference documents).
|
| 296 |
+
- `thinking` β optional reasoning text; `null` if none.
|
| 297 |
+
|
| 298 |
+
**200 response β full `structured_flow` turn** (planner ran β all sections present):
|
| 299 |
+
|
| 300 |
+
```json
|
| 301 |
+
{
|
| 302 |
+
"analysis_id": "an_42",
|
| 303 |
+
"message_id": "msg_88f1",
|
| 304 |
+
"intent": "structured_flow",
|
| 305 |
+
"generated_at": "2026-06-30T03:21:09.114Z",
|
| 306 |
+
"planning": {
|
| 307 |
+
"goal_restated": "Find which regions drive revenue and why Q1 dipped.",
|
| 308 |
+
"assumptions": ["'last quarter' = Q1 2026"],
|
| 309 |
+
"steps": [
|
| 310 |
+
{ "step": 1, "stage": "data_understanding", "objective": "Inventory the sales source" },
|
| 311 |
+
{ "step": 2, "stage": "modeling", "objective": "Aggregate revenue by region" }
|
| 312 |
+
]
|
| 313 |
+
},
|
| 314 |
+
"thinking": "The question needs a per-region breakdown plus a cause, so I inventory the source, aggregate revenue by region, then compare quarters.",
|
| 315 |
+
"tool_calls": [
|
| 316 |
+
{
|
| 317 |
+
"order": 1,
|
| 318 |
+
"name": "check_data",
|
| 319 |
+
"input": { "source_hint": "structured" },
|
| 320 |
+
"output": { "kind": "table", "summary": "1 source Β· 1 table (orders) Β· 48,213 rows" },
|
| 321 |
+
"status": "success"
|
| 322 |
+
},
|
| 323 |
+
{
|
| 324 |
+
"order": 2,
|
| 325 |
+
"name": "retrieve_data",
|
| 326 |
+
"input": { "source_id": "src_sales_db", "table_id": "orders",
|
| 327 |
+
"select": ["region", "amount"], "group_by": ["region"] },
|
| 328 |
+
"output": { "kind": "table", "columns": ["region", "total"], "row_count": 4,
|
| 329 |
+
"preview": [["Central", 1210000], ["East", 740000]] },
|
| 330 |
+
"status": "success"
|
| 331 |
+
}
|
| 332 |
+
],
|
| 333 |
+
"sources": [
|
| 334 |
+
{
|
| 335 |
+
"type": "database",
|
| 336 |
+
"source_id": "src_sales_db",
|
| 337 |
+
"name": "orders",
|
| 338 |
+
"query": "SELECT region, SUM(amount) AS total FROM orders GROUP BY region",
|
| 339 |
+
"detail": { "tables": ["orders"], "row_count": 48213 }
|
| 340 |
+
}
|
| 341 |
+
]
|
| 342 |
+
}
|
| 343 |
+
```
|
| 344 |
+
|
| 345 |
+
**200 response β `unstructured_flow` turn** (no planner; source = document, with the retrieval query):
|
| 346 |
+
|
| 347 |
+
```json
|
| 348 |
+
{
|
| 349 |
+
"analysis_id": "an_42",
|
| 350 |
+
"message_id": "msg_55",
|
| 351 |
+
"intent": "unstructured_flow",
|
| 352 |
+
"generated_at": "2026-06-30T03:40:02.001Z",
|
| 353 |
+
"planning": null,
|
| 354 |
+
"thinking": null,
|
| 355 |
+
"tool_calls": [
|
| 356 |
+
{ "order": 1, "name": "retrieve_knowledge",
|
| 357 |
+
"input": { "query": "technology stack used in this project", "top_k": 4 },
|
| 358 |
+
"output": { "kind": "documents", "row_count": 4 }, "status": "success" }
|
| 359 |
+
],
|
| 360 |
+
"sources": [
|
| 361 |
+
{ "type": "document", "document_id": "doc_7", "filename": "tech_handbook.pdf",
|
| 362 |
+
"page_label": "12", "query": "technology stack used in this project",
|
| 363 |
+
"snippet": "The backend is built on FastAPI with async SQLAlchemyβ¦", "score": 0.83 }
|
| 364 |
+
]
|
| 365 |
+
}
|
| 366 |
+
```
|
| 367 |
+
|
| 368 |
+
**200 response β simple `chat` / greeting turn** (nothing to trace):
|
| 369 |
+
|
| 370 |
+
```json
|
| 371 |
+
{
|
| 372 |
+
"analysis_id": "an_42",
|
| 373 |
+
"message_id": "msg_12",
|
| 374 |
+
"intent": "chat",
|
| 375 |
+
"generated_at": "2026-06-30T03:05:00.000Z",
|
| 376 |
+
"planning": null,
|
| 377 |
+
"thinking": null,
|
| 378 |
+
"tool_calls": [],
|
| 379 |
+
"sources": []
|
| 380 |
+
}
|
| 381 |
+
```
|
| 382 |
+
|
| 383 |
+
**404** β no provenance for that message yet (turn still running or unknown id):
|
| 384 |
+
|
| 385 |
+
```json
|
| 386 |
+
{ "detail": "No observability for message 'msg_88f1' yet." }
|
| 387 |
+
```
|
| 388 |
+
|
| 389 |
+
> β οΈ **Richness is path-dependent.** Full `planning` + tool I/O exist only when
|
| 390 |
+
> `ENABLE_SLOW_PATH=true`. Fast chat / single-query / help still record `sources` + the single
|
| 391 |
+
> tool call but have `planning: null`. This matches the rule "planning only when the planner runs."
|
DEV_PLAN.md
CHANGED
|
@@ -1,10 +1,47 @@
|
|
| 1 |
-
# Data Eyond β Current Development Plan (post 2026-06-24
|
| 2 |
|
| 3 |
**Purpose:** context file for Claude Code sessions working on the current sprint.
|
| 4 |
-
**Branch:** `pr/
|
| 5 |
**Companion:** [REPO_STATUS.md](REPO_STATUS.md) describes the repo's *current built state*; this file
|
| 6 |
-
describes the *in-flight plan* that changes it.
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
---
|
| 10 |
|
|
|
|
| 1 |
+
# Data Eyond β Current Development Plan (post 2026-06-24 β 2026-06-30 checkpoints)
|
| 2 |
|
| 3 |
**Purpose:** context file for Claude Code sessions working on the current sprint.
|
| 4 |
+
**Branch:** `pr/5` Β· **Snapshot:** 2026-06-30.
|
| 5 |
**Companion:** [REPO_STATUS.md](REPO_STATUS.md) describes the repo's *current built state*; this file
|
| 6 |
+
describes the *in-flight plan* that changes it. The **active sprint is pr/5** ([Β§0](#0-current-sprint--pr5-observability--endpoint-restructure)); sections Β§1βΒ§6 are the prior
|
| 7 |
+
2026-06-24/25 pivot (now largely β
), kept for context.
|
| 8 |
+
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
## 0. Current sprint β pr/5: Observability + Endpoint Restructure
|
| 12 |
+
|
| 13 |
+
From the **2026-06-30 checkpoint**. Direction: **Python β generation/AI-only**; Go owns the analysis
|
| 14 |
+
lifecycle + data plane. Endpoint contract sent to Harry on 2026-06-30:
|
| 15 |
+
[API_ENDPOINTS_RESTRUCTURE.md](API_ENDPOINTS_RESTRUCTURE.md) (chatβv2, tools regroup, observability β
|
| 16 |
+
observability marked tentative). REPO_STATUS carries a matching `pr/5` direction banner.
|
| 17 |
+
|
| 18 |
+
Mentor's task order: **unwire β regroup endpoints β add tools (retrieve-data + observability)**; share
|
| 19 |
+
the endpoint contract *before* coding the tools. Status legend: β¬ not started Β· π in progress Β· β
done Β·
|
| 20 |
+
β blocked Β· π verify Β· βΈοΈ deferred.
|
| 21 |
+
|
| 22 |
+
| Phase | Task | Owner | Status | Notes |
|
| 23 |
+
|---|---|---|---|---|
|
| 24 |
+
| **P0 β contract** | Draft + send endpoint contract to Harry (chat v2 Β· tools group Β· observability) | Rifqi + Sofhia | β
| `API_ENDPOINTS_RESTRUCTURE.md` sent 2026-06-30 (before-noon deadline met). Observability section flagged tentative. |
|
| 25 |
+
| **1 β unwire** | Unwire `users`(login)/`document`/`room`/`db_client`/`data_catalog`/`analysis` from `main` + Swagger | Sofhia | β
| **KM-686**, commit `0b2d678`. Commented, not deleted; `chat`/`report`/`tools` kept mounted. Resolves the analysis-CRUD scope Q β whole `analysis` router unwired (Go owns it). |
|
| 26 |
+
| **2 β v2 + regroup** | Create `src/api/v2/` and move the chat pilot there | Rifqi/Sofhia | β¬ | Only chat moves to v2; mirror v1 structure. |
|
| 27 |
+
| **2 β v2 + regroup** | Chat: `room_id` β **`analysis_id`** (request field + handler + history) | Rifqi | β¬ | `ChatRequest` [chat.py:51](src/api/v1/chat.py:51); `done` event returns `message_id`. Ties to #25 (`analyses_messages`). |
|
| 28 |
+
| **2 β v2 + regroup** | Move report under tools β `/api/v1/tools/report` (+ version routes) | Rifqi | β¬ | Today standalone `/api/v1/report` (still mounted). Same functionality, new path. |
|
| 29 |
+
| **2 β v2 + regroup** | Move help under tools β `POST /api/v1/tools/help` (dedicated endpoint) | Sofhia | β¬ | Today help is a chat **intent** only. Dedicated endpoint = deterministic dispatch (open Q for Harry). |
|
| 30 |
+
| **2 β v2 + regroup** | Tools list β `/api/v1/tools/list` | Sofhia | β¬ | Today `GET /api/v1/tools` ([tools.py:133](src/api/v1/tools.py:133)). |
|
| 31 |
+
| **2 β v2 + regroup** | FE: slash menu = `/help` only; report = right-side button | Mentor (FE) | β¬ | Coordination note, not Python work. |
|
| 32 |
+
| **3 β tools + obs** | Finish `help` so it actually **calls** (not just lists) + test | Sofhia | β¬ | Mentor: help currently only lists tools. Core #2 after chat. |
|
| 33 |
+
| **3 β tools + obs** | Observability **scratchpad** (decorator) accumulating in the chat agent | Rifqi + Sofhia | β¬ | Capture planning / tool I/O / sources during the run; flush one record on `done`. |
|
| 34 |
+
| **3 β tools + obs** | Audit `report_inputs` β covers planning + tool I/O + source? add cols / new store | Rifqi | β¬ | **Rec:** dedicated provenance store = 1 JSONB row per message (logical 3 sections); keep Langfuse for engineering. |
|
| 35 |
+
| **3 β tools + obs** | Build `GET /api/v1/observability` (one merged response) | Rifqi | β¬ | Intent-based source rules (greeting/help = none; retrieve = required). Richness path-dependent (full planning only on slow path). |
|
| 36 |
+
| **3 β tools + obs** | Keep stream **text-only**; observability is a separate parallel call | Rifqi | β¬ | Per mentor β don't slow the stream. |
|
| 37 |
+
| **3 β tools + obs** | Resolve `message_id` correlation (stream β observability) with Harry | Rifqi β Harry | β¬ | **Rec:** Go mints `message_id`, passes in chat request, Python echoes on `done`. |
|
| 38 |
+
| **4 β biz questions** | Get Go folder; confirm `business_questions` in create-analysis (max 5); sync Python | Harry/Mentor β Rifqi | β¬ | Go currently missing the field ("lagi difixing"). Python already models objective + business_questions. |
|
| 39 |
+
| **deferred** | Report formats: PPT (preferred) / PDF / infographic on download | β | βΈοΈ | MD is fine for the FE preview stage now. |
|
| 40 |
+
| **deferred** | Charts (PlotlyβJSON) + images tables | β | βΈοΈ | Carried from Β§4 #26/#27. |
|
| 41 |
+
|
| 42 |
+
**Next up:** Phase 2 β stand up `src/api/v2/` for the chat pilot (`analysis_id`) and regroup
|
| 43 |
+
`help`/`report`/`list` under `/api/v1/tools/`. The observability scratchpad (Phase 3) can start in
|
| 44 |
+
parallel since its shape is already speced in the contract.
|
| 45 |
|
| 46 |
---
|
| 47 |
|
REPO_STATUS.md
CHANGED
|
@@ -2,13 +2,27 @@
|
|
| 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).
|
| 9 |
> `REPO_CONTEXT.md` / `ARCHITECTURE.md` are the original Phase-2 design docs and are
|
| 10 |
> stale on the router, joins, and the analysis/report stack.
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
---
|
| 13 |
|
| 14 |
## 1. The product in one paragraph
|
|
@@ -31,9 +45,13 @@ streaming.
|
|
| 31 |
| Repo | Role | We edit? |
|
| 32 |
|---|---|---|
|
| 33 |
| **Python** β `Agentic-Service-Data-Eyond-Catalog` (this repo) | The agentic LLM service: router, gate, skills, slow analytical path, structured query engine, unstructured RAG, report generation, analysis-session state. FastAPI + async SQLAlchemy + LangChain + Azure GPT-4o. | **Yes β the only repo we edit.** |
|
| 34 |
-
| **Go** β `Orchestrator-Agent-Service` | Gateway / data plane:
|
| 35 |
| **FE** β `E2E-Frontend-Data-Eyond` | React/Vite SPA. Talks to Go for everything and to Python only for chat streaming. | Reference only. |
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
Shared infra: **Postgres** (app tables + `data_catalog` jsonb + PGVector `langchain_pg_embedding`), **Azure Blob**, and (Python-only) **Redis**.
|
| 38 |
|
| 39 |
---
|
|
@@ -59,6 +77,10 @@ Tests live locally and are gitignored. Run with `./.venv/Scripts/python.exe -m p
|
|
| 59 |
Entry: `POST /api/v1/chat/stream` (`src/api/v1/chat.py`) β `ChatHandler.handle(...)`
|
| 60 |
(`src/agents/chat_handler.py`). One shared `ChatHandler` per process keeps the Azure clients warm.
|
| 61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
```
|
| 63 |
POST /chat/stream { user_id, room_id, message }
|
| 64 |
β (analysis_id == room_id β one session = one analysis = one chat room)
|
|
@@ -131,6 +153,11 @@ Two facts to internalise:
|
|
| 131 |
|
| 132 |
## 7. API surface (this repo, all under `/api/v1`)
|
| 133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
| Endpoint | Purpose | Caller |
|
| 135 |
|---|---|---|
|
| 136 |
| `POST /chat/stream` | Main chat SSE (router β dispatch) | FE β Go β Python (the only FEβPython call today) |
|
|
@@ -153,10 +180,18 @@ unless `SKIP_INIT_DB=true`.
|
|
| 153 |
| `documents`, `databases` | uploads + DB creds (Fernet-encrypted) | Go ingestion | executor cred resolution |
|
| 154 |
| `data_catalog` | per-user jsonb `Catalog` (Source β Table β Column) | Go ingestion / Python pipeline | CatalogReader, planner, tools |
|
| 155 |
| `langchain_pg_embedding` | PGVector document chunks | Go ingestion | DocumentRetriever |
|
| 156 |
-
| `analysis_records` | jsonb `AnalysisRecord`, one per slow-path run | slow path | ReportGenerator, report readiness |
|
| 157 |
-
| `
|
| 158 |
-
| `reports` *(dedorch)* | uuid, `title` + markdown `content` + `version` | ReportStore | report API |
|
| 159 |
-
| `data_sources` *(dedorch)* | per-analysis binding; `reference_id` = catalog source_id | `/analysis/create` | structured-flow scoping, report appendix |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
**Catalog shape** (the jsonb in `data_catalog`):
|
| 162 |
`Catalog β Source[ {source_id, source_type β schema|tabular|unstructured, name, location_ref} β Table[ {table_id, name, row_count, foreign_keys[]} β Column[ {column_id, name, data_type, nullable, pii_flag, sample_values|null, stats} ] ] ]`. PII columns have `sample_values: null` so real values never enter prompts.
|
|
@@ -268,25 +303,40 @@ copies disagree with the current code on:
|
|
| 268 |
| Analysis / report / gate / slow path | "Phase 2 spine only" | All built and present |
|
| 269 |
| `analysis_id` | open question | resolved: **`analysis_id == room_id`** |
|
| 270 |
| Report source | (newer invariant) "from records, never chat history" | confirmed: generator reads `AnalysisRecord`s |
|
|
|
|
| 271 |
|
| 272 |
---
|
| 273 |
|
| 274 |
## 12. dedorch migration β current state
|
| 275 |
|
| 276 |
The Python DB is moving from `dataeyond` β **dedorch** (Go owns dedorch migrations; Python is
|
| 277 |
-
consumer-only).
|
| 278 |
-
|
| 279 |
-
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
|
| 291 |
---
|
| 292 |
|
|
|
|
| 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. **Cross-repo update 2026-06-29:** Β§2/Β§8/Β§11/Β§12 re-verified against
|
| 6 |
+
the **Go source** (`Orchestrator-Agent-Service`), not its docs. The Go service has moved well past its
|
| 7 |
+
own (uncommitted, stale) design docs: it now hosts the **dedorch SQL migrations** in-repo and a full
|
| 8 |
+
**`/api/v1/analyses` + `/api/v1/skills`** REST surface. Go does **not** call Python yet β those skills
|
| 9 |
+
are placeholders (see Β§12).
|
| 10 |
|
| 11 |
> This file is grounded in the source, not the older design docs. Where the two
|
| 12 |
> disagree, the code wins β see [Β§11 Doc-vs-code](#11-where-the-older-docs-are-stale).
|
| 13 |
> `REPO_CONTEXT.md` / `ARCHITECTURE.md` are the original Phase-2 design docs and are
|
| 14 |
> stale on the router, joins, and the analysis/report stack.
|
| 15 |
|
| 16 |
+
> π§ **Direction update 2026-06-30 (pr/5 β DECIDED Β· IN PROGRESS).** The 30 June checkpoint locked a
|
| 17 |
+
> restructure (contract: [API_ENDPOINTS_RESTRUCTURE.md](API_ENDPOINTS_RESTRUCTURE.md); live tracker:
|
| 18 |
+
> [DEV_PLAN Β§0](DEV_PLAN.md)). **Python is becoming a generation/AI-only service** β Go owns the full
|
| 19 |
+
> analysis lifecycle *and* the data-plane endpoints. Scope:
|
| 20 |
+
> - **Unwired from `main` + Swagger** (router files kept, *not* deleted): `analysis` CRUD, `room`, `db_client`, `document`, `data_catalog`, `users`/login. **β
DONE β KM-686, commit `0b2d678`** (so the Β§7 rows for these are now commented out of `main.py`).
|
| 21 |
+
> - **AI surface that stays live:** `chat` β **`POST /api/v2/chat/stream`** (explicit **`analysis_id`**, not `room_id`); the skills regroup under **`/api/v1/tools/`** (`list` Β· `help` Β· `report`); plus a **new `GET /api/v1/observability`** (Responsible-AI provenance per answer, backed by a provenance store β shape TBD). **β¬ pending.**
|
| 22 |
+
> - **Only `chat/stream` moves to `/api/v2`;** everything else stays `/api/v1`.
|
| 23 |
+
>
|
| 24 |
+
> Β§2/Β§4/Β§7 below still describe the **pre-restructure wiring** except the unwire above, which has landed.
|
| 25 |
+
|
| 26 |
---
|
| 27 |
|
| 28 |
## 1. The product in one paragraph
|
|
|
|
| 45 |
| Repo | Role | We edit? |
|
| 46 |
|---|---|---|
|
| 47 |
| **Python** β `Agentic-Service-Data-Eyond-Catalog` (this repo) | The agentic LLM service: router, gate, skills, slow analytical path, structured query engine, unstructured RAG, report generation, analysis-session state. FastAPI + async SQLAlchemy + LangChain + Azure GPT-4o. | **Yes β the only repo we edit.** |
|
| 48 |
+
| **Go** β `Orchestrator-Agent-Service` | Gateway / data plane: auth/JWT, documents (Azure Blob + CSV/XLSXβParquet + embeddings), database_clients (Fernet creds), **catalog ingestion** (moved into Go, KM-578/590), **all dedorch SQL migrations** (now embedded in the Go repo: `internal/repository/postgres/migrations/0001β0004`), and the **full analysis-lifecycle REST surface** (`/api/v1/analyses` CRUD + messages + reports, `/api/v1/skills`). The **interview agent and chat-rooms are deprecated β HTTP 410** (`internal/api/deprecation.go`). | Reference only. |
|
| 49 |
| **FE** β `E2E-Frontend-Data-Eyond` | React/Vite SPA. Talks to Go for everything and to Python only for chat streaming. | Reference only. |
|
| 50 |
|
| 51 |
+
> **Β» pr/5 (decided, not yet in code):** Python's non-AI endpoints (analysis CRUD, `room`, `document`,
|
| 52 |
+
> `db_client`, `data_catalog`, `users`/login) are being **unwired** β Python keeps only the
|
| 53 |
+
> generation/AI surface (chat, tools: `help`/`report`/`list`, observability). See the Direction-update banner.
|
| 54 |
+
|
| 55 |
Shared infra: **Postgres** (app tables + `data_catalog` jsonb + PGVector `langchain_pg_embedding`), **Azure Blob**, and (Python-only) **Redis**.
|
| 56 |
|
| 57 |
---
|
|
|
|
| 77 |
Entry: `POST /api/v1/chat/stream` (`src/api/v1/chat.py`) β `ChatHandler.handle(...)`
|
| 78 |
(`src/agents/chat_handler.py`). One shared `ChatHandler` per process keeps the Azure clients warm.
|
| 79 |
|
| 80 |
+
> **Β» pr/5:** this endpoint moves to **`POST /api/v2/chat/stream`** with an explicit **`analysis_id`**
|
| 81 |
+
> field (replacing `room_id`), and the observability detail (planning / tool I/O / sources) moves out of
|
| 82 |
+
> the stream to a separate `GET /api/v1/observability` call. See the Direction-update banner.
|
| 83 |
+
|
| 84 |
```
|
| 85 |
POST /chat/stream { user_id, room_id, message }
|
| 86 |
β (analysis_id == room_id β one session = one analysis = one chat room)
|
|
|
|
| 153 |
|
| 154 |
## 7. API surface (this repo, all under `/api/v1`)
|
| 155 |
|
| 156 |
+
> **Β» pr/5 (decided, not yet in code):** chat β `/api/v2/chat/stream` (`analysis_id`); `/tools` splits
|
| 157 |
+
> into `/tools/list` + `/tools/help` + `/tools/report`; new `/api/v1/observability`; and the
|
| 158 |
+
> analysis-CRUD / `room` / `users` / `document` / `db_client` / `data_catalog` rows are unwired from
|
| 159 |
+
> `main` + Swagger. See the Direction-update banner.
|
| 160 |
+
|
| 161 |
| Endpoint | Purpose | Caller |
|
| 162 |
|---|---|---|
|
| 163 |
| `POST /chat/stream` | Main chat SSE (router β dispatch) | FE β Go β Python (the only FEβPython call today) |
|
|
|
|
| 180 |
| `documents`, `databases` | uploads + DB creds (Fernet-encrypted) | Go ingestion | executor cred resolution |
|
| 181 |
| `data_catalog` | per-user jsonb `Catalog` (Source β Table β Column) | Go ingestion / Python pipeline | CatalogReader, planner, tools |
|
| 182 |
| `langchain_pg_embedding` | PGVector document chunks | Go ingestion | DocumentRetriever |
|
| 183 |
+
| `report_inputs` *(was `analysis_records`)* | jsonb `AnalysisRecord`, one per slow-path run; **Python-owned** | slow path | ReportGenerator, report readiness |
|
| 184 |
+
| `analyses` *(dedorch, plural)* | uuid `id`, `user_id`, `analysis_title`, `objective`, `business_questions` jsonb, `status` (active\|inactive), `data_bind`(+`data_bind_version`), `report_id`, `report_collection` β **defined by Go migrations**; `problem_statement`/`problem_validated`/`owner_id` already **dropped** there (`0003`/`0004`) | Go `/api/v1/analyses`; Python state store | gate (no-op), Help, report |
|
| 185 |
+
| `reports` *(dedorch)* | uuid, `analysis_id`, `user_id`, `title` + markdown `content` + `version` (UNIQUE per analysis) | Go + Python ReportStore | report API |
|
| 186 |
+
| `data_sources` *(dedorch)* | per-analysis binding; `reference_id` = catalog source_id; `type β document\|database` | Go `/analyses/{id}/data-bind` (+ Python `/analysis/create`) | structured-flow scoping, report appendix |
|
| 187 |
+
| `analyses_messages` *(dedorch)* | the analysis chat room (`role β user\|ai`); replaces deprecated `rooms`/`chat_messages` | Go `/analyses/{id}/messages` | Python chat path **not yet migrated here** (Β§12) |
|
| 188 |
+
|
| 189 |
+
> β οΈ **Python ORM β dedorch drift (verified 2026-06-29).** Python's `AnalysisStateRow` + `state_store.py`
|
| 190 |
+
> still model **`problem_statement` / `problem_validated`** and do **not** carry `objective` /
|
| 191 |
+
> `business_questions`, but the Go migrations have already dropped the former and added the latter.
|
| 192 |
+
> Pre-cutover this is harmless (Python runs `create_all` on its own copy); **post-`SKIP_INIT_DB`**, when
|
| 193 |
+
> Python reads dedorch directly, ORM column selection on the dropped columns will break. Reconcile the
|
| 194 |
+
> Python model before the connection-string cutover.
|
| 195 |
|
| 196 |
**Catalog shape** (the jsonb in `data_catalog`):
|
| 197 |
`Catalog β Source[ {source_id, source_type β schema|tabular|unstructured, name, location_ref} β Table[ {table_id, name, row_count, foreign_keys[]} β Column[ {column_id, name, data_type, nullable, pii_flag, sample_values|null, stats} ] ] ]`. PII columns have `sample_values: null` so real values never enter prompts.
|
|
|
|
| 303 |
| Analysis / report / gate / slow path | "Phase 2 spine only" | All built and present |
|
| 304 |
| `analysis_id` | open question | resolved: **`analysis_id == room_id`** |
|
| 305 |
| Report source | (newer invariant) "from records, never chat history" | confirmed: generator reads `AnalysisRecord`s |
|
| 306 |
+
| Go service scope | "interview agent + ingestion; dedorch migrations live outside the repos" | Go now hosts the **dedorch migrations in-repo** + a full **`/api/v1/analyses` + `/api/v1/skills`** REST surface; interview/rooms **deprecated (410)**. (Go's own `PROJECT_SUMMARY.md`/`REPO_CONTEXT.md` are uncommitted + stale.) |
|
| 307 |
|
| 308 |
---
|
| 309 |
|
| 310 |
## 12. dedorch migration β current state
|
| 311 |
|
| 312 |
The Python DB is moving from `dataeyond` β **dedorch** (Go owns dedorch migrations; Python is
|
| 313 |
+
consumer-only). State **re-verified against the Go source 2026-06-29**:
|
| 314 |
+
|
| 315 |
+
- **The dedorch migrations now live IN the Go repo** β embedded SQL at
|
| 316 |
+
`internal/repository/postgres/migrations/0001_create_core_schema.sql β¦ 0004_replace_chat_with_analysis_scope.sql`,
|
| 317 |
+
run on startup by `RunMigrations`. (This corrects the earlier note that the migrations were
|
| 318 |
+
invisible / asserted only by Python docstrings.) The full schema is now readable there.
|
| 319 |
+
- **Go owns the analysis family end-to-end.** `analyses` / `analyses_messages` / `reports` /
|
| 320 |
+
`data_sources` / `message_sources` / `data_catalog` are created by Go migrations and served by a
|
| 321 |
+
full REST surface: `internal/api/analysis.go` (CRUD + `data-bind` w/ optimistic `expected_version`
|
| 322 |
+
+ messages + reports) and `internal/api/skills.go`. `analyses` already has the **pivot shape**
|
| 323 |
+
(`objective` + `business_questions`, `status`, `data_bind`/`_version`, `report_collection`) and has
|
| 324 |
+
**dropped** `problem_statement`/`problem_validated`/`owner_id`. Migration `0004` renames the legacy
|
| 325 |
+
`rooms`/`chat_messages`/`interview_*` tables to `zdeprecated_*`.
|
| 326 |
+
- **`report_inputs`** (the slow-path structured output, formerly `analysis_records`) stays
|
| 327 |
+
**Python-owned**; its finalized schema goes to Harry so the dedorch migration creates it post-cutover.
|
| 328 |
+
- The connection-string cutover (paired with `SKIP_INIT_DB`) **has not happened yet**; Python still
|
| 329 |
+
runs `create_all` on its own models until then.
|
| 330 |
+
|
| 331 |
+
**β οΈ Integration gap (verified β the big one).** Go's `/api/v1/analyses` and `/api/v1/skills`
|
| 332 |
+
(`help` / `report`) are **placeholders that return dummy data** β the `SendMessage` / `GenerateReport`
|
| 333 |
+
handlers and the skills handler explicitly note *"placeholder integrasi backend agentic β¦ will be
|
| 334 |
+
replaced by the external skills service."* **Go currently never calls Python's `/chat/stream`,
|
| 335 |
+
`/report`, or any skill** (no outbound HTTP to the agentic service exists in the Go source). So today
|
| 336 |
+
there are **two parallel, unconnected analysis stacks**: Go's self-contained placeholder lifecycle
|
| 337 |
+
(gate: β₯3 user messages; AI replies are canned) and Python's real agentic spine (router β slow path β
|
| 338 |
+
records-based report; floor: β₯1 `analyze_*` success). Wiring Go β Python is the open integration work
|
| 339 |
+
(DEV_PLAN #7/#18/#25), plus reconciling the two different report gates.
|
| 340 |
|
| 341 |
---
|
| 342 |
|