robinroy03 commited on
Commit
3a0abc1
1 Parent(s): a3c9500

first commit

Browse files
Files changed (3) hide show
  1. Dockerfile +16 -0
  2. main.py +150 -0
  3. 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