mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
Merge 4963ce552a into 2d2290067c
This commit is contained in:
commit
bc611ac432
4 changed files with 79 additions and 2 deletions
|
|
@ -536,7 +536,7 @@ class PassThroughEndpointLogging:
|
|||
**kwargs,
|
||||
)
|
||||
return
|
||||
elif self.is_langfuse_route(url_route):
|
||||
elif self.is_langfuse_route(url_route, custom_llm_provider):
|
||||
# Don't log langfuse pass-through requests
|
||||
return
|
||||
elif self.is_transcribe_route(custom_llm_provider) and TranscribePassthroughLoggingHandler.is_priced_job_start(
|
||||
|
|
@ -646,7 +646,9 @@ class PassThroughEndpointLogging:
|
|||
def is_openrouter_decisions_route(self, url_route: str, custom_llm_provider: str | None) -> bool:
|
||||
return custom_llm_provider == "openrouter" and urlparse(url_route).path.endswith("/alpha/decisions")
|
||||
|
||||
def is_langfuse_route(self, url_route: str):
|
||||
def is_langfuse_route(self, url_route: str, custom_llm_provider: str | None = None):
|
||||
if custom_llm_provider == "langfuse":
|
||||
return True
|
||||
parsed_url: Final = urlparse(url_route)
|
||||
for route in self.TRACKED_LANGFUSE_ROUTES:
|
||||
if route in parsed_url.path:
|
||||
|
|
|
|||
|
|
@ -223,6 +223,9 @@ async def langfuse_proxy_route(
|
|||
target=target_url,
|
||||
custom_headers=target_headers,
|
||||
query_params=dict(request.query_params),
|
||||
# tags the request so the success handler skips logging it as an LLM call;
|
||||
# the target URL path (LANGFUSE_HOST/api/public/...) has no `/langfuse/` to match on
|
||||
custom_llm_provider="langfuse",
|
||||
) # dynamically construct pass-through endpoint based on incoming path
|
||||
received_value: Final = await endpoint_func(
|
||||
request,
|
||||
|
|
|
|||
|
|
@ -518,6 +518,49 @@ def test_is_langfuse_route():
|
|||
assert handler.is_langfuse_route("") is False
|
||||
|
||||
|
||||
def test_is_langfuse_route_matches_langfuse_provider_on_target_url():
|
||||
"""
|
||||
The /langfuse/* proxy route forwards to LANGFUSE_HOST/api/public/..., so the
|
||||
target URL never contains `/langfuse/`; the route is identified by its provider.
|
||||
"""
|
||||
handler = PassThroughEndpointLogging()
|
||||
|
||||
assert handler.is_langfuse_route("https://cloud.langfuse.com/api/public/scores", "langfuse") is True
|
||||
assert handler.is_langfuse_route("https://langfuse.internal/api/public/otel/v1/traces", "langfuse") is True
|
||||
assert handler.is_langfuse_route("https://cloud.langfuse.com/api/public/scores") is False
|
||||
assert handler.is_langfuse_route("https://api.openai.com/v1/chat/completions", "openai") is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pass_through_async_success_handler_skips_langfuse_provider():
|
||||
log_dispatch = AsyncMock()
|
||||
handler = PassThroughEndpointLogging(log_dispatch=log_dispatch)
|
||||
logging_obj = MagicMock()
|
||||
logging_obj.model_call_details = {}
|
||||
target_url = "https://cloud.langfuse.com/api/public/scores"
|
||||
|
||||
result = await handler.pass_through_async_success_handler(
|
||||
httpx_response=httpx.Response(200, request=httpx.Request("POST", target_url)),
|
||||
response_body={"id": "score-1"},
|
||||
logging_obj=logging_obj,
|
||||
url_route=target_url,
|
||||
result="",
|
||||
start_time=datetime.now(),
|
||||
end_time=datetime.now(),
|
||||
cache_hit=False,
|
||||
request_body={},
|
||||
passthrough_logging_payload=PassthroughStandardLoggingPayload(
|
||||
url=target_url,
|
||||
request_body={},
|
||||
request_method="POST",
|
||||
),
|
||||
custom_llm_provider="langfuse",
|
||||
)
|
||||
|
||||
assert result is None
|
||||
log_dispatch.assert_not_called()
|
||||
|
||||
|
||||
def test_is_vertex_route_ignores_plain_predict_path_segment():
|
||||
"""
|
||||
Regression for LIT-4527: a custom (non-Vertex) passthrough URL whose path
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import base64
|
||||
import socket
|
||||
|
||||
import pytest
|
||||
|
|
@ -100,3 +101,31 @@ def test_dynamic_langfuse_proxy_target_preserves_host_header_for_http(monkeypatc
|
|||
|
||||
assert target_url == "http://8.8.8.8/api/public/projects"
|
||||
assert headers["Host"] == "langfuse.example"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_langfuse_proxy_route_tags_pass_through_as_langfuse_provider(monkeypatch):
|
||||
"""The provider tag is what lets the success handler skip logging these calls as LLM requests."""
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from litellm.proxy.vertex_ai_endpoints import langfuse_endpoints
|
||||
|
||||
monkeypatch.setenv("LANGFUSE_HOST", "https://langfuse.example.com")
|
||||
monkeypatch.setenv("LANGFUSE_PUBLIC_KEY", "global-public")
|
||||
monkeypatch.setenv("LANGFUSE_SECRET_KEY", "global-secret")
|
||||
monkeypatch.setattr(langfuse_endpoints, "user_api_key_auth", AsyncMock(return_value=MagicMock()))
|
||||
monkeypatch.setattr(langfuse_endpoints, "get_dynamic_logging_metadata", MagicMock(return_value=None))
|
||||
endpoint_func = AsyncMock(return_value={"ok": True})
|
||||
create_route = MagicMock(return_value=endpoint_func)
|
||||
monkeypatch.setattr(langfuse_endpoints, "create_pass_through_route", create_route)
|
||||
|
||||
request = MagicMock()
|
||||
request.headers = {"Authorization": "Basic " + base64.b64encode(b"x:sk-virtual-key").decode()}
|
||||
request.query_params = {}
|
||||
|
||||
await langfuse_endpoints.langfuse_proxy_route(
|
||||
endpoint="api/public/scores", request=request, fastapi_response=MagicMock()
|
||||
)
|
||||
|
||||
assert create_route.call_args.kwargs["custom_llm_provider"] == "langfuse"
|
||||
assert create_route.call_args.kwargs["target"] == "https://langfuse.example.com/api/public/scores"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue