mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
fix(types): break the CachedTokensDetails import cycle
CodeQL flagged two module-level cyclic imports introduced by defining CachedTokensDetails in litellm.types.llms.openai and importing it from litellm.types.utils and litellm.cost_calculator. The class now lives in litellm.types.llms.base, which imports nothing from litellm, and every user imports it from there. Also pins that combining realtime usages where only one response.done carries cached_tokens_details keeps the earlier modality split in both orders, and commits the regenerated dashboard API types.
This commit is contained in:
parent
a28e595a9d
commit
76ae35dfcd
7 changed files with 53 additions and 12 deletions
|
|
@ -97,6 +97,7 @@ from litellm.llms.vertex_ai.cost_calculator import cost_router as google_cost_ro
|
||||||
from litellm.llms.xai.cost_calculator import cost_per_token as xai_cost_per_token
|
from litellm.llms.xai.cost_calculator import cost_per_token as xai_cost_per_token
|
||||||
from litellm.responses.utils import ResponseAPILoggingUtils
|
from litellm.responses.utils import ResponseAPILoggingUtils
|
||||||
from litellm.types.agents import LiteLLMSendMessageResponse
|
from litellm.types.agents import LiteLLMSendMessageResponse
|
||||||
|
from litellm.types.llms.base import CachedTokensDetails
|
||||||
from litellm.types.llms.openai import (
|
from litellm.types.llms.openai import (
|
||||||
HttpxBinaryResponseContent,
|
HttpxBinaryResponseContent,
|
||||||
ImageGenerationRequestQuality,
|
ImageGenerationRequestQuality,
|
||||||
|
|
@ -109,7 +110,6 @@ from litellm.types.llms.openai import (
|
||||||
)
|
)
|
||||||
from litellm.types.rerank import RerankBilledUnits, RerankResponse
|
from litellm.types.rerank import RerankBilledUnits, RerankResponse
|
||||||
from litellm.types.utils import (
|
from litellm.types.utils import (
|
||||||
CachedTokensDetails,
|
|
||||||
CallTypesLiteral,
|
CallTypesLiteral,
|
||||||
LiteLLMRealtimeStreamLoggingObject,
|
LiteLLMRealtimeStreamLoggingObject,
|
||||||
LlmProviders,
|
LlmProviders,
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,9 @@ from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging
|
||||||
from litellm.responses.litellm_completion_transformation.session_handler import (
|
from litellm.responses.litellm_completion_transformation.session_handler import (
|
||||||
ResponsesSessionHandler,
|
ResponsesSessionHandler,
|
||||||
)
|
)
|
||||||
|
from litellm.types.llms.base import CachedTokensDetails
|
||||||
from litellm.types.llms.openai import (
|
from litellm.types.llms.openai import (
|
||||||
AllMessageValues,
|
AllMessageValues,
|
||||||
CachedTokensDetails,
|
|
||||||
ChatCompletionAssistantMessage,
|
ChatCompletionAssistantMessage,
|
||||||
ChatCompletionImageObject,
|
ChatCompletionImageObject,
|
||||||
ChatCompletionImageUrlObject,
|
ChatCompletionImageUrlObject,
|
||||||
|
|
|
||||||
|
|
@ -75,3 +75,9 @@ class HiddenParams(OpenAIObject):
|
||||||
data: Final = super().model_dump(**kwargs)
|
data: Final = super().model_dump(**kwargs)
|
||||||
data["_response_ms"] = self._response_ms
|
data["_response_ms"] = self._response_ms
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class CachedTokensDetails(BaseModel):
|
||||||
|
text_tokens: int | None = None
|
||||||
|
audio_tokens: int | None = None
|
||||||
|
image_tokens: int | None = None
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ from typing_extensions import (
|
||||||
override,
|
override,
|
||||||
)
|
)
|
||||||
|
|
||||||
from litellm.types.llms.base import BaseLiteLLMOpenAIResponseObject
|
from litellm.types.llms.base import BaseLiteLLMOpenAIResponseObject, CachedTokensDetails
|
||||||
from litellm.types.responses.main import (
|
from litellm.types.responses.main import (
|
||||||
CustomToolCallOutputItem,
|
CustomToolCallOutputItem,
|
||||||
GenericResponseOutputItem,
|
GenericResponseOutputItem,
|
||||||
|
|
@ -1285,12 +1285,6 @@ class OutputTokensDetails(BaseLiteLLMOpenAIResponseObject):
|
||||||
model_config = {"extra": "allow"}
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
|
||||||
class CachedTokensDetails(BaseModel):
|
|
||||||
text_tokens: int | None = None
|
|
||||||
audio_tokens: int | None = None
|
|
||||||
image_tokens: int | None = None
|
|
||||||
|
|
||||||
|
|
||||||
class InputTokensDetails(BaseLiteLLMOpenAIResponseObject):
|
class InputTokensDetails(BaseLiteLLMOpenAIResponseObject):
|
||||||
audio_tokens: int | None = None
|
audio_tokens: int | None = None
|
||||||
cached_tokens: int = 0
|
cached_tokens: int = 0
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ from litellm._logging import verbose_logger
|
||||||
from litellm._uuid import uuid
|
from litellm._uuid import uuid
|
||||||
from litellm.types.llms.base import (
|
from litellm.types.llms.base import (
|
||||||
BaseLiteLLMOpenAIResponseObject,
|
BaseLiteLLMOpenAIResponseObject,
|
||||||
|
CachedTokensDetails,
|
||||||
LiteLLMPydanticObjectBase,
|
LiteLLMPydanticObjectBase,
|
||||||
)
|
)
|
||||||
from litellm.types.mcp import MCPServerCostInfo
|
from litellm.types.mcp import MCPServerCostInfo
|
||||||
|
|
@ -60,7 +61,6 @@ from .llms.base import HiddenParams
|
||||||
from .llms.openai import (
|
from .llms.openai import (
|
||||||
AllMessageValues,
|
AllMessageValues,
|
||||||
Batch,
|
Batch,
|
||||||
CachedTokensDetails,
|
|
||||||
ChatCompletionAnnotation,
|
ChatCompletionAnnotation,
|
||||||
ChatCompletionReasoningItem,
|
ChatCompletionReasoningItem,
|
||||||
ChatCompletionRedactedThinkingBlock,
|
ChatCompletionRedactedThinkingBlock,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ from litellm.cost_calculator import (
|
||||||
)
|
)
|
||||||
from litellm.litellm_core_utils.litellm_logging import Logging
|
from litellm.litellm_core_utils.litellm_logging import Logging
|
||||||
from litellm.llms.base_llm.ocr.transformation import OCRPage, OCRResponse, OCRUsageInfo
|
from litellm.llms.base_llm.ocr.transformation import OCRPage, OCRResponse, OCRUsageInfo
|
||||||
|
from litellm.types.llms.base import CachedTokensDetails
|
||||||
from litellm.types.llms.openai import OpenAIRealtimeStreamList
|
from litellm.types.llms.openai import OpenAIRealtimeStreamList
|
||||||
from litellm.types.rerank import RerankResponse
|
from litellm.types.rerank import RerankResponse
|
||||||
from litellm.types.utils import (
|
from litellm.types.utils import (
|
||||||
|
|
@ -4896,6 +4897,48 @@ def test_realtime_combine_sums_nested_cached_tokens_details():
|
||||||
assert combined.prompt_tokens_details.cached_tokens_details.image_tokens is None
|
assert combined.prompt_tokens_details.cached_tokens_details.image_tokens is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("details_first", [True, False])
|
||||||
|
def test_realtime_combine_keeps_cached_split_when_only_one_usage_has_details(details_first: bool):
|
||||||
|
with_details: Final = {
|
||||||
|
"type": "response.done",
|
||||||
|
"response": {
|
||||||
|
"usage": {
|
||||||
|
"input_tokens": 283,
|
||||||
|
"output_tokens": 0,
|
||||||
|
"total_tokens": 283,
|
||||||
|
"input_token_details": {
|
||||||
|
"text_tokens": 116,
|
||||||
|
"audio_tokens": 167,
|
||||||
|
"cached_tokens": 192,
|
||||||
|
"cached_tokens_details": {"text_tokens": 64, "audio_tokens": 128},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
without_details: Final = {
|
||||||
|
"type": "response.done",
|
||||||
|
"response": {
|
||||||
|
"usage": {
|
||||||
|
"input_tokens": 150,
|
||||||
|
"output_tokens": 0,
|
||||||
|
"total_tokens": 150,
|
||||||
|
"input_token_details": {"text_tokens": 50, "audio_tokens": 100, "cached_tokens": 100},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
results: OpenAIRealtimeStreamList = (
|
||||||
|
[with_details, without_details] if details_first else [without_details, with_details]
|
||||||
|
)
|
||||||
|
|
||||||
|
combined = RealtimeAPITokenUsageProcessor.collect_and_combine_usage_from_realtime_stream_results(
|
||||||
|
results=results,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert combined.prompt_tokens_details is not None
|
||||||
|
assert combined.prompt_tokens_details.cached_tokens == 292
|
||||||
|
assert combined.prompt_tokens_details.cached_tokens_details == CachedTokensDetails(text_tokens=64, audio_tokens=128)
|
||||||
|
|
||||||
|
|
||||||
def test_usage_without_cached_tokens_details_omits_key():
|
def test_usage_without_cached_tokens_details_omits_key():
|
||||||
usage = Usage(
|
usage = Usage(
|
||||||
prompt_tokens=10,
|
prompt_tokens=10,
|
||||||
|
|
|
||||||
2
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
2
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
|
|
@ -16781,7 +16781,6 @@ export interface paths {
|
||||||
* - permissions: Optional[dict] - [Not Implemented Yet] User-specific permissions, eg. turning off pii masking.
|
* - permissions: Optional[dict] - [Not Implemented Yet] User-specific permissions, eg. turning off pii masking.
|
||||||
* - metadata: Optional[dict] - Metadata for user, store information for user. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" }
|
* - metadata: Optional[dict] - Metadata for user, store information for user. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" }
|
||||||
* - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x.
|
* - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x.
|
||||||
* - soft_budget: Optional[float] - Get alerts when user crosses given budget, doesn't block requests.
|
|
||||||
* - model_max_budget: Optional[dict] - Model-specific max budget for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-budgets-to-keys)
|
* - model_max_budget: Optional[dict] - Model-specific max budget for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-budgets-to-keys)
|
||||||
* - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}.
|
* - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}.
|
||||||
* - model_rpm_limit: Optional[float] - Model-specific rpm limit for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-limits-to-keys)
|
* - model_rpm_limit: Optional[float] - Model-specific rpm limit for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-limits-to-keys)
|
||||||
|
|
@ -16887,7 +16886,6 @@ export interface paths {
|
||||||
* - permissions: Optional[dict] - [Not Implemented Yet] User-specific permissions, eg. turning off pii masking.
|
* - permissions: Optional[dict] - [Not Implemented Yet] User-specific permissions, eg. turning off pii masking.
|
||||||
* - metadata: Optional[dict] - Metadata for user, store information for user. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" }
|
* - metadata: Optional[dict] - Metadata for user, store information for user. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" }
|
||||||
* - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x.
|
* - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x.
|
||||||
* - soft_budget: Optional[float] - Get alerts when user crosses given budget, doesn't block requests.
|
|
||||||
* - model_max_budget: Optional[dict] - Model-specific max budget for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-budgets-to-keys)
|
* - model_max_budget: Optional[dict] - Model-specific max budget for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-budgets-to-keys)
|
||||||
* - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}.
|
* - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}.
|
||||||
* - model_rpm_limit: Optional[float] - Model-specific rpm limit for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-limits-to-keys)
|
* - model_rpm_limit: Optional[float] - Model-specific rpm limit for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-limits-to-keys)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue