Spaces:
Sleeping
Sleeping
| 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 | |
| } | |
| } | |