Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import tempfile | |
| from datetime import datetime | |
| # --------------------------------------------------------------------------- | |
| # Backend logic per mode (stub functions to plug your real engine into) | |
| # --------------------------------------------------------------------------- | |
| def handle_threat_intel(message, files): | |
| out = f"[Threat Intel] Processed: {message}" | |
| if files: | |
| out += f" | Files: {[f.name for f in files]}" | |
| return out | |
| def handle_translation(message, files): | |
| out = f"[Translation] Interpreted: {message}" | |
| if files: | |
| out += " | (Files attached for context)" | |
| return out | |
| def handle_marketplace_watch(message, files): | |
| out = f"[Marketplace Watch] Monitoring request: {message}" | |
| return out | |
| def handle_analyst_tools(message, files): | |
| out = f"[Analyst Tools] Action: {message}" | |
| return out | |
| MODE_ROUTER = { | |
| "Threat Intel": handle_threat_intel, | |
| "Translation": handle_translation, | |
| "Marketplace Watch": handle_marketplace_watch, | |
| "Analyst Tools": handle_analyst_tools, | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Core message processing | |
| # --------------------------------------------------------------------------- | |
| def process_message(message, files, history, mode): | |
| if not message and not files: | |
| return history, history # no-op | |
| handler = MODE_ROUTER.get(mode, handle_threat_intel) | |
| response = handler(message or "(no text, files only)", files) | |
| user_label = f"{mode}: {message or '[files only]'}" | |
| history = history + [(user_label, response)] | |
| # Clear text input on submit handled by UI (by setting value="") | |
| return history, history | |
| def clear_chat(): | |
| return [], [] | |
| def download_chat(history): | |
| # Convert chat history to a text file and return its path | |
| if not history: | |
| content = "No conversation yet.\n" | |
| else: | |
| lines = [] | |
| for user_msg, bot_msg in history: | |
| lines.append(f"[User]: {user_msg}") | |
| lines.append(f"[System]: {bot_msg}") | |
| lines.append("") # blank line between turns | |
| content = "\n".join(lines) | |
| tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".txt") | |
| with open(tmp_file.name, "w", encoding="utf-8") as f: | |
| f.write(content) | |
| return tmp_file.name | |
| # --------------------------------------------------------------------------- | |
| # Mobile-first UI | |
| # --------------------------------------------------------------------------- | |
| with gr.Blocks(title="Mobile-First Intelligence Console") as demo: | |
| gr.Markdown("## 🛰 Intelligence Console") | |
| # Global chat state | |
| chat_state = gr.State([]) | |
| # Mode selector at top (full width) | |
| mode = gr.Radio( | |
| ["Threat Intel", "Translation", "Marketplace Watch", "Analyst Tools"], | |
| value="Threat Intel", | |
| label="Mode", | |
| interactive=True | |
| ) | |
| # Chat area (full-width, tall enough but scrollable on mobile) | |
| chat = gr.Chatbot( | |
| label="Dialogue", | |
| height=430, | |
| show_copy_button=True | |
| ) | |
| # Utility + attachments collapsed on mobile to reduce clutter | |
| with gr.Accordion("Attachments & Utilities", open=False): | |
| file_input = gr.File( | |
| label="Upload files (logs, screenshots, docs)", | |
| file_count="multiple" | |
| ) | |
| with gr.Row(variant="compact"): | |
| clear_btn = gr.Button("Clear Chat", variant="secondary") | |
| download_btn = gr.Button("Download Transcript", variant="secondary") | |
| download_file = gr.File( | |
| label="Transcript File", | |
| interactive=False | |
| ) | |
| # Input bar at bottom: textbox + send button in a compact row | |
| with gr.Row(variant="compact"): | |
| user_input = gr.Textbox( | |
| placeholder="Type your message...", | |
| label="", | |
| scale=5 | |
| ) | |
| send_btn = gr.Button("Send", variant="primary", scale=1) | |
| # ----------------------------------------------------------------------- | |
| # Event wiring | |
| # ----------------------------------------------------------------------- | |
| # Send via button | |
| send_btn.click( | |
| fn=process_message, | |
| inputs=[user_input, file_input, chat_state, mode], | |
| outputs=[chat, chat_state] | |
| ).then( | |
| lambda: "", | |
| inputs=None, | |
| outputs=user_input # clear input after send | |
| ) | |
| # Send via Enter key | |
| user_input.submit( | |
| fn=process_message, | |
| inputs=[user_input, file_input, chat_state, mode], | |
| outputs=[chat, chat_state] | |
| ).then( | |
| lambda: "", | |
| inputs=None, | |
| outputs=user_input | |
| ) | |
| # Clear chat | |
| clear_btn.click( | |
| fn=clear_chat, | |
| inputs=None, | |
| outputs=[chat, chat_state] | |
| ) | |
| # Download transcript | |
| download_btn.click( | |
| fn=download_chat, | |
| inputs=chat_state, | |
| outputs=download_file | |
| ) | |
| demo.launch() |