56 lines
2.0 KiB
Python
56 lines
2.0 KiB
Python
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
|