mito-ai 0.1.44__py3-none-any.whl → 0.1.46__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 (76) hide show
  1. mito_ai/__init__.py +10 -1
  2. mito_ai/_version.py +1 -1
  3. mito_ai/anthropic_client.py +92 -8
  4. mito_ai/app_deploy/app_deploy_utils.py +25 -0
  5. mito_ai/app_deploy/handlers.py +9 -12
  6. mito_ai/app_deploy/models.py +4 -1
  7. mito_ai/chat_history/handlers.py +63 -0
  8. mito_ai/chat_history/urls.py +32 -0
  9. mito_ai/completions/handlers.py +44 -20
  10. mito_ai/completions/models.py +1 -0
  11. mito_ai/completions/prompt_builders/prompt_constants.py +22 -4
  12. mito_ai/constants.py +3 -0
  13. mito_ai/streamlit_conversion/agent_utils.py +148 -30
  14. mito_ai/streamlit_conversion/prompts/prompt_constants.py +147 -24
  15. mito_ai/streamlit_conversion/prompts/streamlit_app_creation_prompt.py +2 -1
  16. mito_ai/streamlit_conversion/prompts/streamlit_error_correction_prompt.py +2 -2
  17. mito_ai/streamlit_conversion/prompts/streamlit_finish_todo_prompt.py +4 -3
  18. mito_ai/streamlit_conversion/prompts/update_existing_app_prompt.py +50 -0
  19. mito_ai/streamlit_conversion/streamlit_agent_handler.py +101 -104
  20. mito_ai/streamlit_conversion/streamlit_system_prompt.py +1 -0
  21. mito_ai/streamlit_conversion/streamlit_utils.py +18 -17
  22. mito_ai/streamlit_conversion/validate_streamlit_app.py +66 -62
  23. mito_ai/streamlit_preview/handlers.py +5 -3
  24. mito_ai/streamlit_preview/utils.py +11 -7
  25. mito_ai/tests/chat_history/test_chat_history.py +211 -0
  26. mito_ai/tests/deploy_app/test_app_deploy_utils.py +71 -0
  27. mito_ai/tests/message_history/test_message_history_utils.py +43 -19
  28. mito_ai/tests/providers/test_anthropic_client.py +180 -8
  29. mito_ai/tests/streamlit_conversion/test_apply_patch_to_text.py +368 -0
  30. mito_ai/tests/streamlit_conversion/test_fix_diff_headers.py +533 -0
  31. mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +71 -158
  32. mito_ai/tests/streamlit_conversion/test_streamlit_utils.py +16 -16
  33. mito_ai/tests/streamlit_conversion/test_validate_streamlit_app.py +16 -28
  34. mito_ai/tests/streamlit_preview/test_streamlit_preview_handler.py +2 -2
  35. mito_ai/tests/user/__init__.py +2 -0
  36. mito_ai/tests/user/test_user.py +120 -0
  37. mito_ai/tests/utils/test_anthropic_utils.py +4 -4
  38. mito_ai/user/handlers.py +33 -0
  39. mito_ai/user/urls.py +21 -0
  40. mito_ai/utils/anthropic_utils.py +15 -21
  41. mito_ai/utils/message_history_utils.py +4 -3
  42. mito_ai/utils/telemetry_utils.py +7 -4
  43. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +100 -100
  44. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/package.json +2 -2
  45. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +1 -1
  46. mito_ai-0.1.44.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.cf2e3ad2797fbb53826b.js → mito_ai-0.1.46.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.20f12766ecd3d430568e.js +1520 -300
  47. mito_ai-0.1.46.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.20f12766ecd3d430568e.js.map +1 -0
  48. mito_ai-0.1.44.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.5482493d1270f55b7283.js → mito_ai-0.1.46.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.54126ab6511271265443.js +18 -18
  49. mito_ai-0.1.44.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.5482493d1270f55b7283.js.map → mito_ai-0.1.46.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.54126ab6511271265443.js.map +1 -1
  50. {mito_ai-0.1.44.dist-info → mito_ai-0.1.46.dist-info}/METADATA +2 -2
  51. {mito_ai-0.1.44.dist-info → mito_ai-0.1.46.dist-info}/RECORD +75 -63
  52. mito_ai-0.1.44.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.cf2e3ad2797fbb53826b.js.map +0 -1
  53. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
  54. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +0 -0
  55. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +0 -0
  56. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +0 -0
  57. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
  58. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +0 -0
  59. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +0 -0
  60. {mito_ai-0.1.44.data → mito_ai-0.1.46.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
  61. {mito_ai-0.1.44.data → mito_ai-0.1.46.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
  62. {mito_ai-0.1.44.data → mito_ai-0.1.46.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
  63. {mito_ai-0.1.44.data → mito_ai-0.1.46.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
  64. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +0 -0
  65. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +0 -0
  66. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +0 -0
  67. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +0 -0
  68. {mito_ai-0.1.44.data → mito_ai-0.1.46.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
  69. {mito_ai-0.1.44.data → mito_ai-0.1.46.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
  70. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +0 -0
  71. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +0 -0
  72. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
  73. {mito_ai-0.1.44.data → mito_ai-0.1.46.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
  74. {mito_ai-0.1.44.dist-info → mito_ai-0.1.46.dist-info}/WHEEL +0 -0
  75. {mito_ai-0.1.44.dist-info → mito_ai-0.1.46.dist-info}/entry_points.txt +0 -0
  76. {mito_ai-0.1.44.dist-info → mito_ai-0.1.46.dist-info}/licenses/LICENSE +0 -0
@@ -3,8 +3,14 @@
3
3
 
4
4
  from typing import List
5
5
  import re
6
+ from anthropic.types import MessageParam
7
+ from mito_ai.streamlit_conversion.streamlit_system_prompt import streamlit_system_prompt
8
+ from mito_ai.utils.anthropic_utils import stream_anthropic_completion_from_mito_server
6
9
  from unidiff import PatchSet
7
10
  from mito_ai.streamlit_conversion.prompts.prompt_constants import MITO_TODO_PLACEHOLDER
11
+ from mito_ai.completions.models import MessageType
12
+
13
+ STREAMLIT_AI_MODEL = "claude-3-5-haiku-latest"
8
14
 
9
15
  def extract_todo_placeholders(agent_response: str) -> List[str]:
10
16
  """Extract TODO placeholders from the agent's response"""
@@ -23,6 +29,8 @@ def apply_patch_to_text(text: str, diff: str) -> str:
23
29
  diff : str
24
30
  A unified diff that transforms *text* into the desired output.
25
31
  The diff must reference exactly one file (the Streamlit app).
32
+ NOTE: This assumes a custom format where BOTH -X,Y and +X,Y
33
+ reference the original file line numbers.
26
34
 
27
35
  Returns
28
36
  -------
@@ -42,37 +50,46 @@ def apply_patch_to_text(text: str, diff: str) -> str:
42
50
  patch = PatchSet(diff.splitlines(keepends=True))
43
51
 
44
52
  # We expect a single-file patch (what the prompt asks the model to emit)
45
- if len(patch) != 1:
53
+ if len(patch) == 0:
54
+ raise ValueError("No patches found in diff")
55
+
56
+ # Check that all patches are for the same file
57
+ file_names = set(p.source_file for p in patch)
58
+ if len(file_names) > 1:
46
59
  raise ValueError(
47
- f"Expected a patch for exactly one file, got {len(patch)} files."
60
+ f"Expected patches for exactly one file, got files: {file_names}"
48
61
  )
49
62
 
50
- file_patch = patch[0]
51
-
63
+ # Apply all hunks from all patches (they should all be for the same file)
52
64
  original_lines = text.splitlines(keepends=True)
53
65
  result_lines: List[str] = []
54
-
55
66
  cursor = 0 # index in original_lines (0-based)
56
67
 
57
- for hunk in file_patch:
58
- # Copy unchanged lines before this hunk
59
- while cursor < hunk.source_start - 1:
60
- result_lines.append(original_lines[cursor])
61
- cursor += 1
62
-
63
- # Apply hunk line-by-line
64
- for line in hunk:
65
- if line.is_context:
66
- result_lines.append(original_lines[cursor])
68
+ # Process all hunks from all patches
69
+ # We only expect one patch file, but it always returns as a list
70
+ # so we just iterate over it
71
+ for file_patch in patch:
72
+ for hunk in file_patch:
73
+ # Since hunks reference the original file, just convert to 0-based
74
+ hunk_start = hunk.source_start - 1
75
+
76
+ # Copy unchanged lines before this hunk
77
+ while cursor < hunk_start:
78
+ if cursor < len(original_lines):
79
+ result_lines.append(original_lines[cursor])
67
80
  cursor += 1
68
- elif line.is_removed:
69
- cursor += 1 # Skip this line from the original
70
- elif line.is_added:
71
- # Ensure added line ends with newline for consistency
72
- val = line.value
73
- if not val.endswith("\n"):
74
- val += "\n"
75
- result_lines.append(val)
81
+
82
+ # Apply hunk line-by-line
83
+ for line in hunk:
84
+ if line.is_context:
85
+ # Use the line from the diff to preserve exact formatting
86
+ result_lines.append(line.value)
87
+ cursor += 1
88
+ elif line.is_removed:
89
+ cursor += 1 # Skip this line from the original
90
+ elif line.is_added:
91
+ # Use the line from the diff to preserve exact formatting
92
+ result_lines.append(line.value)
76
93
 
77
94
  # Copy any remaining lines after the last hunk
78
95
  result_lines.extend(original_lines[cursor:])
@@ -80,11 +97,66 @@ def apply_patch_to_text(text: str, diff: str) -> str:
80
97
  return "".join(result_lines)
81
98
 
82
99
 
100
+ def fix_context_lines(diff: str) -> str:
101
+ """
102
+ Fix context lines in unified diff to ensure they all start with a space character.
103
+
104
+ In unified diffs, context lines (unchanged lines) must start with a single space ' ',
105
+ even if the line itself is empty. The AI sometimes generates diffs where empty
106
+ context lines are just blank lines without the leading space, which causes the
107
+ unidiff parser to fail.
108
+
109
+ Args:
110
+ diff (str): The unified diff string
111
+
112
+ Returns:
113
+ str: The corrected diff with proper context line formatting
114
+ """
115
+ lines = diff.split('\n')
116
+ corrected_lines = []
117
+ in_hunk = False
118
+
119
+ for i, line in enumerate(lines):
120
+ # Check if we're entering a hunk
121
+ if line.startswith('@@'):
122
+ in_hunk = True
123
+ corrected_lines.append(line)
124
+ continue
125
+
126
+ # Check if we're leaving a hunk (new file header)
127
+ if line.startswith('---') or line.startswith('+++'):
128
+ in_hunk = False
129
+ corrected_lines.append(line)
130
+ continue
131
+
132
+ if in_hunk:
133
+ # We're inside a hunk
134
+ if line.startswith(' ') or line.startswith('-') or line.startswith('+'):
135
+ # Already has proper diff marker
136
+ corrected_lines.append(line)
137
+ elif line.strip() == '':
138
+ # Empty line should be a context line with leading space
139
+ corrected_lines.append(' ')
140
+ else:
141
+ # Line without diff marker - treat as context line
142
+ corrected_lines.append(' ' + line)
143
+ else:
144
+ # Outside hunk - keep as is
145
+ corrected_lines.append(line)
146
+
147
+ return '\n'.join(corrected_lines)
148
+
149
+
83
150
  def fix_diff_headers(diff: str) -> str:
84
151
  """
85
152
  The AI is generally not very good at counting the number of lines in the diff. If the hunk header has
86
153
  an incorrect count, then the patch will fail. So instead we just calculate the counts ourselves, its deterministic.
154
+
155
+ If no header is provided at all, then there is nothing to fix.
87
156
  """
157
+ # First fix context lines to ensure they have proper leading spaces
158
+ diff = fix_context_lines(diff)
159
+
88
160
  lines = diff.split('\n')
89
161
 
90
162
  for i, line in enumerate(lines):
@@ -99,18 +171,64 @@ def fix_diff_headers(diff: str) -> str:
99
171
  old_count = 0
100
172
  new_count = 0
101
173
 
174
+ # Find the end of this hunk (next @@ line or end of file)
175
+ hunk_end = len(lines)
102
176
  for j in range(i + 1, len(lines)):
103
- next_line = lines[j]
104
- if next_line.startswith('@@') or next_line.startswith('---'):
177
+ if lines[j].startswith('@@'):
178
+ hunk_end = j
105
179
  break
106
- if next_line.startswith(' ') or next_line.startswith('-'):
180
+
181
+ # Count lines in this hunk
182
+ for j in range(i + 1, hunk_end):
183
+ hunk_line = lines[j]
184
+ # Empty lines are treated as context lines
185
+ if hunk_line == '' or hunk_line.startswith(' ') or hunk_line.startswith('-'):
107
186
  old_count += 1
108
- if next_line.startswith(' ') or next_line.startswith('+'):
187
+ if hunk_line == '' or hunk_line.startswith(' ') or hunk_line.startswith('+'):
109
188
  new_count += 1
110
189
 
111
190
  # Replace the header with correct counts
112
191
  lines[i] = f"@@ -{old_start},{old_count} +{new_start},{new_count} @@"
113
192
 
114
- return '\n'.join(lines)
115
-
116
-
193
+ corrected_diff = '\n'.join(lines)
194
+ corrected_diff = corrected_diff.lstrip()
195
+
196
+ # If there is no diff, just return it without fixing file headers
197
+ if len(corrected_diff) == 0:
198
+ return corrected_diff
199
+
200
+ # Remove known problametic file component headers that the AI sometimes returns
201
+ problamatic_file_header_components = ['--- a/app.py +++ b/app.py']
202
+ for problamatic_file_header_component in problamatic_file_header_components:
203
+ corrected_diff = corrected_diff.removeprefix(problamatic_file_header_component).lstrip()
204
+
205
+
206
+ # If the diff is missing the file component of the header, add it
207
+ valid_header_component = """--- a/app.py
208
+ +++ b/app.py"""
209
+ if not corrected_diff.startswith(valid_header_component):
210
+ corrected_diff = valid_header_component + '\n' + corrected_diff
211
+
212
+ return corrected_diff
213
+
214
+
215
+ async def get_response_from_agent(message_to_agent: List[MessageParam]) -> str:
216
+ """Gets the streaming response from the agent using the mito server"""
217
+ model = STREAMLIT_AI_MODEL
218
+ max_tokens = 8192 # 64_000
219
+ temperature = 0.2
220
+
221
+ accumulated_response = ""
222
+ async for stream_chunk in stream_anthropic_completion_from_mito_server(
223
+ model = model,
224
+ max_tokens = max_tokens,
225
+ temperature = temperature,
226
+ system = streamlit_system_prompt,
227
+ messages = message_to_agent,
228
+ stream=True,
229
+ message_type=MessageType.STREAMLIT_CONVERSION,
230
+ reply_fn=None,
231
+ message_id=""
232
+ ):
233
+ accumulated_response += stream_chunk
234
+ return accumulated_response
@@ -3,26 +3,100 @@
3
3
 
4
4
  MITO_TODO_PLACEHOLDER = "# MITO_TODO_PLACEHOLDER"
5
5
 
6
- unified_diff_instrucrions = f"""
6
+ unified_diff_instructions = f"""
7
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`.
8
+
9
+ A unified diff looks is the following and tells the system which lines of code to add, remove, or modify:
10
+ --- a/app.py
11
+ +++ b/app.py
12
+ @@ -START_LINE,1 +START_LINE,1 @@
13
+ x = 1
14
+ -y = 2
15
+ +y = 3
16
+
17
+ The components of the unified diff are the following:
18
+ - `--- a/app.py` -> The original file. We will always use the file app.py
19
+ - `+++ b/app.py` -> The modified file. We will always use the file app.py
20
+ - `@@ -START_LINE,1 +START_LINE,1 @@` -> The hunk header
21
+ - `x = 1` -> The original context line
22
+ - `-y = 2` -> The removed line
23
+ - `+y = 3` -> The added line
24
+
25
+ When you create a unified diff, you must follow the following format:
26
+ - Begin with a ```unified_diff marker and a ``` end marker.
27
+ - Always, include the standard header. On line 1: `--- a/app.py` and on line 2: `+++ b/app.py` like in the example above.
10
28
  - Show only the modified hunks; each hunk must start with an `@@` header with line numbers.
11
29
  - Within each hunk:
12
- * Unchanged context lines start with a single space.
30
+ * Unchanged context lines start with a single space ` `.
13
31
  * Removed lines start with `-`.
14
32
  * Added lines start with `+`.
15
33
  - If there are **no changes**, return an empty string.
16
34
  - Do not include the line numbers in your response.
17
35
 
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.**
36
+ **CRITICAL: INDENTATION HANDLING**
37
+ When modifying indented code (like content inside tabs, functions, or loops), you MUST:
38
+ - **Preserve exact indentation levels** in your added lines
39
+ - **Show the complete indentation change** when moving code between indentation levels
40
+ - **Include all whitespace** in your diff - indentation is part of the code structure
41
+ - When unindenting code (removing tabs/context), show the original indented line with `-` and the unindented version with `+`
42
+ - When indenting code (adding tabs/context), show the original unindented line with `-` and the indented version with `+`
43
+
44
+ **HUNK HEADER FORMAT:**
45
+ Use `@@ -START_LINE,1 +START_LINE,1 @@` where:
46
+ - START_LINE is the line number in the **original file** where this hunk begins
47
+ - Always use `1` for both count values (the system will calculate correct counts later)
48
+ - All line numbers must reference the **original file**, not the modified version
49
+ - For example, if the hunk begins on line 12, use `@@ -12,1 +12,1 @@`
50
+
51
+ **WRONG FORMATS (DO NOT USE):**
52
+ `@@ -12:` -> This is wrong because it is using a colon, doesn't have the count value, and doesn't have both sets of start_line numbers and lines counts.
53
+ `@@ -12,1` -> This is wrong because it doesn't have both sets of start_line numbers and lines counts.
54
+ `@@ 12,1 12,1 @@` -> This is wrong because it doesn't use - and + before the start_line
55
+ `@@-12,1 +12,1@@` -> This is wrong because it doesn't have a space after the first @@ and doesn't have a space before the second @@.
56
+
57
+ **MULTIPLE HUNKS:**
58
+ - If changes are separated by 5+ unchanged lines, create separate hunks
59
+ - Each hunk needs its own `@@` header with the correct START_LINE for that section
60
+ - Hunks must be in ascending order by line number
61
+
62
+ <Example 1: Single change in middle of file>
63
+
64
+ Assume `data_list = [` is on line 57 of the original file:
65
+ ```unified_diff
66
+ --- a/app.py
67
+ +++ b/app.py
68
+ @@ -57,1 +57,1 @@
69
+ data_list = [
70
+ - {{'id': 1, 'name': 'Old'}},
71
+ + {{'id': 1, 'name': 'New'}},
72
+ + {{'id': 2, 'name': 'Also New'}},
73
+ ```
74
+ </Example 1>
75
+
76
+ <Example 2: Multiple separate changes>
77
+ Assume the original file has:
78
+
79
+ Line 5: import os
80
+ Line 30: def process():
81
+
82
+ ```unified_diff
83
+ --- a/app.py
84
+ +++ b/app.py
85
+ @@ -5,1 +5,1 @@
86
+ import os
87
+ +import sys
88
+ @@ -30,1 +30,1 @@
89
+ -def process():
90
+ +def process_data():
91
+ ```
92
+ </Example 2>
19
93
 
20
- <Example Response>
94
+ <Example 3: Adding multiple entries to a list while respecting indentations>
21
95
 
22
96
  In the example below, assume that the line of code `data_list = [` is on line 57 of the existing streamlit app.
23
97
 
24
98
  ```unified_diff
25
- --- a/app.py
99
+ --- a/app.py
26
100
  +++ b/app.py
27
101
  @@ -57,1 +57,1 @@
28
102
  data_list = [
@@ -36,24 +110,73 @@ In the example below, assume that the line of code `data_list = [` is on line 57
36
110
  + {{'id': 7, 'name': 'Item G', 'category': 'Type 7', 'value': 700}},
37
111
  + {{'id': 8, 'name': 'Item H', 'category': 'Type 8', 'value': 800}},
38
112
  + {{'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}}
113
+ + {{'id': 10, 'name': 'Item J', 'category': 'Type 10', 'value': 1000}}
114
+ ```
115
+ </Example 3>
116
+
117
+ <Example 4: Consolidating tabs - removing tab structure and unindenting content>
118
+
119
+ Assume the original file has tabs starting at line 10:
120
+ ```python
121
+ tab1, tab2 = st.tabs(["Cat", "Dog"])
122
+
123
+ with tab1:
124
+ st.header("A cat")
125
+ st.image("https://static.streamlit.io/examples/cat.jpg", width=200)
126
+ with tab2:
127
+ st.header("A dog")
128
+ st.image("https://static.streamlit.io/examples/dog.jpg", width=200)
129
+ ```
130
+
131
+ To consolidate into a single screen without tabs:
132
+ ```unified_diff
133
+ --- a/app.py
134
+ +++ b/app.py
135
+ @@ -10,1 +10,1 @@
136
+ -tab1, tab2 = st.tabs(["Cat", "Dog"])
137
+ -
138
+ -with tab1:
139
+ - st.header("A cat")
140
+ - st.image("https://static.streamlit.io/examples/cat.jpg", width=200)
141
+ -with tab2:
142
+ - st.header("A dog")
143
+ - st.image("https://static.streamlit.io/examples/dog.jpg", width=200)
144
+ +st.header("A cat")
145
+ +st.image("https://static.streamlit.io/examples/cat.jpg", width=200)
146
+ +st.header("A dog")
147
+ +st.image("https://static.streamlit.io/examples/dog.jpg", width=200)
148
+ ```
149
+ </Example 4>
150
+
151
+ <Example 5: Adding tab structure - indenting existing content>
152
+
153
+ Assume the original file has content starting at line 10:
154
+ ```python
155
+ st.header("A cat")
156
+ st.image("https://static.streamlit.io/examples/cat.jpg", width=200)
157
+ st.header("A dog")
158
+ st.image("https://static.streamlit.io/examples/dog.jpg", width=200)
159
+ ```
160
+
161
+ To add tab structure:
162
+ ```unified_diff
163
+ --- a/app.py
164
+ +++ b/app.py
165
+ @@ -10,1 +10,1 @@
166
+ -st.header("A cat")
167
+ -st.image("https://static.streamlit.io/examples/cat.jpg", width=200)
168
+ -st.header("A dog")
169
+ -st.image("https://static.streamlit.io/examples/dog.jpg", width=200)
170
+ +tab1, tab2 = st.tabs(["Cat", "Dog"])
171
+ +
172
+ +with tab1:
173
+ + st.header("A cat")
174
+ + st.image("https://static.streamlit.io/examples/cat.jpg", width=200)
175
+ +with tab2:
176
+ + st.header("A dog")
177
+ + st.image("https://static.streamlit.io/examples/dog.jpg", width=200)
55
178
  ```
56
- </Example Response>
179
+ </Example 5>
57
180
 
58
181
  Your response must consist **only** of valid unified-diff block.
59
182
  """
@@ -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 unified_diff_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
+ {unified_diff_instructions}
16
16
 
17
17
  ===============================================
18
18
 
@@ -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, unified_diff_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
+ {unified_diff_instructions}
28
29
 
29
30
  ===============================================
30
31
 
@@ -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 unified_diff_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
+ {unified_diff_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
+ """