-
-
Notifications
You must be signed in to change notification settings - Fork 11.6k
fix(proxy): release budget reservation when a request is cancelled mid-flight #30522
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2531,6 +2531,7 @@ async def async_streaming_data_generator( | |
| debug_enabled = verbose_proxy_logger.isEnabledFor(logging.DEBUG) | ||
| stream_completed = False | ||
| client_disconnected = False | ||
| delivered_chunk = False | ||
| try: | ||
| str_so_far = "" | ||
| async for ( | ||
|
|
@@ -2547,36 +2548,38 @@ async def async_streaming_data_generator( | |
| "async_data_generator: received streaming chunk - %s", chunk | ||
| ) | ||
|
|
||
| if fast_path: | ||
| yield serialize_chunk(chunk) | ||
| continue | ||
|
|
||
| chunk = await proxy_logging_obj.async_post_call_streaming_hook( | ||
| user_api_key_dict=user_api_key_dict, | ||
| response=chunk, | ||
| data=request_data, | ||
| str_so_far=str_so_far, | ||
| ) | ||
|
|
||
| if isinstance(chunk, (ModelResponse, ModelResponseStream)): | ||
| response_str = litellm.get_response_string(response_obj=chunk) | ||
| str_so_far += response_str | ||
| elif hasattr(chunk, "model_dump"): | ||
| try: | ||
| d = chunk.model_dump(mode="json", exclude_none=True) | ||
| if isinstance(d, dict): | ||
| str_so_far += str(d.get("content", "")) | ||
| except Exception: | ||
| pass | ||
| elif isinstance(chunk, dict): | ||
| str_so_far += str(chunk.get("content", "")) | ||
| if not fast_path: | ||
| chunk = await proxy_logging_obj.async_post_call_streaming_hook( | ||
| user_api_key_dict=user_api_key_dict, | ||
| response=chunk, | ||
| data=request_data, | ||
| str_so_far=str_so_far, | ||
| ) | ||
|
|
||
| model_name = request_data.get("model", "") | ||
| chunk = ( | ||
| ProxyBaseLLMRequestProcessing._process_chunk_with_cost_injection( | ||
| if isinstance(chunk, (ModelResponse, ModelResponseStream)): | ||
| response_str = litellm.get_response_string(response_obj=chunk) | ||
| str_so_far += response_str | ||
| elif hasattr(chunk, "model_dump"): | ||
| try: | ||
| d = chunk.model_dump(mode="json", exclude_none=True) | ||
| if isinstance(d, dict): | ||
| str_so_far += str(d.get("content", "")) | ||
| except Exception: | ||
| pass | ||
| elif isinstance(chunk, dict): | ||
| str_so_far += str(chunk.get("content", "")) | ||
|
|
||
| model_name = request_data.get("model", "") | ||
| chunk = ProxyBaseLLMRequestProcessing._process_chunk_with_cost_injection( | ||
| chunk, model_name | ||
| ) | ||
| ) | ||
|
|
||
| # Set before the yield: an async generator suspends at the yield, | ||
| # so a GeneratorExit on client disconnect is raised there and any | ||
| # statement after the yield never runs. The slow-path hook is | ||
| # awaited above, so a cancellation during it still leaves this | ||
| # False and refunds. | ||
| delivered_chunk = True | ||
| yield serialize_chunk(chunk) | ||
| stream_completed = True | ||
| except (asyncio.CancelledError, GeneratorExit): | ||
|
|
@@ -2591,6 +2594,14 @@ async def async_streaming_data_generator( | |
| user_api_key_dict | ||
| ) | ||
| client_disconnected = True | ||
| if not delivered_chunk: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Medium: Streaming budget undercount
|
||
| from litellm.proxy.spend_tracking.budget_reservation import ( | ||
| release_budget_reservation_on_cancel, | ||
| ) | ||
|
|
||
| await release_budget_reservation_on_cancel( | ||
|
veria-ai[bot] marked this conversation as resolved.
|
||
| getattr(user_api_key_dict, "budget_reservation", None) | ||
| ) | ||
| raise | ||
| except Exception as e: | ||
| verbose_proxy_logger.exception( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.