Coverage for lintro / ai / availability.py: 54%

46 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Graceful degradation for AI dependencies. 

2 

3Checks whether the required AI provider packages are installed and 

4provides clear error messages when they are not. 

5""" 

6 

7from __future__ import annotations 

8 

9from typing import TYPE_CHECKING 

10 

11import click 

12 

13if TYPE_CHECKING: 

14 from lintro.ai.registry import AIProvider 

15 

16_AI_AVAILABLE: bool | None = None 

17 

18 

19def is_ai_available() -> bool: 

20 """Check if at least one AI provider package is installed. 

21 

22 Returns: 

23 bool: True if anthropic or openai is importable. 

24 """ 

25 global _AI_AVAILABLE 

26 

27 if _AI_AVAILABLE is not None: 

28 return _AI_AVAILABLE 

29 

30 try: 

31 import anthropic # noqa: F401 -- import-only availability check 

32 

33 _AI_AVAILABLE = True 

34 return True 

35 except ImportError: 

36 pass 

37 

38 try: 

39 import openai # noqa: F401 -- import-only availability check 

40 

41 _AI_AVAILABLE = True 

42 return True 

43 except ImportError: 

44 pass 

45 

46 _AI_AVAILABLE = False 

47 return False 

48 

49 

50def is_provider_available(provider: AIProvider | str) -> bool: 

51 """Check if a specific provider package is installed. 

52 

53 Args: 

54 provider: Provider name ("anthropic" or "openai"). 

55 

56 Returns: 

57 bool: True if the provider's package is importable. 

58 """ 

59 from lintro.ai.registry import AIProvider 

60 

61 if isinstance(provider, AIProvider): 

62 provider_value = provider.value 

63 else: 

64 provider_value = str(provider).lower() 

65 

66 if provider_value not in {p.value for p in AIProvider}: 

67 from loguru import logger 

68 

69 supported = ", ".join(p.value for p in AIProvider) 

70 logger.warning( 

71 "Unknown AI provider {!r}; supported providers: {}", 

72 provider_value, 

73 supported, 

74 ) 

75 return False 

76 

77 try: 

78 if provider_value == AIProvider.ANTHROPIC.value: 

79 import anthropic # noqa: F401 -- import-only availability check 

80 

81 return True 

82 if provider_value == AIProvider.OPENAI.value: 

83 import openai # noqa: F401 -- import-only availability check 

84 

85 return True 

86 except ImportError: 

87 pass 

88 return False 

89 

90 

91def require_ai() -> None: 

92 """Ensure AI dependencies are installed. 

93 

94 Raises: 

95 click.UsageError: If no AI provider packages are installed, 

96 with installation instructions. 

97 """ 

98 if not is_ai_available(): 

99 raise click.UsageError( 

100 "AI features require lintro[ai]. " 

101 "Install with: uv pip install 'lintro[ai]'", 

102 ) 

103 

104 

105def reset_availability_cache() -> None: 

106 """Reset the cached availability check. 

107 

108 Useful for testing when mocking imports. 

109 """ 

110 global _AI_AVAILABLE 

111 _AI_AVAILABLE = None