From bfdfdfb53cf6c755ffc2d5235b3b407e73f60772 Mon Sep 17 00:00:00 2001 From: abiyasa05 Date: Fri, 6 Jun 2025 14:55:44 +0700 Subject: [PATCH] create: model for llm --- .../__pycache__/model_llmnya.cpython-310.pyc | Bin 0 -> 5726 bytes Model LLM/fastapi-llama/model_llmnya.py | 183 ++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 Model LLM/fastapi-llama/__pycache__/model_llmnya.cpython-310.pyc create mode 100644 Model LLM/fastapi-llama/model_llmnya.py diff --git a/Model LLM/fastapi-llama/__pycache__/model_llmnya.cpython-310.pyc b/Model LLM/fastapi-llama/__pycache__/model_llmnya.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77115ce39b4a9e3ff0289fcb3343ffa28221f55f GIT binary patch literal 5726 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 max_chars: + logging.warning(f"Teks terlalu panjang ({len(text)} karakter), dipotong jadi {max_chars}") + return text[:max_chars] + return text + +@app.post("/generate-from-material/") +async def generate_from_material(request: MaterialRequest): + try: + if request.question_count < 1 or request.question_count > 20: + raise HTTPException(status_code=400, detail="Jumlah soal harus antara 1-20") + + if request.question_type not in ["multiple_choice", "essay"]: + raise HTTPException(status_code=400, detail="Jenis soal tidak valid. Pilih: multiple_choice atau essay") + + mc_count = request.question_count if request.question_type == "multiple_choice" else 0 + essay_count = request.question_count if request.question_type == "essay" else 0 + + content_bersih = potong_konten(request.content.strip()) + + prompt = f""" +Buat soal latihan berdasarkan teks materi berikut untuk siswa SD kelas 3. + +**Instruksi:** +1. Buat total {request.question_count} soal dengan rincian: + - Soal pilihan ganda: {mc_count} + - Soal isian: {essay_count} + +2. **Untuk soal isian:** + - Ambil kutipan **minimal dua kalimat yang saling terhubung** dari teks sebagai dasar soal + - Awali dengan: **Bacalah kutipan berikut: "[kutipan]"** + - Buat pertanyaan berdasarkan kutipan tersebut + - Sertakan **jawaban singkat** + - Beri bobot antara **3–5** sesuai kompleksitas + +3. **Untuk soal pilihan ganda (jika ada):** + - Ambil kutipan **1 kalimat yang relevan** dari teks + - Buat pertanyaan dan 4 pilihan jawaban (A–D) + - Beri jawaban benar dan bobot antara **1–2** sesuai tingkat kesulitan + +4. Gunakan bahasa sederhana dan sesuai dengan siswa SD kelas 3. + +5. Jangan menambahkan informasi di luar teks materi. + +**Format Output:** +""" + (""" +**Soal Pilihan Ganda:** +1. Bacalah kutipan berikut: "[2 kalimat atau lebih dari teks]" + Pertanyaan: [Pertanyaan] + A. [Opsi A] + B. [Opsi B] + C. [Opsi C] + D. [Opsi D] + Jawaban: [Huruf Opsi] + Bobot: [1 atau 2] +""" if mc_count > 0 else "") + (""" + +**Soal Isian:** +1. Bacalah kutipan berikut: "[2 kalimat atau lebih dari teks]". [Pertanyaan] + Jawaban: [Jawaban] + Bobot: [3 - 5] +""" if essay_count > 0 else "") + f""" + +--- + +**Materi:** +{content_bersih} +""".strip() + + logging.info("Mengirim prompt ke Ollama...") + async with httpx.AsyncClient(timeout=120) as client: + response = await client.post(OLLAMA_URL, json={ + "model": "llama3.1:latest", + "prompt": prompt, + "stream": False, + "options": { + "num_predict": 2048 + } + }) + + response.raise_for_status() + result = response.json() + generated_text = result.get("response", "").strip() + + return { + "generated_questions": generated_text, + "question_type": request.question_type, + "question_count": request.question_count, + "mc_count": mc_count, + "essay_count": essay_count + } + + except Exception as e: + logging.error(f"Error saat generate: {e}") + raise HTTPException(status_code=500, detail=f"Terjadi kesalahan internal: {str(e)}") + +@app.post("/generate-feedback/") +async def generate_feedback(request: FeedbackRequest): + try: + user_answer = request.user_answer.strip() + expected_answer = request.expected_answer.strip() + + prompt_hash = hashlib.sha256(f"{user_answer}|{expected_answer}".encode()).hexdigest() + if prompt_hash in feedback_cache: + logging.info("Feedback dari cache.") + return {"feedback": feedback_cache[prompt_hash]} + + prompt = f""" +Kamu adalah asisten pengajar untuk siswa SD kelas 3. Siswa memberikan jawaban berikut untuk soal isian. + +**Jawaban Siswa:** {user_answer} +**Jawaban Ideal:** {expected_answer} + +Beri feedback singkat dan membangun, maksimal 2 kalimat. Gunakan bahasa yang mudah dimengerti oleh siswa SD. Jika jawaban siswa salah, berikan petunjuk atau koreksi yang membantu. +""" + + logging.info("Mengirim prompt feedback ke Ollama...") + async with httpx.AsyncClient(timeout=60) as client: + response = await client.post(OLLAMA_URL, json={ + "model": "llama3.1:latest", + "prompt": prompt, + "stream": False + }) + + response.raise_for_status() + result = response.json() + feedback = result.get("response", "").strip() + + feedback_cache[prompt_hash] = feedback + return {"feedback": feedback} + + except Exception as e: + logging.error(f"Error saat generate feedback: {e}") + raise HTTPException(status_code=500, detail=f"Terjadi kesalahan: {str(e)}") \ No newline at end of file