coderg / app_02.py
prashantmatlani's picture
history display
2503583
Raw
History Blame Contribute Delete
11.4 kB

# ./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")