From 4c27209d1d99988f251971d06c8c357a642f70f5 Mon Sep 17 00:00:00 2001 From: KevinRawal Date: Sun, 31 Aug 2025 23:23:09 +0530 Subject: [PATCH 1/3] updated prompt engineering and implemented tech description --- ...417469bac4de_removed_auth_type_from_otp.py | 40 ++++++++++++ .../versions/a4b5fabfdeac_add_otp_table.py | 40 ++++++------ .../versions/a6d1c6c13842_add_role_field.py | 38 ++++++++++++ app/api/routes/questions.py | 28 +++++++-- app/main.py | 9 --- app/prompts/daily_question_prompt.py | 61 +++++++++++++------ app/schemas/utils.py | 12 ++++ app/services/questions.py | 32 +++++----- 8 files changed, 189 insertions(+), 71 deletions(-) create mode 100644 app/alembic/versions/417469bac4de_removed_auth_type_from_otp.py create mode 100644 app/alembic/versions/a6d1c6c13842_add_role_field.py diff --git a/app/alembic/versions/417469bac4de_removed_auth_type_from_otp.py b/app/alembic/versions/417469bac4de_removed_auth_type_from_otp.py new file mode 100644 index 0000000..f7bca05 --- /dev/null +++ b/app/alembic/versions/417469bac4de_removed_auth_type_from_otp.py @@ -0,0 +1,40 @@ +"""removed auth-type from OTP + +Revision ID: 417469bac4de +Revises: a6d1c6c13842 +Create Date: 2025-08-31 09:57:49.661025 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "417469bac4de" +down_revision: Union[str, Sequence[str], None] = "a6d1c6c13842" +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.drop_column("otps", "provider_id") + op.drop_column("otps", "auth_type") + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "otps", + sa.Column("auth_type", sa.VARCHAR(), autoincrement=False, nullable=False), + ) + op.add_column( + "otps", + sa.Column("provider_id", sa.VARCHAR(), autoincrement=False, nullable=True), + ) + # ### end Alembic commands ### diff --git a/app/alembic/versions/a4b5fabfdeac_add_otp_table.py b/app/alembic/versions/a4b5fabfdeac_add_otp_table.py index 3eb8d0c..dc5e853 100644 --- a/app/alembic/versions/a4b5fabfdeac_add_otp_table.py +++ b/app/alembic/versions/a4b5fabfdeac_add_otp_table.py @@ -5,14 +5,15 @@ Create Date: 2025-07-24 18:30:31.802426 """ + from typing import Sequence, Union import sqlalchemy as sa from alembic import op # revision identifiers, used by Alembic. -revision: str = 'a4b5fabfdeac' -down_revision: Union[str, Sequence[str], None] = 'ccf5dc612027' +revision: str = "a4b5fabfdeac" +down_revision: Union[str, Sequence[str], None] = "ccf5dc612027" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None @@ -20,28 +21,29 @@ def upgrade() -> None: """Upgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### - op.create_table('otps', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('email', sa.String(), nullable=False), - sa.Column('otp_code', sa.String(), nullable=False), - sa.Column('name', sa.String(), nullable=False), - sa.Column('password', sa.String(), nullable=True), - sa.Column('is_active', sa.Integer(), nullable=True), - sa.Column('provider_id', sa.String(), nullable=True), - sa.Column('auth_type', sa.String(), nullable=False), - sa.Column('created_at', sa.DateTime(), nullable=True), - sa.Column('expires_at', sa.DateTime(), nullable=False), - sa.PrimaryKeyConstraint('id') + op.create_table( + "otps", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("email", sa.String(), nullable=False), + sa.Column("otp_code", sa.String(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.Column("password", sa.String(), nullable=True), + sa.Column("is_active", sa.Integer(), nullable=True), + sa.Column("provider_id", sa.String(), nullable=True), + sa.Column("auth_type", sa.String(), nullable=False), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("expires_at", sa.DateTime(), nullable=False), + sa.PrimaryKeyConstraint("id"), ) - op.create_index(op.f('ix_otps_email'), 'otps', ['email'], unique=False) - op.create_index(op.f('ix_otps_id'), 'otps', ['id'], unique=False) + op.create_index(op.f("ix_otps_email"), "otps", ["email"], unique=False) + op.create_index(op.f("ix_otps_id"), "otps", ["id"], unique=False) # ### end Alembic commands ### def downgrade() -> None: """Downgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f('ix_otps_id'), table_name='otps') - op.drop_index(op.f('ix_otps_email'), table_name='otps') - op.drop_table('otps') + op.drop_index(op.f("ix_otps_id"), table_name="otps") + op.drop_index(op.f("ix_otps_email"), table_name="otps") + op.drop_table("otps") # ### end Alembic commands ### diff --git a/app/alembic/versions/a6d1c6c13842_add_role_field.py b/app/alembic/versions/a6d1c6c13842_add_role_field.py new file mode 100644 index 0000000..47d592e --- /dev/null +++ b/app/alembic/versions/a6d1c6c13842_add_role_field.py @@ -0,0 +1,38 @@ +"""add role field + +Revision ID: a6d1c6c13842 +Revises: a4b5fabfdeac +Create Date: 2025-08-31 09:15:05.419403 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "a6d1c6c13842" +down_revision: Union[str, Sequence[str], None] = "a4b5fabfdeac" +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! ### + userrole_enum = sa.Enum("ADMIN", "USER", name="userrole") + userrole_enum.create(op.get_bind()) + + # Then add the column using the enum type + op.add_column("users", sa.Column("role", userrole_enum, nullable=False)) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("users", "role") + # Then drop the enum type + sa.Enum(name="userrole").drop(op.get_bind()) + # ### end Alembic commands ### diff --git a/app/api/routes/questions.py b/app/api/routes/questions.py index 4286e12..783ea9f 100644 --- a/app/api/routes/questions.py +++ b/app/api/routes/questions.py @@ -1,11 +1,12 @@ from fastapi import APIRouter +import json -from app.schemas.utils import UserPrompt +from app.schemas.utils import UserPrompt, TechDescription from app.services.questions import add_daily_question_to_mongodb -from app.services.questions import \ - add_daily_question_to_store as add_question_in_chroma_db -from app.services.questions import \ - generate_daily_question as generate_daily_question_service +from app.services.questions import ( + generate_daily_question as generate_daily_question_service, + add_tech_description_to_store) + from app.services.questions import get_most_recent_daily_question router = APIRouter() @@ -18,9 +19,24 @@ def generate_daily_question(request: UserPrompt): or user's input. """ response = generate_daily_question_service(user_prompt=request.user_prompt) - add_daily_question_to_mongodb(response) # Store question in MongoDB + try: + question = json.loads(response) # Parse the JSON string to a dictionary + except json.JSONDecodeError: + return {"error": "Failed to parse the generated question."} + add_daily_question_to_mongodb(question) # Store question in MongoDB return response + +@router.post("/add_tech_description") +def add_tech_description(request: TechDescription): + """ + Add a technical description to the vector store. + Whenever admin add a new tech description/Tag, it will be stored in ChromaDB. + """ + add_tech_description_to_store(description=request.description, metadata=request.metadata) + return {"message": "Tech description added successfully."} + + @router.get("/get_daily_question") def get_daily_question(): """ diff --git a/app/main.py b/app/main.py index 9b81e04..8d83dcd 100644 --- a/app/main.py +++ b/app/main.py @@ -10,15 +10,6 @@ version="0.1.0" ) -# CORS middleware configuration -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], # In production, replace with specific origins - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - app.include_router(api_router, prefix=settings.API_V1_STR, tags=["v1"]) # Health check endpoint diff --git a/app/prompts/daily_question_prompt.py b/app/prompts/daily_question_prompt.py index 6fe699d..512c69e 100644 --- a/app/prompts/daily_question_prompt.py +++ b/app/prompts/daily_question_prompt.py @@ -1,27 +1,48 @@ system_template = """ -You are an AI assistant specialized in generating **realistic scenario-based problems for full-stack software engineer** -that help software engineers improve their coding, thinking, problem-solving and technical skills. - -Guidelines: -- The question should be realistic and relevant to **working proffessional and students** in software engineering. -- Focus on overall areas of software engineer such as Optimization, System desing, DSA, Frontend optimization, Database, scaling, AI and so on. -- The question must be concise but detailed enough to challenge critical thinking. -- Avoid trivial "quiz-style" questions. Instead, aim for **real-world problem-solving**. -- Yes, you can sometime ask quick quiz-style questions to test the knowledge of the user. -- If the user provides context, use it to tailor the question. - -If provided with context (retrieved from a vector database), -incorporate that knowledge into the question. +You are an expert software engineering mentor specializing in creating practical, industry-relevant challenges. Your mission is to generate real-world scenarios that help students and working professionals develop their technical skills through hands-on problem-solving. + +## Core Guidelines: + +### Focus & Scope +- Generate questions focused on a SINGLE software engineering concept or technology +- Create practical, scenario-based problems that mirror real industry challenges +- Ensure questions are challenging yet solvable (ranging from easy to hard level), promoting deep thinking and analytical skills + +### Quality Standards +- Questions should be directly applicable to current industry practices +- Scenarios must be realistic and based on common professional situations +- Content should encourage best practices and modern development approaches +- Include edge cases and real-world constraints when appropriate + +### Adaptability +- Draw from the full spectrum of software engineering disciplines +- Balance foundational concepts with emerging technologies +- Consider both technical and soft skills relevant to software development +- Include cross-cutting concerns like security, performance, and maintainability +- Adapt topic complexity and focus based on provided context or current industry needs """ human_template = """ -User Context (from DB or input): {context} +## Input Context +User Context: {context} + +## Task +Generate a comprehensive, scenario-based software engineering question that challenges problem-solving skills and reflects real-world professional situations. + +**Context Handling:** +- If context is provided: Tailor the question to the specified topic/area +- If no context: Select a random but relevant topic within software engineering + +## Required Output Format -Task: -Generate a daily realistic software engineering scenario question -based on the above context. +Provide a valid JSON object with the following structure: -Output Format: -- Title: A short title for the question -- Question: The full scenario-based question +{{ + "title": "string", // Concise, descriptive title (max 100 characters) + "description": "string", // Complete problem scenario with clear requirements and constraints + "hints": ["string"], // 2-4 progressive hints that guide without giving away the solution + "difficulty": "easy|medium|hard", // Based on required knowledge depth and complexity + "tags": ["string"], // 3-6 relevant technical tags for categorization + "learning_objectives": ["string"] // 2-3 key skills/concepts the question teaches +}} """ diff --git a/app/schemas/utils.py b/app/schemas/utils.py index 8e96644..5c761f9 100644 --- a/app/schemas/utils.py +++ b/app/schemas/utils.py @@ -4,3 +4,15 @@ class UserPrompt(BaseModel): """Schema for user prompt input.""" user_prompt: str | None = None + +class TechDescriptionMetadata(BaseModel): + """Schema for tech description metadata.""" + topic: str + tech_stack: str + difficulty: str + tags: str + +class TechDescription(BaseModel): + """Schema for tech description input.""" + description: str + metadata: TechDescriptionMetadata \ No newline at end of file diff --git a/app/services/questions.py b/app/services/questions.py index 128ccc8..c35bf36 100644 --- a/app/services/questions.py +++ b/app/services/questions.py @@ -1,3 +1,4 @@ +import uuid from datetime import datetime from langchain_core.documents import Document @@ -6,39 +7,35 @@ from app.config.vectorestore import chroma_db from app.llm.provider import invoke_with_retries from app.pipelines.daily_question_pipeline import daily_question_chain +from app.schemas.utils import TechDescriptionMetadata - -def generate_daily_question(user_prompt: str = None) -> str: +def generate_daily_question(user_prompt: str = None) -> any: """Generate a daily question using the LLM pipeline.""" payload = {"user_prompt": user_prompt} response = invoke_with_retries(daily_question_chain, payload) # ChatHuggingFace returns an AIMessage by default; str() yields content. - return str(response.content) if hasattr(response, "content") else str(response) - -def add_daily_question_to_store(question: str) -> None: - """Add a selected question to the ChromaDB collection.""" - doc = Document(page_content=question, metadata={}) - chroma_db.add_documents(collection="daily_questions",documents=[doc]) + return response.content -def add_daily_question_to_mongodb(question: str) -> None: +def add_daily_question_to_mongodb(question: dict) -> None: """Add a daily question to the MongoDB collection.""" question_document = { "_id": f"q_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}", - "title": question.split("?")[0] + "?", # Extract title from question - "description": question, + "title": question.get("title", ""), # Extract title from question + "description": question.get("description", ""), # Extract description from question "created_at": datetime.utcnow().isoformat(), "likes": 0, "dislikes": 0, - "hints": [], - "tags": ["daily_question"], - "difficulty": "medium", + "hints": question.get("hints", []), + "tags": question.get("tags", []), + "difficulty": question.get("difficulty", "None"), "status": "published" } questions_collection.insert_one(question_document) -def add_tech_description_to_store(description: str, metadata: dict) -> None: +def add_tech_description_to_store(description: str, metadata: TechDescriptionMetadata) -> None: """Add a tech description to the ChromaDB collection.""" - doc = Document(page_content=description, metadata=metadata) + doc_id = str(uuid.uuid4()) + doc = Document(id=doc_id, page_content=description, metadata=metadata.model_dump()) chroma_db.add_documents(collection="tech_description",documents=[doc]) def get_most_recent_daily_question(): @@ -54,6 +51,7 @@ def get_most_recent_daily_question(): "dislikes": recent_question["dislikes"], "tags": recent_question["tags"], "difficulty": recent_question["difficulty"], - "status": recent_question["status"] + "status": recent_question["status"], + "hints": recent_question.get("hints", []), } return None From 5abfa02969e90bdd28cc24ae37138c52310ecc5f Mon Sep 17 00:00:00 2001 From: KevinRawal Date: Mon, 1 Sep 2025 07:55:46 +0530 Subject: [PATCH 2/3] lint fix --- app/alembic/env.py | 4 +-- app/api/main.py | 4 ++- app/api/routes/questions.py | 21 ++++++++++------ app/config/config_mongo_fb.py | 1 + app/config/vectorestore.py | 19 ++++++++------ app/llm/provider.py | 9 ++++--- app/main.py | 5 ++-- app/models/__init__.py | 2 +- app/pipelines/daily_question_pipeline.py | 32 ++++++++++++++---------- app/schemas/utils.py | 7 +++++- app/services/questions.py | 16 +++++++++--- 11 files changed, 76 insertions(+), 44 deletions(-) diff --git a/app/alembic/env.py b/app/alembic/env.py index 7ec92d3..1cde81d 100644 --- a/app/alembic/env.py +++ b/app/alembic/env.py @@ -68,9 +68,7 @@ def run_migrations_online() -> None: ) with connectable.connect() as connection: - context.configure( - connection=connection, target_metadata=target_metadata - ) + context.configure(connection=connection, target_metadata=target_metadata) with context.begin_transaction(): context.run_migrations() diff --git a/app/api/main.py b/app/api/main.py index 04ed683..30e0062 100644 --- a/app/api/main.py +++ b/app/api/main.py @@ -5,4 +5,6 @@ api_router = APIRouter() # TODO: Add routes as needed -api_router.include_router(questions.router, prefix="/questions", tags=["question_generation"]) \ No newline at end of file +api_router.include_router( + questions.router, prefix="/questions", tags=["question_generation"] +) diff --git a/app/api/routes/questions.py b/app/api/routes/questions.py index 783ea9f..cbba7d1 100644 --- a/app/api/routes/questions.py +++ b/app/api/routes/questions.py @@ -1,12 +1,15 @@ -from fastapi import APIRouter import json -from app.schemas.utils import UserPrompt, TechDescription -from app.services.questions import add_daily_question_to_mongodb -from app.services.questions import ( - generate_daily_question as generate_daily_question_service, - add_tech_description_to_store) +from fastapi import APIRouter +from app.schemas.utils import TechDescription, UserPrompt +from app.services.questions import ( + add_daily_question_to_mongodb, + add_tech_description_to_store, +) +from app.services.questions import ( + generate_daily_question as generate_daily_question_service, +) from app.services.questions import get_most_recent_daily_question router = APIRouter() @@ -33,7 +36,9 @@ def add_tech_description(request: TechDescription): Add a technical description to the vector store. Whenever admin add a new tech description/Tag, it will be stored in ChromaDB. """ - add_tech_description_to_store(description=request.description, metadata=request.metadata) + add_tech_description_to_store( + description=request.description, metadata=request.metadata + ) return {"message": "Tech description added successfully."} @@ -45,4 +50,4 @@ def get_daily_question(): recent_question = get_most_recent_daily_question() if recent_question: return recent_question - return {"message": "No daily question found."} \ No newline at end of file + return {"message": "No daily question found."} diff --git a/app/config/config_mongo_fb.py b/app/config/config_mongo_fb.py index ff0f2fb..310b081 100644 --- a/app/config/config_mongo_fb.py +++ b/app/config/config_mongo_fb.py @@ -13,6 +13,7 @@ def __init__(self, uri: str, db_name: str): def get_collection(self, collection_name: str): return self.db[collection_name] + # MongoDB connection setup MONGO_URI = "mongodb://mongodb:27017" MONGO_DB_NAME = "questions_db" diff --git a/app/config/vectorestore.py b/app/config/vectorestore.py index 624cbd1..66e3f7f 100644 --- a/app/config/vectorestore.py +++ b/app/config/vectorestore.py @@ -11,13 +11,14 @@ # Using singleton pattern for ChromaDB to ensure a single instance is used across the application class ChromaDB: """Singleton class for managing ChromaDB collections and operations.""" - _instance = None + + _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance - + def __init__(self): self.path = settings.CHROMA_PATH self.model = settings.EMBEDDING_MODEL @@ -26,7 +27,7 @@ def __init__(self): self._client = PersistentClient(path=self.path) self._collections: Dict[str, Chroma] = {} - + def get_collection(self, name: str) -> Chroma: """Get or create a Chroma-backed collection.""" if name not in self._collections: @@ -36,21 +37,23 @@ def get_collection(self, name: str) -> Chroma: embedding_function=self._embeddings, ) return self._collections[name] - + def add_documents(self, collection: str, documents: List[Document]) -> None: """ Add documents to the ChromaDB collection. """ store = self.get_collection(collection) store.add_documents(documents) - - def as_retriever(self,collection: str, search_kwargs: Optional[Dict[str, Any]] = None) -> Any: + + def as_retriever( + self, collection: str, search_kwargs: Optional[Dict[str, Any]] = None + ) -> Any: """ Get a retriever for the ChromaDB collection. search_kwargs: can be used to specify additional search parameters like 'k' for KNN. """ store = self.get_collection(collection) return store.as_retriever(search_kwargs=search_kwargs) - -chroma_db = ChromaDB() \ No newline at end of file + +chroma_db = ChromaDB() diff --git a/app/llm/provider.py b/app/llm/provider.py index 93b09ec..8e222d4 100644 --- a/app/llm/provider.py +++ b/app/llm/provider.py @@ -17,7 +17,8 @@ def build_chat_model() -> ChatHuggingFace: ) return ChatHuggingFace(llm=llm) -#TODO: Improve this function + +# TODO: Improve this function def invoke_with_retries(chain, payload: Dict[str, Any]): last_err = None for attempt in range(1, settings.LLM_RETRIES + 1): @@ -26,5 +27,7 @@ def invoke_with_retries(chain, payload: Dict[str, Any]): except Exception as e: # noqa: BLE001 last_err = e # Exponential backoff with cap - time.sleep(min(2 ** attempt, 8)) - raise RuntimeError(f"LLM invocation failed after {settings.LLM_RETRIES} attempts") from last_err + time.sleep(min(2**attempt, 8)) + raise RuntimeError( + f"LLM invocation failed after {settings.LLM_RETRIES} attempts" + ) from last_err diff --git a/app/main.py b/app/main.py index 8d83dcd..f6c2b4b 100644 --- a/app/main.py +++ b/app/main.py @@ -7,12 +7,13 @@ app = FastAPI( title="Generator Service", description="API for generating and managing coding problems", - version="0.1.0" + version="0.1.0", ) app.include_router(api_router, prefix=settings.API_V1_STR, tags=["v1"]) + # Health check endpoint @app.get("/health") async def health_check(): - return {"status": "healthy", "service": "JamAndFlow-generator"} \ No newline at end of file + return {"status": "healthy", "service": "JamAndFlow-generator"} diff --git a/app/models/__init__.py b/app/models/__init__.py index 38f5c5a..0a91b6c 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -1,3 +1,3 @@ # Add models here to be imported by Alembic # example: -# from .models.user import User # noqa: F401, F403 \ No newline at end of file +# from .models.user import User # noqa: F401, F403 diff --git a/app/pipelines/daily_question_pipeline.py b/app/pipelines/daily_question_pipeline.py index fd5e98e..f328371 100644 --- a/app/pipelines/daily_question_pipeline.py +++ b/app/pipelines/daily_question_pipeline.py @@ -1,8 +1,10 @@ from typing import Dict -from langchain_core.prompts import (ChatPromptTemplate, - HumanMessagePromptTemplate, - SystemMessagePromptTemplate) +from langchain_core.prompts import ( + ChatPromptTemplate, + HumanMessagePromptTemplate, + SystemMessagePromptTemplate, +) from langchain_core.runnables import RunnableParallel from app.config.vectorestore import chroma_db @@ -12,17 +14,21 @@ # using tech_description collection to retrieve context. # It will return top k relevant document based on the user prompt. -# The context structure stores tech-stack and relevant real-world concepts +# The context structure stores tech-stack and relevant real-world concepts # On which the question can be framed. -retriever = chroma_db.as_retriever(collection="tech_description", - search_kwargs={"k": settings.RETRIEVE_K}) +retriever = chroma_db.as_retriever( + collection="tech_description", search_kwargs={"k": settings.RETRIEVE_K} +) -prompt = ChatPromptTemplate.from_messages([ - SystemMessagePromptTemplate.from_template(system_template), - HumanMessagePromptTemplate.from_template(human_template), -]) +prompt = ChatPromptTemplate.from_messages( + [ + SystemMessagePromptTemplate.from_template(system_template), + HumanMessagePromptTemplate.from_template(human_template), + ] +) -#TODO: we might required a context for last n question for uniquness and variety + +# TODO: we might required a context for last n question for uniquness and variety # But for now the concern is on the limit of the tokens hence not implementing it def _retrieve_context(inputs: Dict): """It will only trigger when user gives a contet if not it will return 'No extra context'""" @@ -33,9 +39,9 @@ def _retrieve_context(inputs: Dict): context = " ".join([d.page_content for d in docs]) if docs else "No extra context" return {"context": context, "user_prompt": inputs["user_prompt"]} + context_retriever = RunnableParallel( - user_question=lambda x: x["user_prompt"], - context=_retrieve_context + user_question=lambda x: x["user_prompt"], context=_retrieve_context ) chat_model = build_chat_model() diff --git a/app/schemas/utils.py b/app/schemas/utils.py index 5c761f9..5059453 100644 --- a/app/schemas/utils.py +++ b/app/schemas/utils.py @@ -3,16 +3,21 @@ class UserPrompt(BaseModel): """Schema for user prompt input.""" + user_prompt: str | None = None + class TechDescriptionMetadata(BaseModel): """Schema for tech description metadata.""" + topic: str tech_stack: str difficulty: str tags: str + class TechDescription(BaseModel): """Schema for tech description input.""" + description: str - metadata: TechDescriptionMetadata \ No newline at end of file + metadata: TechDescriptionMetadata diff --git a/app/services/questions.py b/app/services/questions.py index c35bf36..9344d25 100644 --- a/app/services/questions.py +++ b/app/services/questions.py @@ -9,6 +9,7 @@ from app.pipelines.daily_question_pipeline import daily_question_chain from app.schemas.utils import TechDescriptionMetadata + def generate_daily_question(user_prompt: str = None) -> any: """Generate a daily question using the LLM pipeline.""" payload = {"user_prompt": user_prompt} @@ -16,27 +17,34 @@ def generate_daily_question(user_prompt: str = None) -> any: # ChatHuggingFace returns an AIMessage by default; str() yields content. return response.content + def add_daily_question_to_mongodb(question: dict) -> None: """Add a daily question to the MongoDB collection.""" question_document = { "_id": f"q_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}", "title": question.get("title", ""), # Extract title from question - "description": question.get("description", ""), # Extract description from question + "description": question.get( + "description", "" + ), # Extract description from question "created_at": datetime.utcnow().isoformat(), "likes": 0, "dislikes": 0, "hints": question.get("hints", []), "tags": question.get("tags", []), "difficulty": question.get("difficulty", "None"), - "status": "published" + "status": "published", } questions_collection.insert_one(question_document) -def add_tech_description_to_store(description: str, metadata: TechDescriptionMetadata) -> None: + +def add_tech_description_to_store( + description: str, metadata: TechDescriptionMetadata +) -> None: """Add a tech description to the ChromaDB collection.""" doc_id = str(uuid.uuid4()) doc = Document(id=doc_id, page_content=description, metadata=metadata.model_dump()) - chroma_db.add_documents(collection="tech_description",documents=[doc]) + chroma_db.add_documents(collection="tech_description", documents=[doc]) + def get_most_recent_daily_question(): """Fetch the most recent daily question from MongoDB.""" From 1995087f5a5ea2e4c021e736c04b38ef79756d4f Mon Sep 17 00:00:00 2001 From: KevinRawal Date: Mon, 1 Sep 2025 07:56:49 +0530 Subject: [PATCH 3/3] lint fix --- app/api/routes/questions.py | 11 ++++------- app/pipelines/daily_question_pipeline.py | 8 +++----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/app/api/routes/questions.py b/app/api/routes/questions.py index cbba7d1..e91e5b6 100644 --- a/app/api/routes/questions.py +++ b/app/api/routes/questions.py @@ -3,13 +3,10 @@ from fastapi import APIRouter from app.schemas.utils import TechDescription, UserPrompt -from app.services.questions import ( - add_daily_question_to_mongodb, - add_tech_description_to_store, -) -from app.services.questions import ( - generate_daily_question as generate_daily_question_service, -) +from app.services.questions import (add_daily_question_to_mongodb, + add_tech_description_to_store) +from app.services.questions import \ + generate_daily_question as generate_daily_question_service from app.services.questions import get_most_recent_daily_question router = APIRouter() diff --git a/app/pipelines/daily_question_pipeline.py b/app/pipelines/daily_question_pipeline.py index f328371..8ec1628 100644 --- a/app/pipelines/daily_question_pipeline.py +++ b/app/pipelines/daily_question_pipeline.py @@ -1,10 +1,8 @@ from typing import Dict -from langchain_core.prompts import ( - ChatPromptTemplate, - HumanMessagePromptTemplate, - SystemMessagePromptTemplate, -) +from langchain_core.prompts import (ChatPromptTemplate, + HumanMessagePromptTemplate, + SystemMessagePromptTemplate) from langchain_core.runnables import RunnableParallel from app.config.vectorestore import chroma_db