Skip to main content

xpile_llm/
lib.rs

1//! LLM invocation + content-addressed cache.
2//!
3//! Cache key is `sha256(source_bytes || xpile_version || model_id || skills_hash)`.
4//! On cache hit, returns the exact bytes stored — byte-identical across
5//! runs and machines. This is what makes the stochastic agent loop
6//! reproducible at the artifact level.
7//!
8//! See `contracts/xpile-determinism-v1.yaml` (TODO).
9
10use sha2::{Digest, Sha256};
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub struct CacheKey(pub [u8; 32]);
14
15impl CacheKey {
16    pub fn compute(source: &[u8], xpile_version: &str, model_id: &str, skills_hash: &[u8]) -> Self {
17        let mut hasher = Sha256::new();
18        hasher.update(source);
19        hasher.update(b"\x00");
20        hasher.update(xpile_version.as_bytes());
21        hasher.update(b"\x00");
22        hasher.update(model_id.as_bytes());
23        hasher.update(b"\x00");
24        hasher.update(skills_hash);
25        let result = hasher.finalize();
26        let mut bytes = [0u8; 32];
27        bytes.copy_from_slice(&result);
28        Self(bytes)
29    }
30
31    pub fn hex(&self) -> String {
32        hex::encode(self.0)
33    }
34}
35
36#[derive(Debug, thiserror::Error)]
37pub enum LlmError {
38    #[error("model invocation failed: {0}")]
39    Invocation(String),
40    #[error("cache error: {0}")]
41    Cache(String),
42}