use std::{collections::HashMap, sync::Arc};
use nanocodex_core::{MODEL, responses::RequestProfile};
use sha2::{Digest, Sha256};
use tokio::sync::{Mutex, OnceCell};
use crate::{NanocodexError, Result};
type PrefixFingerprint = [u8; 32];
type WarmupEntry = Arc<OnceCell<()>>;
type WarmupEntries = HashMap<PrefixFingerprint, WarmupEntry>;
#[derive(Clone)]
pub(crate) struct ModelPromptCache {
key: Arc<str>,
shared: Option<SharedPromptCache>,
}
#[derive(Clone, Default)]
pub(crate) struct SharedPromptCache {
entries: Arc<Mutex<WarmupEntries>>,
}
impl ModelPromptCache {
pub(crate) fn new(key: Arc<str>, shared: Option<SharedPromptCache>) -> Self {
Self { key, shared }
}
pub(crate) fn key(&self) -> &str {
&self.key
}
pub(crate) fn shared(&self) -> Option<&SharedPromptCache> {
self.shared.as_ref()
}
}
impl SharedPromptCache {
pub(crate) async fn entry(&self, profile: &RequestProfile) -> Result<WarmupEntry> {
let fingerprint = prefix_fingerprint(profile)?;
let mut entries = self.entries.lock().await;
Ok(Arc::clone(
entries.entry(fingerprint).or_insert_with(Arc::default),
))
}
}
fn prefix_fingerprint(profile: &RequestProfile) -> Result<PrefixFingerprint> {
let encoded = serde_json::to_vec(&(MODEL, profile.prompt_cache_key(), profile.prefix()))
.map_err(NanocodexError::SerializePromptPrefix)?;
Ok(Sha256::digest(encoded).into())
}