Coverage for src / dataknobs_bots / api / dependencies.py: 0%

35 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-16 10:13 -0700

1"""Dependency injection helpers for FastAPI. 

2 

3This module provides singleton management and FastAPI dependency injection 

4for bot-related services. 

5 

6Example: 

7 ```python 

8 from fastapi import FastAPI 

9 from dataknobs_bots.api.dependencies import ( 

10 init_bot_manager, 

11 BotManagerDep, 

12 ) 

13 

14 app = FastAPI() 

15 

16 @app.on_event("startup") 

17 async def startup(): 

18 # Initialize with a config loader 

19 init_bot_manager(config_loader=my_loader) 

20 

21 @app.post("/chat/{bot_id}") 

22 async def chat( 

23 bot_id: str, 

24 message: str, 

25 manager: BotManagerDep, 

26 ): 

27 bot = await manager.get_or_create(bot_id) 

28 return await bot.chat(message, context) 

29 ``` 

30""" 

31 

32from __future__ import annotations 

33 

34import logging 

35from typing import Annotated, Any 

36 

37from dataknobs_bots.bot.manager import BotManager, ConfigLoaderType 

38 

39 

40logger = logging.getLogger(__name__) 

41 

42 

43class _BotManagerSingleton: 

44 """Singleton container for BotManager instance. 

45 

46 Using a class-based approach avoids global statement warnings 

47 while maintaining singleton semantics. 

48 """ 

49 

50 _instance: BotManager | None = None 

51 

52 @classmethod 

53 def get(cls) -> BotManager: 

54 """Get the singleton instance, creating with defaults if needed.""" 

55 if cls._instance is None: 

56 cls._instance = BotManager() 

57 logger.info("Created default BotManager singleton (no config loader)") 

58 return cls._instance 

59 

60 @classmethod 

61 def init( 

62 cls, 

63 config_loader: ConfigLoaderType | None = None, 

64 **kwargs: Any, 

65 ) -> BotManager: 

66 """Initialize the singleton with configuration.""" 

67 cls._instance = BotManager(config_loader=config_loader, **kwargs) 

68 logger.info("Initialized BotManager singleton") 

69 return cls._instance 

70 

71 @classmethod 

72 def reset(cls) -> None: 

73 """Reset the singleton instance.""" 

74 cls._instance = None 

75 logger.info("Reset BotManager singleton") 

76 

77 

78def get_bot_manager() -> BotManager: 

79 """Get or create BotManager singleton instance. 

80 

81 Returns: 

82 BotManager instance 

83 

84 Note: 

85 Call `init_bot_manager()` during app startup to configure 

86 the singleton before using this dependency. 

87 """ 

88 return _BotManagerSingleton.get() 

89 

90 

91def init_bot_manager( 

92 config_loader: ConfigLoaderType | None = None, 

93 **kwargs: Any, 

94) -> BotManager: 

95 """Initialize the BotManager singleton with configuration. 

96 

97 Call this during application startup to configure the singleton. 

98 

99 Args: 

100 config_loader: Optional configuration loader for bots 

101 **kwargs: Additional arguments passed to BotManager 

102 

103 Returns: 

104 Configured BotManager instance 

105 

106 Example: 

107 ```python 

108 @app.on_event("startup") 

109 async def startup(): 

110 init_bot_manager( 

111 config_loader=MyConfigLoader("./configs") 

112 ) 

113 ``` 

114 """ 

115 return _BotManagerSingleton.init(config_loader=config_loader, **kwargs) 

116 

117 

118def reset_bot_manager() -> None: 

119 """Reset the BotManager singleton. 

120 

121 Useful for testing or when reconfiguring the application. 

122 """ 

123 _BotManagerSingleton.reset() 

124 

125 

126# Dependency function for FastAPI 

127def _get_bot_manager_dep() -> BotManager: 

128 """Dependency function for FastAPI.""" 

129 return get_bot_manager() 

130 

131 

132# Type alias for FastAPI dependency injection 

133# Usage: async def endpoint(manager: BotManagerDep): 

134try: 

135 from fastapi import Depends 

136 

137 BotManagerDep = Annotated[BotManager, Depends(_get_bot_manager_dep)] 

138except ImportError: 

139 # FastAPI not installed - provide a placeholder 

140 BotManagerDep = BotManager # type: ignore