diff --git a/src/app/api/api_v1/api.py b/src/app/api/api_v1/api.py index 10397e9..a769be6 100644 --- a/src/app/api/api_v1/api.py +++ b/src/app/api/api_v1/api.py @@ -2,13 +2,14 @@ from fastapi import APIRouter -from src.app.api.api_v1.endpoints import chat, metric, micro_learning, search, user -from src.app.tutor.api import router +from src.app.api.api_v1.endpoints import chat, metric, micro_learning, user +from src.app.search.api import router as search_router +from src.app.tutor.api import router as tutor_router api_router = APIRouter() api_router.include_router(chat.router, prefix="/qna", tags=["qna"]) -api_router.include_router(search.router, prefix="/search", tags=["search"]) -api_router.include_router(router.router, prefix="/tutor", tags=["tutor"]) +api_router.include_router(search_router.router, prefix="/search", tags=["search"]) +api_router.include_router(tutor_router.router, prefix="/tutor", tags=["tutor"]) api_router.include_router(metric.router, prefix="/metric", tags=["metric"]) api_router.include_router( micro_learning.router, prefix="/micro_learning", tags=["micro_learning"] diff --git a/src/app/api/api_v1/endpoints/chat.py b/src/app/api/api_v1/endpoints/chat.py index eb85d42..f708167 100644 --- a/src/app/api/api_v1/endpoints/chat.py +++ b/src/app/api/api_v1/endpoints/chat.py @@ -12,15 +12,15 @@ from pydantic import BaseModel from src.app.models import chat as models -from src.app.services.constants import subjects as subjectsDict +from src.app.search.services.search import SearchService, get_search_service from src.app.services.data_collection import get_data_collection_service -from src.app.services.exceptions import ( +from src.app.shared.domain.constants import subjects as subjectsDict +from src.app.shared.domain.exceptions import ( EmptyQueryError, InvalidQuestionError, LanguageNotSupportedError, bad_request, ) -from src.app.services.search import SearchService, get_search_service from src.app.shared.infra.abst_chat import get_chat_service from src.app.shared.utils.dependencies import get_settings from src.app.utils.logger import logger as utils_logger diff --git a/src/app/api/api_v1/endpoints/micro_learning.py b/src/app/api/api_v1/endpoints/micro_learning.py index ea75559..bc33bb6 100644 --- a/src/app/api/api_v1/endpoints/micro_learning.py +++ b/src/app/api/api_v1/endpoints/micro_learning.py @@ -3,13 +3,13 @@ from welearn_database.data.models import ContextDocument from src.app.models.documents import JourneySectionType -from src.app.models.search import FilterDefinition, SearchFilters +from src.app.search.models.search import FilterDefinition, SearchFilters +from src.app.search.services.search import SearchService, get_search_service from src.app.services.helpers import ( choose_readability_according_journey_section_type, collection_and_model_id_according_lang, convert_embedding_bytes, ) -from src.app.services.search import SearchService, get_search_service from src.app.services.sql_db.queries import ( get_context_documents, get_subject, diff --git a/src/app/models/chat.py b/src/app/models/chat.py index cb8a50e..42359c5 100644 --- a/src/app/models/chat.py +++ b/src/app/models/chat.py @@ -5,7 +5,7 @@ from pydantic import BaseModel, Field from qdrant_client.models import ScoredPoint -from src.app.models.search import SDGFilter +from src.app.search.models.search import SDGFilter from .documents import Document diff --git a/src/app/services/tutor/__init__.py b/src/app/search/__init__.py similarity index 100% rename from src/app/services/tutor/__init__.py rename to src/app/search/__init__.py diff --git a/src/app/search/api/__init__.py b/src/app/search/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app/api/api_v1/endpoints/search.py b/src/app/search/api/router.py similarity index 96% rename from src/app/api/api_v1/endpoints/search.py rename to src/app/search/api/router.py index c894665..663f4d8 100644 --- a/src/app/api/api_v1/endpoints/search.py +++ b/src/app/search/api/router.py @@ -11,27 +11,27 @@ from fastapi.concurrency import run_in_threadpool from src.app.models.documents import Document -from src.app.models.search import ( +from src.app.search.helpers.search_helpers import search_multi_inputs +from src.app.search.models.search import ( EnhancedSearchQuery, SDGFilter, SearchMethods, SearchOutput, SearchQuery, ) +from src.app.search.services.search import SearchService, get_search_service from src.app.services.data_collection import get_data_collection_service -from src.app.services.exceptions import ( - CollectionNotFoundError, - EmptyQueryError, - ModelNotFoundError, - bad_request, -) -from src.app.services.search import SearchService, get_search_service -from src.app.services.search_helpers import search_multi_inputs from src.app.services.sql_db.queries import ( get_collections_sync, get_documents_payload_by_ids_sync, get_nb_docs_sync, ) +from src.app.shared.domain.exceptions import ( + CollectionNotFoundError, + EmptyQueryError, + ModelNotFoundError, + bad_request, +) from src.app.utils.logger import logger as logger_utils router = APIRouter() diff --git a/src/app/search/domain/__init__.py b/src/app/search/domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app/search/helpers/__init__.py b/src/app/search/helpers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app/services/search_helpers.py b/src/app/search/helpers/search_helpers.py similarity index 89% rename from src/app/services/search_helpers.py rename to src/app/search/helpers/search_helpers.py index 03e35bb..80ce727 100644 --- a/src/app/services/search_helpers.py +++ b/src/app/search/helpers/search_helpers.py @@ -4,8 +4,8 @@ from fastapi import BackgroundTasks from qdrant_client.http.models import ScoredPoint -from src.app.models.search import EnhancedSearchQuery, SearchMethods -from src.app.services.exceptions import handle_error +from src.app.search.models.search import EnhancedSearchQuery, SearchMethods +from src.app.shared.domain.exceptions import handle_error from src.app.utils.logger import logger as logger_utils logger = logger_utils(__name__) diff --git a/src/app/search/models/__init__.py b/src/app/search/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app/models/search.py b/src/app/search/models/search.py similarity index 100% rename from src/app/models/search.py rename to src/app/search/models/search.py diff --git a/src/app/search/services/__init__.py b/src/app/search/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app/services/search.py b/src/app/search/services/search.py similarity index 99% rename from src/app/services/search.py rename to src/app/search/services/search.py index fdad4c1..7050519 100644 --- a/src/app/services/search.py +++ b/src/app/search/services/search.py @@ -16,19 +16,19 @@ from transformers import AutoModel, AutoTokenizer from src.app.models.collections import Collection -from src.app.models.search import ( +from src.app.search.models.search import ( EnhancedSearchQuery, FilterDefinition, SearchFilters, SearchMethods, ) from src.app.services.data_quality import DataQualityChecker -from src.app.services.exceptions import CollectionNotFoundError, ModelNotFoundError from src.app.services.helpers import convert_embedding_bytes from src.app.services.sql_db.queries import ( get_embeddings_model_id_according_name, get_subject, ) +from src.app.shared.domain.exceptions import CollectionNotFoundError, ModelNotFoundError from src.app.utils.decorators import log_time_and_error, log_time_and_error_sync from src.app.utils.logger import logger as logger_utils diff --git a/src/app/services/agent.py b/src/app/services/agent.py index 51d78da..6bc178c 100644 --- a/src/app/services/agent.py +++ b/src/app/services/agent.py @@ -6,9 +6,9 @@ from langchain_core.tools import tool from src.app.models.documents import Document -from src.app.models.search import EnhancedSearchQuery +from src.app.search.models.search import EnhancedSearchQuery +from src.app.search.services.search import SearchService from src.app.services.helpers import stringify_docs_content -from src.app.services.search import SearchService from src.app.utils.decorators import log_time_and_error from src.app.utils.logger import logger as utils_logger diff --git a/src/app/services/helpers.py b/src/app/services/helpers.py index 9fce3d5..f773265 100644 --- a/src/app/services/helpers.py +++ b/src/app/services/helpers.py @@ -11,8 +11,8 @@ from src.app.models.collections import Collection from src.app.models.documents import JourneySectionType -from src.app.services.exceptions import LanguageNotSupportedError from src.app.services.sql_db.queries import get_embeddings_model_id_according_name +from src.app.shared.domain.exceptions import LanguageNotSupportedError from src.app.utils.decorators import log_time_and_error_sync from src.app.utils.logger import logger as utils_logger diff --git a/src/app/services/sql_db/queries.py b/src/app/services/sql_db/queries.py index 370c44a..31a9ba8 100644 --- a/src/app/services/sql_db/queries.py +++ b/src/app/services/sql_db/queries.py @@ -30,9 +30,9 @@ from src.app.models.chat import Role from src.app.models.documents import Document, DocumentPayloadModel, JourneySection -from src.app.models.search import ContextType -from src.app.services.constants import APP_NAME +from src.app.search.models.search import ContextType from src.app.services.sql_db.sql_service import session_maker +from src.app.shared.domain.constants import APP_NAME model_id_lock = Lock() diff --git a/src/app/shared/domain/__init__.py b/src/app/shared/domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app/services/constants.py b/src/app/shared/domain/constants.py similarity index 100% rename from src/app/services/constants.py rename to src/app/shared/domain/constants.py diff --git a/src/app/services/exceptions.py b/src/app/shared/domain/exceptions.py similarity index 100% rename from src/app/services/exceptions.py rename to src/app/shared/domain/exceptions.py diff --git a/src/app/shared/infra/abst_chat.py b/src/app/shared/infra/abst_chat.py index 20eabbd..7314942 100644 --- a/src/app/shared/infra/abst_chat.py +++ b/src/app/shared/infra/abst_chat.py @@ -28,20 +28,20 @@ from src.app.models.chat import ReformulatedQueryResponse from src.app.models.documents import Document + +# from src.app.shared.infra.llm_proxy import LLMProxy +from src.app.search.services.search import SearchService from src.app.services import prompts from src.app.services.agent import ( get_resources_about_sustainability, trim_conversation_history, ) -from src.app.services.exceptions import LanguageNotSupportedError from src.app.services.helpers import ( detect_language_from_entry, extract_json_from_response, stringify_docs_content, ) - -# from src.app.shared.infra.llm_proxy import LLMProxy -from src.app.services.search import SearchService +from src.app.shared.domain.exceptions import LanguageNotSupportedError from src.app.shared.utils.dependencies import get_settings from src.app.utils.decorators import log_time_and_error from src.app.utils.logger import log_environmental_impacts diff --git a/src/app/services/security.py b/src/app/shared/infra/security.py similarity index 100% rename from src/app/services/security.py rename to src/app/shared/infra/security.py diff --git a/src/app/tests/api/api_v1/test_chat.py b/src/app/tests/api/api_v1/test_chat.py index f3321c4..d290e60 100644 --- a/src/app/tests/api/api_v1/test_chat.py +++ b/src/app/tests/api/api_v1/test_chat.py @@ -10,7 +10,7 @@ from src.app.models.chat import ReformulatedQueryResponse from src.app.models.documents import Document as DocumentModel from src.app.models.documents import DocumentPayloadModel -from src.app.services.exceptions import LanguageNotSupportedError +from src.app.shared.domain.exceptions import LanguageNotSupportedError from src.main import app client = TestClient(app) @@ -65,7 +65,7 @@ @mock.patch("src.app.services.sql_db.queries.session_maker") @mock.patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) @mock.patch( @@ -331,7 +331,7 @@ async def test_stream(self, *mocks): @mock.patch("psycopg.AsyncConnection.connect", new_callable=mock.AsyncMock) @mock.patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) @mock.patch("src.app.shared.infra.abst_chat.AbstractChat.agent_message") diff --git a/src/app/tests/api/api_v1/test_metric.py b/src/app/tests/api/api_v1/test_metric.py index 8b87694..5ccd4e8 100644 --- a/src/app/tests/api/api_v1/test_metric.py +++ b/src/app/tests/api/api_v1/test_metric.py @@ -23,7 +23,7 @@ @mock.patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) class TestMetricEndpoint(unittest.IsolatedAsyncioTestCase): diff --git a/src/app/tests/api/api_v1/test_micro_learning.py b/src/app/tests/api/api_v1/test_micro_learning.py index 025c729..da9bd30 100644 --- a/src/app/tests/api/api_v1/test_micro_learning.py +++ b/src/app/tests/api/api_v1/test_micro_learning.py @@ -16,7 +16,7 @@ async def __call__(self, *args, **kwargs): @mock.patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) class MicroLearningTests(unittest.IsolatedAsyncioTestCase): @@ -26,7 +26,7 @@ class MicroLearningTests(unittest.IsolatedAsyncioTestCase): ) @mock.patch("src.app.api.api_v1.endpoints.micro_learning.get_context_documents") @mock.patch("src.app.api.api_v1.endpoints.micro_learning.get_subject") - @mock.patch("src.app.services.search.SearchService.search") + @mock.patch("src.app.search.services.search.SearchService.search") @mock.patch("src.app.api.api_v1.endpoints.micro_learning.convert_embedding_bytes") async def test_get_full_journey( self, diff --git a/src/app/tests/api/api_v1/test_search.py b/src/app/tests/api/api_v1/test_search.py index e1eca0a..3ebc105 100644 --- a/src/app/tests/api/api_v1/test_search.py +++ b/src/app/tests/api/api_v1/test_search.py @@ -9,14 +9,14 @@ from src.app.core.config import settings from src.app.models import collections from src.app.models.documents import Document, DocumentPayloadModel -from src.app.models.search import EnhancedSearchQuery -from src.app.services.exceptions import CollectionNotFoundError, ModelNotFoundError -from src.app.services.search import SearchService, sort_slices_using_mmr +from src.app.search.models.search import EnhancedSearchQuery +from src.app.search.services.search import SearchService, sort_slices_using_mmr +from src.app.shared.domain.exceptions import CollectionNotFoundError, ModelNotFoundError from src.main import app client = TestClient(app) -search_pipeline_path = "src.app.services.search.SearchService" +search_pipeline_path = "src.app.search.services.search.SearchService" mocked_collection = collections.Collection( lang="mul", @@ -149,7 +149,7 @@ @patch("src.app.services.sql_db.sql_service.session_maker") @mock.patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) @patch( @@ -238,7 +238,7 @@ async def test_search_all_slices_no_collections(self, *mocks): @patch("src.app.services.sql_db.sql_service.session_maker") @patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) class SearchTestsSlices(IsolatedAsyncioTestCase): @@ -306,7 +306,7 @@ async def test_search_all_slices_no_result(self, *mocks): @patch("src.app.services.sql_db.queries.session_maker") @patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) class SearchTestsAll(IsolatedAsyncioTestCase): @@ -389,7 +389,7 @@ async def test_sort_slices_using_mmr_custom_theta(self, *mocks): @patch("src.app.services.sql_db.queries.session_maker") @patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) class SearchTestsMultiInput(IsolatedAsyncioTestCase): @@ -414,7 +414,7 @@ async def test_search_multi_no_result(self, *mocks): @patch("src.app.services.sql_db.queries.session_maker") @patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) class DocumentsByIdsTests(IsolatedAsyncioTestCase): @@ -540,7 +540,7 @@ async def test_documents_by_ids_corpus_missing(self, session_maker_mock, *mocks) async def test_search_multi_single_query(self, *mocks): with mock.patch( - "src.app.api.api_v1.endpoints.search.search_multi_inputs", + "src.app.search.api.router.search_multi_inputs", ) as search_multi, mock.patch.object( SearchService, "search_handler", return_value=mocked_documents ) as search_handler: diff --git a/src/app/tests/api/api_v1/test_user.py b/src/app/tests/api/api_v1/test_user.py index c291c03..70cc0ac 100644 --- a/src/app/tests/api/api_v1/test_user.py +++ b/src/app/tests/api/api_v1/test_user.py @@ -12,7 +12,7 @@ @mock.patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) class UserApiTests(unittest.IsolatedAsyncioTestCase): diff --git a/src/app/tests/conftest.py b/src/app/tests/conftest.py index c280fcc..166dcfc 100644 --- a/src/app/tests/conftest.py +++ b/src/app/tests/conftest.py @@ -3,7 +3,7 @@ import pytest from fastapi.testclient import TestClient -from src.app.services.search import get_qdrant +from src.app.search.services.search import get_qdrant from src.main import app diff --git a/src/app/tests/services/test_abst_chat.py b/src/app/tests/services/test_abst_chat.py index 71d2e17..45252f4 100644 --- a/src/app/tests/services/test_abst_chat.py +++ b/src/app/tests/services/test_abst_chat.py @@ -2,7 +2,7 @@ from unittest import mock from src.app.models.chat import ReformulatedQueryResponse -from src.app.services.exceptions import LanguageNotSupportedError +from src.app.shared.domain.exceptions import LanguageNotSupportedError from src.app.shared.infra.abst_chat import AbstractChat diff --git a/src/app/tests/services/test_data_quality.py b/src/app/tests/services/test_data_quality.py index e8d74cc..62c5acf 100644 --- a/src/app/tests/services/test_data_quality.py +++ b/src/app/tests/services/test_data_quality.py @@ -16,8 +16,8 @@ WeLearnDocument, ) -from src.app.services.constants import APP_NAME from src.app.services.data_quality import DataQualityChecker +from src.app.shared.domain.constants import APP_NAME def handle_schema_with_sqlite(db_engine: Engine): diff --git a/src/app/tests/services/test_exceptions.py b/src/app/tests/services/test_exceptions.py index 33b4877..520d28f 100644 --- a/src/app/tests/services/test_exceptions.py +++ b/src/app/tests/services/test_exceptions.py @@ -2,7 +2,7 @@ from fastapi import HTTPException -from src.app.services.exceptions import ( +from src.app.shared.domain.exceptions import ( CollectionNotFoundError, EmptyQueryError, InvalidQuestionError, diff --git a/src/app/tests/services/test_helpers.py b/src/app/tests/services/test_helpers.py index 21dfe74..967ae2d 100644 --- a/src/app/tests/services/test_helpers.py +++ b/src/app/tests/services/test_helpers.py @@ -4,13 +4,13 @@ from langdetect.language import Language from src.app.models.documents import Document, DocumentPayloadModel -from src.app.services.exceptions import LanguageNotSupportedError from src.app.services.helpers import ( convert_embedding_bytes, detect_language_from_entry, extract_json_from_response, stringify_docs_content, ) +from src.app.shared.domain.exceptions import LanguageNotSupportedError class HelpersTests(TestCase): diff --git a/src/app/tests/services/test_search.py b/src/app/tests/services/test_search.py index 13a2d05..0340ec5 100644 --- a/src/app/tests/services/test_search.py +++ b/src/app/tests/services/test_search.py @@ -5,7 +5,7 @@ from qdrant_client.models import CollectionDescription, CollectionsResponse, ScoredPoint from src.app.models.collections import Collection -from src.app.services.search import SearchService, concatenate_same_doc_id_slices +from src.app.search.services.search import SearchService, concatenate_same_doc_id_slices os.environ["USE_CACHED_SETTINGS"] = "False" diff --git a/src/app/tests/services/test_security.py b/src/app/tests/services/test_security.py index bb6c361..8c59fe8 100644 --- a/src/app/tests/services/test_security.py +++ b/src/app/tests/services/test_security.py @@ -4,12 +4,12 @@ from fastapi import HTTPException -from src.app.services.security import check_api_key_sync as check_api_key -from src.app.services.security import get_user +from src.app.shared.infra.security import check_api_key_sync as check_api_key +from src.app.shared.infra.security import get_user class SecurityTests(unittest.TestCase): - @mock.patch("src.app.services.security.session_maker") + @mock.patch("src.app.shared.infra.security.session_maker") def test_check_api_key_true_when_active(self, session_maker_mock): session = MagicMock() # Simulate found key with is_active True @@ -18,7 +18,7 @@ def test_check_api_key_true_when_active(self, session_maker_mock): assert check_api_key("secret-key") is True - @mock.patch("src.app.services.security.session_maker") + @mock.patch("src.app.shared.infra.security.session_maker") def test_check_api_key_false_when_not_found(self, session_maker_mock): session = MagicMock() session.execute.return_value.first.return_value = None @@ -26,7 +26,7 @@ def test_check_api_key_false_when_not_found(self, session_maker_mock): assert check_api_key("does-not-exist") is False - @mock.patch("src.app.services.security.session_maker") + @mock.patch("src.app.shared.infra.security.session_maker") def test_check_api_key_false_when_inactive(self, session_maker_mock): session = MagicMock() session.execute.return_value.first.return_value = MagicMock(is_active=False) @@ -38,7 +38,7 @@ def test_check_api_key_false_when_inactive(self, session_maker_mock): class GetUserTests(unittest.IsolatedAsyncioTestCase): @mock.patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=True), ) async def test_get_user_ok(self): @@ -46,7 +46,7 @@ async def test_get_user_ok(self): self.assertEqual(result, "ok") @mock.patch( - "src.app.services.security.check_api_key_sync", + "src.app.shared.infra.security.check_api_key_sync", new=mock.MagicMock(return_value=False), ) async def test_get_user_unauthorized(self): diff --git a/src/app/tutor/api/router.py b/src/app/tutor/api/router.py index 28287cd..53f3379 100644 --- a/src/app/tutor/api/router.py +++ b/src/app/tutor/api/router.py @@ -12,11 +12,11 @@ ) from src.app.core.config import Settings -from src.app.models.search import EnhancedSearchQuery +from src.app.search.helpers.search_helpers import search_multi_inputs +from src.app.search.models.search import EnhancedSearchQuery +from src.app.search.services.search import SearchService, get_search_service from src.app.services.data_collection import get_data_collection_service -from src.app.services.exceptions import NoResultsError -from src.app.services.search import SearchService, get_search_service -from src.app.services.search_helpers import search_multi_inputs +from src.app.shared.domain.exceptions import NoResultsError from src.app.shared.infra.abst_chat import get_chat_service from src.app.shared.utils.dependencies import get_settings from src.app.shared.utils.utils import get_files_content diff --git a/src/main.py b/src/main.py index 12b4350..be9c2f3 100644 --- a/src/main.py +++ b/src/main.py @@ -15,8 +15,8 @@ from src.app.core.config import settings from src.app.core.lifespan import lifespan from src.app.middleware.monitor_requests import MonitorRequestsMiddleware -from src.app.services.security import get_user from src.app.shared.api import health +from src.app.shared.infra.security import get_user from src.app.utils.logger import logger as utils_logger logger = utils_logger(__name__)