Coverage for app/routes/suggestions.py: 60%

96 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-05-02 02:49 +0000

1from flask import Blueprint, request 

2from app.services.suggestion_service import getAvailableModels, getSuggestion, generate_hint_from_gemini, generate_explanation_from_gemini, check_code_correctness, get_suggestion_by_id, generate_refined_prompt 

3from app.models.response import * 

4from app.models.status_codes import StatusCodes 

5from flasgger import swag_from 

6 

7suggestions_bp = Blueprint('suggestions', __name__) 

8 

9@suggestions_bp.route('/suggestion', methods=['POST']) 

10@swag_from({ 

11 'tags': ['Suggestions'], 

12 'summary': 'Generate a suggestion using the AI model', 

13 'description': 'Sends a prompt to the locally running Ollama model with an optional model name and correctness flag, returning the generated suggestion.', 

14 'consumes': ['application/json'], 

15 'produces': ['application/json'], 

16 'parameters': [ 

17 { 

18 'name': 'body', 

19 'in': 'body', 

20 'required': True, 

21 'schema': { 

22 'type': 'object', 

23 'properties': { 

24 'prompt': { 

25 'type': 'string', 

26 'example': 'def add(a, b):' 

27 }, 

28 'vendor': { 

29 'type': 'string', 

30 'example': "ollama" 

31 }, 

32 'model': { 

33 'type': 'string', 

34 'example': 'codellama:7b', 

35 'description': 'The AI model to use for generating the suggestion.' 

36 }, 

37 'parameters': { 

38 'type': 'object', 

39 'example': { 

40 "top_k": 0.2 

41 }, 

42 'description': 'A flag indicating whether the suggestion should be correct.' 

43 } 

44 }, 

45 'required': ['prompt'] 

46 } 

47 } 

48 ], 

49 'responses': { 

50 '200': { 

51 'description': 'Successfully generated suggestion', 

52 'schema': { 

53 'type': 'object', 

54 'properties': { 

55 'suggestions': { 

56 'type': 'array', 

57 'items': {'type': 'string'}, 

58 'example': ["return a + b"] 

59 } 

60 } 

61 } 

62 }, 

63 '400': { 

64 'description': 'Bad Request - No prompt provided', 

65 'schema': { 

66 'type': 'object', 

67 'properties': { 

68 'error': {'type': 'string', 'example': 'No prompt provided'} 

69 } 

70 } 

71 }, 

72 '500': { 

73 'description': 'Internal Server Error - Failed to generate response', 

74 'schema': { 

75 'type': 'object', 

76 'properties': { 

77 'error': {'type': 'string', 'example': 'Connection error'} 

78 } 

79 } 

80 } 

81 } 

82}) 

83def generate_suggestion_route(): 

84 """ 

85 Generate a suggestion based on the provided prompt. 

86 See Swagger docs for more information. 

87 """ 

88 data = request.json 

89 prompt = data.get("prompt", "") 

90 vendor_name = data.get("vendor") 

91 model_name = data.get("model") 

92 model_params = data.get("parameters") 

93 

94 if not prompt: 

95 return error_response( 

96 "No prompt provided", 

97 None, 

98 StatusCodes.BAD_REQUEST 

99 ) 

100 

101 try: 

102 # Call getSuggestion with all parameters, it will decide which model to use 

103 response = getSuggestion( 

104 prompt=prompt, 

105 vendor=vendor_name, 

106 model_name=model_name, 

107 ) 

108 

109 print(f"Response from model: {response}") 

110 

111 return success_response( 

112 "AI Suggestions", 

113 {"response": response if isinstance(response, list) else []}, 

114 StatusCodes.OK 

115 ) 

116 

117 except Exception as e: 

118 print(e) 

119 return error_response( 

120 str(e), 

121 None, 

122 StatusCodes.SERVER_ERROR 

123 ) 

124 

125@suggestions_bp.route('/suggestion/refine', methods=['POST']) 

126def refine_prompt(): 

127 """ 

128 Generate a refined prompt for code completion. 

129 """ 

130 data = request.json 

131 raw_prompt = data.get("rawPrompt", "") 

132 

133 if not raw_prompt: 

134 return error_response( 

135 "Missing required field: rawPrompt", 

136 None, 

137 StatusCodes.BAD_REQUEST 

138 ) 

139 

140 try: 

141 refined = generate_refined_prompt(raw_prompt) 

142 

143 print(f"Refined prompt: {refined}") 

144 

145 return success_response( 

146 "Prompt refined", 

147 {"refinedPrompt": refined}, 

148 StatusCodes.OK 

149 ) 

150 

151 except Exception as e: 

152 return error_response( 

153 str(e), 

154 None, 

155 StatusCodes.SERVER_ERROR 

156 ) 

157 

158@suggestions_bp.route('/suggestion/hint', methods=['POST']) 

159def generate_hint(): 

160 """ 

161 Generate a hint explaining the difference between code versions. 

162 """ 

163 data = request.json 

164 prompt = data.get("prompt", "") 

165 wrong_code = data.get("wrongCode", "") 

166 right_code = data.get("rightCode", "") 

167 

168 if not all([prompt, wrong_code, right_code]): 

169 return error_response( 

170 "Missing required fields (prompt, wrongCode, rightCode)", 

171 None, 

172 StatusCodes.BAD_REQUEST 

173 ) 

174 

175 try: 

176 hint = generate_hint_from_gemini(prompt, wrong_code, right_code) 

177 

178 return success_response( 

179 "Hint generated", 

180 {"hint": hint}, 

181 StatusCodes.OK 

182 ) 

183 

184 except Exception as e: 

185 return error_response( 

186 str(e), 

187 None, 

188 StatusCodes.SERVER_ERROR 

189 ) 

190 

191@suggestions_bp.route('/suggestion/explanation', methods=['POST']) 

192def generate_explanation(): 

193 """ 

194 Generate a explanation telling the user what is wrong with the 'bad code'. 

195 """ 

196 data = request.json 

197 prompt = data.get("prompt", "") 

198 wrong_code = data.get("wrongCode", "") 

199 right_code = data.get("rightCode", "") 

200 

201 if not all([prompt, wrong_code, right_code]): 

202 return error_response( 

203 "Missing required fields (prompt, wrongCode, rightCode)", 

204 None, 

205 StatusCodes.BAD_REQUEST 

206 ) 

207 

208 try: 

209 explanation = generate_explanation_from_gemini(prompt, wrong_code, right_code) 

210 

211 return success_response( 

212 "Explanation generated", 

213 {"explanation": explanation}, 

214 StatusCodes.OK 

215 ) 

216 

217 except Exception as e: 

218 return error_response( 

219 str(e), 

220 None, 

221 StatusCodes.SERVER_ERROR 

222 ) 

223 

224@suggestions_bp.route('/suggestion/answer', methods=['POST']) 

225def validate_fix(): 

226 """ 

227 Validate the user's fixed code using an AI model. 

228 """ 

229 data = request.json 

230 prompt = data.get("prompt", "") 

231 wrong_code = data.get("wrongCode", "") 

232 fixed_code = data.get("fixedCode", "") 

233 

234 if not all([prompt, wrong_code, fixed_code]): 

235 return error_response( 

236 "Missing required fields (prompt, wrongCode, fixedCode)", 

237 None, 

238 StatusCodes.BAD_REQUEST 

239 ) 

240 

241 try: 

242 is_correct = check_code_correctness(prompt, wrong_code, fixed_code) 

243 

244 print(f"Is the fixed code correct? {is_correct}") 

245 

246 return success_response( 

247 "Code validation result", 

248 {"isCorrect": is_correct}, 

249 StatusCodes.OK 

250 ) 

251 

252 except Exception as e: 

253 return error_response( 

254 str(e), 

255 None, 

256 StatusCodes.SERVER_ERROR 

257 ) 

258 

259 

260@suggestions_bp.route('/models', methods=['GET']) 

261@swag_from({ 

262 'tags': ['Suggestions'], 

263 'summary': 'Get all models available from a vendor', 

264 'description': 'Lists all models available from the selected vendor', 

265 'produces': ['application/json'], 

266 'parameters': [ 

267 { 

268 'name': 'vendor', 

269 'in': 'query', # Change from 'body' to 'query' 

270 'required': True, 

271 'type': 'string', 

272 'example': "openai", 

273 } 

274 ], 

275 'responses': { 

276 '200': { 

277 'description': 'List of models from the vendor', 

278 'schema': { 

279 'type': 'object', 

280 'properties': { 

281 'models': { 

282 'type': 'array', 

283 'items': {'type': 'string'} 

284 } 

285 } 

286 } 

287 }, 

288 '400': { 

289 'description': 'Bad Request, missing vendor', 

290 }, 

291 '500': { 

292 'description': 'Internal server error', 

293 } 

294 } 

295}) 

296def list_models_route(): 

297 vendor = request.args.get('vendor') # Get vendor from query string 

298 

299 if not vendor: 

300 return error_response( 

301 "Vendor not included in request", 

302 None, 

303 StatusCodes.BAD_REQUEST 

304 ) 

305 

306 try: 

307 models = getAvailableModels(vendor) # Pass vendor to function 

308 return success_response( 

309 "Models List", 

310 {"models": models} 

311 ) 

312 

313 except Exception as e: 

314 return error_response( 

315 str(e), 

316 None, 

317 StatusCodes.SERVER_ERROR 

318 ) 

319 

320@suggestions_bp.route('/suggestion/<suggestion_id>', methods=['GET']) 

321@swag_from({ 

322 'tags': ['Suggestions'], 

323 'summary': 'Get a suggestion by ID', 

324 'description': 'Retrieves a specific suggestion using its unique identifier.', 

325 'parameters': [ 

326 { 

327 'name': 'suggestion_id', 

328 'in': 'path', 

329 'required': True, 

330 'type': 'string', 

331 'example': 'abc123' 

332 } 

333 ], 

334 'responses': { 

335 '200': { 

336 'description': 'Suggestion retrieved successfully', 

337 'schema': { 

338 'type': 'object', 

339 'properties': { 

340 'id': {'type': 'string'}, 

341 'prompt': {'type': 'string'}, 

342 'suggestion': {'type': 'string'}, 

343 'user_id': {'type': 'string'}, 

344 'created_at': {'type': 'string', 'format': 'date-time'}, 

345 # Add other fields if needed 

346 } 

347 } 

348 }, 

349 '404': { 

350 'description': 'Suggestion not found', 

351 'schema': { 

352 'type': 'object', 

353 'properties': { 

354 'error': {'type': 'string'} 

355 } 

356 } 

357 } 

358 } 

359}) 

360def get_suggestion_details(suggestion_id): 

361 try: 

362 suggestion = get_suggestion_by_id(suggestion_id) 

363 

364 if not suggestion: 

365 return error_response( 

366 f"No suggestion found for id {suggestion_id}", 

367 None, 

368 StatusCodes.NOT_FOUND 

369 ) 

370 

371 return success_response( 

372 f"Suggestion retrieved for id {suggestion_id}", 

373 suggestion.to_json(), 

374 StatusCodes.OK 

375 ) 

376 

377 except Exception as e: 

378 return error_response( 

379 f"Error fetching suggestion {suggestion_id}: {e}", 

380 None, 

381 StatusCodes.SERVER_ERROR 

382 )