diff --git a/litellm/llms/github_copilot/authenticator.py b/litellm/llms/github_copilot/authenticator.py index 8ec9cd544f..7d7ef522a4 100644 --- a/litellm/llms/github_copilot/authenticator.py +++ b/litellm/llms/github_copilot/authenticator.py @@ -132,6 +132,23 @@ class Authenticator: status_code=401, ) + def get_api_base(self) -> Optional[str]: + """ + Get the API endpoint from the api-key.json file. + + Returns: + Optional[str]: The GitHub Copilot API endpoint, or None if not found. + """ + try: + with open(self.api_key_file, "r") as f: + api_key_info = json.load(f) + endpoints = api_key_info.get("endpoints", {}) + api_endpoint = endpoints.get("api") + return api_endpoint + except (IOError, json.JSONDecodeError, KeyError) as e: + verbose_logger.warning(f"Error reading API endpoint from file: {str(e)}") + return None + def _refresh_api_key(self) -> Dict[str, Any]: """ Refresh the API key using the access token. diff --git a/litellm/llms/github_copilot/chat/transformation.py b/litellm/llms/github_copilot/chat/transformation.py index b0aaaf9a51..5f821bd9f5 100644 --- a/litellm/llms/github_copilot/chat/transformation.py +++ b/litellm/llms/github_copilot/chat/transformation.py @@ -25,7 +25,7 @@ class GithubCopilotConfig(OpenAIConfig): api_key: Optional[str], custom_llm_provider: str, ) -> Tuple[Optional[str], Optional[str], str]: - api_base = self.GITHUB_COPILOT_API_BASE + dynamic_api_base = self.authenticator.get_api_base() or self.GITHUB_COPILOT_API_BASE try: dynamic_api_key = self.authenticator.get_api_key() except GetAPIKeyError as e: @@ -34,7 +34,7 @@ class GithubCopilotConfig(OpenAIConfig): llm_provider=custom_llm_provider, message=str(e), ) - return api_base, dynamic_api_key, custom_llm_provider + return dynamic_api_base, dynamic_api_key, custom_llm_provider def _transform_messages( self, diff --git a/tests/test_litellm/llms/github_copilot/test_github_copilot_authenticator.py b/tests/test_litellm/llms/github_copilot/test_github_copilot_authenticator.py index 65413d46b9..c6ae2b9c4e 100644 --- a/tests/test_litellm/llms/github_copilot/test_github_copilot_authenticator.py +++ b/tests/test_litellm/llms/github_copilot/test_github_copilot_authenticator.py @@ -178,3 +178,14 @@ class TestGitHubCopilotAuthenticator: authenticator._get_device_code.assert_called_once() authenticator._poll_for_access_token.assert_called_once_with("mock-device-code") mock_print.assert_called_once() + + def test_get_api_base_from_file(self, authenticator): + """Test retrieving the API base endpoint from a file.""" + mock_api_key_data = json.dumps({ + "token": "mock-api-key", + "expires_at": (datetime.now() + timedelta(hours=1)).timestamp(), + "endpoints": {"api": "https://api.enterprise.githubcopilot.com"} + }) + with patch("builtins.open", mock_open(read_data=mock_api_key_data)): + api_base = authenticator.get_api_base() + assert api_base == "https://api.enterprise.githubcopilot.com" diff --git a/tests/test_litellm/llms/github_copilot/test_github_copilot_transformation.py b/tests/test_litellm/llms/github_copilot/test_github_copilot_transformation.py index 9672f045df..d9afbff53a 100644 --- a/tests/test_litellm/llms/github_copilot/test_github_copilot_transformation.py +++ b/tests/test_litellm/llms/github_copilot/test_github_copilot_transformation.py @@ -40,6 +40,8 @@ def test_github_copilot_config_get_openai_compatible_provider_info(): mock_api_key = "gh.test-key-123456789" config.authenticator = MagicMock() config.authenticator.get_api_key.return_value = mock_api_key + # Test with dynamic endpoint + config.authenticator.get_api_base.return_value = "https://api.enterprise.githubcopilot.com" # Test with default values model = "github_copilot/gpt-4" @@ -54,10 +56,24 @@ def test_github_copilot_config_get_openai_compatible_provider_info(): custom_llm_provider="github_copilot", ) - assert api_base == "https://api.githubcopilot.com/" + assert api_base == "https://api.enterprise.githubcopilot.com" assert dynamic_api_key == mock_api_key assert custom_llm_provider == "github_copilot" + # Test fallback to default if no dynamic endpoint + config.authenticator.get_api_base.return_value = None + ( + api_base, + dynamic_api_key, + custom_llm_provider, + ) = config._get_openai_compatible_provider_info( + model=model, + api_base=None, + api_key=None, + custom_llm_provider="github_copilot", + ) + assert api_base == "https://api.githubcopilot.com/" + # Test with authentication failure config.authenticator.get_api_key.side_effect = GetAPIKeyError( message="Failed to get API key",