| package cache |
|
|
| import ( |
| "crypto/sha256" |
| "encoding/hex" |
| "strings" |
| "sync" |
| "time" |
| ) |
|
|
| |
| type SignatureEntry struct { |
| Signature string |
| Timestamp time.Time |
| } |
|
|
| const ( |
| |
| SignatureCacheTTL = 3 * time.Hour |
|
|
| |
| SignatureTextHashLen = 16 |
|
|
| |
| MinValidSignatureLen = 50 |
|
|
| |
| CacheCleanupInterval = 10 * time.Minute |
| ) |
|
|
| |
| var signatureCache sync.Map |
|
|
| |
| var cacheCleanupOnce sync.Once |
|
|
| |
| type groupCache struct { |
| mu sync.RWMutex |
| entries map[string]SignatureEntry |
| } |
|
|
| |
| func hashText(text string) string { |
| h := sha256.Sum256([]byte(text)) |
| return hex.EncodeToString(h[:])[:SignatureTextHashLen] |
| } |
|
|
| |
| func getOrCreateGroupCache(groupKey string) *groupCache { |
| |
| cacheCleanupOnce.Do(startCacheCleanup) |
|
|
| if val, ok := signatureCache.Load(groupKey); ok { |
| return val.(*groupCache) |
| } |
| sc := &groupCache{entries: make(map[string]SignatureEntry)} |
| actual, _ := signatureCache.LoadOrStore(groupKey, sc) |
| return actual.(*groupCache) |
| } |
|
|
| |
| |
| func startCacheCleanup() { |
| go func() { |
| ticker := time.NewTicker(CacheCleanupInterval) |
| defer ticker.Stop() |
| for range ticker.C { |
| purgeExpiredCaches() |
| } |
| }() |
| } |
|
|
| |
| func purgeExpiredCaches() { |
| now := time.Now() |
| signatureCache.Range(func(key, value any) bool { |
| sc := value.(*groupCache) |
| sc.mu.Lock() |
| |
| for k, entry := range sc.entries { |
| if now.Sub(entry.Timestamp) > SignatureCacheTTL { |
| delete(sc.entries, k) |
| } |
| } |
| isEmpty := len(sc.entries) == 0 |
| sc.mu.Unlock() |
| |
| if isEmpty { |
| signatureCache.Delete(key) |
| } |
| return true |
| }) |
| } |
|
|
| |
| |
| func CacheSignature(modelName, text, signature string) { |
| if text == "" || signature == "" { |
| return |
| } |
| if len(signature) < MinValidSignatureLen { |
| return |
| } |
|
|
| groupKey := GetModelGroup(modelName) |
| textHash := hashText(text) |
| sc := getOrCreateGroupCache(groupKey) |
| sc.mu.Lock() |
| defer sc.mu.Unlock() |
|
|
| sc.entries[textHash] = SignatureEntry{ |
| Signature: signature, |
| Timestamp: time.Now(), |
| } |
| } |
|
|
| |
| |
| func GetCachedSignature(modelName, text string) string { |
| groupKey := GetModelGroup(modelName) |
|
|
| if text == "" { |
| if groupKey == "gemini" { |
| return "skip_thought_signature_validator" |
| } |
| return "" |
| } |
| val, ok := signatureCache.Load(groupKey) |
| if !ok { |
| if groupKey == "gemini" { |
| return "skip_thought_signature_validator" |
| } |
| return "" |
| } |
| sc := val.(*groupCache) |
|
|
| textHash := hashText(text) |
|
|
| now := time.Now() |
|
|
| sc.mu.Lock() |
| entry, exists := sc.entries[textHash] |
| if !exists { |
| sc.mu.Unlock() |
| if groupKey == "gemini" { |
| return "skip_thought_signature_validator" |
| } |
| return "" |
| } |
| if now.Sub(entry.Timestamp) > SignatureCacheTTL { |
| delete(sc.entries, textHash) |
| sc.mu.Unlock() |
| if groupKey == "gemini" { |
| return "skip_thought_signature_validator" |
| } |
| return "" |
| } |
|
|
| |
| entry.Timestamp = now |
| sc.entries[textHash] = entry |
| sc.mu.Unlock() |
|
|
| return entry.Signature |
| } |
|
|
| |
| func ClearSignatureCache(modelName string) { |
| if modelName == "" { |
| signatureCache.Range(func(key, _ any) bool { |
| signatureCache.Delete(key) |
| return true |
| }) |
| return |
| } |
| groupKey := GetModelGroup(modelName) |
| signatureCache.Delete(groupKey) |
| } |
|
|
| |
| func HasValidSignature(modelName, signature string) bool { |
| return (signature != "" && len(signature) >= MinValidSignatureLen) || (signature == "skip_thought_signature_validator" && GetModelGroup(modelName) == "gemini") |
| } |
|
|
| func GetModelGroup(modelName string) string { |
| if strings.Contains(modelName, "gpt") { |
| return "gpt" |
| } else if strings.Contains(modelName, "claude") { |
| return "claude" |
| } else if strings.Contains(modelName, "gemini") { |
| return "gemini" |
| } |
| return modelName |
| } |
|
|