mito-ai 0.1.40__py3-none-any.whl → 0.1.41__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.

Potentially problematic release.


This version of mito-ai might be problematic. Click here for more details.

Files changed (55) hide show
  1. mito_ai/__init__.py +12 -6
  2. mito_ai/_version.py +1 -1
  3. mito_ai/app_builder/handlers.py +1 -2
  4. mito_ai/completions/handlers.py +1 -1
  5. mito_ai/completions/message_history.py +9 -1
  6. mito_ai/completions/models.py +1 -1
  7. mito_ai/completions/prompt_builders/agent_execution_prompt.py +2 -0
  8. mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +8 -0
  9. mito_ai/completions/prompt_builders/agent_system_message.py +17 -0
  10. mito_ai/constants.py +3 -2
  11. mito_ai/file_uploads/__init__.py +3 -0
  12. mito_ai/file_uploads/handlers.py +225 -0
  13. mito_ai/file_uploads/urls.py +21 -0
  14. mito_ai/openai_client.py +1 -1
  15. mito_ai/tests/file_uploads/__init__.py +2 -0
  16. mito_ai/tests/file_uploads/test_handlers.py +267 -0
  17. mito_ai/tests/message_history/test_message_history_utils.py +57 -4
  18. mito_ai/utils/mito_server_utils.py +7 -0
  19. mito_ai/utils/server_limits.py +1 -1
  20. mito_ai/utils/telemetry_utils.py +26 -9
  21. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +102 -100
  22. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/package.json +4 -2
  23. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +3 -1
  24. mito_ai-0.1.40.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.55d9f8ca386d87856d2d.js → mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.01a962c68c8fae380f30.js +1782 -1027
  25. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.01a962c68c8fae380f30.js.map +1 -0
  26. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_aws-amplify_core_dist_esm_singleton_apis_fetchAuthSession_mjs.182232e7bc6311fe4528.js +63 -0
  27. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_aws-amplify_core_dist_esm_singleton_apis_fetchAuthSession_mjs.182232e7bc6311fe4528.js.map +1 -0
  28. mito_ai-0.1.40.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.264103d9addd1e166113.js → mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.9a70f033717ba8689564.js +49 -25
  29. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.9a70f033717ba8689564.js.map +1 -0
  30. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_mjs.16430abf3466c3153f59.js +4574 -0
  31. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_mjs.16430abf3466c3153f59.js.map +1 -0
  32. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_core_dist_esm_singleton_Amplify_mjs.3c0035b95fe369aede82.js +2345 -0
  33. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_core_dist_esm_singleton_Amplify_mjs.3c0035b95fe369aede82.js.map +1 -0
  34. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_core_dist_esm_singleton_apis_fetchAuthSession_mjs-node_modul-758875.dc495fd682071d97070c.js +7498 -0
  35. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_core_dist_esm_singleton_apis_fetchAuthSession_mjs-node_modul-758875.dc495fd682071d97070c.js.map +1 -0
  36. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +1021 -0
  37. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +1 -0
  38. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.61289bff0db44828605b.js +60178 -0
  39. mito_ai-0.1.41.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.61289bff0db44828605b.js.map +1 -0
  40. {mito_ai-0.1.40.dist-info → mito_ai-0.1.41.dist-info}/METADATA +1 -1
  41. {mito_ai-0.1.40.dist-info → mito_ai-0.1.41.dist-info}/RECORD +53 -36
  42. mito_ai-0.1.40.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.55d9f8ca386d87856d2d.js.map +0 -1
  43. mito_ai-0.1.40.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.264103d9addd1e166113.js.map +0 -1
  44. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
  45. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +0 -0
  46. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
  47. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +0 -0
  48. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +0 -0
  49. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js +0 -0
  50. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js.map +0 -0
  51. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
  52. {mito_ai-0.1.40.data → mito_ai-0.1.41.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
  53. {mito_ai-0.1.40.dist-info → mito_ai-0.1.41.dist-info}/WHEEL +0 -0
  54. {mito_ai-0.1.40.dist-info → mito_ai-0.1.41.dist-info}/entry_points.txt +0 -0
  55. {mito_ai-0.1.40.dist-info → mito_ai-0.1.41.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,267 @@
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 tempfile
6
+ import pytest
7
+ from unittest.mock import Mock, patch
8
+ import tornado.web
9
+ from tornado.httputil import HTTPServerRequest
10
+ from tornado.web import Application
11
+
12
+ from mito_ai.file_uploads.handlers import FileUploadHandler
13
+
14
+
15
+ @pytest.fixture
16
+ def temp_dir():
17
+ """Create a temporary directory for test files."""
18
+ temp_dir = tempfile.mkdtemp()
19
+ original_cwd = os.getcwd()
20
+ os.chdir(temp_dir)
21
+ yield temp_dir
22
+ os.chdir(original_cwd)
23
+ # Clean up temporary files
24
+ for file in os.listdir(temp_dir):
25
+ os.remove(os.path.join(temp_dir, file))
26
+ os.rmdir(temp_dir)
27
+
28
+
29
+ @pytest.fixture
30
+ def handler():
31
+ """Create a FileUploadHandler instance for testing."""
32
+ app = Application()
33
+ request = HTTPServerRequest(method="POST", uri="/upload")
34
+
35
+ # Mock the connection to avoid Tornado's assertion
36
+ request.connection = Mock()
37
+
38
+ handler = FileUploadHandler(app, request)
39
+
40
+ # Mock methods properly to avoid mypy errors
41
+ handler.write = Mock() # type: ignore
42
+ handler.finish = Mock() # type: ignore
43
+ handler.set_status = Mock() # type: ignore
44
+ handler.get_argument = Mock() # type: ignore
45
+
46
+ # Mock authentication for Jupyter server
47
+ handler._jupyter_current_user = "test_user" # type: ignore
48
+
49
+ return handler
50
+
51
+
52
+ def test_validate_file_upload_success(handler):
53
+ """Test successful file upload validation."""
54
+ handler.request.files = {"file": [Mock(filename="test.csv", body=b"data")]} # type: ignore
55
+ result = handler._validate_file_upload()
56
+ assert result is True
57
+
58
+
59
+ def test_validate_file_upload_failure(handler):
60
+ """Test file upload validation when no file is present."""
61
+ handler.request.files = {} # type: ignore
62
+ result = handler._validate_file_upload()
63
+ assert result is False
64
+ handler.set_status.assert_called_with(400)
65
+
66
+
67
+ def test_regular_upload_success(handler, temp_dir):
68
+ """Test successful regular (non-chunked) file upload."""
69
+ filename = "test.csv"
70
+ file_data = b"test,data\n1,2"
71
+ notebook_dir = temp_dir
72
+
73
+ handler._handle_regular_upload(filename, file_data, notebook_dir)
74
+
75
+ # Verify file was written
76
+ file_path = os.path.join(notebook_dir, filename)
77
+ with open(file_path, "rb") as f:
78
+ content = f.read()
79
+ assert content == file_data
80
+
81
+ # Verify response
82
+ handler.write.assert_called_with(
83
+ {"success": True, "filename": filename, "path": file_path}
84
+ )
85
+
86
+
87
+ def test_chunked_upload_first_chunk(handler, temp_dir):
88
+ """Test handling first chunk of a chunked upload."""
89
+ filename = "large_file.csv"
90
+ file_data = b"chunk1_data"
91
+ chunk_number = "1"
92
+ total_chunks = "3"
93
+ notebook_dir = temp_dir
94
+
95
+ handler._handle_chunked_upload(
96
+ filename, file_data, chunk_number, total_chunks, notebook_dir
97
+ )
98
+
99
+ # Verify chunk was saved (check temp dir structure)
100
+ assert filename in handler._temp_dirs
101
+ temp_dir_path = handler._temp_dirs[filename]["temp_dir"]
102
+ chunk_file = os.path.join(temp_dir_path, "chunk_1")
103
+ assert os.path.exists(chunk_file)
104
+
105
+ # Verify response indicates chunk received but not complete
106
+ handler.write.assert_called_with(
107
+ {
108
+ "success": True,
109
+ "chunk_received": True,
110
+ "chunk_number": 1,
111
+ "total_chunks": 3,
112
+ }
113
+ )
114
+
115
+
116
+ def test_chunked_upload_completion(handler, temp_dir):
117
+ """Test completing a chunked upload when all chunks are received."""
118
+ filename = "large_file.csv"
119
+ total_chunks = 2
120
+ notebook_dir = temp_dir
121
+
122
+ # Process first chunk
123
+ handler._handle_chunked_upload(
124
+ filename, b"chunk1_data", "1", str(total_chunks), notebook_dir
125
+ )
126
+
127
+ # Process final chunk
128
+ handler._handle_chunked_upload(
129
+ filename, b"chunk2_data", "2", str(total_chunks), notebook_dir
130
+ )
131
+
132
+ # Verify final file was created
133
+ file_path = os.path.join(notebook_dir, filename)
134
+ assert os.path.exists(file_path)
135
+ with open(file_path, "rb") as f:
136
+ content = f.read()
137
+ assert content == b"chunk1_datachunk2_data"
138
+
139
+ # Verify temp dir was cleaned up
140
+ assert filename not in handler._temp_dirs
141
+
142
+ # Verify completion response
143
+ handler.write.assert_called_with(
144
+ {
145
+ "success": True,
146
+ "filename": filename,
147
+ "path": file_path,
148
+ "chunk_complete": True,
149
+ }
150
+ )
151
+
152
+
153
+ def test_error_handling(handler):
154
+ """Test error handling in upload process."""
155
+ error_message = "Test error message"
156
+ status_code = 500
157
+
158
+ handler._handle_error(error_message, status_code)
159
+
160
+ handler.set_status.assert_called_with(status_code)
161
+ handler.write.assert_called_with({"error": error_message})
162
+ handler.finish.assert_called_once()
163
+
164
+
165
+ @patch("mito_ai.file_uploads.handlers.FileUploadHandler._validate_file_upload")
166
+ def test_post_method_regular_upload(mock_validate, handler):
167
+ """Test POST method for regular upload."""
168
+ mock_validate.return_value = True
169
+ handler.request.files = {"file": [Mock(filename="test.csv", body=b"data")]} # type: ignore
170
+ handler.get_argument.return_value = None # No chunk parameters
171
+
172
+ handler.post()
173
+
174
+ mock_validate.assert_called_once()
175
+ handler.finish.assert_called_once()
176
+
177
+
178
+ @patch("mito_ai.file_uploads.handlers.FileUploadHandler._validate_file_upload")
179
+ def test_post_method_chunked_upload(mock_validate, handler):
180
+ """Test POST method for chunked upload."""
181
+ mock_validate.return_value = True
182
+ handler.request.files = {"file": [Mock(filename="test.csv", body=b"data")]} # type: ignore
183
+ handler.get_argument.side_effect = lambda name, default=None: {
184
+ "chunk_number": "1",
185
+ "total_chunks": "3",
186
+ }.get(name, default)
187
+
188
+ handler.post()
189
+
190
+ mock_validate.assert_called_once()
191
+ handler.finish.assert_called_once()
192
+
193
+
194
+ def test_are_all_chunks_received_true(handler, temp_dir):
195
+ """Test that all chunks are detected when present."""
196
+ filename = "test.csv"
197
+ total_chunks = 2
198
+
199
+ # Manually set up the temp dir structure
200
+ temp_dir_path = tempfile.mkdtemp(prefix=f"mito_upload_{filename}_")
201
+ handler._temp_dirs[filename] = {
202
+ "temp_dir": temp_dir_path,
203
+ "total_chunks": total_chunks,
204
+ "received_chunks": {1, 2},
205
+ }
206
+
207
+ result = handler._are_all_chunks_received(filename, total_chunks)
208
+ assert result is True
209
+
210
+ # Clean up
211
+ import shutil
212
+
213
+ shutil.rmtree(temp_dir_path)
214
+
215
+
216
+ def test_are_all_chunks_received_false(handler, temp_dir):
217
+ """Test that missing chunks are detected."""
218
+ filename = "test.csv"
219
+ total_chunks = 2
220
+
221
+ # Manually set up the temp dir structure with only one chunk
222
+ temp_dir_path = tempfile.mkdtemp(prefix=f"mito_upload_{filename}_")
223
+ handler._temp_dirs[filename] = {
224
+ "temp_dir": temp_dir_path,
225
+ "total_chunks": total_chunks,
226
+ "received_chunks": {1}, # Only chunk 1 received
227
+ }
228
+
229
+ result = handler._are_all_chunks_received(filename, total_chunks)
230
+ assert result is False
231
+
232
+ # Clean up
233
+ import shutil
234
+
235
+ shutil.rmtree(temp_dir_path)
236
+
237
+
238
+ def test_save_chunk(handler, temp_dir):
239
+ """Test saving individual chunks."""
240
+ filename = "test.csv"
241
+ file_data = b"chunk_data"
242
+ chunk_number = 1
243
+ total_chunks = 3
244
+
245
+ # Mock the file operations to avoid filesystem issues
246
+ with patch("builtins.open", create=True) as mock_open:
247
+ mock_file = Mock()
248
+ mock_open.return_value.__enter__.return_value = mock_file
249
+
250
+ handler._save_chunk(filename, file_data, chunk_number, total_chunks)
251
+
252
+ # Verify temp dir was created in the handler's tracking
253
+ assert filename in handler._temp_dirs
254
+ temp_dir_path = handler._temp_dirs[filename]["temp_dir"]
255
+
256
+ # Verify the expected chunk filename was used
257
+ expected_chunk_filename = os.path.join(temp_dir_path, f"chunk_{chunk_number}")
258
+ mock_open.assert_called_with(expected_chunk_filename, "wb")
259
+
260
+ # Verify file data was written
261
+ mock_file.write.assert_called_with(file_data)
262
+
263
+ # Verify chunk was marked as received
264
+ assert chunk_number in handler._temp_dirs[filename]["received_chunks"]
265
+
266
+ # Clean up
267
+ del handler._temp_dirs[filename]
@@ -7,9 +7,9 @@ from openai.types.chat import ChatCompletionMessageParam
7
7
  from mito_ai.utils.message_history_utils import trim_sections_from_message_content, trim_old_messages
8
8
  from mito_ai.completions.prompt_builders.chat_prompt import create_chat_prompt
9
9
  from mito_ai.completions.prompt_builders.agent_execution_prompt import create_agent_execution_prompt
10
- from mito_ai.completions.prompt_builders.agent_smart_debug_prompt import (
11
- create_agent_smart_debug_prompt,
12
- )
10
+ from mito_ai.completions.prompt_builders.agent_smart_debug_prompt import create_agent_smart_debug_prompt
11
+ from unittest.mock import Mock, patch
12
+ from mito_ai.completions.message_history import GlobalMessageHistory, ChatThread
13
13
  from mito_ai.completions.prompt_builders.smart_debug_prompt import create_error_prompt
14
14
  from mito_ai.completions.prompt_builders.explain_code_prompt import create_explain_code_prompt
15
15
  from mito_ai.completions.models import (
@@ -27,6 +27,9 @@ from mito_ai.completions.prompt_builders.prompt_constants import (
27
27
  CONTENT_REMOVED_PLACEHOLDER,
28
28
  )
29
29
 
30
+
31
+
32
+
30
33
  # Standard test data for multiple tests
31
34
  TEST_VARIABLES = ["'df': pd.DataFrame({'col1': [1, 2, 3], 'col2': [4, 5, 6]})"]
32
35
  TEST_FILES = ["data.csv", "script.py"]
@@ -386,4 +389,54 @@ def test_trim_mixed_content_messages() -> None:
386
389
 
387
390
  # Verify that the recent messages are untouched
388
391
  assert trimmed_messages[1] == message_list[1]
389
- assert trimmed_messages[2] == message_list[2]
392
+ assert trimmed_messages[2] == message_list[2]
393
+
394
+
395
+ def test_get_display_history_calls_update_last_interaction() -> None:
396
+ """Test that get_display_history calls _update_last_interaction when retrieving a thread."""
397
+
398
+ # Create a mock thread
399
+ thread_id = ThreadID("test-thread-id")
400
+ mock_thread = Mock(spec=ChatThread)
401
+ mock_thread.display_history = [{"role": "user", "content": "test message"}]
402
+ mock_thread.last_interaction_ts = 1234567890.0
403
+
404
+ # Create message history instance and add the mock thread
405
+ message_history = GlobalMessageHistory()
406
+ message_history._chat_threads = {thread_id: mock_thread}
407
+
408
+ # Mock the _update_last_interaction method
409
+ with patch.object(message_history, '_update_last_interaction') as mock_update:
410
+ with patch.object(message_history, '_save_thread_to_disk') as mock_save:
411
+ # Call get_display_history
412
+ result = message_history.get_display_history(thread_id)
413
+
414
+ # Verify _update_last_interaction was called with the thread
415
+ mock_update.assert_called_once_with(mock_thread)
416
+
417
+ # Verify _save_thread_to_disk was also called
418
+ mock_save.assert_called_once_with(mock_thread)
419
+
420
+ # Verify the result is correct
421
+ assert result == [{"role": "user", "content": "test message"}]
422
+
423
+
424
+ def test_get_display_history_returns_empty_for_nonexistent_thread() -> None:
425
+ """Test that get_display_history returns empty list for non-existent thread."""
426
+ from mito_ai.completions.message_history import GlobalMessageHistory
427
+ from mito_ai.completions.models import ThreadID
428
+
429
+ message_history = GlobalMessageHistory()
430
+ thread_id = ThreadID("nonexistent-thread-id")
431
+
432
+ # Mock the methods to ensure they're not called
433
+ with patch.object(message_history, '_update_last_interaction') as mock_update:
434
+ with patch.object(message_history, '_save_thread_to_disk') as mock_save:
435
+ result = message_history.get_display_history(thread_id)
436
+
437
+ # Verify methods were not called since thread doesn't exist
438
+ mock_update.assert_not_called()
439
+ mock_save.assert_not_called()
440
+
441
+ # Verify empty result
442
+ assert result == []
@@ -11,6 +11,7 @@ from tornado.httpclient import HTTPResponse
11
11
  from mito_ai.constants import MITO_GEMINI_URL
12
12
  from mito_ai.utils.utils import _create_http_client
13
13
 
14
+ MITO_ERROR_MARKER = "MITO_ERROR_MARKER:"
14
15
 
15
16
  class ProviderCompletionException(Exception):
16
17
  """Custom exception for Mito server errors that converts well to CompletionError."""
@@ -179,6 +180,12 @@ async def stream_response_from_mito_server(
179
180
  if chunk_processor:
180
181
  processed_chunk = chunk_processor(chunk)
181
182
 
183
+ # Check if this chunk contains an error marker
184
+ if processed_chunk.startswith(MITO_ERROR_MARKER):
185
+ error_message = processed_chunk[len(MITO_ERROR_MARKER):]
186
+ print(f"Detected error in {provider_name} stream: {error_message}")
187
+ raise ProviderCompletionException(error_message, provider_name=provider_name)
188
+
182
189
  if reply_fn is not None and message_id is not None:
183
190
  # Send the chunk directly to the frontend
184
191
  reply_fn(CompletionStreamChunk(
@@ -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
@@ -337,15 +337,6 @@ def log_db_connection_success(connection_type: str, schema: Dict[str, Any]) -> N
337
337
  },
338
338
  )
339
339
 
340
- def log_ai_completion_retry(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: BaseException) -> None:
341
- log(MITO_AI_COMPLETION_RETRY, params={KEY_TYPE_PARAM: key_type, "message_type": message_type}, key_type=key_type, error=error)
342
-
343
- def log_ai_completion_error(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: BaseException) -> None:
344
- log(MITO_AI_COMPLETION_ERROR, params={KEY_TYPE_PARAM: key_type, "message_type": message_type}, key_type=key_type, error=error)
345
-
346
- def log_mito_server_free_tier_limit_reached(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType) -> None:
347
- log(MITO_SERVER_FREE_TIER_LIMIT_REACHED, params={KEY_TYPE_PARAM: key_type, "message_type": message_type}, key_type=key_type)
348
-
349
340
  def log_db_connection_error(connection_type: str, error_message: str) -> None:
350
341
  log(
351
342
  "mito_ai_db_connection_error",
@@ -354,7 +345,33 @@ def log_db_connection_error(connection_type: str, error_message: str) -> None:
354
345
  "error_message": error_message,
355
346
  }
356
347
  )
348
+
349
+ def log_file_upload_attempt(
350
+ filename: str, file_extension: str, is_chunked: bool, total_chunks: int
351
+ ) -> None:
352
+ log(
353
+ "mito_ai_file_upload_attempt",
354
+ params={
355
+ "filename": filename,
356
+ "file_extension": file_extension,
357
+ "is_chunked": is_chunked,
358
+ "total_chunks": total_chunks,
359
+ },
360
+ )
361
+
362
+ def log_file_upload_failure(error: str) -> None:
363
+ log("mito_ai_file_upload_failure", params={"error_message": error})
364
+
365
+ def log_ai_completion_retry(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: BaseException) -> None:
366
+ log(MITO_AI_COMPLETION_RETRY, params={KEY_TYPE_PARAM: key_type, "message_type": message_type}, key_type=key_type, error=error)
357
367
 
368
+ def log_ai_completion_error(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: BaseException) -> None:
369
+ log(MITO_AI_COMPLETION_ERROR, params={KEY_TYPE_PARAM: key_type, "message_type": message_type}, key_type=key_type, error=error)
370
+
371
+ def log_mito_server_free_tier_limit_reached(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType) -> None:
372
+ log(MITO_SERVER_FREE_TIER_LIMIT_REACHED, params={KEY_TYPE_PARAM: key_type, "message_type": message_type}, key_type=key_type)
373
+
374
+
358
375
  #################################
359
376
  # Streamlit Conversion
360
377
  #################################