package dialogue import ( "sync" "time" ) type Intent string const ( IntentAct Intent = "act" IntentReminder Intent = "reminder" IntentFact Intent = "fact" IntentNote Intent = "note" IntentQuery Intent = "query" IntentChat Intent = "chat" IntentSystem Intent = "system" ) type Slots struct { Time time.Time HasTime bool Key string HasKey bool Text string Fn string Args []string HasFn bool } // Turn represents one utterance in a multi-turn dialogue history. // Carried by Session.History for cross-intent reference and anaphora // resolution (a later turn's pronoun points to an earlier turn's entity). type Turn struct { Intent Intent Slots Slots Text string // raw utterance } type Session struct { Intent Intent Slots Slots Timestamp time.Time TTL time.Duration History []Turn // most recent turns, newest last; used for anaphora + cross-intent } func (s *Session) IsExpired(now time.Time) bool { return now.After(s.Timestamp.Add(s.TTL)) } type SessionStore struct { mu sync.RWMutex sessions map[string]*Session defaultTTL time.Duration } func NewSessionStore(defaultTTL time.Duration) *SessionStore { if defaultTTL <= 0 { defaultTTL = 2 * time.Minute } return &SessionStore{ sessions: make(map[string]*Session), defaultTTL: defaultTTL, } } func (s *SessionStore) Get(id string, now time.Time) *Session { s.mu.RLock() sess, ok := s.sessions[id] s.mu.RUnlock() if !ok { return nil } if sess.IsExpired(now) { s.Delete(id) return nil } return sess } func (s *SessionStore) Put(id string, sess *Session) { if sess.TTL <= 0 { sess.TTL = s.defaultTTL } s.mu.Lock() s.sessions[id] = sess s.mu.Unlock() } func (s *SessionStore) Delete(id string) { s.mu.Lock() delete(s.sessions, id) s.mu.Unlock() } func InheritSlots(prev, cur Slots) Slots { out := cur if !out.HasTime && prev.HasTime { out.Time = prev.Time out.HasTime = true } if !out.HasKey && prev.HasKey { out.Key = prev.Key out.HasKey = true } if out.Text == "" && prev.Text != "" { out.Text = prev.Text } if !out.HasFn && prev.HasFn { out.Fn = prev.Fn out.HasFn = true } if len(out.Args) == 0 && len(prev.Args) > 0 { out.Args = append([]string(nil), prev.Args...) } return out }