use lru::LruCache;
use parking_lot::RwLock;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::num::NonZeroUsize;
use std::sync::LazyLock;
use crate::prompts::system::SystemPromptReport;
const MAX_SHARD_SIZE: usize = 32;
const NUM_SHARDS: usize = 16;
const SHARD_MASK: usize = NUM_SHARDS - 1;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum TaskType {
System,
Lightweight,
Specialized,
}
pub trait PromptProvider {
fn cache_key(&self) -> String;
fn task_type(&self) -> TaskType;
}
pub struct SystemPromptCache<V: Clone> {
shards: [RwLock<LruCache<String, V>>; NUM_SHARDS],
}
impl<V: Clone> Default for SystemPromptCache<V> {
fn default() -> Self {
Self::new()
}
}
impl<V: Clone> SystemPromptCache<V> {
pub fn new() -> Self {
let shard_size = NonZeroUsize::new(MAX_SHARD_SIZE).unwrap_or(NonZeroUsize::MIN);
let shard = || RwLock::new(LruCache::new(shard_size));
Self { shards: [(); NUM_SHARDS].map(|_| shard()) }
}
#[inline]
fn shard_index(key: &str) -> usize {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
(hasher.finish() as usize) & SHARD_MASK
}
pub fn get_or_insert_with<F>(&self, key: &str, builder: F) -> V
where
F: FnOnce() -> V,
{
let idx = Self::shard_index(key);
{
let shard = self.shards[idx].read();
if let Some(value) = shard.peek(key) {
return value.clone();
}
}
let value = builder();
let mut shard = self.shards[idx].write();
if let Some(value) = shard.get(key) {
return value.clone();
}
shard.put(key.to_string(), value.clone());
value
}
pub fn get(&self, key: &str) -> Option<V> {
let shard = self.shards[Self::shard_index(key)].read();
shard.peek(key).cloned()
}
pub fn insert(&self, key: String, value: V) {
let idx = Self::shard_index(&key);
let mut shard = self.shards[idx].write();
shard.put(key, value);
}
pub fn clear(&self) {
for shard in &self.shards {
shard.write().clear();
}
}
}
pub static PROMPT_CACHE: LazyLock<SystemPromptCache<(String, SystemPromptReport)>> =
LazyLock::new(SystemPromptCache::new);