cyberguide / app.py
samch183's picture
Upload app.py
b2b4257 verified
"""
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(
'<div class="footer">'
"<p>⚠️ AUTHORIZED USE ONLY ⚠️</p>"
"<p>This tool is for authorized security testing only. "
"Unauthorized access or attack on systems is illegal.</p>"
"</div>"
)
# 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,
)