embedding_cache.hpp 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #pragma once
  2. #include <cstdint>
  3. #include <functional>
  4. #include <list>
  5. #include <mutex>
  6. #include <optional>
  7. #include <string>
  8. #include <unordered_map>
  9. #include <vector>
  10. namespace svapi {
  11. /// Thread-safe, process-singleton LRU cache mapping an embedding request to its float vector.
  12. /// Evicts by both entry count (capacity) and total vector-data bytes (maxBytes).
  13. /// Supports optional TTL-based expiry (lazy) and text normalisation (trim + lower-case).
  14. class EmbeddingCache {
  15. public:
  16. struct Key {
  17. std::string apiBase;
  18. std::string model;
  19. std::string text;
  20. };
  21. /// capacity — max live entries.
  22. /// maxBytes — max sum of vec.size()*sizeof(float) across all entries.
  23. /// ttlSec — 0 means no time-based expiry.
  24. /// normalize — when true, text is trimmed and lower-cased before keying.
  25. explicit EmbeddingCache(size_t capacity = 4096,
  26. size_t maxBytes = 268435456 /*256 MiB*/,
  27. uint32_t ttlSec = 86400,
  28. bool normalize = false);
  29. /// Returns the cached vector (refreshing its LRU position) or nullopt on
  30. /// miss / expiry. Increments hits or misses accordingly.
  31. std::optional<std::vector<float>> get(const Key&);
  32. /// Insert or replace an entry then immediately evict to satisfy both bounds.
  33. void put(const Key&, std::vector<float>);
  34. void clear();
  35. /// Re-apply all four parameters live; evicts excess entries immediately.
  36. void reconfigure(size_t capacity, size_t maxBytes, uint32_t ttlSec, bool normalize);
  37. struct Stats {
  38. size_t hits = 0;
  39. size_t misses = 0;
  40. size_t size = 0;
  41. size_t capacity = 0;
  42. size_t bytes = 0;
  43. };
  44. Stats stats() const;
  45. // ---- test seam: replace the wall-clock source (NOT for production use) --
  46. void setClockForTesting(std::function<int64_t()> fn);
  47. private:
  48. // Normalise text per the normalize_ flag.
  49. std::string normalizeText(const std::string& t) const;
  50. // Build the composite string key used in the hash map.
  51. std::string makeMapKey(const Key& k) const;
  52. struct Entry {
  53. std::vector<float> vec;
  54. int64_t insertedAt; // epoch seconds from now_()
  55. };
  56. // LRU list: front = most-recently used, back = least-recently used.
  57. using LruList = std::list<std::pair<std::string, Entry>>;
  58. using LruIt = LruList::iterator;
  59. using IndexMap = std::unordered_map<std::string, LruIt>;
  60. void evict(); // evict from back until both bounds satisfied (caller holds mutex_)
  61. mutable std::mutex mutex_;
  62. LruList lru_;
  63. IndexMap index_;
  64. size_t capacity_;
  65. size_t maxBytes_;
  66. uint32_t ttlSec_;
  67. bool normalize_;
  68. size_t bytes_ = 0;
  69. size_t hits_ = 0;
  70. size_t misses_ = 0;
  71. std::function<int64_t()> now_;
  72. };
  73. /// Process-wide singleton (function-local static, constructed with defaults).
  74. EmbeddingCache& embeddingCache();
  75. } // namespace svapi