Coverage for src/chuck_data/commands/bug.py: 0%

62 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-06-05 22:56 -0700

1""" 

2Command handler for submitting bug reports. 

3 

4This module contains the handler for submitting bug reports to Amperity, 

5including current configuration (without tokens) and session logs. 

6""" 

7 

8import logging 

9import os 

10import platform 

11from datetime import datetime 

12from typing import Optional, Any, Dict 

13 

14from ..clients.databricks import DatabricksAPIClient 

15from ..clients.amperity import AmperityAPIClient 

16from ..command_registry import CommandDefinition 

17from ..commands.base import CommandResult 

18from ..config import get_config_manager, get_amperity_token 

19from ..logger import get_current_log_file 

20 

21 

22def handle_command( 

23 client: Optional[DatabricksAPIClient], **kwargs: Any 

24) -> CommandResult: 

25 """ 

26 Submit a bug report to Amperity's API. 

27 

28 Args: 

29 client: Not used for this command 

30 **kwargs: Command parameters including: 

31 - description: Bug description from user 

32 - rest: Alternative way to provide description 

33 - raw_args: Fallback for unparsed args 

34 

35 Returns: 

36 CommandResult indicating success or failure 

37 """ 

38 # Try to get description from multiple sources 

39 description = kwargs.get("description", "").strip() 

40 

41 # If no explicit description, check for rest/raw_args (free-form text) 

42 if not description: 

43 description = kwargs.get("rest", "").strip() 

44 

45 if not description and "raw_args" in kwargs: 

46 raw_args = kwargs["raw_args"] 

47 if isinstance(raw_args, list): 

48 description = " ".join(raw_args).strip() 

49 elif isinstance(raw_args, str): 

50 description = raw_args.strip() 

51 

52 if not description: 

53 return CommandResult( 

54 False, 

55 message="Bug description is required. Usage: /bug Your bug description here", 

56 ) 

57 

58 # Check for Amperity token 

59 amperity_token = get_amperity_token() 

60 if not amperity_token: 

61 return CommandResult( 

62 False, 

63 message="Amperity authentication required. Please run /auth to authenticate first.", 

64 ) 

65 

66 try: 

67 # Prepare bug report payload 

68 payload = _prepare_bug_report(description) 

69 

70 # Submit to Amperity API using the client 

71 amperity_client = AmperityAPIClient() 

72 success, message = amperity_client.submit_bug_report(payload, amperity_token) 

73 

74 if success: 

75 logging.debug("Bug report submitted successfully") 

76 return CommandResult( 

77 True, 

78 message="Bug report submitted successfully. Thank you for your feedback!", 

79 ) 

80 else: 

81 return CommandResult(False, message=message) 

82 

83 except Exception as e: 

84 logging.error(f"Error submitting bug report: {e}", exc_info=True) 

85 return CommandResult( 

86 False, error=e, message=f"Error submitting bug report: {str(e)}" 

87 ) 

88 

89 

90def _prepare_bug_report(description: str) -> Dict[str, Any]: 

91 """ 

92 Prepare the bug report payload. 

93 

94 Args: 

95 description: User's bug description 

96 

97 Returns: 

98 Dictionary containing bug report data 

99 """ 

100 # Get config without tokens 

101 config_data = _get_sanitized_config() 

102 

103 # Get session log content 

104 log_content = _get_session_log() 

105 

106 # Get system information 

107 system_info = { 

108 "platform": platform.platform(), 

109 "python_version": platform.python_version(), 

110 "system": platform.system(), 

111 "machine": platform.machine(), 

112 } 

113 

114 return { 

115 "type": "bug_report", 

116 "timestamp": datetime.utcnow().isoformat(), 

117 "description": description, 

118 "config": config_data, 

119 "session_log": log_content, 

120 "system_info": system_info, 

121 } 

122 

123 

124def _get_sanitized_config() -> Dict[str, Any]: 

125 """ 

126 Get current configuration without sensitive data (tokens). 

127 

128 Returns: 

129 Dictionary of sanitized config data 

130 """ 

131 config_manager = get_config_manager() 

132 config = config_manager.get_config() 

133 

134 # Create sanitized version - NEVER include tokens 

135 sanitized = { 

136 "workspace_url": config.workspace_url, 

137 "active_model": config.active_model, 

138 "warehouse_id": config.warehouse_id, 

139 "active_catalog": config.active_catalog, 

140 "active_schema": config.active_schema, 

141 "usage_tracking_consent": config.usage_tracking_consent, 

142 } 

143 

144 # Remove None values 

145 return {k: v for k, v in sanitized.items() if v is not None} 

146 

147 

148def _get_session_log() -> str: 

149 """ 

150 Get the current session's log content. 

151 

152 Returns: 

153 String containing log content or error message 

154 """ 

155 log_file = get_current_log_file() 

156 if not log_file or not os.path.exists(log_file): 

157 return "Session log not available" 

158 

159 try: 

160 with open(log_file, "r") as f: 

161 # Read last 10KB of log to avoid huge payloads 

162 f.seek(0, os.SEEK_END) 

163 file_size = f.tell() 

164 read_size = min(file_size, 10240) # 10KB max 

165 f.seek(max(0, file_size - read_size)) 

166 return f.read() 

167 except Exception as e: 

168 logging.error(f"Failed to read session log: {e}") 

169 return f"Error reading session log: {str(e)}" 

170 

171 

172DEFINITION = CommandDefinition( 

173 name="bug", 

174 description="Submit a bug report with current configuration and session logs", 

175 handler=handle_command, 

176 parameters={ 

177 "description": { 

178 "type": "string", 

179 "description": "Description of the bug or issue you're experiencing", 

180 }, 

181 "rest": { 

182 "type": "string", 

183 "description": "Bug description provided as free-form text after the command", 

184 }, 

185 "raw_args": { 

186 "type": ["array", "string"], 

187 "description": "Raw unparsed arguments for the bug description", 

188 }, 

189 }, 

190 required_params=[], # No required params since we accept multiple input methods 

191 tui_aliases=["/bug"], 

192 needs_api_client=False, # We use Amperity token directly 

193 visible_to_user=True, 

194 visible_to_agent=False, # Bug reports should come from users, not agents 

195 usage_hint="Example: /bug The table list is not refreshing properly", 

196)