mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
fix(proxy): restore spend logging for /cursor/chat/completions (#30126)
The /cursor/chat/completions route processes requests through the Responses API pipeline, but _get_metadata_variable_name did not treat it as a litellm_metadata route. Proxy auth metadata therefore landed in the user-facing metadata param, so the spend tracking callback saw user_api_key=None and silently skipped the SpendLogs write. Add the route to LITELLM_METADATA_ROUTES and cover it with regression tests.
This commit is contained in:
parent
530c0b2326
commit
efcb0f8b02
3 changed files with 73 additions and 0 deletions
|
|
@ -110,6 +110,7 @@ LITELLM_METADATA_ROUTES = (
|
|||
"/v1/messages",
|
||||
"responses",
|
||||
"files",
|
||||
"/cursor/chat/completions",
|
||||
)
|
||||
|
||||
_UNTRUSTED_ROOT_CONTROL_FIELDS = (
|
||||
|
|
|
|||
|
|
@ -198,6 +198,74 @@ class TestResponsesAPIEndpoints(unittest.TestCase):
|
|||
assert response_cost_value == pytest.approx(0.0005, abs=1e-10)
|
||||
|
||||
|
||||
class TestCursorChatCompletionsSpendTracking:
|
||||
@patch("litellm.proxy.proxy_server.llm_router")
|
||||
def test_proxy_auth_metadata_forwarded_as_litellm_metadata(self, mock_router):
|
||||
"""
|
||||
/cursor/chat/completions runs through the Responses API pipeline, so
|
||||
proxy auth metadata (user_api_key etc.) must be sent as
|
||||
`litellm_metadata`. If it lands in the user-facing `metadata` param
|
||||
instead, the spend tracking callback sees user_api_key=None and
|
||||
silently skips the SpendLogs write. Regression test for #30126.
|
||||
"""
|
||||
from openai.types.responses import ResponseOutputMessage, ResponseOutputText
|
||||
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
|
||||
from litellm.types.llms.openai import ResponsesAPIResponse
|
||||
|
||||
mock_router.aresponses = AsyncMock(
|
||||
return_value=ResponsesAPIResponse(
|
||||
id="resp_cursor123",
|
||||
created_at=1234567890,
|
||||
model="gpt-4o",
|
||||
object="response",
|
||||
output=[
|
||||
ResponseOutputMessage(
|
||||
id="msg_cursor123",
|
||||
type="message",
|
||||
role="assistant",
|
||||
status="completed",
|
||||
content=[
|
||||
ResponseOutputText(
|
||||
type="output_text",
|
||||
text="Hello from Cursor!",
|
||||
annotations=[],
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth(
|
||||
api_key="hashed-test-key",
|
||||
user_id="test-user",
|
||||
request_route="/cursor/chat/completions",
|
||||
)
|
||||
try:
|
||||
client = TestClient(app)
|
||||
response = client.post(
|
||||
"/cursor/chat/completions",
|
||||
json={
|
||||
"model": "gpt-4o",
|
||||
"input": [{"role": "user", "content": "Hello"}],
|
||||
},
|
||||
headers={"Authorization": "Bearer sk-1234"},
|
||||
)
|
||||
finally:
|
||||
app.dependency_overrides.pop(user_api_key_auth, None)
|
||||
|
||||
assert response.status_code == 200
|
||||
mock_router.aresponses.assert_called_once()
|
||||
request_kwargs = mock_router.aresponses.call_args.kwargs
|
||||
litellm_metadata = request_kwargs.get("litellm_metadata") or {}
|
||||
assert litellm_metadata.get("user_api_key") == "hashed-test-key"
|
||||
assert litellm_metadata.get("user_api_key_user_id") == "test-user"
|
||||
# proxy internals must not leak into the user-facing metadata param
|
||||
assert "user_api_key" not in (request_kwargs.get("metadata") or {})
|
||||
|
||||
|
||||
import json
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,10 @@ class TestGetMetadataVariableName:
|
|||
request = self._make_request("/v1/files")
|
||||
assert _get_metadata_variable_name(request) == "litellm_metadata"
|
||||
|
||||
def test_returns_litellm_metadata_for_cursor_chat_completions(self):
|
||||
request = self._make_request("/cursor/chat/completions")
|
||||
assert _get_metadata_variable_name(request) == "litellm_metadata"
|
||||
|
||||
def test_returns_metadata_for_chat_completions(self):
|
||||
request = self._make_request("/chat/completions")
|
||||
assert _get_metadata_variable_name(request) == "metadata"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue