mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-09 22:31:41 +00:00
fix(github-copilot): preserve legacy endpoint routing
This commit is contained in:
parent
58d0e1010b
commit
23f01440db
2 changed files with 70 additions and 1 deletions
|
|
@ -4,6 +4,8 @@ import time
|
|||
from typing import Final
|
||||
|
||||
import httpx
|
||||
from pydantic import TypeAdapter, ValidationError
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.llms.custom_httpx.http_handler import _get_httpx_client
|
||||
|
|
@ -15,6 +17,17 @@ from .common_utils import (
|
|||
get_copilot_auth_headers,
|
||||
)
|
||||
|
||||
|
||||
class _LegacyCopilotEndpoints(TypedDict, total=False):
|
||||
api: str
|
||||
|
||||
|
||||
class _LegacyCopilotTokenCache(TypedDict, total=False):
|
||||
endpoints: _LegacyCopilotEndpoints
|
||||
|
||||
|
||||
_LEGACY_COPILOT_TOKEN_CACHE_ADAPTER = TypeAdapter(_LegacyCopilotTokenCache)
|
||||
|
||||
# Constants (default values — overridable via environment variables at call time)
|
||||
DEFAULT_GITHUB_CLIENT_ID: Final = "Iv1.b507a08c87ecfe98"
|
||||
DEFAULT_GITHUB_DEVICE_CODE_URL: Final = "https://github.com/login/device/code"
|
||||
|
|
@ -33,6 +46,14 @@ class Authenticator:
|
|||
self.token_dir,
|
||||
os.getenv("GITHUB_COPILOT_ACCESS_TOKEN_FILE", "access-token"),
|
||||
)
|
||||
self.legacy_api_key_file = os.path.join(
|
||||
self.token_dir,
|
||||
os.getenv("GITHUB_COPILOT_API_KEY_FILE", "api-key.json"),
|
||||
)
|
||||
if os.getenv("GITHUB_COPILOT_API_KEY_URL"):
|
||||
verbose_logger.warning(
|
||||
"GITHUB_COPILOT_API_KEY_URL is no longer used; LiteLLM sends the OAuth access token directly"
|
||||
)
|
||||
self._ensure_token_dir()
|
||||
|
||||
def get_access_token(self) -> str:
|
||||
|
|
@ -82,7 +103,18 @@ class Authenticator:
|
|||
)
|
||||
|
||||
def get_api_base(self) -> str | None:
|
||||
return os.getenv("GITHUB_COPILOT_API_BASE")
|
||||
configured_api_base = os.getenv("GITHUB_COPILOT_API_BASE")
|
||||
if configured_api_base:
|
||||
return configured_api_base
|
||||
try:
|
||||
with open(self.legacy_api_key_file, "r") as legacy_api_key_file:
|
||||
legacy_cache = _LEGACY_COPILOT_TOKEN_CACHE_ADAPTER.validate_json(legacy_api_key_file.read())
|
||||
except IOError:
|
||||
return None
|
||||
except ValidationError as e:
|
||||
verbose_logger.warning(f"Error reading legacy GitHub Copilot API endpoint: {str(e)}")
|
||||
return None
|
||||
return legacy_cache.get("endpoints", {}).get("api")
|
||||
|
||||
def _ensure_token_dir(self) -> None:
|
||||
"""Ensure the token directory exists."""
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class TestGitHubCopilotAuthenticator:
|
|||
auth = Authenticator()
|
||||
assert auth.token_dir.endswith("/github_copilot")
|
||||
assert auth.access_token_file.endswith("/access-token")
|
||||
assert auth.legacy_api_key_file.endswith("/api-key.json")
|
||||
mock_makedirs.assert_called_once()
|
||||
|
||||
def test_ensure_token_dir(self):
|
||||
|
|
@ -52,6 +53,42 @@ class TestGitHubCopilotAuthenticator:
|
|||
auth = Authenticator()
|
||||
mock_makedirs.assert_called_once_with(auth.token_dir, exist_ok=True)
|
||||
|
||||
def test_get_api_base_prefers_environment(self, authenticator):
|
||||
with (
|
||||
patch.dict(
|
||||
os.environ,
|
||||
{"GITHUB_COPILOT_API_BASE": "https://configured.githubcopilot.example"},
|
||||
clear=True,
|
||||
),
|
||||
patch("builtins.open", mock_open()) as mock_file,
|
||||
):
|
||||
assert authenticator.get_api_base() == "https://configured.githubcopilot.example"
|
||||
mock_file.assert_not_called()
|
||||
|
||||
def test_get_api_base_uses_legacy_endpoint(self, authenticator):
|
||||
legacy_cache = '{"token":"ignored","endpoints":{"api":"https://api.enterprise.githubcopilot.com"}}'
|
||||
with (
|
||||
patch.dict(os.environ, {}, clear=True),
|
||||
patch("builtins.open", mock_open(read_data=legacy_cache)),
|
||||
):
|
||||
assert authenticator.get_api_base() == "https://api.enterprise.githubcopilot.com"
|
||||
|
||||
def test_deprecated_api_key_url_warns(self):
|
||||
with (
|
||||
patch.dict(
|
||||
os.environ,
|
||||
{"GITHUB_COPILOT_API_KEY_URL": "https://deprecated.example.com/token"},
|
||||
clear=True,
|
||||
),
|
||||
patch("os.path.exists", return_value=True),
|
||||
patch("litellm.llms.github_copilot.authenticator.verbose_logger.warning") as mock_warning,
|
||||
):
|
||||
Authenticator()
|
||||
|
||||
mock_warning.assert_called_once_with(
|
||||
"GITHUB_COPILOT_API_KEY_URL is no longer used; LiteLLM sends the OAuth access token directly"
|
||||
)
|
||||
|
||||
def test_get_github_headers(self, authenticator):
|
||||
headers = authenticator._get_github_headers()
|
||||
assert headers == {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue