system_router.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. import textwrap
  2. from datetime import datetime, timezone
  3. from typing import Optional
  4. import psutil
  5. from fastapi import Depends, Query
  6. from core.base import R2RException, RunType
  7. from core.base.api.models import (
  8. GenericMessageResponse,
  9. WrappedGenericMessageResponse,
  10. WrappedLogsResponse,
  11. WrappedServerStatsResponse,
  12. WrappedSettingsResponse,
  13. )
  14. from core.providers import (
  15. HatchetOrchestrationProvider,
  16. SimpleOrchestrationProvider,
  17. )
  18. from .base_router import BaseRouterV3
  19. class SystemRouter(BaseRouterV3):
  20. def __init__(
  21. self,
  22. providers,
  23. services,
  24. orchestration_provider: (
  25. HatchetOrchestrationProvider | SimpleOrchestrationProvider
  26. ),
  27. run_type: RunType = RunType.MANAGEMENT,
  28. ):
  29. super().__init__(providers, services, orchestration_provider, run_type)
  30. self.start_time = datetime.now(timezone.utc)
  31. def _setup_routes(self):
  32. @self.router.get(
  33. "/health",
  34. openapi_extra={
  35. "x-codeSamples": [
  36. {
  37. "lang": "Python",
  38. "source": textwrap.dedent(
  39. """
  40. from r2r import R2RClient
  41. client = R2RClient("http://localhost:7272")
  42. # when using auth, do client.login(...)
  43. result = client.system.health()
  44. """
  45. ),
  46. },
  47. {
  48. "lang": "JavaScript",
  49. "source": textwrap.dedent(
  50. """
  51. const { r2rClient } = require("r2r-js");
  52. const client = new r2rClient("http://localhost:7272");
  53. function main() {
  54. const response = await client.system.health();
  55. }
  56. main();
  57. """
  58. ),
  59. },
  60. {
  61. "lang": "CLI",
  62. "source": textwrap.dedent(
  63. """
  64. r2r health
  65. """
  66. ),
  67. },
  68. {
  69. "lang": "cURL",
  70. "source": textwrap.dedent(
  71. """
  72. curl -X POST "https://api.example.com/v3/health"\\
  73. -H "Content-Type: application/json" \\
  74. -H "Authorization: Bearer YOUR_API_KEY" \\
  75. """
  76. ),
  77. },
  78. ]
  79. },
  80. )
  81. @self.base_endpoint
  82. async def health_check() -> WrappedGenericMessageResponse:
  83. return GenericMessageResponse(message="ok") # type: ignore
  84. @self.router.get(
  85. "/system/settings",
  86. openapi_extra={
  87. "x-codeSamples": [
  88. {
  89. "lang": "Python",
  90. "source": textwrap.dedent(
  91. """
  92. from r2r import R2RClient
  93. client = R2RClient("http://localhost:7272")
  94. # when using auth, do client.login(...)
  95. result = client.system.settings()
  96. """
  97. ),
  98. },
  99. {
  100. "lang": "JavaScript",
  101. "source": textwrap.dedent(
  102. """
  103. const { r2rClient } = require("r2r-js");
  104. const client = new r2rClient("http://localhost:7272");
  105. function main() {
  106. const response = await client.system.settings();
  107. }
  108. main();
  109. """
  110. ),
  111. },
  112. {
  113. "lang": "CLI",
  114. "source": textwrap.dedent(
  115. """
  116. r2r system settings
  117. """
  118. ),
  119. },
  120. {
  121. "lang": "cURL",
  122. "source": textwrap.dedent(
  123. """
  124. curl -X POST "https://api.example.com/v3/system/settings" \\
  125. -H "Content-Type: application/json" \\
  126. -H "Authorization: Bearer YOUR_API_KEY" \\
  127. """
  128. ),
  129. },
  130. ]
  131. },
  132. )
  133. @self.base_endpoint
  134. async def app_settings(
  135. auth_user=Depends(self.providers.auth.auth_wrapper),
  136. ) -> WrappedSettingsResponse:
  137. if not auth_user.is_superuser:
  138. raise R2RException(
  139. "Only a superuser can call the `system/settings` endpoint.",
  140. 403,
  141. )
  142. return await self.services["management"].app_settings()
  143. @self.router.get(
  144. "/system/status",
  145. openapi_extra={
  146. "x-codeSamples": [
  147. {
  148. "lang": "Python",
  149. "source": textwrap.dedent(
  150. """
  151. from r2r import R2RClient
  152. client = R2RClient("http://localhost:7272")
  153. # when using auth, do client.login(...)
  154. result = client.system.status()
  155. """
  156. ),
  157. },
  158. {
  159. "lang": "JavaScript",
  160. "source": textwrap.dedent(
  161. """
  162. const { r2rClient } = require("r2r-js");
  163. const client = new r2rClient("http://localhost:7272");
  164. function main() {
  165. const response = await client.system.status();
  166. }
  167. main();
  168. """
  169. ),
  170. },
  171. {
  172. "lang": "CLI",
  173. "source": textwrap.dedent(
  174. """
  175. r2r system status
  176. """
  177. ),
  178. },
  179. {
  180. "lang": "cURL",
  181. "source": textwrap.dedent(
  182. """
  183. curl -X POST "https://api.example.com/v3/system/status" \\
  184. -H "Content-Type: application/json" \\
  185. -H "Authorization: Bearer YOUR_API_KEY" \\
  186. """
  187. ),
  188. },
  189. ]
  190. },
  191. )
  192. @self.base_endpoint
  193. async def server_stats(
  194. auth_user=Depends(self.providers.auth.auth_wrapper),
  195. ) -> WrappedServerStatsResponse:
  196. if not auth_user.is_superuser:
  197. raise R2RException(
  198. "Only an authorized user can call the `system/status` endpoint.",
  199. 403,
  200. )
  201. return { # type: ignore
  202. "start_time": self.start_time.isoformat(),
  203. "uptime_seconds": (
  204. datetime.now(timezone.utc) - self.start_time
  205. ).total_seconds(),
  206. "cpu_usage": psutil.cpu_percent(),
  207. "memory_usage": psutil.virtual_memory().percent,
  208. }
  209. @self.router.get(
  210. "/system/logs",
  211. openapi_extra={
  212. "x-codeSamples": [
  213. {
  214. "lang": "Python",
  215. "source": textwrap.dedent(
  216. """
  217. from r2r import R2RClient
  218. client = R2RClient("http://localhost:7272")
  219. # when using auth, do client.login(...)
  220. result = client.system.logs()
  221. """
  222. ),
  223. },
  224. {
  225. "lang": "JavaScript",
  226. "source": textwrap.dedent(
  227. """
  228. const { r2rClient } = require("r2r-js");
  229. const client = new r2rClient("http://localhost:7272");
  230. function main() {
  231. const response = await client.system.logs({});
  232. }
  233. main();
  234. """
  235. ),
  236. },
  237. {
  238. "lang": "CLI",
  239. "source": textwrap.dedent(
  240. """
  241. r2r system logs
  242. """
  243. ),
  244. },
  245. {
  246. "lang": "cURL",
  247. "source": textwrap.dedent(
  248. """
  249. curl -X POST "https://api.example.com/v3/system/logs" \\
  250. -H "Content-Type: application/json" \\
  251. -H "Authorization: Bearer YOUR_API_KEY" \\
  252. """
  253. ),
  254. },
  255. ]
  256. },
  257. )
  258. @self.base_endpoint
  259. async def logs(
  260. run_type_filter: Optional[str] = Query(""),
  261. offset: int = Query(
  262. 0,
  263. ge=0,
  264. description="Specifies the number of objects to skip. Defaults to 0.",
  265. ),
  266. limit: int = Query(
  267. 100,
  268. ge=1,
  269. le=1000,
  270. description="Specifies a limit on the number of objects to return, ranging between 1 and 100. Defaults to 100.",
  271. ),
  272. auth_user=Depends(self.providers.auth.auth_wrapper),
  273. ) -> WrappedLogsResponse:
  274. if not auth_user.is_superuser:
  275. raise R2RException(
  276. "Only a superuser can call the `system/logs` endpoint.",
  277. 403,
  278. )
  279. return await self.services["management"].logs(
  280. run_type_filter=run_type_filter,
  281. offset=offset,
  282. limit=limit,
  283. )