mito-ai 0.1.33__py3-none-any.whl → 0.1.49__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 (146) hide show
  1. mito_ai/__init__.py +49 -9
  2. mito_ai/_version.py +1 -1
  3. mito_ai/anthropic_client.py +142 -67
  4. mito_ai/{app_builder → app_deploy}/__init__.py +1 -1
  5. mito_ai/app_deploy/app_deploy_utils.py +44 -0
  6. mito_ai/app_deploy/handlers.py +345 -0
  7. mito_ai/{app_builder → app_deploy}/models.py +35 -22
  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/agent_execution_handler.py +1 -1
  19. mito_ai/completions/completion_handlers/chat_completion_handler.py +4 -4
  20. mito_ai/completions/completion_handlers/utils.py +99 -37
  21. mito_ai/completions/handlers.py +57 -20
  22. mito_ai/completions/message_history.py +9 -1
  23. mito_ai/completions/models.py +31 -7
  24. mito_ai/completions/prompt_builders/agent_execution_prompt.py +21 -2
  25. mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +8 -0
  26. mito_ai/completions/prompt_builders/agent_system_message.py +115 -42
  27. mito_ai/completions/prompt_builders/chat_name_prompt.py +6 -6
  28. mito_ai/completions/prompt_builders/chat_prompt.py +18 -11
  29. mito_ai/completions/prompt_builders/chat_system_message.py +4 -0
  30. mito_ai/completions/prompt_builders/prompt_constants.py +23 -4
  31. mito_ai/completions/prompt_builders/utils.py +72 -10
  32. mito_ai/completions/providers.py +81 -47
  33. mito_ai/constants.py +25 -24
  34. mito_ai/file_uploads/__init__.py +3 -0
  35. mito_ai/file_uploads/handlers.py +248 -0
  36. mito_ai/file_uploads/urls.py +21 -0
  37. mito_ai/gemini_client.py +44 -48
  38. mito_ai/log/handlers.py +10 -3
  39. mito_ai/log/urls.py +3 -3
  40. mito_ai/openai_client.py +30 -44
  41. mito_ai/path_utils.py +70 -0
  42. mito_ai/streamlit_conversion/agent_utils.py +37 -0
  43. mito_ai/streamlit_conversion/prompts/prompt_constants.py +172 -0
  44. mito_ai/streamlit_conversion/prompts/prompt_utils.py +10 -0
  45. mito_ai/streamlit_conversion/prompts/streamlit_app_creation_prompt.py +46 -0
  46. mito_ai/streamlit_conversion/prompts/streamlit_error_correction_prompt.py +28 -0
  47. mito_ai/streamlit_conversion/prompts/streamlit_finish_todo_prompt.py +45 -0
  48. mito_ai/streamlit_conversion/prompts/streamlit_system_prompt.py +56 -0
  49. mito_ai/streamlit_conversion/prompts/update_existing_app_prompt.py +50 -0
  50. mito_ai/streamlit_conversion/search_replace_utils.py +94 -0
  51. mito_ai/streamlit_conversion/streamlit_agent_handler.py +144 -0
  52. mito_ai/streamlit_conversion/streamlit_utils.py +85 -0
  53. mito_ai/streamlit_conversion/validate_streamlit_app.py +105 -0
  54. mito_ai/streamlit_preview/__init__.py +6 -0
  55. mito_ai/streamlit_preview/handlers.py +111 -0
  56. mito_ai/streamlit_preview/manager.py +152 -0
  57. mito_ai/streamlit_preview/urls.py +22 -0
  58. mito_ai/streamlit_preview/utils.py +29 -0
  59. mito_ai/tests/chat_history/test_chat_history.py +211 -0
  60. mito_ai/tests/completions/completion_handlers_utils_test.py +190 -0
  61. mito_ai/tests/deploy_app/test_app_deploy_utils.py +89 -0
  62. mito_ai/tests/file_uploads/__init__.py +2 -0
  63. mito_ai/tests/file_uploads/test_handlers.py +282 -0
  64. mito_ai/tests/message_history/test_generate_short_chat_name.py +0 -4
  65. mito_ai/tests/message_history/test_message_history_utils.py +103 -23
  66. mito_ai/tests/open_ai_utils_test.py +18 -22
  67. mito_ai/tests/providers/test_anthropic_client.py +447 -0
  68. mito_ai/tests/providers/test_azure.py +2 -6
  69. mito_ai/tests/providers/test_capabilities.py +120 -0
  70. mito_ai/tests/{test_gemini_client.py → providers/test_gemini_client.py} +40 -36
  71. mito_ai/tests/providers/test_mito_server_utils.py +448 -0
  72. mito_ai/tests/providers/test_model_resolution.py +130 -0
  73. mito_ai/tests/providers/test_openai_client.py +57 -0
  74. mito_ai/tests/providers/test_provider_completion_exception.py +66 -0
  75. mito_ai/tests/providers/test_provider_limits.py +42 -0
  76. mito_ai/tests/providers/test_providers.py +382 -0
  77. mito_ai/tests/providers/test_retry_logic.py +389 -0
  78. mito_ai/tests/providers/test_stream_mito_server_utils.py +140 -0
  79. mito_ai/tests/providers/utils.py +85 -0
  80. mito_ai/tests/streamlit_conversion/__init__.py +3 -0
  81. mito_ai/tests/streamlit_conversion/test_apply_search_replace.py +240 -0
  82. mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +246 -0
  83. mito_ai/tests/streamlit_conversion/test_streamlit_utils.py +193 -0
  84. mito_ai/tests/streamlit_conversion/test_validate_streamlit_app.py +112 -0
  85. mito_ai/tests/streamlit_preview/test_streamlit_preview_handler.py +118 -0
  86. mito_ai/tests/streamlit_preview/test_streamlit_preview_manager.py +292 -0
  87. mito_ai/tests/test_constants.py +31 -3
  88. mito_ai/tests/test_telemetry.py +12 -0
  89. mito_ai/tests/user/__init__.py +2 -0
  90. mito_ai/tests/user/test_user.py +120 -0
  91. mito_ai/tests/utils/test_anthropic_utils.py +6 -6
  92. mito_ai/user/handlers.py +45 -0
  93. mito_ai/user/urls.py +21 -0
  94. mito_ai/utils/anthropic_utils.py +55 -121
  95. mito_ai/utils/create.py +17 -1
  96. mito_ai/utils/error_classes.py +42 -0
  97. mito_ai/utils/gemini_utils.py +39 -94
  98. mito_ai/utils/message_history_utils.py +7 -4
  99. mito_ai/utils/mito_server_utils.py +242 -0
  100. mito_ai/utils/open_ai_utils.py +38 -155
  101. mito_ai/utils/provider_utils.py +49 -0
  102. mito_ai/utils/server_limits.py +1 -1
  103. mito_ai/utils/telemetry_utils.py +137 -5
  104. {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +102 -100
  105. {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/package.json +4 -2
  106. {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +3 -1
  107. {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +2 -2
  108. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.281f4b9af60d620c6fb1.js → mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js +15948 -8403
  109. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js.map +1 -0
  110. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +198 -0
  111. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +1 -0
  112. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.4f1d00fd0c58fcc05d8d.js → mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.8b24b5b3b93f95205b56.js +58 -33
  113. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.8b24b5b3b93f95205b56.js.map +1 -0
  114. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.06083e515de4862df010.js → mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +10 -2
  115. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +1 -0
  116. mito_ai-0.1.49.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
  117. mito_ai-0.1.49.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
  118. mito_ai-0.1.49.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
  119. mito_ai-0.1.49.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
  120. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +1021 -0
  121. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +1 -0
  122. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +59698 -0
  123. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +1 -0
  124. mito_ai-0.1.49.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
  125. mito_ai-0.1.49.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
  126. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js → mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +2 -240
  127. mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +1 -0
  128. {mito_ai-0.1.33.dist-info → mito_ai-0.1.49.dist-info}/METADATA +5 -2
  129. mito_ai-0.1.49.dist-info/RECORD +205 -0
  130. mito_ai/app_builder/handlers.py +0 -218
  131. mito_ai/tests/providers_test.py +0 -438
  132. mito_ai/tests/test_anthropic_client.py +0 -270
  133. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.281f4b9af60d620c6fb1.js.map +0 -1
  134. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.4f1d00fd0c58fcc05d8d.js.map +0 -1
  135. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.06083e515de4862df010.js.map +0 -1
  136. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_html2canvas_dist_html2canvas_js.ea47e8c8c906197f8d19.js +0 -7842
  137. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_html2canvas_dist_html2canvas_js.ea47e8c8c906197f8d19.js.map +0 -1
  138. mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js.map +0 -1
  139. mito_ai-0.1.33.dist-info/RECORD +0 -134
  140. {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
  141. {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
  142. {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
  143. {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
  144. {mito_ai-0.1.33.dist-info → mito_ai-0.1.49.dist-info}/WHEEL +0 -0
  145. {mito_ai-0.1.33.dist-info → mito_ai-0.1.49.dist-info}/entry_points.txt +0 -0
  146. {mito_ai-0.1.33.dist-info → mito_ai-0.1.49.dist-info}/licenses/LICENSE +0 -0
@@ -10,6 +10,8 @@ import asyncio
10
10
  import json
11
11
  import time
12
12
  from typing import Any, Dict, List, Optional, Final, Union, AsyncGenerator, Tuple, Callable
13
+ from mito_ai.utils.mito_server_utils import get_response_from_mito_server, stream_response_from_mito_server
14
+ from mito_ai.utils.provider_utils import does_message_require_fast_model
13
15
  from tornado.httpclient import AsyncHTTPClient
14
16
  from openai.types.chat import ChatCompletionMessageParam
15
17
 
@@ -27,6 +29,8 @@ from mito_ai.constants import MITO_OPENAI_URL
27
29
  __user_email: Optional[str] = None
28
30
  __user_id: Optional[str] = None
29
31
 
32
+ FAST_OPENAI_MODEL = "gpt-4.1-nano"
33
+
30
34
  def _prepare_request_data_and_headers(
31
35
  last_message_content: Union[str, None],
32
36
  ai_completion_data: Dict[str, Any],
@@ -46,10 +50,7 @@ def _prepare_request_data_and_headers(
46
50
 
47
51
  Returns:
48
52
  A tuple containing the request data and headers
49
- """
50
- # Check that the user is allowed to use the Mito Server
51
- check_mito_server_quota(message_type)
52
-
53
+ """
53
54
  global __user_email, __user_id
54
55
 
55
56
  if __user_email is None:
@@ -89,47 +90,15 @@ async def get_ai_completion_from_mito_server(
89
90
  message_type
90
91
  )
91
92
 
92
- # Create HTTP client with appropriate timeout settings
93
- http_client, http_client_timeout = _create_http_client(timeout, max_retries)
94
-
95
- # There are several types of timeout errors that can happen here.
96
- # == 504 Timeout (tornado.httpclient.HTTPClientError: 504) ==
97
- # The server (AWS Lambda) took too long to process your request
98
- # == 599 Timeout (tornado.httpclient.HTTPClientError: 599) ==
99
- # The client (Tornado) gave up waiting for a response
100
-
101
- start_time = time.time()
102
- try:
103
- res = await http_client.fetch(
104
- # Important: DO NOT CHANGE MITO_AI_URL. If you want to use the dev endpoint,
105
- # go to the top of this file and change MITO_AI_URL to MITO_AI_DEV_URL. We
106
- # have a pytest that ensures that the MITO_AI_URL is always set to MITO_AI_PROD_URL
107
- # before merging into dev. So if you change which variable we are using here, the
108
- # test will not catch our mistakes.
109
- MITO_OPENAI_URL,
110
- method="POST",
111
- headers=headers,
112
- body=json.dumps(data),
113
- # For some reason, we need to add the request_timeout here as well
114
- request_timeout=http_client_timeout
115
- )
116
- print(f"Request completed in {time.time() - start_time:.2f} seconds")
117
- except Exception as e:
118
- print(f"Request failed after {time.time() - start_time:.2f} seconds with error: {str(e)}")
119
- raise
120
- finally:
121
- http_client.close()
122
-
123
- # The lambda function returns a dictionary with a completion entry in it,
124
- # so we just return that.
125
- content = json.loads(res.body)
126
-
127
- if "completion" in content:
128
- return content["completion"] # type: ignore
129
- elif "error" in content:
130
- raise Exception(f"{content['error']}")
131
- else:
132
- raise Exception(f"No completion found in response: {content}")
93
+ return await get_response_from_mito_server(
94
+ MITO_OPENAI_URL,
95
+ headers,
96
+ data,
97
+ timeout,
98
+ max_retries,
99
+ message_type,
100
+ provider_name="OpenAI"
101
+ )
133
102
 
134
103
  async def stream_ai_completion_from_mito_server(
135
104
  last_message_content: Union[str, None],
@@ -137,8 +106,8 @@ async def stream_ai_completion_from_mito_server(
137
106
  timeout: int,
138
107
  max_retries: int,
139
108
  message_type: MessageType,
140
- reply_fn: Optional[Callable[[Union[CompletionReply, CompletionStreamChunk]], None]] = None,
141
- message_id: Optional[str] = None,
109
+ reply_fn: Callable[[Union[CompletionReply, CompletionStreamChunk]], None],
110
+ message_id: str,
142
111
  ) -> AsyncGenerator[str, None]:
143
112
  """
144
113
  Stream AI completions from the Mito server.
@@ -158,7 +127,7 @@ async def stream_ai_completion_from_mito_server(
158
127
  Yields:
159
128
  Chunks of text from the streaming response
160
129
  """
161
- # ===== STEP 1: Prepare request data and headers =====
130
+ # Prepare request data and headers
162
131
  data, headers = _prepare_request_data_and_headers(
163
132
  last_message_content,
164
133
  ai_completion_data,
@@ -167,118 +136,36 @@ async def stream_ai_completion_from_mito_server(
167
136
  message_type
168
137
  )
169
138
 
170
- # ===== STEP 2: Create HTTP client with appropriate timeout settings =====
171
- http_client, http_client_timeout = _create_http_client(timeout, max_retries)
172
-
173
- # ===== STEP 3: Set up streaming infrastructure =====
174
- start_time = time.time()
175
- chunk_queue: asyncio.Queue[str] = asyncio.Queue()
176
- fetch_complete = False
177
-
178
- # Define a callback to process chunks and add them to the queue
179
- def chunk_callback(chunk: bytes) -> None:
180
- try:
181
- chunk_str = chunk.decode('utf-8')
182
- asyncio.create_task(chunk_queue.put(chunk_str))
183
- except Exception as e:
184
- print(f"Error processing streaming chunk: {str(e)}")
185
-
186
- # ===== STEP 4: Execute the streaming request =====
187
- fetch_future = None
188
- try:
189
- # Use fetch with streaming_callback to handle streaming responses.
190
- # The streaming_callback is not sent as part of the POST request.
191
- # It's a parameter for the Tornado AsyncHTTPClient.fetch() method that specifies
192
- # how to handle incoming data chunks as they arrive from the server.
193
- # When the server sends data in chunks, this callback function is called each time
194
- # a new chunk arrives, allowing for immediate processing without waiting for the
195
- # entire response to complete.
196
- fetch_future = http_client.fetch(
197
- MITO_OPENAI_URL,
198
- method="POST",
199
- headers=headers,
200
- body=json.dumps(data),
201
- request_timeout=http_client_timeout,
202
- streaming_callback=chunk_callback
203
- )
204
-
205
- # Create a task to wait for the fetch to complete
206
- async def wait_for_fetch() -> None:
207
- try:
208
- await fetch_future
209
- nonlocal fetch_complete
210
- fetch_complete = True
211
- print("Fetch completed")
212
- except Exception as e:
213
- print(f"Error in fetch: {str(e)}")
214
- raise
215
-
216
- # Start the task to wait for fetch completion
217
- fetch_task = asyncio.create_task(wait_for_fetch())
218
-
219
- # ===== STEP 5: Yield chunks as they arrive =====
220
- while not (fetch_complete and chunk_queue.empty()):
221
- try:
222
- # Wait for a chunk with a timeout. By setting the timeout, we 1. prevent deadlocks
223
- # which could happen if fetch_complete has not been set to true yet, and 2. it enables
224
- # periodic checking if the queue has a new chunk.
225
- chunk = await asyncio.wait_for(chunk_queue.get(), timeout=0.1)
226
-
227
- # If reply_fn is provided, send the chunk directly to the frontend
228
- if reply_fn and message_id:
229
- reply_fn(CompletionStreamChunk(
230
- parent_id=message_id,
231
- chunk=CompletionItem(
232
- content=chunk,
233
- isIncomplete=True,
234
- token=message_id,
235
- ),
236
- done=False,
237
- ))
238
-
239
- yield chunk
240
- except asyncio.TimeoutError:
241
- # No chunk available within timeout, check if fetch is complete
242
- if fetch_complete and chunk_queue.empty():
243
- break
244
-
245
- # Otherwise continue waiting for chunks
246
- continue
247
-
248
- print(f"\nStream completed in {time.time() - start_time:.2f} seconds")
249
-
250
- # Send a final chunk to indicate completion if reply_fn is provided
251
- if reply_fn and message_id:
252
- reply_fn(CompletionStreamChunk(
253
- parent_id=message_id,
254
- chunk=CompletionItem(
255
- content="",
256
- isIncomplete=False,
257
- token=message_id,
258
- ),
259
- done=True,
260
- ))
261
- except Exception as e:
262
- print(f"\nStream failed after {time.time() - start_time:.2f} seconds with error: {str(e)}")
263
- # If an exception occurred, ensure the fetch future is awaited to properly clean up
264
- if fetch_future:
265
- try:
266
- await fetch_future
267
- except Exception:
268
- pass
269
- raise
270
- finally:
271
- # ===== STEP 6: Clean up resources =====
272
- http_client.close()
139
+ # Use the unified streaming function
140
+ async for chunk in stream_response_from_mito_server(
141
+ url=MITO_OPENAI_URL,
142
+ headers=headers,
143
+ data=data,
144
+ timeout=timeout,
145
+ max_retries=max_retries,
146
+ message_type=message_type,
147
+ reply_fn=reply_fn,
148
+ message_id=message_id,
149
+ chunk_processor=None,
150
+ provider_name="OpenAI",
151
+ ):
152
+ yield chunk
273
153
 
274
154
 
275
155
  def get_open_ai_completion_function_params(
156
+ message_type: MessageType,
276
157
  model: str,
277
158
  messages: List[ChatCompletionMessageParam],
278
159
  stream: bool,
279
160
  response_format_info: Optional[ResponseFormatInfo] = None,
280
161
  ) -> Dict[str, Any]:
281
162
 
163
+ print("MESSAGE TYPE: ", message_type)
164
+ message_requires_fast_model = does_message_require_fast_model(message_type)
165
+ model = FAST_OPENAI_MODEL if message_requires_fast_model else model
166
+
167
+ print(f"model: {model}")
168
+
282
169
  completion_function_params = {
283
170
  "model": model,
284
171
  "stream": stream,
@@ -309,9 +196,5 @@ def get_open_ai_completion_function_params(
309
196
  "strict": True
310
197
  }
311
198
  }
312
-
313
- # o3-mini will error if we try setting the temperature
314
- if model == "gpt-4o-mini":
315
- completion_function_params["temperature"] = 0.0
316
199
 
317
200
  return completion_function_params
@@ -0,0 +1,49 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ from typing import Union
5
+
6
+ from mito_ai.completions.models import MessageType
7
+
8
+
9
+ def get_model_provider(model: str) -> Union[str, None]:
10
+ """
11
+ Determine the model type based on the model name prefix
12
+ """
13
+ if not model:
14
+ return None
15
+
16
+ model_lower = model.lower()
17
+
18
+ if model_lower.startswith('claude'):
19
+ return 'claude'
20
+ elif model_lower.startswith('gemini'):
21
+ return 'gemini'
22
+ elif model_lower.startswith('ollama'):
23
+ return 'ollama'
24
+ elif model_lower.startswith('gpt'):
25
+ return 'openai'
26
+
27
+ return None
28
+
29
+
30
+ def does_message_require_fast_model(message_type: MessageType) -> bool:
31
+ """
32
+ Determines if a message requires the fast model.
33
+
34
+ The fast model is used for messages that are not chat messages.
35
+ For example, inline completions and chat name generation need to be fast
36
+ so they don't slow down the user's experience.
37
+ """
38
+
39
+ if message_type in (MessageType.CHAT, MessageType.SMART_DEBUG, MessageType.CODE_EXPLAIN, MessageType.AGENT_EXECUTION, MessageType.AGENT_AUTO_ERROR_FIXUP):
40
+ return False
41
+ elif message_type in (MessageType.INLINE_COMPLETION, MessageType.CHAT_NAME_GENERATION):
42
+ return True
43
+ elif message_type in (MessageType.START_NEW_CHAT, MessageType.FETCH_HISTORY, MessageType.GET_THREADS, MessageType.DELETE_THREAD, MessageType.UPDATE_MODEL_CONFIG):
44
+ # These messages don't use any model, but we add them here for type safety
45
+ return True
46
+ else:
47
+ raise ValueError(f"Invalid message type: {message_type}")
48
+
49
+
@@ -28,7 +28,7 @@ free tier, but running AI models is expensive, so we need to limit the usage
28
28
  or we will no longer be able to provide this free tier.
29
29
  """
30
30
  # Monthly chat completions limit for free tier users
31
- OS_MONTHLY_AI_COMPLETIONS_LIMIT: Final[int] = 50
31
+ OS_MONTHLY_AI_COMPLETIONS_LIMIT: Final[int] = 150
32
32
 
33
33
  # Monthly autocomplete limit for free tier users
34
34
  OS_MONTHLY_AUTOCOMPLETE_LIMIT: Final[int] = 5000
@@ -3,7 +3,7 @@
3
3
 
4
4
  import json
5
5
  import os
6
- from typing import Any, Dict, Literal, Optional
6
+ from typing import Any, Dict, Literal, Optional, List
7
7
  from mito_ai.utils.version_utils import MITOSHEET_HELPER_PRIVATE, is_pro
8
8
  from mito_ai.utils.schema import UJ_AI_MITO_API_NUM_USAGES, UJ_MITOSHEET_TELEMETRY, UJ_STATIC_USER_ID, UJ_USER_EMAIL, UJ_FEEDBACKS_V2
9
9
  from mito_ai.utils.db import get_user_field
@@ -24,6 +24,7 @@ PRINT_LOGS = False
24
24
  # Constants for logging the success or error of Mito AI
25
25
  MITO_AI_COMPLETION_SUCCESS = 'mito_ai_success'
26
26
  MITO_AI_COMPLETION_ERROR = 'mito_ai_error'
27
+ MITO_AI_COMPLETION_RETRY = 'mito_ai_retry'
27
28
 
28
29
  # Params
29
30
  # - logging the type of key
@@ -82,7 +83,6 @@ def telemetry_turned_on(key_type: Optional[str] = None) -> bool:
82
83
  Helper function that tells you if logging is turned on or
83
84
  turned off on the entire Mito instance
84
85
  """
85
-
86
86
  # If the user is on the Mito server, then they are sending
87
87
  # us their information already
88
88
  if key_type == 'mito_server_key':
@@ -104,7 +104,7 @@ def telemetry_turned_on(key_type: Optional[str] = None) -> bool:
104
104
 
105
105
  return bool(telemetry)
106
106
 
107
- def identify(key_type: Optional[str] = None) -> None:
107
+ def identify(key_type: Optional[str] = None, is_electron: Optional[bool] = None) -> None:
108
108
  """
109
109
  Helper function for identifying a user. We just take
110
110
  their python version, mito version, and email.
@@ -125,6 +125,11 @@ def identify(key_type: Optional[str] = None) -> None:
125
125
  IS_DEV_MODE_PARAM: is_dev_mode(),
126
126
  UJ_FEEDBACKS_V2: feedbacks_v2
127
127
  }
128
+
129
+ if is_electron is not None:
130
+ # Only update field when we get info from the client. Don't default to False.
131
+ # becase we are only sending this info to the first completion_handler identify call.
132
+ params['is_mito_desktop'] = is_electron
128
133
 
129
134
  if not is_running_test():
130
135
  # TODO: If the user is in JupyterLite, we need to do some extra work.
@@ -174,7 +179,6 @@ def log(
174
179
  If telemetry is not turned off and we are not running tests,
175
180
  we log the ai event
176
181
  """
177
-
178
182
  final_params: Dict[str, Any] = params or {}
179
183
 
180
184
  # Then, make sure to add the user email
@@ -186,7 +190,24 @@ def log(
186
190
 
187
191
  if thread_id is not None:
188
192
  final_params['thread_id'] = thread_id
189
-
193
+
194
+ # Process parameters that need chunking
195
+ params_to_remove = []
196
+ params_to_add = {}
197
+
198
+ for param_name, param_value in final_params.items():
199
+ if isinstance(param_value, str) and len(param_value) > 250:
200
+ # Mark for removal
201
+ params_to_remove.append(param_name)
202
+ # Get chunked parameters
203
+ chunked_params = chunk_param(param_value, param_name)
204
+ params_to_add.update(chunked_params)
205
+
206
+ # Apply the changes
207
+ for param_name in params_to_remove:
208
+ del final_params[param_name]
209
+ final_params.update(params_to_add)
210
+
190
211
  # Finally, do the acutal logging. We do not log anything when tests are
191
212
  # running, or if telemetry is turned off
192
213
  if not is_running_test() and telemetry_turned_on(key_type):
@@ -346,3 +367,114 @@ def log_db_connection_error(connection_type: str, error_message: str) -> None:
346
367
  "error_message": error_message,
347
368
  }
348
369
  )
370
+
371
+ def log_file_upload_attempt(
372
+ filename: str, file_extension: str, is_chunked: bool, total_chunks: int
373
+ ) -> None:
374
+ log(
375
+ "mito_ai_file_upload_attempt",
376
+ params={
377
+ "filename": filename,
378
+ "file_extension": file_extension,
379
+ "is_chunked": is_chunked,
380
+ "total_chunks": total_chunks,
381
+ },
382
+ )
383
+
384
+ def log_file_upload_failure(error: str) -> None:
385
+ log("mito_ai_file_upload_failure", params={"error_message": error})
386
+
387
+ def log_ai_completion_retry(key_type: Literal['mito_server_key', 'user_key'], thread_id: str, message_type: MessageType, error: BaseException) -> None:
388
+ log(MITO_AI_COMPLETION_RETRY, params={KEY_TYPE_PARAM: key_type, "message_type": message_type.value}, thread_id=thread_id, key_type=key_type, error=error)
389
+
390
+ def log_ai_completion_error(
391
+ key_type: Literal['mito_server_key', 'user_key'],
392
+ thread_id: str,
393
+ message_type: MessageType,
394
+ error: BaseException
395
+ ) -> None:
396
+ log(MITO_AI_COMPLETION_ERROR, params={KEY_TYPE_PARAM: key_type, "message_type": message_type.value}, thread_id=thread_id, key_type=key_type, error=error)
397
+
398
+ def log_mito_server_free_tier_limit_reached(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType) -> None:
399
+ log(MITO_SERVER_FREE_TIER_LIMIT_REACHED, params={KEY_TYPE_PARAM: key_type, "message_type": message_type.value}, key_type=key_type)
400
+
401
+
402
+ #################################
403
+ # Streamlit Logs
404
+ #################################
405
+
406
+ ###
407
+ # Converting Notebook into Streamlit App code
408
+ ###
409
+
410
+ def log_streamlit_app_conversion_success(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, edit_prompt: str) -> None:
411
+ log(
412
+ "mito_ai_streamlit_app_conversion_success",
413
+ key_type=key_type,
414
+ params={
415
+ "edit_prompt": edit_prompt,
416
+ "message_type": message_type.value
417
+ }
418
+ )
419
+
420
+ def log_streamlit_app_validation_retry(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: List[str]) -> None:
421
+ log(
422
+ "mito_ai_streamlit_app_conversion_retry",
423
+ params={
424
+ "error_message": error,
425
+ "message_type": message_type.value
426
+ },
427
+ key_type=key_type
428
+ )
429
+
430
+ def log_streamlit_app_conversion_error(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error_message: str, formatted_traceback: str, edit_prompt: str) -> None:
431
+ log(
432
+ "mito_ai_streamlit_app_conversion_error",
433
+ params={
434
+ "error_message": error_message,
435
+ "traceback": formatted_traceback,
436
+ "edit_prompt": edit_prompt,
437
+ "message_type": message_type.value
438
+ },
439
+ key_type=key_type
440
+ )
441
+
442
+ ###
443
+ # Setting up Preview
444
+ ###
445
+
446
+ def log_streamlit_app_preview_success(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, edit_prompt: str) -> None:
447
+ log(
448
+ "mito_ai_streamlit_app_preview_success",
449
+ key_type=key_type,
450
+ params={
451
+ "edit_prompt": edit_prompt,
452
+ "message_type": message_type.value
453
+ }
454
+ )
455
+
456
+ def log_streamlit_app_preview_failure(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error_message: str, formatted_traceback: str, edit_prompt: str) -> None:
457
+ log(
458
+ "mito_ai_streamlit_app_preview_failure",
459
+ key_type=key_type,
460
+ params={
461
+ "error_message": error_message,
462
+ "traceback": formatted_traceback,
463
+ "message_type": message_type.value,
464
+ "edit_prompt": edit_prompt
465
+ }
466
+ )
467
+
468
+ ###
469
+ # Deploying Streamlit App
470
+ ###
471
+
472
+ def log_streamlit_app_deployment_failure(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: Dict) -> None:
473
+ log(
474
+ "mito_ai_streamlit_app_deployment_failure",
475
+ key_type=key_type,
476
+ params={
477
+ "error": error, # Contains all details in app_deploy.models.AppDeployError class
478
+ "message_type": message_type.value
479
+ }
480
+ )