fix(hitl): keep the diagnosis in the HITL ledger so edited messages retain evidence and rationale

버튼 처리·만료 후 메시지를 편집할 때 빈 진단으로 다시 그려 증거·근거 필드가 "-"로 지워지던
결함을 고친다. ai.hitl_messages에 diagnosis jsonb를 추가해 게시 시점의 진단을 저장하고
편집 시 그대로 재사용한다.
This commit is contained in:
seon committed 2026-10-08 01:47:04 +09:00
1 parent c442601ab3
commit 43696d48c8
2 files changed
+12 -13

No files matched your search

+3 -8
View File
@@ -131,7 +131,7 @@ func (b *Bot) Post(ctx context.Context, req PostRequest) error {
if err != nil { if err != nil {
return err return err
} }
if err := b.store.InsertHitl(ctx, rag.HitlMessage{Token: req.Token, ChannelID: msg.ChannelID, MessageID: msg.ID, ActionID: req.ActionID, Target: req.Target, RunID: req.RunID, ScenarioID: req.ScenarioID, SessionID: req.SessionID, ExpiresAt: expires}); err != nil { if err := b.store.InsertHitl(ctx, rag.HitlMessage{Token: req.Token, ChannelID: msg.ChannelID, MessageID: msg.ID, ActionID: req.ActionID, Target: req.Target, Diagnosis: req.Diagnosis, RunID: req.RunID, ScenarioID: req.ScenarioID, SessionID: req.SessionID, ExpiresAt: expires}); err != nil {
return err return err
} }
b.arm(req.Token, time.Until(expires)) b.arm(req.Token, time.Until(expires))
@@ -217,14 +217,9 @@ func (b *Bot) Decide(ctx context.Context, token, decision, by string) (string, e
return "", fmt.Errorf("unknown decision %s", decision) return "", fmt.Errorf("unknown decision %s", decision)
} }
// edit re-renders the original message with the stored diagnosis, the new status and disabled buttons.
func (b *Bot) edit(m *rag.HitlMessage, status string, color int, result map[string]any) error { func (b *Bot) edit(m *rag.HitlMessage, status string, color int, result map[string]any) error {
ctx := context.Background() em := embedFor(m.Diagnosis, m.ActionID, m.Target, m.RunID, m.ScenarioID, status, color)
d := map[string]any{}
if se, err := b.store.GetHitl(ctx, m.Token); err == nil {
m = se
}
em := embedFor(d, m.ActionID, m.Target, m.RunID, m.ScenarioID, status, color)
em.Fields = em.Fields[1:] // the diagnosis fields are already in the original message; keep the edit compact
if result != nil { if result != nil {
rb, _ := json.Marshal(result) rb, _ := json.Marshal(result)
em.Fields = append(em.Fields, &discordgo.MessageEmbedField{Name: "Result", Value: "```json\n" + truncate(string(rb), 900) + "\n```"}) em.Fields = append(em.Fields, &discordgo.MessageEmbedField{Name: "Result", Value: "```json\n" + truncate(string(rb), 900) + "\n```"})
+9 -5
View File
@@ -57,6 +57,7 @@ CREATE TABLE IF NOT EXISTS ai.hitl_messages (
message_id text NOT NULL, message_id text NOT NULL,
action_id text NOT NULL, action_id text NOT NULL,
target jsonb NOT NULL DEFAULT '{}'::jsonb, target jsonb NOT NULL DEFAULT '{}'::jsonb,
diagnosis jsonb NOT NULL DEFAULT '{}'::jsonb,
run_id text, run_id text,
scenario_id text, scenario_id text,
session_id text, session_id text,
@@ -302,6 +303,7 @@ type HitlMessage struct {
MessageID string MessageID string
ActionID string ActionID string
Target map[string]any Target map[string]any
Diagnosis map[string]any
RunID *string RunID *string
ScenarioID *string ScenarioID *string
SessionID string SessionID string
@@ -313,20 +315,22 @@ type HitlMessage struct {
func (s *Store) InsertHitl(ctx context.Context, m HitlMessage) error { func (s *Store) InsertHitl(ctx context.Context, m HitlMessage) error {
t, _ := json.Marshal(m.Target) t, _ := json.Marshal(m.Target)
_, err := s.pool.Exec(ctx, `INSERT INTO ai.hitl_messages (token,channel_id,message_id,action_id,target,run_id,scenario_id,session_id,expires_at) d, _ := json.Marshal(m.Diagnosis)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`, m.Token, m.ChannelID, m.MessageID, m.ActionID, t, m.RunID, m.ScenarioID, m.SessionID, m.ExpiresAt) _, err := s.pool.Exec(ctx, `INSERT INTO ai.hitl_messages (token,channel_id,message_id,action_id,target,diagnosis,run_id,scenario_id,session_id,expires_at)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`, m.Token, m.ChannelID, m.MessageID, m.ActionID, t, d, m.RunID, m.ScenarioID, m.SessionID, m.ExpiresAt)
return err return err
} }
func (s *Store) GetHitl(ctx context.Context, token string) (*HitlMessage, error) { func (s *Store) GetHitl(ctx context.Context, token string) (*HitlMessage, error) {
var m HitlMessage var m HitlMessage
var t []byte var t, d []byte
err := s.pool.QueryRow(ctx, `SELECT token,channel_id,message_id,action_id,target,run_id,scenario_id,COALESCE(session_id,''),expires_at,decided_at,decision,decided_by FROM ai.hitl_messages WHERE token=$1`, token). err := s.pool.QueryRow(ctx, `SELECT token,channel_id,message_id,action_id,target,diagnosis,run_id,scenario_id,COALESCE(session_id,''),expires_at,decided_at,decision,decided_by FROM ai.hitl_messages WHERE token=$1`, token).
Scan(&m.Token, &m.ChannelID, &m.MessageID, &m.ActionID, &t, &m.RunID, &m.ScenarioID, &m.SessionID, &m.ExpiresAt, &m.DecidedAt, &m.Decision, &m.DecidedBy) Scan(&m.Token, &m.ChannelID, &m.MessageID, &m.ActionID, &t, &d, &m.RunID, &m.ScenarioID, &m.SessionID, &m.ExpiresAt, &m.DecidedAt, &m.Decision, &m.DecidedBy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
_ = json.Unmarshal(t, &m.Target) _ = json.Unmarshal(t, &m.Target)
_ = json.Unmarshal(d, &m.Diagnosis)
return &m, nil return &m, nil
} }