File size: 12,222 Bytes
134cc0c
 
 
19b8d26
134cc0c
017de5c
 
134cc0c
 
 
 
 
 
 
 
 
 
 
 
 
017de5c
 
cf0a35b
017de5c
d9680ae
134cc0c
a8453ea
017de5c
134cc0c
 
 
 
017de5c
 
 
 
 
 
134cc0c
 
 
 
 
017de5c
 
 
134cc0c
d9680ae
017de5c
 
134cc0c
9a41d1c
134cc0c
 
 
 
 
 
 
 
 
 
 
 
460cc3e
 
 
134cc0c
 
017de5c
134cc0c
 
 
017de5c
 
 
 
134cc0c
 
 
 
017de5c
 
 
 
 
134cc0c
017de5c
134cc0c
 
017de5c
134cc0c
 
 
 
 
017de5c
134cc0c
017de5c
134cc0c
 
 
 
 
 
 
 
017de5c
134cc0c
 
 
 
 
 
 
017de5c
 
 
 
 
 
 
 
 
 
 
134cc0c
 
017de5c
134cc0c
 
 
 
 
 
017de5c
134cc0c
017de5c
134cc0c
 
 
 
 
 
 
 
 
 
 
017de5c
 
134cc0c
017de5c
134cc0c
017de5c
 
 
 
 
 
 
 
 
 
 
134cc0c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
017de5c
134cc0c
 
 
 
 
 
 
 
 
 
 
017de5c
134cc0c
 
 
 
 
 
 
 
 
 
 
 
 
017de5c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134cc0c
 
 
 
 
 
 
 
 
 
 
 
 
017de5c
134cc0c
 
 
 
017de5c
134cc0c
 
 
 
 
 
017de5c
134cc0c
 
017de5c
 
 
 
134cc0c
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# 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.")