ultrafast_gateway/json_optimization.rs
1//! # JSON Optimization Module
2//!
3//! This module provides comprehensive JSON optimization utilities for the Ultrafast Gateway,
4//! reducing payload sizes and improving performance through intelligent JSON compression
5//! and field optimization.
6//!
7//! ## Overview
8//!
9//! The JSON optimization system provides:
10//! - **Payload Size Reduction**: Remove unnecessary fields and null values
11//! - **Field Compression**: Compress field names and values
12//! - **Request Optimization**: Optimize outgoing requests to providers
13//! - **Response Optimization**: Minimize response payload sizes
14//! - **Size Tracking**: Monitor payload size reductions
15//! - **Performance Metrics**: Track optimization effectiveness
16//!
17//! ## Optimization Strategies
18//!
19//! ### Field Removal
20//!
21//! Removes unnecessary fields to reduce payload size:
22//! - **Null Values**: Automatically removes null fields
23//! - **Empty Arrays**: Removes empty array fields
24//! - **Default Values**: Removes fields with default values
25//! - **Optional Fields**: Removes optional fields when not needed
26//!
27//! ### Field Compression
28//!
29//! Compresses field names and values for size reduction:
30//! - **Field Name Mapping**: Maps long field names to short codes
31//! - **Value Compression**: Compresses repeated values
32//! - **Numeric Optimization**: Optimizes number representations
33//! - **String Compression**: Compresses string values
34//!
35//! ### Request-Specific Optimization
36//!
37//! Optimizes different request types:
38//! - **Chat Completions**: Optimizes chat completion requests
39//! - **Embeddings**: Optimizes embedding requests
40//! - **Image Generation**: Optimizes image generation requests
41//! - **Audio Processing**: Optimizes audio processing requests
42//!
43//! ## Usage
44//!
45//! ```rust
46//! use ultrafast_gateway::json_optimization::JsonOptimizer;
47//! use serde_json::Value;
48//!
49//! // Optimize a request payload
50//! let original_request: Value = serde_json::from_str(r#"{
51//! "model": "gpt-4",
52//! "messages": [{"role": "user", "content": "Hello"}],
53//! "temperature": 0.7,
54//! "max_tokens": 100,
55//! "unnecessary_field": null
56//! }"#)?;
57//!
58//! let optimized = JsonOptimizer::optimize_request_payload(&original_request);
59//!
60//! // Calculate size reduction
61//! let reduction = JsonOptimizer::get_size_reduction(&original_request, &optimized);
62//! println!("Size reduction: {:.2}%", reduction * 100.0);
63//! ```
64//!
65//! ## Performance Benefits
66//!
67//! The optimization system provides significant benefits:
68//!
69//! - **Reduced Bandwidth**: Smaller payloads reduce network usage
70//! - **Faster Transfers**: Smaller payloads transfer faster
71//! - **Lower Costs**: Reduced data transfer costs
72//! - **Better Caching**: Smaller payloads cache more efficiently
73//! - **Improved Latency**: Faster request/response cycles
74//!
75//! ## Compression Algorithms
76//!
77//! The system uses multiple compression techniques:
78//!
79//! - **Field Mapping**: Maps common field names to short codes
80//! - **Value Deduplication**: Removes duplicate values
81//! - **Structure Optimization**: Optimizes JSON structure
82//! - **Type Optimization**: Optimizes data type representations
83//!
84//! ## Monitoring
85//!
86//! The system tracks optimization metrics:
87//!
88//! - **Size Reduction**: Percentage of size reduction achieved
89//! - **Compression Ratios**: Compression effectiveness metrics
90//! - **Performance Impact**: Optimization overhead tracking
91//! - **Cache Efficiency**: Cache hit rate improvements
92
93use serde_json::{Map, Value};
94use std::collections::HashMap;
95
96/// JSON optimization utilities for reducing payload sizes and improving performance.
97///
98/// This struct provides methods for optimizing JSON payloads by removing
99/// unnecessary fields, compressing data, and minimizing payload sizes
100/// while maintaining functionality.
101pub struct JsonOptimizer;
102
103impl JsonOptimizer {
104 /// Optimize JSON payload by removing unnecessary fields and minimizing size.
105 ///
106 /// This method recursively processes JSON objects and arrays to remove
107 /// null values, empty arrays, and optimize the structure for smaller
108 /// payload sizes while maintaining data integrity.
109 ///
110 /// # Arguments
111 ///
112 /// * `json` - The JSON value to optimize
113 ///
114 /// # Returns
115 ///
116 /// Returns an optimized JSON value with reduced size.
117 ///
118 /// # Examples
119 ///
120 /// ```rust
121 /// use ultrafast_gateway::json_optimization::JsonOptimizer;
122 /// use serde_json::json;
123 ///
124 /// let original = json!({
125 /// "model": "gpt-4",
126 /// "messages": [{"role": "user", "content": "Hello"}],
127 /// "unnecessary_field": null,
128 /// "empty_array": []
129 /// });
130 ///
131 /// let optimized = JsonOptimizer::optimize_request_payload(&original);
132 /// // Result: {"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}]}
133 /// ```
134 pub fn optimize_request_payload(json: &Value) -> Value {
135 match json {
136 Value::Object(obj) => {
137 let mut optimized = Map::new();
138
139 for (key, value) in obj {
140 // Skip null values to reduce payload size
141 if value.is_null() {
142 continue;
143 }
144
145 // Optimize nested objects
146 let optimized_value = match value {
147 Value::Object(_) => Self::optimize_request_payload(value),
148 Value::Array(arr) => {
149 // Optimize arrays by processing each element
150 let optimized_array: Vec<Value> =
151 arr.iter().map(Self::optimize_request_payload).collect();
152 Value::Array(optimized_array)
153 }
154 _ => value.clone(),
155 };
156
157 optimized.insert(key.clone(), optimized_value);
158 }
159
160 Value::Object(optimized)
161 }
162 Value::Array(arr) => {
163 let optimized_array: Vec<Value> =
164 arr.iter().map(Self::optimize_request_payload).collect();
165 Value::Array(optimized_array)
166 }
167 _ => json.clone(),
168 }
169 }
170
171 /// Create a minimal JSON response with only essential fields.
172 ///
173 /// This method creates a standardized minimal response structure
174 /// that contains only the essential data field, reducing response
175 /// size and improving parsing performance.
176 ///
177 /// # Arguments
178 ///
179 /// * `data` - The data to include in the response
180 ///
181 /// # Returns
182 ///
183 /// Returns a minimal JSON response structure.
184 ///
185 /// # Examples
186 ///
187 /// ```rust
188 /// use ultrafast_gateway::json_optimization::JsonOptimizer;
189 /// use serde_json::json;
190 ///
191 /// let data = json!({"result": "success"});
192 /// let response = JsonOptimizer::create_minimal_response(&data);
193 /// // Result: {"data": {"result": "success"}}
194 /// ```
195 pub fn create_minimal_response(data: &Value) -> Value {
196 let mut response = Map::new();
197 response.insert("data".to_string(), data.clone());
198 Value::Object(response)
199 }
200
201 /// Optimize chat completion request by removing unnecessary fields.
202 ///
203 /// This method specifically optimizes chat completion requests by
204 /// keeping only the essential fields required for the API call,
205 /// removing any unnecessary or null fields to reduce payload size.
206 ///
207 /// # Arguments
208 ///
209 /// * `request` - The chat completion request to optimize
210 ///
211 /// # Returns
212 ///
213 /// Returns an optimized chat completion request with only essential fields.
214 ///
215 /// # Examples
216 ///
217 /// ```rust
218 /// use ultrafast_gateway::json_optimization::JsonOptimizer;
219 /// use serde_json::json;
220 ///
221 /// let request = json!({
222 /// "model": "gpt-4",
223 /// "messages": [{"role": "user", "content": "Hello"}],
224 /// "temperature": 0.7,
225 /// "max_tokens": 100,
226 /// "unnecessary_field": null,
227 /// "extra_config": {}
228 /// });
229 ///
230 /// let optimized = JsonOptimizer::optimize_chat_request(&request);
231 /// // Keeps only: model, messages, temperature, max_tokens, etc.
232 /// ```
233 pub fn optimize_chat_request(request: &Value) -> Value {
234 if let Value::Object(obj) = request {
235 let mut optimized = Map::new();
236
237 // Keep only essential fields for chat completion
238 let essential_fields = [
239 "model",
240 "messages",
241 "max_tokens",
242 "temperature",
243 "top_p",
244 "frequency_penalty",
245 "presence_penalty",
246 "stream",
247 ];
248
249 for field in &essential_fields {
250 if let Some(value) = obj.get(*field) {
251 if !value.is_null() {
252 optimized.insert(field.to_string(), value.clone());
253 }
254 }
255 }
256
257 Value::Object(optimized)
258 } else {
259 request.clone()
260 }
261 }
262
263 /// Optimize embedding request by removing unnecessary fields.
264 ///
265 /// This method specifically optimizes embedding requests by keeping
266 /// only the essential fields required for the embedding API call,
267 /// removing any unnecessary or null fields to reduce payload size.
268 ///
269 /// # Arguments
270 ///
271 /// * `request` - The embedding request to optimize
272 ///
273 /// # Returns
274 ///
275 /// Returns an optimized embedding request with only essential fields.
276 ///
277 /// # Examples
278 ///
279 /// ```rust
280 /// use ultrafast_gateway::json_optimization::JsonOptimizer;
281 /// use serde_json::json;
282 ///
283 /// let request = json!({
284 /// "model": "text-embedding-ada-002",
285 /// "input": "Hello world",
286 /// "encoding_format": "float",
287 /// "dimensions": 1536,
288 /// "unnecessary_field": null
289 /// });
290 ///
291 /// let optimized = JsonOptimizer::optimize_embedding_request(&request);
292 /// // Keeps only: model, input, encoding_format, dimensions
293 /// ```
294 pub fn optimize_embedding_request(request: &Value) -> Value {
295 if let Value::Object(obj) = request {
296 let mut optimized = Map::new();
297
298 // Keep only essential fields for embeddings
299 let essential_fields = ["model", "input", "encoding_format", "dimensions"];
300
301 for field in &essential_fields {
302 if let Some(value) = obj.get(*field) {
303 if !value.is_null() {
304 optimized.insert(field.to_string(), value.clone());
305 }
306 }
307 }
308
309 Value::Object(optimized)
310 } else {
311 request.clone()
312 }
313 }
314
315 /// Compress JSON by using shorter field names where possible
316 pub fn compress_json(json: &Value) -> Value {
317 let mut compressed = json.clone();
318
319 // Replace common field names with shorter versions
320 if let Value::Object(obj) = &mut compressed {
321 let field_mapping = HashMap::from([
322 ("messages", "m"),
323 ("max_tokens", "mt"),
324 ("temperature", "t"),
325 ("top_p", "tp"),
326 ("frequency_penalty", "fp"),
327 ("presence_penalty", "pp"),
328 ("model", "md"),
329 ("content", "c"),
330 ("role", "r"),
331 ]);
332
333 let mut new_obj = Map::new();
334 for (key, value) in obj {
335 let key_str = key.as_str();
336 let new_key = field_mapping.get(key_str).unwrap_or(&key_str);
337 new_obj.insert(new_key.to_string(), value.clone());
338 }
339
340 compressed = Value::Object(new_obj);
341 }
342
343 compressed
344 }
345
346 /// Decompress JSON by restoring original field names
347 pub fn decompress_json(json: &Value) -> Value {
348 let mut decompressed = json.clone();
349
350 // Restore original field names
351 if let Value::Object(obj) = &mut decompressed {
352 let field_mapping = HashMap::from([
353 ("m", "messages"),
354 ("mt", "max_tokens"),
355 ("t", "temperature"),
356 ("tp", "top_p"),
357 ("fp", "frequency_penalty"),
358 ("pp", "presence_penalty"),
359 ("md", "model"),
360 ("c", "content"),
361 ("r", "role"),
362 ]);
363
364 let mut new_obj = Map::new();
365 for (key, value) in obj {
366 let key_str = key.as_str();
367 let new_key = field_mapping.get(key_str).unwrap_or(&key_str);
368 new_obj.insert(new_key.to_string(), value.clone());
369 }
370
371 decompressed = Value::Object(new_obj);
372 }
373
374 decompressed
375 }
376
377 /// Calculate JSON payload size in bytes
378 pub fn calculate_payload_size(json: &Value) -> usize {
379 serde_json::to_string(json).map(|s| s.len()).unwrap_or(0)
380 }
381
382 /// Get payload size reduction percentage
383 pub fn get_size_reduction(original: &Value, optimized: &Value) -> f64 {
384 let original_size = Self::calculate_payload_size(original);
385 let optimized_size = Self::calculate_payload_size(optimized);
386
387 if original_size == 0 {
388 return 0.0;
389 }
390
391 let reduction = original_size - optimized_size;
392 (reduction as f64 / original_size as f64) * 100.0
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399 use serde_json::json;
400
401 #[test]
402 fn test_optimize_request_payload() {
403 let request = json!({
404 "model": "claude-3-5-haiku-20241022",
405 "messages": [
406 {"role": "user", "content": "Hello"}
407 ],
408 "max_tokens": 100,
409 "temperature": 0.7,
410 "unnecessary_field": null,
411 "another_null": null
412 });
413
414 let optimized = JsonOptimizer::optimize_request_payload(&request);
415
416 // Should remove null fields
417 assert!(!optimized
418 .as_object()
419 .unwrap()
420 .contains_key("unnecessary_field"));
421 assert!(!optimized.as_object().unwrap().contains_key("another_null"));
422
423 // Should keep essential fields
424 assert!(optimized.as_object().unwrap().contains_key("model"));
425 assert!(optimized.as_object().unwrap().contains_key("messages"));
426 }
427
428 #[test]
429 fn test_compress_json() {
430 let request = json!({
431 "model": "claude-3-5-haiku-20241022",
432 "messages": [
433 {"role": "user", "content": "Hello"}
434 ],
435 "max_tokens": 100
436 });
437
438 let compressed = JsonOptimizer::compress_json(&request);
439 let obj = compressed.as_object().unwrap();
440
441 // Should use compressed field names
442 assert!(obj.contains_key("md")); // model
443 assert!(obj.contains_key("m")); // messages
444 assert!(obj.contains_key("mt")); // max_tokens
445 }
446
447 #[test]
448 fn test_size_reduction() {
449 let original = json!({
450 "model": "claude-3-5-haiku-20241022",
451 "messages": [
452 {"role": "user", "content": "Hello"}
453 ],
454 "max_tokens": 100,
455 "temperature": 0.7,
456 "unnecessary_field": null
457 });
458
459 let optimized = JsonOptimizer::optimize_request_payload(&original);
460 let reduction = JsonOptimizer::get_size_reduction(&original, &optimized);
461
462 // Should have some size reduction
463 assert!(reduction > 0.0);
464 }
465}