""" CyberGuide Gradio Web App Modern dark-themed UI with cybersecurity terminal aesthetic """ import os import gradio as gr from model import chat_streaming, get_model # CSS for dark theme with green terminal aesthetic CUSTOM_CSS = """ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Inter:wght@400;500;600&display=swap'); * { font-family: 'Inter', sans-serif; } /* Root dark theme */ :root { --primary-green: #00ff41; --dark-bg: #0d1117; --darker-bg: #010409; --card-bg: #161b22; --border-color: #30363d; --text-light: #c9d1d9; --text-muted: #8b949e; } body, .gradio-container { background-color: var(--dark-bg) !important; color: var(--text-light) !important; } /* Main container */ .gradio-container { max-width: 1400px; } /* Header */ .gradio-container > .prose { background: linear-gradient(135deg, var(--darker-bg) 0%, var(--card-bg) 100%); border-bottom: 2px solid var(--primary-green); padding: 20px !important; border-radius: 0; } .gradio-container > .prose h1 { color: var(--primary-green) !important; text-shadow: 0 0 10px rgba(0, 255, 65, 0.3); font-weight: 600; margin: 0 !important; font-size: 28px !important; } /* Main layout - row */ .row { gap: 20px; } /* Sidebar Container */ .sidebar-container { display: flex; flex-direction: column; gap: 15px; background-color: var(--card-bg); padding: 20px; border-radius: 8px; border: 1px solid var(--border-color); height: fit-content; } .sidebar-container > label { color: var(--primary-green) !important; font-weight: 600; font-size: 14px; text-transform: uppercase; letter-spacing: 0.5px; } /* Quick question buttons */ .quick-btn { background: linear-gradient(135deg, rgba(0, 255, 65, 0.1) 0%, rgba(0, 255, 65, 0.05) 100%); border: 1px solid var(--primary-green) !important; color: var(--primary-green) !important; font-weight: 500; padding: 12px 16px !important; border-radius: 6px; cursor: pointer; transition: all 0.3s ease; font-size: 13px; text-align: left; font-family: 'JetBrains Mono', monospace; } .quick-btn:hover { background: linear-gradient(135deg, rgba(0, 255, 65, 0.2) 0%, rgba(0, 255, 65, 0.1) 100%); box-shadow: 0 0 15px rgba(0, 255, 65, 0.4); transform: translateX(4px); } /* Chat area */ .chat-container { display: flex; flex-direction: column; gap: 15px; } /* Chatbot styling */ .gr-chatbot { background-color: var(--darker-bg) !important; border: 1px solid var(--border-color) !important; border-radius: 8px !important; } .gr-chatbot .message.bot { background-color: var(--card-bg) !important; border-left: 3px solid var(--primary-green); border-radius: 6px; } .gr-chatbot .message.bot .md-box { color: var(--text-light) !important; } .gr-chatbot .message.user { background: linear-gradient(135deg, rgba(0, 255, 65, 0.1) 0%, rgba(0, 255, 65, 0.05) 100%); border-left: 3px solid var(--primary-green); } .gr-chatbot .message.user .md-box { color: var(--text-light) !important; } /* Code blocks in chat */ .gr-chatbot code { background-color: rgba(0, 255, 65, 0.1) !important; color: var(--primary-green) !important; border-left: 2px solid var(--primary-green) !important; font-family: 'JetBrains Mono', monospace !important; } .gr-chatbot pre { background: var(--darker-bg) !important; border: 1px solid var(--border-color) !important; border-left: 3px solid var(--primary-green) !important; } /* Textbox for input */ .gr-textbox textarea, .gr-textbox input { background-color: var(--card-bg) !important; border: 1px solid var(--border-color) !important; color: var(--text-light) !important; border-radius: 6px !important; font-family: 'JetBrains Mono', monospace !important; } .gr-textbox textarea::placeholder { color: var(--text-muted) !important; } .gr-textbox textarea:focus, .gr-textbox input:focus { border-color: var(--primary-green) !important; box-shadow: 0 0 0 2px rgba(0, 255, 65, 0.2) !important; } /* Sliders */ .gr-slider { background-color: var(--card-bg); border-radius: 8px; } .gr-slider input { accent-color: var(--primary-green) !important; } .gr-slider label { color: var(--text-light) !important; } /* Buttons */ .gr-button { background: linear-gradient(135deg, var(--primary-green) 0%, #00dd36 100%); border: none !important; color: var(--darker-bg) !important; font-weight: 600; border-radius: 6px !important; transition: all 0.3s ease; padding: 12px 24px !important; } .gr-button:hover { box-shadow: 0 0 20px rgba(0, 255, 65, 0.5); transform: translateY(-2px); } .gr-button:active { transform: translateY(0); } /* Copy button styling */ .copy-btn { background: rgba(0, 255, 65, 0.2) !important; border: 1px solid var(--primary-green) !important; color: var(--primary-green) !important; font-size: 12px !important; padding: 6px 12px !important; border-radius: 4px !important; } .copy-btn:hover { background: rgba(0, 255, 65, 0.3) !important; } /* Parameter section */ .parameters { background-color: var(--card-bg); padding: 15px; border-radius: 8px; border: 1px solid var(--border-color); } .parameters > label { color: var(--text-light) !important; font-weight: 600; font-size: 14px; } /* Footer */ .footer { background: linear-gradient(135deg, #1a0000 0%, rgba(255, 0, 0, 0.05) 100%); border-top: 2px solid #ff4444; padding: 15px; border-radius: 6px; margin-top: 20px; text-align: center; } .footer p { color: #ff6666 !important; margin: 0 !important; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; } /* Markdown styling */ .prose { color: var(--text-light) !important; } .prose h1, .prose h2, .prose h3 { color: var(--primary-green) !important; } /* Scrollbar styling */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: var(--card-bg); } ::-webkit-scrollbar-thumb { background: var(--primary-green); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #00dd36; } /* Responsive */ @media (max-width: 768px) { .row { flex-direction: column; } .sidebar-container { max-width: 100%; } } """ QUICK_QUESTIONS = [ "🔍 How do I use nmap for network scanning?", "🎯 Explain SQL injection vulnerability", "🛡️ How to analyze suspicious network traffic?", "🔐 Steps for privilege escalation testing", "🐛 Debug this bash script", "📊 How to write a Metasploit module?", "🔑 SSL/TLS certificate analysis process", "⚔️ Lateral movement techniques in pen testing", ] def process_message(msg_text, chat_history, temperature, max_tokens, compute_mode): """Process user message and stream response""" chat_history = chat_history or [] chat_history.append({"role": "user", "content": msg_text}) chat_history.append({"role": "assistant", "content": ""}) yield chat_history response = "" for chunk in chat_streaming( msg_text, temperature, max_tokens, device_preference=compute_mode, ): response += chunk chat_history[-1]["content"] = response yield chat_history def add_quick_question(question, chat_history, temperature, max_tokens, compute_mode): """Handle quick question button clicks""" yield from process_message( question, chat_history, temperature, max_tokens, compute_mode, ) def create_app(): """Create and configure the Gradio app""" with gr.Blocks( css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="green"), title="CyberGuide - AI Security Assistant", ) as app: # Header gr.Markdown( "# 🚨 CyberGuide - AI Security Assistant\n" "*Your intelligent companion for cybersecurity analysis and penetration testing*" ) with gr.Row(): # Sidebar with gr.Column(scale=1, min_width=280): gr.Markdown( "### Quick Questions\n" "Click a button to start a conversation" ) with gr.Column(): quick_btns = [] for question in QUICK_QUESTIONS: btn = gr.Button( value=question, elem_classes="quick-btn", size="sm", ) quick_btns.append((btn, question)) # Parameters section gr.Markdown("### Settings") temperature_slider = gr.Slider( minimum=0.0, maximum=1.0, value=0.7, step=0.05, label="Temperature", info="Higher = more creative", ) max_tokens_slider = gr.Slider( minimum=128, maximum=2048, value=512, step=128, label="Max Tokens", info="Response length limit", ) compute_mode_radio = gr.Radio( choices=["auto", "gpu", "cpu"], value="auto", label="Compute Mode", info="Select auto, GPU, or CPU. Model reloads when mode changes.", ) # Main chat area with gr.Column(scale=3): chatbot = gr.Chatbot( type="messages", label="Chat", height=600, show_label=False, elem_classes="chat-container", avatar_images=( "https://i.imgur.com/rplKIZs.png", # User avatar "https://i.imgur.com/N8gJmq4.png", # AI avatar ), ) # Input area with gr.Row(): msg = gr.Textbox( label="Your message", placeholder="Ask CyberGuide about security topics...", lines=3, max_lines=5, scale=4, show_label=False, ) submit_btn = gr.Button( "Send", scale=1, size="lg", ) # Footer warning gr.HTML( '" ) # Event handlers submit_btn.click( fn=process_message, inputs=[ msg, chatbot, temperature_slider, max_tokens_slider, compute_mode_radio, ], outputs=[chatbot], queue=True, api_name=False, ).then( lambda: gr.Textbox(value=""), outputs=[msg], api_name=False, ) msg.submit( fn=process_message, inputs=[ msg, chatbot, temperature_slider, max_tokens_slider, compute_mode_radio, ], outputs=[chatbot], queue=True, api_name=False, ).then( lambda: gr.Textbox(value=""), outputs=[msg], api_name=False, ) # Quick question buttons for btn, question in quick_btns: question_state = gr.State(question) btn.click( fn=add_quick_question, inputs=[ question_state, chatbot, temperature_slider, max_tokens_slider, compute_mode_radio, ], outputs=[chatbot], queue=True, api_name=False, ) return app if __name__ == "__main__": print("Initializing CyberGuide...") app = create_app() app.queue(max_size=20, default_concurrency_limit=1) is_hf_space = bool(os.getenv("SPACE_ID")) app.launch( server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")), share=not is_hf_space, show_api=False, show_error=True, )