Coverage for skema/rest/api.py: 100%

44 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-30 17:15 +0000

1import os 

2from typing import Dict 

3 

4from fastapi import Depends, FastAPI, Response, status 

5from fastapi.responses import PlainTextResponse 

6 

7from skema.rest import ( 

8 config, 

9 schema, 

10 workflows, 

11 proxies, 

12 integrated_text_reading_proxy, 

13 morae_proxy, 

14 metal_proxy, 

15 llm_proxy, 

16 utils 

17) 

18from skema.isa import isa_service 

19from skema.img2mml import eqn2mml 

20from skema.skema_py import server as code2fn 

21from skema.gromet.execution_engine import server as execution_engine 

22from skema.program_analysis.comment_extractor import server as comment_service 

23import httpx 

24 

25VERSION: str = os.environ.get("APP_VERSION", "????") 

26 

27# markdown 

28description = """ 

29REST API for the SKEMA system 

30""" 

31 

32contact = {"name": "SKEMA team", "url": "https://github.com/ml4ai/skema/issues"} 

33 

34tags_metadata = [ 

35 { 

36 "name": "core", 

37 "description": "Status checks, versions, etc.", 

38 "externalDocs": { 

39 "description": "Issues", 

40 "url": "https://github.com/ml4ai/skema/issues?q=is%3Aopen+is%3Aissue+label%3AIntegration", 

41 }, 

42 }, 

43 { 

44 "name": "workflows", 

45 "description": "Operations related to end-to-end SKEMA workflows", 

46 "externalDocs": { 

47 "description": "Issues", 

48 "url": "https://github.com/ml4ai/skema/issues?q=is%3Aopen+is%3Aissue+label%3AIntegration", 

49 }, 

50 }, 

51 { 

52 "name": "code2fn", 

53 "description": "Operations to transform source code into function networks.", 

54 "externalDocs": { 

55 "description": "Issues", 

56 "url": "https://github.com/ml4ai/skema/issues?q=is%3Aopen+is%3Aissue+label%3ACode2FN", 

57 }, 

58 }, 

59 { 

60 "name": "eqn2mml", 

61 "description": "Operations to transform equations", 

62 "externalDocs": { 

63 "description": "Issues", 

64 "url": "https://github.com/ml4ai/skema/issues?q=is%3Aopen+is%3Aissue+label%3AEquations", 

65 }, 

66 }, 

67 { 

68 "name": "morae", 

69 "description": "Operations to MORAE.", 

70 "externalDocs": { 

71 "description": "Issues", 

72 "url": "https://github.com/ml4ai/skema/issues?q=is%3Aopen+is%3Aissue+label%3AMORAE", 

73 }, 

74 }, 

75 { 

76 "name": "isa", 

77 "description": "Operations to ISA", 

78 "externalDocs": { 

79 "description": "Issues", 

80 "url": "https://github.com/ml4ai/skema/issues?q=is%3Aopen+is%3Aissue+label%3AISA", 

81 }, 

82 }, 

83 { 

84 "name": "text reading", 

85 "description": "Unified proxy and integration code for MIT and SKEMA TR pipelines", 

86 "externalDocs": { 

87 "description": "Issues", 

88 "url": "https://github.com/ml4ai/skema/issues?q=is%3Aopen+is%3Aissue+label%3AText%20Reading", 

89 }, 

90 }, 

91 { 

92 "name": "metal", 

93 "description": "AMR linking endpoints", 

94 }, 

95] 

96 

97app = FastAPI( 

98 version=VERSION, 

99 title="SKEMA API", 

100 description=description, 

101 contact=contact, 

102 openapi_tags=tags_metadata, 

103) 

104 

105app.include_router( 

106 workflows.router, 

107 prefix="/workflows", 

108 tags=["workflows"], 

109) 

110 

111app.include_router( 

112 eqn2mml.router, 

113 prefix="/eqn2mml", 

114 tags=["eqn2mml"], 

115) 

116 

117app.include_router( 

118 code2fn.router, 

119 prefix="/code2fn", 

120 tags=["code2fn"], 

121) 

122 

123app.include_router( 

124 comment_service.router, 

125 prefix="/code2fn", 

126 tags=["code2fn"], 

127) 

128 

129app.include_router( 

130 execution_engine.router, 

131 prefix="/execution-engine", 

132 tags=["execution-engine"], 

133) 

134app.include_router( 

135 morae_proxy.router, 

136 prefix="/morae", 

137 tags=["morae", "skema-rs"], 

138) 

139 

140app.include_router( 

141 llm_proxy.router, 

142 prefix="/morae", 

143 tags=["morae"], 

144) 

145 

146app.include_router( 

147 integrated_text_reading_proxy.router, 

148 prefix="/text-reading", 

149 tags=["text reading"] 

150) 

151 

152app.include_router( 

153 metal_proxy.router, 

154 prefix="/metal", 

155 tags=["metal"] 

156) 

157 

158app.include_router( 

159 isa_service.router, 

160 prefix="/isa", 

161 tags=["isa"] 

162) 

163 

164@app.head( 

165 "/version", 

166 tags=["core"], 

167 summary="API version", 

168 status_code=status.HTTP_200_OK 

169) 

170@app.get( 

171 "/version", 

172 tags=["core"], 

173 summary="API version", 

174 status_code=status.HTTP_200_OK 

175) 

176def version() -> str: 

177 return PlainTextResponse(VERSION) 

178 

179 

180@app.get( 

181 "/healthcheck", 

182 tags=["core"], 

183 summary="Health of component services", 

184 status_code=status.HTTP_200_OK, 

185 responses={ 

186 200: { 

187 "model": schema.HealthStatus, 

188 "description": "All component services are healthy (200 status)", 

189 }, 

190 500: { 

191 "model": schema.HealthStatus, 

192 "description": "One or more component services are unhealthy (non-200 status)", 

193 }, 

194 }, 

195) 

196async def healthcheck(response: Response, client: httpx.AsyncClient = Depends(utils.get_client)) -> schema.HealthStatus: 

197 morae_status = await morae_proxy.healthcheck(client) 

198 mathjax_status = eqn2mml.latex2mml_healthcheck() 

199 eqn2mml_status = eqn2mml.img2mml_healthcheck() 

200 code2fn_status = code2fn.healthcheck() 

201 text_reading_status = integrated_text_reading_proxy.healthcheck() 

202 metal_status = metal_proxy.healthcheck() 

203 # check if any services failing and alter response status code accordingly 

204 status_code = ( 

205 status.HTTP_200_OK 

206 if all( 

207 code == 200 

208 for code in [ 

209 morae_status, 

210 mathjax_status, 

211 eqn2mml_status, 

212 code2fn_status, 

213 text_reading_status, 

214 metal_status 

215 ] 

216 ) 

217 else status.HTTP_500_INTERNAL_SERVER_ERROR 

218 ) 

219 response.status_code = status_code 

220 return schema.HealthStatus( 

221 morae=morae_status, 

222 mathjax=mathjax_status, 

223 eqn2mml=eqn2mml_status, 

224 code2fn=code2fn_status, 

225 integrated_text_reading=text_reading_status, 

226 metal=metal_status 

227 ) 

228 

229@app.get("/environment-variables", tags=["core"], summary="Values of environment variables", include_in_schema=False) 

230async def environment_variables() -> Dict: 

231 return { 

232 "SKEMA_GRAPH_DB_PROTO": proxies.SKEMA_GRAPH_DB_PROTO, 

233 "SKEMA_GRAPH_DB_HOST": proxies.SKEMA_GRAPH_DB_HOST, 

234 "SKEMA_GRAPH_DB_PORT": proxies.SKEMA_GRAPH_DB_PORT, 

235 "SKEMA_RS_ADDRESS": proxies.SKEMA_RS_ADDESS, 

236 "SKEMA_RS_DEFAULT_TIMEOUT": config.SKEMA_RS_DEFAULT_TIMEOUT, 

237 

238 "SKEMA_MATHJAX_PROTOCOL": proxies.SKEMA_MATHJAX_PROTOCOL, 

239 "SKEMA_MATHJAX_HOST": proxies.SKEMA_MATHJAX_HOST, 

240 "SKEMA_MATHJAX_PORT": proxies.SKEMA_MATHJAX_PORT, 

241 "SKEMA_MATHJAX_ADDRESS": proxies.SKEMA_MATHJAX_ADDRESS, 

242 

243 "MIT_TR_ADDRESS": proxies.MIT_TR_ADDRESS, 

244 "SKEMA_TR_ADDRESS": proxies.SKEMA_TR_ADDRESS, 

245 "COSMOS_ADDRESS": proxies.COSMOS_ADDRESS 

246 }