File size: 13,182 Bytes
9dc3c7d
3453851
9dc3c7d
e6b8201
be8e488
 
 
e6b8201
4201d50
be8e488
e6b8201
0f94ba9
4201d50
 
 
cbe4c46
4201d50
 
 
cbe4c46
4201d50
 
 
 
 
 
 
cbe4c46
4201d50
 
 
cbe4c46
53757d2
9bb5347
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
9bb5347
 
8668d04
 
466ba96
 
 
 
9bb5347
4201d50
0f94ba9
dfaca80
 
4201d50
0f94ba9
 
f2d66b8
 
 
 
 
 
 
 
0f94ba9
 
f2d66b8
 
 
 
 
 
 
 
 
 
 
0f94ba9
f2d66b8
0f94ba9
f2d66b8
 
 
 
 
 
 
 
 
 
 
e6b8201
0f94ba9
 
 
 
 
 
3f85f3b
0f94ba9
 
3f85f3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6fd428e
 
 
de0cc73
 
 
 
6fd428e
de0cc73
 
 
3f85f3b
6fd428e
 
 
 
 
 
 
3f85f3b
de0cc73
6fd428e
de0cc73
6fd428e
 
 
 
3f85f3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0f94ba9
de530b2
6fd428e
0f94ba9
de0cc73
 
 
 
 
 
0f94ba9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b495687
0f94ba9
 
 
 
 
e6b8201
b495687
 
 
0f94ba9
 
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
from huggingface_hub import InferenceClient
from sentence_transformers import SentenceTransformer
import gradio as gr
import numpy as np 
import os 

hf_token = os.getenv("HF_TOKEN")

encoder_model = SentenceTransformer("all-MiniLM-L6-v2")
client = InferenceClient("meta-llama/Meta-Llama-3-8B-Instruct", token=hf_token)

# Knowledge Base -- all thypes of errors and their description.
knowledge_base = [
    {
        "error_type": "IndexError: list index out of range",
        "context": "Occurs when trying to access an index that doesn't exist in a list. Remember Python and many other programming languages lists start at index 0. If a list has 3 items, valid indices are 0, 1, and 2."
    },
    {
        "error_type": "TypeError: 'int' object is not iterable",
        "context": "Occurs when trying to loop through or unpack an integer instead of a collection like a list or dictionary. Check if you passed a number to a for loop or any other type of loop."
    },
    {
        "error_type": "KeyError",
        "context": "Occurs when trying to access a dictionary key that does not exist. Check for typos or use the .get() method to handle missing keys safely."
    },
    {
        "error_type": "IndentationError: unexpected indent",
        "context": "Python and many other programming languages relies heavily on whitespace. Some languages like C++ and Javascript may rely on curly braces. Ensure you are consistently using either 4 spaces or 1 tab inside loops and functions."
    },
    {
        "error_type": "NameError: name is not defined",
        "context": "Occurs when using a variable or function name that Python or any other programming language doesn't recognize. Common causes: a typo, or calling it before it is defined."
    },
    {
        "error_type": "TypeError: unsupported operand type(s)",
        "context": "Occurs when performing an operation between incompatible data types, such as adding a string and an integer. Check the types of the variables involved."
    },
    {
        "error_type": "ValueError",
        "context": "Occurs when a function receives the correct type of argument but an invalid value. Example: converting 'hello' to an integer using int()."
    },
    {
        "error_type": "AttributeError",
        "context": "Occurs when trying to access a method or attribute that an object does not have. Check for typos or whether the object is the expected type."
    },
    {
        "error_type": "ZeroDivisionError",
        "context": "Occurs when attempting to divide a number by zero. Ensure the denominator is not zero before performing division."
    },
    {
        "error_type": "SyntaxError",
        "context": "Occurs when Python encounters invalid code structure. Common causes include missing colons, parentheses, quotes, or incorrect statement formatting."
    },
    {
        "error_type": "ModuleNotFoundError",
        "context": "Occurs when Python cannot find the module being imported. Verify the module is installed and the import statement is correct."
    },
    {
        "error_type": "ImportError",
        "context": "Occurs when Python finds a module but cannot import a specific object from it. Check spelling and module contents."
    },
    {
        "error_type": "FileNotFoundError",
        "context": "Occurs when trying to open or access a file that does not exist at the specified path. Verify the filename and location."
    },
    {
        "error_type": "PermissionError",
        "context": "Occurs when the program does not have permission to access a file, folder, or system resource. Check file permissions and access rights."
    },
    {
        "error_type": "UnboundLocalError",
        "context": "Occurs when a local variable is referenced before it has been assigned a value within a function."
    },
    {
        "error_type": "RecursionError",
        "context": "Occurs when a function calls itself too many times without reaching a stopping condition. Verify the base case of recursive functions."
    },
    {
        "error_type": "MemoryError",
        "context": "Occurs when the program runs out of available memory. This often happens with very large datasets or infinite data structures."
    },
    {
        "error_type": "AssertionError",
        "context": "Occurs when an assert statement evaluates to False. Check whether the expected condition is actually true."
    },
    {
        "error_type": "RuntimeError",
        "context": "A generic error indicating something unexpected happened during execution that doesn't fit a more specific exception type."
    },
    {
        "error_type": "KeyboardInterrupt",
        "context": "Occurs when the user manually stops the program, usually by pressing Ctrl+C."
    },
    {
        "error_type": "EOFError",
        "context": "Occurs when input() reaches the end of input unexpectedly. Common in online judges or automated testing environments."
    },
    {
        "error_type": "Wrong Data Type",
        "context": "The program runs but produces incorrect results because a variable contains a different type than expected."
    }
]

kb_contexts = []
kb_embeddings = []

# Encoded context for the code error in the knowledge base

for item in knowledge_base:
    context_text = item["context"]
    kb_contexts.append(context_text)
    embedding = encoder_model.encode(context_text)
    kb_embeddings.append(embedding)

kb_embeddings = np.array(kb_embeddings)

#Used to find cosine similarity and used AI to look at what the np.argmax and np.linalg.norm() and the general logic behind implementing it in the chat.

def get_relevant_context(user_message):
    user_embedding = encoder_model.encode(user_message)
    
    similarities = np.dot(kb_embeddings, user_embedding) / (
        np.linalg.norm(kb_embeddings, axis=1) * np.linalg.norm(user_embedding)
    )
    
    best_idx = np.argmax(similarities)

    if similarities[best_idx] > 0.3:
        return kb_contexts[best_idx]
    return "No specific database match found. Use general debugging wisdom to prompt the user."

def response(message, history):
    retrieved_context = get_relevant_context(message)

    system_prompt = (
        "You are 'The Sherlock of Bugs', an interactive, clever, and supportive debugging mentor.\n\n"
        "YOUR RULES:\n"
        "1. When the user presents an error message, DO NOT give away the direct solution or fix immediately.\n"
        "2. Instead, use the provided Context to give them architectural clues or structural hints. Ask them how they think they should fix it, and encourage them to try writing a solution.\n"
        "3. If the user responds with a fix or an explanation, evaluate it. Tell them clearly if they are correct or incorrect.\n"
        "4. If they are correct, praise them and summarize the clean code. If they are struggling or incorrect, guide them closer or finally reveal the exact, elegant solution.\n\n"
        f"RELEVANT BACKGROUND CONTEXT FOR THIS CONVERSATION:\n{retrieved_context}"
    )
    
    messages = [{"role": "system", "content": system_prompt}]
    
    for turn in history:
        messages.append({"role": turn.get("role", "user"), "content": turn.get("content", "")})
        
    messages.append({"role": "user", "content": message})

    res = client.chat_completion(messages, max_tokens=1000)
    return res.choices[0].message.content.strip()

custom_css = """
body, .gradio-container { 
    background-color: #0b001a !important; 
    color: #ff77ff !important; 
    font-family: 'Segoe UI', system-ui, sans-serif !important; 
}
.gradio-container { 
    border: 2px solid #ff007f !important; 
    border-radius: 16px !important; 
    box-shadow: 0 0 35px rgba(255, 0, 127, 0.25), inset 0 0 15px rgba(112, 0, 255, 0.15) !important; 
    padding: 25px !important; 
    background: radial-gradient(circle at center, #1a0033 0%, #060010 100%) !important; 
}



/* Glowing Header Board -- Used AI for this part to make the image expanded */
.logo-header {     
    text-align: center;     
    border: 2px dashed #ff007f;     
    background: rgba(255, 0, 127, 0.03);     
    padding: 0px !important; /* Forces padding to 0 so the image touches the border */
    border-radius: 8px;     
    margin-bottom: 25px;     
    box-shadow: 0 0 20px rgba(255, 0, 127, 0.15);     
    animation: pulse-glow 4s infinite alternate; 
    overflow: hidden; /* Keeps the image corners tucked within the border-radius */
}

/* Force Gradio's internal layout divs to expand fully */
.logo-header > div, .logo-header .image-container {
    width: 100% !important;
    height: 100% !important;
}

/* Forces the banner image to stretch horizontally across the box */
.logo-header img {
    width: 100% !important;
    height: auto !important;
    object-fit: cover !important; /* Scales smoothly without losing proportions */
    display: block;
}

/* Chatbot Window Customization */
.chatbot {
    border: 1px solid #7000ff !important;
    background: rgba(15, 0, 30, 0.6) !important;
}

/* Styling Chat Bubbles Natively */
.message.user { 
    background-color: #3c006b !important; 
    color: #ffffff !important; 
    border: 1px solid #ff007f !important; 
    box-shadow: 0 0 10px rgba(255, 0, 127, 0.2);
}
.message.bot { 
    background-color: #16002c !important; 
    color: #ff77ff !important; 
    border: 1px solid #7000ff !important; 
    box-shadow: 0 0 10px rgba(112, 0, 255, 0.2);
}

/* Buttons Styling */
.cyber-btn { 
    background: #ff007f !important; 
    color: #ffffff !important; 
    font-weight: bold !important; 
    border: none !important; 
    border-radius: 6px !important; 
    box-shadow: 0 0 15px rgba(255, 0, 127, 0.4) !important; 
    cursor: pointer; 
    transition: all 0.25s ease !important; 
    text-transform: uppercase; 
    letter-spacing: 1px; 
}
.cyber-btn:hover { 
    background: #7000ff !important; 
    box-shadow: 0 0 25px #7000ff !important; 
}

.secondary-btn { 
    background-color: rgba(112, 0, 255, 0.1) !important; 
    color: #ff77ff !important; 
    border: 1px solid #7000ff !important; 
    border-radius: 6px !important; 
    box-shadow: 0 0 10px rgba(112, 0, 255, 0.2) !important; 
    text-transform: uppercase; 
    transition: all 0.25s ease !important;
}
.secondary-btn:hover { 
    background: rgba(255, 0, 127, 0.15) !important; 
    border-color: #ff007f !important;
    box-shadow: 0 0 20px #ff007f !important; 
    color: #fff !important;
}

footer { display: none !important; }

@keyframes pulse-glow {
    0% { box-shadow: 0 0 15px rgba(255, 0, 127, 0.1); border-color: #ff007f; }
    100% { box-shadow: 0 0 30px rgba(112, 0, 255, 0.4); border-color: #7000ff; }
}
"""

with gr.Blocks(css=custom_css, theme="hmb/vaporwave") as demo:
    # 1. Header
    with gr.Group(elem_classes=["logo-header"]):
        gr.Image(
            value="TheSherLockOfBugs.jpeg", 
            show_label=False, 
            container=False, 
            interactive=False
        )
    # 2. Main Chat Interface
    chatbot_window = gr.ChatInterface(fn=response)

    # 4. Quick Action Controls - Giving hint button and resetting the chatbot board 
    with gr.Row():
        clue_btn = gr.Button("🔍 Give Me Another Hint", elem_classes=["cyber-btn"])
        clear_btn = gr.Button("📁 Reset Investigation Board", elem_classes=["secondary-btn"])

    # 5. Sample Error Testing PaRT of the code 
    gr.HTML("<h3 style='color: #ff77ff; margin-top: 20px; text-shadow: 0 0 5px #ff007f;'>🧪 Select a Sample Case File to Test:</h3>")
    with gr.Row():
        btn_index = gr.Button("IndexError Example", elem_classes=["secondary-btn"])
        btn_type = gr.Button("TypeError Example", elem_classes=["secondary-btn"])
        btn_key = gr.Button("KeyError Example", elem_classes=["secondary-btn"])
        btn_zero = gr.Button("ZeroDivision Example", elem_classes=["secondary-btn"])

    # Used AI for this part to know how to make buttons using gradio, the lambda function is something i replaced to make it more organized. 

    def load_error_1(): 
        return "my_list = [1, 2, 3]\nprint(my_list[5])\nIndexError: list index out of range"
    def load_error_2(): 
        return "for i in 10:\nprint(i)\nTypeError: 'int' object is not iterable"
    def load_error_3(): 
        return "user_data = {'name': 'Alice'}\nprint(user_data['age'])\nKeyError: 'age'"
    def load_error_4(): 
        return "result = 10 / 0\nZeroDivisionError: division by zero"
    def ask_for_hint(): 
        return "I am stuck on this error. Can you give me another clue or hint on what part of my structure to look at?"

    # Attach event listeners to your buttons...
    btn_index.click(fn=load_error_1, outputs=chatbot_window.textbox)
    btn_type.click(fn=load_error_2, outputs=chatbot_window.textbox)
    btn_key.click(fn=load_error_3, outputs=chatbot_window.textbox)
    btn_zero.click(fn=load_error_4, outputs=chatbot_window.textbox)
    clue_btn.click(fn=ask_for_hint, outputs=chatbot_window.textbox)
    
    # --- ADD THIS LINE TO FIX THE RESET BUTTON ---
    clear_btn.click(fn=lambda: ([], ""), outputs=[chatbot_window.chatbot, chatbot_window.textbox])

# Launch the custom chatbot app directly 
demo.launch()