Miguel Diaz commited on
Commit
26e1cfd
1 Parent(s): d14194b

initial commit

Browse files
.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: Chattest
3
- emoji: 🏢
4
- colorFrom: blue
5
- colorTo: blue
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"]