Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -59,6 +59,8 @@ def has_code(text):
|
|
| 59 |
# ββ Detect student signal ββββββββββββββββββββββββββββββββββββ
|
| 60 |
def detect_student_signal(text):
|
| 61 |
text_lower = text.lower()
|
|
|
|
|
|
|
| 62 |
if any(k in text_lower for k in ["hint", "help", "don't know", "idk", "no idea", "i give up", "tell me", "just give"]):
|
| 63 |
return "hint_request"
|
| 64 |
if any(k in text_lower for k in ["i think", "maybe", "is it", "could it be", "how about", "like this", "would it"]):
|
|
@@ -69,24 +71,29 @@ def detect_student_signal(text):
|
|
| 69 |
return "submitted_code"
|
| 70 |
return "general"
|
| 71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
# ββ Build adaptive prompt ββββββββββββββββββββββββββββββββββββ
|
| 73 |
def build_prompt(user_message, history, language):
|
| 74 |
signal = detect_student_signal(user_message)
|
| 75 |
|
| 76 |
-
# history: list of gr.ChatMessage objects with .role and .content
|
| 77 |
history_text = ""
|
| 78 |
if history:
|
| 79 |
pairs = []
|
| 80 |
i = 0
|
| 81 |
while i < len(history) - 1:
|
| 82 |
a, b = history[i], history[i + 1]
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
a_content = a.content if hasattr(a, "content") else a.get("content", "")
|
| 86 |
-
b_role = b.role if hasattr(b, "role") else b.get("role", "")
|
| 87 |
-
b_content = b.content if hasattr(b, "content") else b.get("content", "")
|
| 88 |
-
if a_role == "user" and b_role == "assistant":
|
| 89 |
-
pairs.append((a_content, b_content))
|
| 90 |
i += 2
|
| 91 |
else:
|
| 92 |
i += 1
|
|
@@ -96,6 +103,7 @@ def build_prompt(user_message, history, language):
|
|
| 96 |
context = f"Previous conversation:\n{history_text}" if history_text else ""
|
| 97 |
|
| 98 |
styles = {
|
|
|
|
| 99 |
"hint_request": "The student is asking for a hint.\n- Give ONE small hint only β not the answer\n- Point them in the right direction with a question\n- Do NOT write complete code",
|
| 100 |
"submitted_code": "The student has written code.\n- First check if the logic is correct or not\n- If CORRECT: validate genuinely then push forward with a question\n- If WRONG: identify the specific issue, give a targeted hint\n- If copy-pasted (too perfect): ask them to walk you through it\n- Never rewrite their code for them",
|
| 101 |
"attempting": "The student is guessing or attempting an explanation.\n- If CORRECT: confirm clearly and move to the next concept\n- If WRONG: gently correct their thinking with a question\n- Always build on what they said",
|
|
@@ -113,13 +121,31 @@ Detected language: {language}
|
|
| 113 |
{style}
|
| 114 |
|
| 115 |
{context}### Instruction:
|
| 116 |
-
|
| 117 |
|
| 118 |
### Input:
|
| 119 |
{user_message}
|
| 120 |
|
| 121 |
### Response:
|
| 122 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
# ββ Generate response ββββββββββββββββββββββββββββββββββββββββ
|
| 125 |
def generate(prompt):
|
|
@@ -133,9 +159,10 @@ def generate(prompt):
|
|
| 133 |
temperature=0.7,
|
| 134 |
top_p=0.9,
|
| 135 |
repetition_penalty=1.2,
|
|
|
|
| 136 |
)
|
| 137 |
full = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 138 |
-
return
|
| 139 |
|
| 140 |
# ββ Log conversations ββββββββββββββββββββββββββββββββββββββββ
|
| 141 |
def log_conversation(user_msg, bot_msg, language):
|
|
@@ -147,11 +174,9 @@ def log_conversation(user_msg, bot_msg, language):
|
|
| 147 |
def chat(user_message, history):
|
| 148 |
language = detect_language(user_message)
|
| 149 |
if language == "General" and history:
|
| 150 |
-
for
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
if role == "user":
|
| 154 |
-
detected = detect_language(content)
|
| 155 |
if detected != "General":
|
| 156 |
language = detected
|
| 157 |
break
|
|
@@ -165,7 +190,6 @@ with gr.Blocks(title="CodeMentor AI") as demo:
|
|
| 165 |
gr.Markdown("# π CodeMentor β AI Programming Tutor")
|
| 166 |
gr.Markdown("*Ask any programming question. I'll guide you β not give you the answer!*")
|
| 167 |
|
| 168 |
-
# No type= param β use gr.ChatMessage objects for compatibility
|
| 169 |
chatbot = gr.Chatbot(height=450)
|
| 170 |
|
| 171 |
lang_display = gr.Textbox(label="Detected Language", interactive=False, value="π General")
|
|
@@ -180,9 +204,9 @@ with gr.Blocks(title="CodeMentor AI") as demo:
|
|
| 180 |
if not message.strip():
|
| 181 |
return "", chat_history, lang_display.value
|
| 182 |
response, language = chat(message, chat_history)
|
| 183 |
-
#
|
| 184 |
-
chat_history.append(
|
| 185 |
-
chat_history.append(
|
| 186 |
return "", chat_history, f"π {language}"
|
| 187 |
|
| 188 |
send.click(respond, [msg, chatbot], [msg, chatbot, lang_display])
|
|
|
|
| 59 |
# ββ Detect student signal ββββββββββββββββββββββββββββββββββββ
|
| 60 |
def detect_student_signal(text):
|
| 61 |
text_lower = text.lower()
|
| 62 |
+
if any(k in text_lower for k in ["hello", "hi", "hey", "good morning", "good evening", "howdy"]):
|
| 63 |
+
return "greeting"
|
| 64 |
if any(k in text_lower for k in ["hint", "help", "don't know", "idk", "no idea", "i give up", "tell me", "just give"]):
|
| 65 |
return "hint_request"
|
| 66 |
if any(k in text_lower for k in ["i think", "maybe", "is it", "could it be", "how about", "like this", "would it"]):
|
|
|
|
| 71 |
return "submitted_code"
|
| 72 |
return "general"
|
| 73 |
|
| 74 |
+
# ββ Extract content from a history message (dict or ChatMessage) ββ
|
| 75 |
+
def msg_role(m):
|
| 76 |
+
return m["role"] if isinstance(m, dict) else m.role
|
| 77 |
+
|
| 78 |
+
def msg_content(m):
|
| 79 |
+
c = m["content"] if isinstance(m, dict) else m.content
|
| 80 |
+
# If content is itself a dict (ChatMessage serialization artifact), unwrap it
|
| 81 |
+
if isinstance(c, dict):
|
| 82 |
+
return c.get("text", c.get("content", str(c)))
|
| 83 |
+
return str(c)
|
| 84 |
+
|
| 85 |
# ββ Build adaptive prompt ββββββββββββββββββββββββββββββββββββ
|
| 86 |
def build_prompt(user_message, history, language):
|
| 87 |
signal = detect_student_signal(user_message)
|
| 88 |
|
|
|
|
| 89 |
history_text = ""
|
| 90 |
if history:
|
| 91 |
pairs = []
|
| 92 |
i = 0
|
| 93 |
while i < len(history) - 1:
|
| 94 |
a, b = history[i], history[i + 1]
|
| 95 |
+
if msg_role(a) == "user" and msg_role(b) == "assistant":
|
| 96 |
+
pairs.append((msg_content(a), msg_content(b)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
i += 2
|
| 98 |
else:
|
| 99 |
i += 1
|
|
|
|
| 103 |
context = f"Previous conversation:\n{history_text}" if history_text else ""
|
| 104 |
|
| 105 |
styles = {
|
| 106 |
+
"greeting": "The student just said hello.\n- Greet them briefly and warmly (1 sentence max)\n- Ask what programming topic or problem they want to work on today\n- Do NOT give any code or explanations yet",
|
| 107 |
"hint_request": "The student is asking for a hint.\n- Give ONE small hint only β not the answer\n- Point them in the right direction with a question\n- Do NOT write complete code",
|
| 108 |
"submitted_code": "The student has written code.\n- First check if the logic is correct or not\n- If CORRECT: validate genuinely then push forward with a question\n- If WRONG: identify the specific issue, give a targeted hint\n- If copy-pasted (too perfect): ask them to walk you through it\n- Never rewrite their code for them",
|
| 109 |
"attempting": "The student is guessing or attempting an explanation.\n- If CORRECT: confirm clearly and move to the next concept\n- If WRONG: gently correct their thinking with a question\n- Always build on what they said",
|
|
|
|
| 121 |
{style}
|
| 122 |
|
| 123 |
{context}### Instruction:
|
| 124 |
+
Respond to the student's message below.
|
| 125 |
|
| 126 |
### Input:
|
| 127 |
{user_message}
|
| 128 |
|
| 129 |
### Response:
|
| 130 |
+
CodeMentor:"""
|
| 131 |
+
|
| 132 |
+
# ββ Extract clean response βββββββββββββββββββββββββββββββββββ
|
| 133 |
+
def extract_response(full_text):
|
| 134 |
+
if "### Response:" in full_text:
|
| 135 |
+
after = full_text.split("### Response:")[-1]
|
| 136 |
+
else:
|
| 137 |
+
after = full_text
|
| 138 |
+
|
| 139 |
+
if after.lstrip().startswith("CodeMentor:"):
|
| 140 |
+
after = after.lstrip()[len("CodeMentor:"):].strip()
|
| 141 |
+
else:
|
| 142 |
+
after = after.strip()
|
| 143 |
+
|
| 144 |
+
for stop in ["### Input:", "### Instruction:", "Student:", "\n###"]:
|
| 145 |
+
if stop in after:
|
| 146 |
+
after = after.split(stop)[0]
|
| 147 |
+
|
| 148 |
+
return after.strip()
|
| 149 |
|
| 150 |
# ββ Generate response ββββββββββββββββββββββββββββββββββββββββ
|
| 151 |
def generate(prompt):
|
|
|
|
| 159 |
temperature=0.7,
|
| 160 |
top_p=0.9,
|
| 161 |
repetition_penalty=1.2,
|
| 162 |
+
pad_token_id=tokenizer.eos_token_id,
|
| 163 |
)
|
| 164 |
full = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 165 |
+
return extract_response(full)
|
| 166 |
|
| 167 |
# ββ Log conversations ββββββββββββββββββββββββββββββββββββββββ
|
| 168 |
def log_conversation(user_msg, bot_msg, language):
|
|
|
|
| 174 |
def chat(user_message, history):
|
| 175 |
language = detect_language(user_message)
|
| 176 |
if language == "General" and history:
|
| 177 |
+
for m in history:
|
| 178 |
+
if msg_role(m) == "user":
|
| 179 |
+
detected = detect_language(msg_content(m))
|
|
|
|
|
|
|
| 180 |
if detected != "General":
|
| 181 |
language = detected
|
| 182 |
break
|
|
|
|
| 190 |
gr.Markdown("# π CodeMentor β AI Programming Tutor")
|
| 191 |
gr.Markdown("*Ask any programming question. I'll guide you β not give you the answer!*")
|
| 192 |
|
|
|
|
| 193 |
chatbot = gr.Chatbot(height=450)
|
| 194 |
|
| 195 |
lang_display = gr.Textbox(label="Detected Language", interactive=False, value="π General")
|
|
|
|
| 204 |
if not message.strip():
|
| 205 |
return "", chat_history, lang_display.value
|
| 206 |
response, language = chat(message, chat_history)
|
| 207 |
+
# Plain dicts β the correct format for this Gradio version
|
| 208 |
+
chat_history.append({"role": "user", "content": message})
|
| 209 |
+
chat_history.append({"role": "assistant", "content": response})
|
| 210 |
return "", chat_history, f"π {language}"
|
| 211 |
|
| 212 |
send.click(respond, [msg, chatbot], [msg, chatbot, lang_display])
|