From 713354371e954ae018e1df60573e987011d2e87e Mon Sep 17 00:00:00 2001 From: timothyafolami Date: Thu, 15 Aug 2024 23:17:17 +0100 Subject: [PATCH] parallel processing added --- __pycache__/utils.cpython-311.pyc | Bin 21162 -> 22890 bytes data/documents.json | 1 - index/faiss_index_data/index.faiss | Bin 1047597 -> 1047597 bytes index/faiss_index_data/index.pkl | Bin 1019951 -> 1019500 bytes loggings/app.log | 128 +++++++++++++ requirements.txt | 3 +- search_note.ipynb | 287 ++++++++++++++++++++++++++++- utils.py | 157 +++++++++------- 8 files changed, 500 insertions(+), 76 deletions(-) delete mode 100644 data/documents.json diff --git a/__pycache__/utils.cpython-311.pyc b/__pycache__/utils.cpython-311.pyc index e43127e3a03cf0b52c190691a6c1f1823531efb6..98455ab403c1bb87a8e0ed2f164c0285a6a33099 100644 GIT binary patch delta 7794 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 delta 6045 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 Q+VEsCSv|Z?3F<@fe=P`CkN^Mx delta 12450 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 E27)@xt^fc4 diff --git a/loggings/app.log b/loggings/app.log index f3e75e05..17a9c530 100644 --- a/loggings/app.log +++ b/loggings/app.log @@ -533,3 +533,131 @@ To avoid injury, hold up the wheel when unscrewing the bolts. 2024-08-15 12:50:08,732 - INFO - Vector store created 2024-08-15 12:50:08,732 - INFO - Saving the vector store 2024-08-15 12:50:08,745 - INFO - Vector store saved +2024-08-15 22:27:53,640 - INFO - Loading data from ./data +2024-08-15 22:28:26,454 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:15,773 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:18,312 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:21,194 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:23,563 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:25,707 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:28,079 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:30,046 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:32,244 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:33,863 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:34,996 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:37,244 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:39,593 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:41,416 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:42,381 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:43,283 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:45,605 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:46,734 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:50,144 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:53,144 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:54,149 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:29:56,683 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:01,059 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:04,416 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:07,578 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:08,304 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:12,904 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:16,304 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:19,593 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:20,690 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:22,950 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:27,134 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:29,830 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 503 Service Unavailable" +2024-08-15 22:30:31,234 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:44,310 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:53,513 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:30:57,355 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:30:57,849 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:30:58,324 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:30:58,763 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:30:59,189 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:30:59,624 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:31:00,064 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:31:00,434 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:31:00,843 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:31:01,323 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:31:01,834 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:38:18,873 - INFO - Loading data from ./data +2024-08-15 22:38:45,628 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:39:49,550 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:39:54,348 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:39:57,845 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:01,631 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:04,163 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:06,901 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:07,582 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:08,282 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:09,997 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:12,182 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:12,765 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:15,845 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:17,389 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:18,451 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:21,247 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:21,334 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:23,360 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:24,218 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:24,552 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:28,755 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:33,043 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:35,045 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:37,390 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:41,155 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:45,315 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:49,518 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:50,481 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:53,073 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:40:56,877 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:01,323 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:02,802 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:05,165 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:09,849 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:13,516 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:13,872 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:16,590 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:20,432 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:23,509 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:25,284 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:25,999 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/audio/translations "HTTP/1.1 200 OK" +2024-08-15 22:41:32,085 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:32,567 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:32,987 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:33,440 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:33,916 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:34,331 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:34,824 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:35,191 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:35,573 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:36,235 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:36,731 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:37,163 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:37,556 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:38,165 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:38,601 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:38,987 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:39,409 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:39,901 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:40,290 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:40,640 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:41,031 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:41,393 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:41,835 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:42,190 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:42,604 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:42,974 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:43,339 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:43,773 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:44,140 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:44,574 - INFO - HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 200 OK" +2024-08-15 22:41:44,574 - INFO - Data loaded +2024-08-15 22:41:44,574 - INFO - Creating vector store +2024-08-15 22:41:46,104 - WARNING - C:\Users\timmy_3aupohg\anaconda3\envs\smog_env\Lib\site-packages\transformers\models\bert\modeling_bert.py:439: UserWarning: 1Torch was not compiled with flash attention. (Triggered internally at C:\cb\pytorch_1000000000000\work\aten\src\ATen\native\transformers\cuda\sdp_utils.cpp:555.) + attn_output = torch.nn.functional.scaled_dot_product_attention( + +2024-08-15 22:41:56,183 - INFO - Vector store created +2024-08-15 22:41:56,198 - INFO - Saving the vector store +2024-08-15 22:41:56,198 - INFO - Vector store saved diff --git a/requirements.txt b/requirements.txt index 0cf4c531..13f49dfa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,4 +18,5 @@ groq python-dotenv pydub moviepy -ffmpeg-python \ No newline at end of file +ffmpeg-python +langchain-groq \ No newline at end of file diff --git a/search_note.ipynb b/search_note.ipynb index ad86dd88..a00b0774 100644 --- a/search_note.ipynb +++ b/search_note.ipynb @@ -6,30 +6,299 @@ "metadata": {}, "outputs": [], "source": [ - "from utils import search\n", - "import sys, os" + "# !pip install langchain-groq" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from utils import search\n", + "import sys, os\n", + "from dotenv import load_dotenv\n", + "from langchain_groq import ChatGroq\n", + "from langchain_core.prompts.prompt import PromptTemplate\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "load_dotenv()" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# setting up groq api key\n", + "os.environ[\"GROQ_API_KEY\"] = os.getenv('GROQ_API_KEY')" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "\n", + "# chat set up\n", + "GROQ_LLM = ChatGroq(temperature=0, model_name=\"llama3-8b-8192\", max_tokens=100)\n", + "\n", + "\n", + "### Chains #####\n", + "# Initiator\n", + "def doc_summarizer(document_page: list) -> str:\n", + " initiator_prompt = PromptTemplate(\n", + " template=\"\"\"<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n", + " Create a short summary of the document based on the provided text. \n", + " \n", + " Start with: This document is about...\n", + " \n", + " <|eot_id|><|start_header_id|>user<|end_header_id|>\n", + " DOCUMENT: {document_page} \\n\n", + " \n", + " <|eot_id|><|start_header_id|>assistant<|end_header_id|>\"\"\",\n", + " input_variables=[\"document_page\"],\n", + " )\n", + "\n", + " initiator_router = initiator_prompt | GROQ_LLM | StrOutputParser()\n", + " output = initiator_router.invoke({\"document_page\":document_page})\n", + " return output\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "document_page = 'How to change the engine oil of a toyota corrolla.'\n", + "# testing the function\n", + "summary = doc_summarizer(document_page)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'This document is about providing a step-by-step guide on how to change the engine oil of a Toyota Corolla.'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "summary" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "docs = search(document_page)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'source': './data\\\\How to change engine oil and filter on TOYOTA Corolla.txt',\n", + " 'page': 1,\n", + " 'file_type': 'text'},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 438},\n", + " {'source': './data\\\\How to change engine oil and filter on TOYOTA Corolla.txt',\n", + " 'page': 3,\n", + " 'file_type': 'text'},\n", + " {'source': './data\\\\How to change engine oil and filter on TOYOTA Corolla.txt',\n", + " 'page': 2,\n", + " 'file_type': 'text'},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 525},\n", + " {'source': './data\\\\How to change spark plugs on TOYOTA COROLLA.docx',\n", + " 'page': 2,\n", + " 'file_type': 'text'},\n", + " {'source': './data\\\\How to change spark plugs on TOYOTA COROLLA.docx',\n", + " 'page': 3,\n", + " 'file_type': 'text'},\n", + " {'source': './data\\\\How to change engine oil and filter on TOYOTA Corolla.txt',\n", + " 'page': 0,\n", + " 'file_type': 'text'},\n", + " {'source': './data\\\\How to change spark plugs on TOYOTA COROLLA.docx',\n", + " 'page': 5,\n", + " 'file_type': 'text'},\n", + " {'source': './data\\\\How to change spark plugs on TOYOTA COROLLA.docx',\n", + " 'page': 6,\n", + " 'file_type': 'text'},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 526},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 422},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 514},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 153},\n", + " {'filename': 'audio-2', 'duration': '0-3 minutes', 'file_type': 'audio'},\n", + " {'filename': 'audio-2', 'duration': '3-6 minutes', 'file_type': 'audio'},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 149},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 513},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 436},\n", + " {'source': './data\\\\corolla-2020-toyota-owners-manual.pdf', 'page': 148}]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "docs" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import defaultdict\n", + "\n", + "def transform_file_data(input_data):\n", + " # Create a dictionary to aggregate data by filename\n", + " aggregated_data = defaultdict(lambda: {\n", + " 'filename': '',\n", + " 'pages': [],\n", + " 'timestamps': [],\n", + " 'description': 'lorem ipsum',\n", + " 'filetype': '',\n", + " 'thumbnail': '',\n", + " 'track_id': 123\n", + " })\n", + "\n", + " for item in input_data:\n", + " if 'source' in item:\n", + " file_path = item['source']\n", + " filename = file_path.split('\\\\')[-1]\n", + " extension = filename.split('.')[-1]\n", + "\n", + " aggregated_data[filename]['filename'] = filename\n", + " aggregated_data[filename]['filetype'] = extension\n", + " aggregated_data[filename]['thumbnail'] = f\"{filename.split('.')[0]}.jpg\"\n", + "\n", + " if extension in ['pdf', 'txt', 'docx']:\n", + " aggregated_data[filename]['pages'].append(item['page'])\n", + " elif extension in ['mp4', 'mkv', 'flv']:\n", + " aggregated_data[filename]['timestamps'].append(item['page'])\n", + " elif extension in ['mp3', 'wav', 'flac']:\n", + " aggregated_data[filename]['timestamps'].append(item['page'])\n", + " elif extension in ['jpg', 'jpeg', 'png', 'gif', 'bmp']:\n", + " aggregated_data[filename].pop('pages', None) # Remove pages if it's an image\n", + " aggregated_data[filename].pop('timestamps', None) # Remove timestamps if it's an image\n", + "\n", + " elif 'filename' in item:\n", + " filename = item['filename']\n", + " extension = item['file_type']\n", + " aggregated_data[filename]['filename'] = f\"{filename}.{extension}\"\n", + " aggregated_data[filename]['filetype'] = extension\n", + " aggregated_data[filename]['thumbnail'] = f\"{filename}.jpg\"\n", + " if 'duration' in item:\n", + " start_time, end_time = item['duration'].split(' minutes')[0].split('-')\n", + " aggregated_data[filename]['timestamps'].append((int(start_time), int(end_time)))\n", + "\n", + " # Convert aggregated data to the desired output format\n", + " output_data = []\n", + " for filename, data in aggregated_data.items():\n", + " # Remove empty lists for pages and timestamps\n", + " if not data['pages']:\n", + " data.pop('pages', None)\n", + " if not data['timestamps']:\n", + " data.pop('timestamps', None)\n", + " output_data.append(data)\n", + "\n", + " return output_data\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'filename': 'How to change engine oil and filter on TOYOTA Corolla.txt', 'pages': [1, 3, 2, 0], 'description': 'lorem ipsum', 'filetype': 'txt', 'thumbnail': 'How to change engine oil and filter on TOYOTA Corolla.jpg', 'track_id': 123}\n", + "{'filename': 'corolla-2020-toyota-owners-manual.pdf', 'pages': [438, 525, 526, 422, 514, 153, 149, 513, 436, 148], 'description': 'lorem ipsum', 'filetype': 'pdf', 'thumbnail': 'corolla-2020-toyota-owners-manual.jpg', 'track_id': 123}\n", + "{'filename': 'How to change spark plugs on TOYOTA COROLLA.docx', 'pages': [2, 3, 5, 6], 'description': 'lorem ipsum', 'filetype': 'docx', 'thumbnail': 'How to change spark plugs on TOYOTA COROLLA.jpg', 'track_id': 123}\n", + "{'filename': 'audio-2.audio', 'timestamps': [(0, 3), (3, 6)], 'description': 'lorem ipsum', 'filetype': 'audio', 'thumbnail': 'audio-2.jpg', 'track_id': 123}\n" + ] + } + ], + "source": [ + "output = transform_file_data(docs)\n", + "for item in output:\n", + " print(item)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'filename': 'How to change engine oil and filter on TOYOTA Corolla.txt',\n", + " 'pages': [1, 3, 2, 0],\n", + " 'description': 'lorem ipsum',\n", + " 'filetype': 'txt',\n", + " 'thumbnail': 'How to change engine oil and filter on TOYOTA Corolla.jpg',\n", + " 'track_id': 123},\n", + " {'filename': 'corolla-2020-toyota-owners-manual.pdf',\n", + " 'pages': [438, 525, 526, 422, 514, 153, 149, 513, 436, 148],\n", + " 'description': 'lorem ipsum',\n", + " 'filetype': 'pdf',\n", + " 'thumbnail': 'corolla-2020-toyota-owners-manual.jpg',\n", + " 'track_id': 123},\n", + " {'filename': 'How to change spark plugs on TOYOTA COROLLA.docx',\n", + " 'pages': [2, 3, 5, 6],\n", + " 'description': 'lorem ipsum',\n", + " 'filetype': 'docx',\n", + " 'thumbnail': 'How to change spark plugs on TOYOTA COROLLA.jpg',\n", + " 'track_id': 123},\n", + " {'filename': 'audio-2.audio',\n", + " 'timestamps': [(0, 3), (3, 6)],\n", + " 'description': 'lorem ipsum',\n", + " 'filetype': 'audio',\n", + " 'thumbnail': 'audio-2.jpg',\n", + " 'track_id': 123}]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "output" + ] }, { "cell_type": "code", diff --git a/utils.py b/utils.py index 499ff917..9f79117c 100644 --- a/utils.py +++ b/utils.py @@ -6,14 +6,19 @@ from langchain_community.vectorstores import FAISS from langchain_community.document_loaders import PyPDFLoader from langchain_community.document_loaders import TextLoader from langchain_community.document_loaders import Docx2txtLoader +from langchain_groq import ChatGroq +from langchain_core.prompts.prompt import PromptTemplate +from langchain_core.output_parsers import StrOutputParser from uuid import uuid4 from langchain_core.documents import Document from text_extractor import TextExtractor import os +from concurrent.futures import ThreadPoolExecutor import math import json from groq import Groq import re +import time import shutil import numpy as np from pydub import AudioSegment @@ -26,10 +31,15 @@ load_dotenv() # OpenAI API Key api_key = os.getenv('OPENAI_API_KEY') +# setting up groq api key +os.environ["GROQ_API_KEY"] = os.getenv('GROQ_API_KEY') client = Groq(api_key = os.getenv('GROQ_API_KEY')) model = 'whisper-large-v3' +# chat set up +GROQ_LLM = ChatGroq(temperature=0, model_name="llama3-8b-8192", max_tokens=100) + # ---------------------------------------------------------------------------------------------------- # loading the embedding model def load_embedding_model(): @@ -337,6 +347,25 @@ def preprocess_video_data(video_path: str, time_interval: int): return documents +#----------------------------------------------------DOC SUMMARIZER -------------------------------------------------- +def doc_summarizer(document_page: list) -> str: + initiator_prompt = PromptTemplate( + template="""<|begin_of_text|><|start_header_id|>system<|end_header_id|> + Create a short summary of the document based on the provided text. + + Start with: This document is about... + + <|eot_id|><|start_header_id|>user<|end_header_id|> + DOCUMENT: {document_page} \n + + <|eot_id|><|start_header_id|>assistant<|end_header_id|>""", + input_variables=["document_page"], + ) + + initiator_router = initiator_prompt | GROQ_LLM | StrOutputParser() + output = initiator_router.invoke({"document_page":document_page}) + return output + #-----------------------------------------------------OTHERS-------------------------------------------------------------- @@ -348,88 +377,86 @@ def load_embedded_data(embeddings=embeddings, key="data"): embed_db = FAISS.load_local(f"index/faiss_index_{key}", embeddings, allow_dangerous_deserialization=True) return embed_db +#-----------------------------------------------------Data Loading Process---------------------------------------------------- # creating a function to load all documents from a directory. +def process_document(path, extension, text_doc, image_doc, audio_doc, video_doc): + doc_name = os.path.basename(path).split('.')[0] + + process_map = { + "text": load_document, + "image": create_image_document, + "audio": create_audio_document, + "video": preprocess_video_data + } + + if extension in text_doc: + doc = process_map["text"](path) + num_pages = len(doc) + elif extension in image_doc: + doc = process_map["image"](path) + num_pages = 1 + doc_name = doc[0].metadata['filename'] + elif extension in audio_doc: + doc = process_map["audio"](path) + num_pages = len(doc) + doc_name = doc[0].metadata['filename'] + elif extension in video_doc: + doc = process_map["video"](path, time_interval=30) + num_pages = len(doc) + doc_name = doc[0].metadata['filename'] + else: + return None, None, None # Unhandled extension + + print(f"Document {doc_name} loaded") + return doc, doc_name, num_pages + def load_documents_from_directory(directory_path: str): text_doc = ['pdf', 'txt', 'docx', 'doc', 'md'] image_doc = ['jpg', 'jpeg', 'png', 'gif', 'bmp'] audio_doc = ['mp3', 'wav', 'flac', 'ogg', 'm4a'] video_doc = ['mp4', 'avi', 'mkv', 'flv', 'mov'] - # accessing the name of the files in the directory files = os.listdir(directory_path) - # creating a list to store the documents documents = [] - # another list for the document names doc_names = [] - # counting the number of pages in the document - num_pages= [] - # iterating through the files in the directory - for file in files: - # updating the path - path = os.path.join(directory_path, file) - # getting the file extension and doc name - doc_name, extension = path.split('/')[-1].split('.')[0] , file.split('.')[-1] - # checking if the file is a text document - if extension in text_doc: - # loading the document - doc = load_document(path) - # appending the document to the documents list + num_pages = [] + doc_summary = [] + + def process_with_delay(file): + result = process_document(os.path.join(directory_path, file), file.split('.')[-1], text_doc, image_doc, audio_doc, video_doc) + time.sleep(0.1) # Introduce a 0.1s delay between processing each document + return result + + with ThreadPoolExecutor() as executor: + results = executor.map(process_with_delay, files) + + for doc, doc_name, pages in results: + if doc is not None: documents.append(doc) - # appending the number of pages in the document - num_pages.append(len(doc)) - # adding the document name to the doc_names list doc_names.append(doc_name) - print(f"Document {doc_name} loaded") - elif extension in image_doc: - # creating an image document - doc = create_image_document(path) - # appending the document to the documents list - documents.append(doc) - # appending the number of pages in the document - num_pages.append(1) - # adding the document name to the doc_names list - doc_names.append(doc[0].metadata['filename']) - print(f"Document {doc[0].metadata['filename']} loaded") - elif extension in audio_doc: - # creating an audio document - doc = create_audio_document(path) - # appending the document to the documents list - documents.append(doc) - # appending the number of pages in the document - num_pages.append(len(doc)) - # adding the document name to the doc_names list - doc_names.append(doc[0].metadata['filename']) - print(f"Document {doc[0].metadata['filename']} loaded") - elif extension in video_doc: - # creating a video document - doc = preprocess_video_data(path, time_interval=30) - # appending the document to the documents list - documents.append(doc) - # appending the number of pages in the document - num_pages.append(len(doc)) - # adding the document name to the doc_names list - doc_names.append(doc[0].metadata['filename']) - print(f"Document {doc[0].metadata['filename']} loaded") + num_pages.append(pages) - # so we need to create a document id for each document - docs_id = [uuid4().hex for i in range(len(documents))] - # creating a json file to store the documents, checking if it exists then open it, else create it - json_file = f"{directory_path}/documents.json" + # creating doc summary + first_page = doc[0].page_content + summary = doc_summarizer(first_page) + doc_summary.append(summary) + + docs_id = [uuid4().hex for _ in range(len(documents))] + + json_file = os.path.join(directory_path, 'data.json') + data = {'doc_names': doc_names, 'docs_id': docs_id, 'num_pages': num_pages, 'doc_summaary': doc_summary} + if os.path.exists(json_file): - with open(json_file, 'r') as f: - data = json.load(f) - data['doc_names'] = doc_names - data['docs_id'] = docs_id - data['num_pages'] = num_pages - with open(json_file, 'w') as f: - json.dump(data, f) + with open(json_file, 'r+') as f: + existing_data = json.load(f) + existing_data.update(data) + f.seek(0) + json.dump(existing_data, f) else: - data = {'doc_names': doc_names, 'docs_id': docs_id, 'num_pages': num_pages} with open(json_file, 'w') as f: json.dump(data, f) - # returning the documents, and doc ids return documents, docs_id, num_pages @@ -475,6 +502,6 @@ def search(query, k=20): all = [] info = [] for doc in docs: - all.append({doc.page_content}) + # all.append({doc.page_content}) info.append(dict(doc.metadata)) - return docs[0].page_content, all, info + return info