Spaces:
Sleeping
Sleeping
robinroy03
commited on
Commit
•
3a0abc1
1
Parent(s):
a3c9500
first commit
Browse files- Dockerfile +16 -0
- main.py +150 -0
- requirements.txt +89 -0
Dockerfile
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3
|
2 |
+
|
3 |
+
RUN useradd -m -u 1000 user
|
4 |
+
USER user
|
5 |
+
ENV HOME=/home/user \
|
6 |
+
PATH=/home/user/.local/bin:$PATH
|
7 |
+
|
8 |
+
COPY --chown=user . $HOME/discordbot
|
9 |
+
|
10 |
+
WORKDIR $HOME/discordbot
|
11 |
+
|
12 |
+
RUN mkdir $HOME/.cache
|
13 |
+
|
14 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
15 |
+
|
16 |
+
CMD ["gunicorn", "-w", "1", "-b", "0.0.0.0:7860","main:main"]
|
main.py
ADDED
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import discord
|
2 |
+
import aiohttp
|
3 |
+
|
4 |
+
import ast
|
5 |
+
import os
|
6 |
+
import threading
|
7 |
+
|
8 |
+
|
9 |
+
intents = discord.Intents.default()
|
10 |
+
intents.message_content = True
|
11 |
+
bot = discord.Bot(intents = intents)
|
12 |
+
token = os.environ.get('TOKEN_DISCORD')
|
13 |
+
|
14 |
+
|
15 |
+
class Like_Dislike(discord.ui.View):
|
16 |
+
@discord.ui.button(style=discord.ButtonStyle.primary, emoji="👍")
|
17 |
+
async def like_button(self, button, interaction):
|
18 |
+
await interaction.response.send_message("You liked the response")
|
19 |
+
|
20 |
+
@discord.ui.button(style=discord.ButtonStyle.primary, emoji="👎")
|
21 |
+
async def dislike_button(self, button, interaction):
|
22 |
+
await interaction.response.send_message("You disliked the response")
|
23 |
+
|
24 |
+
|
25 |
+
@bot.event
|
26 |
+
async def on_ready():
|
27 |
+
print(f"{bot.user} is ready and online!")
|
28 |
+
|
29 |
+
|
30 |
+
@bot.slash_command(name="help", description="list of commands and other info.")
|
31 |
+
async def help(ctx: discord.ApplicationContext):
|
32 |
+
await ctx.respond("Hello! FURY Bot responds to all your messages\
|
33 |
+
\n1)Inside Forum channel and\
|
34 |
+
\n2)Those that tag the bot.")
|
35 |
+
|
36 |
+
|
37 |
+
async def llm_output(question: str, context: str) -> str:
|
38 |
+
"""
|
39 |
+
Returns output from the LLM using the given user-question and retrived context
|
40 |
+
"""
|
41 |
+
|
42 |
+
URL_LLM = 'https://robinroy03-fury-bot.hf.space'
|
43 |
+
# URL_LLM = 'http://localhost:11434' # NOTE: FOR TESTING
|
44 |
+
|
45 |
+
prompt = f"""
|
46 |
+
You are a senior FURY developer. FURY is a high level python graphics API similar to VTK.
|
47 |
+
|
48 |
+
Question: {question}
|
49 |
+
|
50 |
+
Context: {context}
|
51 |
+
"""
|
52 |
+
obj = {
|
53 |
+
'model': 'llama3-70b-8192',
|
54 |
+
'prompt': prompt,
|
55 |
+
'stream': False
|
56 |
+
}
|
57 |
+
|
58 |
+
async with aiohttp.ClientSession() as session:
|
59 |
+
async with session.post(URL_LLM + "/api/generate", json=obj) as response:
|
60 |
+
response_json = await response.json()
|
61 |
+
|
62 |
+
return response_json['choices'][0]['message']['content']
|
63 |
+
|
64 |
+
async def embedding_output(message: str) -> list:
|
65 |
+
"""
|
66 |
+
Returns embeddings for the given message
|
67 |
+
|
68 |
+
rtype: list of embeddings. Length depends on the model.
|
69 |
+
"""
|
70 |
+
|
71 |
+
URL_EMBEDDING = 'https://robinroy03-fury-embeddings-endpoint.hf.space'
|
72 |
+
|
73 |
+
async with aiohttp.ClientSession() as session:
|
74 |
+
async with session.post(URL_EMBEDDING + "/embedding", json={"text": message}) as response:
|
75 |
+
response_json = await response.json(content_type=None)
|
76 |
+
|
77 |
+
return response_json['output']
|
78 |
+
|
79 |
+
|
80 |
+
async def db_output(embedding: list) -> dict:
|
81 |
+
"""
|
82 |
+
Returns the KNN results.
|
83 |
+
|
84 |
+
rtype: JSON
|
85 |
+
"""
|
86 |
+
|
87 |
+
URL_DB = 'https://robinroy03-fury-db-endpoint.hf.space'
|
88 |
+
|
89 |
+
async with aiohttp.ClientSession() as session:
|
90 |
+
async with session.post(URL_DB + "/query", json={"embeddings": embedding}) as response:
|
91 |
+
response_json = await response.json()
|
92 |
+
|
93 |
+
return response_json
|
94 |
+
|
95 |
+
|
96 |
+
@bot.event
|
97 |
+
async def on_message(message):
|
98 |
+
"""
|
99 |
+
Returns llm answer with the relevant context.
|
100 |
+
"""
|
101 |
+
|
102 |
+
if (message.author == bot.user) or not(bot.user.mentioned_in(message)):
|
103 |
+
return
|
104 |
+
|
105 |
+
print(message.content)
|
106 |
+
await message.reply(content="Your message was received, it'll take around 30 seconds for FURY to process an answer.")
|
107 |
+
|
108 |
+
question = message.content.replace("<@1243428204124045385>", "")
|
109 |
+
|
110 |
+
embedding: list = await embedding_output(question)
|
111 |
+
|
112 |
+
db_knn: dict = await db_output(embedding)
|
113 |
+
db_context = ""
|
114 |
+
references = ""
|
115 |
+
for i in range(len(db_knn['matches'])):
|
116 |
+
data = db_knn['matches'][i]['metadata']['data']
|
117 |
+
db_context += (data + "\n")
|
118 |
+
data = ast.literal_eval(data)
|
119 |
+
references += ("<https://github.com/fury-gl/fury/tree/master/" + data['path'] + ">")
|
120 |
+
if data.get("function_name"):
|
121 |
+
references += f"\tFunction Name: {data.get('function_name')}"
|
122 |
+
else:
|
123 |
+
references += f"\tClass Name: {data.get('class_name')}"
|
124 |
+
references += "\n"
|
125 |
+
|
126 |
+
llm_answer: str = await llm_output(question, db_context) # for the highest knn result (for the test only right now) TODO: make this better
|
127 |
+
|
128 |
+
try:
|
129 |
+
await message.reply(content=llm_answer[:1990], view=Like_Dislike()) # TODO: handle large responses (>2000)
|
130 |
+
await message.reply(content=f"**References**\n{references}")
|
131 |
+
except Exception as e: # TODO: make exception handling better
|
132 |
+
print(e)
|
133 |
+
await message.reply("An error occurred. Retry again.")
|
134 |
+
|
135 |
+
def run_bot():
|
136 |
+
bot.run(token)
|
137 |
+
|
138 |
+
# ===========================================================================================================================================================
|
139 |
+
|
140 |
+
from flask import Flask
|
141 |
+
|
142 |
+
app = Flask(__name__)
|
143 |
+
|
144 |
+
@app.route("/")
|
145 |
+
def home():
|
146 |
+
return "The bot is online."
|
147 |
+
|
148 |
+
|
149 |
+
threading.Thread(target=run_bot).start()
|
150 |
+
app.run()
|
requirements.txt
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiodns==3.2.0
|
2 |
+
aiofiles==23.2.1
|
3 |
+
aiohttp==3.9.5
|
4 |
+
aiosignal==1.3.1
|
5 |
+
altair==5.3.0
|
6 |
+
annotated-types==0.7.0
|
7 |
+
anyio==4.4.0
|
8 |
+
attrs==23.2.0
|
9 |
+
blinker==1.8.2
|
10 |
+
Brotli==1.1.0
|
11 |
+
certifi==2024.6.2
|
12 |
+
cffi==1.16.0
|
13 |
+
charset-normalizer==3.3.2
|
14 |
+
click==8.1.7
|
15 |
+
contourpy==1.2.1
|
16 |
+
cycler==0.12.1
|
17 |
+
dnspython==2.6.1
|
18 |
+
email_validator==2.1.1
|
19 |
+
fastapi==0.111.0
|
20 |
+
fastapi-cli==0.0.4
|
21 |
+
ffmpy==0.3.2
|
22 |
+
filelock==3.14.0
|
23 |
+
Flask==3.0.3
|
24 |
+
fonttools==4.53.0
|
25 |
+
frozenlist==1.4.1
|
26 |
+
fsspec==2024.6.0
|
27 |
+
gradio==4.33.0
|
28 |
+
gradio_client==0.17.0
|
29 |
+
gunicorn==22.0.0
|
30 |
+
h11==0.14.0
|
31 |
+
httpcore==1.0.5
|
32 |
+
httptools==0.6.1
|
33 |
+
httpx==0.27.0
|
34 |
+
huggingface-hub==0.23.3
|
35 |
+
idna==3.7
|
36 |
+
importlib_resources==6.4.0
|
37 |
+
itsdangerous==2.2.0
|
38 |
+
Jinja2==3.1.4
|
39 |
+
jsonschema==4.22.0
|
40 |
+
jsonschema-specifications==2023.12.1
|
41 |
+
kiwisolver==1.4.5
|
42 |
+
markdown-it-py==3.0.0
|
43 |
+
MarkupSafe==2.1.5
|
44 |
+
matplotlib==3.9.0
|
45 |
+
mdurl==0.1.2
|
46 |
+
msgspec==0.18.6
|
47 |
+
multidict==6.0.5
|
48 |
+
numpy==1.26.4
|
49 |
+
orjson==3.10.3
|
50 |
+
packaging==24.0
|
51 |
+
pandas==2.2.2
|
52 |
+
pillow==10.3.0
|
53 |
+
py-cord==2.5.0
|
54 |
+
pycares==4.4.0
|
55 |
+
pycparser==2.22
|
56 |
+
pydantic==2.7.3
|
57 |
+
pydantic_core==2.18.4
|
58 |
+
pydub==0.25.1
|
59 |
+
Pygments==2.18.0
|
60 |
+
pyparsing==3.1.2
|
61 |
+
python-dateutil==2.9.0.post0
|
62 |
+
python-dotenv==1.0.1
|
63 |
+
python-multipart==0.0.9
|
64 |
+
pytz==2024.1
|
65 |
+
PyYAML==6.0.1
|
66 |
+
referencing==0.35.1
|
67 |
+
requests==2.32.3
|
68 |
+
rich==13.7.1
|
69 |
+
rpds-py==0.18.1
|
70 |
+
ruff==0.4.7
|
71 |
+
semantic-version==2.10.0
|
72 |
+
shellingham==1.5.4
|
73 |
+
six==1.16.0
|
74 |
+
sniffio==1.3.1
|
75 |
+
starlette==0.37.2
|
76 |
+
tomlkit==0.12.0
|
77 |
+
toolz==0.12.1
|
78 |
+
tqdm==4.66.4
|
79 |
+
typer==0.12.3
|
80 |
+
typing_extensions==4.12.1
|
81 |
+
tzdata==2024.1
|
82 |
+
ujson==5.10.0
|
83 |
+
urllib3==2.2.1
|
84 |
+
uvicorn==0.30.1
|
85 |
+
uvloop==0.19.0
|
86 |
+
watchfiles==0.22.0
|
87 |
+
websockets==11.0.3
|
88 |
+
Werkzeug==3.0.3
|
89 |
+
yarl==1.9.4
|