diff --git a/codeaide/ui/chat_window.py b/codeaide/ui/chat_window.py index 6a1b418..de388f8 100644 --- a/codeaide/ui/chat_window.py +++ b/codeaide/ui/chat_window.py @@ -3,6 +3,7 @@ import traceback from PyQt5.QtCore import Qt, QTimer +from PyQt5.QtGui import QColor from PyQt5.QtWidgets import ( QApplication, QHBoxLayout, @@ -46,6 +47,8 @@ def __init__(self, chat_handler): self.add_to_chat("AI", INITIAL_MESSAGE) self.check_api_key() + self.input_text.setTextColor(QColor(CHAT_WINDOW_FG)) + signal.signal(signal.SIGINT, self.sigint_handler) self.timer = QTimer() self.timer.start(500) @@ -67,9 +70,14 @@ def setup_ui(self): self.input_text = QTextEdit(self) self.input_text.setStyleSheet( - f"background-color: {CHAT_WINDOW_BG}; color: {USER_MESSAGE_COLOR}; border: 1px solid #ccc; padding: 5px;" + f""" + background-color: {CHAT_WINDOW_BG}; + color: {CHAT_WINDOW_FG}; + border: 1px solid #ccc; + padding: 5px; + """ ) - self.input_text.setAcceptRichText(True) + self.input_text.setAcceptRichText(False) # Add this line self.input_text.setFont(general_utils.set_font(USER_FONT)) self.input_text.setFixedHeight(100) self.input_text.textChanged.connect(self.on_modify) diff --git a/codeaide/utils/api_utils.py b/codeaide/utils/api_utils.py index 9abe89e..bb269f1 100644 --- a/codeaide/utils/api_utils.py +++ b/codeaide/utils/api_utils.py @@ -25,17 +25,13 @@ def get_api_client(service="anthropic"): ) ) api_key = auto_config(f"{service.upper()}_API_KEY", default=None) - if api_key is None: - raise MissingAPIKeyException(service) + if api_key is None or api_key.strip() == "": + return None # Return None if API key is missing or empty - if service == "anthropic": + if service.lower() == "anthropic": return anthropic.Anthropic(api_key=api_key) - # Add more elif blocks here for other API services else: raise ValueError(f"Unsupported service: {service}") - except MissingAPIKeyException: - # If the API key is missing, return None instead of raising the exception - return None except Exception as e: print(f"Error initializing {service.capitalize()} API client: {str(e)}") return None @@ -77,29 +73,15 @@ def save_api_key(service, api_key): def send_api_request(client, conversation_history, max_tokens=MAX_TOKENS): system_prompt = SYSTEM_PROMPT try: - print(f"\n\n{'='*50}\n") - print( - f"Sending API request. The max tokens is {max_tokens}. Here's the conversation history:" - ) - for message in conversation_history: - print(f"{message['role']}: {message['content']}") - print("\n") - response = client.messages.create( model=AI_MODEL, max_tokens=max_tokens, messages=conversation_history, system=system_prompt, ) - - content = response.content[0].text if response.content else "" - - if not content: - print("Warning: Received empty response from API") + if not response.content: return None - return response - except Exception as e: print(f"Error in API request: {str(e)}") return None @@ -125,19 +107,17 @@ def parse_response(response): return None, None, None, None, None, None -def check_api_connection(service="anthropic"): - client = get_api_client(service) +def check_api_connection(): + client = get_api_client() + if client is None: + return False, "API key is missing or invalid" try: - if service == "anthropic": - response = client.messages.create( - model=AI_MODEL, - max_tokens=100, - messages=[{"role": "user", "content": "Hi, are we communicating?"}], - ) - return True, response.content[0].text.strip() - # Add more elif blocks here for other API services - else: - raise ValueError(f"Unsupported service: {service}") + response = client.messages.create( + model=AI_MODEL, + max_tokens=100, + messages=[{"role": "user", "content": "Hi Claude, are we communicating?"}], + ) + return True, response.content[0].text.strip() except Exception as e: return False, str(e) diff --git a/tests/utils/test_api_utils.py b/tests/utils/test_api_utils.py index 65421ed..ba288f5 100644 --- a/tests/utils/test_api_utils.py +++ b/tests/utils/test_api_utils.py @@ -1,14 +1,17 @@ import json from collections import namedtuple -from unittest.mock import Mock +from unittest.mock import Mock, patch import pytest +import anthropic from anthropic import APIError from codeaide.utils.api_utils import ( check_api_connection, parse_response, send_api_request, + get_api_client, + MissingAPIKeyException, ) from codeaide.utils.constants import AI_MODEL, MAX_TOKENS, SYSTEM_PROMPT @@ -23,6 +26,49 @@ ] +@pytest.fixture +def mock_anthropic_client(): + with patch("anthropic.Anthropic") as mock_anthropic: + mock_client = Mock() + mock_messages = Mock() + mock_client.messages = mock_messages + mock_anthropic.return_value = mock_client + yield mock_client + + +class TestGetApiClient: + def test_get_api_client_success(self, monkeypatch): + monkeypatch.setenv("ANTHROPIC_API_KEY", "test_key") + client = get_api_client() + assert client is not None + assert hasattr( + client, "messages" + ) # Check for a common attribute of Anthropic client + + @patch("codeaide.utils.api_utils.AutoConfig") + def test_get_api_client_missing_key(self, mock_auto_config, monkeypatch): + mock_config = Mock() + mock_config.return_value = None + mock_auto_config.return_value = mock_config + monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False) + client = get_api_client() + assert client is None + + def test_get_api_client_empty_key(self, monkeypatch): + monkeypatch.setenv("ANTHROPIC_API_KEY", "") + client = get_api_client() + assert client is None + + @patch("codeaide.utils.api_utils.AutoConfig") + def test_get_api_client_unsupported_service(self, mock_auto_config): + mock_config = Mock() + mock_config.return_value = "dummy_key" + mock_auto_config.return_value = mock_config + + result = get_api_client("unsupported_service") + assert result is None + + class TestSendAPIRequest: def test_send_api_request_success(self, mock_anthropic_client): conversation_history = [{"role": "user", "content": "Hello, Claude!"}] @@ -30,7 +76,7 @@ def test_send_api_request_success(self, mock_anthropic_client): mock_response.content = [Mock(text="Hello! How can I assist you today?")] mock_anthropic_client.messages.create.return_value = mock_response - result = send_api_request(conversation_history) + result = send_api_request(mock_anthropic_client, conversation_history) mock_anthropic_client.messages.create.assert_called_once_with( model=AI_MODEL, @@ -46,9 +92,9 @@ def test_send_api_request_empty_response(self, mock_anthropic_client): mock_response.content = [] mock_anthropic_client.messages.create.return_value = mock_response - result = send_api_request(conversation_history) + result = send_api_request(mock_anthropic_client, conversation_history) - assert result == (None, True) + assert result is None def test_send_api_request_api_error(self, mock_anthropic_client): conversation_history = [{"role": "user", "content": "Hello, Claude!"}] @@ -59,9 +105,9 @@ def test_send_api_request_api_error(self, mock_anthropic_client): body={"error": {"message": "API Error"}}, ) - result = send_api_request(conversation_history) + result = send_api_request(mock_anthropic_client, conversation_history) - assert result == (None, True) + assert result is None def test_send_api_request_custom_max_tokens(self, mock_anthropic_client): conversation_history = [{"role": "user", "content": "Hello, Claude!"}] @@ -70,7 +116,9 @@ def test_send_api_request_custom_max_tokens(self, mock_anthropic_client): mock_response.content = [Mock(text="Hello! How can I assist you today?")] mock_anthropic_client.messages.create.return_value = mock_response - result = send_api_request(conversation_history, max_tokens=custom_max_tokens) + result = send_api_request( + mock_anthropic_client, conversation_history, max_tokens=custom_max_tokens + ) mock_anthropic_client.messages.create.assert_called_once_with( model=AI_MODEL, @@ -191,18 +239,35 @@ def test_parse_response_malformed_json(self): class TestAPIConnection: - def check_api_connection_success(self, mock_anthropic_client): + @patch("codeaide.utils.api_utils.get_api_client") + def test_check_api_connection_success(self, mock_get_api_client): + mock_client = Mock() mock_response = Mock() mock_response.content = [Mock(text="Yes, we are communicating.")] - mock_anthropic_client.messages.create.return_value = mock_response + mock_client.messages.create.return_value = mock_response + mock_get_api_client.return_value = mock_client + result = check_api_connection() + assert result[0] == True assert result[1] == "Yes, we are communicating." - def check_api_connection_failure(self, mock_anthropic_client): - mock_anthropic_client.messages.create.side_effect = Exception( - "Connection failed" - ) + @patch("codeaide.utils.api_utils.get_api_client") + def test_check_api_connection_failure(self, mock_get_api_client): + mock_client = Mock() + mock_client.messages.create.side_effect = Exception("Connection failed") + mock_get_api_client.return_value = mock_client + result = check_api_connection() + assert result[0] == False assert "Connection failed" in result[1] + + @patch("codeaide.utils.api_utils.get_api_client") + def test_check_api_connection_missing_key(self, mock_get_api_client): + mock_get_api_client.return_value = None + + result = check_api_connection() + + assert result[0] == False + assert "API key is missing or invalid" in result[1]