| package handler |
|
|
| import ( |
| "strings" |
| "sync" |
| "time" |
| ) |
|
|
| |
| |
| |
| |
| type cacheTracker struct { |
| mu sync.Mutex |
| seen map[string]time.Time |
| ttl time.Duration |
| } |
|
|
| var globalCacheTracker = &cacheTracker{ |
| seen: make(map[string]time.Time), |
| ttl: 5 * time.Minute, |
| } |
|
|
| |
| func estTokens(text string) int { |
| text = strings.TrimSpace(text) |
| if text == "" { |
| return 0 |
| } |
| n := len(text) / 4 |
| if n < 1 { |
| n = 1 |
| } |
| return n |
| } |
|
|
| |
| func cacheFP(part string) string { |
| return part |
| } |
|
|
| |
| |
| |
| func (t *cacheTracker) record(conversationID, instructions, input string) (cacheWriteTokens, cachedTokens int) { |
| t.mu.Lock() |
| defer t.mu.Unlock() |
|
|
| t.gc() |
|
|
| type block struct { |
| fp string |
| tokens int |
| } |
| var blocks []block |
| if instructions != "" { |
| blocks = append(blocks, block{cacheFP("instructions:" + instructions), estTokens(instructions)}) |
| } |
| if input != "" { |
| blocks = append(blocks, block{cacheFP("input:" + input), estTokens(input)}) |
| } |
|
|
| for _, b := range blocks { |
| if b.tokens == 0 { |
| continue |
| } |
| if _, ok := t.seen[b.fp]; ok { |
| cachedTokens += b.tokens |
| } else { |
| cacheWriteTokens += b.tokens |
| t.seen[b.fp] = time.Now() |
| } |
| } |
| return |
| } |
|
|
| |
| func (t *cacheTracker) gc() { |
| now := time.Now() |
| for fp, seen := range t.seen { |
| if now.Sub(seen) > t.ttl { |
| delete(t.seen, fp) |
| } |
| } |
| } |
|
|
| |
| |
| |
| func RecordCache(conversationID, instructions, input string) (cacheWriteTokens, cachedTokens int) { |
| return globalCacheTracker.record(conversationID, instructions, input) |
| } |
|
|