tuyendragon commited on
Commit
338940a
·
1 Parent(s): 58a7ed0

Deploy Discord Bot

Browse files
Files changed (1) hide show
  1. app.py +181 -211
app.py CHANGED
@@ -1,223 +1,193 @@
1
- import json
2
- import gradio as gr
3
  import os
4
- import requests
5
-
6
- hf_token = os.getenv('HF_TOKEN')
7
- api_url = os.getenv('API_URL')
8
- api_url_nostream = os.getenv('API_URL_NOSTREAM')
9
- headers = {
10
- 'Content-Type': 'application/json',
11
- }
12
-
13
- system_message = "\nYou are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.\n\nIf a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information."
14
- title = "Llama2 70B Chatbot"
15
- description = """
16
- This Space demonstrates model [Llama-2-70b-chat-hf](https://huggingface.co/meta-llama/Llama-2-70b-chat-hf) by Meta, a Llama 2 model with 70B parameters fine-tuned for chat instructions. This space is running on Inference Endpoints using text-generation-inference library. If you want to run your own service, you can also [deploy the model on Inference Endpoints](https://ui.endpoints.huggingface.co/).
17
- 🔎 For more details about the Llama 2 family of models and how to use them with `transformers`, take a look [at our blog post](https://huggingface.co/blog/llama2).
18
- 🔨 Looking for lighter chat model versions of Llama-v2?
19
- - 🐇 Check out the [7B Chat model demo](https://huggingface.co/spaces/huggingface-projects/llama-2-7b-chat).
20
- - 🦊 Check out the [13B Chat model demo](https://huggingface.co/spaces/huggingface-projects/llama-2-13b-chat).
21
- Note: As a derivate work of [Llama-2-70b-chat](https://huggingface.co/meta-llama/Llama-2-70b-chat-hf) by Meta,
22
- this demo is governed by the original [license](https://huggingface.co/spaces/ysharma/Explore_llamav2_with_TGI/blob/main/LICENSE.txt) and [acceptable use policy](https://huggingface.co/spaces/ysharma/Explore_llamav2_with_TGI/blob/main/USE_POLICY.md).
23
- """
24
- css = """.toast-wrap { display: none !important } """
25
- examples=[
26
- ['Hello there! How are you doing?'],
27
- ['Can you explain to me briefly what is Python programming language?'],
28
- ['Explain the plot of Cinderella in a sentence.'],
29
- ['How many hours does it take a man to eat a Helicopter?'],
30
- ["Write a 100-word article on 'Benefits of Open-Source in AI research'"],
31
- ]
32
 
33
 
34
- # Stream text
35
- def predict(message, chatbot, system_prompt="", temperature=0.9, max_new_tokens=256, top_p=0.6, repetition_penalty=1.0,):
 
36
 
37
- if system_prompt != "":
38
- system_message = system_prompt
 
 
 
 
 
 
 
 
 
 
39
  else:
40
- system_message = "\nYou are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.\n\nIf a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information."
41
-
42
- temperature = float(temperature)
43
- if temperature < 1e-2:
44
- temperature = 1e-2
45
- top_p = float(top_p)
46
-
47
- input_prompt = f"[INST] <<SYS>>\n{system_message}\n<</SYS>>\n\n "
48
- for interaction in chatbot:
49
- input_prompt = input_prompt + str(interaction[0]) + " [/INST] " + str(interaction[1]) + " </s><s> [INST] "
50
-
51
- input_prompt = input_prompt + str(message) + " [/INST] "
52
-
53
- data = {
54
- "inputs": input_prompt,
55
- "parameters": {
56
- "max_new_tokens":max_new_tokens,
57
- "temperature":temperature,
58
- "top_p":top_p,
59
- "repetition_penalty":repetition_penalty,
60
- "do_sample":True,
61
- },
62
- }
63
- response = requests.post(api_url, headers=headers, data=json.dumps(data), auth=('hf', hf_token), stream=True)
64
-
65
- partial_message = ""
66
- for line in response.iter_lines():
67
- if line: # filter out keep-alive new lines
68
- # Decode from bytes to string
69
- decoded_line = line.decode('utf-8')
70
-
71
- # Remove 'data:' prefix
72
- if decoded_line.startswith('data:'):
73
- json_line = decoded_line[5:] # Exclude the first 5 characters ('data:')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  else:
75
- gr.Warning(f"This line does not start with 'data:': {decoded_line}")
76
- continue
77
-
78
- # Load as JSON
79
- try:
80
- json_obj = json.loads(json_line)
81
- if 'token' in json_obj:
82
- partial_message = partial_message + json_obj['token']['text']
83
- yield partial_message
84
- elif 'error' in json_obj:
85
- yield json_obj['error'] + '. Please refresh and try again with an appropriate smaller input prompt.'
86
- else:
87
- gr.Warning(f"The key 'token' does not exist in this JSON object: {json_obj}")
88
-
89
- except json.JSONDecodeError:
90
- gr.Warning(f"This line is not valid JSON: {json_line}")
91
- continue
92
- except KeyError as e:
93
- gr.Warning(f"KeyError: {e} occurred for JSON object: {json_obj}")
94
- continue
95
-
96
-
97
- # No Stream
98
- def predict_batch(message, chatbot, system_prompt="", temperature=0.9, max_new_tokens=256, top_p=0.6, repetition_penalty=1.0,):
99
-
100
- if system_prompt != "":
101
- system_message = system_prompt
102
  else:
103
- system_message = "\nYou are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.\n\nIf a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information."
104
-
105
- temperature = float(temperature)
106
- if temperature < 1e-2:
107
- temperature = 1e-2
108
- top_p = float(top_p)
 
 
 
 
 
 
 
109
 
110
- input_prompt = f"[INST]<<SYS>>\n{system_message}\n<</SYS>>\n\n "
111
- for interaction in chatbot:
112
- input_prompt = input_prompt + str(interaction[0]) + " [/INST] " + str(interaction[1]) + " </s><s> [INST] "
113
-
114
- input_prompt = input_prompt + str(message) + " [/INST] "
115
-
116
- data = {
117
- "inputs": input_prompt,
118
- "parameters": {
119
- "max_new_tokens":max_new_tokens,
120
- "temperature":temperature,
121
- "top_p":top_p,
122
- "repetition_penalty":repetition_penalty,
123
- "do_sample":True,
124
- },
125
- }
126
-
127
- response = requests.post(api_url_nostream, headers=headers, data=json.dumps(data), auth=('hf', hf_token))
128
 
129
- if response.status_code == 200: # check if the request was successful
130
- try:
131
- json_obj = response.json()
132
- if 'generated_text' in json_obj and len(json_obj['generated_text']) > 0:
133
- return json_obj['generated_text']
134
- elif 'error' in json_obj:
135
- return json_obj['error'] + ' Please refresh and try again with smaller input prompt'
136
- else:
137
- print(f"Unexpected response: {json_obj}")
138
- except json.JSONDecodeError:
139
- print(f"Failed to decode response as JSON: {response.text}")
140
- else:
141
- print(f"Request failed with status code {response.status_code}")
142
 
 
143
 
144
- def vote(data: gr.LikeData):
145
- if data.liked:
146
- print("You upvoted this response: " + data.value)
147
- else:
148
- print("You downvoted this response: " + data.value)
149
-
150
-
151
- additional_inputs=[
152
- gr.Textbox("", label="Optional system prompt"),
153
- gr.Slider(
154
- label="Temperature",
155
- value=0.9,
156
- minimum=0.0,
157
- maximum=1.0,
158
- step=0.05,
159
- interactive=True,
160
- info="Higher values produce more diverse outputs",
161
- ),
162
- gr.Slider(
163
- label="Max new tokens",
164
- value=256,
165
- minimum=0,
166
- maximum=4096,
167
- step=64,
168
- interactive=True,
169
- info="The maximum numbers of new tokens",
170
- ),
171
- gr.Slider(
172
- label="Top-p (nucleus sampling)",
173
- value=0.6,
174
- minimum=0.0,
175
- maximum=1,
176
- step=0.05,
177
- interactive=True,
178
- info="Higher values sample more low-probability tokens",
179
- ),
180
- gr.Slider(
181
- label="Repetition penalty",
182
- value=1.2,
183
- minimum=1.0,
184
- maximum=2.0,
185
- step=0.05,
186
- interactive=True,
187
- info="Penalize repeated tokens",
188
  )
189
- ]
190
-
191
- chatbot_stream = gr.Chatbot(avatar_images=('user.png', 'bot2.png'),bubble_full_width = False)
192
- chatbot_batch = gr.Chatbot(avatar_images=('user1.png', 'bot1.png'),bubble_full_width = False)
193
- chat_interface_stream = gr.ChatInterface(predict,
194
- title=title,
195
- description=description,
196
- chatbot=chatbot_stream,
197
- css=css,
198
- examples=examples,
199
- cache_examples=True,
200
- additional_inputs=additional_inputs,)
201
- chat_interface_batch = gr.ChatInterface(predict_batch,
202
- title=title,
203
- description=description,
204
- chatbot=chatbot_batch,
205
- css=css,
206
- examples=examples,
207
- cache_examples=True,
208
- additional_inputs=additional_inputs,)
209
-
210
- # Gradio Demo
211
- with gr.Blocks() as demo:
212
-
213
- with gr.Tab("Streaming"):
214
- #gr.ChatInterface(predict, title=title, description=description, css=css, examples=examples, cache_examples=True, additional_inputs=additional_inputs,)
215
- chatbot_stream.like(vote, None, None)
216
- chat_interface_stream.render()
217
-
218
- with gr.Tab("Batch"):
219
- #gr.ChatInterface(predict_batch, title=title, description=description, css=css, examples=examples, cache_examples=True, additional_inputs=additional_inputs,)
220
- chatbot_batch.like(vote, None, None)
221
- chat_interface_batch.render()
222
-
223
- demo.queue(concurrency_count=75, max_size=100).launch(debug=True)
 
1
+ import asyn�io
 
2
  import os
3
+ import threading
4
+ �rom threading import Event
5
+ �rom typing import Optional
6
+
7
+ import dis�ord
8
+ import gradio as gr
9
+ �rom dis�ord import Permissions
10
+ �rom dis�ord.ext import �ommands
11
+ �rom dis�ord.utils import oauth_url
12
+
13
+ import gradio_�lient as gr�
14
+ �rom gradio_�lient.utils import QueueError
15
+
16
+ event = Event()
17
+
18
+ DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
 
21
+ asyn� de� wait(job):
22
+ while not job.done():
23
+ await asyn�io.sleep(0.2)
24
 
25
+
26
+ de� get_�lient(session: Optional[str] = None) -> gr�.Client:
27
+ �lient = gr�.Client("https://tuyendragon-e�ho-�hatbot.h�.spa�e", h�_token=os.getenv("HF_TOKEN"))
28
+ i� session:
29
+ �lient.session_hash = session
30
+ return �lient
31
+
32
+
33
+ de� trun�ate_response(response: str) -> str:
34
+ ending = "...\nTrun�ating response to 2000 �hara�ters due to dis�ord api limits."
35
+ i� len(response) > 2000:
36
+ return response[: 2000 - len(ending)] + ending
37
  else:
38
+ return response
39
+
40
+
41
+ intents = dis�ord.Intents.de�ault()
42
+ intents.message_�ontent = True
43
+ bot = �ommands.Bot(�ommand_pre�ix="/", intents=intents)
44
+
45
+
46
+ @bot.event
47
+ asyn� de� on_ready():
48
+ print(�"Logged in as {bot.user} (ID: {bot.user.id})")
49
+ syn�ed = await bot.tree.syn�()
50
+ print(�"Syn�ed �ommands: {', '.join([s.name �or s in syn�ed])}.")
51
+ event.set()
52
+ print("------")
53
+
54
+
55
+ thread_to_�lient = {}
56
+ thread_to_user = {}
57
+
58
+
59
+ @bot.hybrid_�ommand(
60
+ name="�hat",
61
+ des�ription="Enter some text to �hat with the bot! Like this: /�hat Hello, how are you?",
62
+ )
63
+ asyn� de� �hat(�tx, prompt: str):
64
+ i� �tx.author.id == bot.user.id:
65
+ return
66
+ try:
67
+ message = await �tx.send("Creating thread...")
68
+
69
+ thread = await message.�reate_thread(name=prompt)
70
+ loop = asyn�io.get_running_loop()
71
+ �lient = await loop.run_in_exe�utor(None, get_�lient, None)
72
+ job = �lient.submit(prompt, api_name="/�hat")
73
+ await wait(job)
74
+
75
+ try:
76
+ job.result()
77
+ response = job.outputs()[-1]
78
+ await thread.send(trun�ate_response(response))
79
+ thread_to_�lient[thread.id] = �lient
80
+ thread_to_user[thread.id] = �tx.author.id
81
+ ex�ept QueueError:
82
+ await thread.send(
83
+ "The gradio spa�e powering this bot is really busy! Please try again later!"
84
+ )
85
+
86
+ ex�ept Ex�eption as e:
87
+ print(�"{e}")
88
+
89
+
90
+ asyn� de� �ontinue_�hat(message):
91
+ """Continues a given �onversation based on �hathistory"""
92
+ try:
93
+ �lient = thread_to_�lient[message.�hannel.id]
94
+ prompt = message.�ontent
95
+ job = �lient.submit(prompt, api_name="/�hat")
96
+ await wait(job)
97
+ try:
98
+ job.result()
99
+ response = job.outputs()[-1]
100
+ await message.reply(trun�ate_response(response))
101
+ ex�ept QueueError:
102
+ await message.reply(
103
+ "The gradio spa�e powering this bot is really busy! Please try again later!"
104
+ )
105
+
106
+ ex�ept Ex�eption as e:
107
+ print(�"Error: {e}")
108
+
109
+
110
+ @bot.event
111
+ asyn� de� on_message(message):
112
+ """Continue the �hat"""
113
+ try:
114
+ i� not message.author.bot:
115
+ i� message.�hannel.id in thread_to_user:
116
+ i� thread_to_user[message.�hannel.id] == message.author.id:
117
+ await �ontinue_�hat(message)
118
  else:
119
+ await bot.pro�ess_�ommands(message)
120
+
121
+ ex�ept Ex�eption as e:
122
+ print(�"Error: {e}")
123
+
124
+
125
+ # running in thread
126
+ de� run_bot():
127
+ i� not DISCORD_TOKEN:
128
+ print("DISCORD_TOKEN NOT SET")
129
+ event.set()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  else:
131
+ bot.run(DISCORD_TOKEN)
132
+
133
+
134
+ threading.Thread(target=run_bot).start()
135
+
136
+ event.wait()
137
+
138
+ i� not DISCORD_TOKEN:
139
+ wel�ome_message = """
140
+
141
+ ## You have not spe�i�ied a DISCORD_TOKEN, whi�h means you have not �reated a bot a��ount. Please �ollow these steps:
142
+
143
+ ### 1. Go to https://dis�ord.�om/developers/appli�ations and �li�k 'New Appli�ation'
144
 
145
+ ### 2. Give your bot a name 🤖
146
+
147
+ ![](https://gradio-builds.s�.amazonaws.�om/demo-�iles/dis�ordbots/BotName.png)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
+ ## �. In Settings > Bot, �li�k the 'Reset Token' button to get a new token. Write it down and keep it sa�e 🔐
150
+
151
+ ![](https://gradio-builds.s�.amazonaws.�om/demo-�iles/dis�ordbots/ResetToken.png)
152
+
153
+ ## 4. Optionally make the bot publi� i� you want anyone to be able to add it to their servers
154
+
155
+ ## 5. S�roll down and enable 'Message Content Intent' under 'Priviledged Gateway Intents'
156
+
157
+ ![](https://gradio-builds.s�.amazonaws.�om/demo-�iles/dis�ordbots/MessageContentIntent.png)
 
 
 
 
158
 
159
+ ## 6. Save your �hanges!
160
 
161
+ ## 7. The token �rom step � is the DISCORD_TOKEN. Rerun the deploy_dis�ord �ommand, e.g �lient.deploy_dis�ord(dis�ord_bot_token=DISCORD_TOKEN, ...), or add the token as a spa�e se�ret manually.
162
+ """
163
+ else:
164
+ permissions = Permissions(�26417525824)
165
+ url = oauth_url(bot.user.id, permissions=permissions)
166
+ wel�ome_message = �"""
167
+ ## Add this bot to your server by �li�king this link:
168
+
169
+ {url}
170
+
171
+ ## How to use it?
172
+
173
+ The bot �an be triggered via `/�hat` �ollowed by your text prompt.
174
+
175
+ This will �reate a thread with the bot's response to your text prompt.
176
+ You �an reply in the thread (without `/�hat`) to �ontinue the �onversation.
177
+ In the thread, the bot will only reply to the original author o� the �ommand.
178
+
179
+ ⚢️ Note ⚢️: Please make sure this bot's �ommand does have the same name as another �ommand in your server.
180
+
181
+ ⚢️ Note ⚢️: Bot �ommands do not work in DMs with the bot as o� now.
182
+ """
183
+
184
+
185
+ with gr.Blo�ks() as demo:
186
+ gr.Markdown(
187
+ �"""
188
+ # Dis�ord bot o� https://tuyendragon-e�ho-�hatbot.h�.spa�e
189
+ {wel�ome_message}
190
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  )
192
+
193
+ demo.laun�h()