feat: auto-narasi LLM (OmniRoute sonnet-5) — hero briefing + narasi_awam report, migrasi 0006
CI / go test + web typecheck (push) Canceled after 0s
CI / build + deploy (Nix) — flowsight (push) Canceled after 0s

This commit is contained in:
asepharyana
2026-09-15 19:50:48 +07:00
parent 390133663a
commit 9fa47240dc
9 changed files with 86 additions and 12 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ func (s *Server) Chat(w http.ResponseWriter, r *http.Request) {
citesRaw = cites
} else {
// Unscoped: ground on the latest briefing + watchlist.
_, payload, cites, err := s.DB.LatestBriefing()
_, payload, cites, _, err := s.DB.LatestBriefing()
if err != nil {
ground = "no briefing or report data yet"
} else {
+1 -1
View File
@@ -16,7 +16,7 @@ func (s *Server) Health(w http.ResponseWriter, r *http.Request) {
lastCycle, schedOK := s.Sched.Status()
today := time.Now().Format("2006-01-02")
credits := s.DB.CreditsToday(today)
lastDate, _, _, _ := s.DB.LatestBriefing()
lastDate, _, _, _, _ := s.DB.LatestBriefing()
cutoff := model.StaleSession(time.Now())
stale := []string{}
if lastDate != "" {
+18 -2
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"net/http"
"strconv"
"strings"
"time"
"github.com/go-chi/chi/v5"
@@ -115,7 +116,7 @@ func (s *Server) RunHistory(w http.ResponseWriter, r *http.Request) {
// When no briefing exists yet it generates one on demand from current
// snapshots (so the UI never shows an empty page), then persists it.
func (s *Server) BriefingToday(w http.ResponseWriter, r *http.Request) {
date, payload, cites, err := s.DB.LatestBriefing()
date, payload, cites, narasi, err := s.DB.LatestBriefing()
if err != nil {
uk := s.userKey(r)
p, cc, gerr := s.Engine.BriefingFor(r.Context(), uk)
@@ -130,8 +131,23 @@ func (s *Server) BriefingToday(w http.ResponseWriter, r *http.Request) {
}
date, payload, cites = todayUTC(), p, string(ccJSON)
}
// Lazy narration: polish once via LLM, cache in DB, serve thereafter.
// Any LLM failure leaves narasi empty (FE falls back to rules summary).
if narasi == "" && s.LLM.Available() {
if text, nerr := s.LLM.Complete(r.Context(), s.Cfg.LLMSynth,
"Kamu asisten pasar saham Indonesia yang ramah. Poles ringkasan data "+
"berikut jadi TEPAT 3 kalimat Bahasa Indonesia santai untuk orang awam. "+
"Kalimat 1: saham apa yang paling diborong + artinya. "+
"Kalimat 2: arah uang asing. Kalimat 3: hal yang perlu diperhatikan. "+
"Setiap angka WAJIB berasal dari data — jangan mengarang. Tanpa sapaan.",
"Data:\n"+head(payload, 2500), 300); nerr == nil && text != "" {
narasi = strings.TrimSpace(text)
_ = s.DB.SaveNarasi(date, narasi)
}
}
writeJSON(w, http.StatusOK, map[string]any{
"date": date, "payload": payload, "citations": cites,
"date": date, "payload": payload, "citations": cites, "narasi": narasi,
})
}