lucas-wa
commited on
Commit
•
6e06893
1
Parent(s):
daeddd3
Test uni module
Browse files- .gitattributes +0 -35
- .gitignore +24 -0
- .inference.py.swp +0 -0
- Dockerfile +31 -33
- README.md +0 -12
- docker +0 -1
- entrypoint.sh +5 -3
- server/__pycache__/app.cpython-310.pyc +0 -0
- server/__pycache__/inference.cpython-310.pyc +0 -0
- app.py → server/app.py +149 -146
- check_token.py → server/check_token.py +6 -6
- inference.py → server/inference.py +59 -59
- server/ngrok_config.py +11 -0
- requirements.txt → server/requirements.txt +12 -13
- server/static/assets/index-4iaAfGg3.css +1 -0
- server/static/assets/index-Ovl1jNHo.js +0 -0
- server/static/index.html +14 -0
- server/static/vite.svg +1 -0
- web/.eslintrc.cjs +21 -0
- web/README.md +8 -0
- web/index.html +12 -0
- web/package-lock.json +0 -0
- web/package.json +34 -0
- web/postcss.config.js +6 -0
- web/src/App.jsx +14 -0
- web/src/assets/react.svg +1 -0
- web/src/components/ChatMessageBubble.jsx +33 -0
- web/src/components/ChatWindow.jsx +177 -0
- web/src/components/Navbar.jsx +16 -0
- web/src/components/UploadDocumentsForm.jsx +202 -0
- web/src/index.css +22 -0
- web/src/lib/axios.js +5 -0
- web/src/main.jsx +10 -0
- web/tailwind.config.js +36 -0
- web/vite.config.js +11 -0
.gitattributes
DELETED
@@ -1,35 +0,0 @@
|
|
1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Logs
|
2 |
+
logs
|
3 |
+
*.log
|
4 |
+
npm-debug.log*
|
5 |
+
yarn-debug.log*
|
6 |
+
yarn-error.log*
|
7 |
+
pnpm-debug.log*
|
8 |
+
lerna-debug.log*
|
9 |
+
|
10 |
+
node_modules
|
11 |
+
dist
|
12 |
+
dist-ssr
|
13 |
+
*.local
|
14 |
+
|
15 |
+
# Editor directories and files
|
16 |
+
.vscode/*
|
17 |
+
!.vscode/extensions.json
|
18 |
+
.idea
|
19 |
+
.DS_Store
|
20 |
+
*.suo
|
21 |
+
*.ntvs*
|
22 |
+
*.njsproj
|
23 |
+
*.sln
|
24 |
+
*.sw?
|
.inference.py.swp
DELETED
Binary file (12.3 kB)
|
|
Dockerfile
CHANGED
@@ -1,33 +1,31 @@
|
|
1 |
-
FROM
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
|
1 |
+
FROM debian
|
2 |
+
|
3 |
+
RUN apt-get update
|
4 |
+
|
5 |
+
RUN apt-get install -y python3 pip
|
6 |
+
|
7 |
+
WORKDIR /code
|
8 |
+
|
9 |
+
COPY server/* /code/server/
|
10 |
+
|
11 |
+
RUN pip install --no-cache-dir --upgrade -r /code/server/requirements.txt --break-system-packages
|
12 |
+
|
13 |
+
RUN pip install huggingface-hub --break-system-packages
|
14 |
+
|
15 |
+
COPY entrypoint.sh /code/entrypoint.sh
|
16 |
+
|
17 |
+
RUN chmod +x /code/entrypoint.sh
|
18 |
+
|
19 |
+
COPY web /code/web/
|
20 |
+
|
21 |
+
RUN apt-get install -y curl
|
22 |
+
|
23 |
+
RUN curl -fsSL https://deb.nodesource.com/setup_21.x | bash - && apt-get install -y nodejs
|
24 |
+
|
25 |
+
RUN cd web && npm install
|
26 |
+
|
27 |
+
RUN cd web && npm run build
|
28 |
+
|
29 |
+
WORKDIR /code/server
|
30 |
+
|
31 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "3000"]
|
|
|
|
README.md
DELETED
@@ -1,12 +0,0 @@
|
|
1 |
-
---
|
2 |
-
title: Rag Chat
|
3 |
-
emoji: 💻
|
4 |
-
colorFrom: purple
|
5 |
-
colorTo: green
|
6 |
-
sdk: docker
|
7 |
-
pinned: false
|
8 |
-
license: apache-2.0
|
9 |
-
app_port: 8000
|
10 |
-
---
|
11 |
-
|
12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docker
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
failed to get console mode for stdout: Identificador inválido.
|
|
|
|
entrypoint.sh
CHANGED
@@ -1,15 +1,17 @@
|
|
1 |
#!/bin/bash
|
2 |
|
3 |
# Verifique o token
|
4 |
-
|
|
|
|
|
5 |
|
6 |
# Verifique o status de saída
|
7 |
if [ $? -eq 0 ]; then
|
8 |
echo "Login bem-sucedido!"
|
9 |
-
else
|
10 |
echo "Falha no login."
|
11 |
exit 1
|
12 |
fi
|
13 |
|
14 |
# Inicie o seu aplicativo
|
15 |
-
exec "$@"
|
|
|
1 |
#!/bin/bash
|
2 |
|
3 |
# Verifique o token
|
4 |
+
python3 /code/server/check_token.py $HF_TOKEN
|
5 |
+
# python3 /code/server/ngrok_config.py $NGROK_TOKEN
|
6 |
+
|
7 |
|
8 |
# Verifique o status de saída
|
9 |
if [ $? -eq 0 ]; then
|
10 |
echo "Login bem-sucedido!"
|
11 |
+
else
|
12 |
echo "Falha no login."
|
13 |
exit 1
|
14 |
fi
|
15 |
|
16 |
# Inicie o seu aplicativo
|
17 |
+
# exec "$@"
|
server/__pycache__/app.cpython-310.pyc
ADDED
Binary file (3.61 kB). View file
|
|
server/__pycache__/inference.cpython-310.pyc
ADDED
Binary file (2.16 kB). View file
|
|
app.py → server/app.py
RENAMED
@@ -1,146 +1,149 @@
|
|
1 |
-
import os
|
2 |
-
import shutil
|
3 |
-
import shutil
|
4 |
-
import numpy as np
|
5 |
-
from uuid import uuid4
|
6 |
-
from io import BytesIO
|
7 |
-
from pydantic import BaseModel
|
8 |
-
from fastapi.
|
9 |
-
from fastapi.
|
10 |
-
from fastapi.
|
11 |
-
from fastapi import
|
12 |
-
from
|
13 |
-
from llama_index import
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
import
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
)
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
)
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import shutil
|
3 |
+
import shutil
|
4 |
+
import numpy as np
|
5 |
+
from uuid import uuid4
|
6 |
+
from io import BytesIO
|
7 |
+
from pydantic import BaseModel
|
8 |
+
from fastapi.staticfiles import StaticFiles
|
9 |
+
from fastapi.encoders import jsonable_encoder
|
10 |
+
from fastapi.responses import StreamingResponse
|
11 |
+
from fastapi.middleware.cors import CORSMiddleware
|
12 |
+
from fastapi import FastAPI, UploadFile, File, Response, status
|
13 |
+
from llama_index.readers import StringIterableReader, PDFReader, SimpleDirectoryReader
|
14 |
+
from llama_index import (
|
15 |
+
VectorStoreIndex,
|
16 |
+
ServiceContext,
|
17 |
+
set_global_service_context,
|
18 |
+
)
|
19 |
+
# from pyngrok import ngrok
|
20 |
+
import inference
|
21 |
+
|
22 |
+
|
23 |
+
app = FastAPI()
|
24 |
+
|
25 |
+
app.add_middleware(
|
26 |
+
CORSMiddleware,
|
27 |
+
allow_origins=["*"],
|
28 |
+
allow_credentials=True,
|
29 |
+
allow_methods=["*"],
|
30 |
+
allow_headers=["*"],
|
31 |
+
)
|
32 |
+
|
33 |
+
class Message(BaseModel):
|
34 |
+
content: str
|
35 |
+
|
36 |
+
if not os.path.exists("tmp"):
|
37 |
+
os.mkdir("tmp")
|
38 |
+
|
39 |
+
vector_stores = {}
|
40 |
+
|
41 |
+
@app.post("/retriveal/ingest")
|
42 |
+
async def store_file(
|
43 |
+
file: UploadFile = File(...)
|
44 |
+
):
|
45 |
+
|
46 |
+
try:
|
47 |
+
|
48 |
+
print(file.filename)
|
49 |
+
id = str(uuid4())
|
50 |
+
file_location = f"tmp/{id}"
|
51 |
+
|
52 |
+
if not os.path.exists(file_location):
|
53 |
+
os.mkdir(file_location)
|
54 |
+
|
55 |
+
with open(f"{file_location}/{file.filename}", "wb+") as f:
|
56 |
+
shutil.copyfileobj(file.file, f)
|
57 |
+
|
58 |
+
pdf = SimpleDirectoryReader(f"tmp/{id}").load_data()
|
59 |
+
|
60 |
+
vector_stores[id] = VectorStoreIndex.from_documents(pdf)
|
61 |
+
|
62 |
+
return jsonable_encoder({"uuid": id})
|
63 |
+
|
64 |
+
except Exception as e:
|
65 |
+
|
66 |
+
# response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
67 |
+
return jsonable_encoder({"error": str(e)})
|
68 |
+
|
69 |
+
@app.post("/retriveal/ingest/{id}")
|
70 |
+
async def store_file_with_id(
|
71 |
+
id,
|
72 |
+
file: UploadFile = File(...)
|
73 |
+
):
|
74 |
+
|
75 |
+
try:
|
76 |
+
|
77 |
+
print(file.filename)
|
78 |
+
|
79 |
+
if(id == None or id == ""):
|
80 |
+
raise Exception("Id is required")
|
81 |
+
|
82 |
+
file_location = f"tmp/{id}"
|
83 |
+
|
84 |
+
if not os.path.exists(file_location):
|
85 |
+
os.mkdir(file_location)
|
86 |
+
|
87 |
+
with open(f"{file_location}/{file.filename}", "wb+") as f:
|
88 |
+
shutil.copyfileobj(file.file, f)
|
89 |
+
|
90 |
+
pdf = SimpleDirectoryReader(f"tmp/{id}").load_data()
|
91 |
+
|
92 |
+
vector_stores[id] = VectorStoreIndex.from_documents(pdf)
|
93 |
+
|
94 |
+
return jsonable_encoder({"uuid": id})
|
95 |
+
|
96 |
+
except Exception as e:
|
97 |
+
|
98 |
+
# response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
99 |
+
return jsonable_encoder({"error": str(e)})
|
100 |
+
|
101 |
+
@app.delete("/session/{id}")
|
102 |
+
async def delete_session(id):
|
103 |
+
try:
|
104 |
+
shutil.rmtree(f"tmp/{id}")
|
105 |
+
return jsonable_encoder({"message": "ok"})
|
106 |
+
except Exception as e:
|
107 |
+
return jsonable_encoder({"error": str(e)})
|
108 |
+
|
109 |
+
@app.post("/retriveal/{id}")
|
110 |
+
async def inference(
|
111 |
+
id,
|
112 |
+
message: Message
|
113 |
+
):
|
114 |
+
|
115 |
+
if(id == None or id == ""):
|
116 |
+
raise Exception("Id is required")
|
117 |
+
|
118 |
+
query = message.content
|
119 |
+
|
120 |
+
query_engine = vector_stores[id].as_query_engine()
|
121 |
+
|
122 |
+
inference = query_engine.query(query)
|
123 |
+
|
124 |
+
return inference
|
125 |
+
|
126 |
+
|
127 |
+
def stream_inference(gen):
|
128 |
+
for token in gen:
|
129 |
+
yield token
|
130 |
+
|
131 |
+
|
132 |
+
@app.post("/retriveal/stream/{id}")
|
133 |
+
async def inference(
|
134 |
+
id,
|
135 |
+
message: Message
|
136 |
+
):
|
137 |
+
|
138 |
+
if(id == None or id == ""):
|
139 |
+
raise Exception("Id is required")
|
140 |
+
|
141 |
+
query = message.content
|
142 |
+
|
143 |
+
query_engine = vector_stores[id].as_query_engine(streaming=True)
|
144 |
+
|
145 |
+
gen = query_engine.query(query).response_gen
|
146 |
+
|
147 |
+
return StreamingResponse(stream_inference(gen))
|
148 |
+
|
149 |
+
app.mount("/", StaticFiles(directory="static", html = True), name="static")
|
check_token.py → server/check_token.py
RENAMED
@@ -1,6 +1,6 @@
|
|
1 |
-
import os
|
2 |
-
from huggingface_hub._login import _login
|
3 |
-
|
4 |
-
HF_KEY = os.environ["HF_KEY"]
|
5 |
-
|
6 |
-
_login(token=HF_KEY, add_to_git_credential=False)
|
|
|
1 |
+
import os
|
2 |
+
from huggingface_hub._login import _login
|
3 |
+
|
4 |
+
HF_KEY = os.environ["HF_KEY"]
|
5 |
+
|
6 |
+
_login(token=HF_KEY, add_to_git_credential=False)
|
inference.py → server/inference.py
RENAMED
@@ -1,59 +1,59 @@
|
|
1 |
-
import logging
|
2 |
-
import sys
|
3 |
-
from IPython.display import Markdown, display
|
4 |
-
import torch
|
5 |
-
from llama_index.llms.huggingface import HuggingFaceLLM
|
6 |
-
from llama_index.prompts import PromptTemplate
|
7 |
-
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
|
8 |
-
|
9 |
-
from llama_index import (
|
10 |
-
ServiceContext,
|
11 |
-
set_global_service_context,
|
12 |
-
)
|
13 |
-
|
14 |
-
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
|
15 |
-
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
|
16 |
-
|
17 |
-
|
18 |
-
# Model names (make sure you have access on HF)
|
19 |
-
LLAMA2_7B = "meta-llama/Llama-2-7b-hf"
|
20 |
-
LLAMA2_7B_CHAT = "meta-llama/Llama-2-7b-chat-hf"
|
21 |
-
LLAMA2_13B = "meta-llama/Llama-2-13b-hf"
|
22 |
-
LLAMA2_13B_CHAT = "meta-llama/Llama-2-13b-chat-hf"
|
23 |
-
LLAMA2_70B = "meta-llama/Llama-2-70b-hf"
|
24 |
-
LLAMA2_70B_CHAT = "meta-llama/Llama-2-70b-chat-hf"
|
25 |
-
|
26 |
-
selected_model = LLAMA2_7B_CHAT
|
27 |
-
|
28 |
-
SYSTEM_PROMPT = """Você é um assistente de IA que responde a perguntas de maneira amigável, com base nos documentos fornecidos. Aqui estão algumas regras que você sempre segue:
|
29 |
-
- Gerar saídas legíveis para humanos, evitando criar texto sem sentido.
|
30 |
-
- Gerar apenas a saída solicitada, sem incluir qualquer outro idioma antes ou depois da saída solicitada.
|
31 |
-
- Nunca agradecer, expressar felicidade em ajudar, mencionar que é um agente de IA, etc. Apenas responda diretamente.
|
32 |
-
- Gerar linguagem profissional geralmente usada em documentos comerciais na América do Norte.
|
33 |
-
- Nunca gerar linguagem ofensiva ou obscena.
|
34 |
-
- Traduza as suas respostas sempre para Português Brasileiro. Nunca responsa nada em inglês.
|
35 |
-
"""
|
36 |
-
|
37 |
-
query_wrapper_prompt = PromptTemplate(
|
38 |
-
"[INST]<<SYS>>\n" + SYSTEM_PROMPT + "<</SYS>>\n\n{query_str}[/INST] "
|
39 |
-
)
|
40 |
-
|
41 |
-
llm = HuggingFaceLLM(
|
42 |
-
context_window=4096,
|
43 |
-
max_new_tokens=2048,
|
44 |
-
generate_kwargs={"temperature": 0.0, "do_sample": False},
|
45 |
-
query_wrapper_prompt=query_wrapper_prompt,
|
46 |
-
tokenizer_name=selected_model,
|
47 |
-
model_name=selected_model,
|
48 |
-
device_map="auto",
|
49 |
-
# change these settings below depending on your GPU
|
50 |
-
# model_kwargs={"torch_dtype": torch.float16, "load_in_8bit": True},
|
51 |
-
)
|
52 |
-
|
53 |
-
|
54 |
-
embed_model = HuggingFaceEmbedding(model_name="neuralmind/bert-base-portuguese-cased")
|
55 |
-
# embed_model = FlagModel("BAAI/bge-m3")
|
56 |
-
|
57 |
-
service_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)
|
58 |
-
|
59 |
-
set_global_service_context(service_context)
|
|
|
1 |
+
import logging
|
2 |
+
import sys
|
3 |
+
from IPython.display import Markdown, display
|
4 |
+
import torch
|
5 |
+
from llama_index.llms.huggingface import HuggingFaceLLM
|
6 |
+
from llama_index.prompts import PromptTemplate
|
7 |
+
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
|
8 |
+
|
9 |
+
from llama_index import (
|
10 |
+
ServiceContext,
|
11 |
+
set_global_service_context,
|
12 |
+
)
|
13 |
+
|
14 |
+
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
|
15 |
+
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
|
16 |
+
|
17 |
+
|
18 |
+
# Model names (make sure you have access on HF)
|
19 |
+
LLAMA2_7B = "meta-llama/Llama-2-7b-hf"
|
20 |
+
LLAMA2_7B_CHAT = "meta-llama/Llama-2-7b-chat-hf"
|
21 |
+
LLAMA2_13B = "meta-llama/Llama-2-13b-hf"
|
22 |
+
LLAMA2_13B_CHAT = "meta-llama/Llama-2-13b-chat-hf"
|
23 |
+
LLAMA2_70B = "meta-llama/Llama-2-70b-hf"
|
24 |
+
LLAMA2_70B_CHAT = "meta-llama/Llama-2-70b-chat-hf"
|
25 |
+
|
26 |
+
selected_model = LLAMA2_7B_CHAT
|
27 |
+
|
28 |
+
SYSTEM_PROMPT = """Você é um assistente de IA que responde a perguntas de maneira amigável, com base nos documentos fornecidos. Aqui estão algumas regras que você sempre segue:
|
29 |
+
- Gerar saídas legíveis para humanos, evitando criar texto sem sentido.
|
30 |
+
- Gerar apenas a saída solicitada, sem incluir qualquer outro idioma antes ou depois da saída solicitada.
|
31 |
+
- Nunca agradecer, expressar felicidade em ajudar, mencionar que é um agente de IA, etc. Apenas responda diretamente.
|
32 |
+
- Gerar linguagem profissional geralmente usada em documentos comerciais na América do Norte.
|
33 |
+
- Nunca gerar linguagem ofensiva ou obscena.
|
34 |
+
- Traduza as suas respostas sempre para Português Brasileiro. Nunca responsa nada em inglês.
|
35 |
+
"""
|
36 |
+
|
37 |
+
query_wrapper_prompt = PromptTemplate(
|
38 |
+
"[INST]<<SYS>>\n" + SYSTEM_PROMPT + "<</SYS>>\n\n{query_str}[/INST] "
|
39 |
+
)
|
40 |
+
|
41 |
+
llm = HuggingFaceLLM(
|
42 |
+
context_window=4096,
|
43 |
+
max_new_tokens=2048,
|
44 |
+
generate_kwargs={"temperature": 0.0, "do_sample": False},
|
45 |
+
query_wrapper_prompt=query_wrapper_prompt,
|
46 |
+
tokenizer_name=selected_model,
|
47 |
+
model_name=selected_model,
|
48 |
+
device_map="auto",
|
49 |
+
# change these settings below depending on your GPU
|
50 |
+
# model_kwargs={"torch_dtype": torch.float16, "load_in_8bit": True},
|
51 |
+
)
|
52 |
+
|
53 |
+
|
54 |
+
embed_model = HuggingFaceEmbedding(model_name="neuralmind/bert-base-portuguese-cased")
|
55 |
+
# embed_model = FlagModel("BAAI/bge-m3")
|
56 |
+
|
57 |
+
service_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)
|
58 |
+
|
59 |
+
set_global_service_context(service_context)
|
server/ngrok_config.py
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import os
|
2 |
+
# import uvicorn
|
3 |
+
# from pyngrok import ngrok
|
4 |
+
|
5 |
+
# NGROK_KEY = os.environ["NGROK_KEY"]
|
6 |
+
|
7 |
+
# ngrok.set_auth_token(NGROK_KEY)
|
8 |
+
|
9 |
+
# port = 80
|
10 |
+
|
11 |
+
# public_url = ngrok.connect(port).public_url
|
requirements.txt → server/requirements.txt
RENAMED
@@ -1,13 +1,12 @@
|
|
1 |
-
langchain==0.1.7
|
2 |
-
fastapi==0.109.2
|
3 |
-
python-multipart==0.0.9
|
4 |
-
uvicorn==0.27.1
|
5 |
-
pypdf==4.0.1
|
6 |
-
pyngrok==7.1.2
|
7 |
-
ipywidgets==7.7.1
|
8 |
-
bitsandbytes==0.42.0
|
9 |
-
accelerate==0.27.2
|
10 |
-
llama-index==0.9.47
|
11 |
-
|
12 |
-
|
13 |
-
pyngrok
|
|
|
1 |
+
langchain==0.1.7
|
2 |
+
fastapi==0.109.2
|
3 |
+
python-multipart==0.0.9
|
4 |
+
uvicorn==0.27.1
|
5 |
+
pypdf==4.0.1
|
6 |
+
pyngrok==7.1.2
|
7 |
+
ipywidgets==7.7.1
|
8 |
+
bitsandbytes==0.42.0
|
9 |
+
accelerate==0.27.2
|
10 |
+
llama-index==0.9.47
|
11 |
+
transformers==4.37.2
|
12 |
+
torch==2.0.1
|
|
server/static/assets/index-4iaAfGg3.css
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
:root{--toastify-color-light: #fff;--toastify-color-dark: #121212;--toastify-color-info: #3498db;--toastify-color-success: #07bc0c;--toastify-color-warning: #f1c40f;--toastify-color-error: #e74c3c;--toastify-color-transparent: rgba(255, 255, 255, .7);--toastify-icon-color-info: var(--toastify-color-info);--toastify-icon-color-success: var(--toastify-color-success);--toastify-icon-color-warning: var(--toastify-color-warning);--toastify-icon-color-error: var(--toastify-color-error);--toastify-toast-width: 320px;--toastify-toast-offset: 16px;--toastify-toast-top: max(var(--toastify-toast-offset), env(safe-area-inset-top));--toastify-toast-right: max(var(--toastify-toast-offset), env(safe-area-inset-right));--toastify-toast-left: max(var(--toastify-toast-offset), env(safe-area-inset-left));--toastify-toast-bottom: max(var(--toastify-toast-offset), env(safe-area-inset-bottom));--toastify-toast-background: #fff;--toastify-toast-min-height: 64px;--toastify-toast-max-height: 800px;--toastify-toast-bd-radius: 6px;--toastify-font-family: sans-serif;--toastify-z-index: 9999;--toastify-text-color-light: #757575;--toastify-text-color-dark: #fff;--toastify-text-color-info: #fff;--toastify-text-color-success: #fff;--toastify-text-color-warning: #fff;--toastify-text-color-error: #fff;--toastify-spinner-color: #616161;--toastify-spinner-color-empty-area: #e0e0e0;--toastify-color-progress-light: linear-gradient( to right, #4cd964, #5ac8fa, #007aff, #34aadc, #5856d6, #ff2d55 );--toastify-color-progress-dark: #bb86fc;--toastify-color-progress-info: var(--toastify-color-info);--toastify-color-progress-success: var(--toastify-color-success);--toastify-color-progress-warning: var(--toastify-color-warning);--toastify-color-progress-error: var(--toastify-color-error);--toastify-color-progress-bgo: .2}.Toastify__toast-container{z-index:var(--toastify-z-index);-webkit-transform:translate3d(0,0,var(--toastify-z-index));position:fixed;padding:4px;width:var(--toastify-toast-width);box-sizing:border-box;color:#fff}.Toastify__toast-container--top-left{top:var(--toastify-toast-top);left:var(--toastify-toast-left)}.Toastify__toast-container--top-center{top:var(--toastify-toast-top);left:50%;transform:translate(-50%)}.Toastify__toast-container--top-right{top:var(--toastify-toast-top);right:var(--toastify-toast-right)}.Toastify__toast-container--bottom-left{bottom:var(--toastify-toast-bottom);left:var(--toastify-toast-left)}.Toastify__toast-container--bottom-center{bottom:var(--toastify-toast-bottom);left:50%;transform:translate(-50%)}.Toastify__toast-container--bottom-right{bottom:var(--toastify-toast-bottom);right:var(--toastify-toast-right)}@media only screen and (max-width : 480px){.Toastify__toast-container{width:100vw;padding:0;left:env(safe-area-inset-left);margin:0}.Toastify__toast-container--top-left,.Toastify__toast-container--top-center,.Toastify__toast-container--top-right{top:env(safe-area-inset-top);transform:translate(0)}.Toastify__toast-container--bottom-left,.Toastify__toast-container--bottom-center,.Toastify__toast-container--bottom-right{bottom:env(safe-area-inset-bottom);transform:translate(0)}.Toastify__toast-container--rtl{right:env(safe-area-inset-right);left:initial}}.Toastify__toast{--y: 0;position:relative;touch-action:none;min-height:var(--toastify-toast-min-height);box-sizing:border-box;margin-bottom:1rem;padding:8px;border-radius:var(--toastify-toast-bd-radius);box-shadow:0 4px 12px #0000001a;display:flex;justify-content:space-between;max-height:var(--toastify-toast-max-height);font-family:var(--toastify-font-family);cursor:default;direction:ltr;z-index:0;overflow:hidden}.Toastify__toast--stacked{position:absolute;width:100%;transform:translate3d(0,var(--y),0) scale(var(--s));transition:transform .3s}.Toastify__toast--stacked[data-collapsed] .Toastify__toast-body,.Toastify__toast--stacked[data-collapsed] .Toastify__close-button{transition:opacity .1s}.Toastify__toast--stacked[data-collapsed=false]{overflow:visible}.Toastify__toast--stacked[data-collapsed=true]:not(:last-child)>*{opacity:0}.Toastify__toast--stacked:after{content:"";position:absolute;left:0;right:0;height:calc(var(--g) * 1px);bottom:100%}.Toastify__toast--stacked[data-pos=top]{top:0}.Toastify__toast--stacked[data-pos=bot]{bottom:0}.Toastify__toast--stacked[data-pos=bot].Toastify__toast--stacked:before{transform-origin:top}.Toastify__toast--stacked[data-pos=top].Toastify__toast--stacked:before{transform-origin:bottom}.Toastify__toast--stacked:before{content:"";position:absolute;left:0;right:0;bottom:0;height:100%;transform:scaleY(3);z-index:-1}.Toastify__toast--rtl{direction:rtl}.Toastify__toast--close-on-click{cursor:pointer}.Toastify__toast-body{margin:auto 0;flex:1 1 auto;padding:6px;display:flex;align-items:center}.Toastify__toast-body>div:last-child{word-break:break-word;flex:1}.Toastify__toast-icon{margin-inline-end:10px;width:20px;flex-shrink:0;display:flex}.Toastify--animate{animation-fill-mode:both;animation-duration:.5s}.Toastify--animate-icon{animation-fill-mode:both;animation-duration:.3s}@media only screen and (max-width : 480px){.Toastify__toast{margin-bottom:0;border-radius:0}}.Toastify__toast-theme--dark{background:var(--toastify-color-dark);color:var(--toastify-text-color-dark)}.Toastify__toast-theme--light,.Toastify__toast-theme--colored.Toastify__toast--default{background:var(--toastify-color-light);color:var(--toastify-text-color-light)}.Toastify__toast-theme--colored.Toastify__toast--info{color:var(--toastify-text-color-info);background:var(--toastify-color-info)}.Toastify__toast-theme--colored.Toastify__toast--success{color:var(--toastify-text-color-success);background:var(--toastify-color-success)}.Toastify__toast-theme--colored.Toastify__toast--warning{color:var(--toastify-text-color-warning);background:var(--toastify-color-warning)}.Toastify__toast-theme--colored.Toastify__toast--error{color:var(--toastify-text-color-error);background:var(--toastify-color-error)}.Toastify__progress-bar-theme--light{background:var(--toastify-color-progress-light)}.Toastify__progress-bar-theme--dark{background:var(--toastify-color-progress-dark)}.Toastify__progress-bar--info{background:var(--toastify-color-progress-info)}.Toastify__progress-bar--success{background:var(--toastify-color-progress-success)}.Toastify__progress-bar--warning{background:var(--toastify-color-progress-warning)}.Toastify__progress-bar--error{background:var(--toastify-color-progress-error)}.Toastify__progress-bar-theme--colored.Toastify__progress-bar--info,.Toastify__progress-bar-theme--colored.Toastify__progress-bar--success,.Toastify__progress-bar-theme--colored.Toastify__progress-bar--warning,.Toastify__progress-bar-theme--colored.Toastify__progress-bar--error{background:var(--toastify-color-transparent)}.Toastify__close-button{color:#fff;background:transparent;outline:none;border:none;padding:0;cursor:pointer;opacity:.7;transition:.3s ease;align-self:flex-start;z-index:1}.Toastify__close-button--light{color:#000;opacity:.3}.Toastify__close-button>svg{fill:currentColor;height:16px;width:14px}.Toastify__close-button:hover,.Toastify__close-button:focus{opacity:1}@keyframes Toastify__trackProgress{0%{transform:scaleX(1)}to{transform:scaleX(0)}}.Toastify__progress-bar{position:absolute;bottom:0;left:0;width:100%;height:100%;z-index:var(--toastify-z-index);opacity:.7;transform-origin:left;border-bottom-left-radius:var(--toastify-toast-bd-radius)}.Toastify__progress-bar--animated{animation:Toastify__trackProgress linear 1 forwards}.Toastify__progress-bar--controlled{transition:transform .2s}.Toastify__progress-bar--rtl{right:0;left:initial;transform-origin:right;border-bottom-left-radius:initial;border-bottom-right-radius:var(--toastify-toast-bd-radius)}.Toastify__progress-bar--wrp{position:absolute;bottom:0;left:0;width:100%;height:5px;border-bottom-left-radius:var(--toastify-toast-bd-radius)}.Toastify__progress-bar--wrp[data-hidden=true]{opacity:0}.Toastify__progress-bar--bg{opacity:var(--toastify-color-progress-bgo);width:100%;height:100%}.Toastify__spinner{width:20px;height:20px;box-sizing:border-box;border:2px solid;border-radius:100%;border-color:var(--toastify-spinner-color-empty-area);border-right-color:var(--toastify-spinner-color);animation:Toastify__spin .65s linear infinite}@keyframes Toastify__bounceInRight{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(3000px,0,0)}60%{opacity:1;transform:translate3d(-25px,0,0)}75%{transform:translate3d(10px,0,0)}90%{transform:translate3d(-5px,0,0)}to{transform:none}}@keyframes Toastify__bounceOutRight{20%{opacity:1;transform:translate3d(-20px,var(--y),0)}to{opacity:0;transform:translate3d(2000px,var(--y),0)}}@keyframes Toastify__bounceInLeft{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(-3000px,0,0)}60%{opacity:1;transform:translate3d(25px,0,0)}75%{transform:translate3d(-10px,0,0)}90%{transform:translate3d(5px,0,0)}to{transform:none}}@keyframes Toastify__bounceOutLeft{20%{opacity:1;transform:translate3d(20px,var(--y),0)}to{opacity:0;transform:translate3d(-2000px,var(--y),0)}}@keyframes Toastify__bounceInUp{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(0,3000px,0)}60%{opacity:1;transform:translate3d(0,-20px,0)}75%{transform:translate3d(0,10px,0)}90%{transform:translate3d(0,-5px,0)}to{transform:translateZ(0)}}@keyframes Toastify__bounceOutUp{20%{transform:translate3d(0,calc(var(--y) - 10px),0)}40%,45%{opacity:1;transform:translate3d(0,calc(var(--y) + 20px),0)}to{opacity:0;transform:translate3d(0,-2000px,0)}}@keyframes Toastify__bounceInDown{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(0,-3000px,0)}60%{opacity:1;transform:translate3d(0,25px,0)}75%{transform:translate3d(0,-10px,0)}90%{transform:translate3d(0,5px,0)}to{transform:none}}@keyframes Toastify__bounceOutDown{20%{transform:translate3d(0,calc(var(--y) - 10px),0)}40%,45%{opacity:1;transform:translate3d(0,calc(var(--y) + 20px),0)}to{opacity:0;transform:translate3d(0,2000px,0)}}.Toastify__bounce-enter--top-left,.Toastify__bounce-enter--bottom-left{animation-name:Toastify__bounceInLeft}.Toastify__bounce-enter--top-right,.Toastify__bounce-enter--bottom-right{animation-name:Toastify__bounceInRight}.Toastify__bounce-enter--top-center{animation-name:Toastify__bounceInDown}.Toastify__bounce-enter--bottom-center{animation-name:Toastify__bounceInUp}.Toastify__bounce-exit--top-left,.Toastify__bounce-exit--bottom-left{animation-name:Toastify__bounceOutLeft}.Toastify__bounce-exit--top-right,.Toastify__bounce-exit--bottom-right{animation-name:Toastify__bounceOutRight}.Toastify__bounce-exit--top-center{animation-name:Toastify__bounceOutUp}.Toastify__bounce-exit--bottom-center{animation-name:Toastify__bounceOutDown}@keyframes Toastify__zoomIn{0%{opacity:0;transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes Toastify__zoomOut{0%{opacity:1}50%{opacity:0;transform:translate3d(0,var(--y),0) scale3d(.3,.3,.3)}to{opacity:0}}.Toastify__zoom-enter{animation-name:Toastify__zoomIn}.Toastify__zoom-exit{animation-name:Toastify__zoomOut}@keyframes Toastify__flipIn{0%{transform:perspective(400px) rotateX(90deg);animation-timing-function:ease-in;opacity:0}40%{transform:perspective(400px) rotateX(-20deg);animation-timing-function:ease-in}60%{transform:perspective(400px) rotateX(10deg);opacity:1}80%{transform:perspective(400px) rotateX(-5deg)}to{transform:perspective(400px)}}@keyframes Toastify__flipOut{0%{transform:translate3d(0,var(--y),0) perspective(400px)}30%{transform:translate3d(0,var(--y),0) perspective(400px) rotateX(-20deg);opacity:1}to{transform:translate3d(0,var(--y),0) perspective(400px) rotateX(90deg);opacity:0}}.Toastify__flip-enter{animation-name:Toastify__flipIn}.Toastify__flip-exit{animation-name:Toastify__flipOut}@keyframes Toastify__slideInRight{0%{transform:translate3d(110%,0,0);visibility:visible}to{transform:translate3d(0,var(--y),0)}}@keyframes Toastify__slideInLeft{0%{transform:translate3d(-110%,0,0);visibility:visible}to{transform:translate3d(0,var(--y),0)}}@keyframes Toastify__slideInUp{0%{transform:translate3d(0,110%,0);visibility:visible}to{transform:translate3d(0,var(--y),0)}}@keyframes Toastify__slideInDown{0%{transform:translate3d(0,-110%,0);visibility:visible}to{transform:translate3d(0,var(--y),0)}}@keyframes Toastify__slideOutRight{0%{transform:translate3d(0,var(--y),0)}to{visibility:hidden;transform:translate3d(110%,var(--y),0)}}@keyframes Toastify__slideOutLeft{0%{transform:translate3d(0,var(--y),0)}to{visibility:hidden;transform:translate3d(-110%,var(--y),0)}}@keyframes Toastify__slideOutDown{0%{transform:translate3d(0,var(--y),0)}to{visibility:hidden;transform:translate3d(0,500px,0)}}@keyframes Toastify__slideOutUp{0%{transform:translate3d(0,var(--y),0)}to{visibility:hidden;transform:translate3d(0,-500px,0)}}.Toastify__slide-enter--top-left,.Toastify__slide-enter--bottom-left{animation-name:Toastify__slideInLeft}.Toastify__slide-enter--top-right,.Toastify__slide-enter--bottom-right{animation-name:Toastify__slideInRight}.Toastify__slide-enter--top-center{animation-name:Toastify__slideInDown}.Toastify__slide-enter--bottom-center{animation-name:Toastify__slideInUp}.Toastify__slide-exit--top-left,.Toastify__slide-exit--bottom-left{animation-name:Toastify__slideOutLeft;animation-timing-function:ease-in;animation-duration:.3s}.Toastify__slide-exit--top-right,.Toastify__slide-exit--bottom-right{animation-name:Toastify__slideOutRight;animation-timing-function:ease-in;animation-duration:.3s}.Toastify__slide-exit--top-center{animation-name:Toastify__slideOutUp;animation-timing-function:ease-in;animation-duration:.3s}.Toastify__slide-exit--bottom-center{animation-name:Toastify__slideOutDown;animation-timing-function:ease-in;animation-duration:.3s}@keyframes Toastify__spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.absolute{position:absolute}.left-4{left:1rem}.top-0{top:0}.top-4{top:1rem}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mr-4{margin-right:1rem}.mr-auto{margin-right:auto}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.flex{display:flex}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.aspect-video{aspect-ratio:16 / 9}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-48{width:12rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.min-w-\[200px\]{min-width:200px}.max-w-\[80\%\]{max-width:80%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.self-end{align-self:flex-end}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.border-b{border-bottom-width:1px}.bg-\[\#131318\]{--tw-bg-opacity: 1;background-color:rgb(19 19 24 / var(--tw-bg-opacity))}.bg-\[\#7159c1\]{--tw-bg-opacity: 1;background-color:rgb(113 89 193 / var(--tw-bg-opacity))}.bg-black\/80{background-color:#000c}.bg-sky-600{--tw-bg-opacity: 1;background-color:rgb(2 132 199 / var(--tw-bg-opacity))}.bg-slate-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.bg-slate-600{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.fill-sky-800{fill:#075985}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-center{text-align:center}.text-xs{font-size:.75rem;line-height:1rem}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-white{--tw-ring-opacity: 1;--tw-ring-color: rgb(255 255 255 / var(--tw-ring-opacity))}.transition-\[flex-grow\]{transition-property:flex-grow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.hover\:brightness-110:hover{--tw-brightness: brightness(1.1);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.disabled\:brightness-75:disabled{--tw-brightness: brightness(.75);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.disabled\:hover\:brightness-75:hover:disabled{--tw-brightness: brightness(.75);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}@media (min-width: 768px){.md\:sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.md\:static{position:static}.md\:w-full{width:100%}.md\:animate-none{animation:none}.md\:border-l-2{border-left-width:2px}.md\:border-l-white{--tw-border-opacity: 1;border-left-color:rgb(255 255 255 / var(--tw-border-opacity))}.md\:bg-transparent{background-color:transparent}}@media (prefers-color-scheme: dark){.dark\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}}
|
server/static/assets/index-Ovl1jNHo.js
ADDED
The diff for this file is too large to render.
See raw diff
|
|
server/static/index.html
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>Vite + React</title>
|
8 |
+
<script type="module" crossorigin src="/assets/index-Ovl1jNHo.js"></script>
|
9 |
+
<link rel="stylesheet" crossorigin href="/assets/index-4iaAfGg3.css">
|
10 |
+
</head>
|
11 |
+
<body>
|
12 |
+
<div id="root"></div>
|
13 |
+
</body>
|
14 |
+
</html>
|
server/static/vite.svg
ADDED
web/.eslintrc.cjs
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module.exports = {
|
2 |
+
root: true,
|
3 |
+
env: { browser: true, es2020: true },
|
4 |
+
extends: [
|
5 |
+
'eslint:recommended',
|
6 |
+
'plugin:react/recommended',
|
7 |
+
'plugin:react/jsx-runtime',
|
8 |
+
'plugin:react-hooks/recommended',
|
9 |
+
],
|
10 |
+
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
11 |
+
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
12 |
+
settings: { react: { version: '18.2' } },
|
13 |
+
plugins: ['react-refresh'],
|
14 |
+
rules: {
|
15 |
+
'react/jsx-no-target-blank': 'off',
|
16 |
+
'react-refresh/only-export-components': [
|
17 |
+
'warn',
|
18 |
+
{ allowConstantExport: true },
|
19 |
+
],
|
20 |
+
},
|
21 |
+
}
|
web/README.md
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# React + Vite
|
2 |
+
|
3 |
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
4 |
+
|
5 |
+
Currently, two official plugins are available:
|
6 |
+
|
7 |
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
8 |
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
web/index.html
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6 |
+
<title>Rag Chat</title>
|
7 |
+
</head>
|
8 |
+
<body>
|
9 |
+
<div id="root" class=""></div>
|
10 |
+
<script type="module" src="/src/main.jsx"></script>
|
11 |
+
</body>
|
12 |
+
</html>
|
web/package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
web/package.json
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "front-end",
|
3 |
+
"private": true,
|
4 |
+
"version": "0.0.0",
|
5 |
+
"type": "module",
|
6 |
+
"scripts": {
|
7 |
+
"dev": "vite",
|
8 |
+
"build": "vite build",
|
9 |
+
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
10 |
+
"preview": "vite preview"
|
11 |
+
},
|
12 |
+
"dependencies": {
|
13 |
+
"ai": "^3.0.5",
|
14 |
+
"axios": "^1.6.7",
|
15 |
+
"langchain": "^0.1.25",
|
16 |
+
"lucide-react": "^0.344.0",
|
17 |
+
"react": "^18.2.0",
|
18 |
+
"react-dom": "^18.2.0",
|
19 |
+
"react-toastify": "^10.0.4"
|
20 |
+
},
|
21 |
+
"devDependencies": {
|
22 |
+
"@types/react": "^18.2.56",
|
23 |
+
"@types/react-dom": "^18.2.19",
|
24 |
+
"@vitejs/plugin-react": "^4.2.1",
|
25 |
+
"autoprefixer": "^10.4.18",
|
26 |
+
"eslint": "^8.56.0",
|
27 |
+
"eslint-plugin-react": "^7.33.2",
|
28 |
+
"eslint-plugin-react-hooks": "^4.6.0",
|
29 |
+
"eslint-plugin-react-refresh": "^0.4.5",
|
30 |
+
"postcss": "^8.4.35",
|
31 |
+
"tailwindcss": "^3.4.1",
|
32 |
+
"vite": "^5.1.4"
|
33 |
+
}
|
34 |
+
}
|
web/postcss.config.js
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
}
|
web/src/App.jsx
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ChatWindow } from "./components/ChatWindow";
|
2 |
+
import { UploadDocumentsForm } from "./components/UploadDocumentsForm";
|
3 |
+
|
4 |
+
export default function App() {
|
5 |
+
|
6 |
+
return (
|
7 |
+
<div className="flex min-h-screen bg-[#131318] text-white">
|
8 |
+
<UploadDocumentsForm></UploadDocumentsForm>
|
9 |
+
<ChatWindow
|
10 |
+
endpoint={import.meta.env.VITE_PUBLIC_API_URL + "/retriveal/stream"}
|
11 |
+
></ChatWindow>
|
12 |
+
</div>
|
13 |
+
);
|
14 |
+
}
|
web/src/assets/react.svg
ADDED
web/src/components/ChatMessageBubble.jsx
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function ChatMessageBubble(props) {
|
2 |
+
const colorClassName =
|
3 |
+
props.message.role === "user" ? "bg-sky-600" : "bg-slate-50 text-black";
|
4 |
+
const alignmentClassName =
|
5 |
+
props.message.role === "user" ? "ml-auto" : "mr-auto";
|
6 |
+
return (
|
7 |
+
<div
|
8 |
+
className={`${alignmentClassName} ${colorClassName} rounded px-4 py-2 max-w-[80%] mb-8 flex`}
|
9 |
+
>
|
10 |
+
<div className="whitespace-pre-wrap flex flex-col">
|
11 |
+
<span>{props.message.content}</span>
|
12 |
+
{props.sources && props.sources.length ? <>
|
13 |
+
<code className="mt-4 mr-auto bg-slate-600 px-2 py-1 rounded">
|
14 |
+
<h2>
|
15 |
+
🔍 Sources:
|
16 |
+
</h2>
|
17 |
+
</code>
|
18 |
+
<code className="mt-1 mr-2 bg-slate-600 px-2 py-1 rounded text-xs">
|
19 |
+
{props.sources?.map((source, i) => (
|
20 |
+
<div className="mt-2" key={"source:" + i}>
|
21 |
+
{i + 1}. "{source.pageContent}"{
|
22 |
+
source.metadata?.loc?.lines !== undefined
|
23 |
+
? <div><br/>Lines {source.metadata?.loc?.lines?.from} to {source.metadata?.loc?.lines?.to}</div>
|
24 |
+
: ""
|
25 |
+
}
|
26 |
+
</div>
|
27 |
+
))}
|
28 |
+
</code>
|
29 |
+
</> : ""}
|
30 |
+
</div>
|
31 |
+
</div>
|
32 |
+
);
|
33 |
+
}
|
web/src/components/ChatWindow.jsx
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ToastContainer, toast } from "react-toastify";
|
2 |
+
import "react-toastify/dist/ReactToastify.css";
|
3 |
+
|
4 |
+
import { useState, useEffect } from "react";
|
5 |
+
|
6 |
+
import { ChatMessageBubble } from "./ChatMessageBubble";
|
7 |
+
import { api } from "../lib/axios";
|
8 |
+
|
9 |
+
export function ChatWindow({
|
10 |
+
endpoint,
|
11 |
+
placeholder,
|
12 |
+
showIngestForm,
|
13 |
+
showIntermediateStepsToggle
|
14 |
+
}) {
|
15 |
+
const [messages, setMessages] = useState([]);
|
16 |
+
const [input, setInput] = useState("");
|
17 |
+
const [isLoading, setIsLoading] = useState(false);
|
18 |
+
const [sourcesForMessages, setSourcesForMessages] = useState({});
|
19 |
+
|
20 |
+
const handleInputChange = (e) =>
|
21 |
+
setInput(e.currentTarget.value);
|
22 |
+
|
23 |
+
const sendMessage = async (e) => {
|
24 |
+
e.preventDefault();
|
25 |
+
|
26 |
+
setIsLoading(true);
|
27 |
+
|
28 |
+
if (sessionStorage.getItem("ragChat@sessiorId") === null) {
|
29 |
+
toast("É necessário enviar um arquivo!", {
|
30 |
+
theme: "dark",
|
31 |
+
});
|
32 |
+
}
|
33 |
+
|
34 |
+
if (!input) {
|
35 |
+
setIsLoading(false);
|
36 |
+
return;
|
37 |
+
}
|
38 |
+
|
39 |
+
const tmp = "Me responda isso em português: " + input;
|
40 |
+
|
41 |
+
setInput("");
|
42 |
+
|
43 |
+
if (!messages.length) {
|
44 |
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
45 |
+
}
|
46 |
+
|
47 |
+
setMessages(
|
48 |
+
messages.concat({
|
49 |
+
id: messages.length.toString(),
|
50 |
+
content: input,
|
51 |
+
role: "user",
|
52 |
+
}),
|
53 |
+
);
|
54 |
+
|
55 |
+
try {
|
56 |
+
const response = await api.post(
|
57 |
+
`/retriveal/stream/${sessionStorage.getItem("ragChat@sessiorId")}`,
|
58 |
+
{ content: tmp }
|
59 |
+
);
|
60 |
+
|
61 |
+
|
62 |
+
const json = response.data;
|
63 |
+
|
64 |
+
if (response.status === 200) {
|
65 |
+
|
66 |
+
|
67 |
+
setMessages((prevState) => {
|
68 |
+
const prev = [...prevState];
|
69 |
+
|
70 |
+
prev.push({
|
71 |
+
id: prev.length.toString(),
|
72 |
+
content: json.replace("</s>", " "),
|
73 |
+
role: "assistant",
|
74 |
+
});
|
75 |
+
|
76 |
+
return prev;
|
77 |
+
});
|
78 |
+
} else {
|
79 |
+
if (json.error) {
|
80 |
+
toast(json.error, {
|
81 |
+
theme: "dark",
|
82 |
+
});
|
83 |
+
throw new Error(json.error);
|
84 |
+
}
|
85 |
+
}
|
86 |
+
} catch (error) {
|
87 |
+
toast(error.message, {
|
88 |
+
theme: "dark",
|
89 |
+
});
|
90 |
+
}
|
91 |
+
|
92 |
+
setIsLoading(false);
|
93 |
+
};
|
94 |
+
|
95 |
+
// Preciso que quando essa página for fechada o sessionStorage, alertar o usuário que ele perderá os dados
|
96 |
+
// e enviar uma requisição para o servidor para que ele apague os dados do usuário
|
97 |
+
useEffect(() => {
|
98 |
+
|
99 |
+
const listener = () => {
|
100 |
+
const session = sessionStorage.getItem("ragChat@sessiorId");
|
101 |
+
|
102 |
+
if (!session) return;
|
103 |
+
try {
|
104 |
+
navigator.sendBeacon(`/session/${session}`);
|
105 |
+
sessionStorage.removeItem("ragChat@sessiorId");
|
106 |
+
sessionStorage.removeItem("uploadedDocuments");
|
107 |
+
} catch (error) {
|
108 |
+
console.log(error);
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
window.addEventListener("unload", listener);
|
113 |
+
|
114 |
+
return () => window.removeEventListener("unload", listener)
|
115 |
+
}, []);
|
116 |
+
|
117 |
+
return (
|
118 |
+
<div
|
119 |
+
className={`flex w-full max-h-screen flex-2 flex-col items-center p-4 grow overflow-hidden md:border-l-2 md:border-l-white`}
|
120 |
+
>
|
121 |
+
<div className="flex flex-col-reverse w-full mb-4 overflow-auto transition-[flex-grow] ease-in-out grow p-2.5">
|
122 |
+
{messages.length > 0
|
123 |
+
? [...messages].reverse().map((m, i) => {
|
124 |
+
const sourceKey = (messages.length - 1 - i).toString();
|
125 |
+
return (<ChatMessageBubble
|
126 |
+
key={m.id}
|
127 |
+
message={m}
|
128 |
+
sources={sourcesForMessages[sourceKey]}
|
129 |
+
></ChatMessageBubble>)
|
130 |
+
})
|
131 |
+
: ""}
|
132 |
+
</div>
|
133 |
+
|
134 |
+
<form onSubmit={sendMessage} className="flex w-full flex-col">
|
135 |
+
<div className="flex w-full mt-4">
|
136 |
+
<input
|
137 |
+
className="grow mr-2 p-2.5 rounded outline-none text-black"
|
138 |
+
value={input}
|
139 |
+
placeholder={"Me faça uma pergunta!"}
|
140 |
+
onChange={handleInputChange}
|
141 |
+
/>
|
142 |
+
<button
|
143 |
+
type="submit"
|
144 |
+
className="shrink-0 px-4 bg-[#7159c1] rounded hover:brightness-110 transition-all"
|
145 |
+
>
|
146 |
+
<div role="status" className={`flex justify-center`}>
|
147 |
+
{isLoading ? (
|
148 |
+
<>
|
149 |
+
<svg
|
150 |
+
aria-hidden="true"
|
151 |
+
className="w-6 h-6 text-white animate-spin dark:text-white fill-sky-800"
|
152 |
+
viewBox="0 0 100 101"
|
153 |
+
fill="none"
|
154 |
+
xmlns="http://www.w3.org/2000/svg"
|
155 |
+
>
|
156 |
+
<path
|
157 |
+
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
158 |
+
fill="currentColor"
|
159 |
+
/>
|
160 |
+
<path
|
161 |
+
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
162 |
+
fill="currentFill"
|
163 |
+
/>
|
164 |
+
</svg>
|
165 |
+
<span className="sr-only">Loading...</span>
|
166 |
+
</>
|
167 |
+
) : (
|
168 |
+
<span>Send</span>
|
169 |
+
)}
|
170 |
+
</div>
|
171 |
+
</button>
|
172 |
+
</div>
|
173 |
+
</form>
|
174 |
+
<ToastContainer />
|
175 |
+
</div>
|
176 |
+
);
|
177 |
+
}
|
web/src/components/Navbar.jsx
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client";
|
2 |
+
|
3 |
+
import { usePathname } from 'next/navigation';
|
4 |
+
|
5 |
+
export function Navbar() {
|
6 |
+
const pathname = usePathname();
|
7 |
+
return (
|
8 |
+
<nav className="mb-4">
|
9 |
+
<a className={`mr-4 ${pathname === "/" ? "text-white border-b" : ""}`} href="/">🏴☠️ Chat</a>
|
10 |
+
<a className={`mr-4 ${pathname === "/structured_output" ? "text-white border-b" : ""}`} href="/structured_output">🧱 Structured Output</a>
|
11 |
+
<a className={`mr-4 ${pathname === "/agents" ? "text-white border-b" : ""}`} href="/agents">🦜 Agents</a>
|
12 |
+
<a className={`mr-4 ${pathname === "/retrieval" ? "text-white border-b" : ""}`} href="/retrieval">🐶 Retrieval</a>
|
13 |
+
<a className={`mr-4 ${pathname === "/retrieval_agents" ? "text-white border-b" : ""}`} href="/retrieval_agents">🤖 Retrieval Agents</a>
|
14 |
+
</nav>
|
15 |
+
);
|
16 |
+
}
|
web/src/components/UploadDocumentsForm.jsx
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState, useRef, useEffect } from "react";
|
2 |
+
import { ChevronLeft, FileText, Menu } from "lucide-react";
|
3 |
+
|
4 |
+
export function UploadDocumentsForm() {
|
5 |
+
const [isLoading, setIsLoading] = useState(false);
|
6 |
+
const [document, setDocument] = useState(null);
|
7 |
+
const [uploadedDocuments, setUploadedDocuments] = useState([]);
|
8 |
+
const formRef = useRef(null);
|
9 |
+
const inputRef = useRef(null);
|
10 |
+
|
11 |
+
const [formState, setFormState] = useState(true);
|
12 |
+
|
13 |
+
const [errorMessage, setErrorMessage] = useState("");
|
14 |
+
|
15 |
+
const handleFileInput = (event) => {
|
16 |
+
setDocument(event.target.files[0]);
|
17 |
+
};
|
18 |
+
|
19 |
+
|
20 |
+
|
21 |
+
const ingest = async (e) => {
|
22 |
+
e.preventDefault();
|
23 |
+
setIsLoading(true);
|
24 |
+
|
25 |
+
if (!document) {
|
26 |
+
setErrorMessage("Selecione um documento para enviar");
|
27 |
+
setIsLoading(false);
|
28 |
+
return;
|
29 |
+
}
|
30 |
+
|
31 |
+
const isDocumentUploaded = uploadedDocuments.find(
|
32 |
+
(doc) => doc.name === document.name,
|
33 |
+
);
|
34 |
+
|
35 |
+
if (isDocumentUploaded) {
|
36 |
+
setErrorMessage("Este documento já foi enviado");
|
37 |
+
setIsLoading(false);
|
38 |
+
return;
|
39 |
+
}
|
40 |
+
|
41 |
+
try {
|
42 |
+
const formData = new FormData();
|
43 |
+
formData.append("file", document);
|
44 |
+
|
45 |
+
const url = sessionStorage.getItem("ragChat@sessiorId")
|
46 |
+
? `/retriveal/ingest/${sessionStorage.getItem("ragChat@sessiorId")}`
|
47 |
+
: "/retriveal/ingest";
|
48 |
+
|
49 |
+
const response = await fetch(url, {
|
50 |
+
method: 'POST',
|
51 |
+
body: formData,
|
52 |
+
});
|
53 |
+
|
54 |
+
if (response.status === 200) {
|
55 |
+
console.log("Document ingested successfully");
|
56 |
+
|
57 |
+
const data = await response.json()
|
58 |
+
|
59 |
+
// Salva o nome do arquivo e o tamanho do arquivo no estado uploadedDocuments
|
60 |
+
setUploadedDocuments((prevState) => {
|
61 |
+
return [...prevState, { name: document.name, size: document.size }];
|
62 |
+
});
|
63 |
+
|
64 |
+
// Salva essas informações no sessionStorage
|
65 |
+
sessionStorage.setItem(
|
66 |
+
"uploadedDocuments",
|
67 |
+
JSON.stringify(uploadedDocuments),
|
68 |
+
);
|
69 |
+
|
70 |
+
// Salva id recebido no sessionStorage
|
71 |
+
if (!sessionStorage.getItem("ragChat@sessiorId"))
|
72 |
+
sessionStorage.setItem("ragChat@sessiorId", data.uuid);
|
73 |
+
|
74 |
+
setErrorMessage("");
|
75 |
+
setDocument(null);
|
76 |
+
if (inputRef.current) inputRef.current.value = "";
|
77 |
+
} else if (response.status < 500) {
|
78 |
+
console.error("Error ingesting document");
|
79 |
+
console.log(await response.text())
|
80 |
+
setErrorMessage("Erro ao enviar o documento! Recurso não encontrado.");
|
81 |
+
}
|
82 |
+
} catch (error) {
|
83 |
+
console.error("Error ingesting document", error);
|
84 |
+
setErrorMessage(
|
85 |
+
"Erro ao enviar o documento! Tente novamente mais tarde.",
|
86 |
+
);
|
87 |
+
}
|
88 |
+
|
89 |
+
setIsLoading(false);
|
90 |
+
};
|
91 |
+
|
92 |
+
useEffect(() => {
|
93 |
+
const savedDocuments = sessionStorage.getItem("uploadedDocuments");
|
94 |
+
if (savedDocuments) {
|
95 |
+
setUploadedDocuments(JSON.parse(savedDocuments));
|
96 |
+
}
|
97 |
+
|
98 |
+
if (window.innerWidth) setFormState(window.innerWidth > 768)
|
99 |
+
|
100 |
+
window.addEventListener("resize", () => {
|
101 |
+
if (window.innerWidth > 768) {
|
102 |
+
setFormState(true);
|
103 |
+
} else {
|
104 |
+
setFormState(false);
|
105 |
+
}
|
106 |
+
});
|
107 |
+
|
108 |
+
}, []);
|
109 |
+
|
110 |
+
return (
|
111 |
+
<>
|
112 |
+
<Menu className="w-8 h-8 aspect-square md:sr-only absolute top-4 left-4" onClick={e => setFormState(prev => !prev)} />
|
113 |
+
|
114 |
+
<form
|
115 |
+
onSubmit={ingest}
|
116 |
+
ref={formRef}
|
117 |
+
className={`absolute h-full top-0 bg-black/80 md:bg-transparent md:static flex flex-col md:w-full flex-1 p-4 gap-2.5 min-w-[200px] ${formState ? "animate-slideIn md:animate-none" : "-translate-x-full transition-all bg-transparent"}`}
|
118 |
+
>
|
119 |
+
|
120 |
+
<ChevronLeft className="w-8 h-8 aspect-square md:sr-only self-end" onClick={e => setFormState(prev => !prev)} />
|
121 |
+
|
122 |
+
<input
|
123 |
+
type="file"
|
124 |
+
id="document-input"
|
125 |
+
className="sr-only"
|
126 |
+
accept=".pdf,.doc,.docx"
|
127 |
+
onInput={handleFileInput}
|
128 |
+
ref={inputRef}
|
129 |
+
/>
|
130 |
+
|
131 |
+
<label htmlFor="document-input">
|
132 |
+
<div className="w-48 md:w-full aspect-video flex flex-col gap-1 items-center p-2.5 justify-center ring-2 ring-white rounded cursor-pointer">
|
133 |
+
<FileText />
|
134 |
+
<span className="text-center text-xs">
|
135 |
+
{document && document.name
|
136 |
+
? document.name.length > 15
|
137 |
+
? document.name.substring(0, 15) +
|
138 |
+
"..." +
|
139 |
+
document.name.substring(document.name.length - 5)
|
140 |
+
: document.name
|
141 |
+
: "Faça o upload de um documento"}
|
142 |
+
</span>
|
143 |
+
</div>
|
144 |
+
</label>
|
145 |
+
|
146 |
+
<button
|
147 |
+
type="submit"
|
148 |
+
className="shrink-0 p-2.5 bg-[#7159c1] rounded w-full hover:brightness-110 transition-all disabled:hover:brightness-75 disabled:brightness-75"
|
149 |
+
disabled={isLoading || !document}
|
150 |
+
>
|
151 |
+
<div
|
152 |
+
role="status"
|
153 |
+
className={`${isLoading ? "" : "hidden"} flex justify-center`}
|
154 |
+
>
|
155 |
+
<svg
|
156 |
+
aria-hidden="true"
|
157 |
+
className="w-6 h-6 text-white animate-spin dark:text-white fill-sky-800"
|
158 |
+
viewBox="0 0 100 101"
|
159 |
+
fill="none"
|
160 |
+
xmlns="http://www.w3.org/2000/svg"
|
161 |
+
>
|
162 |
+
<path
|
163 |
+
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
164 |
+
fill="currentColor"
|
165 |
+
/>
|
166 |
+
<path
|
167 |
+
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
168 |
+
fill="currentFill"
|
169 |
+
/>
|
170 |
+
</svg>
|
171 |
+
<span className="sr-only">Loading...</span>
|
172 |
+
</div>
|
173 |
+
<span className={isLoading ? "hidden" : ""}>Enviar</span>
|
174 |
+
</button>
|
175 |
+
|
176 |
+
<div className="flex flex-col gap-2.5 w-full">
|
177 |
+
{uploadedDocuments.map((doc, index) => (
|
178 |
+
<div
|
179 |
+
key={index}
|
180 |
+
className="flex justify-between items-center text-xs p-2.5 ring-2 ring-white rounded"
|
181 |
+
>
|
182 |
+
<FileText />
|
183 |
+
<span>
|
184 |
+
{doc.name.length > 10
|
185 |
+
? doc.name.substring(0, 10) +
|
186 |
+
"..." +
|
187 |
+
doc.name.substring(doc.name.length - 5)
|
188 |
+
: doc.name}
|
189 |
+
</span>
|
190 |
+
</div>
|
191 |
+
))}
|
192 |
+
</div>
|
193 |
+
|
194 |
+
{errorMessage && (
|
195 |
+
<div className="flex flex-col gap-2.5 w-full">
|
196 |
+
<span className="text-red-500">{errorMessage}</span>
|
197 |
+
</div>
|
198 |
+
)}
|
199 |
+
</form>
|
200 |
+
</>
|
201 |
+
);
|
202 |
+
}
|
web/src/index.css
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@tailwind base;
|
2 |
+
@tailwind components;
|
3 |
+
@tailwind utilities;
|
4 |
+
|
5 |
+
::-webkit-scrollbar {
|
6 |
+
/* Customize the scrollbar width */
|
7 |
+
width: 10px;
|
8 |
+
}
|
9 |
+
|
10 |
+
::-webkit-scrollbar-track {
|
11 |
+
/* Customize the scrollbar track */
|
12 |
+
background-color: #131318;
|
13 |
+
border-radius: 2px;
|
14 |
+
|
15 |
+
}
|
16 |
+
|
17 |
+
::-webkit-scrollbar-thumb {
|
18 |
+
/* Customize the scrollbar thumb */
|
19 |
+
background-color: #ffffff50;
|
20 |
+
border-radius: 2px;
|
21 |
+
|
22 |
+
}
|
web/src/lib/axios.js
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import axios from "axios";
|
2 |
+
|
3 |
+
export const api = axios.create({
|
4 |
+
baseURL: "/"
|
5 |
+
});
|
web/src/main.jsx
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react'
|
2 |
+
import ReactDOM from 'react-dom/client'
|
3 |
+
import App from './App.jsx'
|
4 |
+
import './index.css'
|
5 |
+
|
6 |
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
7 |
+
<React.StrictMode>
|
8 |
+
<App />
|
9 |
+
</React.StrictMode>,
|
10 |
+
)
|
web/tailwind.config.js
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import('tailwindcss').Config} */
|
2 |
+
export default {
|
3 |
+
content: [
|
4 |
+
"./index.html",
|
5 |
+
"./src/**/*.{js,ts,jsx,tsx}",
|
6 |
+
],
|
7 |
+
theme: {
|
8 |
+
extend: {
|
9 |
+
backgroundImage: {
|
10 |
+
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
11 |
+
'gradient-conic':
|
12 |
+
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
13 |
+
},
|
14 |
+
|
15 |
+
// Annimation for lateral menu
|
16 |
+
keyframes: {
|
17 |
+
slideIn: {
|
18 |
+
'0%': { transform: 'translateX(-100%)' },
|
19 |
+
'100%': { transform: 'translateX(0)' },
|
20 |
+
},
|
21 |
+
slideOut: {
|
22 |
+
'0%': { transform: 'translateX(0)' },
|
23 |
+
'100%': { transform: 'translateX(-100%)' },
|
24 |
+
},
|
25 |
+
},
|
26 |
+
|
27 |
+
animation: {
|
28 |
+
slideIn: 'slideIn 0.5s ease-in-out',
|
29 |
+
slideOut: 'slideOut 0.5s ease-in-out',
|
30 |
+
},
|
31 |
+
|
32 |
+
},
|
33 |
+
},
|
34 |
+
plugins: [],
|
35 |
+
}
|
36 |
+
|
web/vite.config.js
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { defineConfig } from 'vite'
|
2 |
+
import react from '@vitejs/plugin-react'
|
3 |
+
|
4 |
+
// https://vitejs.dev/config/
|
5 |
+
export default defineConfig({
|
6 |
+
plugins: [react()],
|
7 |
+
build: {
|
8 |
+
outDir: "../server/static",
|
9 |
+
emptyOutDir: true
|
10 |
+
}
|
11 |
+
})
|