#![allow(dead_code)]
use std::fs;
use std::path::Path;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, Request, Respond, ResponseTemplate};
pub const STUB_DIM: usize = 1024;
pub const STUB_API_KEY: &str = "sk-or-v1-test-offline-stub";
pub const STUB_MODEL: &str = "qwen/qwen3-embedding-8b";
pub fn deterministic_vector(text: &str) -> Vec<f32> {
deterministic_vector_dim(text, STUB_DIM)
}
pub fn deterministic_vector_dim(text: &str, dim: usize) -> Vec<f32> {
let dim = dim.max(1);
let mut v = vec![0.0f32; dim];
for token in text
.split(|c: char| !c.is_alphanumeric())
.filter(|t| !t.is_empty())
{
let lower = token.to_ascii_lowercase();
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for b in lower.as_bytes() {
h ^= u64::from(*b);
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
let a = (h % dim as u64) as usize;
let b = ((h >> 32) % dim as u64) as usize;
v[a] += 1.0;
v[b] += 0.5;
}
if v.iter().all(|x| *x == 0.0) {
v[0] = 1.0;
}
let norm: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
if norm > 0.0 {
for x in &mut v {
*x /= norm;
}
}
v
}
struct EmbeddingsResponder;
impl Respond for EmbeddingsResponder {
fn respond(&self, request: &Request) -> ResponseTemplate {
let body: serde_json::Value = match serde_json::from_slice(&request.body) {
Ok(v) => v,
Err(e) => {
return ResponseTemplate::new(400).set_body_json(serde_json::json!({
"error": { "code": "invalid_request", "message": format!("stub: {e}") }
}))
}
};
let inputs: Vec<String> = match body.get("input") {
Some(serde_json::Value::String(s)) => vec![s.clone()],
Some(serde_json::Value::Array(items)) => items
.iter()
.map(|i| i.as_str().unwrap_or_default().to_string())
.collect(),
_ => {
return ResponseTemplate::new(400).set_body_json(serde_json::json!({
"error": { "code": "invalid_request", "message": "stub: missing input" }
}))
}
};
let dim = body
.get("dimensions")
.and_then(serde_json::Value::as_u64)
.map(|d| d as usize)
.unwrap_or(STUB_DIM)
.max(1);
let data: Vec<serde_json::Value> = inputs
.iter()
.enumerate()
.map(|(i, text)| {
let vec = deterministic_vector_dim(text, dim);
serde_json::json!({ "embedding": vec, "index": i })
})
.collect();
ResponseTemplate::new(200).set_body_json(serde_json::json!({
"object": "list",
"data": data,
"model": body.get("model").cloned().unwrap_or(serde_json::Value::Null),
}))
}
}
struct ChatResponder {
content: String,
}
impl Respond for ChatResponder {
fn respond(&self, _request: &Request) -> ResponseTemplate {
ResponseTemplate::new(200).set_body_json(serde_json::json!({
"id": "chatcmpl-stub",
"choices": [{
"message": { "content": self.content },
"finish_reason": "stop"
}],
"usage": { "cost": 0.0, "prompt_tokens": 1, "completion_tokens": 1 }
}))
}
}
#[must_use = "dropping the guard stops the stub server mid-test"]
pub struct OpenRouterStub {
embeddings_url: String,
chat_url: String,
_server: MockServer,
_rt: tokio::runtime::Runtime,
}
impl OpenRouterStub {
pub fn start() -> Self {
Self::start_with_chat_content(r#"{"entities":[],"relationships":[]}"#)
}
pub fn start_with_chat_content(chat_content: &str) -> Self {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.enable_all()
.build()
.expect("openrouter stub: multi-thread runtime must build");
let content = chat_content.to_string();
let server = rt.block_on(async move {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/api/v1/embeddings"))
.respond_with(EmbeddingsResponder)
.mount(&server)
.await;
Mock::given(method("POST"))
.and(path("/api/v1/chat/completions"))
.respond_with(ChatResponder { content })
.mount(&server)
.await;
server
});
let uri = server.uri();
OpenRouterStub {
embeddings_url: format!("{uri}/api/v1/embeddings"),
chat_url: format!("{uri}/api/v1/chat/completions"),
_server: server,
_rt: rt,
}
}
pub fn embeddings_url(&self) -> &str {
&self.embeddings_url
}
pub fn chat_url(&self) -> &str {
&self.chat_url
}
}
pub fn global_stub() -> &'static OpenRouterStub {
static STUB: std::sync::OnceLock<OpenRouterStub> = std::sync::OnceLock::new();
STUB.get_or_init(OpenRouterStub::start)
}
pub fn write_sandbox_config(config_dir: &Path, db: Option<&Path>) {
write_sandbox_config_inner(config_dir, db, true);
}
pub fn write_sandbox_config_without_key(config_dir: &Path, db: Option<&Path>) {
write_sandbox_config_inner(config_dir, db, false);
}
fn write_sandbox_config_inner(config_dir: &Path, db: Option<&Path>, with_key: bool) {
fs::create_dir_all(config_dir).expect("write_sandbox_config: mkdir config");
let stub = global_stub();
let file = config_dir.join("config.toml");
let mut settings: Vec<String> = Vec::new();
let mut had_db = false;
if let Ok(existing) = fs::read_to_string(&file) {
for line in existing.lines() {
let t = line.trim();
if !(t.starts_with('"') && t.contains('=')) {
continue;
}
if t.starts_with("\"network.openrouter.") {
continue; }
if t.starts_with("\"db.path\"") {
had_db = true;
if db.is_some() {
continue; }
}
settings.push(t.to_string());
}
}
let _ = had_db;
if let Some(db) = db {
let db_str = db
.display()
.to_string()
.replace('\\', "\\\\")
.replace('"', "\\\"");
settings.push(format!("\"db.path\" = \"{db_str}\""));
}
settings.push(format!(
"\"network.openrouter.embeddings_url\" = \"{}\"",
stub.embeddings_url()
));
settings.push(format!(
"\"network.openrouter.chat_url\" = \"{}\"",
stub.chat_url()
));
let keys = if with_key {
let fingerprint = blake3::hash(STUB_API_KEY.as_bytes()).to_hex().to_string();
format!(
"\n[[keys]]\nprovider = \"openrouter\"\nvalue = \"{STUB_API_KEY}\"\nadded_at = \"2026-01-01T00:00:00Z\"\nfingerprint = \"{fingerprint}\"\n"
)
} else {
String::new()
};
let cfg = format!(
"schema_version = 1\n\n[settings]\n{}\n{}",
settings.join("\n"),
keys,
);
fs::write(&file, cfg).expect("write_sandbox_config: write config.toml");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vector_is_deterministic_and_unit_length() {
let a = deterministic_vector("agent memory architecture");
let b = deterministic_vector("agent memory architecture");
assert_eq!(a, b, "same text must yield the same vector");
assert_eq!(a.len(), STUB_DIM);
let norm: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
assert!((norm - 1.0).abs() < 1e-5, "vector must be L2-normalised");
}
#[test]
fn shared_tokens_score_higher_than_disjoint_ones() {
let base = deterministic_vector("jwt authentication rotation");
let near = deterministic_vector("jwt authentication policy");
let far = deterministic_vector("kubernetes ingress controller");
let cos = |x: &[f32], y: &[f32]| -> f32 { x.iter().zip(y).map(|(a, b)| a * b).sum() };
assert!(
cos(&base, &near) > cos(&base, &far),
"overlapping text must be closer than unrelated text"
);
}
#[test]
fn empty_text_is_not_the_zero_vector() {
let v = deterministic_vector(" ");
assert!(v.iter().any(|x| *x != 0.0), "stub must never return zeros");
}
}