Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

def get_streamlit_app_creation_prompt(notebook: List[dict]) -> str:
"""
This prompt is used to create a streamlit app from a notebook.
This prompt is used to create a vizro app from a notebook.
"""
return f"""Convert the following Jupyter notebook into a Streamlit application.
return f"""Convert the following Jupyter notebook into a Vizro dashboard application.

GOAL: Create a complete, runnable Streamlit app that accurately represents the notebook. It must completely convert the notebook.
GOAL: Create a complete, runnable Vizro app that accurately represents the notebook. It must completely convert the notebook.

TODO PLACEHOLDER RULES:
If you decide to leave any TODOs, you must mark them with {MITO_TODO_PLACEHOLDER}. You should use {MITO_TODO_PLACEHOLDER} instead of comments like the following:
If you decide to leave any TODOs, you must mark them with {MITO_TODO_PLACEHOLDER}. You should use {MITO_TODO_PLACEHOLDER} instead of comments like the following:
- # ... (include all mappings from the notebook)
- # ... (include all violation codes from the notebook)
- # Fill in the rest of the code here
Expand Down Expand Up @@ -43,4 +43,4 @@ def get_streamlit_app_creation_prompt(notebook: List[dict]) -> str:
Notebook to convert:

{notebook}
"""
"""
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
from mito_ai.streamlit_conversion.prompts.prompt_utils import add_line_numbers_to_code

def get_streamlit_error_correction_prompt(error: str, streamlit_app_code: str) -> str:

existing_streamlit_app_code_with_line_numbers = add_line_numbers_to_code(streamlit_app_code)
return f"""You've created a Streamlit app, but it has an error in it when you try to run it.

return f"""You've created a Vizro dashboard app, but it has an error in it when you try to run it.

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.

{search_replace_instructions}

===============================================

EXISTING STREAMLIT APP:
EXISTING VIZRO APP:
{existing_streamlit_app_code_with_line_numbers}

===============================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from mito_ai.streamlit_conversion.prompts.prompt_utils import add_line_numbers_to_code

def get_finish_todo_prompt(notebook: List[dict], existing_streamlit_app_code: str, todo_placeholder: str) -> str:

existing_streamlit_app_code_with_line_numbers = add_line_numbers_to_code(existing_streamlit_app_code)
return f"""You've already created the first draft of a Streamlit app representation of a Jupyter notebook, but you left yourself some TODOs marked as `{MITO_TODO_PLACEHOLDER}`.

return f"""You've already created the first draft of a Vizro dashboard representation of a Jupyter notebook, but you left yourself some TODOs marked as `{MITO_TODO_PLACEHOLDER}`.

**CRITICAL COMPLETION REQUIREMENT:**
You have ONE and ONLY ONE opportunity to complete this TODO. 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.
Expand All @@ -29,17 +29,17 @@ def get_finish_todo_prompt(notebook: List[dict], existing_streamlit_app_code: st

===============================================

Input Notebook that you are converting into the Streamlit app:
Input Notebook that you are converting into the Vizro dashboard:
{notebook}

===============================================

EXISTING STREAMLIT APP:
EXISTING VIZRO APP:
{existing_streamlit_app_code_with_line_numbers}

===============================================

Please make the changes for this TODO. Only focus on this one TODO right now. You will be asked to fix others later:
{todo_placeholder}

"""
"""
Original file line number Diff line number Diff line change
@@ -1,56 +1,81 @@
# Copyright (c) Saga Inc.
# Distributed under the terms of the GNU Affero General Public License v3.0 License.

streamlit_system_prompt = """You are a code conversion specialist who converts Jupyter notebooks into Streamlit applications with ABSOLUTE FIDELITY.
streamlit_system_prompt = """You are a code conversion specialist who converts Jupyter notebooks into Vizro dashboard applications with ABSOLUTE FIDELITY.

ROLE AND EXPERTISE:
- Expert in Python, Jupyter notebooks, Streamlit, and data visualization
- Expert in Python, Jupyter notebooks, Vizro, and data visualization
- Experienced in creating executive-ready dashboards for business stakeholders
- Skilled in translating technical analysis into clear, interactive presentations

TASK REQUIREMENTS:
1. Convert Jupyter notebook content into a complete Streamlit application (app.py)
1. Convert Jupyter notebook content into a complete Vizro dashboard application (app.py)
2. Preserve ALL outputs from code cells and markdown cells as they appear in the notebook
3. Maintain the logical flow and structure of the original analysis
4. Create an executive-friendly dashboard suitable for company leadership

STREAMLIT IMPLEMENTATION GUIDELINES:
- Use appropriate Streamlit components (st.title, st.header, st.subheader, st.markdown, etc.)
- Display all visualizations using st.pyplot(), st.plotly_chart(), or st.altair_chart() as appropriate
- Do not convert database connections into Streamlit's secret.toml format. If the user inlined their database credentials, are importing from an environment variable, or reading from a connections file, assume that same approach will work in the streamlit app.
- Show dataframes and tables using st.dataframe() or st.table()
- Include all text explanations and insights from markdown cells
- Add interactive elements where beneficial (filters, selectors, etc.)
VIZRO IMPLEMENTATION GUIDELINES:
- Use Vizro's declarative structure: vm.Page, vm.Dashboard, vm.Graph, vm.Table, vm.Card, etc.
- Import vizro.plotly.express as px for charts (scatter, histogram, line, bar, etc.)
- Display visualizations using vm.Graph(figure=px.chart_type(...))
- Show dataframes and tables using vm.Table(figure=df)
- Include text content using vm.Card(text="...")
- Add interactive elements using vm.Filter() and vm.Parameter() where beneficial
- Do not convert database connections. If the user inlined their database credentials, are importing from an environment variable, or reading from a connections file, assume that same approach will work in the vizro app.
- Ensure professional styling and layout suitable for executives
- Just create the streamlit app code, do not include a _main_ function block. The file will be run directly using `streamlit run app.py`.
- Structure the app as: load data → create page(s) with components → build dashboard → run

VIZRO APP STRUCTURE:
```python
import vizro.plotly.express as px
from vizro import Vizro
import vizro.models as vm

# Data loading and processing code here

# Create page with components
page = vm.Page(
title="Dashboard Title",
components=[
vm.Graph(figure=px.scatter(...)),
vm.Table(figure=df),
vm.Card(text="Insights and explanations")
],
controls=[
vm.Filter(column="category"),
],
)

# Build and run dashboard
dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()
```

CRITICAL REQUIREMENTS:
1. **PRESERVE ALL CODE EXACTLY**: Every line of code, every data structure, every import must be included in full
2. **NO PLACEHOLDERS**: Never use comments like "# Add more data here" or "# Fill in the rest"
3. **NO SIMPLIFICATION**: Do not replace actual data with sample data or hardcoded examples
4. **COMPLETE DATA STRUCTURES**: If a notebook has a 1000-line dictionary, include all 1000 lines
5. **PRESERVE DATA LOADING**: If the notebook reads from files, the Streamlit app must read from the same files
6. **NO IMPROVIZAITION**: Do not provide your own interpretations of the analysis. Just convert the existing analysis into a streamlit app.
5. **PRESERVE DATA LOADING**: If the notebook reads from files, the Vizro app must read from the same files
6. **NO IMPROVIZAITION**: Do not provide your own interpretations of the analysis. Just convert the existing analysis into a vizro dashboard.

COMPONENT MAPPING:
- Code cells that generate plots → vm.Graph(figure=px.chart_type(...))
- DataFrames → vm.Table(figure=df)
- Markdown cells with text → vm.Card(text="...")
- Variables that users might want to filter → vm.Filter(column="...")
- Maintain the order of outputs as they appear in the notebook

STYLE GUIDELINES:
STYLE GUIDELINES:
- Create a professional, executive-friendly dashboard
- If there are variables in the notebook that the streamlit app viewer would likely want to configure, then use the appropriate streamlit component to allow them to do so. For examples, if the notebook has a variable called "start_date" and "end_date", then use the st.date_input component to allow the user to select the start and end dates.
- If there are variables in the notebook that viewers would likely want to configure, use vm.Filter or vm.Parameter
- Do not use emojis unless they are in the notebook already
- Do not modify the graphs or analysis. If the notebook has a graph, use the same graph in the streamlit app.
- Always include the following code at the top of the file so the user does not use the wrong deploy button
```python
st.markdown(\"\"\"
<style>
#MainMenu {visibility: hidden;}
.stAppDeployButton {display:none;}
footer {visibility: hidden;}
.stMainBlockContainer {padding: 2rem 1rem 2rem 1rem;}
</style>
\"\"\", unsafe_allow_html=True)
```
- Do not modify the graphs or analysis. If the notebook has a graph, use the same graph in the vizro app
- Always end with: Vizro().build(dashboard).run()

OUTPUT FORMAT:
- Output the complete, runnable app.py file.
- Do not output any extra text, just give the python code.
- Output the complete, runnable app.py file
- Do not output any extra text, just give the python code
- The file will be run directly using Python (not streamlit run)

"""
"""
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

def get_update_existing_app_prompt(notebook: List[dict], streamlit_app_code: str, edit_prompt: str) -> str:
"""
This prompt is used to update an existing streamlit app.
This prompt is used to update an existing vizro app.
"""

existing_streamlit_app_code_with_line_numbers = add_line_numbers_to_code(streamlit_app_code)

return f"""

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.
GOAL: You've previously created a first draft of the Vizro dashboard app. Now the user reviewed it and provided feedback. Update the existing vizro 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.

**CRITICAL COMPLETION REQUIREMENT:**
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.
Expand All @@ -34,17 +34,17 @@ def get_update_existing_app_prompt(notebook: List[dict], streamlit_app_code: str

===============================================

INPUT NOTEBOOK:
INPUT NOTEBOOK:
{notebook}

===============================================

EXISTING STREAMLIT APP:
EXISTING VIZRO APP:
{existing_streamlit_app_code_with_line_numbers}

===============================================

USER EDIT REQUEST:
{edit_prompt}

"""
"""
34 changes: 17 additions & 17 deletions mito-ai/mito_ai/streamlit_conversion/streamlit_agent_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,24 +120,24 @@ async def streamlit_handler(notebook_path: AbsoluteNotebookPath, app_file_name:
# Otherwise generate a new streamlit app
streamlit_code = await generate_new_streamlit_code(notebook_code)

# Then, after creating/updating the app, validate that the new code runs
errors = validate_app(streamlit_code, notebook_path)
tries = 0
while len(errors)>0 and tries < 5:
for error in errors:
streamlit_code = await correct_error_in_generation(error, streamlit_code)
errors = validate_app(streamlit_code, notebook_path)
if len(errors)>0:
# TODO: We can't easily get the key type here, so for the beta release
# we are just defaulting to the mito server key since that is by far the most common.
log_streamlit_app_validation_retry('mito_server_key', MessageType.STREAMLIT_CONVERSION, errors)
tries+=1
# Validation disabled for Vizro POC
# errors = validate_app(streamlit_code, notebook_path)
# tries = 0
# while len(errors)>0 and tries < 5:
# for error in errors:
# streamlit_code = await correct_error_in_generation(error, streamlit_code)
#
# errors = validate_app(streamlit_code, notebook_path)
#
# if len(errors)>0:
# # TODO: We can't easily get the key type here, so for the beta release
# # we are just defaulting to the mito server key since that is by far the most common.
# log_streamlit_app_validation_retry('mito_server_key', MessageType.STREAMLIT_CONVERSION, errors)
# tries+=1

if len(errors)>0:
final_errors = ', '.join(errors)
raise StreamlitConversionError(f"Streamlit agent failed generating code after max retries. Errors: {final_errors}", 500)
# if len(errors)>0:
# final_errors = ', '.join(errors)
# raise StreamlitConversionError(f"Streamlit agent failed generating code after max retries. Errors: {final_errors}", 500)

# Finally, update the app.py file with the new code
create_app_file(app_path, streamlit_code)
Expand Down
59 changes: 35 additions & 24 deletions mito-ai/mito_ai/streamlit_preview/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,36 +38,47 @@ def get_free_port(self) -> int:
return port

def start_streamlit_preview(self, app_directory: AbsoluteNotebookDirPath, app_file_name: AppFileName, preview_id: str) -> int:
"""Start a streamlit preview process.
"""Start a vizro preview process.

Args:
app_code: The streamlit app code to run
app_code: The vizro app code to run
preview_id: Unique identifier for this preview

Returns:
Tuple of (success, message, port)
"""

try:

# Get free port
port = self.get_free_port()

# Start streamlit process

# For Vizro, we need to modify the app.py to use the port we allocated
# Read the app file and inject the port configuration
import os
app_path = os.path.join(app_directory, app_file_name)
with open(app_path, 'r') as f:
app_code = f.read()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Missing UTF-8 encoding when reading/writing app file

The file operations at lines 61 and 82 use open() without specifying encoding='utf-8', but the app file was originally created with UTF-8 encoding in create_app_file(). On Windows, Python uses the system default encoding (often not UTF-8) when no encoding is specified. This can cause encoding errors or data corruption when the generated code contains non-ASCII characters like unicode symbols, accented characters, or emoji.

Additional Locations (1)

Fix in Cursor Fix in Web


# Replace the run() call with run(port=X)
# Handle both .run() and .run(...)
import re
if re.search(r'\.run\(\s*\)', app_code):
# Simple case: .run() with no arguments
app_code = re.sub(r'\.run\(\s*\)', f'.run(port={port})', app_code)
elif re.search(r'\.run\(', app_code):
# Has arguments - insert port as first argument
app_code = re.sub(r'\.run\(', f'.run(port={port}, ', app_code)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Regex replaces all .run() calls, not just Vizro

The regex patterns \.run\(\s*\) and \.run\( match any .run( call in the code, not specifically the Vizro Vizro().build(dashboard).run() call. This will incorrectly modify unrelated method calls like subprocess.run(), asyncio.run(), or df.run() that may be present in the generated code. Additionally, if the preview is started multiple times without regenerating the file, the port injection will stack (e.g., .run(port=X) becomes .run(port=Y, port=X)), causing a runtime error from duplicate keyword arguments.

Fix in Cursor Fix in Web


# Write back the modified code
with open(app_path, 'w') as f:
f.write(app_code)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Race condition when modifying app file for port

The file read/modify/write sequence for port injection is not atomic or protected by a lock. If two concurrent preview requests target the same app file, one request can read the file, a second request can overwrite it with a different port, and then the first request's subprocess reads the modified file with the wrong port. The first request will then wait on a port where nothing is listening, causing a timeout. The original Streamlit implementation passed the port via command-line argument which didn't have this issue.

Fix in Cursor Fix in Web


# Start python process to run the Vizro app
cmd = [
"streamlit", "run", app_file_name,
"--server.port", str(port),
"--server.headless", "true",
"--server.address", "localhost",
"--server.enableXsrfProtection", "false",
"--server.runOnSave", "true", # auto-reload when app is saved
"--logger.level", "error"
"python", app_file_name
]

# TODO: Security considerations for production:
# - Consider enabling XSRF protection if needed, but we might already get this with the APIHandler?
# - Add authentication headers to streamlit


proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
Expand All @@ -81,7 +92,7 @@ def start_streamlit_preview(self, app_directory: AbsoluteNotebookDirPath, app_fi
if not ready:
proc.terminate()
proc.wait()
raise StreamlitPreviewError("Streamlit app failed to start as app is not ready", 500)
raise StreamlitPreviewError("Vizro app failed to start as app is not ready", 500)

# Register the process
with self._lock:
Expand All @@ -90,11 +101,11 @@ def start_streamlit_preview(self, app_directory: AbsoluteNotebookDirPath, app_fi
port=port,
)

self.log.info(f"Started streamlit preview {preview_id} on port {port}")
self.log.info(f"Started vizro preview {preview_id} on port {port}")
return port

except Exception as e:
self.log.error(f"Error starting streamlit preview: {e}")
self.log.error(f"Error starting vizro preview: {e}")
raise StreamlitPreviewError(f"Failed to start preview: {str(e)}", 500)

def _wait_for_app_ready(self, port: int, timeout: int = 30) -> bool:
Expand Down
1 change: 1 addition & 0 deletions mito-ai/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dependencies = [
# Mito App Dependencies
"pipreqs",
"streamlit",
"vizro",
# LLM Dependencies
"openai>=1.0.0",
"google-genai",
Expand Down