# app.py import os import json import streamlit as st from app_assets import app_logo import io from app_utils import parse_data_dictionary, display_notebook, style_css from workflow_nodes import ( think_sections_node, enhance_plan_node, write_code_node, modify_notebook_node, execute_notebook_node, correct_notebook_node, write_insights_node ) from workflow_nodes import InteractiveCaseStudyState st.set_page_config(layout="wide") # Apply CSS styling # This will apply the styles defined in style.css to the Streamlit app style_css("/home/user/app/src/style.css") st.title("πŸ““ NBforge") #st.logo(app_logo, size='large', link='https://www.mygreatlearning.com/') # ─── Session State Initialization ───────────────────────────────────────────── if "state" not in st.session_state: st.session_state.state = InteractiveCaseStudyState() st.session_state.plan_ready = False st.session_state.plan_approved = None st.session_state.notebook_ready = False st.session_state.notebook_approved = None st.session_state.exec_ready = False st.session_state.execution_approved = None s = st.session_state.state # ─── 1) Collect Inputs ──────────────────────────────────────────────────────── st.header("1. πŸ”§ Provide Your Inputs") s["domain"] = st.selectbox("πŸ“‚ Select Domain", [ "EDA", "Machine Learning", "Deep Learning", "Natural Language Processing", "Computer Vision", "Generative AI Prompt Engineering", "Generative AI RAG" ]) s["topics_and_subtopics"] = st.text_area("πŸ“š Topics & Subtopics", height=80) s["problem_statement"] = st.text_area("🧩 Problem Statement", height=80) s["dataset_type"] = st.selectbox("πŸ“ Dataset Type", ["csv", "pdf", "images", "json"]) uploaded = st.file_uploader("⬆️ Upload Dataset", accept_multiple_files=False) if uploaded: os.makedirs("data", exist_ok=True) path = f"data/{uploaded.name}" with open(path, "wb") as f: f.write(uploaded.getbuffer()) s["dataset_file_path"] = path st.success(f"Dataset saved to `{path}`") s["data_dictionary"] = {} dd = st.text_area("πŸ“– Data Dictionary") s["data_dictionary"] = parse_data_dictionary(dd) s["additional_instructions"] = st.text_area( "πŸ“ Additional Instructions (optional)", height=68 ) # Kick off plan generation if not st.session_state.plan_ready: if st.button("πŸš€ Generate Plan") and uploaded: with st.spinner("Designing notebook plan…"): s = think_sections_node(s) st.session_state.plan_ready = True st.session_state.plan_approved = None st.session_state.notebook_ready = False st.session_state.exec_ready = False # ─── 2) Review & Enhance Plan ──────────────────────────────────────────────── if st.session_state.plan_ready: st.subheader("2. πŸ” Review Notebook Plan") with st.expander("πŸ“‘ View Generated Plan"): st.json(s["plan"]) # ask approval once if st.session_state.plan_approved is None: st.session_state.plan_approved = st.radio("Approve this plan?", index=None, options=["YES", "NO"]) st.write(f"Plan approved: {st.session_state.plan_approved}") # on approval, let user move on if st.session_state.plan_approved == "YES": if st.button("➑️ Next: Write Code"): with st.spinner("Writing notebook skeleton…"): s = write_code_node(s) st.session_state.notebook_ready = True st.session_state.notebook_approved = None # on rejection, collect feedback & loop back elif st.session_state.plan_approved == "NO": st.text_area("What should be changed in the plan?", key="plan_fb") st.write(f"Plan feedback: {st.session_state.plan_fb}") if st.button("πŸ›  Apply Plan Changes"): s["plan_feedback"] = st.session_state.plan_fb with st.spinner("Modifying notebook plan…"): s = enhance_plan_node(s) # reset approval so we review again st.session_state.plan_approved = None st.rerun() # ─── 3) Review & Modify Notebook ───────────────────────────────────────────── if st.session_state.notebook_ready: st.subheader("3. πŸ’» Review Notebook Skeleton") with st.expander("πŸ“„ View Notebook Skeleton"): display_notebook(io.StringIO(json.dumps(s["raw_notebook"], indent=2))) st.info("----------------------------------------------------------------") st.write("**NOTE**: If it’s hard to read here, download and open in Colab:") st.download_button( "Download raw_notebook.ipynb", data=json.dumps(s["raw_notebook"], indent=2), file_name="raw_notebook.ipynb", mime="application/json", ) if st.session_state.notebook_approved is None: st.session_state.notebook_approved = st.radio("Looks good?", index=None, options=["YES", "NO"]) st.write(f"notebook approved: {st.session_state.notebook_approved}") if st.session_state.notebook_approved == "YES": if st.button("➑️ Next: Execute Notebook"): with st.spinner("Executing notebook…"): s = execute_notebook_node(s) st.session_state.exec_ready = True st.session_state.execution_approved = None elif st.session_state.notebook_approved == "NO": st.write("πŸ›  Please provide your feedback on the notebook skeleton:") st.text_area("What needs changing in the notebook skeleton?", key="nb_fb") st.write(f"Notebook feedback: {st.session_state.nb_fb}") if st.button("πŸ›  Apply Notebook Changes"): s["notebook_feedback"] = st.session_state.nb_fb with st.spinner("Modifying notebook skeleton…"): s = modify_notebook_node(s) st.session_state.notebook_approved = None st.rerun() # ─── 4) Review & Correct Execution ─────────────────────────────────────────── if st.session_state.exec_ready: st.subheader("4. πŸš€ Execution Results") with st.expander("πŸ“„ View Executed Notebook"): display_notebook(io.StringIO(s["executed_notebook"])) st.info("----------------------------------------------------------------") st.write("**NOTE**: If it’s hard to read here, download and open in Colab:") st.download_button( "Download executed_notebook.ipynb", data=s["executed_notebook"], file_name="executed_notebook.ipynb", mime="application/json", ) if s.get("execution_error"): # Always reset approval until they choose Download As-Is st.session_state.execution_approved = False st.error(f"❌ Execution failed:\n{s['execution_error']}") # Three-way branching st.session_state.exec_action = st.radio( "What would you like to do with this broken notebook?", index=None, options=["Download As-Is", "Provide Feedback", "Auto-Correct & Retry"] ) st.write(f"Execution action: {st.session_state.exec_action}") if st.session_state.exec_action == "Download As-Is": # mark approved so we fall through to downloads st.session_state.execution_approved = True elif st.session_state.exec_action == "Provide Feedback": fb = st.text_area("πŸ“ Describe how I should fix the error:", height=80, key="exec_feedback") st.write(f"Execution feedback: {st.session_state.exec_feedback}") if st.button("πŸ›  Apply Feedback"): s["execution_feedback"] = fb with st.spinner("Applying your feedback and re-executing…"): s = correct_notebook_node(s) s = execute_notebook_node(s) # loop back into this block st.session_state.exec_ready = True st.session_state.execution_approved = None st.rerun() elif st.session_state.exec_action == "Auto-Correct & Retry": if st.button("πŸ”§ Auto-Correct & Retry"): s["execution_feedback"] = "" with st.spinner("Auto-correcting and re-executing…"): s = correct_notebook_node(s) s = execute_notebook_node(s) # loop back into this block st.session_state.exec_ready = True st.session_state.execution_approved = None st.rerun() # If execution succeeded OR user chose β€œDownload As-Is”, show downloads if not s.get("execution_error") or st.session_state.execution_approved: st.success("βœ… Ready to download your artifacts!") st.download_button("πŸ“₯ Download plan.json", data=json.dumps(s["plan"], indent=2), file_name="plan.json", mime="application/json" ) st.download_button("πŸ“₯ Download raw_notebook.ipynb", data=json.dumps(s["raw_notebook"], indent=2), file_name="raw_notebook.ipynb", mime="application/json" ) st.download_button("πŸ“₯ Download executed_notebook.ipynb", data=s["executed_notebook"], file_name="executed_notebook.ipynb", mime="application/json" ) # ─── 5) Generate Insights (Optional) ────────────────────────────────────────── # Only show this once the user has downloaded (or chosen) the executed notebook if st.session_state.exec_ready and ( not s.get("execution_error") or st.session_state.execution_approved ): st.markdown("---") st.subheader("5. ✍️ Generate Insights (Optional)") # If there was an error and they chose Download As-Is, still allow insights if s.get("execution_error"): st.info("Your notebook contains errors, but you can still generate insights on available outputs.") else: st.success("Notebook executed end-to-end without errors!") st.info("You can now generate insights and conclusions based on the executed notebook.") # Ask permission st.session_state.insights_opt = st.radio( "Shall I proceed to write the insights and conclusion for this notebook?", index=None, options=["YES", "NO"] ) if st.session_state.insights_opt == "YES": if st.button("πŸ“ Generate Insights Now"): with st.spinner("Writing observations & final recommendations…"): s = write_insights_node(s) st.success("πŸŽ‰ Insights generated!") # Render the final notebook with insights inline st.markdown("#### Final Notebook with Insights") with st.expander("πŸ“„ View Final Notebook with Insights"): display_notebook(io.StringIO(json.dumps(s["final_notebook"], indent=2))) # Offer download st.download_button( "πŸ“₯ Download Final Notebook (.ipynb)", data=json.dumps(s["final_notebook"], indent=2), file_name="final_notebook.ipynb", mime="application/json", ) st.balloons() st.write("Thanks for using NBforge! πŸš€") elif st.session_state.insights_opt == "NO": st.snow() st.write("πŸ‘ No problem! Feel free to revisit this notebook later or download any artifact above.")