Spaces:
Sleeping
Sleeping
| 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 | |
| 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) | |