Coverage for src/local_deep_research/llm/providers/implementations/anthropic.py: 95%

38 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-03 23:15 +0000

1"""Anthropic LLM provider for Local Deep Research.""" 

2 

3from langchain_anthropic import ChatAnthropic 

4from loguru import logger 

5 

6from ....config.thread_settings import ( 

7 get_setting_from_snapshot, 

8 NoSettingsContextError, 

9) 

10from ..openai_base import OpenAICompatibleProvider 

11 

12 

13class AnthropicProvider(OpenAICompatibleProvider): 

14 """Anthropic provider for Local Deep Research. 

15 

16 This is the official Anthropic API provider. 

17 """ 

18 

19 provider_name = "Anthropic" 

20 api_key_setting = "llm.anthropic.api_key" 

21 default_model = "" # User must explicitly pick a model — no silent fallback 

22 default_base_url = "https://api.anthropic.com/v1" 

23 

24 # Metadata for auto-discovery 

25 provider_key = "ANTHROPIC" 

26 company_name = "Anthropic" 

27 is_cloud = True 

28 

29 @classmethod 

30 def create_llm(cls, model_name=None, temperature=0.7, **kwargs): 

31 """Factory function for Anthropic LLMs. 

32 

33 Args: 

34 model_name: Name of the model to use 

35 temperature: Model temperature (0.0-1.0) 

36 **kwargs: Additional arguments including settings_snapshot 

37 

38 Returns: 

39 A configured ChatAnthropic instance 

40 

41 Raises: 

42 ValueError: If API key is not configured 

43 """ 

44 settings_snapshot = kwargs.get("settings_snapshot") 

45 

46 # Get API key from settings 

47 api_key = get_setting_from_snapshot( 

48 cls.api_key_setting, 

49 default=None, 

50 settings_snapshot=settings_snapshot, 

51 ) 

52 

53 if not api_key: 

54 logger.error(f"{cls.provider_name} API key not found in settings") 

55 raise ValueError( 

56 f"{cls.provider_name} API key not configured. " 

57 f"Please set {cls.api_key_setting} in settings." 

58 ) 

59 

60 # Require an explicit model — no silent fallback to a hardcoded default. 

61 if not model_name or not model_name.strip(): 

62 logger.error(f"{cls.provider_name} model name not provided") 

63 raise ValueError( 

64 f"{cls.provider_name} model not configured. " 

65 f"Please set llm.model in settings " 

66 f"(e.g. 'claude-3-5-sonnet-20241022')." 

67 ) 

68 

69 # Build Anthropic-specific parameters 

70 anthropic_params = { 

71 "model": model_name, 

72 "anthropic_api_key": api_key, 

73 "temperature": temperature, 

74 } 

75 

76 # Add max_tokens if specified in settings 

77 try: 

78 max_tokens = get_setting_from_snapshot( 

79 "llm.max_tokens", 

80 default=None, 

81 settings_snapshot=settings_snapshot, 

82 ) 

83 if max_tokens: 

84 anthropic_params["max_tokens"] = int(max_tokens) 

85 except NoSettingsContextError: 

86 pass # Optional parameter 

87 

88 logger.info( 

89 f"Creating {cls.provider_name} LLM with model: {model_name}, " 

90 f"temperature: {temperature}" 

91 ) 

92 

93 return ChatAnthropic(**anthropic_params) 

94 

95 @classmethod 

96 def is_available(cls, settings_snapshot=None): 

97 """Check if this provider is available. 

98 

99 Args: 

100 settings_snapshot: Optional settings snapshot to use 

101 

102 Returns: 

103 True if API key is configured, False otherwise 

104 """ 

105 try: 

106 # Check if API key is configured 

107 api_key = get_setting_from_snapshot( 

108 cls.api_key_setting, 

109 default=None, 

110 settings_snapshot=settings_snapshot, 

111 ) 

112 return bool(api_key and str(api_key).strip()) 

113 except Exception: 

114 return False