Coverage for mindsdb / api / http / namespaces / models.py: 75%

156 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-21 00:36 +0000

1from http import HTTPStatus 

2 

3import json 

4 

5from flask import request 

6from flask_restx import Resource 

7import pandas as pd 

8 

9from mindsdb.api.http.namespaces.configs.projects import ns_conf 

10from mindsdb.api.http.utils import http_error 

11from mindsdb.api.executor.controllers.session_controller import SessionController 

12from mindsdb.metrics.metrics import api_endpoint_metrics 

13from mindsdb.interfaces.model.functions import PredictorRecordNotFound 

14from mindsdb.interfaces.storage.db import Predictor 

15from mindsdb_sql_parser import parse_sql 

16from mindsdb_sql_parser.ast.mindsdb import CreatePredictor 

17from mindsdb.utilities.exception import EntityNotExistsError 

18from mindsdb.utilities import log 

19 

20logger = log.getLogger(__name__) 

21 

22 

23@ns_conf.route("/<project_name>/models") 

24class ModelsList(Resource): 

25 @ns_conf.doc("list_models") 

26 @api_endpoint_metrics("GET", "/models") 

27 def get(self, project_name): 

28 """List all models""" 

29 session = SessionController() 

30 

31 try: 

32 session.database_controller.get_project(project_name) 

33 except EntityNotExistsError: 

34 return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist") 

35 

36 return session.model_controller.get_models(with_versions=True, project_name=project_name) 

37 

38 @ns_conf.doc("train_model") 

39 @api_endpoint_metrics("POST", "/models") 

40 def post(self, project_name): 

41 """Creates a new model and trains it""" 

42 session = SessionController() 

43 

44 if "query" not in request.json: 

45 return http_error(HTTPStatus.BAD_REQUEST, "Query required", 'Missing "query" SQL statement') 

46 query = request.json["query"] 

47 

48 project_datanode = session.datahub.get(project_name) 

49 if project_datanode is None: 

50 return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist") 

51 

52 try: 

53 create_statement = parse_sql(query) 

54 except Exception: 

55 return http_error( 

56 HTTPStatus.BAD_REQUEST, "Invalid query string", f"SQL CREATE statement is invalid: {query}" 

57 ) 

58 

59 if type(create_statement) is not CreatePredictor: 

60 return http_error( 

61 HTTPStatus.BAD_REQUEST, 

62 "Invalid CREATE SQL statement", 

63 f"SQL statement is not a CREATE model statement: {query}", 

64 ) 

65 

66 model_name = create_statement.name.parts[1].lower() 

67 try: 

68 session.model_controller.get_model(model_name, project_name=project_name) 

69 return http_error( 

70 HTTPStatus.CONFLICT, "Model already exists", f"Model with name {model_name} already exists" 

71 ) 

72 except PredictorRecordNotFound: 

73 pass 

74 

75 ml_integration = "lightwood" 

76 if create_statement.using is not None: 76 ↛ 81line 76 didn't jump to line 81 because the condition on line 76 was always true

77 # Convert using to lowercase 

78 create_statement.using = {k.lower(): v for k, v in create_statement.using.items()} 

79 ml_integration = create_statement.using.pop("engine", ml_integration) 

80 

81 try: 

82 ml_handler = session.integration_controller.get_ml_handler(ml_integration) 

83 except Exception: 

84 return http_error( 

85 HTTPStatus.NOT_FOUND, "ML handler not found", f"Cannot find ML handler with name {ml_integration}" 

86 ) 

87 

88 try: 

89 model_df = session.model_controller.create_model(create_statement, ml_handler) 

90 # Consistent format with GET /projects/<project_name>/models/<model_name> 

91 return { 

92 "name": model_df.at[0, "NAME"], 

93 "accuracy": None, 

94 "active": model_df.at[0, "ACTIVE"], 

95 "version": model_df.at[0, "VERSION"], 

96 "status": model_df.at[0, "STATUS"], 

97 "predict": model_df.at[0, "PREDICT"], 

98 "mindsdb_version": model_df.at[0, "MINDSDB_VERSION"], 

99 "error": model_df.at[0, "ERROR"], 

100 "fetch_data_query": model_df.at[0, "SELECT_DATA_QUERY"], 

101 "problem_definition": model_df.at[0, "TRAINING_OPTIONS"], 

102 }, HTTPStatus.CREATED 

103 except Exception as e: 

104 logger.exception("Something went wrong while creating and training model") 

105 return http_error( 

106 HTTPStatus.INTERNAL_SERVER_ERROR, 

107 "Unable to train model", 

108 f"Something went wrong while creating and training model {model_name}: {e}", 

109 ) 

110 

111 

112@ns_conf.route("/<project_name>/models/<model_name>") 

113@ns_conf.param("project_name", "Name of the project") 

114@ns_conf.param("model_name", "Name of the model") 

115class ModelResource(Resource): 

116 @ns_conf.doc("get_model") 

117 @api_endpoint_metrics("GET", "/models/model") 

118 def get(self, project_name, model_name): 

119 """Get a model by name and version""" 

120 session = SessionController() 

121 

122 project_datanode = session.datahub.get(project_name) 

123 if project_datanode is None: 

124 return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist") 

125 

126 name_no_version, version = Predictor.get_name_and_version(model_name) 

127 try: 

128 return session.model_controller.get_model(name_no_version, version=version, project_name=project_name) 

129 except PredictorRecordNotFound: 

130 return http_error(HTTPStatus.NOT_FOUND, "Model not found", f"Model with name {model_name} not found") 

131 

132 @ns_conf.doc("update_model") 

133 @api_endpoint_metrics("PUT", "/models/model") 

134 def put(self, project_name, model_name): 

135 """Update model""" 

136 

137 session = SessionController() 

138 

139 project_datanode = session.datahub.get(project_name) 

140 if project_datanode is None: 

141 return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist") 

142 

143 if "problem_definition" not in request.json: 

144 return http_error( 

145 HTTPStatus.BAD_REQUEST, "problem_definition required", 'Missing "problem_definition" field' 

146 ) 

147 

148 problem_definition = request.json["problem_definition"] 

149 

150 model_name, version = Predictor.get_name_and_version(model_name) 

151 

152 session.model_controller.update_model( 

153 session, 

154 project_name, 

155 model_name, 

156 version=version, 

157 problem_definition=problem_definition, 

158 ) 

159 return session.model_controller.get_model(model_name, version=version, project_name=project_name) 

160 

161 @ns_conf.doc("delete_model") 

162 @api_endpoint_metrics("DELETE", "/models/model") 

163 def delete(self, project_name, model_name): 

164 """Deletes a model by name""" 

165 

166 session = SessionController() 

167 

168 project_datanode = session.datahub.get(project_name) 

169 if project_datanode is None: 

170 return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist") 

171 

172 name_no_version, version = Predictor.get_name_and_version(model_name) 

173 try: 

174 session.model_controller.get_model(name_no_version, version=version, project_name=project_name) 

175 except PredictorRecordNotFound: 

176 return http_error(HTTPStatus.NOT_FOUND, "Model not found", f"Model with name {model_name} not found") 

177 

178 try: 

179 session.model_controller.delete_model(name_no_version, project_name, version=version) 

180 except Exception as e: 

181 logger.exception(f"Something went wrong while deleting model '{model_name}'") 

182 return http_error( 

183 HTTPStatus.INTERNAL_SERVER_ERROR, 

184 "Error deleting model", 

185 f"Something went wrong while deleting {model_name}: {e}", 

186 ) 

187 

188 return "", HTTPStatus.NO_CONTENT 

189 

190 

191@ns_conf.route("/<project_name>/models/<model_name>/predict") 

192@ns_conf.param("project_name", "Name of the project") 

193@ns_conf.param("model_name", "Name of the model") 

194class ModelPredict(Resource): 

195 @ns_conf.doc("post_model_predict") 

196 @api_endpoint_metrics("POST", "/models/model/predict") 

197 def post(self, project_name, model_name): 

198 """Call prediction""" 

199 

200 name_no_version, version = Predictor.get_name_and_version(model_name) 

201 

202 session = SessionController() 

203 project_datanode = session.datahub.get(project_name) 

204 if project_datanode is None: 

205 return http_error( 

206 HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist" 

207 ) 

208 

209 try: 

210 session.model_controller.get_model(name_no_version, version=version, project_name=project_name) 

211 except PredictorRecordNotFound: 

212 return http_error(HTTPStatus.NOT_FOUND, "Model not found", f"Model with name {model_name} not found") 

213 

214 data = request.json["data"] 

215 if isinstance(data, str): 

216 # Support object or serialized object. 

217 data = json.loads(data) 

218 params = request.json.get("params") 

219 

220 predictions = project_datanode.predict( 

221 model_name=name_no_version, 

222 df=pd.DataFrame(data), 

223 version=version, 

224 params=params, 

225 ) 

226 

227 return predictions.to_dict("records") 

228 

229 

230@ns_conf.route("/<project_name>/models/<model_name>/describe") 

231@ns_conf.param("project_name", "Name of the project") 

232@ns_conf.param("model_name", "Name of the model") 

233class ModelDescribe(Resource): 

234 @ns_conf.doc("describe_model") 

235 @api_endpoint_metrics("GET", "/models/model/describe") 

236 def get(self, project_name, model_name): 

237 """Describes a model""" 

238 session = SessionController() 

239 

240 project_datanode = session.datahub.get(project_name) 

241 if project_datanode is None: 

242 return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist") 

243 

244 name_no_version, version = Predictor.get_name_and_version(model_name) 

245 

246 try: 

247 session.model_controller.get_model(name_no_version, version=version, project_name=project_name) 

248 except PredictorRecordNotFound: 

249 return http_error(HTTPStatus.NOT_FOUND, "Model not found", f"Model with name {model_name} not found") 

250 

251 attribute = None 

252 if "attribute" in request.json: 

253 attribute = request.json["attribute"] 

254 

255 try: 

256 description_df = session.model_controller.describe_model( 

257 session, project_name, name_no_version, attribute, version=version 

258 ) 

259 return description_df.to_dict("records") 

260 except Exception: 

261 return http_error( 

262 HTTPStatus.BAD_REQUEST, 

263 "ML handler unsupported", 

264 f"ML handler for {model_name} does not support model description", 

265 )