da60c14399
- 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.
119 lines
2.3 KiB
Go
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
|
|
}
|