-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
fix(streaming): use provider-reported usage cost for OpenRouter streams #32255
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
8a49423
21c1264
214130e
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 |
|---|---|---|
|
|
@@ -467,6 +467,7 @@ def _usage_chunk_calculation_helper(self, usage_chunk: Usage) -> dict: | |
| cache_read_input_tokens: Optional[int] = None | ||
| completion_tokens_details: Optional[CompletionTokensDetails] = None | ||
| prompt_tokens_details: Optional[PromptTokensDetailsWrapper] = None | ||
| cost: Optional[float] = None | ||
|
|
||
| if "prompt_tokens" in usage_chunk: | ||
| prompt_tokens = usage_chunk.get("prompt_tokens", 0) or 0 | ||
|
|
@@ -476,6 +477,8 @@ def _usage_chunk_calculation_helper(self, usage_chunk: Usage) -> dict: | |
| cache_creation_input_tokens = usage_chunk.get("cache_creation_input_tokens") | ||
| if "cache_read_input_tokens" in usage_chunk: | ||
| cache_read_input_tokens = usage_chunk.get("cache_read_input_tokens") | ||
| if "cost" in usage_chunk: | ||
| cost = usage_chunk.get("cost") | ||
| if hasattr(usage_chunk, "completion_tokens_details"): | ||
| if isinstance(usage_chunk.completion_tokens_details, dict): | ||
| completion_tokens_details = CompletionTokensDetails(**usage_chunk.completion_tokens_details) | ||
|
|
@@ -494,6 +497,7 @@ def _usage_chunk_calculation_helper(self, usage_chunk: Usage) -> dict: | |
| "cache_read_input_tokens": cache_read_input_tokens, | ||
| "completion_tokens_details": completion_tokens_details, | ||
| "prompt_tokens_details": prompt_tokens_details, | ||
| "cost": cost, | ||
| } | ||
|
|
||
| def count_reasoning_tokens(self, response: ModelResponse) -> Optional[int]: | ||
|
|
@@ -512,6 +516,22 @@ def count_reasoning_tokens(self, response: ModelResponse) -> Optional[int]: | |
|
|
||
| return reasoning_tokens | ||
|
|
||
| @staticmethod | ||
| def _extract_usage_chunk(chunk: dict[str, Any] | ModelResponse | ModelResponseStream) -> Usage | None: | ||
| usage_chunk: Usage | dict[str, Any] | None = None | ||
| if hasattr(chunk, "usage") and chunk.usage is not None: | ||
| usage_chunk = chunk.usage | ||
| elif "usage" in chunk: | ||
| usage_chunk = chunk["usage"] | ||
| elif (isinstance(chunk, ModelResponse) or isinstance(chunk, ModelResponseStream)) and hasattr( | ||
| chunk, "_hidden_params" | ||
| ): | ||
| usage_chunk = chunk._hidden_params.get("usage", None) | ||
|
|
||
| if isinstance(usage_chunk, dict): | ||
| return Usage(**usage_chunk) | ||
| return usage_chunk | ||
|
|
||
| def _calculate_usage_per_chunk( | ||
| self, | ||
| chunks: List[Union[Dict[str, Any], ModelResponse]], | ||
|
|
@@ -548,18 +568,12 @@ def _calculate_usage_per_chunk( | |
| # is last-wins, so without preserving this separately the 1h breakdown is | ||
| # lost and 1h cache writes get billed at the 5m rate. | ||
| cache_creation_token_details: Optional[CacheCreationTokenDetails] = None | ||
| cost: Optional[float] = None | ||
|
|
||
| for chunk in chunks: | ||
| usage_chunk: Optional[Usage] = None | ||
| if "usage" in chunk: | ||
| usage_chunk = chunk["usage"] | ||
| elif (isinstance(chunk, ModelResponse) or isinstance(chunk, ModelResponseStream)) and hasattr( | ||
| chunk, "_hidden_params" | ||
| ): | ||
| usage_chunk = chunk._hidden_params.get("usage", None) | ||
| usage_chunk = self._extract_usage_chunk(chunk) | ||
|
|
||
| if usage_chunk is not None: | ||
| if isinstance(usage_chunk, dict): | ||
| usage_chunk = Usage(**usage_chunk) | ||
| usage_chunk_dict = self._usage_chunk_calculation_helper(usage_chunk) | ||
| if usage_chunk_dict["prompt_tokens"] is not None and usage_chunk_dict["prompt_tokens"] > 0: | ||
| prompt_tokens = usage_chunk_dict["prompt_tokens"] | ||
|
|
@@ -610,6 +624,9 @@ def _calculate_usage_per_chunk( | |
| prompt_tokens_details, cache_creation_token_details | ||
| ) | ||
|
|
||
| if usage_chunk_dict["cost"] is not None: | ||
| cost = usage_chunk_dict["cost"] | ||
|
|
||
| prompt_tokens_details = self._attach_cache_creation_token_details( | ||
| prompt_tokens_details, cache_creation_token_details | ||
| ) | ||
|
|
@@ -629,6 +646,7 @@ def _calculate_usage_per_chunk( | |
| web_search_requests=web_search_requests, | ||
| completion_tokens_details=completion_tokens_details, | ||
| prompt_tokens_details=prompt_tokens_details, | ||
| cost=cost, | ||
| ) | ||
|
|
||
| @staticmethod | ||
|
|
@@ -727,6 +745,7 @@ def calculate_usage( | |
| prompt_tokens_details: Optional[PromptTokensDetailsWrapper] = calculated_usage_per_chunk[ | ||
| "prompt_tokens_details" | ||
| ] | ||
| cost: Optional[float] = calculated_usage_per_chunk["cost"] | ||
|
|
||
| try: | ||
| returned_usage.prompt_tokens = prompt_tokens or token_counter(model=model, messages=messages) | ||
|
|
@@ -784,6 +803,9 @@ def calculate_usage( | |
| else: | ||
| returned_usage.prompt_tokens_details.web_search_requests = web_search_requests | ||
|
|
||
| if cost is not None: | ||
| setattr(returned_usage, "cost", cost) | ||
|
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. Low: Provider cost disclosure
Contributor
Author
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. The For provider-reported cost specifically, the non-streaming path already returns it to the same authenticated client today: litellm always requests Spend tracking does not depend on the public field either way; the cost calculator reads |
||
|
|
||
| # Return a new usage object with the new values | ||
|
|
||
| returned_usage = Usage(**returned_usage.model_dump()) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.