Files
ds2api/internal/httpapi/openai/responses/response_store.go
2026-04-26 06:58:20 +08:00

110 lines
2.0 KiB
Go

package responses
import (
"sync"
"time"
"ds2api/internal/auth"
)
type storedResponse struct {
Owner string
Value map[string]any
ExpiresAt time.Time
}
type responseStore struct {
mu sync.Mutex
ttl time.Duration
items map[string]storedResponse
}
func newResponseStore(ttl time.Duration) *responseStore {
if ttl <= 0 {
ttl = 15 * time.Minute
}
return &responseStore{
ttl: ttl,
items: make(map[string]storedResponse),
}
}
func responseStoreKey(owner, id string) string {
return owner + "\x00" + id
}
func responseStoreOwner(a *auth.RequestAuth) string {
if a == nil {
return ""
}
return a.CallerID
}
func (s *responseStore) put(owner, id string, value map[string]any) {
if s == nil || owner == "" || id == "" || value == nil {
return
}
now := time.Now()
s.mu.Lock()
defer s.mu.Unlock()
s.sweepLocked(now)
s.items[responseStoreKey(owner, id)] = storedResponse{
Owner: owner,
Value: cloneAnyMap(value),
ExpiresAt: now.Add(s.ttl),
}
}
func (s *responseStore) get(owner, id string) (map[string]any, bool) {
if s == nil || owner == "" || id == "" {
return nil, false
}
now := time.Now()
s.mu.Lock()
defer s.mu.Unlock()
s.sweepLocked(now)
item, ok := s.items[responseStoreKey(owner, id)]
if !ok {
return nil, false
}
if item.Owner != owner {
return nil, false
}
return cloneAnyMap(item.Value), true
}
func (s *responseStore) sweepLocked(now time.Time) {
for k, v := range s.items {
if now.After(v.ExpiresAt) {
delete(s.items, k)
}
}
}
func cloneAnyMap(in map[string]any) map[string]any {
if in == nil {
return nil
}
out := make(map[string]any, len(in))
for k, v := range in {
out[k] = v
}
return out
}
func (h *Handler) getResponseStore() *responseStore {
if h == nil {
return nil
}
h.responsesMu.Lock()
defer h.responsesMu.Unlock()
if h.responses == nil {
ttl := 15 * time.Minute
if h.Store != nil {
ttl = time.Duration(h.Store.ResponsesStoreTTLSeconds()) * time.Second
}
h.responses = newResponseStore(ttl)
}
return h.responses
}