mito-ai 0.1.50__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. mito_ai/__init__.py +114 -0
  2. mito_ai/_version.py +4 -0
  3. mito_ai/anthropic_client.py +334 -0
  4. mito_ai/app_deploy/__init__.py +6 -0
  5. mito_ai/app_deploy/app_deploy_utils.py +44 -0
  6. mito_ai/app_deploy/handlers.py +345 -0
  7. mito_ai/app_deploy/models.py +98 -0
  8. mito_ai/app_manager/__init__.py +4 -0
  9. mito_ai/app_manager/handlers.py +167 -0
  10. mito_ai/app_manager/models.py +71 -0
  11. mito_ai/app_manager/utils.py +24 -0
  12. mito_ai/auth/README.md +18 -0
  13. mito_ai/auth/__init__.py +6 -0
  14. mito_ai/auth/handlers.py +96 -0
  15. mito_ai/auth/urls.py +13 -0
  16. mito_ai/chat_history/handlers.py +63 -0
  17. mito_ai/chat_history/urls.py +32 -0
  18. mito_ai/completions/completion_handlers/__init__.py +3 -0
  19. mito_ai/completions/completion_handlers/agent_auto_error_fixup_handler.py +59 -0
  20. mito_ai/completions/completion_handlers/agent_execution_handler.py +66 -0
  21. mito_ai/completions/completion_handlers/chat_completion_handler.py +141 -0
  22. mito_ai/completions/completion_handlers/code_explain_handler.py +113 -0
  23. mito_ai/completions/completion_handlers/completion_handler.py +42 -0
  24. mito_ai/completions/completion_handlers/inline_completer_handler.py +48 -0
  25. mito_ai/completions/completion_handlers/smart_debug_handler.py +160 -0
  26. mito_ai/completions/completion_handlers/utils.py +147 -0
  27. mito_ai/completions/handlers.py +415 -0
  28. mito_ai/completions/message_history.py +401 -0
  29. mito_ai/completions/models.py +404 -0
  30. mito_ai/completions/prompt_builders/__init__.py +3 -0
  31. mito_ai/completions/prompt_builders/agent_execution_prompt.py +57 -0
  32. mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +160 -0
  33. mito_ai/completions/prompt_builders/agent_system_message.py +472 -0
  34. mito_ai/completions/prompt_builders/chat_name_prompt.py +15 -0
  35. mito_ai/completions/prompt_builders/chat_prompt.py +116 -0
  36. mito_ai/completions/prompt_builders/chat_system_message.py +92 -0
  37. mito_ai/completions/prompt_builders/explain_code_prompt.py +32 -0
  38. mito_ai/completions/prompt_builders/inline_completer_prompt.py +197 -0
  39. mito_ai/completions/prompt_builders/prompt_constants.py +170 -0
  40. mito_ai/completions/prompt_builders/smart_debug_prompt.py +199 -0
  41. mito_ai/completions/prompt_builders/utils.py +84 -0
  42. mito_ai/completions/providers.py +284 -0
  43. mito_ai/constants.py +63 -0
  44. mito_ai/db/__init__.py +3 -0
  45. mito_ai/db/crawlers/__init__.py +6 -0
  46. mito_ai/db/crawlers/base_crawler.py +61 -0
  47. mito_ai/db/crawlers/constants.py +43 -0
  48. mito_ai/db/crawlers/snowflake.py +71 -0
  49. mito_ai/db/handlers.py +168 -0
  50. mito_ai/db/models.py +31 -0
  51. mito_ai/db/urls.py +34 -0
  52. mito_ai/db/utils.py +185 -0
  53. mito_ai/docker/mssql/compose.yml +37 -0
  54. mito_ai/docker/mssql/init/setup.sql +21 -0
  55. mito_ai/docker/mysql/compose.yml +18 -0
  56. mito_ai/docker/mysql/init/setup.sql +13 -0
  57. mito_ai/docker/oracle/compose.yml +17 -0
  58. mito_ai/docker/oracle/init/setup.sql +20 -0
  59. mito_ai/docker/postgres/compose.yml +17 -0
  60. mito_ai/docker/postgres/init/setup.sql +13 -0
  61. mito_ai/enterprise/__init__.py +3 -0
  62. mito_ai/enterprise/utils.py +15 -0
  63. mito_ai/file_uploads/__init__.py +3 -0
  64. mito_ai/file_uploads/handlers.py +248 -0
  65. mito_ai/file_uploads/urls.py +21 -0
  66. mito_ai/gemini_client.py +232 -0
  67. mito_ai/log/handlers.py +38 -0
  68. mito_ai/log/urls.py +21 -0
  69. mito_ai/logger.py +37 -0
  70. mito_ai/openai_client.py +382 -0
  71. mito_ai/path_utils.py +70 -0
  72. mito_ai/rules/handlers.py +44 -0
  73. mito_ai/rules/urls.py +22 -0
  74. mito_ai/rules/utils.py +56 -0
  75. mito_ai/settings/handlers.py +41 -0
  76. mito_ai/settings/urls.py +20 -0
  77. mito_ai/settings/utils.py +42 -0
  78. mito_ai/streamlit_conversion/agent_utils.py +37 -0
  79. mito_ai/streamlit_conversion/prompts/prompt_constants.py +172 -0
  80. mito_ai/streamlit_conversion/prompts/prompt_utils.py +10 -0
  81. mito_ai/streamlit_conversion/prompts/streamlit_app_creation_prompt.py +46 -0
  82. mito_ai/streamlit_conversion/prompts/streamlit_error_correction_prompt.py +28 -0
  83. mito_ai/streamlit_conversion/prompts/streamlit_finish_todo_prompt.py +45 -0
  84. mito_ai/streamlit_conversion/prompts/streamlit_system_prompt.py +56 -0
  85. mito_ai/streamlit_conversion/prompts/update_existing_app_prompt.py +50 -0
  86. mito_ai/streamlit_conversion/search_replace_utils.py +94 -0
  87. mito_ai/streamlit_conversion/streamlit_agent_handler.py +144 -0
  88. mito_ai/streamlit_conversion/streamlit_utils.py +85 -0
  89. mito_ai/streamlit_conversion/validate_streamlit_app.py +105 -0
  90. mito_ai/streamlit_preview/__init__.py +6 -0
  91. mito_ai/streamlit_preview/handlers.py +111 -0
  92. mito_ai/streamlit_preview/manager.py +152 -0
  93. mito_ai/streamlit_preview/urls.py +22 -0
  94. mito_ai/streamlit_preview/utils.py +29 -0
  95. mito_ai/tests/__init__.py +3 -0
  96. mito_ai/tests/chat_history/test_chat_history.py +211 -0
  97. mito_ai/tests/completions/completion_handlers_utils_test.py +190 -0
  98. mito_ai/tests/conftest.py +53 -0
  99. mito_ai/tests/create_agent_system_message_prompt_test.py +22 -0
  100. mito_ai/tests/data/prompt_lg.py +69 -0
  101. mito_ai/tests/data/prompt_sm.py +6 -0
  102. mito_ai/tests/data/prompt_xl.py +13 -0
  103. mito_ai/tests/data/stock_data.sqlite3 +0 -0
  104. mito_ai/tests/db/conftest.py +39 -0
  105. mito_ai/tests/db/connections_test.py +102 -0
  106. mito_ai/tests/db/mssql_test.py +29 -0
  107. mito_ai/tests/db/mysql_test.py +29 -0
  108. mito_ai/tests/db/oracle_test.py +29 -0
  109. mito_ai/tests/db/postgres_test.py +29 -0
  110. mito_ai/tests/db/schema_test.py +93 -0
  111. mito_ai/tests/db/sqlite_test.py +31 -0
  112. mito_ai/tests/db/test_db_constants.py +61 -0
  113. mito_ai/tests/deploy_app/test_app_deploy_utils.py +89 -0
  114. mito_ai/tests/file_uploads/__init__.py +2 -0
  115. mito_ai/tests/file_uploads/test_handlers.py +282 -0
  116. mito_ai/tests/message_history/test_generate_short_chat_name.py +120 -0
  117. mito_ai/tests/message_history/test_message_history_utils.py +469 -0
  118. mito_ai/tests/open_ai_utils_test.py +152 -0
  119. mito_ai/tests/performance_test.py +329 -0
  120. mito_ai/tests/providers/test_anthropic_client.py +447 -0
  121. mito_ai/tests/providers/test_azure.py +631 -0
  122. mito_ai/tests/providers/test_capabilities.py +120 -0
  123. mito_ai/tests/providers/test_gemini_client.py +195 -0
  124. mito_ai/tests/providers/test_mito_server_utils.py +448 -0
  125. mito_ai/tests/providers/test_model_resolution.py +130 -0
  126. mito_ai/tests/providers/test_openai_client.py +57 -0
  127. mito_ai/tests/providers/test_provider_completion_exception.py +66 -0
  128. mito_ai/tests/providers/test_provider_limits.py +42 -0
  129. mito_ai/tests/providers/test_providers.py +382 -0
  130. mito_ai/tests/providers/test_retry_logic.py +389 -0
  131. mito_ai/tests/providers/test_stream_mito_server_utils.py +140 -0
  132. mito_ai/tests/providers/utils.py +85 -0
  133. mito_ai/tests/rules/conftest.py +26 -0
  134. mito_ai/tests/rules/rules_test.py +117 -0
  135. mito_ai/tests/server_limits_test.py +406 -0
  136. mito_ai/tests/settings/conftest.py +26 -0
  137. mito_ai/tests/settings/settings_test.py +70 -0
  138. mito_ai/tests/settings/test_settings_constants.py +9 -0
  139. mito_ai/tests/streamlit_conversion/__init__.py +3 -0
  140. mito_ai/tests/streamlit_conversion/test_apply_search_replace.py +240 -0
  141. mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +246 -0
  142. mito_ai/tests/streamlit_conversion/test_streamlit_utils.py +193 -0
  143. mito_ai/tests/streamlit_conversion/test_validate_streamlit_app.py +112 -0
  144. mito_ai/tests/streamlit_preview/test_streamlit_preview_handler.py +118 -0
  145. mito_ai/tests/streamlit_preview/test_streamlit_preview_manager.py +292 -0
  146. mito_ai/tests/test_constants.py +47 -0
  147. mito_ai/tests/test_telemetry.py +12 -0
  148. mito_ai/tests/user/__init__.py +2 -0
  149. mito_ai/tests/user/test_user.py +120 -0
  150. mito_ai/tests/utils/__init__.py +3 -0
  151. mito_ai/tests/utils/test_anthropic_utils.py +162 -0
  152. mito_ai/tests/utils/test_gemini_utils.py +98 -0
  153. mito_ai/tests/version_check_test.py +169 -0
  154. mito_ai/user/handlers.py +45 -0
  155. mito_ai/user/urls.py +21 -0
  156. mito_ai/utils/__init__.py +3 -0
  157. mito_ai/utils/anthropic_utils.py +168 -0
  158. mito_ai/utils/create.py +94 -0
  159. mito_ai/utils/db.py +74 -0
  160. mito_ai/utils/error_classes.py +42 -0
  161. mito_ai/utils/gemini_utils.py +133 -0
  162. mito_ai/utils/message_history_utils.py +87 -0
  163. mito_ai/utils/mito_server_utils.py +242 -0
  164. mito_ai/utils/open_ai_utils.py +200 -0
  165. mito_ai/utils/provider_utils.py +49 -0
  166. mito_ai/utils/schema.py +86 -0
  167. mito_ai/utils/server_limits.py +152 -0
  168. mito_ai/utils/telemetry_utils.py +480 -0
  169. mito_ai/utils/utils.py +89 -0
  170. mito_ai/utils/version_utils.py +94 -0
  171. mito_ai/utils/websocket_base.py +88 -0
  172. mito_ai/version_check.py +60 -0
  173. mito_ai-0.1.50.data/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +7 -0
  174. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/build_log.json +728 -0
  175. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/package.json +243 -0
  176. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +238 -0
  177. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +37 -0
  178. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js +21602 -0
  179. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js.map +1 -0
  180. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +198 -0
  181. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +1 -0
  182. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.78d3ccb73e7ca1da3aae.js +619 -0
  183. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.78d3ccb73e7ca1da3aae.js.map +1 -0
  184. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/style.js +4 -0
  185. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +712 -0
  186. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +1 -0
  187. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_apis_signOut_mjs-node_module-75790d.688c25857e7b81b1740f.js +533 -0
  188. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_apis_signOut_mjs-node_module-75790d.688c25857e7b81b1740f.js.map +1 -0
  189. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js +6941 -0
  190. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js.map +1 -0
  191. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +1021 -0
  192. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +1 -0
  193. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +59698 -0
  194. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +1 -0
  195. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_react-dom_client_js-node_modules_aws-amplify_ui-react_dist_styles_css.b43d4249e4d3dac9ad7b.js +7440 -0
  196. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_react-dom_client_js-node_modules_aws-amplify_ui-react_dist_styles_css.b43d4249e4d3dac9ad7b.js.map +1 -0
  197. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +2792 -0
  198. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +1 -0
  199. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +4859 -0
  200. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +1 -0
  201. mito_ai-0.1.50.dist-info/METADATA +221 -0
  202. mito_ai-0.1.50.dist-info/RECORD +205 -0
  203. mito_ai-0.1.50.dist-info/WHEEL +4 -0
  204. mito_ai-0.1.50.dist-info/entry_points.txt +2 -0
  205. mito_ai-0.1.50.dist-info/licenses/LICENSE +3 -0
mito_ai/utils/utils.py ADDED
@@ -0,0 +1,89 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ import os
5
+ import pkg_resources
6
+ import subprocess
7
+ import sys
8
+ import uuid
9
+ from typing import List, Optional, Tuple, Union
10
+ from tornado.httpclient import AsyncHTTPClient
11
+
12
+ def get_random_id() -> str:
13
+ """
14
+ Creates a new random ID for the user, which for any given user,
15
+ should only happen once.
16
+ """
17
+ return str(uuid.uuid1())
18
+
19
+ def is_running_test() -> bool:
20
+ """
21
+ A helper function that quickly returns if the current code is running
22
+ inside of a test, which is useful for making sure we don't generate
23
+ tons of logs.
24
+ """
25
+ # Pytest injects PYTEST_CURRENT_TEST into the current environment when running
26
+ running_pytests = "PYTEST_CURRENT_TEST" in os.environ
27
+
28
+ # Github injects CI into the environment when running
29
+ running_ci = 'CI' in os.environ and os.environ['CI'] is not None
30
+
31
+ return running_pytests or running_ci
32
+
33
+ def get_installed_packages() -> List[str]:
34
+ """
35
+ Get a list of all installed packages.
36
+ """
37
+ return [
38
+ package.key
39
+ for package in sorted(pkg_resources.working_set, key=lambda x: x.key)
40
+ ]
41
+
42
+ def install_packages(packages: List[str]) -> dict[str, Union[bool, str, None]]:
43
+ """
44
+ Install a list of packages.
45
+
46
+ Returns:
47
+ dict: A dictionary containing:
48
+ - 'success': bool
49
+ - 'error': error message captured from the subprocess call or None if no error
50
+ """
51
+ result: dict[str, Union[bool, str, None]] = {
52
+ 'success': True,
53
+ 'error': None
54
+ }
55
+
56
+ for package in packages:
57
+ try:
58
+ subprocess.check_call([sys.executable, "-m", "pip", "install", package])
59
+ except subprocess.CalledProcessError as e:
60
+ result['success'] = False
61
+ result['error'] = str(e)
62
+
63
+ return result
64
+
65
+ def _create_http_client(timeout: int, max_retries: int) -> Tuple[AsyncHTTPClient, Optional[int]]:
66
+ """
67
+ Create an HTTP client with appropriate timeout settings.
68
+
69
+ Args:
70
+ timeout: The timeout in seconds
71
+ max_retries: The maximum number of retries
72
+
73
+ Returns:
74
+ A tuple containing the HTTP client and the timeout value in milliseconds
75
+ """
76
+ from .utils import is_running_test # local import to avoid circular import if needed
77
+ if is_running_test():
78
+ # If we are running in a test environment, setting the request_timeout fails for some reason.
79
+ http_client = AsyncHTTPClient(defaults=dict(user_agent="Mito-AI client"))
80
+ http_client_timeout = None
81
+ else:
82
+ # To avoid 599 client timeout errors, we set the request_timeout. By default, the HTTP client
83
+ # timesout after 20 seconds. We update this to match the timeout we give to OpenAI.
84
+ # The OpenAI timeouts are denoted in seconds, whereas the HTTP client expects milliseconds.
85
+ # We also give the HTTP client a 10 second buffer to account for
86
+ http_client_timeout = timeout * 1000 * max_retries + 10000
87
+ http_client = AsyncHTTPClient(defaults=dict(user_agent="Mito-AI client", request_timeout=http_client_timeout))
88
+
89
+ return http_client, http_client_timeout
@@ -0,0 +1,94 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ import os
5
+ from typing import cast
6
+ from .schema import UJ_MITOSHEET_ENTERPRISE, UJ_MITOSHEET_PRO
7
+ from .db import get_user_field
8
+
9
+ # Check if helper packages are installed
10
+ try:
11
+ import mitosheet_helper_pro
12
+ MITOSHEET_HELPER_PRO = True
13
+ except ImportError:
14
+ MITOSHEET_HELPER_PRO = False
15
+ try:
16
+ import mitosheet_helper_enterprise
17
+ MITOSHEET_HELPER_ENTERPRISE = True
18
+ except ImportError:
19
+ MITOSHEET_HELPER_ENTERPRISE = False
20
+
21
+ try:
22
+ import mitosheet_private
23
+ MITOSHEET_PRIVATE = True
24
+ except ImportError:
25
+ MITOSHEET_PRIVATE = False
26
+
27
+ # This is a legacy helper that we don't use anymore, however, we're keeping it for now
28
+ # for backwards compatibility, since I'm not 100% confident that nobody is currently using it.
29
+ try:
30
+ import mitosheet_helper_private
31
+ MITOSHEET_HELPER_PRIVATE = True
32
+ except ImportError:
33
+ MITOSHEET_HELPER_PRIVATE = False
34
+
35
+
36
+ def is_pro() -> bool:
37
+ """
38
+ Helper function for returning if this is a
39
+ pro deployment of mito
40
+ """
41
+
42
+ # This package overides the user.json
43
+ if MITOSHEET_HELPER_PRO:
44
+ return True
45
+
46
+ # This package overides the user.json
47
+ if MITOSHEET_PRIVATE:
48
+ return True
49
+
50
+ # Check if the config is set
51
+ # TODO: Check if the mito config pro is set to true.
52
+ # I don't think that any user is on pro via this method
53
+
54
+ # If you're on Mito Enterprise, then you get all Mito Pro features
55
+ if is_enterprise():
56
+ return True
57
+
58
+ pro = get_user_field(UJ_MITOSHEET_PRO)
59
+ if pro is None:
60
+ return False
61
+
62
+ return bool(pro)
63
+
64
+ def is_enterprise() -> bool:
65
+ """
66
+ Helper function for returning if this is a Mito Enterprise
67
+ users
68
+ """
69
+
70
+ # This package overides the user.json
71
+ if MITOSHEET_HELPER_ENTERPRISE:
72
+ return True
73
+
74
+ # TODO: Check if the mito config enterprise is set to true.
75
+ # I don't think that any user is on enterprise via this method
76
+
77
+ is_enterprise = get_user_field(UJ_MITOSHEET_ENTERPRISE)
78
+ if is_enterprise is None:
79
+ return False
80
+
81
+ # TODO: Check if someone has a temp enterprise license set
82
+
83
+ return bool(is_enterprise)
84
+
85
+
86
+ def is_mitosheet_private() -> bool:
87
+ """
88
+ Helper function for returning if this is a Mito Private
89
+ users
90
+ """
91
+
92
+ if MITOSHEET_PRIVATE:
93
+ return True
94
+ return False
@@ -0,0 +1,88 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ import json
5
+ import logging
6
+ from dataclasses import asdict
7
+ from http import HTTPStatus
8
+ from typing import Any, Dict, Optional
9
+
10
+ import tornado
11
+ from jupyter_core.utils import ensure_async
12
+ from jupyter_server.base.handlers import JupyterHandler
13
+ from tornado.websocket import WebSocketHandler
14
+
15
+ from mito_ai.logger import get_logger
16
+
17
+
18
+ class BaseWebSocketHandler(JupyterHandler, WebSocketHandler):
19
+ """Base WebSocket handler with common functionality for Mito AI services."""
20
+
21
+ def initialize(self) -> None:
22
+ """Initialize the WebSocket handler."""
23
+ super().initialize()
24
+ self.log.debug("Initializing websocket connection %s", self.request.path)
25
+
26
+ @property
27
+ def log(self) -> logging.Logger:
28
+ """Use Mito AI logger."""
29
+ return get_logger()
30
+
31
+ async def pre_get(self) -> None:
32
+ """Handle WebSocket authentication/authorization."""
33
+ # Authenticate the request before opening the WebSocket
34
+ user = self.current_user
35
+ if user is None:
36
+ self.log.warning("Couldn't authenticate WebSocket connection")
37
+ raise tornado.web.HTTPError(HTTPStatus.UNAUTHORIZED)
38
+
39
+ # Authorize the user
40
+ if not await ensure_async(
41
+ self.authorizer.is_authorized(self, user, "execute", "mito-ai")
42
+ ):
43
+ raise tornado.web.HTTPError(HTTPStatus.FORBIDDEN)
44
+
45
+ async def get(self, *args: Any, **kwargs: Dict[str, Any]) -> None:
46
+ """Get an event to open a socket or check service availability."""
47
+ # Check if this is just a service availability check
48
+ if self.get_query_argument('check_availability', None) == 'true':
49
+ self.set_status(HTTPStatus.OK)
50
+ self.finish()
51
+ return
52
+
53
+ await ensure_async(self.pre_get()) # type: ignore
54
+
55
+ reply = super().get(*args, **kwargs)
56
+ if reply is not None:
57
+ await reply
58
+
59
+ def reply(self, reply: Any) -> None:
60
+ """Write a reply object to the WebSocket connection.
61
+
62
+ Args:
63
+ reply: The reply object, which must be a dataclass instance.
64
+ """
65
+ message = asdict(reply)
66
+ super().write_message(message)
67
+
68
+ def on_close(self) -> None:
69
+ """Invoked when the WebSocket is closed."""
70
+ self.log.debug("WebSocket closed: %s", self.request.path)
71
+
72
+ def parse_message(self, message: str) -> Dict[str, Any]:
73
+ """Parse an incoming message.
74
+
75
+ Args:
76
+ message: The JSON message string to parse.
77
+
78
+ Returns:
79
+ The parsed message as a dict.
80
+
81
+ Raises:
82
+ ValueError: If the message is not valid JSON.
83
+ """
84
+ try:
85
+ return json.loads(message) # type: ignore
86
+ except ValueError as e:
87
+ self.log.error("Invalid message: %s", e)
88
+ raise
@@ -0,0 +1,60 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ """
5
+ Version check endpoint for Mito AI to compare local and PyPI versions.
6
+ """
7
+ import json
8
+ import logging
9
+ import time
10
+ from functools import lru_cache
11
+ from typing import Tuple, Optional, Any
12
+ import requests
13
+ import tornado.web
14
+ from tornado.web import RequestHandler
15
+ from mito_ai._version import __version__
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ class VersionCheckHandler(RequestHandler):
20
+ """Handler for checking the latest version of Mito AI on PyPI."""
21
+
22
+ # Cache PyPI results for 1 hour (3600 seconds)
23
+ @staticmethod
24
+ @lru_cache(maxsize=1)
25
+ def _get_latest_version() -> Tuple[Optional[str], float]:
26
+ """Get the latest version from PyPI with caching."""
27
+ cache_time = time.time()
28
+ try:
29
+ response = requests.get("https://pypi.org/pypi/mito-ai/json", timeout=3)
30
+ response.raise_for_status()
31
+ pypi_data = response.json()
32
+ latest_version = pypi_data.get("info", {}).get("version", "")
33
+ return latest_version, cache_time
34
+ except (requests.RequestException, json.JSONDecodeError) as e:
35
+ logger.warning(f"Failed to fetch latest version from PyPI: {str(e)}")
36
+ return None, cache_time
37
+
38
+ def get(self) -> None:
39
+ """Get the current and latest Mito AI versions."""
40
+ try:
41
+ # Get current package version from _version.py
42
+ current_version = __version__
43
+
44
+ # Get latest version from PyPI with caching
45
+ latest_version, cache_time = self._get_latest_version()
46
+ cache_age = time.time() - cache_time
47
+
48
+ # Include cache information in response
49
+ self.set_header("Content-Type", "application/json")
50
+ self.write(json.dumps({
51
+ "current_version": current_version,
52
+ "latest_version": latest_version,
53
+ "cache_age_seconds": int(cache_age)
54
+ }))
55
+ except Exception as e:
56
+ logger.error(f"Error in version check: {str(e)}")
57
+ self.set_status(500)
58
+ self.write(json.dumps({
59
+ "error": "Failed to check versions"
60
+ }))
@@ -0,0 +1,7 @@
1
+ {
2
+ "ServerApp": {
3
+ "jpserver_extensions": {
4
+ "mito_ai": true
5
+ }
6
+ }
7
+ }