Files
cthulhu/internal/app/speech_history.go

106 lines
2.0 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"strings"
"sync"
"git.stormux.org/storm/cthulhu/internal/config"
)
// speechHistory retains the most recent distinct utterances. The history
// window suspends capture so its own feedback never changes the list.
type speechHistory struct {
mu sync.Mutex
items []string
limit int
paused bool
monitor chan string
}
func newSpeechHistory(limit int) *speechHistory {
if limit < config.MinSpeechHistoryLimit || limit > config.MaxSpeechHistoryLimit {
limit = config.DefaultSpeechHistoryLimit
}
return &speechHistory{limit: limit}
}
func (h *speechHistory) setLimit(limit int) {
if limit < config.MinSpeechHistoryLimit || limit > config.MaxSpeechHistoryLimit {
return
}
h.mu.Lock()
defer h.mu.Unlock()
h.limit = limit
if len(h.items) > limit {
clear(h.items[limit:])
h.items = h.items[:limit]
}
}
func (h *speechHistory) add(text, role string) {
if role == "password text" {
return
}
text = strings.TrimSpace(text)
if text == "" {
return
}
h.mu.Lock()
defer h.mu.Unlock()
if h.monitor != nil {
select {
case h.monitor <- text:
default:
}
}
if h.paused {
return
}
for _, item := range h.items {
if item == text {
return
}
}
h.items = append(h.items, "")
copy(h.items[1:], h.items[:len(h.items)-1])
h.items[0] = text
if len(h.items) > h.limit {
clear(h.items[h.limit:])
h.items = h.items[:h.limit]
}
}
func (h *speechHistory) snapshot() []string {
h.mu.Lock()
defer h.mu.Unlock()
return append([]string(nil), h.items...)
}
func (h *speechHistory) remove(text string) bool {
h.mu.Lock()
defer h.mu.Unlock()
for i, item := range h.items {
if item == text {
h.items = append(h.items[:i], h.items[i+1:]...)
return true
}
}
return false
}
func (h *speechHistory) setPaused(paused bool) {
h.mu.Lock()
h.paused = paused
h.mu.Unlock()
}
func (h *speechHistory) setMonitor(ch chan string) {
h.mu.Lock()
h.monitor = ch
h.mu.Unlock()
}