Skip to content

Commit cf57542

Browse files
committed
Add caching for summaries
1 parent e3bcae8 commit cf57542

3 files changed

Lines changed: 215 additions & 183 deletions

File tree

youtubevideotranscriptbot/database.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import mysql.connector.pooling
33
from mysql.connector import Error
44
from config import DB_CONFIG
5+
from config import MODEL_TO_USE
56
import logging
67
import asyncio
78
from contextlib import contextmanager
@@ -158,3 +159,30 @@ async def store_summarization_request_async(user_id, video_id, language, transcr
158159
loop = asyncio.get_event_loop()
159160
await loop.run_in_executor(None, store_summarization_request, user_id, video_id, language, transcript_request_id, tokens_used, estimated_cost, word_count, status, model, summary)
160161

162+
def get_summary_by_video_language(video_id, language, model=MODEL_TO_USE):
163+
"""
164+
Retrieves the summary from summarization_requests table for a given video_id, language, and status 'completed'.
165+
Returns the summary string if found, otherwise None.
166+
"""
167+
try:
168+
with db_cursor() as cursor:
169+
query = """
170+
SELECT summary FROM summarization_requests
171+
WHERE video_id = %s AND language = %s AND model = %s AND status = 'completed'
172+
ORDER BY id DESC LIMIT 1
173+
"""
174+
cursor.execute(query, (video_id, language, model))
175+
result = cursor.fetchone()
176+
if result:
177+
logger.info(f"Summary fetched for video_id={video_id}, language={language}")
178+
return result[0]
179+
else:
180+
logger.info(f"Summary NOT found for video_id={video_id}, language={language}")
181+
return None
182+
except Exception as e:
183+
logger.error(f"Failed to fetch summary for video_id={video_id}, language={language}: {e}")
184+
return None
185+
186+
async def get_summary_by_video_language_async(video_id, language, model=MODEL_TO_USE):
187+
loop = asyncio.get_event_loop()
188+
return await loop.run_in_executor(None, get_summary_by_video_language, video_id, language, model)

youtubevideotranscriptbot/summarize.py

Lines changed: 83 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,27 @@
55
from translate import translate_text # Import the translation function
66
from config import OPENAI_API_KEY
77
from config import DEEPSEEK_API_KEY
8+
from config import MODEL_TO_USE # Import the model selection
89
import tiktoken
910
from translate import split_text
1011
import math
12+
import asyncio
1113

1214
# Set up OpenAI
1315
# openai.api_key = OPENAI_API_KEY
1416

1517
logger = logging.getLogger(__name__)
1618

17-
model_to_use = 2 # 1 for OpenAI, 2 for DeepSeek
19+
if MODEL_TO_USE:
20+
if "gpt" in MODEL_TO_USE.lower():
21+
model_to_use = 1 # OpenAI model
22+
elif "deepseek" in MODEL_TO_USE.lower():
23+
model_to_use = 2
24+
else:
25+
logger.error("Invalid model selection in config. Please select 'gpt' for OpenAI or 'deepseek' for DeepSeek.")
26+
logger.warning("Falling back to DeepSeek model as default.")
27+
model_to_use = 2 # 1 for OpenAI, 2 for DeepSeek
28+
raise ValueError("Invalid model selection in config. Please select 'gpt' for OpenAI or 'deepseek' for DeepSeek.")
1829

1930
if model_to_use == 1:
2031
tokens_per_chunk = 100000
@@ -35,8 +46,58 @@
3546
logger.error("Invalid model selection. Please select 1 for OpenAI or 2 for DeepSeek.")
3647

3748

49+
async def summarize_chunk(chunk, language):
50+
loop = asyncio.get_event_loop()
51+
return await loop.run_in_executor(None, _summarize_sync, chunk, language)
52+
53+
def _summarize_sync(chunk, language):
54+
"""
55+
Synchronously summarizes a chunk of text using the OpenAI API.
56+
57+
Args:
58+
chunk (str): A chunk of text to summarize.
59+
language (str): The language of the text.
60+
61+
Returns:
62+
summary (str): The summarized text.
63+
tokens_used (int): The number of tokens used for the OpenAI API call.
64+
estimated_cost (float): The estimated cost of the OpenAI API call.
65+
word_count (int): The word count of the summary.
66+
"""
67+
try:
68+
# Define the prompt for summarization
69+
system_role = "You are a master of extracting pearls of knowledge from YouTube video transcripts. You grasp the very essence and distill it in a concise form for users. You always provide the response in the same language in which the transcript is provided. Your answers are always clear, concise and nicely formatted."
70+
prompt = (
71+
f"If I did not have time to read this YouTube video transcript, what are the most important things I absolutely must know. Enlighten me in no more than 200 words. "
72+
f"Always provide your response in the same language as the transcript. In this case it might be in '{language}' language. Here is the transcript itself:\n\n{chunk}"
73+
)
74+
75+
response = client.chat.completions.create(
76+
model=model,
77+
messages=[
78+
{"role": "system", "content": system_role},
79+
{"role": "user", "content": prompt},
80+
],
81+
max_tokens=max_tokens,
82+
temperature=0.5,
83+
stream=False
84+
)
85+
86+
# Access the response attributes correctly
87+
summary = response.choices[0].message.content
88+
tokens_used = response.usage.total_tokens
89+
estimated_cost = (tokens_used / 1000) * 0.002 # Adjust based on DeepSeek pricing
90+
word_count = len(summary.split())
91+
92+
return summary, tokens_used, estimated_cost, word_count
93+
94+
except Exception as e:
95+
logger.error(f"Summarization failed for chunk: {e}")
96+
raise
97+
98+
3899
# Summarize text using OpenAI GPT
39-
def summarize_text(text, language, num_key_points=3):
100+
async def summarize_text(text, language, num_key_points=3):
40101
"""
41102
Summarizes the given text in the specified language.
42103
@@ -82,103 +143,32 @@ def summarize_text(text, language, num_key_points=3):
82143
total_estimated_cost = 0.0
83144
total_word_count = 0
84145

85-
for chunk in chunks:
86-
try:
87-
# Define the prompt for summarization
88-
system_role = "You are a master of extracting pearls of knowledge from YouTube video transcripts. You grasp the very essence and distill it in a concise form for users. You always provide the response in the same language in which the transcript is provided. Your answers are always clear, concise and nicely formatted."
89-
prompt = (
90-
f"If I did not have time to read this YouTube video transcript, what are the most important things I absolutely must know. Enlighten me in no more than 200 words. "
91-
f"Always provide your response in the same language as the transcript. In this case it might be in '{language}' language. Here is the transcript itself:\n\n{chunk}"
92-
)
93-
94-
response = client.chat.completions.create(
95-
model=model,
96-
messages=[
97-
{"role": "system", "content": system_role},
98-
{"role": "user", "content": prompt},
99-
],
100-
max_tokens=max_tokens,
101-
temperature=0.5,
102-
stream=False
103-
)
104-
105-
# if model_to_use == 1:
106-
# # Call the OpenAI API
107-
# response = openai.ChatCompletion.create(
108-
# model="gpt-3.5-turbo",
109-
# messages=[
110-
# {"role": "system", "content": system_role},
111-
# {"role": "user", "content": prompt}
112-
# ],
113-
# max_tokens=500, # Increased token limit to ensure the summary is complete
114-
# temperature=0.5
115-
# )
116-
# elif model_to_use == 2:
117-
# # DeepSeek API
118-
# response = client.chat.completions.create(
119-
# model="deepseek-chat",
120-
# messages=[
121-
# {"role": "system", "content": system_role},
122-
# {"role": "user", "content": prompt},
123-
# ],
124-
# max_tokens=1024,
125-
# temperature=0.5,
126-
# stream=False
127-
# )
128-
129-
# else:
130-
# logger.error("Invalid model selection. Please select 1 for OpenAI or 2 for DeepSeek.")
131-
132-
# summary = response['choices'][0]['message']['content']
133-
# tokens_used = response['usage']['total_tokens']
134-
# estimated_cost = (tokens_used / 1000) * 0.002 # Adjust based on GPT pricing
135-
# word_count = len(summary.split())
136-
137-
# Access the response attributes correctly
138-
summary = response.choices[0].message.content
139-
tokens_used = response.usage.total_tokens
140-
estimated_cost = (tokens_used / 1000) * 0.002 # Adjust based on DeepSeek pricing
141-
word_count = len(summary.split())
146+
try:
147+
tasks = [summarize_chunk(chunk, language) for chunk in chunks]
148+
results = await asyncio.gather(*tasks)
149+
except Exception as e:
150+
logger.error(f"Summarization process failed due to: {e}")
151+
raise
142152

153+
try:
154+
for summary, tokens_used, estimated_cost, word_count in results:
143155
combined_summary += summary + "\n\n"
144156
total_tokens_used += tokens_used
145157
total_estimated_cost += estimated_cost
146158
total_word_count += word_count
147-
logger.info(f"Chunk summarization successful: {word_count} words, {tokens_used} tokens, cost: ${estimated_cost:.2f}")
148-
except Exception as e:
149-
logger.error(f"Summarization failed for chunk: {e}")
150-
raise
151-
159+
logger.info(f"Summarization successful: {word_count} words, {tokens_used} tokens, cost: ${estimated_cost:.2f}")
160+
except Exception as e:
161+
logger.error(f"Summarization failed: {e}")
162+
raise
152163

153-
logger.info(f"Summarization completed for all chunks.")
164+
logger.info(f"Summarization completed for all chunks.\nTotal word count: {total_word_count}.\nTotal tokens used: {total_tokens_used}.\nEstimated cost: ${total_estimated_cost:.2f}")
154165

155166
return combined_summary.strip(), total_tokens_used, total_estimated_cost, total_word_count
156167

157-
# try:
158-
# # Define the prompt for summarization
159-
# prompt = (
160-
# f"If I did not have time to read this YouTube video transcript, what are the most important things I absolutely must know. Enlighten me in no more than 200 words. "
161-
# f"Always provide your response in the same language as the transcript. In this case it might be in '{language}' language. Here is the transcript itself:\n\n{text}"
162-
# )
163-
164-
# # Call the OpenAI API
165-
# response = openai.ChatCompletion.create(
166-
# model="gpt-3.5-turbo",
167-
# messages=[
168-
# {"role": "system", "content": "You are a master of extracting pearls of knowledge from YouTube video transcripts. You grasp the very essense and distill it in a consice form for users. You always provide the response in the same language in which the transcript is provided. Your answers are always clear, consise and nicely formatted."},
169-
# {"role": "user", "content": prompt}
170-
# ],
171-
# max_tokens=500, # Increased token limit to ensure the summary is complete
172-
# temperature=0.5
173-
# )
174-
# summary = response['choices'][0]['message']['content']
175-
# tokens_used = response['usage']['total_tokens']
176-
# estimated_cost = (tokens_used / 1000) * 0.002 # Adjust based on GPT pricing
177-
# word_count = len(summary.split())
178-
# return summary, tokens_used, estimated_cost, word_count
179-
# except Exception as e:
180-
# logger.error(f"Summarization failed: {e}")
181-
# raise
168+
169+
async def translate_summary_async(summary, src_lang, dest_lang):
170+
loop = asyncio.get_event_loop()
171+
return await loop.run_in_executor(None, translate_summary, summary, src_lang, dest_lang)
182172

183173
# Translate the summary into the target language
184174
def translate_summary(summary, src_lang, dest_lang):
@@ -203,7 +193,7 @@ def translate_summary(summary, src_lang, dest_lang):
203193
raise
204194

205195
# Handle summarization request
206-
def handle_summarization_request(text, original_language, target_language, num_key_points=3):
196+
async def handle_summarization_request(text, original_language, target_language, num_key_points=3):
207197
"""
208198
Handles the summarization request.
209199
@@ -224,15 +214,15 @@ def handle_summarization_request(text, original_language, target_language, num_k
224214

225215
try:
226216
# Summarize the original transcript
227-
summary, tokens_used, estimated_cost, word_count = summarize_text(text, original_language, num_key_points)
217+
summary, tokens_used, estimated_cost, word_count = await summarize_text(text, original_language, num_key_points)
228218

229219
if target_language == 'orig':
230220
target_language = original_language
231221
logger.info(f"Summary generated in {target_language} language. Translation skipped.")
232222

233223
# Translate the summary if the target language is not the original language
234224
if target_language != original_language:
235-
summary = translate_summary(summary, src_lang=original_language, dest_lang=target_language)
225+
summary = await translate_summary_async(summary, src_lang=original_language, dest_lang=target_language)
236226

237227
return summary, tokens_used, estimated_cost, word_count, model
238228
except Exception as e:

0 commit comments

Comments
 (0)