This project enables a user to insert an audio file url or write plain text with personal information and syntomps. Then use Google Cloud serverless functions to perform a transcript (if needed), extract structured and relevant data about the patient and its symptoms, and perform a diagnose using Open AI models.
- Python 3.11.9
- Google Cloud account with
gcloudCLI installed and configured. Also firebase. - Open AI API Key
This project is basically divided into two main components that could be decoupled into microservices if needed.
-
Frontend: It is a streamlit app that bascially offers to the user to put an audio url or plain text (audio url preferred) and serves the endpoint so when the user submits, sends the requests and shows the response, either a successsful or unsuccessful in a friendly way.
-
Backend: It is a set of modules containing all the logic and endpoints to transcribe, extract and diagnose served in a API way.
General view of the project structure
/ai-disease-diagnoser
βββ README.md
βββ frontend
β βββ .streamlit
β βββ config.toml # Config of streamlit
β
β βββ common # Package of common modules. Can be decoupled as external library.
β βββ __init__.py # No description needed
β βββ exceptions.py # Exceptions module
β βββ response_adapters.py # Adapters for displaying the API response in a friendly way
β βββ schemas.py # Schemas modules for validation
β
β βββ tests # Tests folder
β βββ # Left some generic ones.
β
β βββ .dockerignore # # No description needed
β βββ Dockerfile # File to build the docker image (if needed)
β βββ app.py # Simple app definition
β βββ config.py # Basic configs
β βββ sample.env # Basic configs
β βββ requirements.txt # No description needed
β
βββ functions # Backend package. The name functions is needed due to firebase functions constraints.
β
β βββ common # Package of common modules. Can be decoupled as external library.
β βββ __init__.py # No description needed
β βββ decorators.py # Basic configs
β βββ exceptions.py # Exceptions module
β βββ utils.py # Utils
β βββ openai_client.py # Singleton client
β βββ firestore_utils.py # Utils to write to firestore
β βββ schemas.py # Schemas modules for validation
β
β βββ diagnoser # Module of diagnoser logic
β βββ __init__.py # No description needed
β βββ main.py # Core logic
β βββ prompt.txt # Customizable prompt. Can be decoupled to avoid touching code when changing
β
β βββ extractor # Module of extractor logic
β βββ __init__.py # No description needed
β βββ main.py # Core logic
β βββ prompt.txt # Customizable prompt. Can be decoupled to avoid touching code when changing
β
β βββ transcriber # Module of transciber logic
β βββ __init__.py # No description needed
β βββ main.py # Core logic
β
β βββ tests # Tests folder
β βββ # Left some generic ones.
β
β βββ main.py # Main file containing endpoint definitions and orchestrator function (explained later)
β βββ __init__.py # No description needed
β βββ .gitignore # No description needed
β βββ sample.env # Important file to add your environment variables.
β βββ config.py # Basic configurations file
β βββ requirements_test.txt # No description needed
β βββ requirements.txt # No description needed
β
βββ .gitignore # No description needed
βββ firebase.json # Functions config
βββ LICENSE # No description needed
Install firebase CLI y log in.
Add you API key into functions/sample.env file and rename the file to .env
First create a venv in functions directory.
python3 -m venv functions/venv
Activate env and install requirements
source functions/venv/bin/activate && pip install -r functions/requirements.txt
Run firebase emulators.
firebase emulators:start --only functions,hosting
After doing this, the backend should be up and running.
You need to check your terminal to see the links for accesing the functions locally. Something like this:

Save up the URL showed (in the image case http://127.0.0.1:5001/ai-diagnoser/us-central1/process_medical_data) because you will need it for the streamlit app
First, rename the sample.env to .env and add the url above showed into the file as the ENDPOINT_URL var.
In another terminal, run the following.
pip install -r frontend/requirements.txt
streamlit run frontend/app.py
You should be able to see something like this if everything went good.
Option 1: https://audiourl.something:
Even though this project project was made following good practices of Python developing, like modular structure, standard API responses, KISS, single responsibility of the functions, etc, it was also done following an MVP quick-and-dirty philosphy, which means there's some technical debt that could be tackled in next versions.
If you take a closer look ito the functions/main.py, the principal endpoint called /process that calls the function process_medical_data it is actually following an orchestrator architecture. Which means only 1 cloud function is needed (process_medical_data) and the other ones (transcribe, extract , process) are not deployed as cloud functions (they could be, take a look i created the endpoints.) but only used as helper functions.
Why?
Because following chaining of cloud functions calls via HTTP introduced more complexity within this pahse of the project, like more latency and costs (cold start for example) and the frontend should be more complex handling retries and response of 3 distinct functions, or the last function should wait for the response of the first, etc. It was also easier to handle errors and logs in a single orchestrator function that calls helper functions.
Of course we could also follow an event/triggers or pub/sub architecture but it will be explained more in detail why not
As stated above, the current structure does not allow to deploy every function (diagnose, extractor, transcriber) as a single function. The change is easy though.
Ideally, whe SHOULD store all the relevant data in firestore, to be used later. It is better to have useless data than not having data.
Why we did not do that?
It required more time setting everything up.
Why we did not do that?
We needed to store data into firestore, and also adapt the code to be completely asynchronous. This is a mess to handle primarily in the frontend app, as it will ned to be polling the backend for the response of the last function.
I did not do that because setting up network within docker containers and local host takes more time. But everything is there to do it. Actually you can run the build of the image and run the container and it will work.
I did not have enough time to do it :(
I added some generic files to do so, but did not take the time to configure it. Ideally we should have it running tests and deploying versions of the functions




