File size: 3,681 Bytes
75b206c
d74f193
734411c
60e502c
dd686dc
734411c
d74f193
 
21b2f6b
 
3596665
21b2f6b
 
734411c
21b2f6b
 
 
 
d74f193
21b2f6b
2149ab2
21b2f6b
 
dd686dc
21b2f6b
 
734411c
21b2f6b
 
 
d74f193
21b2f6b
 
 
3596665
d74f193
21b2f6b
734411c
 
 
 
 
 
 
 
21b2f6b
734411c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21b2f6b
d74f193
 
 
 
734411c
 
 
 
493a0a1
21b2f6b
 
fca1e2d
5b13510
2c81e95
d74f193
734411c
 
 
 
21b2f6b
 
 
 
 
 
 
 
734411c
21b2f6b
 
734411c
 
9573ebf
21b2f6b
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
import torch
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
import streamlit as st

# Use CPU
device = torch.device("cpu")

# Set page config
st.set_page_config(page_title="Python Tutor", page_icon="🐍")
st.title("Python Tutor for Beginners (LoRA)")
st.markdown("Ask me any *Python programming* question below:")

# Load tokenizer and base model
@st.cache_resource
def load_model():
    tokenizer = AutoTokenizer.from_pretrained("TinyLLaMA/TinyLLaMA-1.1B-Chat-v1.0")
    tokenizer.pad_token = tokenizer.eos_token if tokenizer.pad_token is None else tokenizer.pad_token

    base_model = AutoModelForCausalLM.from_pretrained(
        "TinyLlaMA/TinyLlaMA-1.1B-Chat-v1.0",
        torch_dtype=torch.float32,
    ).to(device)

    model = PeftModel.from_pretrained(base_model, "lora_adapter").to(device)
    model.eval()

    classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli", device=-1)

    return tokenizer, model, classifier

tokenizer, model, classifier = load_model()

# User input
user_question = st.text_input("Your question:", placeholder="e.g. What is a Python dictionary?")

# Helper to check topic relevance
def is_python_related(question):
    candidate_labels = [
        "Python programming", "Java programming", "General knowledge",
        "Geography", "Politics", "Entertainment", "History", "Science"
    ]
    result = classifier(question, candidate_labels)
    return result['labels'][0] == "Python programming"

# Code block formatter
def format_code_blocks(text):
    if "```" in text:
        return text
    lines = text.split("\n")
    in_code = False
    formatted = []
    for line in lines:
        if line.strip().startswith((">>>", "#")) or line.strip().endswith(":") or ("=" in line and not line.strip().startswith("-")):
            if not in_code:
                formatted.append("```python")
                in_code = True
        elif in_code and line.strip() == "":
            formatted.append("```")
            in_code = False
        formatted.append(line)
    if in_code:
        formatted.append("```")
    return "\n".join(formatted)

# Process the question
if user_question:
    if len(user_question.strip()) < 10:
        st.warning("Please ask a more specific Python question.")
    else:
            if not is_python_related(user_question):
                st.error("Sorry, I am a Python tutor. I cannot answer this.")
            else:
                with st.spinner("Thinking..."):

                    prompt = f"""You are a helpful and knowledgeable Python programming tutor.
Always provide short, clear, beginner-friendly explanations with examples.

Question: {user_question}
Answer:"""

                    inputs = tokenizer(prompt, return_tensors="pt").to(device)

                    with torch.inference_mode():
                        output = model.generate(
                            **inputs,
                            max_new_tokens=512,
                            do_sample=False,
                            repetition_penalty=1.1,
                            eos_token_id=tokenizer.eos_token_id,
                            pad_token_id=tokenizer.pad_token_id
                        )

                    decoded = tokenizer.decode(output[0], skip_special_tokens=True)
                    answer = decoded.split("Answer:")[-1].split("Question:")[0].strip()  # clean hallucinated extra question
                    formatted = format_code_blocks(answer)

                    st.markdown("### 💡 Answer:")
                    with st.expander("🔍 Click to view response"):
                        st.markdown(formatted)