Spaces:
Sleeping
Sleeping
File size: 2,344 Bytes
da590a7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | package tools
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"sync"
"time"
)
// CacheEntry represents a cached tool result
type CacheEntry struct {
Result *ToolResult
Timestamp time.Time
TTL time.Duration
}
// ToolCache provides a simple in-memory cache for tool results
type ToolCache struct {
entries sync.Map
ttl time.Duration
}
// NewToolCache creates a new tool cache with default TTL
func NewToolCache(defaultTTL time.Duration) *ToolCache {
c := &ToolCache{
ttl: defaultTTL,
}
// Start cleanup goroutine
go c.cleanupLoop()
return c
}
// Get retrieves a cached result if available and not expired
func (c *ToolCache) Get(key string) (*ToolResult, bool) {
val, ok := c.entries.Load(key)
if !ok {
return nil, false
}
entry := val.(CacheEntry)
if time.Since(entry.Timestamp) > entry.TTL {
c.entries.Delete(key)
return nil, false
}
return entry.Result, true
}
// Set stores a tool result in the cache
func (c *ToolCache) Set(key string, result *ToolResult, ttl time.Duration) {
if ttl == 0 {
ttl = c.ttl
}
c.entries.Store(key, CacheEntry{
Result: result,
Timestamp: time.Now(),
TTL: ttl,
})
}
// GenerateKey generates a unique cache key for a tool execution
func (c *ToolCache) GenerateKey(toolName string, args map[string]interface{}) string {
// Sort keys to ensure consistent hashing?
// JSON marshaling of maps in Go is sorted by key order since Go 1.
// But let's rely on json.Marshal for simplicity, assuming consistent output.
argsJSON, err := json.Marshal(args)
if err != nil {
// Fallback for unmarshalable args (should happen rarely for tool args)
return fmt.Sprintf("%s:%v", toolName, args)
}
hash := sha256.Sum256(argsJSON)
return fmt.Sprintf("%s:%s", toolName, hex.EncodeToString(hash[:]))
}
func (c *ToolCache) cleanupLoop() {
ticker := time.NewTicker(5 * time.Minute)
for range ticker.C {
c.entries.Range(func(key, value interface{}) bool {
entry := value.(CacheEntry)
if time.Since(entry.Timestamp) > entry.TTL {
c.entries.Delete(key)
}
return true
})
}
}
// IsCacheable checks if a tool is suitable for caching based on its name
func IsCacheable(toolName string) bool {
switch toolName {
case "read_file", "list_dir", "web_fetch", "web_search":
return true
default:
return false
}
}
|