use std::path::Path;
pub struct Dist {
pub p50: f64,
pub p95: f64,
pub max: f64,
}
impl Dist {
fn from_json(v: &serde_json::Value) -> Option<Self> {
Some(Self {
p50: v.get("p50")?.as_f64()?,
p95: v.get("p95")?.as_f64()?,
max: v.get("max")?.as_f64()?,
})
}
}
pub struct AgentStats {
pub in_tokens: Dist,
#[allow(dead_code)]
pub blocks: Dist,
}
impl AgentStats {
pub fn load(path: &Path) -> Result<Self, String> {
let text = std::fs::read_to_string(path).map_err(|e| e.to_string())?;
let v: serde_json::Value = serde_json::from_str(&text).map_err(|e| e.to_string())?;
let in_b = v
.get("in_tokens")
.and_then(Dist::from_json)
.ok_or("stats: 缺 in_tokens")?;
let bl = v
.get("blocks_per_request")
.and_then(Dist::from_json)
.ok_or("stats: 缺 blocks_per_request")?;
Ok(Self {
in_tokens: in_b,
blocks: bl,
})
}
fn draw(d: &Dist, r: f64, jitter: f64) -> u32 {
let (lo, hi) = if r < 0.5 {
(d.p50, d.p95)
} else {
(d.p95, d.max)
};
let t = if r < 0.5 { r / 0.5 } else { (r - 0.5) / 0.5 };
let v = lo + (hi - lo) * t;
let v = v * (1.0 + jitter * (r - 0.5));
v.max(16.0) as u32
}
}
pub const VOCAB: u32 = 248_320;
pub struct Rng(pub u64);
impl Rng {
pub fn next(&mut self) -> u64 {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.0 = x;
x
}
pub fn frac(&mut self) -> f64 {
(self.next() >> 11) as f64 / (1u64 << 53) as f64
}
}
pub enum Mode {
Uniform,
Agent(std::path::PathBuf, f64),
}
pub fn gen_tokens(
mode: &Mode,
stats: Option<&AgentStats>,
n_reqs: usize,
cap_token: u32,
seed: u64,
) -> Result<Vec<u32>, String> {
let mut rng = Rng(seed);
Ok(match mode {
Mode::Uniform => {
let n = n_reqs * 24; (0..n).map(|_| (rng.next() % VOCAB as u64) as u32).collect()
}
Mode::Agent(path, hot_hit) => {
let st = match stats {
Some(s) => s,
None => &AgentStats::load(path)?,
};
let mut out = Vec::new();
let mut hot: Vec<u32> = Vec::with_capacity(1 << 16);
for _ in 0..n_reqs {
let n_tok = AgentStats::draw(&st.in_tokens, rng.frac(), 0.10).min(cap_token);
for i in 0..n_tok {
if !hot.is_empty() && rng.frac() < *hot_hit {
let j = (rng.next() % hot.len().min(4096) as u64) as usize;
out.push(hot[hot.len() - 1 - j]);
} else {
let t = (rng.next() % VOCAB as u64) as u32;
hot.push(t);
out.push(t);
}
let _ = i;
}
}
out
}
})
}