shift_proxy/routes/
openai.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 openai_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("openai");
37 let base_url = &state.config.providers.openai;
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 = shift_preflight::stats::record_from_report(&report, "openai", duration_ms);
57 if let Err(e) = shift_preflight::stats::record_run(&record, None) {
58 tracing::warn!("failed to save stats: {}", e);
59 }
60
61 if state.config.verbose {
62 let saved = report.original_size.saturating_sub(report.transformed_size);
63 if saved > 0 {
64 tracing::info!(
65 "OpenAI: saved {:.1}KB ({} tokens)",
66 saved as f64 / 1024.0,
67 report.token_savings.openai_saved(),
68 );
69 }
70 }
71
72 (transformed_json, true)
73 }
74 Ok(None) | Err(_) => (body, false),
75 };
76
77 if state.config.verbose && !optimized {
78 tracing::debug!("OpenAI: no optimization applied (passthrough)");
79 }
80
81 forward_request(
82 &state.http_client,
83 "POST",
84 &target_url,
85 &headers,
86 Some(final_body),
87 )
88 .await
89}