use std::time::Duration;
use reqwest::Client;
use serde::Serialize;
use serde_json::{json, Value};
use crate::context_compressor::MemoryFingerprint;
const ANTHROPIC_VERSION: &str = "2023-06-01";
const DEFAULT_BASE_URL: &str = "https://api.anthropic.com";
#[derive(Clone, Debug, Default)]
pub struct ClientAuth {
pub authorization: Option<String>,
pub x_api_key: Option<String>,
pub anthropic_version: Option<String>,
pub anthropic_beta: Option<String>,
}
#[derive(Clone)]
pub struct AnthropicForwarder {
api_key: Option<String>,
base_url: String,
client: Client,
streaming_client: Client,
}
impl AnthropicForwarder {
pub fn new(api_key: Option<String>, base_url: Option<String>) -> Self {
let client = Client::builder()
.timeout(Duration::from_secs(120))
.build()
.expect("reqwest async client should construct");
let streaming_client = Client::builder()
.build()
.expect("reqwest streaming client should construct");
Self {
api_key,
base_url: base_url.unwrap_or_else(|| DEFAULT_BASE_URL.to_string()),
client,
streaming_client,
}
}
pub fn from_env() -> Option<Self> {
let api_key = std::env::var("ANTHROPIC_API_KEY")
.ok()
.filter(|k| !k.trim().is_empty());
let base_url = std::env::var("ANTHROPIC_BASE_URL").ok();
Some(Self::new(api_key, base_url))
}
pub fn has_own_key(&self) -> bool {
self.api_key.is_some()
}
pub async fn forward_messages_json(
&self,
payload: &Value,
auth: &ClientAuth,
) -> Result<Value, ForwarderError> {
let response = self
.build_request(&self.client, auth)?
.json(payload)
.send()
.await
.map_err(|e| ForwarderError::Network(e.to_string()))?;
let status = response.status();
let body = response
.text()
.await
.map_err(|e| ForwarderError::Network(format!("body read failed: {e}")))?;
if !status.is_success() {
return Err(ForwarderError::Upstream {
status: status.as_u16(),
body,
});
}
serde_json::from_str(&body).map_err(|e| ForwarderError::Decode(e.to_string()))
}
fn build_request(
&self,
client: &Client,
auth: &ClientAuth,
) -> Result<reqwest::RequestBuilder, ForwarderError> {
let url = format!("{}/v1/messages", self.base_url.trim_end_matches('/'));
let mut request = client
.post(&url)
.header("content-type", "application/json")
.header(
"anthropic-version",
auth.anthropic_version
.as_deref()
.unwrap_or(ANTHROPIC_VERSION),
);
if let Some(beta) = auth.anthropic_beta.as_deref() {
request = request.header("anthropic-beta", beta);
}
if let Some(authz) = auth.authorization.as_deref() {
request = request.header("authorization", authz);
} else if let Some(key) = auth.x_api_key.as_deref() {
request = request.header("x-api-key", key);
} else if let Some(key) = self.api_key.as_deref() {
request = request.header("x-api-key", key);
} else {
return Err(ForwarderError::MissingAuth);
}
Ok(request)
}
pub async fn forward_messages_stream(
&self,
payload: &Value,
auth: &ClientAuth,
) -> Result<reqwest::Response, ForwarderError> {
self.build_request(&self.streaming_client, auth)?
.json(payload)
.send()
.await
.map_err(|e| ForwarderError::Network(e.to_string()))
}
}
#[derive(Debug)]
pub enum ForwarderError {
Network(String),
Decode(String),
Upstream {
status: u16,
body: String,
},
MissingAuth,
}
impl std::fmt::Display for ForwarderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ForwarderError::Network(m) => write!(f, "network error: {m}"),
ForwarderError::Decode(m) => write!(f, "decode error: {m}"),
ForwarderError::Upstream { status, body } => {
write!(f, "upstream {status}: {body}")
}
ForwarderError::MissingAuth => write!(
f,
"no upstream credential: client sent no Authorization/x-api-key \
and the proxy has no ANTHROPIC_API_KEY configured"
),
}
}
}
impl std::error::Error for ForwarderError {}
#[derive(Debug, Clone, Serialize)]
pub struct ExtractedContent {
pub role: String,
pub text: String,
pub token_count: usize,
}
#[derive(Debug, Clone)]
pub struct PartitionedMessages {
pub heavy_context: Vec<ExtractedContent>,
pub surviving: Vec<Value>,
pub target_user_index: Option<usize>,
}
pub fn partition_messages(
raw_messages: &[Value],
threshold_tokens: usize,
token_counter: impl Fn(&str) -> usize,
) -> PartitionedMessages {
let mut heavy_context: Vec<ExtractedContent> = Vec::new();
let mut surviving: Vec<Value> = Vec::with_capacity(raw_messages.len());
let mut target_user_index: Option<usize> = None;
for raw in raw_messages {
let role = raw
.get("role")
.and_then(|v| v.as_str())
.unwrap_or("user")
.to_string();
let content_value = raw.get("content").cloned().unwrap_or(Value::Null);
let (kept_content, mut extracted) =
split_content(&role, &content_value, threshold_tokens, &token_counter);
heavy_context.append(&mut extracted);
let kept_empty = content_is_empty(&kept_content);
if !kept_empty {
let mut new_msg = raw.clone();
new_msg["content"] = kept_content;
if role == "user" {
target_user_index = Some(surviving.len());
}
surviving.push(new_msg);
}
}
PartitionedMessages {
heavy_context,
surviving,
target_user_index,
}
}
fn split_system_reminders(text: &str) -> (String, Vec<String>) {
const OPEN: &str = "<system-reminder>";
const CLOSE: &str = "</system-reminder>";
if !text.contains(OPEN) {
return (text.to_string(), Vec::new());
}
let mut remainder = String::with_capacity(text.len());
let mut reminders = Vec::new();
let mut rest = text;
while let Some(start) = rest.find(OPEN) {
remainder.push_str(&rest[..start]);
let after_open = &rest[start..];
match after_open.find(CLOSE) {
Some(end_rel) => {
let end = end_rel + CLOSE.len();
reminders.push(after_open[..end].to_string());
rest = &after_open[end..];
}
None => {
reminders.push(after_open.to_string());
rest = "";
}
}
}
remainder.push_str(rest);
(remainder, reminders)
}
fn split_content(
role: &str,
content: &Value,
threshold_tokens: usize,
token_counter: &impl Fn(&str) -> usize,
) -> (Value, Vec<ExtractedContent>) {
match content {
Value::String(text) => {
let (remainder, reminders) = split_system_reminders(text);
if reminders.is_empty() {
let count = token_counter(text);
if count >= threshold_tokens {
(
Value::String(String::new()),
vec![ExtractedContent {
role: role.to_string(),
text: text.clone(),
token_count: count,
}],
)
} else {
(Value::String(text.clone()), Vec::new())
}
} else {
let count = token_counter(&remainder);
if count >= threshold_tokens {
(
Value::String(reminders.join("\n\n")),
vec![ExtractedContent {
role: role.to_string(),
text: remainder,
token_count: count,
}],
)
} else {
(Value::String(text.clone()), Vec::new())
}
}
}
Value::Array(blocks) => {
let mut kept: Vec<Value> = Vec::with_capacity(blocks.len());
let mut extracted: Vec<ExtractedContent> = Vec::new();
for block in blocks {
let block_type = block.get("type").and_then(|v| v.as_str()).unwrap_or("");
let text_opt = block
.get("text")
.and_then(|v| v.as_str())
.map(str::to_string);
if block_type == "text" {
if let Some(text) = text_opt {
let (remainder, reminders) = split_system_reminders(&text);
let count = token_counter(&remainder);
if count >= threshold_tokens {
extracted.push(ExtractedContent {
role: role.to_string(),
text: remainder,
token_count: count,
});
if !reminders.is_empty() {
let mut kept_block = block.clone();
kept_block["text"] = Value::String(reminders.join("\n\n"));
kept.push(kept_block);
}
continue;
}
}
}
kept.push(block.clone());
}
(Value::Array(kept), extracted)
}
other => (other.clone(), Vec::new()),
}
}
fn content_is_empty(v: &Value) -> bool {
match v {
Value::String(s) => s.trim().is_empty(),
Value::Array(a) => a.is_empty(),
Value::Null => true,
_ => false,
}
}
pub fn build_compressed_payload(
original: &Value,
fingerprint: &MemoryFingerprint,
partitioned: &PartitionedMessages,
) -> Value {
let mut payload = original.clone();
let heavy_text = partitioned
.heavy_context
.iter()
.map(|c| c.text.as_str())
.collect::<Vec<_>>()
.join("\n\n");
let original_tokens: usize = partitioned
.heavy_context
.iter()
.map(|c| c.token_count)
.sum();
let fingerprint_block = if heavy_text.trim().is_empty() {
opaque_fingerprint_block(fingerprint)
} else {
let digest = crate::skeleton::build_digest(
&heavy_text,
&fingerprint.session_id,
original_tokens,
fingerprint.recall_norm,
&fingerprint.state_hash,
3, );
if digest.contains("kind=\"structural-skeleton\"") {
structural_fingerprint_block(fingerprint, &digest)
} else {
opaque_fingerprint_block(fingerprint)
}
};
let mut messages = partitioned.surviving.clone();
match partitioned.target_user_index {
Some(idx) => {
if let Some(msg) = messages.get_mut(idx) {
prepend_to_user_content(msg, &fingerprint_block);
}
}
None => {
messages.push(json!({
"role": "user",
"content": fingerprint_block,
}));
}
}
payload["messages"] = Value::Array(messages);
payload
}
fn opaque_fingerprint_block(fingerprint: &MemoryFingerprint) -> String {
format!(
"<axiom_context_fingerprint session_id=\"{session}\" tokens_compressed=\"{tokens}\" \
schema=\"{schema}\" mode=\"absorbed\">\n\
state_hash={hash}\n\
raw_context=elided\n\
note=Heavy context was absorbed locally via online TTT; it had no extractable \
code structure, so no lossy neural pointer is forwarded (it would be noise to \
this model). Answer from the surrounding turns; the original text is retained \
server-side for this session.\n\
</axiom_context_fingerprint>",
session = fingerprint.session_id,
tokens = fingerprint.context_tokens_processed,
schema = fingerprint.schema,
hash = fingerprint.state_hash,
)
}
fn structural_fingerprint_block(fingerprint: &MemoryFingerprint, digest: &str) -> String {
format!(
"<axiom_context_fingerprint session_id=\"{session}\" tokens_compressed=\"{tokens}\" \
schema=\"{schema}\" mode=\"structural-digest\">\n\
state_hash={hash}\n\
{digest}\n\
note=Structural skeleton of locally-absorbed context: declaration signatures \
kept, bodies elided. Request any dropped body with POST /v1/expand \
{{\"session_id\",\"symbol\"}}.\n\
</axiom_context_fingerprint>",
session = fingerprint.session_id,
tokens = fingerprint.context_tokens_processed,
schema = fingerprint.schema,
hash = fingerprint.state_hash,
)
}
pub fn prepend_block_to_last_user_turn(payload: &mut Value, block: &str) {
let Some(messages) = payload.get_mut("messages").and_then(Value::as_array_mut) else {
return;
};
if let Some(msg) = messages
.iter_mut()
.rev()
.find(|m| m.get("role").and_then(Value::as_str) == Some("user"))
{
prepend_to_user_content(msg, block);
}
}
fn prepend_to_user_content(msg: &mut Value, prepend_text: &str) {
let content = msg.get("content").cloned().unwrap_or(Value::Null);
let new_content = match content {
Value::String(existing) => Value::String(format!("{prepend_text}\n\n{existing}")),
Value::Array(mut blocks) => {
let prepend_block = json!({"type": "text", "text": format!("{prepend_text}\n\n")});
blocks.insert(0, prepend_block);
Value::Array(blocks)
}
Value::Null => Value::String(prepend_text.to_string()),
other => Value::String(format!("{prepend_text}\n\n{other}")),
};
msg["content"] = new_content;
}
pub fn whitespace_token_count(text: &str) -> usize {
text.split_whitespace().count()
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn ws(text: &str) -> usize {
whitespace_token_count(text)
}
#[test]
fn partition_extracts_heavy_string_content() {
let messages = vec![
json!({"role": "user", "content": "short ping"}),
json!({"role": "user", "content": (0..400).map(|i| format!("tok{i}")).collect::<Vec<_>>().join(" ")}),
];
let part = partition_messages(&messages, 100, ws);
assert_eq!(part.heavy_context.len(), 1);
assert_eq!(part.heavy_context[0].role, "user");
assert!(part.heavy_context[0].token_count >= 100);
assert_eq!(part.surviving.len(), 1);
assert_eq!(part.surviving[0]["content"], "short ping");
assert_eq!(part.target_user_index, Some(0));
}
#[test]
fn partition_handles_block_content() {
let big_text = (0..300)
.map(|i| format!("w{i}"))
.collect::<Vec<_>>()
.join(" ");
let messages = vec![json!({
"role": "user",
"content": [
{"type": "text", "text": "explain this codebase"},
{"type": "text", "text": big_text.clone()},
]
})];
let part = partition_messages(&messages, 100, ws);
assert_eq!(part.heavy_context.len(), 1);
assert_eq!(part.heavy_context[0].text, big_text);
assert_eq!(part.surviving.len(), 1);
let blocks = part.surviving[0]["content"].as_array().unwrap();
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0]["text"], "explain this codebase");
}
#[test]
fn partition_excludes_system_reminder_from_heavy_extraction() {
let reminder_body = (0..300)
.map(|i| format!("envfact{i}"))
.collect::<Vec<_>>()
.join(" ");
let text = format!(
"<system-reminder>{reminder_body}</system-reminder>\nwhat does this repo do?"
);
let messages = vec![json!({"role": "user", "content": text.clone()})];
let part = partition_messages(&messages, 100, ws);
assert!(
part.heavy_context.is_empty(),
"system-reminder content must never be extracted as heavy context"
);
assert_eq!(part.surviving.len(), 1);
assert_eq!(part.surviving[0]["content"], text);
}
#[test]
fn partition_still_extracts_genuine_heavy_text_next_to_a_reminder() {
let reminder = "<system-reminder>short boilerplate</system-reminder>";
let big_paste = (0..300)
.map(|i| format!("logline{i}"))
.collect::<Vec<_>>()
.join(" ");
let text = format!("{reminder}\n{big_paste}");
let messages = vec![json!({"role": "user", "content": text})];
let part = partition_messages(&messages, 100, ws);
assert_eq!(part.heavy_context.len(), 1);
assert_eq!(part.heavy_context[0].text.trim(), big_paste);
assert_eq!(part.surviving[0]["content"], reminder);
}
#[test]
fn build_compressed_payload_prepends_fingerprint_to_string_content() {
let original = json!({
"model": "claude-opus-4-7",
"max_tokens": 64,
"messages": [{"role": "user", "content": "summarise the diff please"}],
});
let partitioned = partition_messages(
original["messages"].as_array().unwrap(),
10_000, ws,
);
let fp = MemoryFingerprint {
schema: "axiom-ttt-context-fingerprint/v2".into(),
session_id: "sess-z".into(),
context_tokens_processed: 0,
n_layers: 1,
d_model: 4,
state_hash: "sha256:0000".into(),
layer_frobenius_norms: vec![0.0],
recall_norm: 0.0,
recall_l1: 0.0,
recall_top_k_indices: vec![],
recall_top_k_decoded: "".into(),
elapsed_ms: 0,
};
let payload = build_compressed_payload(&original, &fp, &partitioned);
let messages = payload["messages"].as_array().unwrap();
assert_eq!(messages.len(), 1);
let content = messages[0]["content"].as_str().unwrap();
assert!(content.starts_with("<axiom_context_fingerprint "));
assert!(content.contains("</axiom_context_fingerprint>"));
assert!(content.contains("summarise the diff please"));
}
#[test]
fn build_compressed_payload_appends_user_turn_when_only_heavy_messages() {
let big = (0..400)
.map(|i| format!("tok{i}"))
.collect::<Vec<_>>()
.join(" ");
let original = json!({
"model": "claude-opus-4-7",
"max_tokens": 64,
"messages": [{"role": "user", "content": big.clone()}],
});
let partitioned = partition_messages(original["messages"].as_array().unwrap(), 50, ws);
assert!(partitioned.surviving.is_empty());
let fp = MemoryFingerprint {
schema: "axiom-ttt-context-fingerprint/v2".into(),
session_id: "sess-q".into(),
context_tokens_processed: 400,
n_layers: 1,
d_model: 4,
state_hash: "sha256:abcd".into(),
layer_frobenius_norms: vec![1.0],
recall_norm: 1.0,
recall_l1: 1.0,
recall_top_k_indices: vec![],
recall_top_k_decoded: "".into(),
elapsed_ms: 1,
};
let payload = build_compressed_payload(&original, &fp, &partitioned);
let messages = payload["messages"].as_array().unwrap();
assert_eq!(messages.len(), 1);
let content = messages[0]["content"].as_str().unwrap();
assert!(content.starts_with("<axiom_context_fingerprint "));
assert!(content.contains("tokens_compressed=\"400\""));
assert!(content.contains("state_hash=sha256:abcd"));
assert!(content.contains("raw_context=elided"));
assert!(!content.contains("tok399"));
assert!(content.len() < big.len()); assert!(!content.contains("recall_top_k_indices"));
assert!(!content.contains("layer_frobenius_norms"));
assert!(!content.contains("associative_recall_l1"));
}
#[test]
fn build_compressed_payload_wraps_structural_digest_for_code() {
let code = r#"
use std::collections::HashMap;
pub fn run() -> usize {
let mut values = HashMap::new();
values.insert("secret_body", 1usize);
values.len()
}
"#;
let original = json!({
"model": "claude-opus-4-7",
"max_tokens": 64,
"messages": [{"role": "user", "content": code}],
});
let partitioned = partition_messages(original["messages"].as_array().unwrap(), 10, ws);
assert!(partitioned.surviving.is_empty());
let fp = MemoryFingerprint {
schema: "axiom-ttt-context-fingerprint/v2".into(),
session_id: "sess-code".into(),
context_tokens_processed: 32,
n_layers: 1,
d_model: 4,
state_hash: "sha256:c0de".into(),
layer_frobenius_norms: vec![1.0],
recall_norm: 1.0,
recall_l1: 1.0,
recall_top_k_indices: vec![1, 2],
recall_top_k_decoded: "".into(),
elapsed_ms: 1,
};
let payload = build_compressed_payload(&original, &fp, &partitioned);
let content = payload["messages"][0]["content"].as_str().unwrap();
assert!(content.starts_with("<axiom_context_fingerprint "));
assert!(content.contains("mode=\"structural-digest\""));
assert!(content.contains("<axiom_context_digest "));
assert!(content.contains("fn run"));
assert!(content.contains("usize"));
assert!(!content.contains("secret_body"));
assert!(!content.contains("recall_top_k_indices"));
}
}