From d319cd8cc6ecb64d3a0ac3939858a9b91f44281b Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Mon, 13 Apr 2026 09:08:57 -0700 Subject: [PATCH 1/3] fix: blog dark mode - text invisible on dark background (#25620) The blog CSS selectors for dark mode used descendant selectors like [data-theme='dark'] .blog-wrapper which never matched because both data-theme and .blog-wrapper are applied to the same element by Docusaurus. Fixed by using compound selectors (no space): [data-theme='dark'].blog-wrapper. Also added missing dark-mode overrides for: - pre/code blocks in blog posts - link colors in blog posts - marquee items, separators, and labels on blog list page - pagination links on blog list page - meta text and author separators on blog list page Co-authored-by: Cursor Agent Co-authored-by: Krrish Dholakia --- docs/my-website/src/css/custom.css | 20 +++++++++---- .../src/theme/BlogListPage/styles.module.css | 29 +++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/docs/my-website/src/css/custom.css b/docs/my-website/src/css/custom.css index 29be18a5ae9..b036604cf1f 100644 --- a/docs/my-website/src/css/custom.css +++ b/docs/my-website/src/css/custom.css @@ -899,18 +899,28 @@ video { font-size: 0.85rem; } -[data-theme='dark'] .blog-wrapper article header h1, -[data-theme='dark'] .blog-wrapper article .markdown h2, -[data-theme='dark'] .blog-wrapper article .markdown h3 { +[data-theme='dark'].blog-wrapper article header h1, +[data-theme='dark'].blog-wrapper article .markdown h2, +[data-theme='dark'].blog-wrapper article .markdown h3 { color: #f9fafb; } -[data-theme='dark'] .blog-wrapper article .markdown { +[data-theme='dark'].blog-wrapper article .markdown { color: #d1d5db; } -[data-theme='dark'] .blog-wrapper article .markdown code { +[data-theme='dark'].blog-wrapper article .markdown code { background: #1f2937; border-color: #374151; color: #f9fafb; } + +[data-theme='dark'].blog-wrapper article .markdown pre { + background: #161b22 !important; + border-color: #30363d !important; + box-shadow: none; +} + +[data-theme='dark'].blog-wrapper article .markdown a { + color: #38bdf8; +} diff --git a/docs/my-website/src/theme/BlogListPage/styles.module.css b/docs/my-website/src/theme/BlogListPage/styles.module.css index 520e4c41c61..aab6ad5cf64 100644 --- a/docs/my-website/src/theme/BlogListPage/styles.module.css +++ b/docs/my-website/src/theme/BlogListPage/styles.module.css @@ -252,3 +252,32 @@ [data-theme='dark'] .hiringBtn:hover { background: #fff; } + +[data-theme='dark'] .marqueeItem { + color: #9ca3af; +} + +[data-theme='dark'] .marqueeSep { + color: #374151; +} + +[data-theme='dark'] .marqueeLabel { + color: #6b7280; +} + +[data-theme='dark'] .pageLink { + color: #d1d5db; +} + +[data-theme='dark'] .pageLink:hover { + color: #38bdf8; +} + +[data-theme='dark'] .meta { + color: #9ca3af; +} + +[data-theme='dark'] .metaDash, +[data-theme='dark'] .authorSep { + color: #4b5563; +} From 597cd0000ddf32a675ab7f569d4b4562919b77db Mon Sep 17 00:00:00 2001 From: michelligabriele Date: Mon, 13 Apr 2026 19:31:58 +0200 Subject: [PATCH 2/3] fix(router): honor RetryPolicy.InternalServerErrorRetries in dispatcher --- litellm/router_utils/get_retry_from_policy.py | 7 ++++ .../test_get_retry_from_policy.py | 39 +++++++++++++++++++ .../test_router_helper_utils.py | 1 + 3 files changed, 47 insertions(+) create mode 100644 tests/litellm/router_utils/test_get_retry_from_policy.py diff --git a/litellm/router_utils/get_retry_from_policy.py b/litellm/router_utils/get_retry_from_policy.py index ec326ebb50d..5ec7175c3a9 100644 --- a/litellm/router_utils/get_retry_from_policy.py +++ b/litellm/router_utils/get_retry_from_policy.py @@ -10,6 +10,7 @@ from litellm.exceptions import ( AuthenticationError, BadRequestError, ContentPolicyViolationError, + InternalServerError, RateLimitError, Timeout, ) @@ -28,6 +29,7 @@ def get_num_retries_from_retry_policy( TimeoutErrorRetries: Optional[int] = None RateLimitErrorRetries: Optional[int] = None ContentPolicyViolationErrorRetries: Optional[int] = None + InternalServerErrorRetries: Optional[int] = None """ # if we can find the exception then in the retry policy -> return the number of retries @@ -55,6 +57,11 @@ def get_num_retries_from_retry_policy( and retry_policy.RateLimitErrorRetries is not None ): return retry_policy.RateLimitErrorRetries + if ( + isinstance(exception, InternalServerError) + and retry_policy.InternalServerErrorRetries is not None + ): + return retry_policy.InternalServerErrorRetries if ( isinstance(exception, ContentPolicyViolationError) and retry_policy.ContentPolicyViolationErrorRetries is not None diff --git a/tests/litellm/router_utils/test_get_retry_from_policy.py b/tests/litellm/router_utils/test_get_retry_from_policy.py new file mode 100644 index 00000000000..9c8473333cf --- /dev/null +++ b/tests/litellm/router_utils/test_get_retry_from_policy.py @@ -0,0 +1,39 @@ +"""Unit tests for litellm.router_utils.get_retry_from_policy.""" + +import litellm +from litellm.router_utils.get_retry_from_policy import ( + get_num_retries_from_retry_policy, +) +from litellm.types.router import RetryPolicy + + +def test_internal_server_error_retries_is_honored(): + """Regression: `InternalServerErrorRetries` must be returned for + `InternalServerError` exceptions. Previously the dispatcher had no + branch for this field and silently returned `None`, causing the + caller to fall back to `num_retries`.""" + retry_policy = RetryPolicy(InternalServerErrorRetries=5) + exc = litellm.exceptions.InternalServerError( + message="test", llm_provider="openai", model="gpt-3.5-turbo" + ) + + num_retries = get_num_retries_from_retry_policy( + exception=exc, retry_policy=retry_policy + ) + + assert num_retries == 5 + + +def test_internal_server_error_retries_unset_returns_none(): + """When the field is not set, the dispatcher should return `None` + so the caller falls back to `num_retries`.""" + retry_policy = RetryPolicy() + exc = litellm.exceptions.InternalServerError( + message="test", llm_provider="openai", model="gpt-3.5-turbo" + ) + + num_retries = get_num_retries_from_retry_policy( + exception=exc, retry_policy=retry_policy + ) + + assert num_retries is None diff --git a/tests/router_unit_tests/test_router_helper_utils.py b/tests/router_unit_tests/test_router_helper_utils.py index 34a19f5ce79..68890e6ee4a 100644 --- a/tests/router_unit_tests/test_router_helper_utils.py +++ b/tests/router_unit_tests/test_router_helper_utils.py @@ -957,6 +957,7 @@ def test_track_deployment_metrics(model_list): "ContentPolicyViolationError", 7, ), + (litellm.exceptions.InternalServerError, "InternalServerError", 5), ], ) def test_get_num_retries_from_retry_policy( From 9edc10a5439c9090818ddf310f5cb0e7f872b7bc Mon Sep 17 00:00:00 2001 From: michelligabriele Date: Mon, 13 Apr 2026 19:47:30 +0200 Subject: [PATCH 3/3] fix(router): lazy-import InternalServerError to break cyclic import --- litellm/router_utils/get_retry_from_policy.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/litellm/router_utils/get_retry_from_policy.py b/litellm/router_utils/get_retry_from_policy.py index 5ec7175c3a9..b8c6de9b625 100644 --- a/litellm/router_utils/get_retry_from_policy.py +++ b/litellm/router_utils/get_retry_from_policy.py @@ -10,7 +10,6 @@ from litellm.exceptions import ( AuthenticationError, BadRequestError, ContentPolicyViolationError, - InternalServerError, RateLimitError, Timeout, ) @@ -31,6 +30,11 @@ def get_num_retries_from_retry_policy( ContentPolicyViolationErrorRetries: Optional[int] = None InternalServerErrorRetries: Optional[int] = None """ + # Lazy import: `InternalServerError` is defined late in `litellm/exceptions.py`, + # after a cyclic import re-enters this module during exceptions.py evaluation. + # A module-level import would be flagged by CodeQL as potentially undefined. + from litellm.exceptions import InternalServerError + # if we can find the exception then in the retry policy -> return the number of retries if (