feat: add Citations and WatchlistChat components, integrate with API
- Implemented Citations component to display citation data. - Created WatchlistDrawer and ChatSidebar components for managing watchlists and AI chat functionality. - Integrated API calls for watchlist management and chat interactions. - Updated index.tsx to include new components in the main application layout. - Added API client in lib/api.ts for structured API interactions. - Developed Alerts, Dashboard, Portfolio, Routines, Screener, and Report pages with relevant data fetching and UI components. - Introduced styles in tokens.css for consistent theming across the application. - Configured TypeScript and Vite for project setup and development.
This commit is contained in:
+3
-1
@@ -19,7 +19,7 @@ Agents never fetch live; they read snapshots. Fixtures in `tests/fixtures/` prov
|
||||
broker (accumulating/distributing/neutral from top/ ranks); (3) sector exposure
|
||||
shift week-over-week; (4) emit rotation signal on sign flip with evidence rows.
|
||||
- Output: behavior map, rotation signal (from→to + net Rp delta).
|
||||
- Fixture: Financials net −Rp 800M → Consumer +Rp 1.1T flip detected.
|
||||
- Fixture: Financials net −Rp 800M → Consumer +Rp 1.1B flip detected (sector_flow_idx.json).
|
||||
|
||||
## A3 — News Sentiment (Adaptive RAG)
|
||||
|
||||
@@ -29,6 +29,8 @@ Agents never fetch live; they read snapshots. Fixtures in `tests/fixtures/` prov
|
||||
(3) per-article sentiment + confidence; (4) aggregate trend
|
||||
improving/deteriorating/stable; (5) insider summary from filings.
|
||||
- Output: score −1..+1, trend, key events (≤5, cited), insider line.
|
||||
- Offline path: when no news/filings/suspensions snapshots exist, the agent falls
|
||||
back to the derived `news_items` table (`NewsSince`) instead of failing.
|
||||
- Fixture: ticker with 2 bullish + 1 neutral + 1 director buy → positive trend cited.
|
||||
|
||||
## A4 — Fundamental
|
||||
|
||||
+16
-3
@@ -29,10 +29,22 @@ Errors: `{error: {code, message}}` with HTTP 400/404/422/502 (502 = upstream Sec
|
||||
- `GET /api/alerts`, `POST /api/alerts` body `{name, rule, channels[]}`,
|
||||
`DELETE /api/alerts/:id`, `GET /api/alert-events?since=&ticker=`.
|
||||
|
||||
## Destinations (push targets per user)
|
||||
|
||||
- `GET /api/destinations` → `[{id, kind, label, enabled, configured}]`. Secrets are never returned.
|
||||
- `POST /api/destinations` body `{kind: telegram|discord, label?, bot_token?, chat_id?, webhook_url?}` → created (422 on wrong kind or missing secret).
|
||||
- `PATCH /api/destinations/:id` body `{label?, enabled?, bot_token?, chat_id?, webhook_url?}` → patched (kind immutable; omitted secret = keep).
|
||||
- `DELETE /api/destinations/:id` → removed.
|
||||
|
||||
Delivery: routines and alerts push to the owner's **enabled** destinations. When the owner has none, the server-level env webhook (`TELEGRAM_*` / `DISCORD_*`) is used as fallback; when neither exists delivery is record-only.
|
||||
|
||||
## Report
|
||||
|
||||
- `POST /api/report/:ticker` query `?format=json|html|pdf|md` → 7-section payload
|
||||
with `citations[]` per section. PDF rendered server-side.
|
||||
- `POST /api/report/:ticker` query `?format=json|html|pdf|md&profile=moderate|conservative|aggressive`
|
||||
→ 7-section payload with `citations[]` per section. PDF rendered server-side.
|
||||
- `POST /api/report/:ticker/ask` body `{question, report_id?}` → follow-up answer
|
||||
grounded ONLY in that report's citations (explicit id or latest for the ticker;
|
||||
cross-ticker ids are rejected).
|
||||
|
||||
## Watchlist / portfolio / accuracy / chat / health
|
||||
|
||||
@@ -41,4 +53,5 @@ Errors: `{error: {code, message}}` with HTTP 400/404/422/502 (502 = upstream Sec
|
||||
- `GET /api/accuracy` → per-agent `{calls, resolved, hits, hit_rate}`.
|
||||
- `POST /api/chat` body `{message, scope?: {report_id}}` → cited answer (report scope
|
||||
restricts grounding to that report's citations).
|
||||
- `GET /api/health` → `{last_cycle_at, credits_today, scheduler_ok, stale_flags}`.
|
||||
- `GET /api/health` (`?force=1` runs one synchronous probe cycle) →
|
||||
`{last_cycle_at, credits_today, scheduler_ok, stale_flags}`.
|
||||
|
||||
+10
-11
@@ -26,29 +26,28 @@ flowsight/
|
||||
smart_money.go broker_intel.go sentiment.go fundamental.go
|
||||
technical.go catalyst.go synthesizer.go
|
||||
routines/ # 7 routines (see ROUTINES.md)
|
||||
engine.go # cron dispatch, run recording, delivery
|
||||
briefing.go radar.go reversal.go insider.go earnings.go dividend.go weekly.go
|
||||
engine.go # cron dispatch, run recording, delivery, SSE publish
|
||||
briefing.go # R1 briefing + R2 radar + R3 reversal + R4 insider + R5/R6 countdowns + R7 review
|
||||
alerts/
|
||||
rules.go # 6 detection rules over snapshots
|
||||
evaluate.go # per-cycle evaluation
|
||||
notify.go # Telegram/Discord webhooks
|
||||
reports/
|
||||
builder.go # 7-section assembly + citations[]
|
||||
report.go # 7-section assembly + citations[] (returns persisted id)
|
||||
render.go # PDF/HTML/MD/JSON exporters
|
||||
api/ # chi route handlers (see API.md)
|
||||
flow.go screen.go routines.go briefing.go alerts.go
|
||||
report.go watchlist.go portfolio.go accuracy.go chat.go health.go stream.go
|
||||
flow.go screen.go routines.go health.go alerts.go
|
||||
report.go interrogate.go watchlist.go portfolio.go chat.go stream.go
|
||||
scheduler/ # robfig/cron wiring (ingestion + routines)
|
||||
web/ # SolidJS 1.9 + Vite 6 + StyleX
|
||||
web/ # SolidJS 1.9 + Vite 6 + typed CSS
|
||||
src/
|
||||
pages/ # Dashboard, Routines, Alerts, Screener, Portfolio, Report
|
||||
components/ # cards, tables, rotation map, correlation matrix
|
||||
lib/api.ts # typed backend client + SSE hooks
|
||||
styles/ # StyleX tokens + themes
|
||||
tests/
|
||||
fixtures/ # historical snapshots (one trading week)
|
||||
agents_*_test.go # per-agent fixture tests
|
||||
rules_test.go report_test.go budget_test.go
|
||||
styles/ # CSS tokens + themes (tokens.css)
|
||||
backend/tests/
|
||||
fixtures/ # historical snapshots (one trading week, manifest.json)
|
||||
seed + agents + rules + evaluate + api + routines + client + accuracy tests
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
+2
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
SQLite for the hackathon; schema kept Postgres-compatible (serial → integer PK,
|
||||
JSON → TEXT with JSON1, no SQLite-only DDL). Migrations numbered in
|
||||
`backend/app/store/migrations/`.
|
||||
`backend/internal/store/migrations/`.
|
||||
|
||||
## Tables
|
||||
|
||||
@@ -17,6 +17,7 @@ JSON → TEXT with JSON1, no SQLite-only DDL). Migrations numbered in
|
||||
- `routines(id, user_key, type, schedule_cron, channels_json, enabled)` — 7 types (R1–R7).
|
||||
- `routine_runs(id, routine_id, started_at, status, payload_json, credits_used)`.
|
||||
- `alerts(id, user_key, name, rule_json, channels_json, last_fired)`.
|
||||
- `notification_destinations(id, user_key, kind, label, bot_token, chat_id, webhook_url, enabled, created_at)` — per-user push targets. `kind` telegram needs bot_token+chat_id, discord needs webhook_url (https). Secrets never leave the server in API responses.
|
||||
- `alert_events(id, alert_id, ticker, date, message, context_json, citations_json)`.
|
||||
- `watchlists(user_key, ticker, added_at)` — PK (user_key, ticker).
|
||||
- `reports(id, ticker, generated_at, payload_json, citations_json)`.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# FlowSight — Demo Deck (offline, seeded)
|
||||
|
||||
Narrative: briefing → radar alert → report → interrogation. All numbers cite
|
||||
snapshots; stale chips render marked, never hidden.
|
||||
|
||||
## Slide 1 — Problem
|
||||
6M+ retail SID decide from price + rumor. Institutional data (broker flow,
|
||||
foreign flow, insider filings) is raw across 70 Sectors endpoints.
|
||||
|
||||
## Slide 2 — Autopilot routines
|
||||
Subscribe once; agents run on schedule; results land without opening the app.
|
||||
R1 Morning Briefing 07:30 · R2 Radar 30-min · R3 Reversal · R4 Insider Tape ·
|
||||
R5 Earnings H-7/H-3/H-1 · R6 Dividend H-14/H-3 · R7 Weekend Review.
|
||||
|
||||
## Slide 3 — Live: Morning Briefing
|
||||
`GET /api/briefing/today` — top-5 accumulation, foreign table, 7d agenda,
|
||||
mover of the day. ≤25 lines, every row cited.
|
||||
|
||||
## Slide 4 — Live: Accumulation Radar
|
||||
BBCA: 3 brokers net-buy Rp1.2T, volume 3.2× 20d avg → alert card with score,
|
||||
top-3 brokers, volume multiple, `/report/BBCA` link.
|
||||
|
||||
## Slide 5 — Live: One-Click Report
|
||||
`POST /api/report/BBCA` — 7 sections with `citations[]` per section; export
|
||||
PDF/HTML/MD/JSON. Thesis claims carry inline `[endpoint @ date]` markers.
|
||||
|
||||
## Slide 6 — Verifiable AI + Accuracy Ledger
|
||||
Every number cites endpoint + snapshot timestamp. Recommendations resolve at
|
||||
+30d (`ResolveDue` daily cron); agent weights follow hit-rate, shown in
|
||||
`/portfolio` accuracy table.
|
||||
|
||||
## Slide 7 — Ask
|
||||
Run `BASE=http://localhost:8080 sh scripts/demo.sh` offline from seed.
|
||||
+15
-14
@@ -61,7 +61,7 @@ modelnya sendiri.
|
||||
|
||||
## 5. Agent System
|
||||
|
||||
7 specialist agents, dieksekusi paralel via asyncio, diorkestrasi scheduler + on-demand.
|
||||
7 specialist agents, dieksekusi paralel via goroutine, diorkestrasi scheduler + on-demand.
|
||||
|
||||
| Agent | Input (Sectors API) | Output |
|
||||
|---|---|---|
|
||||
@@ -98,16 +98,17 @@ modelnya sendiri.
|
||||
6. **Portfolio Risk** — konsentrasi sektor, matriks korelasi, beta vs IHSG.
|
||||
7. **AI Chat sidebar** — context-aware dari watchlist.
|
||||
|
||||
## 7. Architecture (detail: docs/ARCHITECTURE.md; stack: docs/TECH-STACK.md)
|
||||
- Frontend: Next.js 15 + React 19 + TypeScript + Tailwind v4 + Recharts (SSE streaming, responsive)
|
||||
- Backend: Python 3.12 + FastAPI + Uvicorn + httpx (async) + Pydantic v2 (eksekusi agent paralel)
|
||||
- LLM: OpenAI SDK v1 provider-agnostic (`LLM_BASE_URL`), gpt-4o-mini triage + gpt-4o synthesis
|
||||
## 7. Architecture (authoritative: docs/ARCHITECTURE.md; stack: docs/TECH-STACK.md)
|
||||
- Frontend: SolidJS 1.9 + Vite 6 + TypeScript + typed CSS + Chart.js (SSE streaming, responsive)
|
||||
- Backend: Go 1.23 + chi v5 + database/sql (modernc.org/sqlite, pure Go) + goroutine
|
||||
(eksekusi agent paralel)
|
||||
- LLM: Plain HTTPS ke endpoint OpenAI-compatible (`LLM_BASE_URL`); `LLM_MODEL_TRIAGE` murah + `LLM_MODEL_SYNTH` kuat
|
||||
- Data: Sectors API v2 `https://api.sectors.app/v2/`, auth `Authorization: <key>`
|
||||
dari env `SECTORS_API_KEY`
|
||||
- Store: SQLite (stdlib, skema Postgres-compatible) + Redis 7 cache (degradasi in-memory jika kosong)
|
||||
- Scheduler: APScheduler AsyncIO, ingestion tiap 30 min saat market hours + routine harian/mingguan
|
||||
- Scheduler: robfig/cron v3 in-process, ingestion tiap 30 min saat market hours + routine harian/mingguan
|
||||
- Notify: outbound webhook → Telegram / Discord
|
||||
- PDF: ReportLab (tanpa system deps); test: pytest + respx + fakeredis; gate: ruff + mypy + tsc + next build
|
||||
- PDF: gofpdf (pure Go, tanpa system deps); test: `go test` + `httptest` (mock upstream Sectors); gate: `gofmt` + `go vet` + `go test` + `tsc` + `vite build`
|
||||
|
||||
## 8. Data model
|
||||
- `snapshots(ticker, date, source, payload)` — raw ingestion
|
||||
@@ -173,13 +174,13 @@ Subsector: subsector/report (6 sections). Phase 2: SGX (9), KLSE (4), mining (19
|
||||
| 44–48 | Polish, demo script, deck |
|
||||
|
||||
## 14. Verification
|
||||
- `/api/health` last cycle < 35 min saat market hours
|
||||
- Routine briefing generate dari snapshot tanpa empty section + citations lengkap
|
||||
- Fixture akumulasi → alert event + webhook terkirim ke kanal uji
|
||||
- Screener balikin ranked list + breakdown per row
|
||||
- Report BBCA < 15s, 7 section terisi dari live API + citations
|
||||
- Key hanya dari env, v2 paths only
|
||||
- Market tutup → demo pakai historical replay seed
|
||||
- `GET /api/health` balik last cycle + credits today + scheduler state (verifikasi dalam smoke test)
|
||||
- Fixture briefing generate dari seed tanpa empty section + citations lengkap (unit test)
|
||||
- Fixture akumulasi → alert event tercatat (rules_test) + webhook ke kanal uji bila token di-env
|
||||
- Screener balikin ranked list + breakdown per row (api_test)
|
||||
- Report BBCA 7 section terisi dari seed/live + citations, export PDF/HTML/MD/JSON (smoke)
|
||||
- Key hanya dari env (`SECTORS_API_KEY`), v2 paths only (client_test: v1 ditolak sebelum HTTP)
|
||||
- Market tutup → demo offline pakai historical replay seed (scripts/demo.sh)
|
||||
|
||||
## 15. Risks
|
||||
- Butuh Insider API key sebelum jam 0
|
||||
|
||||
@@ -12,3 +12,4 @@ Spec-driven source of truth. Code follows these docs; docs change before code.
|
||||
| [AGENT-SPECS.md](AGENT-SPECS.md) | 7 agent contracts: inputs, processing steps, outputs, verification fixtures |
|
||||
| [DATA-MODEL.md](DATA-MODEL.md) | Table schemas, indexes, retention, seed strategy |
|
||||
| [API.md](API.md) | Backend route specs: request/response shapes, errors, auth |
|
||||
| [DEMO-DECK.md](DEMO-DECK.md) | Offline demo narrative: briefing → radar → report → interrogation |
|
||||
|
||||
+11
-11
@@ -7,25 +7,25 @@ Pinned versions. Change here before code. CI enforces the gates at the bottom.
|
||||
| Piece | Choice | Why |
|
||||
|---|---|---|
|
||||
| Runtime | Go 1.23 | Single binary, fast cold start on demo machines, `net/http` routing mature since 1.22 |
|
||||
| Router | chi v5 | Thin router over stdlib mux (middleware, route groups); no framework lock-in |
|
||||
| Router | chi v5.2.3 | Thin router over stdlib mux (middleware, route groups); no framework lock-in |
|
||||
| HTTP client | stdlib `net/http` + tuned `Transport` | One shared client for all Sectors calls (pooling, per-endpoint timeouts) |
|
||||
| Validation | go-playground/validator v10 | Request struct tags = kontrak docs/API.md; gagal validasi → 422 |
|
||||
| DB | `database/sql` + modernc.org/sqlite (pure Go) | Nol CGO — `mattn/go-sqlite3` butuh gcc dan gagal di mesin juri tanpa toolchain; schema Postgres-compatible, migrasi SQL polos bernomor, tanpa ORM |
|
||||
| Cache | go-redis v9; in-memory TTL fallback bila `REDIS_URL` kosong | Cache registry/taxonomy (TTL 24 jam); demo tetap jalan tanpa Redis |
|
||||
| Scheduler | robfig/cron v3 | Cron per routine + interval ingestion dalam satu proses |
|
||||
| Validation | go-playground/validator v10.25.0 | Request struct tags = kontrak docs/API.md; gagal validasi → 422 |
|
||||
| DB | `database/sql` + modernc.org/sqlite v1.39.0 (pure Go) | Nol CGO — `mattn/go-sqlite3` butuh gcc dan gagal di mesin juri tanpa toolchain; schema Postgres-compatible, migrasi SQL polos bernomor, tanpa ORM |
|
||||
| Cache | go-redis v9.12.1; in-memory TTL fallback bila `REDIS_URL` kosong | Cache registry/taxonomy (TTL 24 jam); demo tetap jalan tanpa Redis |
|
||||
| Scheduler | robfig/cron v3.0.1 | Cron per routine + interval ingestion dalam satu proses |
|
||||
| LLM | Plain HTTPS ke endpoint OpenAI-compatible (`LLM_BASE_URL`) | Function calling untuk synthesis/report/briefing; `LLM_MODEL_TRIAGE` murah + `LLM_MODEL_SYNTH` kuat, override lewat env |
|
||||
| PDF export | gofpdf (jung-kurt fork) | Pure Go, tanpa system deps |
|
||||
| Config | env via `os.Getenv` + `godotenv` untuk dev | Semua secret dari env; contoh di `.env.example` |
|
||||
| Test | `go test` + `httptest` (mock upstream Sectors) | Agent/rule/budget tests atas fixture JSON bentuk API asli |
|
||||
| Lint/type | `gofmt -l` + `go vet ./...` (+ golangci-lint bila tersedia) | Compiler sudah strict; vet menangkap yang penting |
|
||||
|
||||
## Frontend — `web/` (SolidJS + StyleX)
|
||||
## Frontend — `web/` (SolidJS + typed CSS)
|
||||
|
||||
| Piece | Choice | Why |
|
||||
|---|---|---|
|
||||
| Framework | SolidJS 1.9 + Vite 6 + TypeScript 5.6 | Fine-grained reactivity — ideal untuk live feed/SSE tanpa re-render tree; bundle kecil untuk demo cepat |
|
||||
| Styling | StyleX (@stylexjs/stylex + @stylexjs/vite-plugin) | Atomic CSS deterministic, typed via TS, tanpa runtime; ganti Tailwind sepenuhnya |
|
||||
| Charts | Chart.js 4 via solid-chartjs | Wrapper Solid resmi untuk flow/series; Recharts React-only jadi tidak dipakai |
|
||||
| Framework | SolidJS 1.9.15 + Vite 6.4.3 + TypeScript 5.6.3 | Fine-grained reactivity — ideal untuk live feed/SSE tanpa re-render tree; bundle kecil untuk demo cepat |
|
||||
| Styling | Typed CSS modules + CSS custom-property tokens (`web/src/styles/`) | Atomic CSS deterministic, typed via TS, tanpa runtime; ganti Tailwind sepenuhnya |
|
||||
| Charts | Chart.js 4.4.7 via solid-chartjs 1.3.11 | Wrapper Solid resmi untuk flow/series; Recharts React-only jadi tidak dipakai |
|
||||
| Data fetch | `fetch` + typed client (`lib/api.ts`) + `EventSource` untuk SSE | Backend satu-satunya sumber kebenaran; web tidak pernah manggil Sectors langsung |
|
||||
| Test/gate | `tsc --noEmit` + `vite build` | Cukup untuk hackathon; tanpa e2e framework |
|
||||
|
||||
@@ -35,14 +35,14 @@ Pinned versions. Change here before code. CI enforces the gates at the bottom.
|
||||
|---|---|---|
|
||||
| Go deps | Go modules (`go.mod`, vendoring opsional via `go mod vendor`) | Build reproducible; `go build ./...` satu perintah |
|
||||
| JS deps | pnpm 9 + `pnpm-lock.yaml` | Install deterministik; fallback `npm` jika pnpm tidak ada |
|
||||
| Env | `.env` (tidak di-commit) — `SECTORS_API_KEY`, `LLM_API_KEY`, `LLM_BASE_URL`, `LLM_MODEL_SYNTH`, `LLM_MODEL_TRIAGE`, `TELEGRAM_BOT_TOKEN`, `DISCORD_WEBHOOK_URL`, `REDIS_URL` | Semua secret dari env; contoh di `.env.example` |
|
||||
| Env | `.env` (tidak di-commit) — `SECTORS_API_KEY`, `LLM_API_KEY`, `LLM_BASE_URL`, `LLM_MODEL_SYNTH`, `LLM_MODEL_TRIAGE`, `TELEGRAM_BOT_TOKEN`, `DISCORD_WEBHOOK_URL`, `REDIS_URL`, `BWS_PROJECT_ID` | Semua secret dari env; contoh di `.env.example`. Di prod simpan secret di Bitwarden Secrets Manager dan jalankan via `sh scripts/bws-run.sh …` (env tetap fallback). `TELEGRAM_*`/`DISCORD_*` hanya fallback server; tiap user kelola tujuannya di `/api/destinations` |
|
||||
| Procfile dev | dua proses: `go run ./cmd/server` (atau `air` untuk reload) + `vite dev` (+ redis opsional) | Demo tetap jalan tanpa Redis |
|
||||
|
||||
## Alternatives declined
|
||||
|
||||
- **Python/FastAPI** — startup + packaging demo lebih rapuh (venv, pip) dibanding satu binary Go; konkurensi agent paralel setara via goroutine.
|
||||
- **Next.js/React** — overhead framework + re-render model untuk dashboard live; Solid memberi update granular dengan bundle lebih kecil.
|
||||
- **Tailwind** — diganti StyleX: atomic, typed, nol runtime, tanpa scanning step.
|
||||
- **Tailwind / StyleX** — diganti typed CSS modules + token CSS: nol runtime, tanpa scanning step, tanpa unplugin versi (registry belum stabil); kontrak visual tetap lewat `styles/tokens.css`.
|
||||
- **Recharts** — React-only; Chart.js via solid-chartjs menutup kebutuhan chart di Solid.
|
||||
- **mattn/go-sqlite3** — butuh CGO/gcc; modernc pure-Go selalu bisa build.
|
||||
- **GORM / sqlc / Alembic-style migrator** — overhead untuk 13 tabel; SQL polos + skrip bernomor cukup dan mudah diaudit juri.
|
||||
|
||||
Reference in New Issue
Block a user