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}