Spaces:
Running
Running
Miguel Diaz
commited on
Commit
•
26e1cfd
1
Parent(s):
d14194b
initial commit
Browse files- .gitignore +4 -0
- Dockerfile +21 -0
- README.md +4 -4
- main.py +216 -0
- requirements.txt +7 -0
- static/conversation.svg +1 -0
- static/css/app.css +215 -0
- static/favicon.png +0 -0
- static/fonts/roman/h-n-roman.woff +0 -0
- static/fonts/roman/h-n-roman.woff2 +0 -0
- static/img/delete.png +0 -0
- static/img/menu.svg +13 -0
- static/img/send.png +0 -0
- static/js/chatHandler.js +174 -0
- static/js/windowHandler.js +157 -0
- static/main.html +38 -0
- supported.py +51 -0
.gitignore
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.history/
|
2 |
+
.vscode/
|
3 |
+
__pycache__/
|
4 |
+
keys/
|
Dockerfile
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.9
|
2 |
+
|
3 |
+
WORKDIR /code
|
4 |
+
COPY ./requirements.txt /code/requirements.txt
|
5 |
+
|
6 |
+
|
7 |
+
RUN useradd -m -u 1000 user
|
8 |
+
|
9 |
+
USER user
|
10 |
+
|
11 |
+
ENV HOME=/home/user \
|
12 |
+
PATH=/home/user/.local/bin:$PATH
|
13 |
+
|
14 |
+
WORKDIR $HOME/app
|
15 |
+
|
16 |
+
|
17 |
+
COPY --chown=user . $HOME/app
|
18 |
+
RUN pip install --user --no-cache-dir --upgrade -r /code/requirements.txt
|
19 |
+
#CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
|
20 |
+
CMD ["hypercorn", "main:app", "--bind", "0.0.0.0:7860"]
|
21 |
+
|
README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
|
|
1 |
---
|
2 |
+
title: Chgpt
|
3 |
+
emoji: 🏃
|
4 |
+
colorFrom: gray
|
5 |
+
colorTo: pink
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
main.py
ADDED
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI, Request, staticfiles, Depends, HTTPException, status
|
2 |
+
from fastapi.responses import HTMLResponse, StreamingResponse, JSONResponse
|
3 |
+
|
4 |
+
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
5 |
+
import os, json, re, requests
|
6 |
+
from datetime import datetime, timedelta
|
7 |
+
import time
|
8 |
+
import jwt
|
9 |
+
import openai
|
10 |
+
from openai import error as openai_error
|
11 |
+
from hashlib import sha256
|
12 |
+
import tiktoken
|
13 |
+
from supported import supp_langs
|
14 |
+
|
15 |
+
app = FastAPI()
|
16 |
+
security = HTTPBasic()
|
17 |
+
tokenizer = tiktoken.encoding_for_model("gpt-3.5-turbo")
|
18 |
+
app.mount("/static", staticfiles.StaticFiles(directory="static"), name="static")
|
19 |
+
users = json.loads(str(os.getenv("USER_KEYS")).replace("\n", ""))
|
20 |
+
for key in users:
|
21 |
+
if key == "master": continue
|
22 |
+
password = key+users[key]+users["master"]
|
23 |
+
users[key] = sha256(password.encode('UTF-8')).hexdigest()
|
24 |
+
|
25 |
+
with open("log.txt", "w") as f:
|
26 |
+
f.write("incio")
|
27 |
+
f.write("\n--------------------------\n")
|
28 |
+
|
29 |
+
|
30 |
+
|
31 |
+
fecha_unix = str(int(time.time()))
|
32 |
+
|
33 |
+
JWT_SECRET = users["master"]
|
34 |
+
JWT_ALGORITHM = "HS256"
|
35 |
+
JWT_EXPIRATION_TIME_MINUTES = 30
|
36 |
+
|
37 |
+
def create_jwt_token(data):
|
38 |
+
to_encode = {"data": data}
|
39 |
+
expire = datetime.utcnow() + timedelta(minutes=JWT_EXPIRATION_TIME_MINUTES)
|
40 |
+
to_encode.update({"exp": expire})
|
41 |
+
encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM)
|
42 |
+
return encoded_jwt
|
43 |
+
|
44 |
+
async def validate_token(request: Request):
|
45 |
+
data = {}
|
46 |
+
try:
|
47 |
+
data = await request.json()
|
48 |
+
token = data.pop("token")
|
49 |
+
payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
|
50 |
+
data["token_data"] = payload["data"]
|
51 |
+
except:
|
52 |
+
raise HTTPException(status_code=404, detail="Datos no válidos")
|
53 |
+
return data
|
54 |
+
|
55 |
+
def authenticate_user(credentials: HTTPBasicCredentials):
|
56 |
+
|
57 |
+
password = credentials.username+credentials.password+users["master"]
|
58 |
+
password = sha256(password.encode('UTF-8')).hexdigest()
|
59 |
+
|
60 |
+
if credentials.username not in users or password != users[credentials.username]:
|
61 |
+
raise HTTPException(
|
62 |
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
63 |
+
detail="Incorrect username or password",
|
64 |
+
headers={"WWW-Authenticate": "Basic"},
|
65 |
+
)
|
66 |
+
|
67 |
+
return True
|
68 |
+
|
69 |
+
|
70 |
+
@app.get("/", response_class=HTMLResponse)
|
71 |
+
async def root(request: Request, credentials: HTTPBasicCredentials = Depends(security)):
|
72 |
+
if authenticate_user(credentials):
|
73 |
+
token = create_jwt_token({"user":credentials.username})
|
74 |
+
with open(os.path.join("static", "main.html")) as f:
|
75 |
+
return HTMLResponse(f.read().replace("{% token %}", token).replace("{% version %}", fecha_unix))
|
76 |
+
|
77 |
+
|
78 |
+
@app.post("/chat_stream")
|
79 |
+
async def chat_stream(data = Depends(validate_token)):
|
80 |
+
messages = data.get("messages", "")
|
81 |
+
if not messages:
|
82 |
+
print("Empty message")
|
83 |
+
error = "What??"
|
84 |
+
raise HTTPException(
|
85 |
+
status_code=status.HTTP_418_IM_A_TEAPOT,
|
86 |
+
detail= error
|
87 |
+
)
|
88 |
+
try:
|
89 |
+
token_length = len(tokenizer.encode(messages[-1]["content"]))
|
90 |
+
except:
|
91 |
+
token_length = len(messages[-1]["content"])
|
92 |
+
print("Error in token length")
|
93 |
+
print("Message:", messages[-1]["content"])
|
94 |
+
|
95 |
+
config = {
|
96 |
+
"temperature": float(data.get("config", []).get("temperature", 1)),
|
97 |
+
"frequency_penalty": float(data.get("config", []).get("frequency_penalty", 1)),
|
98 |
+
"presence_penalty": float(data.get("config", []).get("presence_penalty", 1))
|
99 |
+
}
|
100 |
+
|
101 |
+
try:
|
102 |
+
response = openai.ChatCompletion.create(
|
103 |
+
model="gpt-3.5-turbo",
|
104 |
+
messages=messages,
|
105 |
+
temperature=config["temperature"],
|
106 |
+
frequency_penalty=config["frequency_penalty"],
|
107 |
+
presence_penalty=config["presence_penalty"],
|
108 |
+
request_timeout = 25,
|
109 |
+
stream=True
|
110 |
+
)
|
111 |
+
except requests.exceptions.RequestException as e:
|
112 |
+
print("Timeout (requests)")
|
113 |
+
print(e)
|
114 |
+
raise HTTPException(
|
115 |
+
status_code=status.HTTP_408_REQUEST_TIMEOUT,
|
116 |
+
detail="Los servidores tardaron mucho en responder, puede haber sobrecarga en OpenAI, reintenta luego (error 1)"
|
117 |
+
)
|
118 |
+
except openai_error.APIConnectionError as e:
|
119 |
+
print("APIConnectionError")
|
120 |
+
print(e)
|
121 |
+
raise HTTPException(
|
122 |
+
status_code=status.HTTP_408_REQUEST_TIMEOUT,
|
123 |
+
detail="El servidor no respondió, puede haber sobrecarga en OpenAI, reintenta luego (error 2)"
|
124 |
+
)
|
125 |
+
except openai_error.Timeout as e:
|
126 |
+
print("Timeout (openai)")
|
127 |
+
print(e)
|
128 |
+
raise HTTPException(
|
129 |
+
status_code=status.HTTP_408_REQUEST_TIMEOUT,
|
130 |
+
detail="El servidor no respondió, puede haber sobrecarga en OpenAI, reintenta luego (error 3)"
|
131 |
+
)
|
132 |
+
except openai_error.InvalidRequestError as e:
|
133 |
+
print("Timeout (openai)")
|
134 |
+
print(e)
|
135 |
+
error = "El servidor no respondió, puede haber sobrecarga en OpenAI, reintenta luego (error 3)"
|
136 |
+
if "This model's maximum context length is 4097 tokens" in e.message:
|
137 |
+
error = "ChatGPT se gomitó 🤮, limpia el chat y reintenta."
|
138 |
+
raise HTTPException(
|
139 |
+
status_code=status.HTTP_408_REQUEST_TIMEOUT,
|
140 |
+
detail= error
|
141 |
+
)
|
142 |
+
|
143 |
+
token = create_jwt_token(data.pop("token_data"))
|
144 |
+
|
145 |
+
async def __streamer():
|
146 |
+
yield json.dumps({"object": "chat.token", "token": token})
|
147 |
+
#yield json.dumps({"object": "chat.user.length", "length": token_length})
|
148 |
+
tokens = 0
|
149 |
+
for chunk in response:
|
150 |
+
tokens += 1
|
151 |
+
yield json.dumps(chunk)
|
152 |
+
#yield({"object": "chat.assistant.length", "length": tokens})
|
153 |
+
|
154 |
+
return StreamingResponse(__streamer(), media_type="application/json")
|
155 |
+
|
156 |
+
@app.get("/read_log", response_class=HTMLResponse)
|
157 |
+
async def read_log(request: Request, credentials: HTTPBasicCredentials = Depends(security)):
|
158 |
+
if authenticate_user(credentials):
|
159 |
+
with open("log.txt", "r") as f:
|
160 |
+
return HTMLResponse(f.read())
|
161 |
+
|
162 |
+
|
163 |
+
@app.get("/mfdfastapi")
|
164 |
+
async def webhook_get(request: Request):
|
165 |
+
|
166 |
+
with open("log.txt", "a") as f:
|
167 |
+
f.write("get mfdfastapi")
|
168 |
+
f.write("\n")
|
169 |
+
headers = {}
|
170 |
+
for name, value in request.headers.items(): headers[name] = value
|
171 |
+
f.write("head: ")
|
172 |
+
f.write(json.dumps(headers))
|
173 |
+
f.write("\n")
|
174 |
+
parametros_get = request.query_params
|
175 |
+
f.write("get: ")
|
176 |
+
f.write(json.dumps(dict(parametros_get.items())))
|
177 |
+
f.write("\n")
|
178 |
+
f.write("--------------------------\n")
|
179 |
+
|
180 |
+
mode = request.query_params.get('hub.mode', "")
|
181 |
+
if mode=="subscribe":
|
182 |
+
challenge = request.query_params.get('hub.challenge', "")
|
183 |
+
verify_token = request.query_params.get('hub.verify_token', "")
|
184 |
+
|
185 |
+
if not mode or not challenge or not verify_token:
|
186 |
+
raise HTTPException(status_code=404, detail="Datos no válidos")
|
187 |
+
|
188 |
+
if verify_token != users["master"]:
|
189 |
+
raise HTTPException(status_code=404, detail="Datos no válidos")
|
190 |
+
return HTMLResponse(str(challenge))
|
191 |
+
|
192 |
+
|
193 |
+
|
194 |
+
|
195 |
+
@app.post("/mfdfastapi")
|
196 |
+
async def webhook_post(request: Request):
|
197 |
+
with open("log.txt", "a") as f:
|
198 |
+
f.write("post mfdfastapi")
|
199 |
+
f.write("\n")
|
200 |
+
headers = {}
|
201 |
+
for name, value in request.headers.items(): headers[name] = value
|
202 |
+
f.write("head: ")
|
203 |
+
f.write(json.dumps(headers))
|
204 |
+
f.write("\n")
|
205 |
+
parametros_post = await request.json()
|
206 |
+
parametros_get = request.query_params
|
207 |
+
f.write("get: ")
|
208 |
+
f.write(json.dumps(dict(parametros_get.items())))
|
209 |
+
f.write("\n")
|
210 |
+
f.write("post: ")
|
211 |
+
f.write(json.dumps(parametros_post))
|
212 |
+
f.write("\n")
|
213 |
+
f.write("--------------------------\n")
|
214 |
+
|
215 |
+
return HTMLResponse("ok")
|
216 |
+
|
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
fastapi==0.74.*
|
2 |
+
requests==2.27.*
|
3 |
+
openai==0.27.*
|
4 |
+
uvicorn[standard]==0.17.*
|
5 |
+
PyJWT==2.6.0
|
6 |
+
hypercorn>=0.14.3
|
7 |
+
tiktoken>=0.3.0
|
static/conversation.svg
ADDED
static/css/app.css
ADDED
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
html,
|
2 |
+
body {
|
3 |
+
margin: 0;
|
4 |
+
padding: 0;
|
5 |
+
width: 100%;
|
6 |
+
height: 100%;
|
7 |
+
font-family: Monospace;
|
8 |
+
font-size: 15px;
|
9 |
+
}
|
10 |
+
|
11 |
+
body {
|
12 |
+
display: flex;
|
13 |
+
}
|
14 |
+
button{
|
15 |
+
cursor: pointer;
|
16 |
+
}
|
17 |
+
.wrapper {
|
18 |
+
padding: 20px 20px 80px 20px;
|
19 |
+
border-bottom: 5px solid #222f3d;
|
20 |
+
background: #34495e;
|
21 |
+
margin: 0;
|
22 |
+
min-width: 320px;
|
23 |
+
width: 100%;
|
24 |
+
}
|
25 |
+
.submenu {
|
26 |
+
position: fixed;
|
27 |
+
top: 0;
|
28 |
+
left: 0;
|
29 |
+
width: fit-content;
|
30 |
+
height: fit-content;
|
31 |
+
|
32 |
+
}
|
33 |
+
.submenu .abrir{
|
34 |
+
width: 30px;
|
35 |
+
height: 30px;
|
36 |
+
background-image: url(/static/img/menu.svg);
|
37 |
+
background-color: #8093a5;
|
38 |
+
background-repeat: no-repeat;
|
39 |
+
background-position: center;
|
40 |
+
border-radius: 0px 0px 5px 0px;
|
41 |
+
border-bottom: solid 1px;
|
42 |
+
border-right: solid 1px;
|
43 |
+
position: fixed;
|
44 |
+
}
|
45 |
+
.submenu .configuracion{
|
46 |
+
width: calc( 100vw - 20px);
|
47 |
+
height: 0px;
|
48 |
+
background: #cbcbcb;
|
49 |
+
margin-left: 10px;
|
50 |
+
border-radius: 0px 0px 5px 5px;
|
51 |
+
top: -2px;
|
52 |
+
transition: box-shadow 0.5s, height 0.5s, top 0.5s;
|
53 |
+
}
|
54 |
+
.submenu .configuracion.desplegado{
|
55 |
+
top: 0px;
|
56 |
+
height: 300px;
|
57 |
+
border: solid 1px;
|
58 |
+
border-top: none;
|
59 |
+
box-shadow: 5px 5px 10px 2px #000000ad;
|
60 |
+
}
|
61 |
+
.chat {
|
62 |
+
--textarea: 0px;
|
63 |
+
border-radius: 5px;
|
64 |
+
display: block;
|
65 |
+
width: 100%;
|
66 |
+
height: calc( 100% - var(--textarea) );
|
67 |
+
overflow-y: scroll;
|
68 |
+
overflow-x: hidden;
|
69 |
+
background: rgb(161, 161, 161);
|
70 |
+
padding: 10px 0;
|
71 |
+
}
|
72 |
+
|
73 |
+
.chat .message {
|
74 |
+
display: flex;
|
75 |
+
margin: 5px 20px 5px 10px;
|
76 |
+
filter: opacity(0.9);
|
77 |
+
}
|
78 |
+
.chat .message.me {
|
79 |
+
margin: 5px 10px 5px 20px;
|
80 |
+
}
|
81 |
+
.chat .message.comando {
|
82 |
+
margin: 5px auto;
|
83 |
+
display: table;
|
84 |
+
}
|
85 |
+
.chat .message:last-child {
|
86 |
+
filter: opacity(1);
|
87 |
+
}
|
88 |
+
|
89 |
+
.chat .message.no-opacity {
|
90 |
+
display: flex;
|
91 |
+
margin: 10px 0 0 10px;
|
92 |
+
filter: opacity(1);
|
93 |
+
}
|
94 |
+
|
95 |
+
.chat .message img {
|
96 |
+
margin: 0 10px 0 0;
|
97 |
+
height: 30px;
|
98 |
+
border-radius: 50%;
|
99 |
+
}
|
100 |
+
|
101 |
+
.chat .message.me img {
|
102 |
+
order: 2;
|
103 |
+
margin: 0 0 0 3px;
|
104 |
+
}
|
105 |
+
|
106 |
+
.chat .message div {
|
107 |
+
flex: 1;
|
108 |
+
max-width: 100%;
|
109 |
+
}
|
110 |
+
|
111 |
+
.chat .message div p {
|
112 |
+
max-width: calc( 100% - 20px );
|
113 |
+
display: inline-block;
|
114 |
+
margin: 0;
|
115 |
+
padding: 8px 10px 8px 10px;
|
116 |
+
background: #fff;
|
117 |
+
border-radius: 3px;
|
118 |
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
119 |
+
min-width: 40%;
|
120 |
+
}
|
121 |
+
|
122 |
+
.chat .message.me div p {
|
123 |
+
float: right;
|
124 |
+
background: #b4c2ff;
|
125 |
+
}
|
126 |
+
.chat .message.comando div p {
|
127 |
+
background: #d0a8ff;
|
128 |
+
}
|
129 |
+
.chat .message.warning div p {
|
130 |
+
background: #f0e370;
|
131 |
+
}
|
132 |
+
.chat .message.error div p {
|
133 |
+
background: #f09470;
|
134 |
+
}
|
135 |
+
|
136 |
+
.chat .message div p ul {
|
137 |
+
list-style: none;
|
138 |
+
color: #555;
|
139 |
+
padding-right: 10px;
|
140 |
+
}
|
141 |
+
|
142 |
+
.chat .message:last-child div p ul {
|
143 |
+
list-style: none;
|
144 |
+
color: blue;
|
145 |
+
}
|
146 |
+
|
147 |
+
.chat .message div p ul.ultotal {
|
148 |
+
list-style: none;
|
149 |
+
color: #34495e;
|
150 |
+
font-size: 12px;
|
151 |
+
}
|
152 |
+
|
153 |
+
.chat .message pre {
|
154 |
+
overflow-x: scroll;
|
155 |
+
border: solid 1px #e5e4e4;
|
156 |
+
padding: 10px;
|
157 |
+
}
|
158 |
+
|
159 |
+
.input-box {
|
160 |
+
background: #222f3d;
|
161 |
+
margin: 10px 0;
|
162 |
+
height: 30px;
|
163 |
+
display: flex;
|
164 |
+
border-radius: 5px;
|
165 |
+
}
|
166 |
+
|
167 |
+
.input-box textarea,
|
168 |
+
.input-box button {
|
169 |
+
height: 100%;
|
170 |
+
margin: 0;
|
171 |
+
border: none;
|
172 |
+
padding: 0 15px;
|
173 |
+
}
|
174 |
+
|
175 |
+
.input-box button:focus, .input-box textarea:focus {
|
176 |
+
outline: none;
|
177 |
+
}
|
178 |
+
|
179 |
+
.input-box .input-text {
|
180 |
+
width: 100%;
|
181 |
+
border-radius: 5px 0 0 5px;
|
182 |
+
resize: none;
|
183 |
+
border-top: solid 7px #fff;
|
184 |
+
border-bottom: solid 7px #fff;
|
185 |
+
|
186 |
+
}
|
187 |
+
.input-box button{
|
188 |
+
width: 30px;
|
189 |
+
background-size: 20px;
|
190 |
+
background-color: #ddd;
|
191 |
+
background-repeat: no-repeat;
|
192 |
+
background-position: center;
|
193 |
+
border-left: solid 1px #555;
|
194 |
+
}
|
195 |
+
.input-box .input-send {
|
196 |
+
background-image: url(/static/img/send.png);
|
197 |
+
}
|
198 |
+
.input-box .input-delete {
|
199 |
+
background-image: url(/static/img/delete.png);
|
200 |
+
}
|
201 |
+
.input-box button:first-child{
|
202 |
+
border-left: none;
|
203 |
+
}
|
204 |
+
.input-box button:last-child{
|
205 |
+
border-radius: 0 5px 5px 0;
|
206 |
+
}
|
207 |
+
.input-box button:disabled, .input-box textarea:disabled{
|
208 |
+
background-color: #8b8b8b;
|
209 |
+
border-color: #8b8b8b;
|
210 |
+
|
211 |
+
}
|
212 |
+
|
213 |
+
#message-template{
|
214 |
+
display: none;
|
215 |
+
}
|
static/favicon.png
ADDED
static/fonts/roman/h-n-roman.woff
ADDED
Binary file (44.7 kB). View file
|
|
static/fonts/roman/h-n-roman.woff2
ADDED
Binary file (30.3 kB). View file
|
|
static/img/delete.png
ADDED
static/img/menu.svg
ADDED
static/img/send.png
ADDED
static/js/chatHandler.js
ADDED
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class ChatGPT{
|
2 |
+
constructor(token){
|
3 |
+
|
4 |
+
$("#input-delete").click(()=> $(document).trigger("chat:limpiar"))
|
5 |
+
|
6 |
+
let fecha = new Date().toJSON().slice(0, 10);
|
7 |
+
this.definicion = "Te llamas Chatsito, eres un asistente de apoyo a los amigos de MIA, ";
|
8 |
+
this.definicion += "tu objetivo principal es responder preguntas y hacer sentir bien ";
|
9 |
+
this.definicion += "a tu interlocutor.\n";
|
10 |
+
this.definicion += "Responde de manera amistosa con un poco de comedia.\n";
|
11 |
+
this.definicion += "Knowledge cutoff: 2021-09-01\nCurrent date: "+fecha;
|
12 |
+
this.definicion = {role: "system", content: this.definicion, tokens: 100};
|
13 |
+
|
14 |
+
if (localStorage.getItem("convesacion") !== null) {
|
15 |
+
this.convesacion = JSON.parse(localStorage.getItem("convesacion"));
|
16 |
+
$(document).trigger("chat:cargar", this.convesacion);
|
17 |
+
}else{
|
18 |
+
this.convesacion = [this.definicion];
|
19 |
+
}
|
20 |
+
|
21 |
+
if (localStorage.getItem("config") !== null) {
|
22 |
+
this.config = JSON.parse(localStorage.getItem("config"));
|
23 |
+
}else{
|
24 |
+
this.config = {
|
25 |
+
temperature: 1.0,
|
26 |
+
frequency_penalty: 0.0,
|
27 |
+
presence_penalty: 0.0
|
28 |
+
};
|
29 |
+
}
|
30 |
+
|
31 |
+
this.execStart = 0;
|
32 |
+
|
33 |
+
this.endpointChat = "/chat_stream";
|
34 |
+
this.token = token
|
35 |
+
// this.evCtx = document
|
36 |
+
this.reintentos = 0
|
37 |
+
$(document).on("chat:limpiar", () => {
|
38 |
+
this.limpiarConfig()
|
39 |
+
});
|
40 |
+
$(document).on("chat:enviar", (event, params) => {
|
41 |
+
this.reintentos = 0;
|
42 |
+
this.enviar(params);
|
43 |
+
});
|
44 |
+
$(document).on("enviar:reintentar", (event, params) => {
|
45 |
+
this.reintentos++;
|
46 |
+
this.enviar(params.mensaje)
|
47 |
+
});
|
48 |
+
|
49 |
+
$(document).on("enviar:error", (event, params) => this.reenviar(params));
|
50 |
+
|
51 |
+
|
52 |
+
}
|
53 |
+
|
54 |
+
limpiarConfig(){
|
55 |
+
localStorage.removeItem('convesacion');
|
56 |
+
localStorage.removeItem('config');
|
57 |
+
this.config = {
|
58 |
+
temperature: 1.0,
|
59 |
+
frequency_penalty: 0.0,
|
60 |
+
presence_penalty: 0.0
|
61 |
+
};
|
62 |
+
this.convesacion = [this.definicion];
|
63 |
+
|
64 |
+
}
|
65 |
+
|
66 |
+
reenviar(params){
|
67 |
+
if(params.status==404){
|
68 |
+
$(document).trigger("enviar:fallido",{jqXHR:params.jqXHR, status:params.status, error:params.error, execTime:params.execTime, mensaje:params.mensaje});
|
69 |
+
return
|
70 |
+
}
|
71 |
+
if(this.reintentos < 3 ){
|
72 |
+
$(document).trigger("enviar:reintentar",{jqXHR:params.jqXHR, status:params.status, error:params.error, execTime:params.execTime, mensaje:params.mensaje});
|
73 |
+
$(document).trigger("enviar:enviar",{jqXHR:params.jqXHR, status:params.status, error:params.error, execTime:params.execTime, mensaje:params.mensaje});
|
74 |
+
|
75 |
+
}else{
|
76 |
+
$(document).trigger("enviar:fallido",{jqXHR:params.jqXHR, status:params.status, error:params.error, execTime:params.execTime, mensaje:params.mensaje});
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
enviar(mensaje){
|
81 |
+
let tempMensajes = [];
|
82 |
+
let tokens = 0;
|
83 |
+
for(let i = 0; i < this.convesacion.length; i++){
|
84 |
+
let actMsg = this.convesacion[i];
|
85 |
+
tempMensajes.push({role: actMsg.role, content: actMsg.content})
|
86 |
+
tokens += actMsg.tokens
|
87 |
+
}
|
88 |
+
|
89 |
+
console.log("Enviando: ", mensaje);
|
90 |
+
tempMensajes.push({role: "user", content: mensaje});
|
91 |
+
this.convesacion.push({role: "user", content: mensaje});
|
92 |
+
this.convesacion.push({});
|
93 |
+
$(document).trigger("precarga:inicio", mensaje);
|
94 |
+
let self = this;
|
95 |
+
fetch(this.endpointChat, {
|
96 |
+
method: "POST",
|
97 |
+
body: JSON.stringify({
|
98 |
+
messages: tempMensajes,
|
99 |
+
token: this.token,
|
100 |
+
config: this.config
|
101 |
+
}),
|
102 |
+
}).then(response => ({
|
103 |
+
rb: response.body,
|
104 |
+
rstat: response.status
|
105 |
+
})).then(({rb, rstat}) => {
|
106 |
+
if(!rb){
|
107 |
+
return false;
|
108 |
+
}
|
109 |
+
const reader = rb.getReader();
|
110 |
+
|
111 |
+
return new ReadableStream({
|
112 |
+
start(controller) {
|
113 |
+
function push() {
|
114 |
+
reader.read().then(({done, value}) => {
|
115 |
+
|
116 |
+
if(done){
|
117 |
+
controller.close();
|
118 |
+
if(rstat==200){
|
119 |
+
$(document).trigger("precarga:fin");
|
120 |
+
localStorage.setItem("convesacion", JSON.stringify(self.convesacion))
|
121 |
+
console.log("terminado", self.convesacion[self.convesacion.length-1].content)
|
122 |
+
}else{
|
123 |
+
this.convesacion.pop()
|
124 |
+
console.log("terminado con errores")
|
125 |
+
}
|
126 |
+
return
|
127 |
+
}
|
128 |
+
|
129 |
+
let elements = new TextDecoder("utf-8").decode(value).split("}{");
|
130 |
+
let elLen = elements.length;
|
131 |
+
for(let i in elements){
|
132 |
+
let data = elements[i];
|
133 |
+
if(!data){
|
134 |
+
continue;
|
135 |
+
}
|
136 |
+
|
137 |
+
data =JSON.parse((i>0?"{":"") + data + (i<elLen-1?"}":""));
|
138 |
+
|
139 |
+
if(rstat==200){
|
140 |
+
if(data.object == "chat.token"){
|
141 |
+
self.token = data.token;
|
142 |
+
}else if(data.choices[0].hasOwnProperty("delta")){
|
143 |
+
let temp = data.choices[0].delta;
|
144 |
+
let key = Object.keys(temp)[0];
|
145 |
+
let elActual = self.convesacion[self.convesacion.length-1]
|
146 |
+
if(!elActual.hasOwnProperty(key)){elActual[key]="";}
|
147 |
+
elActual[key] += temp[key]
|
148 |
+
if(elActual.hasOwnProperty("content") && temp.content){
|
149 |
+
$(document).trigger("precarga:mensaje", temp.content);
|
150 |
+
}
|
151 |
+
}
|
152 |
+
}else{
|
153 |
+
$(document).trigger("precarga:error", {status: rstat, mensaje: data.detail});
|
154 |
+
}
|
155 |
+
|
156 |
+
}
|
157 |
+
push();
|
158 |
+
})
|
159 |
+
}
|
160 |
+
|
161 |
+
push();
|
162 |
+
},
|
163 |
+
});
|
164 |
+
}).then(data => {
|
165 |
+
}).catch(err =>{
|
166 |
+
console.log('Solicitud fallida', err)
|
167 |
+
});
|
168 |
+
|
169 |
+
|
170 |
+
}
|
171 |
+
|
172 |
+
|
173 |
+
|
174 |
+
}
|
static/js/windowHandler.js
ADDED
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class WindowHandler{
|
2 |
+
constructor(){
|
3 |
+
this.chatbox = $("#chat");
|
4 |
+
this.template = $('<div class="message"><div><p></p></div></div>');
|
5 |
+
this.active = false;
|
6 |
+
this.mensaje = "";
|
7 |
+
|
8 |
+
// this.evCtx = document;
|
9 |
+
|
10 |
+
$(document).on("chat:enviar", (event, params) => this.recalcularTextarea());
|
11 |
+
$(document).on("chat:limpiar", () => this.limpiarChat());
|
12 |
+
|
13 |
+
$("#input-text").keypress((event) => {
|
14 |
+
if (!event.shiftKey && event.keyCode === 13) {
|
15 |
+
this.manejadorEnviar();
|
16 |
+
}});
|
17 |
+
$("#input-send").click(() => this.manejadorEnviar());
|
18 |
+
$("#input-text").keypress(() => this.recalcularTextarea());
|
19 |
+
$("#input-text").keyup(() => this.recalcularTextarea());
|
20 |
+
$("#input-text").keydown(() => this.recalcularTextarea());
|
21 |
+
this.cargarChat(chatH.convesacion);
|
22 |
+
|
23 |
+
$(document).on("precarga:inicio", (event, params) => {
|
24 |
+
this.respuestaInicio(params)
|
25 |
+
});
|
26 |
+
$(document).on("precarga:mensaje", (event, params) => {
|
27 |
+
this.respuestaMensaje(params)}
|
28 |
+
);
|
29 |
+
$(document).on("precarga:fin", (event, params) => {
|
30 |
+
this.respuestaFin()
|
31 |
+
});
|
32 |
+
$(document).on("precarga:error", (event, params) => {
|
33 |
+
this.respuestaError(params)
|
34 |
+
});
|
35 |
+
|
36 |
+
}
|
37 |
+
|
38 |
+
limpiarChat(){
|
39 |
+
$("#input-text").val("");
|
40 |
+
this.recalcularTextarea();
|
41 |
+
$("#chat").html("");
|
42 |
+
}
|
43 |
+
|
44 |
+
manejadorEnviar(){
|
45 |
+
let mensaje = $("#input-text").val();
|
46 |
+
if(mensaje==""){
|
47 |
+
return false;
|
48 |
+
}
|
49 |
+
$(document).trigger("chat:enviar", mensaje);
|
50 |
+
}
|
51 |
+
|
52 |
+
recalcularTextarea(){
|
53 |
+
$(".input-box").css("height", "30px");
|
54 |
+
let height = parseInt(($(".input-text").prop('scrollHeight')+15)/15)*15;
|
55 |
+
$(".input-box").css("height", height+"px");
|
56 |
+
height -= 30;
|
57 |
+
$(".chat").css("--textarea", height+"px");
|
58 |
+
}
|
59 |
+
|
60 |
+
procesarTexto(texto){
|
61 |
+
let segmentos = texto.split("```");
|
62 |
+
let resultado = "";
|
63 |
+
|
64 |
+
for(let i=0; i<segmentos.length;i++){
|
65 |
+
let textoActual = segmentos[i];
|
66 |
+
if(i%2==0){
|
67 |
+
resultado += textoActual.replace(/\n/g, "<br>").replace(/`(.*?)`/gm, "<b>$1</b>");
|
68 |
+
}else{
|
69 |
+
let temp = textoActual.split("\n",1);
|
70 |
+
resultado += "<pre><code class='language-";
|
71 |
+
resultado += temp[0].length>1?temp[0]:"none";
|
72 |
+
temp = $("<div></div>").text(textoActual.substr(temp[0].length)).html()
|
73 |
+
resultado += "'>"+temp+"</code></pre>";
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
return resultado
|
78 |
+
}
|
79 |
+
|
80 |
+
cargarChat(mensajes){
|
81 |
+
mensajes.forEach((mensaje) => {
|
82 |
+
if(mensaje.role!="system"){
|
83 |
+
let clone = this.template.clone();
|
84 |
+
if(mensaje.role=="user") {clone.addClass("me");}
|
85 |
+
let texto = this.procesarTexto(mensaje.content);
|
86 |
+
clone.find("div p").html(texto);
|
87 |
+
this.chatbox.append(clone);
|
88 |
+
this.active = clone;
|
89 |
+
Prism.highlightAllUnder(this.active[0])
|
90 |
+
this.active = false;
|
91 |
+
this.chatbox.scrollTop(this.chatbox[0].scrollHeight);
|
92 |
+
}
|
93 |
+
});
|
94 |
+
}
|
95 |
+
|
96 |
+
respuestaInicio(mensaje){
|
97 |
+
$("#input-text").val("");
|
98 |
+
$("button").prop("disabled", true);
|
99 |
+
$("textarea").prop("disabled", true);
|
100 |
+
this.mensaje = ""
|
101 |
+
let clone = this.template.clone();
|
102 |
+
clone.addClass("me");
|
103 |
+
clone.find("div p").text(mensaje);
|
104 |
+
this.chatbox.append(clone);
|
105 |
+
|
106 |
+
clone = this.template.clone();
|
107 |
+
clone.find("div p").html("");
|
108 |
+
this.chatbox.append(clone);
|
109 |
+
this.active = clone;
|
110 |
+
this.chatbox.scrollTop(this.chatbox[0].scrollHeight);
|
111 |
+
}
|
112 |
+
|
113 |
+
respuestaMensaje(mensaje){
|
114 |
+
this.mensaje += mensaje
|
115 |
+
let html = this.active.find("div p").html();
|
116 |
+
html += mensaje.replace(/\n/g, "<br>").replace(/`([^`\w\W]+?)`/gm, "<b>$1</b>")
|
117 |
+
this.active.find("div p").html(html);
|
118 |
+
this.chatbox.scrollTop(this.chatbox[0].scrollHeight);
|
119 |
+
|
120 |
+
}
|
121 |
+
|
122 |
+
respuestaFin(){
|
123 |
+
let msgProcesado = this.procesarTexto(this.mensaje);
|
124 |
+
this.mensaje = "";
|
125 |
+
$("button").prop("disabled", false);
|
126 |
+
$("textarea").prop("disabled", false);
|
127 |
+
$("textarea").focus();
|
128 |
+
this.active.find("div p").html(msgProcesado);
|
129 |
+
Prism.highlightAllUnder(this.active[0]);
|
130 |
+
this.active = false;
|
131 |
+
this.chatbox.scrollTop(this.chatbox[0].scrollHeight);
|
132 |
+
}
|
133 |
+
|
134 |
+
respuestaError(error){
|
135 |
+
$("button").prop("disabled", false);
|
136 |
+
$("textarea").prop("disabled", false);
|
137 |
+
$("textarea").focus();
|
138 |
+
this.active.find("div p").html(error.mensaje)
|
139 |
+
switch(error.status){
|
140 |
+
case 404:
|
141 |
+
this.active.addClass("error")
|
142 |
+
case 408:
|
143 |
+
this.active.addClass("warning")
|
144 |
+
default:
|
145 |
+
this.active.addClass("warning")
|
146 |
+
}
|
147 |
+
this.active = false;
|
148 |
+
this.chatbox.scrollTop(this.chatbox[0].scrollHeight)
|
149 |
+
|
150 |
+
|
151 |
+
|
152 |
+
}
|
153 |
+
|
154 |
+
|
155 |
+
|
156 |
+
|
157 |
+
}
|
static/main.html
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<html>
|
2 |
+
<head>
|
3 |
+
<title> Chatbot </title>
|
4 |
+
<meta name="viewport" content="width=device-width">
|
5 |
+
<link rel="shortcut icon" href="static/favicon.png" type="image/png">
|
6 |
+
<link rel="stylesheet" href="static/css/app.css?v={% version %}">
|
7 |
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
8 |
+
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css" rel="stylesheet" />
|
9 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js" integrity="sha512-9khQRAUBYEJDCDVP2yw3LRUQvjJ0Pjx0EShmaQjcHa6AXiOv6qHQu9lCAIR8O+/D8FtaCoJ2c0Tf9Xo7hYH01Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
10 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js" integrity="sha512-SkmBfuA2hqjzEVpmnMt/LINrjop3GKWqsuLSSB3e7iBmYK7JuWw4ldmmxwD9mdm2IRTTi0OxSAfEGvgEi0i2Kw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
11 |
+
<script src="static/js/chatHandler.js?v={% version %}"></script>
|
12 |
+
<script src="static/js/windowHandler.js?v={% version %}"></script>
|
13 |
+
|
14 |
+
</head>
|
15 |
+
<body>
|
16 |
+
<div class="wrapper pag1">
|
17 |
+
<div class="chat" id="chat"></div>
|
18 |
+
<div class='input-box'>
|
19 |
+
<textarea class='input-text' id='input-text' placeholder="Type something" type="text" autofocus=""></textarea>
|
20 |
+
<button class='input-send' id='input-send' ></button>
|
21 |
+
<button class='input-delete' id='input-delete' ></button>
|
22 |
+
</div>
|
23 |
+
</div>
|
24 |
+
|
25 |
+
|
26 |
+
|
27 |
+
<script>
|
28 |
+
let windH = null;
|
29 |
+
let chatH = null;
|
30 |
+
|
31 |
+
$(document).ready(function() {
|
32 |
+
chatH = new ChatGPT("{% token %}");
|
33 |
+
windH = new WindowHandler();
|
34 |
+
});
|
35 |
+
|
36 |
+
</script>
|
37 |
+
</body>
|
38 |
+
</html>
|
supported.py
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
supp_langs = [
|
2 |
+
"markup", "html", "xml", "svg", "mathml", "ssml", "atom", "rss", "css",
|
3 |
+
"clike", "javascript", "js", "abap", "abnf", "actionscript", "ada", "agda",
|
4 |
+
"al", "antlr4", "g4", "apacheconf", "apex", "apl", "applescript", "aql",
|
5 |
+
"arduino", "ino", "arff", "armasm", "arm-asm", "arturo", "art", "asciidoc",
|
6 |
+
"adoc", "aspnet", "asm6502", "asmatmel", "autohotkey", "autoit", "avisynth",
|
7 |
+
"avs", "avro-idl", "avdl", "awk", "gawk", "bash", "sh", "shell", "basic",
|
8 |
+
"batch", "bbcode", "shortcode", "bbj", "bicep", "birb", "bison", "bnf",
|
9 |
+
"rbnf", "bqn", "brainfuck", "brightscript", "bro", "bsl", "oscript", "c",
|
10 |
+
"csharp", "cs", "dotnet", "cpp", "cfscript", "cfc", "chaiscript", "cil",
|
11 |
+
"cilkc", "cilk-c", "cilkcpp", "cilk-cpp", "cilk", "clojure", "cmake", "cobol",
|
12 |
+
"coffeescript", "coffee", "concurnas", "conc", "csp", "cooklang", "coq",
|
13 |
+
"crystal", "css-extras", "csv", "cue", "cypher", "d", "dart", "dataweave",
|
14 |
+
"dax", "dhall", "diff", "django", "jinja2", "dns-zone-file", "dns-zone",
|
15 |
+
"docker", "dockerfile", "dot", "gv", "ebnf", "editorconfig", "eiffel", "ejs",
|
16 |
+
"eta", "elixir", "elm", "etlua", "erb", "erlang", "excel-formula", "xlsx", "xls",
|
17 |
+
"fsharp", "factor", "false", "firestore-security-rules", "flow", "fortran", "ftl",
|
18 |
+
"gml", "gamemakerlanguage", "gap", "gcode", "gdscript", "gedcom", "gettext", "po",
|
19 |
+
"gherkin", "git", "glsl", "gn", "gni", "linker-script", "ld", "go", "go-module",
|
20 |
+
"go-mod", "gradle", "graphql", "groovy", "haml", "handlebars", "hbs", "mustache",
|
21 |
+
"haskell", "hs", "haxe", "hcl", "hlsl", "hoon", "http", "hpkp", "hsts", "ichigojam",
|
22 |
+
"icon", "icu-message-format", "idris", "idr", ".ignore", "gitignore", "hgignore",
|
23 |
+
"npmignore", "inform7", "ini", "io", "j", "java", "javadoc", "javadoclike",
|
24 |
+
"javastacktrace", "jexl", "jolie", "jq", "jsdoc", "js-extras", "json", "webmanifest",
|
25 |
+
"json5", "jsonp", "jsstacktrace", "js-templates", "julia", "keepalived", "keyman",
|
26 |
+
"kotlin", "kt", "kts", "kumir", "kum", "kusto", "latex", "tex", "context", "latte",
|
27 |
+
"less", "lilypond", "ly", "liquid", "lisp", "emacs", "elisp", "emacs-lisp",
|
28 |
+
"livescript", "llvm", "log", "lolcode", "lua", "magma", "makefile", "markdown", "md",
|
29 |
+
"markup-templating", "mata", "matlab", "maxscript", "mel", "mermaid", "metafont",
|
30 |
+
"mizar", "mongodb", "monkey", "moonscript", "moon", "n1ql", "n4js", "n4jsd",
|
31 |
+
"nand2tetris-hdl", "naniscript", "nani", "nasm", "neon", "nevod", "nginx", "nim",
|
32 |
+
"nix", "nsis", "objectivec", "objc", "ocaml", "odin", "opencl", "openqasm", "qasm",
|
33 |
+
"oz", "parigp", "parser", "pascal", "objectpascal", "pascaligo", "psl", "pcaxis",
|
34 |
+
"px", "peoplecode", "pcode", "perl", "php", "phpdoc", "php-extras", "plant-uml",
|
35 |
+
"plantuml", "plsql", "powerquery", "pq", "mscript", "powershell", "processing",
|
36 |
+
"prolog", "promql", ".properties", "protobuf", "pug", "puppet", "pure", "purebasic",
|
37 |
+
"pbfasm", "purescript", "purs", "python", "py", "qsharp", "qs", "q", "qml", "qore",
|
38 |
+
"r", "racket", "rkt", "cshtml", "razor", "jsx", "tsx", "reason", "regex", "rego",
|
39 |
+
"renpy", "rpy", "rescript", "res", "rest", "rip", "roboconf", "robotframework",
|
40 |
+
"robot", "ruby", "rb", "rust", "sas", "sass", "scss", "scala", "scheme",
|
41 |
+
"shell-session", "sh-session", "shellsession", "smali", "smalltalk", "smarty", "sml",
|
42 |
+
"smlnj", "solidity", "sol", "solution-file", "sln", "soy", "sparql", "rq",
|
43 |
+
"splunk-spl", "sqf", "sql", "squirrel", "stan", "stata", "iecst", "stylus",
|
44 |
+
"supercollider", "sclang", "swift", "systemd", "t4-templating", "t4-cs", "t4",
|
45 |
+
"t4-vb", "tap", "tcl", "tt2", "textile", "toml", "tremor", "trickle", "troy",
|
46 |
+
"turtle", "trig", "twig", "typescript", "ts", "typoscript", "tsconfig",
|
47 |
+
"unrealscript", "uscript", "uc", "uorazor", "uri", "url", "v", "vala", "vbnet",
|
48 |
+
"velocity", "verilog", "vhdl", "vim", "visual-basic", "vb", "vba", "warpscript",
|
49 |
+
"wasm", "web-idl", "webidl", "wgsl", "wiki", "wolfram", "mathematica", "nb", "wl",
|
50 |
+
"wren", "xeora", "xeoracube", "xml-doc", "xojo", "xquery", "yaml", "yml", "yang",
|
51 |
+
"zig"]
|