mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
feat: add GET /public/litellm_blog_posts endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7e5c63c134
commit
021a9097c4
2 changed files with 106 additions and 0 deletions
|
|
@ -4,6 +4,12 @@ from typing import List
|
|||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from litellm.litellm_core_utils.get_blog_posts import (
|
||||
BlogPost,
|
||||
BlogPostsResponse,
|
||||
GetBlogPosts,
|
||||
get_blog_posts,
|
||||
)
|
||||
from litellm.proxy._types import CommonProxyErrors
|
||||
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
|
||||
from litellm.types.agents import AgentCard
|
||||
|
|
@ -193,6 +199,27 @@ async def get_litellm_model_cost_map():
|
|||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/public/litellm_blog_posts",
|
||||
tags=["public"],
|
||||
response_model=BlogPostsResponse,
|
||||
)
|
||||
async def get_litellm_blog_posts():
|
||||
"""
|
||||
Public endpoint to get the latest LiteLLM blog posts.
|
||||
|
||||
Fetches from GitHub with a 1-hour in-process cache.
|
||||
Falls back to the bundled local backup on any failure.
|
||||
"""
|
||||
try:
|
||||
posts_data = get_blog_posts()
|
||||
except Exception:
|
||||
posts_data = GetBlogPosts.load_local_blog_posts()
|
||||
|
||||
posts = [BlogPost(**p) for p in posts_data[:5]]
|
||||
return BlogPostsResponse(posts=posts)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/public/agents/fields",
|
||||
tags=["public", "[beta] Agents"],
|
||||
|
|
|
|||
79
tests/proxy_unit_tests/test_blog_posts_endpoint.py
Normal file
79
tests/proxy_unit_tests/test_blog_posts_endpoint.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
"""Tests for the /public/litellm_blog_posts endpoint."""
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
SAMPLE_POSTS = [
|
||||
{
|
||||
"title": "Test Post",
|
||||
"description": "A test post.",
|
||||
"date": "2026-01-01",
|
||||
"url": "https://www.litellm.ai/blog/test",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""Create a TestClient with just the public_endpoints router."""
|
||||
from fastapi import FastAPI
|
||||
|
||||
from litellm.proxy.public_endpoints.public_endpoints import router
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
def test_get_blog_posts_returns_response_shape(client):
|
||||
with patch(
|
||||
"litellm.proxy.public_endpoints.public_endpoints.get_blog_posts",
|
||||
return_value=SAMPLE_POSTS,
|
||||
):
|
||||
response = client.get("/public/litellm_blog_posts")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "posts" in data
|
||||
assert len(data["posts"]) == 1
|
||||
post = data["posts"][0]
|
||||
assert post["title"] == "Test Post"
|
||||
assert post["description"] == "A test post."
|
||||
assert post["date"] == "2026-01-01"
|
||||
assert post["url"] == "https://www.litellm.ai/blog/test"
|
||||
|
||||
|
||||
def test_get_blog_posts_limits_to_five(client):
|
||||
"""Endpoint returns at most 5 posts."""
|
||||
many_posts = [
|
||||
{
|
||||
"title": f"Post {i}",
|
||||
"description": "desc",
|
||||
"date": "2026-01-01",
|
||||
"url": f"https://www.litellm.ai/blog/{i}",
|
||||
}
|
||||
for i in range(10)
|
||||
]
|
||||
|
||||
with patch(
|
||||
"litellm.proxy.public_endpoints.public_endpoints.get_blog_posts",
|
||||
return_value=many_posts,
|
||||
):
|
||||
response = client.get("/public/litellm_blog_posts")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert len(response.json()["posts"]) == 5
|
||||
|
||||
|
||||
def test_get_blog_posts_returns_local_backup_on_failure(client):
|
||||
"""Endpoint returns local backup (non-empty list) when fetcher fails."""
|
||||
with patch(
|
||||
"litellm.proxy.public_endpoints.public_endpoints.get_blog_posts",
|
||||
side_effect=Exception("fetch failed"),
|
||||
):
|
||||
response = client.get("/public/litellm_blog_posts")
|
||||
|
||||
# Should not 500 — returns local backup
|
||||
assert response.status_code == 200
|
||||
assert "posts" in response.json()
|
||||
Loading…
Add table
Reference in a new issue