adjust blog posts to fetch from github first
This commit is contained in:
parent
a0965d5b4a
commit
1ecfbad46e
@ -339,6 +339,10 @@ model_cost_map_url: str = os.getenv(
|
||||
"LITELLM_MODEL_COST_MAP_URL",
|
||||
"https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
||||
)
|
||||
blog_posts_url: str = os.getenv(
|
||||
"LITELLM_BLOG_POSTS_URL",
|
||||
"https://raw.githubusercontent.com/BerriAI/litellm/main/blog_posts.json",
|
||||
)
|
||||
anthropic_beta_headers_url: str = os.getenv(
|
||||
"LITELLM_ANTHROPIC_BETA_HEADERS_URL",
|
||||
"https://raw.githubusercontent.com/BerriAI/litellm/main/litellm/anthropic_beta_headers_config.json",
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
{
|
||||
"posts": [
|
||||
{
|
||||
"title": "LiteLLM: Unified Interface for 100+ LLMs",
|
||||
"description": "Learn how LiteLLM provides a single interface to call any LLM with OpenAI-compatible syntax.",
|
||||
"date": "2026-02-01",
|
||||
"url": "https://www.litellm.ai/blog/litellm"
|
||||
},
|
||||
{
|
||||
"title": "Using the LiteLLM Proxy for Load Balancing",
|
||||
"description": "Set up the LiteLLM proxy server to load balance across multiple LLM providers and deployments.",
|
||||
"date": "2026-01-15",
|
||||
"url": "https://www.litellm.ai/blog/proxy-load-balancing"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
Pulls the latest LiteLLM blog posts from GitHub.
|
||||
|
||||
Falls back to the bundled local backup on any failure.
|
||||
GitHub JSON can be overridden via LITELLM_BLOG_POSTS_URL env var.
|
||||
GitHub JSON URL is configured via litellm.blog_posts_url (or LITELLM_BLOG_POSTS_URL env var).
|
||||
|
||||
Disable remote fetching entirely:
|
||||
export LITELLM_LOCAL_BLOG_POSTS=True
|
||||
@ -19,11 +19,6 @@ from pydantic import BaseModel
|
||||
|
||||
from litellm import verbose_logger
|
||||
|
||||
BLOG_POSTS_GITHUB_URL: str = os.getenv(
|
||||
"LITELLM_BLOG_POSTS_URL",
|
||||
"https://raw.githubusercontent.com/BerriAI/litellm/main/blog_posts.json",
|
||||
)
|
||||
|
||||
BLOG_POSTS_TTL_SECONDS: int = 3600 # 1 hour
|
||||
|
||||
|
||||
@ -57,7 +52,7 @@ class GetBlogPosts:
|
||||
"""Load the bundled local backup blog posts."""
|
||||
content = json.loads(
|
||||
files("litellm")
|
||||
.joinpath("blog_posts_backup.json")
|
||||
.joinpath("blog_posts.json")
|
||||
.read_text(encoding="utf-8")
|
||||
)
|
||||
return content.get("posts", [])
|
||||
@ -93,7 +88,7 @@ class GetBlogPosts:
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def get_blog_posts(cls, url: str = BLOG_POSTS_GITHUB_URL) -> List[Dict[str, str]]:
|
||||
def get_blog_posts(cls, url: str) -> List[Dict[str, str]]:
|
||||
"""
|
||||
Return the blog posts list.
|
||||
|
||||
@ -129,6 +124,6 @@ class GetBlogPosts:
|
||||
return cls._cached_posts
|
||||
|
||||
|
||||
def get_blog_posts(url: str = BLOG_POSTS_GITHUB_URL) -> List[Dict[str, str]]:
|
||||
def get_blog_posts(url: str) -> List[Dict[str, str]]:
|
||||
"""Public entry point — returns the blog posts list."""
|
||||
return GetBlogPosts.get_blog_posts(url=url)
|
||||
|
||||
@ -2,6 +2,7 @@ import json
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
import litellm
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
@ -213,7 +214,7 @@ async def get_litellm_blog_posts():
|
||||
Falls back to the bundled local backup on any failure.
|
||||
"""
|
||||
try:
|
||||
posts_data = get_blog_posts()
|
||||
posts_data = get_blog_posts(url=litellm.blog_posts_url)
|
||||
except Exception as e:
|
||||
verbose_logger.warning(
|
||||
"LiteLLM: get_litellm_blog_posts endpoint fallback triggered: %s", str(e)
|
||||
|
||||
@ -93,11 +93,6 @@ class UISettings(BaseModel):
|
||||
description="If enabled, forwards client headers (e.g. Authorization) to the LLM API. Required for Claude Code with Max subscription.",
|
||||
)
|
||||
|
||||
disable_show_blog: bool = Field(
|
||||
default=False,
|
||||
description="If true, hides the Blog dropdown from the UI navbar.",
|
||||
)
|
||||
|
||||
|
||||
class UISettingsResponse(SettingsResponse):
|
||||
"""Response model for UI settings"""
|
||||
@ -112,7 +107,6 @@ ALLOWED_UI_SETTINGS_FIELDS = {
|
||||
"enabled_ui_pages_internal_users",
|
||||
"require_auth_for_public_ai_hub",
|
||||
"forward_client_headers_to_llm_api",
|
||||
"disable_show_blog",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
def test_ui_settings_has_disable_show_blog_field():
|
||||
"""UISettings model must include disable_show_blog."""
|
||||
from litellm.proxy.ui_crud_endpoints.proxy_setting_endpoints import UISettings
|
||||
|
||||
settings = UISettings()
|
||||
assert hasattr(settings, "disable_show_blog")
|
||||
assert settings.disable_show_blog is False # default
|
||||
|
||||
|
||||
def test_allowed_ui_settings_fields_contains_disable_show_blog():
|
||||
from litellm.proxy.ui_crud_endpoints.proxy_setting_endpoints import ALLOWED_UI_SETTINGS_FIELDS
|
||||
|
||||
assert "disable_show_blog" in ALLOWED_UI_SETTINGS_FIELDS
|
||||
@ -5,8 +5,8 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
import litellm
|
||||
from litellm.litellm_core_utils.get_blog_posts import (
|
||||
BLOG_POSTS_GITHUB_URL,
|
||||
BlogPost,
|
||||
BlogPostsResponse,
|
||||
GetBlogPosts,
|
||||
@ -68,7 +68,7 @@ def test_get_blog_posts_success():
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
with patch("litellm.litellm_core_utils.get_blog_posts.httpx.get", return_value=mock_response):
|
||||
posts = get_blog_posts()
|
||||
posts = get_blog_posts(url=litellm.blog_posts_url)
|
||||
|
||||
assert len(posts) == 1
|
||||
assert posts[0]["title"] == "Test Post"
|
||||
@ -80,7 +80,7 @@ def test_get_blog_posts_network_error_falls_back_to_local():
|
||||
"litellm.litellm_core_utils.get_blog_posts.httpx.get",
|
||||
side_effect=Exception("Network error"),
|
||||
):
|
||||
posts = get_blog_posts()
|
||||
posts = get_blog_posts(url=litellm.blog_posts_url)
|
||||
|
||||
assert isinstance(posts, list)
|
||||
assert len(posts) > 0
|
||||
@ -93,7 +93,7 @@ def test_get_blog_posts_invalid_json_falls_back_to_local():
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
with patch("litellm.litellm_core_utils.get_blog_posts.httpx.get", return_value=mock_response):
|
||||
posts = get_blog_posts()
|
||||
posts = get_blog_posts(url=litellm.blog_posts_url)
|
||||
|
||||
assert isinstance(posts, list)
|
||||
assert len(posts) > 0
|
||||
@ -115,7 +115,7 @@ def test_get_blog_posts_ttl_cache_not_refetched():
|
||||
return m
|
||||
|
||||
with patch("litellm.litellm_core_utils.get_blog_posts.httpx.get", side_effect=mock_get):
|
||||
posts = get_blog_posts()
|
||||
posts = get_blog_posts(url=litellm.blog_posts_url)
|
||||
|
||||
assert call_count == 0 # cache hit, no fetch
|
||||
assert len(posts) == 1
|
||||
@ -133,7 +133,7 @@ def test_get_blog_posts_ttl_expired_refetches():
|
||||
with patch(
|
||||
"litellm.litellm_core_utils.get_blog_posts.httpx.get", return_value=mock_response
|
||||
) as mock_get:
|
||||
posts = get_blog_posts()
|
||||
posts = get_blog_posts(url=litellm.blog_posts_url)
|
||||
|
||||
mock_get.assert_called_once()
|
||||
assert len(posts) == 1
|
||||
@ -142,7 +142,7 @@ def test_get_blog_posts_ttl_expired_refetches():
|
||||
def test_get_blog_posts_local_env_var_skips_remote(monkeypatch):
|
||||
monkeypatch.setenv("LITELLM_LOCAL_BLOG_POSTS", "true")
|
||||
with patch("litellm.litellm_core_utils.get_blog_posts.httpx.get") as mock_get:
|
||||
posts = get_blog_posts()
|
||||
posts = get_blog_posts(url=litellm.blog_posts_url)
|
||||
mock_get.assert_not_called()
|
||||
assert isinstance(posts, list)
|
||||
assert len(posts) > 0
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"dev:webpack": "next dev --webpack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user