Skip to content

Commit

Permalink
FS-101 Add report director (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
mic-smith authored Nov 26, 2024
1 parent ac4e3f8 commit 4f0db2f
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 28 deletions.
13 changes: 7 additions & 6 deletions backend/src/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from src.chat_storage_service import get_chat_message
from src.directors.report_director import report_on_file_upload
from src.session.file_uploads import clear_session_file_uploads
from src.session.redis_session_middleware import reset_session
from src.utils import Config, test_connection
from src.director import question, dataset_upload
from src.directors.chat_director import question, dataset_upload
from src.websockets.connection_manager import connection_manager, parse_message
from src.session import RedisSessionMiddleware
from src.suggestions_generator import generate_suggestions
from src.file_upload_service import handle_file_upload, get_file_upload
from src.utils.file_utils import get_file_upload

config_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "config.ini"))
logging.config.fileConfig(fname=config_file_path, disable_existing_loggers=False)
Expand Down Expand Up @@ -116,12 +117,12 @@ async def suggestions():
return JSONResponse(status_code=500, content=suggestions_failed_response)


@app.post("/uploadfile")
async def create_upload_file(file: UploadFile):
@app.post("/report")
async def report(file: UploadFile):
logger.info(f"upload file type={file.content_type} name={file.filename} size={file.size}")
try:
upload_id = handle_file_upload(file)
return JSONResponse(status_code=200, content={"filename": file.filename, "id": upload_id})
processed_upload = await report_on_file_upload(file)
return JSONResponse(status_code=200, content=processed_upload)
except HTTPException as he:
raise he
except Exception as e:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
logger = logging.getLogger(__name__)
config = Config()
engine = PromptEngine()
director_prompt = engine.load_prompt("director")
director_prompt = engine.load_prompt("chat_director")


async def question(question: str) -> ChatResponse:
Expand Down
23 changes: 23 additions & 0 deletions backend/src/directors/report_director.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

from typing import TypedDict
from fastapi import UploadFile

from src.utils.scratchpad import clear_scratchpad, update_scratchpad
from src.utils.file_utils import handle_file_upload

class FileUploadReport(TypedDict):
id: str
filename: str | None
report: str | None

async def report_on_file_upload(upload:UploadFile) -> FileUploadReport:

file = handle_file_upload(upload)

update_scratchpad(result=file["content"])

report = "#Report on upload as markdown" # await report_agent.invoke(file["content"])

clear_scratchpad()

return {"filename": file["filename"], "id": file["uploadId"], "report": report}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

MAX_FILE_SIZE = 10*1024*1024

def handle_file_upload(file:UploadFile) -> str:
def handle_file_upload(file:UploadFile) -> FileUpload:

if (file.size or 0) > MAX_FILE_SIZE:
raise HTTPException(status_code=413, detail=f"File upload must be less than {MAX_FILE_SIZE} bytes")
Expand Down Expand Up @@ -49,7 +49,7 @@ def handle_file_upload(file:UploadFile) -> str:

update_session_file_uploads(session_file)

return session_file["uploadId"]
return session_file

def get_file_upload(upload_id) -> FileUpload | None:
return get_session_file_upload(upload_id)
Expand Down
11 changes: 11 additions & 0 deletions backend/tests/api/app_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from fastapi.testclient import TestClient
import pytest
from src.chat_storage_service import ChatResponse
from src.directors.report_director import FileUploadReport
from src.api import app, healthy_response, unhealthy_neo4j_response, chat_fail_response

client = TestClient(app)
Expand Down Expand Up @@ -77,6 +78,16 @@ def test_chat_message_not_found(mocker):
mock_get_chat_message.assert_called_with("123")
assert response.status_code == 404

def test_report_response_success(mocker):
mock_reponse = FileUploadReport(filename="filename", id="1", report="some report md")
mock_report = mocker.patch("src.api.app.report_on_file_upload", return_value=mock_reponse)

response = client.post("/report", files={"file": ("filename", "test data".encode("utf-8"), "text/plain")})

mock_report.assert_called_once()
assert response.status_code == 200
assert response.json() == {'filename': 'filename', 'id': '1', 'report': 'some report md'}

@pytest.mark.asyncio
async def test_lifespan_populates_db(mocker) -> None:
mock_dataset_upload = mocker.patch("src.api.app.dataset_upload", return_value=mocker.Mock())
Expand Down
22 changes: 22 additions & 0 deletions backend/tests/directors/report_director_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from io import BytesIO
from fastapi import UploadFile
from fastapi.datastructures import Headers
import pytest

from src.session.file_uploads import FileUpload
from src.directors.report_director import report_on_file_upload

@pytest.mark.asyncio
async def test_report_on_file_upload(mocker):

file_upload = FileUpload(uploadId="1", filename="test.txt", content="test", contentType="text/plain", size=4)

mock_handle_file_upload = mocker.patch("src.directors.report_director.handle_file_upload", return_value=file_upload)

headers = Headers({"content-type": "text/plain"})
file = BytesIO(b"test content")
request_upload_file = UploadFile(file=file, size=12, headers=headers, filename="test.txt")
response = await report_on_file_upload(request_upload_file)

mock_handle_file_upload.assert_called_once_with(request_upload_file)
assert response == {"filename": "test.txt", "id": "1", "report": "#Report on upload as markdown"}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
from fastapi.datastructures import Headers
import pytest

from src.session.file_uploads import FileUpload
from src.file_upload_service import handle_file_upload
from src.utils.file_utils import handle_file_upload

def test_handle_file_upload_size():

Expand All @@ -28,35 +27,23 @@ def test_handle_file_upload_unsupported_type():

def test_handle_file_upload_text(mocker):

mock = mocker.patch("src.file_upload_service.update_session_file_uploads", MagicMock())
mock = mocker.patch("src.utils.file_utils.update_session_file_uploads", MagicMock())

headers = Headers({"content-type": "text/plain"})
file = BytesIO(b"test content")
id = handle_file_upload(UploadFile(file=file, size=12, headers=headers, filename="test.txt"))

session_file = FileUpload(uploadId=id,
contentType="text/plain" ,
filename="test.txt",
content="test content",
size=12)
session_file = handle_file_upload(UploadFile(file=file, size=12, headers=headers, filename="test.txt"))

mock.assert_called_with(session_file)


def test_handle_file_upload_pdf(mocker):

mock = mocker.patch("src.file_upload_service.update_session_file_uploads", MagicMock())
pdf_mock = mocker.patch("src.file_upload_service.PdfReader", MagicMock())
mock = mocker.patch("src.utils.file_utils.update_session_file_uploads", MagicMock())
pdf_mock = mocker.patch("src.utils.file_utils.PdfReader", MagicMock())


headers = Headers({"content-type": "application/pdf"})
id = handle_file_upload(UploadFile(file=BytesIO(), size=12, headers=headers, filename="test.pdf"))

session_file = FileUpload(uploadId=id,
contentType="application/pdf" ,
filename="test.pdf",
content="",
size=12)
session_file = handle_file_upload(UploadFile(file=BytesIO(), size=12, headers=headers, filename="test.pdf"))

pdf_mock.assert_called_once()
mock.assert_called_with(session_file)
Expand Down

0 comments on commit 4f0db2f

Please sign in to comment.