File size: 16,177 Bytes
63d9b2f
0ea40d5
 
2503583
 
 
 
0ea40d5
 
 
 
2503583
f54201d
2503583
 
43cbde3
2503583
 
 
 
 
 
43cbde3
0ea40d5
e80e041
b5a0b96
e2d338a
2503583
 
 
 
 
 
 
 
43cbde3
2503583
 
 
 
 
 
 
 
 
 
 
f54201d
0ea40d5
b5a0b96
0ea40d5
642547d
0ea40d5
 
b5a0b96
2503583
f54201d
 
0ea40d5
 
f54201d
 
be9ab33
2503583
be9ab33
 
 
 
e9c1814
 
 
 
2503583
 
e17517e
2503583
e9c1814
 
 
 
 
be9ab33
e9c1814
 
0ea40d5
e2d338a
 
f54201d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0dd3e86
 
 
 
 
 
 
c341524
0dd3e86
c341524
0dd3e86
 
 
 
 
 
 
2503583
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d193ca
2503583
e2d338a
 
 
 
 
 
 
 
 
 
642547d
fdc8455
dbe5ee2
fdc8455
b5a0b96
 
fb0cae8
 
 
 
 
 
 
 
b5a0b96
 
0ea40d5
fb0cae8
be9ab33
2503583
bd7c5b9
2503583
 
bd7c5b9
 
 
be9ab33
 
 
 
 
 
 
 
 
 
 
642547d
be9ab33
642547d
 
 
 
be9ab33
 
 
 
 
 
 
642547d
f54201d
 
 
 
 
 
 
 
 
0dd3e86
 
 
 
5fabd70
0dd3e86
5fabd70
 
 
 
 
 
 
 
 
 
 
0dd3e86
5fabd70
0dd3e86
 
 
5fabd70
 
0dd3e86
5fabd70
0dd3e86
 
5fabd70
 
 
0dd3e86
 
 
 
 
 
 
 
 
 
 
 
 
2503583
 
 
 
 
 
 
 
 
 
 
 
642547d
e2d338a
 
 
 
 
 
 
 
 
2503583
e2d338a
 
 
 
 
 
 
 
 
2503583
be9ab33
 
 
e2d338a
 
 
 
 
be9ab33
 
e2d338a
 
be9ab33
e2d338a
2503583
 
e2d338a
 
be9ab33
 
 
 
 
 
 
 
 
e2d338a
 
 
 
 
 
 
0dd3e86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fdc8455
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
ο»Ώ# ./app.py

"""

This UI layer sets up a clean initialization screen. It uses a layout state logic gate: 

when the page is loaded, it shows a clean login dialog box. If a valid password is sent, 

the complete history is queried; if an empty string or incorrect password is submitted, 

history retrieval is bypassed entirely, and the main workspace is unlocked securely without global state bleeding.

"""

import gradio as gr
from core_logic import chat_function
from storage import save_chat, load_history, get_chat_content, get_secret_password
from git_agent import manage_github_repo
import os

"""

print("==================================================")

print(f"DEBUG: HF_TOKEN exists? {bool(os.getenv('HF_TOKEN'))}")

print(f"DEBUG: APP_PASSWORD exists? {bool(os.getenv('APP_PASSWORD'))}")

if os.getenv('APP_PASSWORD'):

    print(f"DEBUG: APP_PASSWORD length is {len(os.getenv('APP_PASSWORD'))}")

print("==================================================")

"""

with gr.Blocks() as demo:
    chat_id_state = gr.State("")
    staged_files_state = gr.State([])
    
    # Session state to securely isolate the user's validated password string per browser tab instance
    user_session_password = gr.State("")

    # ==================== LAYER 1: AUTHENTICATION GATE ====================
    with gr.Column(visible=True) as login_layout:
        gr.Markdown("## πŸ” CoderG Enterprise Access Gate")
        gr.Markdown(
            "⚠️ **Workspace Notice:** Please leave the password field blank and press the **Unlock Workspace Environment** to proceed directly to the workspace."
        )
        password_input = gr.Textbox(
            label="Security Access Password", 
            placeholder="Enter password or leave blank for unauthenticated mode...", 
            type="password"
        )
        login_btn = gr.Button("Unlock Workspace Environment", variant="primary")

    # ==================== LAYER 2: MAIN WORKSPACE ====================
    with gr.Row(visible=False) as main_workspace:
        
        # --- Left Panel: Sidebar History ---
        with gr.Column(scale=1, variant="secondary"):
            gr.Markdown("### πŸ› οΈ Silicon Architect")
            new_btn = gr.Button("βž• New Chat", variant="primary")
            
            history_list = gr.Dataset(
                components=[gr.Textbox(visible=False)], 
                label="Recent Conversations", 
                samples=[],
                type="values",
                samples_per_page=20
            )
            
        # --- Center Panel: Main Core Multimodal Chat ---
        with gr.Column(scale=3):
            #chatbot = gr.Chatbot(show_label=False, height=700)
            
            active_chat_indicator = gr.Markdown("### πŸ“ Active Session: *New Conversation*")
            
            chatbot = gr.Chatbot(show_label=False, height=660) # Tweak height slightly to give header room

            chat_input = gr.Textbox(
                interactive=True,
                placeholder="Discuss architecture, paste code blocks, or ask CoderG to produce course documentation...",
                show_label=False,
                lines=1,        
                max_lines=10,   
                scale=8,
                submit_btn=False  
            )
            
            upload_btn = gr.UploadButton(
                "πŸ“Ž Attach Documents/Images",
                file_count="multiple",
                file_types=[".png", ".jpg", ".jpeg", ".bmp", ".pdf", ".xlsx", ".xls", ".doc", ".docx", ".md", ".py", ".html", ".css", ".js", ".json", ".csv", ".zip", ".tar.gz", ".log", ".txt"],
                scale=2
            )

            upload_status = gr.Markdown("")

        # --- Right Panel: Agentic Control Tower ---
        with gr.Column(scale=1, variant="secondary"):
            gr.Markdown("### πŸš€ CoderG Authorization Core")
            gr.Markdown("_Authorize code outputs to be compiled into dedicated remote repositories._")
            
            target_repo = gr.Textbox(
                label="Target Repository Name", 
                placeholder="e.g., advanced-python-course",
                value="dynamic-course-repo"
            )
            commit_txt = gr.Textbox(
                label="Commit Message", 
                value="Automated generation via CoderG Agent"
            )
            staged_files = gr.Textbox(
                label="Staged Files (Comma-separated)", 
                value="COURSE_README.md"
            )
            
            approve_btn = gr.Button("Approve & Push to GitHub", variant="primary")
            
            gr.Markdown("#### πŸ“Š Deployment Telemetry Logs")
            output_log = gr.Markdown("_Awaiting local environment staging completion..._")

            # --- πŸ’€ COGNITIVE DREAM CORE ADDITION ---
            # 1. Dedicated dynamic UI layer addition to house the Dream mode, reconsolidation telemetry logic
            gr.Markdown("---")
            gr.Markdown("### πŸ’€ Cognitive Dream Core")
            dream_btn = gr.Button("πŸŒ™ Trigger Autonomous Audit", variant="secondary")
            
            # This hidden box holds the human-in-the-loop verification gate controls
            with gr.Group(visible=False) as review_gate_panel:
                gr.Markdown("### 🚨 Pending Architectural Patch Proposal")
                proposal_display = gr.Markdown() # Renders the audit analysis button
                
                with gr.Row():
                    apply_patch_btn = gr.Button("βœ… Apply Changes", variant="primary")
                    discard_patch_btn = gr.Button("❌ Discard", variant="stop")
            
            dream_status_log = gr.Markdown("_System operating at nominal capacities._")

    # --- UI ROUTING HANDLERS ---
    def process_login_validation(password_attempt):
        """Step 1: Authenticates strings and updates layout toggles and browser session state."""
        target_password = get_secret_password()
        clean_attempt = str(password_attempt).strip() if password_attempt is not None else ""
        
        # Explicit Guardrail: Raise an error only if they typed an incorrect password.
        if target_password and clean_attempt and clean_attempt != target_password:
            raise gr.Error("❌ Invalid security token entered. Access to environment denied.")
            
        # Returns layout visibility frames alongside the locked-in session password state string
        return gr.update(visible=False), gr.update(visible=True), clean_attempt

    def populate_history_component(session_password):
        """Step 2: Updates dataset samples based cleanly on isolated session token values."""
        # Executes your requested logic: passing session_password down into the storage mechanism
        loaded_samples = load_history(user_password=session_password)
        return gr.update(samples=loaded_samples)

    # --- CORE WORKSPACE LOGIC ---
    def handle_file_upload(uploaded_files, current_staged_files):
        if not current_staged_files:
            current_staged_files = []
        for file_obj in uploaded_files:
            file_path = file_obj.name if hasattr(file_obj, 'name') else file_obj
            if file_path and file_path not in current_staged_files:
                current_staged_files.append(file_path)
        status_msg = f"🟒 **{len(current_staged_files)} file(s) staged successfully and attached to next prompt.**"
        return current_staged_files, status_msg

    def bot_response(message, history, chat_id):
        user_content = message["text"]
        clean_history_snapshot = list(history) 
        history.append({"role": "user", "content": user_content})
        history.append({"role": "assistant", "content": ""})
        
        #for partial_resp in chat_function(message, clean_history_snapshot):
        #    history[-1]["content"] = partial_resp
        #    yield history

        # --- Import and pass client & model down to the function ---
        from core_logic import client, model  # Adjust this import statement based on where your global client instance lives
        
        for partial_resp in chat_function(message, clean_history_snapshot, client, model):
            history[-1]["content"] = partial_resp
            yield history


    """

    def handle_save(history, chat_id, session_password):

        new_id = save_chat(chat_id, history)

        # Keeps sidebar refreshes tied strictly to the current session token authentication context

        current_list = load_history(user_password=session_password)

        if [new_id] not in current_list:

            current_list.insert(0, [new_id])

        return new_id, gr.update(samples=current_list)

    """
    def handle_save(history, chat_id, session_password):
        new_id = save_chat(chat_id, history)
        # Keeps sidebar refreshes tied strictly to the current session token authentication context
        current_list = load_history(user_password=session_password)
        if [new_id] not in current_list:
            current_list.insert(0, [new_id])
        
        # We return the new_id, the dataset update, AND we update the indicator view 
        # in case a brand new chat just generated its first true cloud file name.
        return new_id, gr.update(samples=current_list), f"### πŸ“ Active Session: `{new_id}`"

    """

    def load_past_chat(selected_list):

        chat_id = selected_list[0]

        content = get_chat_content(chat_id)

        return content, chat_id

    """
    def load_past_chat(selected_list):
        chat_id = selected_list[0]
        content = get_chat_content(chat_id)
        # Returns chat log array, updates chat ID tracking state, and updates the header text
        return content, chat_id, f"### πŸ“ Active Session: `{chat_id}`"


    def push_authorized(repo_name, commit_msg, files_list):
        files = [f.strip() for f in files_list.split(",") if f.strip()]
        if not repo_name.strip():
            yield "❌ **Deployment Aborted:** Repository name cannot be empty."
            return
        yield "β—Œ _Connecting to GitHub REST API Engine..._"
        result = manage_github_repo(repo_name.strip(), commit_msg, files)
        yield f"{result}"

    # ==================== BIND COGNITIVE DREAM LIFECYCLES ====================
    # A hidden internal string tracking state to isolate the raw proposal across loops
    active_proposal_state = gr.State("")

    # ==================== COGNITIVE DREAM AGENT FUNCTIONS ====================
    def run_background_dream_cycle():
        """

        Triggers simulation audit and updates the visibility layers of the UI components safely.

        Balanced explicitly to ensure every yield track perfectly populates all 4 output channels.

        """
        # STEP 1: Stream Loading States (Now cleanly providing all 4 required positions)
        yield (
            gr.update(value="β—Œ _Cognitive pipeline active: Running simulation audit on core_logic.py..._"), 
            gr.update(visible=False), 
            "",  # Clears proposal_display layout
            ""   # Clears active_proposal_state memory buffer
        )
        
        # STEP 2: Execute background simulation run
        from dream_agent import execute_dream_reflection
        raw_proposal = execute_dream_reflection(target_file="core_logic.py")
        
        # Format XML tags into clear, readable Markdown blocks for your review window
        user_report_view = raw_proposal.replace("<proposed_patch>", "\n```python\n# === PROPOSED ECOSYSTEM SOURCE PATCH ===\n").replace("</proposed_patch>", "\n```")
        
        # STEP 3: Stream Final Production Payload (All 4 positions accounted for)
        yield (
            gr.update(value="🟒 **Simulation Audit Complete.** Review proposal below before authorizing deployment."), 
            gr.update(visible=True),  # Reveal the gated validation box panel container
            user_report_view,         # Populate report display markdown layout canvas
            raw_proposal              # Lock raw text securely inside internal memory state component
        )

    def commit_authorized_dream_patch(proposal_buffer):
        """Executes patch write operations once explicit user verification is asserted."""
        from dream_agent import apply_authorized_patch
        result_msg = apply_authorized_patch(proposal_buffer, target_file="core_logic.py")
        return gr.update(visible=False), result_msg

    def discard_dream_patch():
        """Declines updates cleanly and zeroes out layout states."""
        return gr.update(visible=False), "_Proposal safely discarded. Local file state left unchanged._"


    # ==================== BIND EVENT LISTENER LIFECYCLES ====================
    
    # Decoupled sequence: Click validates credentials and captures token -> THEN queries data array conditionally
    login_btn.click(
        fn=process_login_validation,
        inputs=[password_input],
        outputs=[login_layout, main_workspace, user_session_password]
    ).then(
        fn=populate_history_component,
        inputs=[user_session_password],
        outputs=[history_list]
    )

    upload_btn.upload(
        fn=handle_file_upload,
        inputs=[upload_btn, staged_files_state],
        outputs=[staged_files_state, upload_status]
    )

    def process_submission(message_text, current_staged_files, history, chat_id):
        if not message_text.strip() and not current_staged_files:
            return history, "", current_staged_files, ""
        payload = {"text": message_text, "files": current_staged_files}
        for updated_history in bot_response(payload, history, chat_id):
            yield updated_history, "", [], ""

    chat_input.submit(
        fn=process_submission, 
        inputs=[chat_input, staged_files_state, chatbot, chat_id_state], 
        outputs=[chatbot, chat_input, staged_files_state, upload_status]
    ).then(
        fn=handle_save, 
        inputs=[chatbot, chat_id_state, user_session_password],
        outputs=[chat_id_state, history_list, 
                 active_chat_indicator] # Updates header text post-save

    )
    
    history_list.click(
        fn=load_past_chat, 
        inputs=[history_list], 
        outputs=[chatbot, chat_id_state, 
                 active_chat_indicator] # Feeds selected chat ID into header
    )

    """

    new_btn.click(

        fn=lambda session_pass: ([], "", [], load_history(user_password=session_pass), "", "_Awaiting local environment staging completion..._"), 

        inputs=[user_session_password], 

        outputs=[chatbot, chat_id_state, staged_files_state, history_list, upload_status, output_log]

    )

    """
    new_btn.click(
        fn=lambda session_pass: (
            [], "", [], load_history(user_password=session_pass), "", 
            "_Awaiting local environment staging completion..._", "### πŸ“ Active Session: *New Conversation*"
        ), 
        inputs=[user_session_password], 
        outputs=[chatbot, chat_id_state, staged_files_state, history_list, upload_status, output_log, active_chat_indicator]
    )

    approve_btn.click(
        fn=push_authorized, 
        inputs=[target_repo, commit_txt, staged_files], 
        outputs=[output_log]
    )

    # Event Assignments
    dream_btn.click(
        fn=run_background_dream_cycle,
        inputs=[],
        outputs=[dream_status_log, review_gate_panel, proposal_display, active_proposal_state]
    )

    apply_patch_btn.click(
        fn=commit_authorized_dream_patch,
        inputs=[active_proposal_state],
        outputs=[review_gate_panel, dream_status_log]
    )

    discard_patch_btn.click(
        fn=discard_dream_patch,
        inputs=[],
        outputs=[review_gate_panel, dream_status_log]
    )

demo.launch(theme=gr.themes.Soft(), css="styles.css")