diff --git a/api/app.py b/api/app.py index 1fb739f..70229f7 100644 --- a/api/app.py +++ b/api/app.py @@ -5,6 +5,7 @@ from api.ai import ai_view from api.ui import ui_router from api.upload import media_view +from api.search import search_view import uvicorn api = FastAPI( @@ -30,6 +31,7 @@ async def health_check(): api.include_router(assistant_view.assistant_router) api.include_router(ai_view.ai_router) api.include_router(media_view.media_router) +api.include_router(search_view.search_router) if __name__ == "__main__": uvicorn.run("api.app:api", host="127.0.0.1", port=8000, reload=True) \ No newline at end of file diff --git a/api/config.py b/api/config.py index d0e80ae..2dba8b7 100644 --- a/api/config.py +++ b/api/config.py @@ -26,6 +26,7 @@ CACHE_CONNECTION_STRING="your-redis-connection-string", CACHE_PREFIX="agent_microservice:", CACHE_ASSISTANT_TIMEOUT=3600, + EXTERNAL_PECHA_API_URL="your-external-pecha-api-url", ) def get(key: str) -> str: diff --git a/api/constant.py b/api/constant.py index 1636e2a..b37e217 100644 --- a/api/constant.py +++ b/api/constant.py @@ -1,3 +1,4 @@ class Constant: CREATED_ASSISTANT_MESSAGE="Assistant created successfully" - IMAGE_UPLOAD_SUCCESS="Image uploaded successfully" \ No newline at end of file + IMAGE_UPLOAD_SUCCESS="Image uploaded successfully" + INSTANCE_TYPE="critical" \ No newline at end of file diff --git a/api/http_message_utils.py b/api/http_message_utils.py new file mode 100644 index 0000000..5395d3f --- /dev/null +++ b/api/http_message_utils.py @@ -0,0 +1,21 @@ +from fastapi import HTTPException +import logging +import httpx + +logger = logging.getLogger(__name__) + + +def handle_http_status_error(e: httpx.HTTPStatusError) -> None: + logger.error(f"External API error: {e.response.status_code} - {e.response.text}") + raise HTTPException( + status_code=e.response.status_code, + detail=f"External API error: {e.response.text}" + ) + + +def handle_request_error(e: httpx.RequestError) -> None: + logger.error(f"Request to external API failed: {str(e)}") + raise HTTPException( + status_code=500, + detail="Failed to connect to the service. Please try again later." + ) \ No newline at end of file diff --git a/api/search/search_response_model.py b/api/search/search_response_model.py new file mode 100644 index 0000000..410ac71 --- /dev/null +++ b/api/search/search_response_model.py @@ -0,0 +1,7 @@ +from pydantic import BaseModel + +class SearchTextsDetailsResponse(BaseModel): + id: str + content: str + type: str + source: str \ No newline at end of file diff --git a/api/search/search_service.py b/api/search/search_service.py new file mode 100644 index 0000000..4860ca5 --- /dev/null +++ b/api/search/search_service.py @@ -0,0 +1,50 @@ +import httpx +from api.http_message_utils import handle_http_status_error, handle_request_error +from api.config import get +from api.constant import Constant +from api.search.search_response_model import SearchTextsDetailsResponse + + +client = httpx.AsyncClient(timeout=httpx.Timeout(30.0)) + +ACCEPT_JSON_HEADER = {"Accept": "application/json"} +EXTERNAL_PECHA_API_URL = get("EXTERNAL_PECHA_API_URL") + +async def get_search_texts_details(text_id: str) -> list[SearchTextsDetailsResponse]: + instances = await call_external_pecha_api_instances(text_id) + if instances: + for instance in instances: + instance_id = instance.get("id") + if instance_id: + content = await call_external_pecha_api_instances_content(instance_id) + instance["content"] = content + + return [SearchTextsDetailsResponse(**instance) for instance in instances] + + +async def call_external_pecha_api_instances( + text_id: str +) -> list[dict]: + endpoint = f"{EXTERNAL_PECHA_API_URL}/texts/{text_id}/instances?instance_type={Constant.INSTANCE_TYPE}" + try: + response = await client.get(endpoint, headers=ACCEPT_JSON_HEADER) + response.raise_for_status() + data = response.json() + return data if isinstance(data, list) else [] + + except httpx.HTTPStatusError as e: + handle_http_status_error(e) + except httpx.RequestError as e: + handle_request_error(e) + +async def call_external_pecha_api_instances_content(instance_id: str) -> str: + endpoint = f"{EXTERNAL_PECHA_API_URL}/instances/{instance_id}?annotation=false&content=true" + try: + response = await client.get(endpoint, headers=ACCEPT_JSON_HEADER) + response.raise_for_status() + data = response.json() + return data.get("content", "") + except httpx.HTTPStatusError as e: + handle_http_status_error(e) + except httpx.RequestError as e: + handle_request_error(e) \ No newline at end of file diff --git a/api/search/search_view.py b/api/search/search_view.py new file mode 100644 index 0000000..0b39984 --- /dev/null +++ b/api/search/search_view.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from fastapi import APIRouter +from starlette import status +from api.search.search_service import get_search_texts_details +from api.search.search_response_model import SearchTextsDetailsResponse + +search_router = APIRouter( + prefix="/search", + tags=["search"] +) + + +@search_router.get("/{text_id}", status_code=status.HTTP_200_OK, response_model=list[SearchTextsDetailsResponse]) +async def read_texts_details( + text_id: str +)->list[SearchTextsDetailsResponse]: + return await get_search_texts_details( + text_id=text_id + ) \ No newline at end of file