From b24e980426a0f05590d1c981836d639eac5995cb Mon Sep 17 00:00:00 2001 From: SaurabhBadole Date: Tue, 23 Apr 2024 23:22:33 +0530 Subject: [PATCH] adding project files --- .env | 4 + .gitignore | 5 + .streamlit/secrets.toml | 1 + Chatbuddy.py | 135 ++++++++++++++++++++ DocAI_history/readme.txt | 1 + chat_history/readme.txt | 1 + htmltemplates.py | 35 ++++++ pages/1_RAG_DocAI_Q&A.py | 201 ++++++++++++++++++++++++++++++ pages/2_Chat_with_search.py | 63 ++++++++++ pages/3_AudioVideo Transcriber.py | 176 ++++++++++++++++++++++++++ pages/4_YouTube_Transcriber.py | 98 +++++++++++++++ pages/5_MoM_Generator.py | 152 ++++++++++++++++++++++ requirements.txt | 20 +++ utils.py | 29 +++++ 14 files changed, 921 insertions(+) create mode 100644 .env create mode 100644 .gitignore create mode 100644 .streamlit/secrets.toml create mode 100644 Chatbuddy.py create mode 100644 DocAI_history/readme.txt create mode 100644 chat_history/readme.txt create mode 100644 htmltemplates.py create mode 100644 pages/1_RAG_DocAI_Q&A.py create mode 100644 pages/2_Chat_with_search.py create mode 100644 pages/3_AudioVideo Transcriber.py create mode 100644 pages/4_YouTube_Transcriber.py create mode 100644 pages/5_MoM_Generator.py create mode 100644 requirements.txt create mode 100644 utils.py diff --git a/.env b/.env new file mode 100644 index 0000000..75615e6 --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +ASSEMBLYAI_API_KEY= #get yours from https://www.assemblyai.com/ +HF_API_TOKEN= #get yours from https://huggingface.co/settings/tokens +NVIDIA_API_KEY= #get yours from https://build.nvidia.com/explore/discover#llama3-70b +api_key = #get yours from https://openai.com/ \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3de7e42 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.venv/ +__pycache__/ +.DS_Store +#secrets.toml +venv \ No newline at end of file diff --git a/.streamlit/secrets.toml b/.streamlit/secrets.toml new file mode 100644 index 0000000..0c7bb49 --- /dev/null +++ b/.streamlit/secrets.toml @@ -0,0 +1 @@ +api_key = 'add your api_key' \ No newline at end of file diff --git a/Chatbuddy.py b/Chatbuddy.py new file mode 100644 index 0000000..2318ac1 --- /dev/null +++ b/Chatbuddy.py @@ -0,0 +1,135 @@ +from openai import OpenAI +import streamlit as st +import json +from datetime import datetime +import base64 +from fpdf import FPDF + +# Sidebar configuration +with st.sidebar: + st.title(":orange[Welcome to ChatBuddy!]") + openai_api_key = st.text_input("OpenAI API Key", key="chatbot_api_key", type="password") + st.markdown("[Get an OpenAI API key](https://platform.openai.com/account/api-keys)") + +st.sidebar.caption(""" +### Get Started +1. **Enter your OpenAI API Key**: To start chatting with ChatBuddy, you'll need to enter your OpenAI API key. +2. **Chat with ChatBuddy**: Once you've entered your API key, you can start a conversation with ChatBuddy. Ask questions, seek advice, or just chat! +3. **Download Your Conversation**: After your chat session, you have the option to download the entire conversation in PDF format. + +### How to Enter Your API Key +- Paste your OpenAI API key in the input box below. +- Click on "Submit chat query" to validate and start your session. + +### Privacy Notice +- Your API key is stored securely during your session and is not shared with anyone. +- Conversations are stored temporarily and can be downloaded in PDF format for your records. + +### Tips +- Make sure your API key is active and has sufficient quota. +- For best results, be clear and concise with your questions. + +:rainbow[Enjoy your conversation with ChatBuddy!] +""") + + + +# Main chat interface +st.markdown( + """ + + """, + unsafe_allow_html=True +) + +st.title("Your ChatBuddy🙋🏻💭") +st.caption("#### :rainbow[**ChatBuddy powered by Streamlit x OpenAI**]") + +# Initialize session state if not already present +if "messages" not in st.session_state: + st.session_state["messages"] = [{"role": "assistant", "content": "Hey Buddy! How can I help you today?"}] + +# Display messages in chat container +st.markdown('
', unsafe_allow_html=True) +for msg in st.session_state.messages: + st.chat_message(msg["role"]).write(msg["content"]) +st.markdown('
', unsafe_allow_html=True) + +# Handle user input +if prompt := st.chat_input(placeholder="Type your message..."): + if not openai_api_key: + st.info("Please add your OpenAI API key to continue.") + st.stop() + + client = OpenAI(api_key=openai_api_key) + st.session_state.messages.append({"role": "user", "content": prompt}) + st.chat_message("user").write(prompt) + + # Simulate loading state + with st.spinner('Generating response...'): + response = client.chat.completions.create(model="gpt-3.5-turbo", messages=st.session_state.messages) + msg = response.choices[0].message.content + st.session_state.messages.append({"role": "assistant", "content": msg}) + st.chat_message("assistant").write(msg) + +# Function to download chat history as PDF +def download_chat_history_pdf(): + pdf = FPDF() + pdf.add_page() + pdf.set_font("Arial", size=12) + + for msg in st.session_state.messages: + if msg["role"] == "user": + pdf.set_text_color(0, 0, 255) # Blue for user messages + pdf.multi_cell(0, 10, f'User: {msg["content"]}') + else: + pdf.set_text_color(255, 0, 0) # Red for assistant messages + pdf.multi_cell(0, 10, f'Assistant: {msg["content"]}') + + pdf_output = f"chat_history/chat_history_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf" + pdf.output(pdf_output) + + with open(pdf_output, "rb") as f: + b64 = base64.b64encode(f.read()).decode() + + href = f'Download PDF' + return href + +# Download button +st.markdown('
', unsafe_allow_html=True) +st.markdown(f'', unsafe_allow_html=True) +st.markdown('
', unsafe_allow_html=True) + +# Clear chat button +if st.button('Clear Chat'): + st.session_state["messages"] = [{"role": "assistant", "content": ""}] + st.experimental_rerun() diff --git a/DocAI_history/readme.txt b/DocAI_history/readme.txt new file mode 100644 index 0000000..3efa5ea --- /dev/null +++ b/DocAI_history/readme.txt @@ -0,0 +1 @@ +This folder contains conversations from the DocAI Q&A RAG application in PDF format. \ No newline at end of file diff --git a/chat_history/readme.txt b/chat_history/readme.txt new file mode 100644 index 0000000..e106b36 --- /dev/null +++ b/chat_history/readme.txt @@ -0,0 +1 @@ +This folder contains the chat history from the ChatBuddy conversations. \ No newline at end of file diff --git a/htmltemplates.py b/htmltemplates.py new file mode 100644 index 0000000..d8fd178 --- /dev/null +++ b/htmltemplates.py @@ -0,0 +1,35 @@ +css = ''' + +''' + +bot_template = ''' + +
+
{{MSG}}
+
+''' + +user_template = ''' + +
+
{{MSG}}
+
+''' diff --git a/pages/1_RAG_DocAI_Q&A.py b/pages/1_RAG_DocAI_Q&A.py new file mode 100644 index 0000000..953d937 --- /dev/null +++ b/pages/1_RAG_DocAI_Q&A.py @@ -0,0 +1,201 @@ +import streamlit as st +from dotenv import load_dotenv +from PyPDF2 import PdfReader +from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings, ChatNVIDIA +from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain_community.vectorstores import FAISS +from langchain.memory import ConversationBufferMemory +from langchain.chains import ConversationalRetrievalChain +from reportlab.lib.pagesizes import letter +from reportlab.lib import colors +from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer +from reportlab.lib.styles import getSampleStyleSheet +import base64 +import tempfile +import os +from htmltemplates import * +from datetime import datetime + + + +def get_pdf_text(pdf_docs): + text = "" + for pdf in pdf_docs: + pdf_reader = PdfReader(pdf) + for page in pdf_reader.pages: + text += page.extract_text() + return text + +def get_text_chunks(text): + text_splitter = RecursiveCharacterTextSplitter( + chunk_size=1000, + chunk_overlap=200, + ) + chunks = text_splitter.split_text(text) + return chunks + +def get_vectorstore(text_chunks): + embeddings = NVIDIAEmbeddings() + vectorstore = FAISS.from_texts(texts=text_chunks, embedding=embeddings) + return vectorstore + +def get_conversation_chain(vectorstore): + llm = ChatNVIDIA(model="meta/llama3-70b-instruct") + memory = ConversationBufferMemory(memory_key='chat_history', return_messages=True) + conversation_chain = ConversationalRetrievalChain.from_llm( + llm=llm, + retriever=vectorstore.as_retriever(), + memory=memory + ) + return conversation_chain + +def save_chat_session(chat_history): + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as temp_file: + for message in chat_history: + temp_file.write(message.content.encode("utf-8") + b"\n") + return temp_file.name + +def handle_userinput(user_question): + response = st.session_state.conversation({'question': user_question}) + st.session_state.chat_history = response['chat_history'] + + for i, message in enumerate(st.session_state.chat_history): + if i % 2 == 0: + st.markdown(user_template.replace("{{MSG}}", message.content), unsafe_allow_html=True) + else: + st.markdown(bot_template.replace("{{MSG}}", message.content), unsafe_allow_html=True) + +def download_pdf(chat_history): + pdf_filename = "chat_session.pdf" + doc = SimpleDocTemplate(pdf_filename, pagesize=letter) + styles = getSampleStyleSheet() + story = [] + + is_user_message = True + for message in chat_history: + if is_user_message: + paragraph = Paragraph(message.content, styles["Normal"]) + else: + paragraph = Paragraph(message.content, styles["Normal"]) + paragraph.textColor = colors.blue + story.append(paragraph) + story.append(Spacer(1, 12)) + is_user_message = not is_user_message + + doc.build(story) + with open(pdf_filename, "rb") as pdf_file: + pdf_contents = pdf_file.read() + return pdf_contents + +##--------------------------------------------------------------------------- +## V1 without using the file rename format. + +# def main(): +# load_dotenv() +# st.set_page_config(page_title="Your Personalized Document Chatbot", page_icon=":books:") + +# if "conversation" not in st.session_state: +# st.session_state.conversation = None +# if "chat_history" not in st.session_state: +# st.session_state.chat_history = None + +# st.header("Your Personalized Document Chatbot :books:") +# st.markdown(css, unsafe_allow_html=True) # Applying custom CSS + +# user_question = st.text_input("Ask a question about your documents:") +# if user_question: +# handle_userinput(user_question) + +# with st.sidebar: +# st.subheader("Your documents") +# pdf_docs = st.file_uploader("Upload your PDFs here and click on 'Process'", accept_multiple_files=True) +# if st.button("Process"): +# with st.spinner("Processing"): +# raw_text = get_pdf_text(pdf_docs) +# text_chunks = get_text_chunks(raw_text) +# vectorstore = get_vectorstore(text_chunks) +# st.session_state.conversation = get_conversation_chain(vectorstore) +# st.success("Vector Store DB Is Ready") + +# if st.session_state.chat_history is not None: +# pdf_contents = download_pdf(st.session_state.chat_history) +# st.download_button( +# "Download above Conversation", +# pdf_contents, +# "chat_session.pdf", +# "application/pdf", +# key="download" +# ) + + +##--------------------------------------------------------------------------- + +def generate_file_name(): + now = datetime.now() + return now.strftime("chat_session_%Y%m%d_%H%M%S.pdf") + +def save_pdf_to_folder(pdf_content, folder_path): + if not os.path.exists(folder_path): + os.makedirs(folder_path) + file_name = generate_file_name() + file_path = os.path.join(folder_path, file_name) + with open(file_path, "wb") as f: + f.write(pdf_content) + return file_path + +def main(): + load_dotenv() + st.set_page_config(page_title="Your Personalized Document Chatbot", page_icon=":books:") + + if "conversation" not in st.session_state: + st.session_state.conversation = None + if "chat_history" not in st.session_state: + st.session_state.chat_history = None + + st.header("Your Personalized Document Chatbot :books:") + st.caption("**Harness the power of Retrieval-Augmented Generation to answer questions from your documents with AI precision and efficiency.**") + st.markdown(css, unsafe_allow_html=True) # Applying custom CSS + + user_question = st.text_input("Ask a question about your documents:") + if user_question: + handle_userinput(user_question) + + with st.sidebar: + st.sidebar.caption(""" ### :orange[Welcome to DocAI Q&A!] """) + st.sidebar.caption("""Harness the power of Retrieval-Augmented Generation to answer questions from your documents with AI precision and efficiency.""") + pdf_docs = st.file_uploader("Upload your PDFs here and click on 'Process'", accept_multiple_files=True) + if st.button("Process"): + with st.spinner("Processing"): + raw_text = get_pdf_text(pdf_docs) + text_chunks = get_text_chunks(raw_text) + vectorstore = get_vectorstore(text_chunks) + st.session_state.conversation = get_conversation_chain(vectorstore) + st.success("Vector Store DB Is Ready") + + + + st.sidebar.caption(""" + ### How to Use + 1. **Upload Your Documents**: Easily upload your documents to the app. + 2. **Ask Questions**: Query the content of your documents using natural language. + 3. **Get Accurate Answers**: Receive precise and relevant answers generated by AI. + 4. **Download Conversations**: Save your query and answer sessions by downloading them in PDF format. + + :rainbow[Enjoy a seamless and intelligent way to interact with your documents!] + """) + + + if st.session_state.chat_history is not None: + pdf_contents = download_pdf(st.session_state.chat_history) + save_folder = os.path.join(os.getcwd(), "DocAI_history") + saved_file_path = save_pdf_to_folder(pdf_contents, save_folder) + + st.download_button( + "Download above Conversation", + pdf_contents, + saved_file_path, + "application/pdf", + key="download" + ) +if __name__ == '__main__': + main() diff --git a/pages/2_Chat_with_search.py b/pages/2_Chat_with_search.py new file mode 100644 index 0000000..fbf3218 --- /dev/null +++ b/pages/2_Chat_with_search.py @@ -0,0 +1,63 @@ +import streamlit as st + +from langchain.agents import initialize_agent, AgentType +from langchain_community.callbacks.streamlit import StreamlitCallbackHandler +from langchain_community.chat_models import ChatOpenAI +from langchain_community.tools import DuckDuckGoSearchRun + +st.sidebar.caption(""" +### :orange[Chat with search🔍]""") +st.sidebar.caption("""Enhance your conversations with integrated search capabilities, providing instant answers and information from the web.""") + + +with st.sidebar: + openai_api_key = st.text_input( + "OpenAI API Key", key="langchain_search_api_key_openai", type="password" + ) + "[Get an OpenAI API key](https://platform.openai.com/account/api-keys)" + + +st.sidebar.caption(""" +### Get Started +1. **Enter your OpenAI API Key**: To start, please enter your OpenAI API key above. +2. **Chat and Search**: Engage in conversations and use the integrated search to get real-time information and answers. +3. **Enjoy Enhanced Interactions**: Experience more informative and dynamic conversations. + + +### Privacy Notice +- Your API key is stored securely during your session and is not shared with anyone. +- Conversations and search queries are handled with care to ensure your privacy. + +:rainbow[Enhance your conversations with Chat with search!] +""") + + +st.title("Chat with search🔍") +st.caption("**Enhance your conversations with integrated search capabilities, providing instant answers and information from the web.**") + +if "messages" not in st.session_state: + st.session_state["messages"] = [ + {"role": "assistant", "content": "Hello! I'm here to assist you by searching the web. What can I find for you today?"} + ] + +for msg in st.session_state.messages: + st.chat_message(msg["role"]).write(msg["content"]) + +if prompt := st.chat_input(placeholder="What is the current exchange rate for USD to EUR?"): + st.session_state.messages.append({"role": "user", "content": prompt}) + st.chat_message("user").write(prompt) + + if not openai_api_key: + st.info("Please add your OpenAI API key to continue.") + st.stop() + + llm = ChatOpenAI(model_name="gpt-3.5-turbo", openai_api_key=openai_api_key, streaming=True) + search = DuckDuckGoSearchRun(name="Search") + search_agent = initialize_agent( + [search], llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, handle_parsing_errors=True + ) + with st.chat_message("assistant"): + st_cb = StreamlitCallbackHandler(st.container(), expand_new_thoughts=False) + response = search_agent.run(st.session_state.messages, callbacks=[st_cb]) + st.session_state.messages.append({"role": "assistant", "content": response}) + st.write(response) diff --git a/pages/3_AudioVideo Transcriber.py b/pages/3_AudioVideo Transcriber.py new file mode 100644 index 0000000..dbab1ed --- /dev/null +++ b/pages/3_AudioVideo Transcriber.py @@ -0,0 +1,176 @@ +import os +import streamlit as st +import base64 +from haystack.components.writers import DocumentWriter +from haystack.components.preprocessors import DocumentSplitter +from haystack.components.embedders import SentenceTransformersDocumentEmbedder +from haystack import Pipeline +from haystack.document_stores.in_memory import InMemoryDocumentStore +from assemblyai_haystack.transcriber import AssemblyAITranscriber +from haystack.document_stores.types import DuplicatePolicy +from haystack.utils import ComponentDevice +from haystack.components.builders.prompt_builder import PromptBuilder +from haystack.components.generators import HuggingFaceAPIGenerator +from haystack.components.embedders import SentenceTransformersTextEmbedder +from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever +from time import sleep +import requests + + +def audiovideomodule(): + + with st.sidebar: + st.caption("### :orange[Welcome to Audio/Video Transcriber!]") + st.caption("Effortlessly transcribe, analyze, and chat with multi-speaker audio/video conversations.") + + ASSEMBLYAI_API_KEY = st.text_input("Enter your ASSEMBLYAI_API_KEY: ", type="password") + st.markdown("[Get your your AssemblyAI API key ](https://www.assemblyai.com/app/account)") + + HF_API_TOKEN = st.text_input("Enter your HF_API_TOKEN: ", type="password") + st.markdown("[Get your Hugging Face API key](https://huggingface.co/settings/tokens)") + + + st.sidebar.caption(""" + + + ### Get Started + 1. **Enter your API Keys**: To begin, please enter your AssemblyAI API key and Hugging Face API token above. + 2. **Upload Your Audio/Video Files**: Easily upload your files for transcription. + 3. **Transcribe and Analyze**: Automatically transcribe and analyze your multi-speaker conversations. + 4. **Chat with Transcriptions**: Engage in interactive chats based on the transcribed content. + + ### Privacy Notice + - Your API keys are stored securely during your session and are not shared with anyone. + - Uploaded files and transcriptions are handled with care to ensure your privacy. + + :rainbow[Enhance your transcription experience with our powerful tools!] + """) + + + def get_api_keys(): + os.environ["ASSEMBLYAI_API_KEY"] = ASSEMBLYAI_API_KEY + os.environ["HF_API_TOKEN"] = HF_API_TOKEN + return ASSEMBLYAI_API_KEY, HF_API_TOKEN + + def read_file(filename, chunk_size=5242880): + with open(filename, 'rb') as _file: + while True: + data = _file.read(chunk_size) + if not data: + break + yield data + + # Set up Streamlit interface + st.title("Audio/Video Transcriber") + st.caption("**Effortlessly transcribe, analyze, and chat with multi-speaker audio/video conversations**") + + # Get API keys + ASSEMBLYAI_API_KEY, HF_API_TOKEN = get_api_keys() + + # File uploader + uploaded_file = st.file_uploader("Upload Audio/Video File", type=["mp3", "mp4", "wav", "m4a"]) + + transcript_text = "" + + # Check if a file is uploaded and API keys are provided + if uploaded_file is not None and ASSEMBLYAI_API_KEY and HF_API_TOKEN: + with open("temp_file", "wb") as f: + f.write(uploaded_file.getbuffer()) + + # Initialize components + speaker_document_store = InMemoryDocumentStore() + transcriber = AssemblyAITranscriber(api_key=ASSEMBLYAI_API_KEY) + speaker_splitter = DocumentSplitter(split_by="sentence", split_length=10, split_overlap=1) + speaker_embedder = SentenceTransformersDocumentEmbedder(device=ComponentDevice.from_str("cpu")) + speaker_writer = DocumentWriter(speaker_document_store, policy=DuplicatePolicy.SKIP) + + # Create the indexing pipeline + indexing_pipeline = Pipeline() + indexing_pipeline.add_component(instance=transcriber, name="transcriber") + indexing_pipeline.add_component(instance=speaker_splitter, name="speaker_splitter") + indexing_pipeline.add_component(instance=speaker_embedder, name="speaker_embedder") + indexing_pipeline.add_component(instance=speaker_writer, name="speaker_writer") + + indexing_pipeline.connect("transcriber.speaker_labels", "speaker_splitter") + indexing_pipeline.connect("speaker_splitter", "speaker_embedder") + indexing_pipeline.connect("speaker_embedder", "speaker_writer") + + # Transcribe the file + st.write("Transcribing the file...") + bar = st.progress(0) + + result = indexing_pipeline.run( + { + "transcriber": { + "file_path": "temp_file", + "summarization": None, + "speaker_labels": True + }, + } + ) + bar.progress(50) + st.success("Transcription complete!") + + # # Extract the transcript text + # transcript_docs = speaker_document_store.get_all_documents_generator() + # transcript_text = "\n".join([doc.content for doc in transcript_docs]) + + bar.progress(100) + + # Provide options for downloading or interacting with the transcript + st.header('Output') + tab1, tab2 = st.tabs(["Download Transcript", "Chat with Transcript"]) + + with tab1: + st.write("Download the transcribed text:") + b64 = base64.b64encode(transcript_text.encode()).decode() + href = f'Download Transcript' + st.markdown(href, unsafe_allow_html=True) + + with tab2: + st.write("Ask questions about the transcript:") + + open_chat_prompt = """ + GPT4 Correct User: You will be provided with a transcription of a recording with each sentence or group of sentences attributed to a Speaker by the word "Speaker" followed by a letter representing the person uttering that sentence. Answer the given question based on the given context. + If you think that given transcription is not enough to answer the question, say so. + + Transcription: + {% for doc in documents %} + {% if doc.meta["speaker"] %} Speaker {{doc.meta["speaker"]}}: {% endif %}{{doc.content}} + {% endfor %} + Question: {{ question }} + + GPT4 Correct Assistant: + """ + + retriever = InMemoryEmbeddingRetriever(speaker_document_store) + text_embedder = SentenceTransformersTextEmbedder(device=ComponentDevice.from_str("cpu")) + answer_generator = HuggingFaceAPIGenerator( + api_type="serverless_inference_api", + api_params={"model": "openchat/openchat-3.5-0106"}, + generation_kwargs={"max_new_tokens":500} + ) + prompt_builder = PromptBuilder(template=open_chat_prompt) + + speaker_rag_pipe = Pipeline() + speaker_rag_pipe.add_component("text_embedder", text_embedder) + speaker_rag_pipe.add_component("retriever", retriever) + speaker_rag_pipe.add_component("prompt_builder", prompt_builder) + speaker_rag_pipe.add_component("llm", answer_generator) + + speaker_rag_pipe.connect("text_embedder.embedding", "retriever.query_embedding") + speaker_rag_pipe.connect("retriever.documents", "prompt_builder.documents") + speaker_rag_pipe.connect("prompt_builder.prompt", "llm.prompt") + + question = st.text_input("Enter your question:", "") + + if st.button("Get Answer") and question: + result = speaker_rag_pipe.run({ + "prompt_builder": {"question": question}, + "text_embedder": {"text": question}, + "retriever": {"top_k": 10} + }) + st.write(result["llm"]["replies"][0]) + +if __name__ == "__main__": + audiovideomodule() diff --git a/pages/4_YouTube_Transcriber.py b/pages/4_YouTube_Transcriber.py new file mode 100644 index 0000000..4f13342 --- /dev/null +++ b/pages/4_YouTube_Transcriber.py @@ -0,0 +1,98 @@ +import os +import requests +import streamlit as st +from pytube import YouTube +from zipfile import ZipFile +from utils import read_file +# from time import sleep + + +def youtube_transcription(): + st.markdown('# **YouTube Transcriber ▶️**') + st.caption("**Easily convert YouTube videos into text with detailed transcripts for better comprehension and analysis.**") + st.sidebar.caption(""" + ### :orange[Welcome to YouTube Transcriber!] + Easily convert YouTube videos into text with detailed transcripts for better comprehension and analysis.""") + + URL = st.sidebar.text_input('Enter URL of YouTube video:') + submit_button = st.sidebar.button('Go') + + st.sidebar.caption(""" + + ### Get Started + 1. **Enter YouTube Video URL**: Simply paste the URL of the YouTube video you want to transcribe. + 2. **Generate Transcript**: Click "Go" to convert the video into a detailed text transcript. + 3. **Analyze and Comprehend**: Use the transcript for better understanding and analysis of the video's content. + + ### Privacy Notice + - Your video URL is used solely for the purpose of generating the transcript and is not shared with anyone. + - Generated transcripts are stored temporarily and can be downloaded for your records. + + :rainbow[Start transcribing your YouTube videos now!] + """) + + if submit_button and URL: + api_key = st.secrets['api_key'] + headers = {"authorization": api_key, "content-type": "application/json"} + bar = st.progress(0) + + # Function to download YouTube audio + def get_yt_audio(url): + video = YouTube(url) + yt = video.streams.get_audio_only() + yt.download() + bar.progress(10) + return yt.default_filename + + # Function to transcribe audio + def transcribe_yt(filename): + bar.progress(20) + response = requests.post('https://api.assemblyai.com/v2/upload', headers=headers, data=read_file(filename)) + audio_url = response.json()['upload_url'] + bar.progress(30) + + json = {"audio_url": audio_url} + transcript_response = requests.post('https://api.assemblyai.com/v2/transcript', json=json, headers=headers) + transcript_id = transcript_response.json()["id"] + bar.progress(50) + + endpoint = f"https://api.assemblyai.com/v2/transcript/{transcript_id}" + status = 'processing' + with st.spinner('Transcription is processing...'): + while status != 'completed': + response = requests.get(endpoint, headers=headers) + status = response.json()['status'] + + bar.progress(100) + return response.json()["text"], endpoint + + # Main transcription process + try: + filename = get_yt_audio(URL) + transcript_text, transcript_endpoint = transcribe_yt(filename) + + st.subheader('Transcription Output') + st.success(transcript_text) + + # Save the transcript in text and srt formats + with open('yt.txt', 'w') as yt_txt: + yt_txt.write(transcript_text) + + srt_response = requests.get(transcript_endpoint + "/srt", headers=headers) + with open("yt.srt", "w") as srt_file: + srt_file.write(srt_response.text) + + # Create a zip file of the transcripts + with ZipFile('transcription.zip', 'w') as zip_file: + zip_file.write('yt.txt') + zip_file.write('yt.srt') + + # Download button for the zip file + with open("transcription.zip", "rb") as zip_download: + st.download_button("Download ZIP", zip_download, "transcription.zip", "application/zip") + + except Exception as e: + st.error(f"An error occurred: {e}") + +if __name__ == '__main__': + youtube_transcription() diff --git a/pages/5_MoM_Generator.py b/pages/5_MoM_Generator.py new file mode 100644 index 0000000..79274a8 --- /dev/null +++ b/pages/5_MoM_Generator.py @@ -0,0 +1,152 @@ +import streamlit as st +import pandas as pd +import requests +import time +import json + +# Constants +AUTH_TOKEN = "3e10e0448d754962ac0491334562f21f" +HEADERS = { + "authorization": AUTH_TOKEN, + "content-type": "application/json" +} +TRANSCRIPT_ENDPOINT = "https://api.assemblyai.com/v2/transcript" +UPLOAD_ENDPOINT = 'https://api.assemblyai.com/v2/upload' + +def mom(): + def upload_audio_to_assemblyai(audio_file): + """Upload audio file to AssemblyAI and get the upload URL.""" + try: + response = requests.post(UPLOAD_ENDPOINT, headers=HEADERS, data=audio_file) + response.raise_for_status() + audio_url = response.json()['upload_url'] + return audio_url + except requests.exceptions.RequestException as e: + st.error(f"Error uploading audio: {e}") + return None + + def request_transcription(audio_url): + """Request transcription from AssemblyAI.""" + json_payload = { + "audio_url": audio_url, + "iab_categories": True, + "auto_chapters": True, + "speaker_labels": True # Enable speaker labels for detailed summary + } + try: + response = requests.post(TRANSCRIPT_ENDPOINT, json=json_payload, headers=HEADERS) + response.raise_for_status() + transcript_id = response.json()['id'] + return transcript_id + except requests.exceptions.RequestException as e: + st.error(f"Error requesting transcription: {e}") + return None + + def poll_transcription_status(transcript_id): + """Poll the transcription status until it's completed.""" + polling_endpoint = f"{TRANSCRIPT_ENDPOINT}/{transcript_id}" + while True: + try: + response = requests.get(polling_endpoint, headers=HEADERS) + response.raise_for_status() + status = response.json()['status'] + if status == 'completed': + return response.json() + elif status == 'failed': + st.error("Transcription failed.") + return None + time.sleep(5) + except requests.exceptions.RequestException as e: + st.error(f"Error polling transcription status: {e}") + return None + + def convert_millis_to_time_format(millis): + """Convert milliseconds to HH:MM:SS or MM:SS format.""" + seconds = int((millis / 1000) % 60) + minutes = int((millis / (1000 * 60)) % 60) + hours = int((millis / (1000 * 60 * 60)) % 24) + return f'{hours:02d}:{minutes:02d}:{seconds:02d}' if hours > 0 else f'{minutes:02d}:{seconds:02d}' + + def display_transcription_results(results): + """Display the transcription results including themes, chapter summaries, and detailed transcript.""" + st.subheader('Main Themes') + with st.expander('Themes'): + categories = results.get('iab_categories_result', {}).get('summary', []) + for category in categories: + st.markdown(f"* {category}") + + st.subheader('Summary Notes') + chapters = results.get('chapters', []) + chapters_df = pd.DataFrame(chapters) + chapters_df['start_str'] = chapters_df['start'].apply(convert_millis_to_time_format) + chapters_df['end_str'] = chapters_df['end'].apply(convert_millis_to_time_format) + + for _, row in chapters_df.iterrows(): + with st.expander(row['gist']): + st.write(row['summary']) + st.button(row['start_str'], on_click=lambda start=row['start']: update_start(start)) + + st.subheader('Detailed Transcript') + with st.expander('Transcript'): + transcript = results.get('text', '') + st.text_area('Transcript', value=transcript, height=300) + st.download_button( + label="Download Transcript", + data=transcript, + file_name="transcript.txt", + mime="text/plain" + ) + + def update_start(start): + """Update the start point for audio playback.""" + st.session_state['start_point'] = int(start / 1000) + + # Streamlit App Layout + st.markdown('# **Automated Minutes of Meetings (MoM) Generator 📝**') + st.caption("**Transform your meeting recordings into detailed, categorized summaries and downloadable transcripts effortlessly.**") + + # Initialize session state + if 'start_point' not in st.session_state: + st.session_state['start_point'] = 0 + + # Sidebar for file upload + + st.sidebar.caption("""### :orange[Welcome to MoM Generator!]""") + st.sidebar.caption("Transform your meeting recordings into detailed, categorized summaries and downloadable transcripts effortlessly.") + + st.sidebar.subheader('Input for Summarization below') + uploaded_file = st.sidebar.file_uploader('Upload a file for summarization') + + st.sidebar.caption(""" + ### How to Use + 1. **Upload Your Audio File**: Simply upload your meeting audio recording. + 2. **Generate Summaries**: The app will process the audio to create detailed minutes and summaries. + 3. **Download Transcripts**: Get a downloadable transcript of your meeting for easy reference. + + ### Features + - **Detailed Summaries**: Get comprehensive and categorized summaries of your meetings. + - **Effortless Transcription**: Transform audio recordings into written transcripts quickly. + - **Easy Downloads**: Download the meeting minutes and transcripts for future use. + + :rainbow[Streamline your meeting documentation with ease!] + """) + + + if uploaded_file is not None: + st.audio(uploaded_file, start_time=st.session_state['start_point']) + + with st.spinner('Uploading audio...'): + audio_url = upload_audio_to_assemblyai(uploaded_file) + + if audio_url: + with st.spinner('Requesting transcription...'): + transcript_id = request_transcription(audio_url) + + if transcript_id: + with st.spinner('Transcribing...'): + transcription_results = poll_transcription_status(transcript_id) + + if transcription_results: + display_transcription_results(transcription_results) +if __name__ == '__main__': + mom() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..31a9389 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,20 @@ +langchain>=0.0.217 +duckduckgo-search +streamlit-feedback +assemblyai +openai +langchain_nvidia_ai_endpoints +langchain_community +faiss-cpu +python-dotenv +streamlit +pypdf +haystack +assemblyai-haystack +sentence-transformers +huggingface_hub +pytube +requests +PyPDF2==3.0.1 +reportlab +fpdf \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..f3f70bb --- /dev/null +++ b/utils.py @@ -0,0 +1,29 @@ +# utils.py + +import os + +def convertMillis(start_ms): + seconds = int((start_ms / 1000) % 60) + minutes = int((start_ms / (1000 * 60)) % 60) + hours = int((start_ms / (1000 * 60 * 60)) % 24) + btn_txt = '' + if hours > 0: + btn_txt += f'{hours:02d}:{minutes:02d}:{seconds:02d}' + else: + btn_txt += f'{minutes:02d}:{seconds:02d}' + return btn_txt + +def read_file(filename, chunk_size=5242880): + with open(filename, 'rb') as _file: + while True: + data = _file.read(chunk_size) + if not data: + break + yield data + +def save_chat_session(chat_history): + import tempfile + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as temp_file: + for message in chat_history: + temp_file.write(message.content.encode("utf-8") + b"\n") + return temp_file.name