shift_proxy/routes/
anthropic.rs1use crate::body::extract_body;
11use crate::forward::forward_request;
12use crate::optimize::optimize_payload;
13use crate::ProxyState;
14use axum::body::Bytes;
15use axum::extract::State;
16use axum::http::{HeaderMap, StatusCode, Uri};
17use axum::response::{IntoResponse, Response};
18
19pub async fn anthropic_handler(
21 State(state): State<ProxyState>,
22 uri: Uri,
23 headers: HeaderMap,
24 body: Bytes,
25) -> Response {
26 let body = match extract_body(&headers, body) {
27 Ok(s) => s,
28 Err(e) => {
29 return (
30 StatusCode::BAD_REQUEST,
31 axum::Json(serde_json::json!({"error": e})),
32 )
33 .into_response();
34 }
35 };
36 let config = state.config.shift_config("anthropic");
37 let base_url = &state.config.providers.anthropic;
38 let query = uri.query().map(|q| format!("?{}", q)).unwrap_or_default();
39 let target_url = format!("{}{}{}", base_url, uri.path(), query);
40
41 let start = std::time::Instant::now();
44 let body_clone = body.clone();
45 let optimization_result =
46 tokio::task::spawn_blocking(move || optimize_payload(&body_clone, &config)).await;
47
48 let (final_body, optimized) = match optimization_result {
49 Ok(Some((transformed_json, report))) => {
50 let duration_ms = start.elapsed().as_millis() as u64;
51
52 state.session.record(&report);
54
55 let record =
57 shift_preflight::stats::record_from_report(&report, "anthropic", duration_ms);
58 if let Err(e) = shift_preflight::stats::record_run(&record, None) {
59 tracing::warn!("failed to save stats: {}", e);
60 }
61
62 if state.config.verbose {
63 let saved = report.original_size.saturating_sub(report.transformed_size);
64 if saved > 0 {
65 tracing::info!(
66 "Anthropic: saved {:.1}KB ({} tokens)",
67 saved as f64 / 1024.0,
68 report.token_savings.anthropic_saved(),
69 );
70 }
71 }
72
73 (transformed_json, true)
74 }
75 Ok(None) | Err(_) => (body, false),
76 };
77
78 if state.config.verbose && !optimized {
79 tracing::debug!("Anthropic: no optimization applied (passthrough)");
80 }
81
82 forward_request(
83 &state.http_client,
84 "POST",
85 &target_url,
86 &headers,
87 Some(final_body),
88 )
89 .await
90}