| 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 |
| } |
|
|
| |
| |
| 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), |
| } |
| } |
|
|
| |
| 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 |
| } |
|
|
| |
| 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 |
| } |
|
|