package wiki import ( "crypto/sha256" "encoding/hex" "errors" "fmt" "os" "path/filepath" "github.com/zzet/gortex/internal/platform" ) // EnhanceCache is a tiny disk-backed cache. Keys are derived from the // stable inputs to one enhance call so re-running with the same // graph + opts yields byte-identical output without invoking the // provider. Misses fall through to the provider; hits short-circuit. // // Layout: //.txt // The file body is the enhanced markdown. type EnhanceCache struct { root string } // NewEnhanceCache constructs a cache rooted at root. The directory is // created lazily on first Set. func NewEnhanceCache(root string) *EnhanceCache { return &EnhanceCache{root: root} } // DefaultEnhanceCacheDir returns the default cache location: // ~/.gortex/cache/wiki-enhance (or $XDG_CACHE_HOME equivalent). func DefaultEnhanceCacheDir() string { return filepath.Join(platform.CacheDir(), "wiki-enhance") } // Key derives a stable cache key for one section. It includes the // prompt version so changing the prompt template busts the cache. func (c *EnhanceCache) Key(section EnhanceSection, providerName string) string { h := sha256.New() // sha256.Hash.Write never returns an error; ignore Fprintf's int+err. write := func(format string, args ...any) { fmt.Fprintf(h, format, args...) } write("v=%d\n", promptVersion) write("provider=%s\n", providerName) write("kind=%s\n", section.Kind) write("title=%s\n", section.PageTitle) for _, a := range section.AnchorSymbolIDs { write("anchor=%s\n", a) } write("ctx=%s\n", section.Context) write("body=%s\n", section.RawMarkdown) return hex.EncodeToString(h.Sum(nil)) } // Get returns the cached enhanced markdown for the key. // Returns ("", false, nil) on a clean miss, ("", false, err) on a // real I/O error. func (c *EnhanceCache) Get(key string) (string, bool, error) { if c == nil || c.root == "" { return "", false, nil } path := c.pathFor(key) body, err := os.ReadFile(path) if errors.Is(err, os.ErrNotExist) { return "", false, nil } if err != nil { return "", false, err } return string(body), true, nil } // Set writes the enhanced markdown for key. func (c *EnhanceCache) Set(key, body string) error { if c == nil || c.root == "" { return nil } path := c.pathFor(key) if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return err } return os.WriteFile(path, []byte(body), 0o644) } func (c *EnhanceCache) pathFor(key string) string { if len(key) < 2 { return filepath.Join(c.root, key+".txt") } return filepath.Join(c.root, key[:2], key+".txt") }