1
1
import json
2
+ import weakref
2
3
from typing import Any , Dict , List , Optional
3
4
4
5
import orjson
8
9
from litellm .proxy ._types import ProxyException
9
10
from litellm .types .router import Deployment
10
11
12
+ # Use a regular dictionary with manual cleanup
13
+ # WeakValueDictionary doesn't work well with our setup since request objects may be kept alive
14
+ _request_body_cache : Dict [int , Dict ] = {}
15
+
11
16
12
17
async def _read_request_body (request : Optional [Request ]) -> Dict :
13
18
"""
@@ -90,16 +95,50 @@ async def _read_request_body(request: Optional[Request]) -> Dict:
90
95
return {}
91
96
92
97
98
+ def clear_request_cache (request : Optional [Request ] = None ) -> None :
99
+ """
100
+ Clear cached request bodies to free memory.
101
+
102
+ Parameters:
103
+ - request: If provided, only clear cache for this specific request.
104
+ If None, clear entire cache.
105
+ """
106
+ global _request_body_cache
107
+
108
+ if request is not None :
109
+ request_id = id (request )
110
+ if request_id in _request_body_cache :
111
+ try :
112
+ del _request_body_cache [request_id ]
113
+ except KeyError :
114
+ pass
115
+ else :
116
+ # Clear entire cache
117
+ _request_body_cache .clear ()
118
+
119
+
93
120
def _safe_get_request_parsed_body (request : Optional [Request ]) -> Optional [dict ]:
94
121
if request is None :
95
122
return None
123
+
124
+ # Try to get from cache first
125
+ request_id = id (request )
126
+ if request_id in _request_body_cache :
127
+ return _request_body_cache [request_id ]
128
+
129
+ # Fallback to checking request.scope for backward compatibility
96
130
if (
97
131
hasattr (request , "scope" )
98
132
and "parsed_body" in request .scope
99
133
and isinstance (request .scope ["parsed_body" ], tuple )
100
134
):
101
135
accepted_keys , parsed_body = request .scope ["parsed_body" ]
102
- return {key : parsed_body [key ] for key in accepted_keys }
136
+ result = {key : parsed_body [key ] for key in accepted_keys }
137
+ # Clean up the scope to free memory
138
+ del request .scope ["parsed_body" ]
139
+ # Store in our cache for consistency
140
+ _request_body_cache [request_id ] = result
141
+ return result
103
142
return None
104
143
105
144
def _safe_get_request_query_params (request : Optional [Request ]) -> Dict :
@@ -122,7 +161,18 @@ def _safe_set_request_parsed_body(
122
161
try :
123
162
if request is None :
124
163
return
125
- request .scope ["parsed_body" ] = (tuple (parsed_body .keys ()), parsed_body )
164
+
165
+ # Store in cache with size limit to prevent unbounded growth
166
+ request_id = id (request )
167
+ _request_body_cache [request_id ] = parsed_body
168
+
169
+ # If cache gets too large, remove oldest entries
170
+ if len (_request_body_cache ) > 1000 : # Maximum 1000 cached requests
171
+ # Remove the oldest 100 entries
172
+ keys_to_remove = list (_request_body_cache .keys ())[:100 ]
173
+ for key in keys_to_remove :
174
+ del _request_body_cache [key ]
175
+
126
176
except Exception as e :
127
177
verbose_proxy_logger .debug (
128
178
"Unexpected error setting request parsed body - {}" .format (e )
0 commit comments