feat: complete pragativeer v2 frontend and backend, docker setup, and cbt engine

This commit is contained in:
2026-07-06 17:08:13 +05:30
parent ce01795aa9
commit 426b98ddab
66 changed files with 7949 additions and 363 deletions

55
backend/llm.py Normal file
View File

@@ -0,0 +1,55 @@
import os
import json
from openai import AsyncOpenAI
from sqlmodel import Session, select
from fastapi.responses import StreamingResponse
from uuid import UUID
import models
# Point the standard AsyncOpenAI client to your local Ollama server
client = AsyncOpenAI(
base_url=os.getenv("LLM_BASE_URL", "http://host.docker.internal:11434/v1"),
api_key=os.getenv("OPENAI_API_KEY", "ollama"), # Required by the SDK, but Ollama ignores it
)
async def stream_tutor_response(user_id: UUID, user_message: str, session: Session):
# 1. Fetch Context: Get the student's active weak areas
active_mistakes = session.exec(
select(models.Mistake)
.where(models.Mistake.user_id == user_id)
.where(models.Mistake.is_mastered == False)
).all()
weak_topics = list(set([m.topic for m in active_mistakes]))
context_string = ", ".join(weak_topics) if weak_topics else "General Syllabus"
# 2. Construct the highly-specific System Prompt
system_prompt = f"""
You are PragatiVeer's elite AI Tutor for JEE & NEET aspirants.
Your goal is to guide students to the answer, not just give it to them.
STUDENT CONTEXT:
This student currently struggles with the following topics: {context_string}.
Keep this in mind and relate explanations back to foundational concepts in these areas if applicable.
RULES:
1. Respond in the student's preferred language (English or Hindi).
2. Use LaTeX wrapped in single $ for inline math and $$ for block equations.
3. Keep responses concise and focused on problem-solving steps.
"""
# 3. Call the LLM
response = await client.chat.completions.create(
model=os.getenv("LLM_MODEL_NAME", "gemma"),
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message}
],
stream=True,
)
# 4. Yield tokens as they arrive
async for chunk in response:
if chunk.choices[0].delta.content is not None:
yield chunk.choices[0].delta.content