From 7e9836817051772983db8549bb653704415f73f2 Mon Sep 17 00:00:00 2001 From: Anna S <> Date: Sun, 6 Jul 2025 14:27:21 -0700 Subject: [PATCH 1/5] Issue #6 completed --- ai-backend/config/settings.py | 47 ++++++++++++------------ ai-backend/test_environment.py | 66 ++++++++++++++++++++++++++++++++++ ai-backend/test_openai.py | 38 ++++++++++++++++++++ 3 files changed, 128 insertions(+), 23 deletions(-) create mode 100644 ai-backend/test_environment.py create mode 100644 ai-backend/test_openai.py diff --git a/ai-backend/config/settings.py b/ai-backend/config/settings.py index 429748c..cc6c103 100644 --- a/ai-backend/config/settings.py +++ b/ai-backend/config/settings.py @@ -8,8 +8,8 @@ from typing import Any from dotenv import load_dotenv -from pydantic import Field, validator -from pydantic_settings import BaseSettings +from pydantic import Field, field_validator +from pydantic_settings import BaseSettings, SettingsConfigDict # Load environment variables from .env file load_dotenv() @@ -21,12 +21,12 @@ class Settings(BaseSettings): """Application settings loaded from environment variables with validation.""" # Required settings - openai_api_key: str = Field(..., min_length=20, description="OpenAI API key") - supabase_url: str = Field(..., description="Supabase project URL") - supabase_service_role_key: str = Field( + OPENAI_API_KEY: str = Field(..., min_length=20, description="OpenAI API key") + SUPABASE_URL: str = Field(..., description="Supabase project URL") + SUPABASE_SERVICE_ROLE_KEY: str = Field( ..., min_length=20, description="Supabase service role key" ) - rapidapi_key: str = Field( + RAPIDAPI_KEY: str = Field( ..., min_length=10, description="RapidAPI key for API-Football" ) @@ -56,39 +56,44 @@ class Settings(BaseSettings): description="API-Football base URL", ) - @validator("openai_api_key") - def validate_openai_key(cls, v: str) -> str: # noqa: N805 + @field_validator("OPENAI_API_KEY") + @classmethod + def validate_openai_key(cls, v: str) -> str: if not v or v == "your-openai-api-key" or v == "sk-...": raise ValueError("Valid OpenAI API key is required") if not v.startswith("sk-"): raise ValueError('OpenAI API key must start with "sk-"') return v - @validator("supabase_url") - def validate_supabase_url(cls, v: str) -> str: # noqa: N805 + @field_validator("SUPABASE_URL") + @classmethod + def validate_supabase_url(cls, v: str) -> str: if not v.startswith("https://"): raise ValueError("Supabase URL must be a valid HTTPS URL") if not v.endswith(".supabase.co"): raise ValueError("Supabase URL must end with .supabase.co") return v - @validator("environment") - def validate_environment(cls, v: str) -> str: # noqa: N805 + @field_validator("environment") + @classmethod + def validate_environment(cls, v: str) -> str: allowed = ["development", "staging", "production"] if v not in allowed: raise ValueError(f"Environment must be one of: {allowed}") return v - @validator("log_level") - def validate_log_level(cls, v: str) -> str: # noqa: N805 + @field_validator("log_level") + @classmethod + def validate_log_level(cls, v: str) -> str: allowed = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] v_upper = v.upper() if v_upper not in allowed: raise ValueError(f"Log level must be one of: {allowed}") return v_upper - @validator("log_format") - def validate_log_format(cls, v: str) -> str: # noqa: N805 + @field_validator("log_format") + @classmethod + def validate_log_format(cls, v: str) -> str: allowed = ["json", "text"] if v not in allowed: raise ValueError(f"Log format must be one of: {allowed}") @@ -110,13 +115,9 @@ def to_dict(self) -> dict[str, Any]: "api_football_base_url": self.api_football_base_url, } - class Config: - """Pydantic configuration for Settings model.""" - - env_file = ".env" - case_sensitive = True - # Allow extra fields for forward compatibility - extra = "ignore" + model_config = SettingsConfigDict( + env_file=".env", case_sensitive=True, extra="ignore" + ) # Global settings instance diff --git a/ai-backend/test_environment.py b/ai-backend/test_environment.py new file mode 100644 index 0000000..3567dfe --- /dev/null +++ b/ai-backend/test_environment.py @@ -0,0 +1,66 @@ +""" +Test script to verify all dependencies are properly installed +""" + +import sys + +print(f"Python version: {sys.version}") + +# Test core dependencies +try: + import openai + + print("✅ OpenAI package imported successfully") +except ImportError as e: + print(f"❌ OpenAI import failed: {e}") + +try: + from agents.researcher import ResearchAgent + + print("✅ OpenAI Agents package imported successfully") +except ImportError as e: + print(f"❌ OpenAI Agents import failed: {e}") + +try: + import fastapi + + print("✅ FastAPI package imported successfully") +except ImportError as e: + print(f"❌ FastAPI import failed: {e}") + +try: + from pydantic import BaseModel + + print("✅ Pydantic package imported successfully") +except ImportError as e: + print(f"❌ Pydantic import failed: {e}") + +try: + from supabase import create_client + + print("✅ Supabase package imported successfully") +except ImportError as e: + print(f"❌ Supabase import failed: {e}") + +try: + import aiohttp + + print("✅ Aiohttp package imported successfully") +except ImportError as e: + print(f"❌ Aiohttp import failed: {e}") + +try: + from dotenv import load_dotenv + + print("✅ Python-dotenv package imported successfully") +except ImportError as e: + print(f"❌ Python-dotenv import failed: {e}") + +try: + import structlog + + print("✅ Structlog package imported successfully") +except ImportError as e: + print(f"❌ Structlog import failed: {e}") + +print("\n🎉 Environment test completed!") diff --git a/ai-backend/test_openai.py b/ai-backend/test_openai.py new file mode 100644 index 0000000..46f810b --- /dev/null +++ b/ai-backend/test_openai.py @@ -0,0 +1,38 @@ +""" +Test OpenAI API connection +""" +import os +from dotenv import load_dotenv +import openai + +# Load environment variables +load_dotenv() + +# Set up OpenAI client +client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY")) + +def test_openai_connection(): + """Test basic OpenAI API connection""" + if not os.getenv("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY") == "your_openai_api_key_here": + print("⚠️ OpenAI API key not set. Skipping connection test.") + return + + try: + # Test with a simple completion + response = client.chat.completions.create( + model="gpt-4.1-nano", + messages=[ + {"role": "user", "content": "Say 'Hello from Sport Scribe AI!'"} + ], + max_tokens=50 + ) + + print("✅ OpenAI API connection successful!") + print(f"Response: {response.choices[0].message.content}") + + except Exception as e: + print(f"❌ OpenAI API connection failed: {e}") + +if __name__ == "__main__": + test_openai_connection() + From 54a72730cfc9c679b598cedcb053e3732a9ccef8 Mon Sep 17 00:00:00 2001 From: Anna S <> Date: Wed, 16 Jul 2025 16:59:28 -0700 Subject: [PATCH 2/5] moved to ai-backend/tests/ --- ai-backend/tests/test_environment.py | 48 ++++++++++++++++++++++++++++ ai-backend/tests/test_openai.py | 37 +++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 ai-backend/tests/test_environment.py create mode 100644 ai-backend/tests/test_openai.py diff --git a/ai-backend/tests/test_environment.py b/ai-backend/tests/test_environment.py new file mode 100644 index 0000000..7898573 --- /dev/null +++ b/ai-backend/tests/test_environment.py @@ -0,0 +1,48 @@ +"""Test script to verify all dependencies are properly installed.""" + +import sys + +print(f"Python version: {sys.version}") + +# Test core dependencies +try: + print("✅ OpenAI package imported successfully") +except ImportError as e: + print(f"❌ OpenAI import failed: {e}") + +try: + print("✅ OpenAI Agents package imported successfully") +except ImportError as e: + print(f"❌ OpenAI Agents import failed: {e}") + +try: + print("✅ FastAPI package imported successfully") +except ImportError as e: + print(f"❌ FastAPI import failed: {e}") + +try: + print("✅ Pydantic package imported successfully") +except ImportError as e: + print(f"❌ Pydantic import failed: {e}") + +try: + print("✅ Supabase package imported successfully") +except ImportError as e: + print(f"❌ Supabase import failed: {e}") + +try: + print("✅ Aiohttp package imported successfully") +except ImportError as e: + print(f"❌ Aiohttp import failed: {e}") + +try: + print("✅ Python-dotenv package imported successfully") +except ImportError as e: + print(f"❌ Python-dotenv import failed: {e}") + +try: + print("✅ Structlog package imported successfully") +except ImportError as e: + print(f"❌ Structlog import failed: {e}") + +print("\n🎉 Environment test completed!") diff --git a/ai-backend/tests/test_openai.py b/ai-backend/tests/test_openai.py new file mode 100644 index 0000000..a51c6d2 --- /dev/null +++ b/ai-backend/tests/test_openai.py @@ -0,0 +1,37 @@ +"""Test OpenAI API connection.""" + +import os + +import openai +from dotenv import load_dotenv + +# Load environment variables +load_dotenv() + +# Set up OpenAI client +client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY")) + + +def test_openai_connection() -> None: + """Test basic OpenAI API connection.""" + if not os.getenv("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY") == "your_openai_api_key_here": + print("⚠️ OpenAI API key not set. Skipping connection test.") + return + + try: + # Test with a simple completion + response = client.chat.completions.create( + model="gpt-4.1-nano", + messages=[{"role": "user", "content": "Say 'Hello from Sport Scribe AI!'"}], + max_tokens=50, + ) + + print("✅ OpenAI API connection successful!") + print(f"Response: {response.choices[0].message.content}") + + except Exception as e: + print(f"❌ OpenAI API connection failed: {e}") + + +if __name__ == "__main__": + test_openai_connection() From 44c5914006cbd29d314455a3852a59ce9ce21c38 Mon Sep 17 00:00:00 2001 From: Anna S <> Date: Wed, 16 Jul 2025 17:00:27 -0700 Subject: [PATCH 3/5] Completed Issue-7 --- ai-backend/tests/test_quality_tools.py | 66 ++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 ai-backend/tests/test_quality_tools.py diff --git a/ai-backend/tests/test_quality_tools.py b/ai-backend/tests/test_quality_tools.py new file mode 100644 index 0000000..44a1d79 --- /dev/null +++ b/ai-backend/tests/test_quality_tools.py @@ -0,0 +1,66 @@ +"""Sample code to test quality tools.""" + +import asyncio + + +class FootballDataProcessor: + """Process football data for AI analysis.""" + + def __init__(self, league: str) -> None: + """Initialize processor with league.""" + self.league = league + self.processed_games: list[dict[str, str]] = [] + + def process_game_data( + self, home_team: str, away_team: str, score: str | None = None + ) -> dict[str, str]: + """Process individual game data. + + Args: + home_team: Name of home team + away_team: Name of away team + score: Optional match score + + Returns: + Processed game data dictionary + """ + game_data = { + "home_team": home_team.strip(), + "away_team": away_team.strip(), + "league": self.league, + } + + if score: + game_data["score"] = score.strip() + + return game_data + + async def fetch_recent_games(self, limit: int = 10) -> list[dict[str, str]]: + """Fetch recent games asynchronously. + + Args: + limit: Maximum number of games to fetch + + Returns: + list of recent games + """ + # Simulate async API call + await asyncio.sleep(0.1) + + return [ + self.process_game_data("Arsenal", "Chelsea", "2-1"), + self.process_game_data("Liverpool", "Manchester City", "1-3"), + ][:limit] + + +def main() -> None: + """Main function to test the processor.""" + processor = FootballDataProcessor("Premier League") + + # Test synchronous processing + game = processor.process_game_data("Arsenal", "Chelsea", "2-1") + print(f"Processed game: {game}") + + +if __name__ == "__main__": + main() From d1e4a831f88f8248b7c152fb2ebc92085cf32bfa Mon Sep 17 00:00:00 2001 From: Anna S <> Date: Wed, 16 Jul 2025 17:17:44 -0700 Subject: [PATCH 4/5] implement ed get_fixtures method and created test script --- ai-backend/tests/test_fixtures.py | 144 ++++++++++++++++++++++++++++++ ai-backend/tools/sports_apis.py | 102 +++++++++++++++++---- 2 files changed, 231 insertions(+), 15 deletions(-) create mode 100644 ai-backend/tests/test_fixtures.py diff --git a/ai-backend/tests/test_fixtures.py b/ai-backend/tests/test_fixtures.py new file mode 100644 index 0000000..ed337bf --- /dev/null +++ b/ai-backend/tests/test_fixtures.py @@ -0,0 +1,144 @@ +import pytest + +from tools.sports_apis import APIFootballClient + + +@pytest.mark.asyncio +async def test_get_fixtures_by_league(monkeypatch): + class MockResponse: + status = 200 + + async def json(self): + return {"response": [{"fixture": 1, "league": 39, "season": 2025}]} + + def raise_for_status(self): + pass + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc, tb): + pass + + class MockSession: + def get(self, *args, **kwargs): + return MockResponse() + + client = APIFootballClient(api_key="test") + client.session = MockSession() + result = await client.get_fixtures(league=39, season=2025) + assert isinstance(result, list) + assert result[0]["league"] == 39 + assert result[0]["season"] == 2025 + + +@pytest.mark.asyncio +async def test_get_fixtures_by_league_missing_season(monkeypatch): + client = APIFootballClient(api_key="test") + result = await client.get_fixtures(league=39) + assert result == [] + + +@pytest.mark.asyncio +async def test_get_fixtures_by_date(monkeypatch): + class MockResponse: + status = 200 + + async def json(self): + return {"response": [{"fixture": 2, "date": "2025-07-10"}]} + + def raise_for_status(self): + pass + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc, tb): + pass + + class MockSession: + def get(self, *args, **kwargs): + return MockResponse() + + client = APIFootballClient(api_key="test") + client.session = MockSession() + result = await client.get_fixtures(date="2025-07-10") + assert isinstance(result, list) + assert result[0]["date"] == "2025-07-10" + + +@pytest.mark.asyncio +async def test_get_fixtures_by_team(monkeypatch): + class MockResponse: + status = 200 + + async def json(self): + return {"response": [{"fixture": 3, "team": 100}]} + + def raise_for_status(self): + pass + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc, tb): + pass + + class MockSession: + def get(self, *args, **kwargs): + return MockResponse() + + client = APIFootballClient(api_key="test") + client.session = MockSession() + result = await client.get_fixtures(team=100) + assert isinstance(result, list) + assert result[0]["team"] == 100 + + +@pytest.mark.asyncio +async def test_get_fixtures_by_multiple(monkeypatch): + class MockResponse: + status = 200 + + async def json(self): + return { + "response": [ + { + "fixture": 4, + "league": 39, + "season": 2025, + "date": "2025-07-10", + "team": 100, + } + ] + } + + def raise_for_status(self): + pass + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc, tb): + pass + + class MockSession: + def get(self, *args, **kwargs): + return MockResponse() + + client = APIFootballClient(api_key="test") + client.session = MockSession() + result = await client.get_fixtures( + league=39, season=2025, date="2025-07-10", team=100 + ) + assert isinstance(result, list) + assert result[0]["league"] == 39 + assert result[0]["date"] == "2025-07-10" + assert result[0]["team"] == 100 + + +@pytest.mark.asyncio +async def test_get_fixtures_no_params(monkeypatch): + client = APIFootballClient(api_key="test") + result = await client.get_fixtures() + assert result == [] diff --git a/ai-backend/tools/sports_apis.py b/ai-backend/tools/sports_apis.py index 78e6216..2ab0722 100644 --- a/ai-backend/tools/sports_apis.py +++ b/ai-backend/tools/sports_apis.py @@ -10,9 +10,13 @@ from typing import Any import aiohttp +from dotenv import load_dotenv from utils.security import sanitize_log_input, sanitize_multiple_log_inputs +load_dotenv() + +logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @@ -27,10 +31,15 @@ class APIFootballClient: def __init__(self, api_key: str | None = None): self.api_key = api_key or os.getenv("RAPIDAPI_KEY") self.base_url = "https://api-football-v1.p.rapidapi.com/v3" - self.headers = { - "X-RapidAPI-Key": self.api_key, - "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com", - } + + if self.api_key: + self.headers = { + "X-RapidAPI-Key": self.api_key, + "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com", + } + else: + self.headers = {} + self.session: aiohttp.ClientSession | None = None async def __aenter__(self) -> "APIFootballClient": @@ -48,27 +57,90 @@ async def __aexit__( async def get_fixtures( self, - league_id: int | None = None, - season: int | None = None, + fixture_id: int | None = None, date: str | None = None, + league: int | None = None, + team: int | None = None, + season: int | None = None, ) -> list[dict[str, Any]]: """ Get football fixtures/matches. Args: - league_id: League ID (e.g., 39 for Premier League) - season: Season year (e.g., 2024) - date: Date in YYYY-MM-DD format + fixture_id: ID of the fixture (optional) + date: Date in YYYY-MM-DD format (optional) + league: League ID (optional) + team: Team ID (optional) + season: Season year (required if league is provided) Returns: List of fixture data dictionaries """ - # TODO: Implement API-Football fixtures endpoint - league_safe, season_safe = sanitize_multiple_log_inputs(league_id, season) - logger.info( - "Fetching fixtures for league %s, season %s", league_safe, season_safe - ) - return [] + + if not self.headers or "X-RapidAPI-Key" not in self.headers: + logger.error("Missing RapidAPI key in headers.") + return [] + + payload = self._build_fixture_payload(fixture_id, date, league, team, season) + + if not payload: + logger.warning("get_fixtures called without any parameters.") + return [] + + url = self.base_url + "/fixtures" + + return await self._fetch_fixtures(url, payload) + + def _build_fixture_payload( + self, + fixture_id: int | None, + date: str | None, + league: int | None, + team: int | None, + season: int | None, + ) -> dict[str, Any]: + """Builds the payload for the fixture request.""" + payload = {} + if fixture_id is not None: + payload["id"] = sanitize_log_input(fixture_id) + if date is not None: + payload["date"] = sanitize_log_input(date) + if league is not None: + if season is None: + logger.error("Season must be provided when filtering by league.") + return {} # Return empty dict to indicate failure + payload["league"] = sanitize_log_input(league) + payload["season"] = sanitize_log_input(season) + if team is not None: + payload["team"] = sanitize_log_input(team) + return payload + + async def _fetch_fixtures( + self, url: str, payload: dict[str, Any] + ) -> list[dict[str, Any]]: + """Fetches fixtures from the API.""" + try: + if self.session is None: + self.session = aiohttp.ClientSession() + + async with self.session.get( + url, headers=self.headers, params=payload + ) as response: + logger.info( + f"Requesting fixtures with params: {payload}. Status: {response.status}" + ) + + response.raise_for_status() + + data = await response.json() + data_response = data.get("response", []) + if not isinstance(data_response, list): + data_response = [] + return data_response + + except aiohttp.ClientError as e: + logger.error(f"An error occurred while calling API-Football: {e}") + return [] async def get_teams(self, league_id: int, season: int) -> list[dict[str, Any]]: """ From 9a5e9890a74ae12e569f460dc05490361170b7e4 Mon Sep 17 00:00:00 2001 From: Anna S <> Date: Wed, 16 Jul 2025 17:18:36 -0700 Subject: [PATCH 5/5] Recommit changegd made in Issue-7. --- ai-backend/.pre-commit-config.yaml | 2 +- ai-backend/agents/editor.py | 4 +- ai-backend/agents/researcher.py | 8 +--- ai-backend/config/settings.py | 12 ++---- ai-backend/main.py | 16 ++------ ai-backend/pytest.ini | 14 +++++-- ai-backend/test_environment.py | 66 ------------------------------ ai-backend/test_openai.py | 38 ----------------- 8 files changed, 22 insertions(+), 138 deletions(-) delete mode 100644 ai-backend/test_environment.py delete mode 100644 ai-backend/test_openai.py diff --git a/ai-backend/.pre-commit-config.yaml b/ai-backend/.pre-commit-config.yaml index 9867e84..35cdd6f 100644 --- a/ai-backend/.pre-commit-config.yaml +++ b/ai-backend/.pre-commit-config.yaml @@ -28,7 +28,7 @@ repos: rev: 5.12.0 hooks: - id: isort - args: ["--profile", "black"] + args: ['--profile', 'black'] - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.7.1 diff --git a/ai-backend/agents/editor.py b/ai-backend/agents/editor.py index 67aa169..8875630 100644 --- a/ai-backend/agents/editor.py +++ b/ai-backend/agents/editor.py @@ -34,9 +34,7 @@ async def review_article( logger.info("Reviewing article content") return article_content, {} - async def fact_check( - self, article_content: str, source_data: dict[str, Any] - ) -> dict[str, Any]: + async def fact_check(self, article_content: str, source_data: dict[str, Any]) -> dict[str, Any]: """Fact-check article content against source data. Args: diff --git a/ai-backend/agents/researcher.py b/ai-backend/agents/researcher.py index e1ea5c2..e7de727 100644 --- a/ai-backend/agents/researcher.py +++ b/ai-backend/agents/researcher.py @@ -21,9 +21,7 @@ def __init__(self, config: dict[str, Any]): self.config = config logger.info("Research Agent initialized") - async def research_team_history( - self, team_id: str, opponent_id: str - ) -> dict[str, Any]: + async def research_team_history(self, team_id: str, opponent_id: str) -> dict[str, Any]: """Research historical matchups between teams. Args: @@ -35,9 +33,7 @@ async def research_team_history( """ # TODO: Implement team history research team_safe, opponent_safe = sanitize_multiple_log_inputs(team_id, opponent_id) - logger.info( - "Researching history between teams: %s vs %s", team_safe, opponent_safe - ) + logger.info("Researching history between teams: %s vs %s", team_safe, opponent_safe) return {} async def research_player_performance( diff --git a/ai-backend/config/settings.py b/ai-backend/config/settings.py index cc6c103..0f0f490 100644 --- a/ai-backend/config/settings.py +++ b/ai-backend/config/settings.py @@ -26,9 +26,7 @@ class Settings(BaseSettings): SUPABASE_SERVICE_ROLE_KEY: str = Field( ..., min_length=20, description="Supabase service role key" ) - RAPIDAPI_KEY: str = Field( - ..., min_length=10, description="RapidAPI key for API-Football" - ) + RAPIDAPI_KEY: str = Field(..., min_length=10, description="RapidAPI key for API-Football") # OpenAI Configuration openai_model: str = Field(default="gpt-4-turbo", description="OpenAI model to use") @@ -40,9 +38,7 @@ class Settings(BaseSettings): # Chainlit Configuration chainlit_host: str = Field(default="127.0.0.1", description="Chainlit host") - chainlit_port: int = Field( - default=8001, ge=1, le=65535, description="Chainlit port" - ) + chainlit_port: int = Field(default=8001, ge=1, le=65535, description="Chainlit port") # Environment Configuration environment: str = Field(default="development", description="Environment name") @@ -115,9 +111,7 @@ def to_dict(self) -> dict[str, Any]: "api_football_base_url": self.api_football_base_url, } - model_config = SettingsConfigDict( - env_file=".env", case_sensitive=True, extra="ignore" - ) + model_config = SettingsConfigDict(env_file=".env", case_sensitive=True, extra="ignore") # Global settings instance diff --git a/ai-backend/main.py b/ai-backend/main.py index b925a95..762378e 100644 --- a/ai-backend/main.py +++ b/ai-backend/main.py @@ -113,9 +113,7 @@ async def generate_article(self, request: ArticleRequest) -> ArticleResponse: except Exception as e: logger.error(f"Error generating article: {e!s}") - raise HTTPException( - status_code=500, detail=f"Failed to generate article: {e!s}" - ) from e + raise HTTPException(status_code=500, detail=f"Failed to generate article: {e!s}") from e async def _collect_game_data( self, collector: DataCollectorAgent, game_id: str @@ -149,15 +147,11 @@ async def _generate_content( }, } - async def _edit_content( - self, editor: EditorAgent, content: dict[str, Any] - ) -> dict[str, Any]: + async def _edit_content(self, editor: EditorAgent, content: dict[str, Any]) -> dict[str, Any]: """Edit and finalize content.""" article_content = content.get("content", "") metadata = content.get("metadata", {}) - edited_content, review_feedback = await editor.review_article( - article_content, metadata - ) + edited_content, review_feedback = await editor.review_article(article_content, metadata) return { "content": edited_content, "metadata": {**metadata, "review_feedback": review_feedback}, @@ -275,9 +269,7 @@ async def root() -> dict[str, str]: if __name__ == "__main__": import uvicorn - logger.info( - "Starting server on %s:%s", settings.fastapi_host, settings.fastapi_port - ) + logger.info("Starting server on %s:%s", settings.fastapi_host, settings.fastapi_port) uvicorn.run( "main:app", host=settings.fastapi_host, diff --git a/ai-backend/pytest.ini b/ai-backend/pytest.ini index c26a6c9..8430f36 100644 --- a/ai-backend/pytest.ini +++ b/ai-backend/pytest.ini @@ -4,8 +4,16 @@ python_files = test_*.py python_classes = Test* python_functions = test_* addopts = + --verbose + --tb=short --strict-markers --disable-warnings - --verbose -asyncio_default_fixture_loop_scope = function -asyncio_mode = auto + --cov=. + --cov-report=term-missing + --cov-report=html:htmlcov + --cov-fail-under=80 +markers = + slow: marks tests as slow (deselect with '-m "not slow"') + integration: marks tests as integration tests + unit: marks tests as unit tests + asyncio: marks tests as asyncio tests diff --git a/ai-backend/test_environment.py b/ai-backend/test_environment.py deleted file mode 100644 index 3567dfe..0000000 --- a/ai-backend/test_environment.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Test script to verify all dependencies are properly installed -""" - -import sys - -print(f"Python version: {sys.version}") - -# Test core dependencies -try: - import openai - - print("✅ OpenAI package imported successfully") -except ImportError as e: - print(f"❌ OpenAI import failed: {e}") - -try: - from agents.researcher import ResearchAgent - - print("✅ OpenAI Agents package imported successfully") -except ImportError as e: - print(f"❌ OpenAI Agents import failed: {e}") - -try: - import fastapi - - print("✅ FastAPI package imported successfully") -except ImportError as e: - print(f"❌ FastAPI import failed: {e}") - -try: - from pydantic import BaseModel - - print("✅ Pydantic package imported successfully") -except ImportError as e: - print(f"❌ Pydantic import failed: {e}") - -try: - from supabase import create_client - - print("✅ Supabase package imported successfully") -except ImportError as e: - print(f"❌ Supabase import failed: {e}") - -try: - import aiohttp - - print("✅ Aiohttp package imported successfully") -except ImportError as e: - print(f"❌ Aiohttp import failed: {e}") - -try: - from dotenv import load_dotenv - - print("✅ Python-dotenv package imported successfully") -except ImportError as e: - print(f"❌ Python-dotenv import failed: {e}") - -try: - import structlog - - print("✅ Structlog package imported successfully") -except ImportError as e: - print(f"❌ Structlog import failed: {e}") - -print("\n🎉 Environment test completed!") diff --git a/ai-backend/test_openai.py b/ai-backend/test_openai.py deleted file mode 100644 index 46f810b..0000000 --- a/ai-backend/test_openai.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -Test OpenAI API connection -""" -import os -from dotenv import load_dotenv -import openai - -# Load environment variables -load_dotenv() - -# Set up OpenAI client -client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY")) - -def test_openai_connection(): - """Test basic OpenAI API connection""" - if not os.getenv("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY") == "your_openai_api_key_here": - print("⚠️ OpenAI API key not set. Skipping connection test.") - return - - try: - # Test with a simple completion - response = client.chat.completions.create( - model="gpt-4.1-nano", - messages=[ - {"role": "user", "content": "Say 'Hello from Sport Scribe AI!'"} - ], - max_tokens=50 - ) - - print("✅ OpenAI API connection successful!") - print(f"Response: {response.choices[0].message.content}") - - except Exception as e: - print(f"❌ OpenAI API connection failed: {e}") - -if __name__ == "__main__": - test_openai_connection() -