Spaces:
Sleeping
Sleeping
File size: 11,396 Bytes
2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca 2503583 4d193ca | 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 | ο»Ώ
# ./app.py
"""
The Interface Skeleton - The code sets up the navigation panel and the multimodal chat interface
The .then() chain: Previously, the save happened "in the background." Now, handle_save explicitly returns the new load_history() results to the history_list component, causing it to "re-render" with the new chat visible.
The chat_id_state: By passing this back and forth, the app knows if it should update an existing file in the HF Dataset or create a new one.
history_list.click: This is the bridge that makes the sidebar interactive. Without this event, clicking the "Recent Conversations" wouldn't do anything.
"""
import gradio as gr
from core_logic import chat_function
from storage import save_chat, load_history, get_chat_content
from git_agent import manage_github_repo
# Theme and css parameters not included under gr.Blocks constructor for Gradio 6
with gr.Blocks() as demo:
# This state keeps track of the filename for the current session
chat_id_state = gr.State("")
# Hidden state tracking variable to maintain files across the chat session
staged_files_state = gr.State([])
with gr.Row():
# --- Left Panel: Sidebar History ---
with gr.Column(scale=1, variant="secondary"):
gr.Markdown("### π οΈ Silicon Architect")
new_btn = gr.Button("β New Chat", variant="primary")
# The sidebar component
history_list = gr.Dataset(
components=[gr.Textbox(visible=False)],
label="Recent Conversations",
samples=load_history(),
type="values",
samples_per_page=20
)
# --- Center Panel: Main Core Multimodal Chat ---
with gr.Column(scale=3):
# type="messages" not included as it is now default/implicit in Gradio 6
chatbot = gr.Chatbot(show_label=False, height=700)
"""
chat_input = gr.MultimodalTextbox(
interactive=True,
placeholder="Discuss architecture or ask CoderG to produce course documentation...",
show_label=False,
# FIX 1: Allow users to stack multiple files before sending
file_count="multiple",
# FIX 2: Stop rich text clipboard objects from hijacking the paste buffer
file_types=[".png", ".jpg", ".jpeg", ".bmp", ".pdf", ".xlsx", ".xls", ".docx", ".md", ".py", ".html", ".cs", ".js", ".json", ".csv", ".zip", ".tar.gz", ".log", ".txt"] # Expanded file type support
)
"""
# 1. Use a standard Textbox. It treats all paste inputs strictly as raw text strings,
# completely preventing the automated file-attachment generation bug.
chat_input = gr.Textbox(
interactive=True,
placeholder="Discuss architecture, paste code blocks, or ask CoderG to produce course documentation...",
show_label=False,
lines=1, # <--- Crucial change: Reverts default Enter behavior back to sending
max_lines=10, # <--- Keeps the box flexible so it expands when pasting large text such as code bases
scale=8,
submit_btn=False # <--- Removes the forced sidebar submit button, allowing 'Enter' to natively act as the submission key
)
# 2. Wire Up the Component Handlers - 2. Provide a distinct, clear upload node that connects to your perception agent pipeline
# Triggered immediately when a user finishes choosing files in the file browser window
upload_btn = gr.UploadButton(
"π Attach Documents/Images",
file_count="multiple",
file_types=[".png", ".jpg", ".jpeg", ".bmp", ".pdf", ".xlsx", ".xls", ".docx", ".md", ".py", ".html", ".cs", ".js", ".json", ".csv", ".zip", ".tar.gz", ".log", ".txt"],
scale=2
)
# Visual text tracker showing exactly what files have successfully staged
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..._")
# --- LOGIC FUNCTIONS ---
# --- 1: Create a staging function to process files when uploaded ---
def handle_file_upload(uploaded_files, current_staged_files):
"""Processes files when selected via browser and appends to staging session state."""
if not current_staged_files:
current_staged_files = []
# Gradio returns a list of file objects when file_count="multiple"
for file_obj in uploaded_files:
# Check the property format of incoming file assets from Gradio 6
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)
# Return a visual message showing how many files are safely staged
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"]
# 1. Pass a CLEAN copy of the historical conversation *before* appending new turns
# This ensures core_logic gets a proper history trail without duplication.
clean_history_snapshot = list(history)
# 2. Now prepare the live local UI array for streaming feedback
history.append({"role": "user", "content": user_content})
history.append({"role": "assistant", "content": ""})
# 3. Run the generator using your clean background state snapshot
for partial_resp in chat_function(message, clean_history_snapshot):
history[-1]["content"] = partial_resp
yield history
def handle_save(history, chat_id):
# 1. Save the actual data
new_id = save_chat(chat_id, history)
# 2. Get the latest from hub
current_list = load_history()
# 3. Ensure the current one is definitely at the top
if [new_id] not in current_list:
current_list.insert(0, [new_id])
return new_id, gr.update(samples=current_list)
def load_past_chat(selected_list):
chat_id = selected_list[0]
content = get_chat_content(chat_id)
return content, chat_id
def push_authorized(repo_name, commit_msg, files_list):
"""Triggers git_agent to build the target repository, clean up local folders, and log tasks."""
# Cleanly split comma separated lists of files
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}"
# --- INTERACTION ARCHITECTURE / EVENT HANDLERS ---
# Hook the UploadButton event listener loop to stage chosen files right away
upload_btn.upload(
fn=handle_file_upload,
inputs=[upload_btn, staged_files_state],
outputs=[staged_files_state, upload_status]
)
# Submission wrapper to package parameters together for your multi-format engine
# When submitting, we pass BOTH the text and the hidden staged files state array!
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, ""
# Packaging matching the identical format of core_logic expectations
payload = {
"text": message_text,
"files": current_staged_files
}
# Stream responses through bot loops and clear inputs when complete
for updated_history in bot_response(payload, history, chat_id):
# Continuously yield state update frames, wiping values upon initial loop entry
yield updated_history, "", [], ""
# 1. Bind Enter/Submit behavior for the chat input text box
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],
outputs=[chat_id_state, history_list]
)
# 2. Click Sidebar Item -> Load Content
history_list.click(
fn=load_past_chat,
inputs=[history_list],
outputs=[chatbot, chat_id_state]
)
# 3. New Chat Button Initialization
new_btn.click(
fn=lambda: ([], "", [], load_history(), "", "_Awaiting local environment staging completion..._"),
inputs=None,
outputs=[chatbot, chat_id_state, staged_files_state, history_list, upload_status, output_log]
)
# 4. Bind the Control Tower Approve Action button
approve_btn.click(
fn=push_authorized,
inputs=[target_repo, commit_txt, staged_files],
outputs=[output_log]
)
"""
# 1. Submit Chat -> Stream Response -> Save -> Refresh Sidebar
chat_input.submit(
bot_response,
[chat_input, chatbot, chat_id_state],
[chatbot]
).then(
handle_save,
[chatbot, chat_id_state],
[chat_id_state, history_list]
)
# 2. Click Sidebar Item -> Load Content
history_list.click(
load_past_chat,
[history_list],
[chatbot, chat_id_state]
)
# 3. New Chat Button Initialization
new_btn.click(
lambda: ([], "", load_history(), "_Awaiting local environment staging completion..._"),
None,
[chatbot, chat_id_state, history_list, output_log]
)
# 4. Bind the Control Tower Approve Action button
approve_btn.click(
push_authorized,
[target_repo, commit_txt, staged_files],
[output_log]
)
"""
# Fully consolidated theme and styling injections down into launch() parameter fields
demo.launch(theme=gr.themes.Soft(), css="styles.css") |