mito-ai 0.1.37__py3-none-any.whl → 0.1.38__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 (40) hide show
  1. mito_ai/__init__.py +9 -1
  2. mito_ai/_version.py +1 -1
  3. mito_ai/app_builder/handlers.py +30 -30
  4. mito_ai/app_builder/models.py +1 -1
  5. mito_ai/log/handlers.py +10 -3
  6. mito_ai/log/urls.py +3 -3
  7. mito_ai/streamlit_conversion/streamlit_agent_handler.py +22 -6
  8. mito_ai/streamlit_conversion/streamlit_system_prompt.py +11 -0
  9. mito_ai/streamlit_conversion/streamlit_utils.py +8 -6
  10. mito_ai/streamlit_conversion/validate_and_run_streamlit_code.py +1 -0
  11. mito_ai/streamlit_preview/__init__.py +7 -0
  12. mito_ai/streamlit_preview/handlers.py +161 -0
  13. mito_ai/streamlit_preview/manager.py +159 -0
  14. mito_ai/streamlit_preview/urls.py +22 -0
  15. mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +16 -15
  16. mito_ai/tests/streamlit_conversion/test_streamlit_utils.py +4 -5
  17. mito_ai/tests/streamlit_preview/test_streamlit_preview_manager.py +302 -0
  18. mito_ai/utils/telemetry_utils.py +28 -1
  19. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +1 -1
  20. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/package.json +2 -2
  21. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +1 -1
  22. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +6 -1
  23. mito_ai-0.1.37.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.831f63b48760c7119b9b.js → mito_ai-0.1.38.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.5d1d7c234e2dc7c9d97b.js +542 -56
  24. mito_ai-0.1.38.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.5d1d7c234e2dc7c9d97b.js.map +1 -0
  25. mito_ai-0.1.37.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.93ecc9bc0edba61535cc.js → mito_ai-0.1.38.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.bcce4ea34631acf6dbbe.js +5 -5
  26. mito_ai-0.1.37.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.93ecc9bc0edba61535cc.js.map → mito_ai-0.1.38.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.bcce4ea34631acf6dbbe.js.map +1 -1
  27. {mito_ai-0.1.37.dist-info → mito_ai-0.1.38.dist-info}/METADATA +1 -1
  28. {mito_ai-0.1.37.dist-info → mito_ai-0.1.38.dist-info}/RECORD +39 -34
  29. mito_ai-0.1.37.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.831f63b48760c7119b9b.js.map +0 -1
  30. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
  31. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
  32. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +0 -0
  33. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +0 -0
  34. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js +0 -0
  35. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js.map +0 -0
  36. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
  37. {mito_ai-0.1.37.data → mito_ai-0.1.38.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
  38. {mito_ai-0.1.37.dist-info → mito_ai-0.1.38.dist-info}/WHEEL +0 -0
  39. {mito_ai-0.1.37.dist-info → mito_ai-0.1.38.dist-info}/entry_points.txt +0 -0
  40. {mito_ai-0.1.37.dist-info → mito_ai-0.1.38.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,22 @@
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 Any, List, Tuple
5
+ from jupyter_server.utils import url_path_join
6
+ from mito_ai.streamlit_preview.handlers import StreamlitPreviewHandler
7
+
8
+ def get_streamlit_preview_urls(base_url: str) -> List[Tuple[str, Any, dict]]:
9
+ """Get all streamlit preview related URL patterns.
10
+
11
+ Args:
12
+ base_url: The base URL for the Jupyter server
13
+
14
+ Returns:
15
+ List of (url_pattern, handler_class, handler_kwargs) tuples
16
+ """
17
+ BASE_URL = base_url + "/mito-ai"
18
+
19
+ return [
20
+ (url_path_join(BASE_URL, "streamlit-preview"), StreamlitPreviewHandler, {}),
21
+ (url_path_join(BASE_URL, "streamlit-preview/(.+)"), StreamlitPreviewHandler, {}),
22
+ ]
@@ -170,19 +170,19 @@ class TestStreamlitHandler:
170
170
  mock_validator.return_value = (False, "")
171
171
 
172
172
  # Mock file creation
173
- mock_create_file.return_value = (True, "File created successfully")
173
+ mock_create_file.return_value = (True, "/path/to/app", "File created successfully")
174
174
 
175
- result = await streamlit_handler("/path/to/notebook.ipynb", "/path/to/app")
175
+ result = await streamlit_handler("/path/to/notebook.ipynb")
176
176
 
177
177
  assert result[0] is True
178
- assert "File created successfully" in result[1]
178
+ assert "File created successfully" in result[2]
179
179
 
180
180
  # Verify calls
181
181
  mock_parse.assert_called_once_with("/path/to/notebook.ipynb")
182
182
  mock_generator_class.assert_called_once_with(mock_notebook_data)
183
183
  mock_generator.generate_streamlit_code.assert_called_once()
184
184
  mock_validator.assert_called_once_with("import streamlit\nst.title('Test')")
185
- mock_create_file.assert_called_once_with("/path/to/app", "import streamlit\nst.title('Test')")
185
+ mock_create_file.assert_called_once_with("/path/to", "import streamlit\nst.title('Test')")
186
186
 
187
187
  @pytest.mark.asyncio
188
188
  @patch('mito_ai.streamlit_conversion.streamlit_agent_handler.parse_jupyter_notebook_to_extract_required_content')
@@ -193,20 +193,21 @@ class TestStreamlitHandler:
193
193
  # Mock notebook parsing
194
194
  mock_notebook_data: dict = {"cells": []}
195
195
  mock_parse.return_value = mock_notebook_data
196
-
196
+
197
197
  # Mock code generation
198
198
  mock_generator = AsyncMock()
199
199
  mock_generator.generate_streamlit_code.return_value = "import streamlit\nst.title('Test')"
200
200
  mock_generator.correct_error_in_generation.return_value = "import streamlit\nst.title('Fixed')"
201
201
  mock_generator_class.return_value = mock_generator
202
-
203
- # Mock validation (always errors)
202
+
203
+ # Mock validation (always errors) - FIX: Return only 2 values
204
204
  mock_validator.return_value = (True, "Persistent error")
205
+
206
+ result = await streamlit_handler("/path/to/notebook.ipynb")
205
207
 
206
- result = await streamlit_handler("/path/to/notebook.ipynb", "/path/to/app")
207
-
208
+ # Verify the result indicates failure
208
209
  assert result[0] is False
209
- assert "Error generating streamlit code by agent" in result[1]
210
+ assert "Error generating streamlit code by agent" in result[2]
210
211
 
211
212
  # Verify that error correction was called 5 times (max retries)
212
213
  assert mock_generator.correct_error_in_generation.call_count == 5
@@ -231,12 +232,12 @@ class TestStreamlitHandler:
231
232
  mock_validator.return_value = (False, "")
232
233
 
233
234
  # Mock file creation failure
234
- mock_create_file.return_value = (False, "Permission denied")
235
+ mock_create_file.return_value = (False, None, "Permission denied")
235
236
 
236
- result = await streamlit_handler("/path/to/notebook.ipynb", "/path/to/app")
237
+ result = await streamlit_handler("/path/to/notebook.ipynb")
237
238
 
238
239
  assert result[0] is False
239
- assert "Permission denied" in result[1]
240
+ assert "Permission denied" in result[2]
240
241
 
241
242
  @pytest.mark.asyncio
242
243
  @patch('mito_ai.streamlit_conversion.streamlit_agent_handler.parse_jupyter_notebook_to_extract_required_content')
@@ -245,7 +246,7 @@ class TestStreamlitHandler:
245
246
  mock_parse.side_effect = FileNotFoundError("Notebook not found")
246
247
 
247
248
  with pytest.raises(FileNotFoundError, match="Notebook not found"):
248
- await streamlit_handler("/path/to/notebook.ipynb", "/path/to/app")
249
+ await streamlit_handler("/path/to/notebook.ipynb")
249
250
 
250
251
  @pytest.mark.asyncio
251
252
  @patch('mito_ai.streamlit_conversion.streamlit_agent_handler.parse_jupyter_notebook_to_extract_required_content')
@@ -262,4 +263,4 @@ class TestStreamlitHandler:
262
263
  mock_generator_class.return_value = mock_generator
263
264
 
264
265
  with pytest.raises(Exception, match="Generation failed"):
265
- await streamlit_handler("/path/to/notebook.ipynb", "/path/to/app")
266
+ await streamlit_handler("/path/to/notebook.ipynb")
@@ -13,7 +13,6 @@ from mito_ai.streamlit_conversion.streamlit_utils import (
13
13
  )
14
14
  from typing import Dict, Any
15
15
 
16
-
17
16
  class TestExtractCodeBlocks:
18
17
  """Test cases for extract_code_blocks function"""
19
18
 
@@ -52,7 +51,7 @@ class TestCreateAppFile:
52
51
  file_path = str(tmp_path)
53
52
  code = "import streamlit\nst.title('Test')"
54
53
 
55
- success, message = create_app_file(file_path, code)
54
+ success, app_path, message = create_app_file(file_path, code)
56
55
 
57
56
  assert success is True
58
57
  assert "Successfully created" in message
@@ -70,7 +69,7 @@ class TestCreateAppFile:
70
69
  file_path = "/nonexistent/path/that/should/fail"
71
70
  code = "import streamlit"
72
71
 
73
- success, message = create_app_file(file_path, code)
72
+ success, app_path, message = create_app_file(file_path, code)
74
73
 
75
74
  assert success is False
76
75
  assert "Error creating file" in message
@@ -81,7 +80,7 @@ class TestCreateAppFile:
81
80
  file_path = "/tmp/test"
82
81
  code = "import streamlit"
83
82
 
84
- success, message = create_app_file(file_path, code)
83
+ success, app_path, message = create_app_file(file_path, code)
85
84
 
86
85
  assert success is False
87
86
  assert "Unexpected error" in message
@@ -91,7 +90,7 @@ class TestCreateAppFile:
91
90
  file_path = str(tmp_path)
92
91
  code = ""
93
92
 
94
- success, message = create_app_file(file_path, code)
93
+ success, app_path, message = create_app_file(file_path, code)
95
94
 
96
95
  assert success is True
97
96
  assert "Successfully created" in message
@@ -0,0 +1,302 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ import pytest
5
+ import time
6
+ import tempfile
7
+ import os
8
+ import shutil
9
+ import subprocess
10
+ import threading
11
+ import requests
12
+ from unittest.mock import Mock, patch, MagicMock
13
+ from typing import Any
14
+
15
+ from mito_ai.streamlit_preview.manager import (
16
+ StreamlitPreviewManager,
17
+ PreviewProcess,
18
+ get_preview_manager
19
+ )
20
+
21
+
22
+ class TestStreamlitPreviewManager:
23
+ """Test cases for StreamlitPreviewManager."""
24
+
25
+ @pytest.fixture
26
+ def manager(self):
27
+ """Create a fresh manager instance for each test."""
28
+ return StreamlitPreviewManager()
29
+
30
+ @pytest.fixture
31
+ def sample_app_code(self):
32
+ """Sample streamlit app code for testing."""
33
+ return """
34
+ import streamlit as st
35
+
36
+ st.title("Test App")
37
+ st.write("Hello, World!")
38
+ """
39
+
40
+ def test_init(self, manager):
41
+ """Test manager initialization."""
42
+ assert manager._previews == {}
43
+ assert isinstance(manager._lock, type(threading.Lock()))
44
+ assert manager.log is not None
45
+
46
+ def test_get_free_port(self, manager):
47
+ """Test getting a free port."""
48
+ port = manager.get_free_port()
49
+ assert isinstance(port, int)
50
+ assert port > 0
51
+ assert port < 65536
52
+
53
+ # Test that we get different ports
54
+ port2 = manager.get_free_port()
55
+ assert port != port2
56
+
57
+ @pytest.mark.parametrize("app_code,preview_id,expected_success", [
58
+ ("import streamlit as st\nst.write('Hello')", "test_preview", True),
59
+ ("", "empty_preview", True),
60
+ ("import streamlit as st\n" * 1000 + "st.write('Large app')", "large_preview", True),
61
+ ])
62
+ def test_start_streamlit_preview_success_cases(self, manager, app_code, preview_id, expected_success):
63
+ """Test successful streamlit preview start with different app codes."""
64
+ with patch('subprocess.Popen') as mock_popen, \
65
+ patch('requests.get') as mock_requests_get, \
66
+ patch('tempfile.mkdtemp') as mock_mkdtemp:
67
+
68
+ # Setup mocks
69
+ app_directory = "/tmp/test_dir"
70
+ mock_mkdtemp.return_value = app_directory
71
+ mock_proc = Mock()
72
+ mock_proc.terminate.return_value = None
73
+ mock_proc.wait.return_value = None
74
+ mock_popen.return_value = mock_proc
75
+
76
+ mock_response = Mock()
77
+ mock_response.status_code = 200
78
+ mock_requests_get.return_value = mock_response
79
+
80
+ # Test
81
+ success, message, port = manager.start_streamlit_preview(app_directory, preview_id)
82
+
83
+ # Assertions
84
+ assert success == expected_success
85
+ if expected_success:
86
+ assert "successfully" in message.lower()
87
+ assert isinstance(port, int)
88
+ assert port > 0
89
+
90
+ # Verify subprocess was called correctly
91
+ mock_popen.assert_called_once()
92
+ call_args = mock_popen.call_args
93
+ assert "streamlit" in call_args[0][0]
94
+ assert "run" in call_args[0][0]
95
+ assert "--server.headless" in call_args[0][0]
96
+ assert "--server.address" in call_args[0][0]
97
+
98
+ # Cleanup
99
+ manager.stop_preview(preview_id)
100
+
101
+ @pytest.mark.parametrize("exception_type,expected_message", [
102
+ (Exception("Temp dir creation failed"), "failed to start preview"),
103
+ (OSError("Permission denied"), "failed to start preview"),
104
+ (ValueError("Invalid argument"), "failed to start preview"),
105
+ ])
106
+ def test_start_streamlit_preview_exceptions(self, manager, sample_app_code, exception_type, expected_message):
107
+ """Test streamlit preview start with different exceptions."""
108
+ with patch('tempfile.mkdtemp', side_effect=exception_type):
109
+ app_directory = "/tmp/test_dir"
110
+ success, message, port = manager.start_streamlit_preview(app_directory, "test_preview")
111
+
112
+ assert success is False
113
+ assert expected_message in message.lower()
114
+ assert port is None
115
+
116
+ @pytest.mark.parametrize("preview_id,expected_result", [
117
+ ("existing_preview", True),
118
+ ("non_existent", False),
119
+ ])
120
+ def test_stop_preview_scenarios(self, manager, sample_app_code, preview_id, expected_result):
121
+ """Test stopping previews with different scenarios."""
122
+ if expected_result:
123
+ # Start a preview first
124
+ with patch('subprocess.Popen') as mock_popen, \
125
+ patch('requests.get') as mock_requests_get, \
126
+ patch('tempfile.mkdtemp') as mock_mkdtemp, \
127
+ patch('builtins.open', create=True) as mock_open, \
128
+ patch('os.path.exists') as mock_exists:
129
+
130
+ app_directory = "/tmp/test_dir"
131
+ mock_mkdtemp.return_value = app_directory
132
+ mock_proc = Mock()
133
+ mock_proc.terminate.return_value = None
134
+ mock_proc.wait.return_value = None
135
+ mock_popen.return_value = mock_proc
136
+
137
+ mock_response = Mock()
138
+ mock_response.status_code = 200
139
+ mock_requests_get.return_value = mock_response
140
+
141
+ # Mock file operations
142
+ mock_file = Mock()
143
+ mock_open.return_value.__enter__.return_value = mock_file
144
+ mock_exists.return_value = True
145
+
146
+ manager.start_streamlit_preview(app_directory, preview_id)
147
+
148
+ @pytest.mark.parametrize("process_behavior,expected_kill_called", [
149
+ (subprocess.TimeoutExpired("cmd", 5), True),
150
+ (None, False), # Normal termination
151
+ ])
152
+ def test_stop_preview_process_behaviors(self, manager, sample_app_code, process_behavior, expected_kill_called):
153
+ """Test stopping preview with different process behaviors."""
154
+ with patch('subprocess.Popen') as mock_popen, \
155
+ patch('requests.get') as mock_requests_get, \
156
+ patch('tempfile.mkdtemp') as mock_mkdtemp, \
157
+ patch('builtins.open', create=True) as mock_open, \
158
+ patch('os.path.exists') as mock_exists:
159
+
160
+ # Setup mocks for start
161
+ app_directory = "/tmp/test_dir"
162
+ mock_mkdtemp.return_value = app_directory
163
+
164
+ mock_proc = Mock()
165
+ mock_proc.terminate.return_value = None
166
+ mock_proc.wait.return_value = None
167
+ mock_popen.return_value = mock_proc
168
+
169
+ mock_response = Mock()
170
+ mock_response.status_code = 200
171
+ mock_requests_get.return_value = mock_response
172
+
173
+ # Mock file operations
174
+ mock_file = Mock()
175
+ mock_open.return_value.__enter__.return_value = mock_file
176
+ mock_exists.return_value = True
177
+
178
+ # Start a preview
179
+ manager.start_streamlit_preview(app_directory, "test_preview")
180
+
181
+ # Setup process behavior for stop
182
+ if process_behavior:
183
+ # Configure the mock to raise the exception when called with timeout
184
+ def wait_with_timeout(*args, **kwargs):
185
+ if 'timeout' in kwargs:
186
+ raise process_behavior
187
+ return None
188
+ mock_proc.wait.side_effect = wait_with_timeout
189
+
190
+ @pytest.mark.parametrize("preview_id,expected_found", [
191
+ ("existing_preview", True),
192
+ ("non_existent", False),
193
+ ])
194
+ def test_get_preview_scenarios(self, manager, sample_app_code, preview_id, expected_found):
195
+ """Test getting previews with different scenarios."""
196
+ if expected_found:
197
+ # Start a preview first
198
+ with patch('subprocess.Popen') as mock_popen, \
199
+ patch('requests.get') as mock_requests_get, \
200
+ patch('tempfile.mkdtemp') as mock_mkdtemp, \
201
+ patch('builtins.open', create=True) as mock_open, \
202
+ patch('os.path.exists') as mock_exists:
203
+
204
+ mock_mkdtemp.return_value = "/tmp/test_dir"
205
+ mock_proc = Mock()
206
+ mock_proc.terminate.return_value = None
207
+ mock_proc.wait.return_value = None
208
+ mock_popen.return_value = mock_proc
209
+
210
+ mock_response = Mock()
211
+ mock_response.status_code = 200
212
+ mock_requests_get.return_value = mock_response
213
+
214
+ # Mock file operations
215
+ mock_file = Mock()
216
+ mock_open.return_value.__enter__.return_value = mock_file
217
+ mock_exists.return_value = True
218
+
219
+ manager.start_streamlit_preview("/tmp/test_dir", preview_id)
220
+
221
+ preview = manager.get_preview(preview_id)
222
+
223
+ if expected_found:
224
+ assert preview is not None
225
+ assert isinstance(preview, PreviewProcess)
226
+ assert preview.port > 0
227
+
228
+ # Cleanup
229
+ manager.stop_preview(preview_id)
230
+ else:
231
+ assert preview is None
232
+
233
+ def test_preview_process_dataclass(self):
234
+ """Test PreviewProcess dataclass."""
235
+ proc = Mock()
236
+ port = 8080
237
+
238
+ preview = PreviewProcess(
239
+ proc=proc,
240
+ port=port
241
+ )
242
+
243
+ assert preview.proc == proc
244
+ assert preview.port == port
245
+
246
+ def test_get_preview_manager_singleton(self):
247
+ """Test that get_preview_manager returns the same instance."""
248
+ manager1 = get_preview_manager()
249
+ manager2 = get_preview_manager()
250
+
251
+ assert manager1 is manager2
252
+ assert isinstance(manager1, StreamlitPreviewManager)
253
+
254
+ @pytest.mark.parametrize("num_previews", [1, 2, 3])
255
+ def test_concurrent_previews(self, manager, sample_app_code, num_previews):
256
+ """Test managing multiple concurrent previews."""
257
+ preview_ids = [f"preview_{i}" for i in range(num_previews)]
258
+ ports = []
259
+
260
+ with patch('subprocess.Popen') as mock_popen, \
261
+ patch('requests.get') as mock_requests_get, \
262
+ patch('tempfile.mkdtemp') as mock_mkdtemp, \
263
+ patch('builtins.open', create=True) as mock_open, \
264
+ patch('os.path.exists') as mock_exists:
265
+
266
+ # Setup mocks
267
+ mock_mkdtemp.return_value = "/tmp/test_dir"
268
+ mock_proc = Mock()
269
+ mock_proc.terminate.return_value = None
270
+ mock_proc.wait.return_value = None
271
+ mock_popen.return_value = mock_proc
272
+
273
+ mock_response = Mock()
274
+ mock_response.status_code = 200
275
+ mock_requests_get.return_value = mock_response
276
+
277
+ # Mock file operations
278
+ mock_file = Mock()
279
+ mock_open.return_value.__enter__.return_value = mock_file
280
+ mock_exists.return_value = True
281
+
282
+ # Start multiple previews
283
+ for preview_id in preview_ids:
284
+ success, _, port = manager.start_streamlit_preview("/tmp/test_dir", preview_id)
285
+ assert success is True
286
+ ports.append(port)
287
+
288
+ # Assertions
289
+ assert len(set(ports)) == num_previews # All ports should be different
290
+
291
+ # Check all previews exist
292
+ for preview_id in preview_ids:
293
+ assert manager.get_preview(preview_id) is not None
294
+
295
+ # Stop all previews
296
+ for preview_id in preview_ids:
297
+ assert manager.stop_preview(preview_id)
298
+
299
+ # Verify they're gone
300
+ for preview_id in preview_ids:
301
+ assert manager.get_preview(preview_id) is None
302
+
@@ -174,7 +174,6 @@ def log(
174
174
  If telemetry is not turned off and we are not running tests,
175
175
  we log the ai event
176
176
  """
177
-
178
177
  final_params: Dict[str, Any] = params or {}
179
178
 
180
179
  # Then, make sure to add the user email
@@ -355,4 +354,32 @@ def log_db_connection_error(connection_type: str, error_message: str) -> None:
355
354
  "error_message": error_message,
356
355
  }
357
356
  )
357
+
358
+ #################################
359
+ # Streamlit Conversion
360
+ #################################
358
361
 
362
+ def log_streamlit_app_creation_success(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType) -> None:
363
+ log(
364
+ "mito_ai_streamlit_app_creation_success",
365
+ key_type=key_type
366
+ )
367
+
368
+ def log_streamlit_app_creation_retry(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: str) -> None:
369
+ log(
370
+ "mito_ai_streamlit_app_creation_retry",
371
+ params={
372
+ "error_message": error,
373
+ },
374
+ key_type=key_type
375
+ )
376
+
377
+ def log_streamlit_app_creation_error(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: str) -> None:
378
+ log(
379
+ "mito_ai_streamlit_app_creation_error",
380
+ params={
381
+ "error_message": error,
382
+ },
383
+ key_type=key_type
384
+ )
385
+
@@ -710,7 +710,7 @@
710
710
  "semver": {},
711
711
  "vscode-diff": {},
712
712
  "mito_ai": {
713
- "version": "0.1.37",
713
+ "version": "0.1.38",
714
714
  "singleton": true,
715
715
  "import": "/home/runner/work/mito/mito/mito-ai/lib/index.js"
716
716
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mito_ai",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "AI chat for JupyterLab",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -138,7 +138,7 @@
138
138
  "outputDir": "mito_ai/labextension",
139
139
  "schemaDir": "schema",
140
140
  "_build": {
141
- "load": "static/remoteEntry.93ecc9bc0edba61535cc.js",
141
+ "load": "static/remoteEntry.bcce4ea34631acf6dbbe.js",
142
142
  "extension": "./extension",
143
143
  "style": "./style"
144
144
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mito_ai",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "AI chat for JupyterLab",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -27,10 +27,15 @@
27
27
  }
28
28
  ],
29
29
  "Notebook": [
30
+ {
31
+ "name": "preview-app",
32
+ "command": "toolbar-button:preview-as-streamlit",
33
+ "rank": 1000
34
+ },
30
35
  {
31
36
  "name": "convert-to-streamlit",
32
37
  "command": "toolbar-button:convert-to-streamlit",
33
- "rank": 1000
38
+ "rank": 1001
34
39
  }
35
40
  ]
36
41
  }