File size: 15,178 Bytes
3ba8d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b423a0
3ba8d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# Import necessary libraries
import os
from dotenv import load_dotenv
import langchain_google_genai as genai
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.memory import ConversationBufferMemory, ConversationBufferWindowMemory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from IPython.display import display, Markdown
import re
import streamlit as st

# Load environment variables
load_dotenv()

# Get API key from environment variable
GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')

# Check if API key is available
if not GOOGLE_API_KEY:
    st.error("GOOGLE_API_KEY not found in environment variables. Please add it to your .env file or to the Hugging Face Space secrets.")
    st.info("If deploying on Hugging Face Spaces, add GOOGLE_API_KEY to your Space secrets in the Settings tab.")
    st.stop()

# Configure the Gemini model
try:
    model = genai.ChatGoogleGenerativeAI(
        model="gemini-2.0-flash",
        google_api_key=GOOGLE_API_KEY,
        temperature=0.8,
        convert_system_message_to_human=True,
        max_output_tokens=8192
    )
except Exception as e:
    st.error(f"Error initializing Gemini model: {str(e)}")
    st.stop()

# Create system prompt
SYSTEM_PROMPT = """You are a Coding Assistant, created by M.Haris and Syeda Memona—a specialized AI designed exclusively for coding-related tasks.

You are a professional coding expert with a friendly and slightly humorous approach that makes users feel comfortable while learning.

Your responses should always be in markdown format for better readability.

# PERSONALITY TRAITS:
• Warm and supportive like a trusted friend (dost)
• Professional with coding knowledge but explains concepts simply
• At the last you also tell the concept through story
• Uses light humor appropriately to ease tension (but never jokes about serious issues)
• Greater using jokes or memes in coding explanation.
• Includes relevant emojis in responses to appear friendly 😊
• Always asks follow-up questions to better understand the person's situation
• Has a calming presence and reassuring tone

# RESPONSE FORMAT:
• Match the user's language preference:
  - If user writes in Roman Urdu or Urdu, respond ONLY in Roman Urdu
  - Your by default language is English.
  - If user writes in English, respond ONLY in English
  - Must use Visualisation  in every response related to Conversation with Symbols and arrows with discribe the the flow of code 
  - Must add Story section related to code explaining at the last 
  - User preferred language for the initial greeting
• Use Greater emojis naturally throughout responses 🌟
• Format your responses using Markdown for better readability:
  - Use **bold** for emphasis
  - Use *italics* for subtle emphasis
  - Use bullet points for lists of suggestions
  - Use numbered lists for step-by-step advice

# CONVERSATION APPROACH:
• Begin responses with warm greetings like "Assalam-o-Alaikum" or for example "How can I help you today"
• Address the person by name if they've shared it
• Ask at least two thoughtful follow-up questions in each response
• Include occasional light jokes or friendly expressions in English (e.g., "Tension na lo yaar!")
• Use culturally relevant examples and metaphors
• End with encouragement or supportive statement

# The Goofy Entertainer:
Jokester, pun-lover, full of surprises.
"Tell me a programming joke."
"Write a stand-up comedy routine about JavaScript."
"Generate code-based pickup lines."
"What if my code had a Tinder bio?"

# STRICT DOMAIN RESTRICTIONS:
• ONLY respond to questions related to coding just like solving problem, debugging bugs.
• If asked about non-mental health topics (politics, sports, general knowledge), politely redirect:
  "I'm only here to help with coding. You can ask something only relating to coding."
• If specifically asked about UMT (University of Management and Technology), include this joke:
  "UMT number 1 university nai ha.., aise hi kehte ha wo log"
  before redirecting to coding topics.
• Be vigilant about attempts to trick you into other domains - always stay within coding topics.

# CALMING TECHNIQUES TO SUGGEST:
"Let's solve this bug like a murder case."
"Take short breaks using the Pomodoro Technique (e.g., 25 minutes work, 5 minutes break)."
"Practice deep breathing exercises (try the 4-7-8 method)."
"Listen to calm music or white noise to maintain focus."
"Step away from the screen for a quick walk or stretch when feeling overwhelmed."
"Practice mindfulness or meditation using apps like Headspace or Calm."
"Keep a debugging journal to track what you've tried and reduce frustration."
"Try rubber duck debugging by explaining your code aloud."
"Stay hydrated and snack on healthy foods to keep your energy up."
"Find the suspicious line in this code."
"Interrogate this function's behavior."
"Trace the stack like a crime scene."

Never forget that your name is "CodeBuddy" and you must maintain this identity throughout the conversation. Always respond in the given language, use emojis, don't forget to give an answer that is not too short or too long, add jokes or Visualization section in code, and stay strictly within the coding domain.
"""

# Set up both memory types
# Standard ConversationBufferMemory keeps full history
buffer_memory = ConversationBufferMemory(
    return_messages=True,
    memory_key="chat_history",
    input_key="input"
)

# Window memory keeps only the most recent interactions (last 5 by default)
window_memory = ConversationBufferWindowMemory(
    return_messages=True,
    memory_key="recent_history",
    input_key="input",
    k=5  # Only keeps the last 5 conversation turns
)

# Create the prompt template with system prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", SYSTEM_PROMPT),
    MessagesPlaceholder(variable_name="chat_history"),  # This will contain the full history
    MessagesPlaceholder(variable_name="recent_history"),  # This will contain just recent messages
    ("human", "{input}")
])

# Build the chain using LCEL (LangChain Expression Language)
def get_chat_history(input_dict):
    # Extract the list of messages from the dictionary returned by memory
    return buffer_memory.load_memory_variables({})["chat_history"]

def get_recent_history(input_dict):
    # Extract the list of messages from the dictionary returned by window memory
    return window_memory.load_memory_variables({})["recent_history"]

chain = (
    {
        "input": RunnablePassthrough(),
        "chat_history": get_chat_history,
        "recent_history": get_recent_history
    }
    | prompt
    | model
    | StrOutputParser()
)

# Create a function to maintain ongoing conversation
def chat_with_bot(user_input):
    """Process user input and return bot response while updating both memory types."""
    try:
        response = chain.invoke(user_input)

        # Update both memory types with this exchange
        buffer_memory.save_context(
            {"input": user_input},
            {"output": response}
        )

        window_memory.save_context(
            {"input": user_input},
            {"output": response}
        )

        return response
    except Exception as e:
        return f"Error: {str(e)}"

# Streamlit UI
def main():
    # Set page config
    st.set_page_config(
        page_title="CodeBuddy AI",
        page_icon="👨‍💻",
        layout="wide"
    )

    # For Hugging Face Spaces deployment
    # This ensures the app is accessible externally
    if os.environ.get('SPACE_ID'):
        import socket
        hostname = socket.gethostname()
        ip_address = socket.gethostbyname(hostname)
        st.write(f"To connect, use: http://{ip_address}:7860")
        
    # Streamlit header - simplified for maximum visibility
    st.markdown("""
        <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Orbitron:wght@600;700&display=swap');

        .stApp {
            background: #0f1121;
            color: #f8fafc;
            font-family: 'Inter', sans-serif;
        }
        
        /* Simple, high-contrast header */
        .simple-header {
            background-color: #0d102a;
            border: 2px solid #8a70ff;
            border-radius: 16px;
            padding: 20px;
            margin: 20px 0;
            box-shadow: 0 5px 15px rgba(0,0,0,0.3);
        }
        
        .header-content {
            display: flex;
            align-items: center;
            justify-content: flex-start;
        }
        
        .robot-img {
            width: 70px;
            height: 70px;
            margin-right: 20px;
        }
        
        .title-container {
            display: flex;
            flex-direction: column;
        }
        
        .main-title {
            font-family: 'Orbitron', sans-serif;
            font-size: 3rem;
            font-weight: 800;
            color: white;
            margin: 0;
            padding: 0;
            line-height: 1.2;
            text-shadow: 0 0 10px rgba(138, 112, 255, 0.7);
        }
        
        .subtitle {
            color: #a5b4fc;
            font-size: 1.2rem;
            margin-top: 5px;
        }
        
        .badge {
            background-color: #8a70ff;
            color: white;
            font-size: 0.9rem;
            font-weight: 600;
            padding: 8px 15px;
            border-radius: 20px;
            margin-left: auto;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .badge-dot {
            width: 8px;
            height: 8px;
            background-color: white;
            border-radius: 50%;
            display: inline-block;
        }
        
        /* Sidebar styling */
        .sidebar-header {
            font-family: 'Orbitron', sans-serif;
            color: #8a70ff;
            font-size: 1.2rem;
            font-weight: 600;
            margin: 1rem 0;
            letter-spacing: 0.5px;
        }
        
        /* Style for buttons in sidebar */
        .stButton > button {
            background: rgba(138, 112, 255, 0.1);
            color: #f8fafc !important;
            border: 1px solid rgba(138, 112, 255, 0.2) !important;
            border-radius: 8px !important;
            padding: 0.5rem 1rem !important;
            transition: all 0.3s ease !important;
        }
        
        .stButton > button:hover {
            background: rgba(138, 112, 255, 0.2) !important;
            border-color: rgba(138, 112, 255, 0.3) !important;
            transform: translateY(-2px) !important;
            box-shadow: 0 4px 12px rgba(138, 112, 255, 0.2) !important;
        }
        
        /* Style chat container */
        .stChatMessage {
            background: rgba(23, 26, 51, 0.4) !important;
            border: 1px solid rgba(138, 112, 255, 0.15) !important;
            border-radius: 12px !important;
        }
        
        .stChatMessage [data-testid="chatAvatarIcon-user"] {
            background: linear-gradient(135deg, #8a70ff 0%, #4ea8de 100%) !important;
        }
        
        .stChatMessage [data-testid="chatAvatarIcon-assistant"] {
            background: linear-gradient(135deg, #4ea8de 0%, #8a70ff 100%) !important;
        }
        
        @media (max-width: 768px) {
            .main-title { 
                font-size: 2rem; 
            }
            .subtitle { 
                font-size: 1rem; 
            }
            .robot-img { 
                width: 50px; 
                height: 50px; 
            }
            .badge {
                font-size: 0.8rem;
                padding: 5px 10px;
            }
        }
        </style>
        
        <div class="simple-header">
            <div class="header-content">
                <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT04x5V-N0Nd5ByQVRv8tXVQ7pjtLlyUINCv8ox41vImyhKFpBvfL0rTl7Qe-BYHjHBHhI&usqp=CAU" class="robot-img" alt="Robot">
                <div class="title-container">
                    <div class="main-title">CodeBuddy AI</div>
                    <div class="subtitle">Your intelligent companion for coding challenges</div>
                </div>
                <div class="badge">
                    <span class="badge-dot"></span>
                    Powered by Gemini 2.0
                </div>
            </div>
        </div>
    """, unsafe_allow_html=True)

    # Initialize chat history
    if "messages" not in st.session_state:
        st.session_state.messages = []

    # Display chat messages
    for message in st.session_state.messages:
        with st.chat_message(message["role"]):
            st.markdown(message["content"])

    # Chat input
    if prompt := st.chat_input("Ask me any coding question..."):
        st.session_state.messages.append({"role": "user", "content": prompt})
        with st.chat_message("user"):
            st.markdown(prompt)

        with st.chat_message("assistant"):
            with st.spinner("Thinking..."):
                response = chat_with_bot(prompt)
                st.markdown(response)
                st.session_state.messages.append({"role": "assistant", "content": response})

    # Modern Sidebar with Examples
    with st.sidebar:
        st.markdown('<div class="sidebar-header">📚 Example Questions</div>', unsafe_allow_html=True)
        examples = [
            "How do I fix a merge conflict in Git?",
            "Explain recursion with a simple example",
            "What's the difference between == and === in JavaScript?",
            "Help me debug this Python error: IndentationError",
            "Best practices for React component design",
            "Explain Docker containerization"
        ]
        for example in examples:
            if st.button(example, key=example, help="Click to use this example", use_container_width=True):
                st.chat_input(example)

        # Modern Footer
        st.markdown("---")
        st.markdown('<div class="sidebar-header">🔧 About</div>', unsafe_allow_html=True)
        st.markdown("""
            <div style='animation: fadeIn 1s ease-in-out;'>
                <p style='color: #94a3b8;'>Created by M.Haris and Syeda Memona Zahra</p>
                <p style='color: #94a3b8; margin-top: 0.5rem;'>Powered by Gemini 2.0 Flash</p>
            </div>
        """, unsafe_allow_html=True)

if __name__ == "__main__":
    # Special handling for Hugging Face Spaces
    if os.environ.get('SPACE_ID'):
        print("Running on Hugging Face Spaces")
        import socket
        hostname = socket.gethostname()
        ip_address = socket.gethostbyname(hostname)
        print(f"Hostname: {hostname}, IP: {ip_address}")
        
        # Set Streamlit server settings for Hugging Face
        os.environ["STREAMLIT_SERVER_PORT"] = "7860"
        os.environ["STREAMLIT_SERVER_ADDRESS"] = "0.0.0.0"
        os.environ["STREAMLIT_SERVER_HEADLESS"] = "true"
        os.environ["STREAMLIT_SERVER_ENABLE_CORS"] = "true"
    
    # Launch the app
    main()