Skip to content

Commit 1701c48

Browse files
feat(langfuse_endpoints.py): support langfuse pass through endpoints by default
1 parent 0ce476a commit 1701c48

File tree

3 files changed

+122
-1
lines changed

3 files changed

+122
-1
lines changed

litellm/proxy/proxy_server.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ def generate_feedback_box():
233233
from litellm.proxy.vertex_ai_endpoints.google_ai_studio_endpoints import (
234234
router as gemini_router,
235235
)
236+
from litellm.proxy.vertex_ai_endpoints.langfuse_endpoints import (
237+
router as langfuse_router,
238+
)
236239
from litellm.proxy.vertex_ai_endpoints.vertex_endpoints import router as vertex_router
237240
from litellm.proxy.vertex_ai_endpoints.vertex_endpoints import set_default_vertex_config
238241
from litellm.router import (
@@ -9738,6 +9741,7 @@ def cleanup_router_config_variables():
97389741
app.include_router(fine_tuning_router)
97399742
app.include_router(vertex_router)
97409743
app.include_router(gemini_router)
9744+
app.include_router(langfuse_router)
97419745
app.include_router(pass_through_router)
97429746
app.include_router(health_router)
97439747
app.include_router(key_management_router)

litellm/proxy/vertex_ai_endpoints/google_ai_studio_endpoints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
What is this?
33
4-
Google AI Studio Pass-Through Endpoints
4+
Provider-specific Pass-Through Endpoints
55
"""
66

77
"""
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
"""
2+
What is this?
3+
4+
Logging Pass-Through Endpoints
5+
"""
6+
7+
"""
8+
1. Create pass-through endpoints for any LITELLM_BASE_URL/langfuse/<endpoint> map to LANGFUSE_BASE_URL/<endpoint>
9+
"""
10+
11+
import ast
12+
import asyncio
13+
import base64
14+
import traceback
15+
from base64 import b64encode
16+
from datetime import datetime, timedelta, timezone
17+
from typing import List, Optional
18+
from urllib.parse import urlencode
19+
20+
import fastapi
21+
import httpx
22+
from fastapi import (
23+
APIRouter,
24+
Depends,
25+
File,
26+
Form,
27+
Header,
28+
HTTPException,
29+
Request,
30+
Response,
31+
UploadFile,
32+
status,
33+
)
34+
from starlette.datastructures import QueryParams
35+
36+
import litellm
37+
from litellm._logging import verbose_proxy_logger
38+
from litellm.batches.main import FileObject
39+
from litellm.fine_tuning.main import vertex_fine_tuning_apis_instance
40+
from litellm.proxy._types import *
41+
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
42+
from litellm.proxy.pass_through_endpoints.pass_through_endpoints import (
43+
create_pass_through_route,
44+
)
45+
46+
router = APIRouter()
47+
default_vertex_config = None
48+
49+
50+
def create_request_copy(request: Request):
51+
return {
52+
"method": request.method,
53+
"url": str(request.url),
54+
"headers": dict(request.headers),
55+
"cookies": request.cookies,
56+
"query_params": dict(request.query_params),
57+
}
58+
59+
60+
@router.api_route("/langfuse/{endpoint:path}", methods=["GET", "POST", "PUT", "DELETE"])
61+
async def langfuse_proxy_route(
62+
endpoint: str,
63+
request: Request,
64+
fastapi_response: Response,
65+
):
66+
## CHECK FOR LITELLM API KEY IN THE QUERY PARAMS - ?..key=LITELLM_API_KEY
67+
api_key = request.headers.get("Authorization") or ""
68+
69+
## decrypt base64 hash
70+
api_key = api_key.replace("Basic ", "")
71+
72+
decoded_bytes = base64.b64decode(api_key)
73+
decoded_str = decoded_bytes.decode("utf-8")
74+
api_key = decoded_str.split(":")[1]
75+
76+
user_api_key_dict = await user_api_key_auth(
77+
request=request, api_key="Bearer {}".format(api_key)
78+
)
79+
80+
base_target_url = os.getenv("LANGFUSE_HOST", "https://cloud.langfuse.com")
81+
if not (
82+
base_target_url.startswith("http://") or base_target_url.startswith("https://")
83+
):
84+
# add http:// if unset, assume communicating over private network - e.g. render
85+
base_target_url = "http://" + base_target_url
86+
87+
encoded_endpoint = httpx.URL(endpoint).path
88+
89+
# Ensure endpoint starts with '/' for proper URL construction
90+
if not encoded_endpoint.startswith("/"):
91+
encoded_endpoint = "/" + encoded_endpoint
92+
93+
# Construct the full target URL using httpx
94+
base_url = httpx.URL(base_target_url)
95+
updated_url = base_url.copy_with(path=encoded_endpoint)
96+
97+
# Add or update query parameters
98+
langfuse_public_key = litellm.utils.get_secret(secret_name="LANGFUSE_PUBLIC_KEY")
99+
langfuse_secret_key = litellm.utils.get_secret(secret_name="LANGFUSE_SECRET_KEY")
100+
101+
langfuse_combined_key = "Basic " + b64encode(
102+
f"{langfuse_public_key}:{langfuse_secret_key}".encode("utf-8")
103+
).decode("ascii")
104+
105+
## CREATE PASS-THROUGH
106+
endpoint_func = create_pass_through_route(
107+
endpoint=endpoint,
108+
target=str(updated_url),
109+
custom_headers={"Authorization": langfuse_combined_key},
110+
) # dynamically construct pass-through endpoint based on incoming path
111+
received_value = await endpoint_func(
112+
request,
113+
fastapi_response,
114+
user_api_key_dict,
115+
)
116+
117+
return received_value

0 commit comments

Comments
 (0)