cursor / services /response_store.go
cacode's picture
Upload 48 files
1766992 verified
package services
import (
"github.com/libaxuan/cursor2api-go/models"
"sync"
"time"
)
const (
defaultResponseStoreTTL = 30 * time.Minute
defaultResponseStoreMaxEntries = 1000
)
type responseStoreEntry struct {
messages []models.Message
createdAt time.Time
}
// ResponseStore keeps recent Responses API conversation state in memory.
// It is best-effort and resets on process restart.
type ResponseStore struct {
mu sync.RWMutex
ttl time.Duration
maxEntries int
entries map[string]responseStoreEntry
order []string
}
func NewResponseStore(ttl time.Duration, maxEntries int) *ResponseStore {
if ttl <= 0 {
ttl = defaultResponseStoreTTL
}
if maxEntries <= 0 {
maxEntries = defaultResponseStoreMaxEntries
}
return &ResponseStore{
ttl: ttl,
maxEntries: maxEntries,
entries: make(map[string]responseStoreEntry),
order: make([]string, 0, maxEntries),
}
}
// Get returns stored messages for a response id if present and not expired.
func (s *ResponseStore) Get(id string) ([]models.Message, bool) {
if id == "" {
return nil, false
}
now := time.Now()
s.mu.Lock()
defer s.mu.Unlock()
s.cleanupLocked(now)
entry, ok := s.entries[id]
if !ok {
return nil, false
}
return cloneMessages(entry.messages), true
}
// Put stores messages for a response id, evicting older entries if needed.
func (s *ResponseStore) Put(id string, messages []models.Message) {
if id == "" || len(messages) == 0 {
return
}
now := time.Now()
s.mu.Lock()
defer s.mu.Unlock()
s.cleanupLocked(now)
if _, exists := s.entries[id]; !exists {
s.order = append(s.order, id)
}
s.entries[id] = responseStoreEntry{
messages: cloneMessages(messages),
createdAt: now,
}
for len(s.entries) > s.maxEntries {
s.evictOldestLocked()
}
}
func (s *ResponseStore) cleanupLocked(now time.Time) {
if s.ttl <= 0 {
return
}
for id, entry := range s.entries {
if now.Sub(entry.createdAt) > s.ttl {
delete(s.entries, id)
s.removeFromOrderLocked(id)
}
}
}
func (s *ResponseStore) evictOldestLocked() {
if len(s.order) == 0 {
return
}
oldest := s.order[0]
s.order = s.order[1:]
delete(s.entries, oldest)
}
func (s *ResponseStore) removeFromOrderLocked(id string) {
for i, key := range s.order {
if key == id {
s.order = append(s.order[:i], s.order[i+1:]...)
return
}
}
}
func cloneMessages(src []models.Message) []models.Message {
if len(src) == 0 {
return nil
}
dst := make([]models.Message, len(src))
copy(dst, src)
return dst
}