Spaces:
Running
Running
Download app.py from Barat123/STASS: direct link, hf CLI and curl.
- Browser
- Download file 8.42 kB
-
https://huggingface.co/spaces/Barat123/STASS/resolve/main/app.py
- Command line
-
hf download hf://spaces/Barat123/STASS/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Barat123/STASS/resolve/main/app.py
8.42 kB
| import os | |
| import gradio as gr | |
| from huggingface_hub import InferenceClient | |
| from docx import Document | |
| from PIL import Image | |
| import pytesseract | |
| import fitz | |
| import pathlib | |
| import threading | |
| import time | |
| def resolve_token(ui_token): | |
| if ui_token and ui_token.strip(): | |
| return ui_token.strip() | |
| env_token = os.getenv("hf") | |
| if env_token: | |
| return env_token.strip() | |
| return None | |
| SUPPORTED_EXT = ( | |
| ".pdf", ".docx", ".txt", ".png", ".jpg", ".jpeg", ".webp", ".bmp", ".tiff" | |
| ) | |
| def extract_text_from_file(filepath): | |
| if not filepath: | |
| return "" | |
| if isinstance(filepath, dict) and "name" in filepath: | |
| filepath = filepath["name"] | |
| ext = pathlib.Path(filepath).suffix.lower() | |
| try: | |
| if ext == ".pdf": | |
| doc = fitz.open(filepath) | |
| return "\n".join(page.get_text() for page in doc) | |
| elif ext == ".docx": | |
| doc = Document(filepath) | |
| return "\n".join(p.text for p in doc.paragraphs) | |
| elif ext == ".txt": | |
| with open(filepath, "r", encoding="utf-8", errors="ignore") as f: | |
| return f.read() | |
| elif ext in (".png", ".jpg", ".jpeg", ".webp", ".bmp", ".tiff"): | |
| img = Image.open(filepath) | |
| return pytesseract.image_to_string(img) | |
| else: | |
| return "Unsupported file type" | |
| except Exception as e: | |
| return f"Error reading file: {str(e)}" | |
| MODELS = { | |
| "DeepSeek-Qwen 1.5B (Fastest)": "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B", | |
| "Qwen 3 4B (Good Speed)": "Qwen/Qwen3-4B-Thinking-2507:nscale", | |
| "Gemma 3 4B": "google/gemma-3n-E4B-it:together", | |
| "DeepSeek-Llama 8B (Recommended)": "deepseek-ai/DeepSeek-R1-Distill-Llama-8B:nscale", | |
| "Qwen 3 8B": "Qwen/Qwen3-8B:nscale", | |
| "OpenAI GPT OSS 120B": "openai/gpt-oss-120b:novita", | |
| "Gemma 4 31B (Best)": "google/gemma-4-31B-it:novita" | |
| } | |
| ALL_MODEL_NAMES = list(MODELS.keys()) | |
| SYSTEM_MSG = """You are an expert Academic Instructor and Curriculum Designer, highly familiar with SASTRA University's academic structure. | |
| - STRICTLY adhere to the provided syllabus/topic. Do not invent concepts, formulas, or fake exam papers. | |
| - Use clean, structured Markdown. Enforce exact formatting where specified. | |
| - Keep outputs practical, concise, and immediately usable. Avoid overcomplication. | |
| - SASTRA RESOURCES: Actively identify and recommend SASTRA-specific resources. Mention SASTRA Material Hub, Material Base, previous year CIA/End-Sem papers, and standard textbooks prescribed by SASTRA faculty. Frame teaching points and questions to match SASTRA's typical assessment patterns (CIA 1, CIA 2, End Semester). | |
| - GENERAL RESOURCES: If a specific standard textbook or verified platform (like NPTEL, standard authors) is widely used for this topic, cite it accurately. | |
| - NEVER hallucinate citations or paper questions. If unsure about a specific SASTRA paper, generalize based on typical university patterns while maintaining the SASTRA difficulty curve.""" | |
| def make_prompts(syllabus): | |
| base = f""" | |
| Academic Context: | |
| {syllabus} | |
| General Requirements: | |
| - Maintain strict alignment with the syllabus | |
| - Format strictly in Markdown | |
| - Keep explanations concise, practical, and exam/teaching ready | |
| - Cite SASTRA-specific resources (Material Hub, Material Base, previous papers) or widely recognized academic platforms | |
| """ | |
| return { | |
| "lesson": base + """ | |
| Generate a period-wise teaching schedule (Lesson Plan): | |
| - Assume each period is 45-50 minutes each. | |
| - Output MUST be a single Markdown table with exactly these columns: | |
| | Period | Topic/Subtopic | Key Teaching Points | | |
| - Keep descriptions brief. Focus on logical progression. | |
| - Reserve the final period for revision. | |
| - Output MUST be a valid GitHub-Flavored Markdown table. | |
| - Do NOT use ASCII separators like +--- or ---- | |
| - Table must render correctly in Markdown. | |
| - Give 'Resources' relevant SASTRA Material Hub modules, slides or materials from the interneton that topic, standard prescribed book chapters, or typical CIA question types for all topics. | |
| - Explain each concepts: Defintion (understandable way only, like points), examples (very important) | |
| """, | |
| "qa": base + """ Generate 8 exam-style questions with answers: | |
| - Match SASTRA CIA and End-Sem difficulty (include tricky and conceptual questions). | |
| - Mix: | |
| * 3 short answers (2β3 lines) | |
| * 3 medium answers (5β6 lines) | |
| * 2 long answers (8β12 lines, may include derivations or explanations or code, for diagrams verify materials/resources/text books) | |
| - Include at least 2 tricky or application-based questions. | |
| - Format strictly: | |
| **Q1.** [Question] | |
| **A:** [Answer] | |
| - For long answers: | |
| - Use stepwise explanation or bullet points | |
| - Include formulas where relevant | |
| - If aligned with SASTRA Material Base or textbook patterns, mention briefly. | |
| """, | |
| "mcq": base + """ | |
| Generate 8 multiple-choice questions: | |
| - 4 options (A-D), one clearly correct, three plausible distractors. | |
| - Format strictly: | |
| **Q1.** [Stem] | |
| A) ... B) ... C) ... D) ... | |
| **Correct:** [Letter] | **Why:** [1-line rationale] | |
| - Focus on core concepts, formula application, and common student mistakes seen in SASTRA exams. | |
| """, | |
| "summary": base + """ | |
| Produce a rapid-revision summary: | |
| - List 5 core concepts/formulas | |
| - Provide 3 high-yield takeaways for SASTRA exams | |
| - Include 1 quick self-check question based on typical CIA patterns | |
| - Keep strictly under 150 words. Use bullet points only. No paragraphs. | |
| """ | |
| } | |
| def stream_llm(model_id, prompt, hf_token): | |
| if not hf_token: | |
| yield "β No Hugging Face API key found." | |
| return | |
| try: | |
| client = InferenceClient(token=hf_token) | |
| stream = client.chat.completions.create( | |
| model=model_id, | |
| messages=[ | |
| {"role": "system", "content": SYSTEM_MSG}, | |
| {"role": "user", "content": prompt}, | |
| ], | |
| max_tokens=2048, | |
| temperature=0.7, | |
| top_p=0.95, | |
| stream=True, | |
| ) | |
| partial = "" | |
| for chunk in stream: | |
| if chunk.choices and chunk.choices[0].delta: | |
| token = chunk.choices[0].delta.get("content", "") | |
| if token: | |
| partial += token | |
| yield partial | |
| except Exception as e: | |
| yield f"β API Error:\n{str(e)}" | |
| def generate_content(text, file, model_label, token): | |
| hf_token = resolve_token(token) | |
| file_text = extract_text_from_file(file) if file else "" | |
| syllabus = (text + "\n\n" + file_text).strip() | |
| if not syllabus: | |
| yield ("Provide topic or file", "", "", "") | |
| return | |
| prompts = make_prompts(syllabus) | |
| model_id = MODELS[model_label] | |
| outputs = ["", "", "", ""] | |
| keys = ["lesson", "qa", "mcq", "summary"] | |
| lock = threading.Lock() | |
| def run_stream(i, key): | |
| for chunk in stream_llm(model_id, prompts[key], hf_token): | |
| with lock: | |
| outputs[i] = chunk | |
| threads = [] | |
| for i, key in enumerate(keys): | |
| t = threading.Thread(target=run_stream, args=(i, key)) | |
| t.start() | |
| threads.append(t) | |
| while any(t.is_alive() for t in threads): | |
| time.sleep(0.1) | |
| yield tuple(outputs) | |
| yield tuple(outputs) | |
| CSS = """ | |
| body,.gradio-container{ | |
| font-family: Inter, sans-serif !important; | |
| } | |
| """ | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# π AI Study Material Generator (Streaming)") | |
| with gr.Row(): | |
| with gr.Column(): | |
| text_input = gr.Textbox(placeholder="Paste syllabus or topic", lines=6) | |
| file_input = gr.File(label="Upload syllabus file", file_types=list(SUPPORTED_EXT)) | |
| with gr.Column(): | |
| model_selector = gr.Dropdown(choices=ALL_MODEL_NAMES, value="DeepSeek-Llama 8B (Recommended)", label="Model") | |
| token_box = gr.Textbox(label="HF API Key (optional)", type="password") | |
| btn = gr.Button("Generate") | |
| with gr.Tabs(): | |
| with gr.TabItem("Lesson Plan"): | |
| lesson = gr.Markdown() | |
| with gr.TabItem("Question and Anwer"): | |
| qa = gr.Markdown() | |
| with gr.TabItem("MCQ"): | |
| mcq = gr.Markdown() | |
| with gr.TabItem("Summary"): | |
| summary = gr.Markdown() | |
| btn.click( | |
| fn=generate_content, | |
| inputs=[text_input, file_input, model_selector, token_box], | |
| outputs=[lesson, qa, mcq, summary] | |
| ) | |
| demo.launch(css=CSS) |