Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3c34532
feat: Implement Sample Paper Generator with format detection and ques…
shubhamxdd May 18, 2026
ea8adb4
feat: Enhance paper creation with resource validation and error handling
shubhamxdd May 18, 2026
334514d
feat: Add selected_resource_ids to chat_sessions and update related f…
shubhamxdd May 18, 2026
811d17c
feat: Improve session selection handling and update navigation logic
shubhamxdd May 18, 2026
8fd6005
feat: Enhance paper format configuration UI with input fields and dyn…
shubhamxdd May 18, 2026
8c0f79b
feat: Implement PDF generation for papers with download functionality
shubhamxdd May 18, 2026
3dad1ef
feat: Add question_pdf_url to paper_outputs and update PDF generation…
shubhamxdd May 18, 2026
dd3fee4
feat: Update generator route to accept optional paperId and sync stat…
shubhamxdd May 18, 2026
6cf4aeb
feat: Enhance job tracking for paper and resource processing with job…
shubhamxdd May 18, 2026
271a024
feat: Implement quota enforcement for free users on paper and resourc…
shubhamxdd May 18, 2026
dc4b94a
feat: Implement monthly quota reset for free users and enhance scroll…
shubhamxdd May 18, 2026
7ed7b85
Finalize Paper Generator: implement persistent context, URL-based rou…
shubhamxdd May 18, 2026
fde1188
error fix
shubhamxdd May 18, 2026
c1eecaf
feat: Update paper creation button logic to disable based on question…
shubhamxdd May 18, 2026
81da731
feat: Add user query and implement monthly paper limit check for free…
shubhamxdd May 18, 2026
c8139f8
refactor: Remove unused imports from Generator component
shubhamxdd May 18, 2026
61f54fe
feat: Enhance paper listing and dashboard with resource and question …
shubhamxdd May 18, 2026
bce477e
feat: Implement PDF export functionality for study guides and questio…
shubhamxdd May 18, 2026
cfc15d2
docs: Update architecture and standards for improved PDF generation a…
shubhamxdd May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""add selected_resource_ids to chat_sessions

Revision ID: 983ad2983d5c
Revises: d3dcf6b6fe3f
Create Date: 2026-05-18 20:55:14.773697

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '983ad2983d5c'
down_revision: Union[str, Sequence[str], None] = 'd3dcf6b6fe3f'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('chat_sessions', sa.Column('selected_resource_ids', sa.JSON(), nullable=True))
# ### end Alembic commands ###


def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('chat_sessions', 'selected_resource_ids')
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""add question_pdf_url to paper_outputs

Revision ID: ab47c0990b86
Revises: 983ad2983d5c
Create Date: 2026-05-18 21:35:55.165368

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'ab47c0990b86'
down_revision: Union[str, Sequence[str], None] = '983ad2983d5c'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('paper_outputs', sa.Column('question_pdf_url', sa.String(), nullable=True))
# ### end Alembic commands ###


def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('paper_outputs', 'question_pdf_url')
# ### end Alembic commands ###
2 changes: 2 additions & 0 deletions backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class Settings(BaseSettings):

FRONTEND_URL: str = "http://localhost:5173"
QUESTIONS_LIMIT: int = 30
PAPERS_LIMIT: int = 3
RESOURCES_LIMIT: int = 3
class Config:
env_file = ".env"

Expand Down
1 change: 1 addition & 0 deletions backend/app/llm/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def __init__(self):
self.base_url = "https://openrouter.ai/api/v1/chat/completions"

async def stream_chat(self, messages: list, model: str = "openrouter/owl-alpha") -> AsyncGenerator[str, None]:
# async def stream_chat(self, messages: list, model: str = "nvidia/nemotron-3-super-120b-a12b:free") -> AsyncGenerator[str, None]:
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
Expand Down
39 changes: 39 additions & 0 deletions backend/app/llm/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,42 @@
Question:
{question}
"""

DETECT_FORMAT_PROMPT = """
Analyse this past year paper and extract the question format.
Return ONLY a JSON object with this exact structure, no explanation:

{
"mcq": <count>,
"short": <count>,
"long": <count>,
"mcq_marks": <marks each>,
"short_marks": <marks each>,
"long_marks": <marks each>,
"total_marks": <total>,
"duration_minutes": <duration or null>
}
"""

GENERATE_PAPER_PROMPT = """
You are generating a sample exam paper for a student.

Format config: {format_config}
Subject context from student's material:
{context_chunks}

Generate exactly the number of questions specified in the format config.
Return ONLY a JSON array of question objects. Each object must have:
- type (mcq | short | long)
- marks (integer)
- topic (string)
- question_text (string)
- For MCQ: also include options (array of 4 strings) and answer (correct option text)
- For short/long: also include answer (model answer string) and explanation (string)

Rules:
- Distribute questions across different topics evenly.
- No repeated questions.
- Difficulty should match a real exam for this level.
- JSON array only. No preamble, no explanation, no markdown fences.
"""
3 changes: 2 additions & 1 deletion backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .routers import auth, resources, solver
from .routers import auth, resources, solver, papers
from .config import settings

app = FastAPI(title="PYQ Solver API")
Expand All @@ -18,6 +18,7 @@
app.include_router(auth.router, prefix="/api")
app.include_router(resources.router, prefix="/api")
app.include_router(solver.router, prefix="/api")
app.include_router(papers.router, prefix="/api")

@app.get("/")
async def root():
Expand Down
3 changes: 2 additions & 1 deletion backend/app/models/chat_session.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from sqlalchemy import Column, String, DateTime, UUID, ForeignKey
from sqlalchemy import Column, String, DateTime, UUID, ForeignKey, JSON
from sqlalchemy.orm import relationship
from datetime import datetime
import uuid
Expand All @@ -10,6 +10,7 @@ class ChatSession(Base):
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
title = Column(String, default="New Chat")
selected_resource_ids = Column(JSON, default=list)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

Expand Down
3 changes: 2 additions & 1 deletion backend/app/models/paper_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class PaperOutput(Base):
questions = Column(JSON, nullable=False) # Array of question objects
include_answers = Column(Boolean, default=True)
include_explanations = Column(Boolean, default=True)
pdf_url = Column(String, nullable=True)
pdf_url = Column(String, nullable=True) # Full version (with answers/explanations as per toggles)
question_pdf_url = Column(String, nullable=True) # Clean question paper only
created_at = Column(DateTime, default=datetime.utcnow)

paper = relationship("Paper", back_populates="output")
Loading