Files
Maven/internal/dialogue/session.go
T
kami da60c14399 chore: docker, config, delivery sinks, dialogue, and agent docs
- Dockerfile: multi-stage build with CGO_ENABLED=0, embedder model copy,
  non-root user, healthcheck, and /data volume.
- docker-compose.yml: mavend + mavweb services with shared volume, health
  checks, and restart policy.
- .gitignore: ignore models/llm/*.gguf, deploy/telegram.env, tmp artifacts.
- deploy/mavend.json: add LLM, phraser, voice sections (embedder, model
  paths, wake sensitivity). Add telegram token env-var expansion.
- deploy/telegram.env.example: template for telegram bot token.
- internal/config/config.go: add LLM config struct, voice config struct
  (embedder, llama, wake sensitivity), telegram token loading.
- telegramsink: add chat intent delivery support alongside existing types.
- voicesink: skip empty payloads in delivery.
- dialogue/session: add chat intent to anaphora resolution, test coverage.
- AGENTS.md: update with LLM embedder, LFM model download/configure steps,
  new UI conventions.
- REARCH.md: architecture research document.
- cmd/mavend/main.go: wire LLM config, phraser, embedder, telegram config,
  WebAuthn, IPC event/routine handlers, and reactive notes.
2026-07-10 15:49:27 +04:00

119 lines
2.3 KiB
Go

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
}