From 0aba944aa1b98a17547d46d0017528e3db9f9959 Mon Sep 17 00:00:00 2001 From: Joeloo1 Date: Wed, 22 Jul 2026 23:10:45 +0100 Subject: [PATCH 1/2] fix: use AsyncClientV2 to avoid blocking the event loop - Switch from sync cohere.ClientV2 to async cohere.AsyncClientV2 - Add await to chat() and embed() calls Closes #1 --- src/services/cohere_client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/cohere_client.py b/src/services/cohere_client.py index ea0bae5..ab6304a 100644 --- a/src/services/cohere_client.py +++ b/src/services/cohere_client.py @@ -20,7 +20,7 @@ class CohereClient: def __init__(self, api_key: str | None = None) -> None: self._api_key = api_key or settings.cohere_api_key - self._client = cohere.ClientV2(api_key=self._api_key) + self._client = cohere.AsyncClientV2(api_key=self._api_key) self._embed_model = settings.cohere_embed_model self._generate_model = settings.cohere_generate_model @@ -54,7 +54,7 @@ async def generate( messages.append({"role": "user", "content": prompt}) try: - response = self._client.chat( + response = await self._client.chat( model=self._generate_model, messages=messages, max_tokens=max_tokens, @@ -125,7 +125,7 @@ async def embed(self, texts: list[str], input_type: str = "search_document") -> return [] try: - response = self._client.embed( + response = await self._client.embed( model=self._embed_model, texts=texts, input_type=input_type, From 5ebe8b2bf0799117f7e55fab3bd602cf9d1d669b Mon Sep 17 00:00:00 2001 From: Joeloo1 Date: Fri, 24 Jul 2026 18:13:39 +0100 Subject: [PATCH 2/2] fix: resolve pre-existing lint errors to pass CI Remove unused imports (os, math, JSONResponse), fix line-too-long violations, and sort import blocks per ruff rules. This satisfies the acceptance criteria in issue #1 requiring ruff check to pass. --- src/knowledge/ingestion.py | 6 ++++-- src/knowledge/vectorstore.py | 1 - src/routes/courses.py | 1 - src/services/cohere_client.py | 6 +++++- src/services/embedding.py | 3 ++- src/services/feedback_engine.py | 5 ++++- src/services/generation.py | 2 +- src/services/quiz_generator.py | 4 ++-- 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/knowledge/ingestion.py b/src/knowledge/ingestion.py index 1ef6894..76a4872 100644 --- a/src/knowledge/ingestion.py +++ b/src/knowledge/ingestion.py @@ -5,7 +5,6 @@ """ import logging -import os from pathlib import Path from src.config import settings @@ -91,7 +90,10 @@ def chunk_text( chunks.append("\n\n".join(current_chunk)) # Keep overlap overlap_text = "\n\n".join(current_chunk) - overlap_chars = overlap_text[-chunk_overlap:] if len(overlap_text) > chunk_overlap else overlap_text + if len(overlap_text) > chunk_overlap: + overlap_chars = overlap_text[-chunk_overlap:] + else: + overlap_chars = overlap_text current_chunk = [overlap_chars] if overlap_chars.strip() else [] current_length = len(overlap_chars) diff --git a/src/knowledge/vectorstore.py b/src/knowledge/vectorstore.py index 69b5cfd..28a2fcd 100644 --- a/src/knowledge/vectorstore.py +++ b/src/knowledge/vectorstore.py @@ -6,7 +6,6 @@ import json import logging -import math from pathlib import Path import numpy as np diff --git a/src/routes/courses.py b/src/routes/courses.py index 2f4680c..a519afd 100644 --- a/src/routes/courses.py +++ b/src/routes/courses.py @@ -3,7 +3,6 @@ import logging from fastapi import APIRouter, HTTPException -from fastapi.responses import JSONResponse from src.models.course import Course, GenerateCourseRequest from src.services.generation import get_generation_service diff --git a/src/services/cohere_client.py b/src/services/cohere_client.py index ab6304a..5ea8d7a 100644 --- a/src/services/cohere_client.py +++ b/src/services/cohere_client.py @@ -108,7 +108,11 @@ async def generate_json( logger.debug("Raw response: %s", raw[:500]) raise ValueError(f"Invalid JSON response from Cohere: {exc}") from exc - async def embed(self, texts: list[str], input_type: str = "search_document") -> list[list[float]]: + async def embed( + self, + texts: list[str], + input_type: str = "search_document", + ) -> list[list[float]]: """Generate embeddings for a list of texts. Args: diff --git a/src/services/embedding.py b/src/services/embedding.py index 7181b4a..797ee05 100644 --- a/src/services/embedding.py +++ b/src/services/embedding.py @@ -34,7 +34,8 @@ async def embed_documents(self, texts: list[str]) -> list[list[float]]: ) all_embeddings.extend(embeddings) - logger.info("Embedded %d documents in %d batches", len(texts), (len(texts) - 1) // batch_size + 1) + num_batches = (len(texts) - 1) // batch_size + 1 + logger.info("Embedded %d documents in %d batches", len(texts), num_batches) return all_embeddings async def embed_query(self, query: str) -> list[float]: diff --git a/src/services/feedback_engine.py b/src/services/feedback_engine.py index 5a115b9..6b1c7e1 100644 --- a/src/services/feedback_engine.py +++ b/src/services/feedback_engine.py @@ -114,7 +114,10 @@ async def quick_feedback( return f"Correct! {question.explanation}" correct_option = question.options[question.correct_index] - user_option = question.options[user_answer] if 0 <= user_answer < len(question.options) else "N/A" + if 0 <= user_answer < len(question.options): + user_option = question.options[user_answer] + else: + user_option = "N/A" return ( f"Incorrect. You chose '{user_option}', but the correct answer is '{correct_option}'. " f"{question.explanation}" diff --git a/src/services/generation.py b/src/services/generation.py index fb3a042..6067c9f 100644 --- a/src/services/generation.py +++ b/src/services/generation.py @@ -2,13 +2,13 @@ import logging +from src.knowledge.vectorstore import get_vectorstore from src.models.course import Course, CourseModule, GenerateCourseRequest from src.models.user_profile import UserProfile from src.prompts.course_generation import build_course_prompt, build_module_prompt from src.prompts.system import COURSE_SYSTEM_PROMPT from src.services.cohere_client import get_cohere_client from src.services.embedding import get_embedding_service -from src.knowledge.vectorstore import get_vectorstore logger = logging.getLogger(__name__) diff --git a/src/services/quiz_generator.py b/src/services/quiz_generator.py index 2309c6c..c1c71a2 100644 --- a/src/services/quiz_generator.py +++ b/src/services/quiz_generator.py @@ -3,12 +3,12 @@ import logging import uuid +from src.knowledge.vectorstore import get_vectorstore from src.models.quiz import GenerateQuizRequest, Quiz, QuizQuestion -from src.prompts.quiz_generation import build_quiz_prompt, build_question_from_content_prompt +from src.prompts.quiz_generation import build_question_from_content_prompt, build_quiz_prompt from src.prompts.system import QUIZ_SYSTEM_PROMPT from src.services.cohere_client import get_cohere_client from src.services.embedding import get_embedding_service -from src.knowledge.vectorstore import get_vectorstore logger = logging.getLogger(__name__)