Spaces:
Sleeping
Sleeping
Upload 6 files
Browse files- .gitattributes +1 -0
- app.py +116 -0
- app_config.py +24 -0
- functions.py +94 -0
- requirements.txt +9 -0
- yt_audio.mp4 +3 -0
- yt_transcription.txt +6 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
yt_audio.mp4 filter=lfs diff=lfs merge=lfs -text
|
app.py
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import random
|
3 |
+
from app_config import SYSTEM_PROMPT, NLP_MODEL_NAME, NUMBER_OF_VECTORS_FOR_RAG, NLP_MODEL_TEMPERATURE, NLP_MODEL_MAX_TOKENS, VECTOR_MAX_TOKENS,SUMMMERIZE_PROMPT
|
4 |
+
from functions import get_vectorstore, tiktoken_len,save_audio_file,get_audio_transcription
|
5 |
+
from langchain.memory import ConversationSummaryBufferMemory
|
6 |
+
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
|
7 |
+
from langchain.chains.summarize import load_summarize_chain
|
8 |
+
from langchain.prompts import PromptTemplate
|
9 |
+
from langchain_groq import ChatGroq
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
from pathlib import Path
|
12 |
+
import os
|
13 |
+
env_path = Path('.') / '.env'
|
14 |
+
load_dotenv(dotenv_path=env_path)
|
15 |
+
|
16 |
+
def response_generator(prompt: str) -> str:
|
17 |
+
"""this function can be used for general quetion answers which are related to tyrex and tyre recycling
|
18 |
+
|
19 |
+
Args:
|
20 |
+
prompt (string): user query
|
21 |
+
|
22 |
+
Returns:
|
23 |
+
string: answer of the query
|
24 |
+
"""
|
25 |
+
|
26 |
+
try:
|
27 |
+
retriever = st.session_state.retriever
|
28 |
+
docs = retriever.invoke(prompt)
|
29 |
+
my_context = [doc.page_content for doc in docs]
|
30 |
+
my_context = '\n\n'.join(my_context)
|
31 |
+
|
32 |
+
system_message = SystemMessage(content = SYSTEM_PROMPT.format(context=my_context, previous_message_summary=st.session_state.rag_memory.moving_summary_buffer))
|
33 |
+
chat_messages = (system_message + st.session_state.rag_memory.chat_memory.messages + HumanMessage(content=prompt)).messages
|
34 |
+
|
35 |
+
response = st.session_state.llm.invoke(chat_messages)
|
36 |
+
return response.content
|
37 |
+
|
38 |
+
except Exception as error:
|
39 |
+
print(error)
|
40 |
+
return "Oops! something went wrong, please try again."
|
41 |
+
|
42 |
+
|
43 |
+
st.markdown(
|
44 |
+
"""
|
45 |
+
<style>
|
46 |
+
.st-emotion-cache-janbn0 {
|
47 |
+
flex-direction: row-reverse;
|
48 |
+
text-align: right;
|
49 |
+
}
|
50 |
+
</style>
|
51 |
+
""",
|
52 |
+
unsafe_allow_html=True,
|
53 |
+
)
|
54 |
+
|
55 |
+
st.header("TubeChat: AI Chatbot for Video Summary and Q&A")
|
56 |
+
if url := st.text_input("Enter the Youtube Video URL: "):
|
57 |
+
|
58 |
+
if "url" in st.session_state and url!= st.session_state.url:
|
59 |
+
st.session_state.clear()
|
60 |
+
|
61 |
+
if "url" not in st.session_state:
|
62 |
+
# save audio file
|
63 |
+
save_audio_file(url)
|
64 |
+
print("save audio file ...")
|
65 |
+
st.session_state.url = url
|
66 |
+
# save transcrption
|
67 |
+
get_audio_transcription()
|
68 |
+
print("save transcription")
|
69 |
+
|
70 |
+
print("SYSTEM MESSAGE")
|
71 |
+
if "messages" not in st.session_state:
|
72 |
+
st.session_state.messages=[{"role": "system", "content": SYSTEM_PROMPT}]
|
73 |
+
|
74 |
+
print("SYSTEM MODEL")
|
75 |
+
if "llm" not in st.session_state:
|
76 |
+
st.session_state.llm = ChatGroq(temperature=NLP_MODEL_TEMPERATURE, groq_api_key=str(os.getenv('GROQ_API_KEY')), model_name=NLP_MODEL_NAME)
|
77 |
+
|
78 |
+
print("rag")
|
79 |
+
if "rag_memory" not in st.session_state:
|
80 |
+
st.session_state.rag_memory = ConversationSummaryBufferMemory(llm=st.session_state.llm, max_token_limit=NLP_MODEL_MAX_TOKENS - tiktoken_len(SYSTEM_PROMPT) - VECTOR_MAX_TOKENS*NUMBER_OF_VECTORS_FOR_RAG)
|
81 |
+
|
82 |
+
print("retrival")
|
83 |
+
if "retriever" not in st.session_state:
|
84 |
+
vector_store, docs = get_vectorstore()
|
85 |
+
st.session_state.retriever = vector_store.as_retriever(k=NUMBER_OF_VECTORS_FOR_RAG)
|
86 |
+
|
87 |
+
#get summary of given youtube video
|
88 |
+
summary_prompt = PromptTemplate(template=SUMMMERIZE_PROMPT,
|
89 |
+
input_variables=["text"])
|
90 |
+
chain = load_summarize_chain(st.session_state.llm,chain_type="stuff",prompt = summary_prompt)
|
91 |
+
output_summary = chain.run(docs)
|
92 |
+
st.session_state.messages.append({"role":"assistant","content":"Video's Summary: \n"+ output_summary})
|
93 |
+
|
94 |
+
print("container")
|
95 |
+
# Display chat messages from history
|
96 |
+
container = st.container(height=700)
|
97 |
+
for message in st.session_state.messages:
|
98 |
+
if message["role"] != "system":
|
99 |
+
with container.chat_message(message["role"]):
|
100 |
+
st.write(message["content"])
|
101 |
+
|
102 |
+
# When user gives input
|
103 |
+
if prompt := st.chat_input("Enter your query here... "):
|
104 |
+
with container.chat_message("user"):
|
105 |
+
st.write(prompt)
|
106 |
+
st.session_state.messages.append({"role":"user" , "content":prompt})
|
107 |
+
|
108 |
+
with container.chat_message("assistant"):
|
109 |
+
response = response_generator(prompt=prompt)
|
110 |
+
print("******************************************************** Response ********************************************************")
|
111 |
+
print("MY RESPONSE IS:", response)
|
112 |
+
st.write(response)
|
113 |
+
|
114 |
+
print("Response is:", response)
|
115 |
+
st.session_state.rag_memory.save_context({'input': prompt}, {'output': response})
|
116 |
+
st.session_state.messages.append({"role":"assistant" , "content":response})
|
app_config.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
SYSTEM_PROMPT = """
|
3 |
+
1. You are a virtual assistant with a specific focus on providing answers related to given context only.now you are not any chatbot which gives every quetions answer. if you can't provide the answer of the quetions then only tell them "Thank you for your question! I'm here to help with information related to provided video.the answer of this question is not given in this video. If you have any queries about those topics, feel free to ask. For other questions, I recommend reaching out to the appropriate source." nothing else.
|
4 |
+
2. User can also give you some greetings like thank you, welcome, please, sorry etc... so you have to handle it appropriately without giving any unnecessary information which is not wanted by user.
|
5 |
+
3. any information must be answered from provided context only, you must not to answer outside to the context.
|
6 |
+
|
7 |
+
context: {context}
|
8 |
+
|
9 |
+
previous_message_summary: {previous_message_summary}
|
10 |
+
"""
|
11 |
+
SUMMMERIZE_PROMPT = """Given the following context, summarize the key points in a concise and informative manner use mark ups for better format:
|
12 |
+
{text}
|
13 |
+
CONSCISE SUMMARY IN BULLET POINTS:
|
14 |
+
"""
|
15 |
+
|
16 |
+
NLP_MODEL_NAME = "llama3-70b-8192"
|
17 |
+
REASONING_MODEL_NAME = "mixtral-8x7b-32768"
|
18 |
+
REASONING_MODEL_TEMPERATURE = 0
|
19 |
+
NLP_MODEL_TEMPERATURE = 0
|
20 |
+
NLP_MODEL_MAX_TOKENS = 5400
|
21 |
+
VECTOR_MAX_TOKENS = 384
|
22 |
+
VECTORS_TOKEN_OVERLAP_SIZE = 20
|
23 |
+
|
24 |
+
NUMBER_OF_VECTORS_FOR_RAG = 7
|
functions.py
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import tiktoken
|
2 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
3 |
+
from langchain_chroma import Chroma
|
4 |
+
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
|
5 |
+
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
|
6 |
+
from transformers import pipeline
|
7 |
+
from app_config import VECTOR_MAX_TOKENS, VECTORS_TOKEN_OVERLAP_SIZE
|
8 |
+
from langchain.docstore.document import Document
|
9 |
+
import torch
|
10 |
+
from pytube import YouTube
|
11 |
+
from dotenv import load_dotenv
|
12 |
+
from pathlib import Path
|
13 |
+
import os
|
14 |
+
env_path = Path('.') / '.env'
|
15 |
+
load_dotenv(dotenv_path=env_path)
|
16 |
+
|
17 |
+
tokenizer = tiktoken.get_encoding('cl100k_base')
|
18 |
+
|
19 |
+
# create the length function
|
20 |
+
def tiktoken_len(text):
|
21 |
+
tokens = tokenizer.encode(
|
22 |
+
text,
|
23 |
+
disallowed_special=()
|
24 |
+
)
|
25 |
+
return len(tokens)
|
26 |
+
|
27 |
+
def save_audio_file(url):
|
28 |
+
try:
|
29 |
+
yt_obj = YouTube(url)
|
30 |
+
metadata = f"Title: {yt_obj.title}, Total Time: {yt_obj.length} seconds, Number of views: {yt_obj.views}, Rating: {yt_obj.rating}"
|
31 |
+
|
32 |
+
metadata += f"""Description: {yt_obj.description} Uploader: {yt_obj.author} Upload Date: {yt_obj.publish_date}
|
33 |
+
Thumbnail URL: {yt_obj.thumbnail_url}
|
34 |
+
Channel URL: {yt_obj.channel_url}
|
35 |
+
Age Restricted: {yt_obj.age_restricted}
|
36 |
+
|
37 |
+
"""
|
38 |
+
with open("yt_transcription.txt","w") as f:
|
39 |
+
f.write(metadata)
|
40 |
+
|
41 |
+
yt_audio_stream = yt_obj.streams.get_by_itag(139)
|
42 |
+
yt_audio_stream.download("","yt_audio.mp4")
|
43 |
+
except:
|
44 |
+
print("Connection Error")
|
45 |
+
|
46 |
+
def get_audio_transcription():
|
47 |
+
# whisper = pipeline("automatic-speech-recognition",
|
48 |
+
# "openai/whisper-tiny.en")
|
49 |
+
device = "cuda:0" if torch.cuda.is_available() else "cpu"
|
50 |
+
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32
|
51 |
+
model_id = "distil-whisper/distil-large-v2"
|
52 |
+
model = AutoModelForSpeechSeq2Seq.from_pretrained(
|
53 |
+
model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
|
54 |
+
)
|
55 |
+
model.to(device)
|
56 |
+
processor = AutoProcessor.from_pretrained(model_id)
|
57 |
+
whisper = pipeline(
|
58 |
+
"automatic-speech-recognition",
|
59 |
+
model=model,
|
60 |
+
tokenizer=processor.tokenizer,
|
61 |
+
feature_extractor=processor.feature_extractor,
|
62 |
+
max_new_tokens=128,
|
63 |
+
torch_dtype=torch_dtype,
|
64 |
+
device=device,
|
65 |
+
)
|
66 |
+
transcription = whisper("yt_audio.mp4",
|
67 |
+
chunk_length_s=30,
|
68 |
+
stride_length_s=5,
|
69 |
+
batch_size=8)
|
70 |
+
|
71 |
+
with open("yt_transcription.txt","a") as f:
|
72 |
+
f.write(transcription['text'])
|
73 |
+
|
74 |
+
def get_vectorstore():
|
75 |
+
model_name = "BAAI/bge-small-en"
|
76 |
+
model_kwargs = {"device": "cpu"}
|
77 |
+
encode_kwargs = {"normalize_embeddings": True}
|
78 |
+
hf = HuggingFaceBgeEmbeddings(
|
79 |
+
model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs
|
80 |
+
)
|
81 |
+
|
82 |
+
f = open("yt_transcription.txt", "r")
|
83 |
+
data = f.read()
|
84 |
+
text_splitter = RecursiveCharacterTextSplitter(
|
85 |
+
chunk_size=VECTOR_MAX_TOKENS,
|
86 |
+
chunk_overlap=VECTORS_TOKEN_OVERLAP_SIZE,
|
87 |
+
length_function=tiktoken_len,
|
88 |
+
separators=["\n\n\n","\n\n", "\n", " ", ""]
|
89 |
+
)
|
90 |
+
|
91 |
+
all_splits = text_splitter.split_text(data)
|
92 |
+
docs = [Document(page_content=t) for t in all_splits]
|
93 |
+
vectorstore = Chroma.from_texts(texts=all_splits ,embedding=hf)
|
94 |
+
return vectorstore,docs
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit
|
2 |
+
langchain
|
3 |
+
langchain_groq
|
4 |
+
python-dotenv
|
5 |
+
langchain_community
|
6 |
+
langchain_chroma
|
7 |
+
tiktoken
|
8 |
+
sentence_transformers
|
9 |
+
pytube
|
yt_audio.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:6f5ae1feffab8b4d9bd71706714a037e0fdbdead74e51f6ba388aa7ef2980ee0
|
3 |
+
size 1160761
|
yt_transcription.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Title: Marvel Studios' AVENGERS 5: THE KANG DYNASTY - Teaser Trailer (2025), Total Time: 190 seconds, Number of views: 5070681, Rating: NoneDescription: None Uploader: Screen Culture Upload Date: 2023-01-11 00:00:00
|
2 |
+
Thumbnail URL: https://i.ytimg.com/vi/3UQN_dgXsHA/hq720.jpg
|
3 |
+
Channel URL: https://www.youtube.com/channel/UCGA2OgjW608QEaGwxA7aRTg
|
4 |
+
Age Restricted: False
|
5 |
+
|
6 |
+
We try to help people. Comes a lot of sacrifices. There's something you lose. Every night, the same dream. And every morning, the same nightmare. I get scared. We need new heroes. If you want them to be yours one day, you have to show me you're strong enough to carry them. The world has been forever changed. The world's broken. Everybody's just in with somebody that takes it. But was there I could have stopped it, I could have. I could have changed it. I've never experienced loss because I've never had a loved one to lose. I've only arrived at this fake monster in life. Oh my god. What is it? He's found everything. You see, in everything, you know who's everything. Someone is coming. Countless different versions of a very dangerous person and they're all sat on war. We need to prepare. You're in the venture. Who are you? I'm mad. Don can give you the one thing you want. I'm here on this guy, his. I just lost so much. We're in the end game now. Is this gonna work for anyone. Everyone just interests us. It's just him. I'm done running. You thought you could be? You Thank you.
|