mito-ai 0.1.45__py3-none-any.whl → 0.1.47__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 (82) hide show
  1. mito_ai/__init__.py +10 -1
  2. mito_ai/_version.py +1 -1
  3. mito_ai/anthropic_client.py +90 -5
  4. mito_ai/app_deploy/handlers.py +97 -77
  5. mito_ai/app_deploy/models.py +16 -12
  6. mito_ai/chat_history/handlers.py +63 -0
  7. mito_ai/chat_history/urls.py +32 -0
  8. mito_ai/completions/handlers.py +18 -20
  9. mito_ai/completions/models.py +4 -1
  10. mito_ai/completions/prompt_builders/agent_execution_prompt.py +6 -1
  11. mito_ai/completions/prompt_builders/agent_system_message.py +63 -4
  12. mito_ai/completions/prompt_builders/chat_system_message.py +4 -0
  13. mito_ai/completions/prompt_builders/prompt_constants.py +1 -0
  14. mito_ai/completions/prompt_builders/utils.py +14 -0
  15. mito_ai/constants.py +3 -0
  16. mito_ai/path_utils.py +56 -0
  17. mito_ai/streamlit_conversion/agent_utils.py +27 -106
  18. mito_ai/streamlit_conversion/prompts/prompt_constants.py +166 -53
  19. mito_ai/streamlit_conversion/prompts/streamlit_app_creation_prompt.py +2 -1
  20. mito_ai/streamlit_conversion/prompts/streamlit_error_correction_prompt.py +3 -3
  21. mito_ai/streamlit_conversion/prompts/streamlit_finish_todo_prompt.py +4 -3
  22. mito_ai/streamlit_conversion/{streamlit_system_prompt.py → prompts/streamlit_system_prompt.py} +1 -0
  23. mito_ai/streamlit_conversion/prompts/update_existing_app_prompt.py +50 -0
  24. mito_ai/streamlit_conversion/search_replace_utils.py +93 -0
  25. mito_ai/streamlit_conversion/streamlit_agent_handler.py +103 -119
  26. mito_ai/streamlit_conversion/streamlit_utils.py +18 -68
  27. mito_ai/streamlit_conversion/validate_streamlit_app.py +78 -96
  28. mito_ai/streamlit_preview/handlers.py +44 -85
  29. mito_ai/streamlit_preview/manager.py +6 -6
  30. mito_ai/streamlit_preview/utils.py +19 -18
  31. mito_ai/tests/chat_history/test_chat_history.py +211 -0
  32. mito_ai/tests/message_history/test_message_history_utils.py +43 -19
  33. mito_ai/tests/providers/test_anthropic_client.py +178 -6
  34. mito_ai/tests/streamlit_conversion/test_apply_search_replace.py +226 -0
  35. mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +87 -114
  36. mito_ai/tests/streamlit_conversion/test_streamlit_utils.py +42 -45
  37. mito_ai/tests/streamlit_conversion/test_validate_streamlit_app.py +20 -14
  38. mito_ai/tests/streamlit_preview/test_streamlit_preview_handler.py +13 -16
  39. mito_ai/tests/streamlit_preview/test_streamlit_preview_manager.py +22 -26
  40. mito_ai/tests/user/__init__.py +2 -0
  41. mito_ai/tests/user/test_user.py +120 -0
  42. mito_ai/user/handlers.py +45 -0
  43. mito_ai/user/urls.py +21 -0
  44. mito_ai/utils/anthropic_utils.py +8 -6
  45. mito_ai/utils/create.py +17 -1
  46. mito_ai/utils/error_classes.py +42 -0
  47. mito_ai/utils/message_history_utils.py +7 -4
  48. mito_ai/utils/telemetry_utils.py +79 -11
  49. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +1 -1
  50. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/package.json +2 -2
  51. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +1 -1
  52. mito_ai-0.1.45.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.0c3368195d954d2ed033.js → mito_ai-0.1.47.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.2db61d2b629817845901.js +2126 -363
  53. mito_ai-0.1.47.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.2db61d2b629817845901.js.map +1 -0
  54. mito_ai-0.1.45.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.684f82575fcc2e3b350c.js → mito_ai-0.1.47.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.e22c6cd4e56c32116daa.js +9 -9
  55. mito_ai-0.1.45.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.684f82575fcc2e3b350c.js.map → mito_ai-0.1.47.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.e22c6cd4e56c32116daa.js.map +1 -1
  56. {mito_ai-0.1.45.dist-info → mito_ai-0.1.47.dist-info}/METADATA +1 -1
  57. {mito_ai-0.1.45.dist-info → mito_ai-0.1.47.dist-info}/RECORD +81 -69
  58. mito_ai-0.1.45.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.0c3368195d954d2ed033.js.map +0 -1
  59. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
  60. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +0 -0
  61. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +0 -0
  62. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +0 -0
  63. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
  64. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +0 -0
  65. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +0 -0
  66. {mito_ai-0.1.45.data → mito_ai-0.1.47.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 +0 -0
  67. {mito_ai-0.1.45.data → mito_ai-0.1.47.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 +0 -0
  68. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js +0 -0
  69. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js.map +0 -0
  70. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +0 -0
  71. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +0 -0
  72. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +0 -0
  73. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +0 -0
  74. {mito_ai-0.1.45.data → mito_ai-0.1.47.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 +0 -0
  75. {mito_ai-0.1.45.data → mito_ai-0.1.47.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 +0 -0
  76. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +0 -0
  77. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +0 -0
  78. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
  79. {mito_ai-0.1.45.data → mito_ai-0.1.47.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
  80. {mito_ai-0.1.45.dist-info → mito_ai-0.1.47.dist-info}/WHEEL +0 -0
  81. {mito_ai-0.1.45.dist-info → mito_ai-0.1.47.dist-info}/entry_points.txt +0 -0
  82. {mito_ai-0.1.45.dist-info → mito_ai-0.1.47.dist-info}/licenses/LICENSE +0 -0
@@ -3,57 +3,170 @@
3
3
 
4
4
  MITO_TODO_PLACEHOLDER = "# MITO_TODO_PLACEHOLDER"
5
5
 
6
- unified_diff_instrucrions = f"""
7
- RESPONSE FORMAT: Return the changes you want to make to the streamlit app as a **unified diff (git-style patch)**:
8
- - Begin with a ````unified_diff` header and a ```` end header.
9
- - Then, include the standard header lines `--- a/app.py` and `+++ b/app.py`.
10
- - Show only the modified hunks; each hunk must start with an `@@` header with line numbers.
11
- - Within each hunk:
12
- * Unchanged context lines start with a single space.
13
- * Removed lines start with `-`.
14
- * Added lines start with `+`.
15
- - If there are **no changes**, return an empty string.
16
- - Do not include the line numbers in your response.
17
-
18
- **IMPORTANT: For the hunk header, use `@@ -START_LINE,1 +START_LINE,1 @@` where we always use 1 as the count value. In a later step, the system will automatically calculate the correct counts.**
19
-
20
- <Example Response>
21
-
22
- In the example below, assume that the line of code `data_list = [` is on line 57 of the existing streamlit app.
23
-
24
- ```unified_diff
25
- --- a/app.py
26
- +++ b/app.py
27
- @@ -57,1 +57,1 @@
28
- data_list = [
29
- {{'id': 1, 'name': 'Item A', 'category': 'Type 1', 'value': 100}},
30
- {{'id': 2, 'name': 'Item B', 'category': 'Type 2', 'value': 200}},
31
- - {MITO_TODO_PLACEHOLDER}: Add remaining entries from notebook
32
- + {{'id': 3, 'name': 'Item C', 'category': 'Type 3', 'value': 300}},
33
- + {{'id': 4, 'name': 'Item D', 'category': 'Type 4', 'value': 400}},
34
- + {{'id': 5, 'name': 'Item E', 'category': 'Type 5', 'value': 500}},
35
- + {{'id': 6, 'name': 'Item F', 'category': 'Type 6', 'value': 600}},
36
- + {{'id': 7, 'name': 'Item G', 'category': 'Type 7', 'value': 700}},
37
- + {{'id': 8, 'name': 'Item H', 'category': 'Type 8', 'value': 800}},
38
- + {{'id': 9, 'name': 'Item I', 'category': 'Type 9', 'value': 900}},
39
- + {{'id': 10, 'name': 'Item J', 'category': 'Type 10', 'value': 1000}},
40
- + {{'id': 11, 'name': 'Item K', 'category': 'Type 11', 'value': 1100}},
41
- + {{'id': 12, 'name': 'Item L', 'category': 'Type 12', 'value': 1200}},
42
- + {{'id': 13, 'name': 'Item M', 'category': 'Type 13', 'value': 1300}},
43
- + {{'id': 14, 'name': 'Item N', 'category': 'Type 14', 'value': 1400}},
44
- + {{'id': 15, 'name': 'Item O', 'category': 'Type 15', 'value': 1500}},
45
- + {{'id': 16, 'name': 'Item P', 'category': 'Type 16', 'value': 1600}},
46
- + {{'id': 17, 'name': 'Item Q', 'category': 'Type 17', 'value': 1700}},
47
- + {{'id': 18, 'name': 'Item R', 'category': 'Type 18', 'value': 1800}},
48
- + {{'id': 19, 'name': 'Item S', 'category': 'Type 19', 'value': 1900}},
49
- + {{'id': 20, 'name': 'Item T', 'category': 'Type 20', 'value': 2000}},
50
- + {{'id': 21, 'name': 'Item U', 'category': 'Type 21', 'value': 2100}},
51
- + {{'id': 22, 'name': 'Item V', 'category': 'Type 22', 'value': 2200}},
52
- + {{'id': 23, 'name': 'Item W', 'category': 'Type 23', 'value': 2300}},
53
- + {{'id': 24, 'name': 'Item X', 'category': 'Type 24', 'value': 2400}},
54
- + {{'id': 25, 'name': 'Item Y', 'category': 'Type 25', 'value': 2500}}
55
- ```
56
- </Example Response>
57
-
58
- Your response must consist **only** of valid unified-diff block.
6
+ search_replace_instructions = f"""
7
+ RESPONSE FORMAT: You can edit the existing code using the **SEARCH_REPLACE format** for exact string matching and replacement.
8
+
9
+ **STRUCTURE:**
10
+ ```search_replace
11
+ >>>>>>> SEARCH
12
+ [exact code currently in the file]
13
+ =======
14
+ [new code to replace it with]
15
+ <<<<<<< REPLACE
16
+ ```
17
+
18
+ **COMPONENTS:**
19
+ ```search_replace - This is the start of the search/replace block
20
+ - `>>>>>>> SEARCH` - Exact text that EXISTS NOW in the file (7 chevrons)
21
+ - `=======` - Separator between the search and replace blocks (7 equals signs)
22
+ - `<<<<<<< REPLACE` - Replacement text (7 chevrons)
23
+
24
+ ---
25
+
26
+ **CRITICAL RULES - READ CAREFULLY:**
27
+
28
+ 1. **SEARCH = CURRENT STATE ONLY**
29
+ - The SEARCH block must contain ONLY code that currently exists in the file
30
+ - NEVER include new code, future code, or code you wish existed in the SEARCH block
31
+ - Copy exact text from the current file, character-for-character
32
+
33
+ 2. **EXACT MATCHING REQUIRED**
34
+ - Every space, tab, newline must match perfectly
35
+ - Preserve exact indentation (spaces vs tabs)
36
+ - Include trailing newlines if present
37
+ - No approximations - even one character difference will fail
38
+
39
+ 3. **SIZE LIMITS**
40
+ - There are no size limits to each search/replace block, however, it is generally preferable to keep the SEARCH blocks small and focused on one change.
41
+ - For large changes, use multiple smaller search/replace blocks
42
+
43
+ 4. **UNIQUENESS**
44
+ - Include enough context to make the SEARCH block unique
45
+ - If text appears multiple times, add surrounding lines
46
+ - Ensure there's only ONE match in the file
47
+
48
+ 5. **VERIFICATION CHECKLIST** (before generating each block):
49
+ Is every line in my SEARCH block currently in the file?
50
+ Did I copy the exact spacing and whitespace?
51
+ Will this match exactly once?
52
+
53
+ 6. **SEARCH REPLACE BLOCK STRUCTURE**
54
+ - You must adhere to to the exact search_replace structure as shown in the examples.
55
+
56
+ ---
57
+
58
+ **MULTIPLE REPLACEMENTS:**
59
+ - You can include multiple search/replace blocks in one response
60
+ - Each block is independent and processed separately
61
+ - Use separate ```search_replace blocks for each change
62
+
63
+ <Example 1: Updating existing content>
64
+
65
+ ```search_replace
66
+ >>>>>>> SEARCH
67
+ st.title("Old Title")
68
+ =======
69
+ st.title("New Title")
70
+ <<<<<<< REPLACE
71
+ ```
72
+ </Example 1>
73
+
74
+ <Example 2: Adding new content>
75
+
76
+ ```search_replace
77
+ >>>>>>> SEARCH
78
+ st.title("My App")
79
+ =======
80
+ st.title("My App")
81
+ st.header("Welcome")
82
+ st.write("This is a test app")
83
+ <<<<<<< REPLACE
84
+ ```
85
+ </Example 2>
86
+
87
+ <Example 3: Deleting existing content>
88
+
89
+ ```search_replace
90
+ >>>>>>> SEARCH
91
+ st.write("Old message")
92
+ =======
93
+ <<<<<<< REPLACE
94
+ ```
95
+ </Example 3>
96
+
97
+ <Example 4: Multiple replacements in one response>
98
+
99
+ ```search_replace
100
+ >>>>>>> SEARCH
101
+ st.title("Old Title")
102
+ =======
103
+ st.title("New Title")
104
+ <<<<<<< REPLACE
105
+ ```
106
+
107
+ ```search_replace
108
+ >>>>>>> SEARCH
109
+ st.write("Old message")
110
+ =======
111
+ st.write("New message")
112
+ <<<<<<< REPLACE
113
+ ```
114
+ </Example 4>
115
+
116
+ <Example 5: Using extra context to identify the correct code to replace>
117
+
118
+ In the below example, assume that the code st.write("Old message") appears multiple times in the file, so we use extra context lines to identify the correct code to replace.
119
+
120
+ ```search_replace
121
+ >>>>>>> SEARCH
122
+ # This is a unique comment
123
+ st.write("Old message")
124
+ =======
125
+ # This is a unique comment
126
+ st.write("New message")
127
+ <<<<<<< REPLACE
128
+ ```
129
+ </Example 5>
130
+
131
+ <Example 6: Search/replace while respecting whitespace and indentation>
132
+
133
+ ```search_replace
134
+ >>>>>>> SEARCH
135
+ data_list = [
136
+ {{'id': 1, 'name': 'Item A'}},
137
+ {MITO_TODO_PLACEHOLDER}: Add remaining entries from notebook
138
+ ]
139
+ =======
140
+ data_list = [
141
+ {{'id': 1, 'name': 'Item A'}},
142
+ {{'id': 2, 'name': 'Item B'}},
143
+ {{'id': 3, 'name': 'Item C'}},
144
+ {{'id': 4, 'name': 'Item D'}}
145
+ ]
146
+ <<<<<<< REPLACE
147
+ ```
148
+ </Example 6>
149
+
150
+ <Example 7: Tab structure changes>
151
+
152
+ ```search_replace
153
+ >>>>>>> SEARCH
154
+ tab1, tab2 = st.tabs(["Cat", "Dog"])
155
+
156
+ with tab1:
157
+ st.header("A cat")
158
+ st.image("https://static.streamlit.io/examples/cat.jpg", width=200)
159
+ with tab2:
160
+ st.header("A dog")
161
+ st.image("https://static.streamlit.io/examples/dog.jpg", width=200)
162
+ =======
163
+ st.header("A cat")
164
+ st.image("https://static.streamlit.io/examples/cat.jpg", width=200)
165
+ st.header("A dog")
166
+ st.image("https://static.streamlit.io/examples/dog.jpg", width=200)
167
+ <<<<<<< REPLACE
168
+ ```
169
+ </Example 7>
170
+
171
+ Your response must consist **only** of valid search_replace blocks.
59
172
  """
@@ -1,9 +1,10 @@
1
1
  # Copyright (c) Saga Inc.
2
2
  # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
3
 
4
+ from typing import List
4
5
  from mito_ai.streamlit_conversion.prompts.prompt_constants import MITO_TODO_PLACEHOLDER
5
6
 
6
- def get_streamlit_app_creation_prompt(notebook: dict) -> str:
7
+ def get_streamlit_app_creation_prompt(notebook: List[dict]) -> str:
7
8
  """
8
9
  This prompt is used to create a streamlit app from a notebook.
9
10
  """
@@ -1,7 +1,7 @@
1
1
  # Copyright (c) Saga Inc.
2
2
  # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
3
 
4
- from mito_ai.streamlit_conversion.prompts.prompt_constants import unified_diff_instrucrions
4
+ from mito_ai.streamlit_conversion.prompts.prompt_constants import search_replace_instructions
5
5
  from mito_ai.streamlit_conversion.prompts.prompt_utils import add_line_numbers_to_code
6
6
 
7
7
  def get_streamlit_error_correction_prompt(error: str, streamlit_app_code: str) -> str:
@@ -12,7 +12,7 @@ def get_streamlit_error_correction_prompt(error: str, streamlit_app_code: str) -
12
12
 
13
13
  Your job is to fix the error now. Only fix the specific error that you are instructed to fix now. Do not fix other error that that you anticipate. You will be asked to fix other errors later.
14
14
 
15
- {unified_diff_instrucrions}
15
+ {search_replace_instructions}
16
16
 
17
17
  ===============================================
18
18
 
@@ -21,7 +21,7 @@ EXISTING STREAMLIT APP:
21
21
 
22
22
  ===============================================
23
23
 
24
- Please create a unified diff that corrects this error. Please keep your fix concise:
24
+ Please create a search/replace block that corrects this error. Please keep your fix concise:
25
25
  {error}
26
26
 
27
27
  """
@@ -1,10 +1,11 @@
1
1
  # Copyright (c) Saga Inc.
2
2
  # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
3
 
4
- from mito_ai.streamlit_conversion.prompts.prompt_constants import MITO_TODO_PLACEHOLDER, unified_diff_instrucrions
4
+ from typing import List
5
+ from mito_ai.streamlit_conversion.prompts.prompt_constants import MITO_TODO_PLACEHOLDER, search_replace_instructions
5
6
  from mito_ai.streamlit_conversion.prompts.prompt_utils import add_line_numbers_to_code
6
7
 
7
- def get_finish_todo_prompt(notebook: dict, existing_streamlit_app_code: str, todo_placeholder: str) -> str:
8
+ def get_finish_todo_prompt(notebook: List[dict], existing_streamlit_app_code: str, todo_placeholder: str) -> str:
8
9
 
9
10
  existing_streamlit_app_code_with_line_numbers = add_line_numbers_to_code(existing_streamlit_app_code)
10
11
 
@@ -24,7 +25,7 @@ You have ONE and ONLY ONE opportunity to complete this TODO. If you do not finis
24
25
  - If creating functions: Implement ALL required functionality
25
26
  - If converting a visualization: Copy over ALL of the visualization code from the notebook, including all styling and formatting.
26
27
 
27
- {unified_diff_instrucrions}
28
+ {search_replace_instructions}
28
29
 
29
30
  ===============================================
30
31
 
@@ -22,6 +22,7 @@ STREAMLIT IMPLEMENTATION GUIDELINES:
22
22
  - Include all text explanations and insights from markdown cells
23
23
  - Add interactive elements where beneficial (filters, selectors, etc.)
24
24
  - Ensure professional styling and layout suitable for executives
25
+ - Just create the streamlit app code, do not include a _main_ function block. The file will be run directly using `streamlit run app.py`.
25
26
 
26
27
  CRITICAL REQUIREMENTS:
27
28
  1. **PRESERVE ALL CODE EXACTLY**: Every line of code, every data structure, every import must be included in full
@@ -0,0 +1,50 @@
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 List
5
+ from mito_ai.streamlit_conversion.prompts.prompt_constants import search_replace_instructions
6
+ from mito_ai.streamlit_conversion.prompts.prompt_utils import add_line_numbers_to_code
7
+
8
+ def get_update_existing_app_prompt(notebook: List[dict], streamlit_app_code: str, edit_prompt: str) -> str:
9
+ """
10
+ This prompt is used to update an existing streamlit app.
11
+ """
12
+
13
+ existing_streamlit_app_code_with_line_numbers = add_line_numbers_to_code(streamlit_app_code)
14
+
15
+ return f"""
16
+
17
+ GOAL: You've previously created a first draft of the Streamlit app. Now the user reviewed it and provided feedback.Update the existing streamlit app according to the feedback provided by the user. Use the input notebook to help you understand what code needs to be added, changed, or modified to fulfill the user's edit request.
18
+
19
+ **CRITICAL COMPLETION REQUIREMENT:**
20
+ You have ONE and ONLY ONE opportunity to complete this edit request. If you do not finish the entire task completely, the application will be broken and unusable. This is your final chance to get it right.
21
+
22
+ **COMPLETION RULES:**
23
+ 1. **NEVER leave partial work** - If the edit request requires generating a list with 100 items, provide ALL 100 items.
24
+ 2. **NEVER use placeholders** - This is your only opportunity to fulfill this edit request, so do not leave yourself another TODOs.
25
+ 3. **NEVER assume "good enough"** - Complete the task to 100% satisfaction.
26
+ 4. **If the task seems large, that's exactly why it needs to be done now** - This is your only chance
27
+
28
+ **HOW TO DETERMINE IF TASK IS COMPLETE:**
29
+ - If building a list/dictionary: Include ALL items that should be in the final data structure.
30
+ - If creating functions: Implement ALL required functionality.
31
+ - If converting a visualization: Copy over ALL of the visualization code from the notebook, including all styling and formatting.
32
+
33
+ {search_replace_instructions}
34
+
35
+ ===============================================
36
+
37
+ INPUT NOTEBOOK:
38
+ {notebook}
39
+
40
+ ===============================================
41
+
42
+ EXISTING STREAMLIT APP:
43
+ {existing_streamlit_app_code_with_line_numbers}
44
+
45
+ ===============================================
46
+
47
+ USER EDIT REQUEST:
48
+ {edit_prompt}
49
+
50
+ """
@@ -0,0 +1,93 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ import re
5
+ from typing import List, Tuple
6
+
7
+ from mito_ai.utils.error_classes import StreamlitConversionError
8
+
9
+
10
+ def extract_search_replace_blocks(message_content: str) -> List[Tuple[str, str]]:
11
+ """
12
+ Extract all search_replace blocks from Claude's response.
13
+
14
+ Returns:
15
+ List of tuples (search_text, replace_text) for each search/replace block
16
+ """
17
+ if "```search_replace" not in message_content:
18
+ return []
19
+
20
+ pattern = r'```search_replace\n(.*?)```'
21
+ matches = re.findall(pattern, message_content, re.DOTALL)
22
+
23
+ search_replace_pairs = []
24
+ for match in matches:
25
+ # Split by the separator
26
+ if "=======" not in match:
27
+ continue
28
+
29
+ parts = match.split("=======", 1)
30
+ if len(parts) != 2:
31
+ continue
32
+
33
+ search_part = parts[0]
34
+ replace_part = parts[1]
35
+
36
+ # Extract search text (after SEARCH marker)
37
+ if ">>>>>>> SEARCH" in search_part:
38
+ search_text = search_part.split(">>>>>>> SEARCH", 1)[1].strip()
39
+ else:
40
+ continue
41
+
42
+ # Extract replace text (before REPLACE marker)
43
+ if "<<<<<<< REPLACE" in replace_part:
44
+ replace_text = replace_part.split("<<<<<<< REPLACE", 1)[0].strip()
45
+ else:
46
+ continue
47
+
48
+ search_replace_pairs.append((search_text, replace_text))
49
+
50
+ return search_replace_pairs
51
+
52
+
53
+ def apply_search_replace(text: str, search_replace_pairs: List[Tuple[str, str]]) -> str:
54
+ """
55
+ Apply search/replace operations to the given text.
56
+
57
+ Parameters
58
+ ----------
59
+ text : str
60
+ The original file contents.
61
+ search_replace_pairs : List[Tuple[str, str]]
62
+ List of (search_text, replace_text) tuples to apply.
63
+
64
+ Returns
65
+ -------
66
+ str
67
+ The updated contents after applying all search/replace operations.
68
+
69
+ Raises
70
+ ------
71
+ ValueError
72
+ If a search text is not found or found multiple times.
73
+ """
74
+ if not search_replace_pairs:
75
+ return text
76
+
77
+ result = text
78
+
79
+ for search_text, replace_text in search_replace_pairs:
80
+ # Count occurrences of search text
81
+ count = result.count(search_text)
82
+
83
+ if count == 0:
84
+ print("Search Text Not Found: ", repr(search_text))
85
+ raise StreamlitConversionError(f"Search text not found: {repr(search_text)}", error_code=500)
86
+ elif count > 1:
87
+ print("Search Text Found Multiple Times: ", repr(search_text))
88
+ raise StreamlitConversionError(f"Search text found {count} times (must be unique): {repr(search_text)}", error_code=500)
89
+
90
+ # Perform the replacement
91
+ result = result.replace(search_text, replace_text)
92
+
93
+ return result