use std::sync::{Arc, Mutex};
use axiom_engine::config::AxiomConfig;
use axiom_engine::context_compressor::CompressorConfig;
use axiom_engine::inference::InferencePipeline;
use axiom_engine::openai_forwarder::OpenAiForwarder;
use axiom_engine::server::{create_router, AppState};
use axum::body::Body;
use axum::extract::State;
use axum::http::{header, Request, StatusCode};
use axum::routing::post;
use axum::{Json, Router};
use candle_core::Device;
use serde_json::{json, Value};
use tower::ServiceExt;
fn pipeline() -> InferencePipeline {
InferencePipeline::new(
AxiomConfig {
d_model: 16,
n_layers: 1,
vocab_size: 64,
lr_inner: 1e-3,
norm_eps: 1e-6,
},
Device::Cpu,
)
.unwrap()
}
#[tokio::test]
async fn compressed_bad_request_retries_original_and_preserves_structural_items() {
std::env::set_var("AXIOM_RESPONSES_COMPRESS", "1");
let captured = Arc::new(Mutex::new(Vec::<Value>::new()));
async fn upstream(
State(captured): State<Arc<Mutex<Vec<Value>>>>,
Json(body): Json<Value>,
) -> (StatusCode, Json<Value>) {
let mut requests = captured.lock().unwrap();
requests.push(body);
if requests.len() == 1 {
(
StatusCode::BAD_REQUEST,
Json(json!({"error":"reject fingerprint"})),
)
} else {
(
StatusCode::OK,
Json(json!({"id":"resp_ok","object":"response"})),
)
}
}
let upstream_app = Router::new()
.route("/v1/responses", post(upstream))
.with_state(captured.clone());
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let base = format!("http://{}", listener.local_addr().unwrap());
let task = tokio::spawn(async move { axum::serve(listener, upstream_app).await.unwrap() });
let test_pipeline = tokio::task::spawn_blocking(pipeline).await.unwrap();
let state = AppState::new(test_pipeline, "test".into())
.with_openai_forwarder(Some(OpenAiForwarder::new(None, Some(base))))
.with_compressor_config(CompressorConfig {
enabled: true,
heavy_message_threshold_tokens: 1,
recall_top_k: 8,
});
let app = create_router(state);
let reasoning = json!({"type":"reasoning","id":"r1","encrypted_content":"opaque","summary":[]});
let heavy_history =
"historical assistant answer with useful context about the build system ".repeat(80);
let original = json!({"model":"gpt-5.5","input":[
{"role":"assistant","content":heavy_history},
reasoning,
{"type":"function_call_output","call_id":"c1","output":"unchanged"},
{"role":"user","content":"continue"}
]});
let response = app
.oneshot(
Request::post("/v1/responses")
.header(header::CONTENT_TYPE, "application/json")
.header(header::AUTHORIZATION, "Bearer test")
.body(Body::from(original.to_string()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let requests = captured.lock().unwrap();
assert_eq!(requests.len(), 2);
assert!(requests[0]["input"][0]["content"]
.as_str()
.unwrap()
.contains("<axiom_context_fingerprint"));
assert_eq!(requests[0]["input"][1], original["input"][1]);
assert_eq!(requests[0]["input"][2], original["input"][2]);
assert_eq!(requests[0]["input"][3], original["input"][3]);
assert_eq!(requests[1], original);
task.abort();
std::env::remove_var("AXIOM_RESPONSES_COMPRESS");
}
#[tokio::test]
async fn compressed_responses_preserves_order_across_multiple_runs() {
let captured = Arc::new(Mutex::new(Vec::<Value>::new()));
async fn upstream(
State(captured): State<Arc<Mutex<Vec<Value>>>>,
Json(body): Json<Value>,
) -> (StatusCode, Json<Value>) {
captured.lock().unwrap().push(body);
(
StatusCode::OK,
Json(json!({"id":"resp_multi","object":"response"})),
)
}
let upstream_app = Router::new()
.route("/v1/responses", post(upstream))
.with_state(captured.clone());
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let base = format!("http://{}", listener.local_addr().unwrap());
let task = tokio::spawn(async move { axum::serve(listener, upstream_app).await.unwrap() });
let test_pipeline = tokio::task::spawn_blocking(pipeline).await.unwrap();
let state = AppState::new(test_pipeline, "test".into())
.with_openai_forwarder(Some(OpenAiForwarder::new(None, Some(base))))
.with_compressor_config(CompressorConfig {
enabled: true,
heavy_message_threshold_tokens: 1,
recall_top_k: 8,
});
let app = create_router(state);
let first_history = "first historical assistant answer ".repeat(120);
let second_history = "second historical assistant answer ".repeat(120);
let original = json!({"model":"gpt-5.5","input":[
{"role":"assistant","content":first_history},
{"role":"user","content":"bridge question"},
{"role":"assistant","content":second_history},
{"role":"user","content":"continue"}
]});
let response = app
.oneshot(
Request::post("/v1/responses")
.header(header::CONTENT_TYPE, "application/json")
.header(header::AUTHORIZATION, "Bearer test")
.body(Body::from(original.to_string()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let requests = captured.lock().unwrap();
assert_eq!(requests.len(), 1);
let input = requests[0]["input"].as_array().unwrap();
assert_eq!(input.len(), 4);
assert!(input[0]["content"]
.as_str()
.unwrap()
.contains("<axiom_context_fingerprint"));
assert_eq!(input[1], original["input"][1]);
assert!(input[2]["content"]
.as_str()
.unwrap()
.contains("<axiom_context_fingerprint"));
assert_eq!(input[3], original["input"][3]);
task.abort();
}
#[tokio::test]
async fn recording_persists_scrubbed_exchange_when_enabled() {
let rec_dir = std::env::temp_dir().join(format!(
"axiom_recproxy_{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::env::set_var("AXIOM_SESSION_RECORD", "1");
std::env::set_var("AXIOM_SESSIONS_DIR", &rec_dir);
async fn upstream(Json(_body): Json<Value>) -> (StatusCode, Json<Value>) {
(StatusCode::OK, Json(json!({"id":"resp_rec","object":"response"})))
}
let upstream_app = Router::new().route("/v1/responses", post(upstream));
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let base = format!("http://{}", listener.local_addr().unwrap());
let task = tokio::spawn(async move { axum::serve(listener, upstream_app).await.unwrap() });
let test_pipeline = tokio::task::spawn_blocking(pipeline).await.unwrap();
let state = AppState::new(test_pipeline, "test".into())
.with_openai_forwarder(Some(OpenAiForwarder::new(None, Some(base))));
let app = create_router(state);
let response = app
.oneshot(
Request::post("/v1/responses")
.header(header::CONTENT_TYPE, "application/json")
.header(header::AUTHORIZATION, "Bearer sk-secret-abcdefghijklmnop")
.header("x-axiom-session-id", "rectest")
.body(Body::from(
json!({"model":"gpt-5.5","input":"hello"}).to_string(),
))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let file = rec_dir.join("rectest.jsonl");
let mut records = Vec::new();
for _ in 0..50 {
if file.exists() {
records = axiom_engine::session_recorder::read_session(&file).unwrap();
if !records.is_empty() {
break;
}
}
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
}
std::env::remove_var("AXIOM_SESSION_RECORD");
std::env::remove_var("AXIOM_SESSIONS_DIR");
let _ = std::fs::remove_dir_all(&rec_dir);
task.abort();
assert_eq!(records.len(), 1, "one exchange must be recorded");
assert_eq!(records[0].endpoint, "/v1/responses");
assert_eq!(records[0].session_id, "rectest");
assert_eq!(records[0].response["streamed"], true);
assert_eq!(records[0].response["status"], 200);
}