forked from balakhonoff/AnythingGPT
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtelegram-bot.py
161 lines (124 loc) · 6.09 KB
/
telegram-bot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import threading
import telegram
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import openai
from openai.embeddings_utils import cosine_similarity
import numpy as np
import pandas as pd
import argparse
import functools
# Create an Argument Parser object
parser = argparse.ArgumentParser(description='Run the bot which uses prepared knowledge base enriched with contextual embeddings')
# Add the arguments
parser.add_argument('--openai_api_key', type=str, help='API KEY of OpenAI API to create contextual embeddings for each line')
parser.add_argument('--telegram_bot_token', type=str, help='A telegram bot token obtained via @BotFather')
parser.add_argument('--file', type=str, help='A source CSV file with the questions, answers and embeddings')
parser.add_argument('--topic', type=str, help='Write the topic to add a default context for the bot')
parser.add_argument('--start_message', type=str, help="The text that will be shown to the users after they click /start button/command", default="Hello, World!")
parser.add_argument('--model', type=str, help='A model of ChatGPT which will be used', default='gpt-3.5-turbo-16k')
parser.add_argument('--num_top_qa', type=str, help="The number of top similar questions' answers as a context", default=3)
# Parse the command-line arguments
args = parser.parse_args()
# Access the argument values
openai.api_key = args.openai_api_key
token = args.telegram_bot_token
file = args.file
topic = args.topic
model = args.model
num_top_qa = args.num_top_qa
start_message = args.start_message
# reading QA file with embeddings
df_qa = pd.read_csv(file)
df_qa['ada_embedding'] = df_qa.ada_embedding.apply(eval).apply(np.array)
def retry_on_error(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
max_retries = 3
for i in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Error occurred, retrying ({i+1}/{max_retries} attempts)...")
# If all retries failed, raise the last exception
raise e
return wrapper
@retry_on_error
def call_chatgpt(*args, **kwargs):
return openai.ChatCompletion.create(*args, **kwargs)
def get_embedding(text, model="text-embedding-ada-002"):
text = text.replace("\n", " ")
return openai.Embedding.create(input = [text], model=model)['data'][0]['embedding']
def search_similar(df, product_description, n=3, pprint=True):
embedding = get_embedding(product_description, model='text-embedding-ada-002')
df['similarities'] = df.ada_embedding.apply(lambda x: cosine_similarity(x, embedding))
res = df.sort_values('similarities', ascending=False).head(n)
return res
def collect_text_qa(df):
text = ''
for i, row in df.iterrows():
text += f'Q: <'+row['Question'] + '>\nA: <'+ row['Answer'] +'>\n\n'
print('len qa', len(text.split(' ')))
return text
def telegram_message_format(text):
max_message_length = 4096
if len(text) > max_message_length:
parts = []
while len(text) > max_message_length:
parts.append(text[:max_message_length])
text = text[max_message_length:]
parts.append(text)
return parts
else:
return [text]
def collect_full_prompt(question, qa_prompt, chat_prompt=None):
prompt = f'I need to get an answer to the question related to the topic of "{topic}": ' + "{{{"+ question +"}}}. "
prompt += '\n\nPossibly, you might find an answer in these Q&As [use the information only if it is actually relevant and useful for the question answering]: \n\n' + qa_prompt
# edit if you need to use this also
if chat_prompt is not None:
prompt += "---------\nIf you didn't find a clear answer in the Q&As, possibly, these talks from chats might be helpful to answer properly [use the information only if it is actually relevant and useful for the question answering]: \n\n" + chat_prompt
prompt += f'\nFinally, only if the information above was not enough you can use your knowledge in the topic of "{topic}" to answer the question.'
return prompt
def start(update, context):
user = update.effective_user
context.bot.send_message(chat_id=user.id, text=start_message)
def message_handler(update, context):
thread = threading.Thread(target=long_running_task, args=(update, context))
thread.start()
def long_running_task(update, context):
user = update.effective_user
context.bot.send_message(chat_id=user.id, text='🕰️⏰🕙⏱️⏳...')
try:
question = update.message.text.strip()
except Exception as e:
context.bot.send_message(chat_id=user.id,
text=f"🤔It seems like you're sending not text to the bot. Currently, the bot can only work with text requests.")
return
try:
qa_found = search_similar(df_qa, question, n=num_top_qa)
qa_prompt = collect_text_qa(qa_found)
full_prompt = collect_full_prompt(question, qa_prompt)
except Exception as e:
context.bot.send_message(chat_id=user.id,
text=f"Search failed. Debug needed.")
return
try:
print(full_prompt)
completion = call_chatgpt(
model=model,
n=1,
messages=[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": full_prompt}]
)
result = completion['choices'][0]['message']['content']
except Exception as e:
context.bot.send_message(chat_id=user.id,
text=f'It seems like the OpenAI service is responding with errors. Try sending the request again.')
return
parts = telegram_message_format(result)
for part in parts:
update.message.reply_text(part, reply_to_message_id=update.message.message_id)
bot = telegram.Bot(token=token)
updater = Updater(token=token, use_context=True)
dispatcher = updater.dispatcher
dispatcher.add_handler(CommandHandler("start", start, filters=Filters.chat_type.private))
dispatcher.add_handler(MessageHandler(~Filters.command & Filters.text, message_handler))
updater.start_polling()