lucas-wa commited on
Commit
6e06893
1 Parent(s): daeddd3

Test uni module

Browse files
.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 python:3.10.12
2
-
3
- WORKDIR /code
4
-
5
- COPY ./requirements.txt /code/requirements.txt
6
-
7
- RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
-
9
- # Assegura que huggingface-cli está instalado
10
- RUN pip install huggingface-hub
11
-
12
- COPY ./app.py /code/app.py
13
-
14
- COPY ./check_token.py /code/check_token.py
15
-
16
- COPY ./inference.py /code/inference.py
17
-
18
- COPY entrypoint.sh /entrypoint.sh
19
-
20
- RUN chmod +x /entrypoint.sh
21
-
22
- RUN useradd -m -u 1000 user
23
- USER user
24
- ENV HOME=/home/user \
25
- PATH=/home/user/.local/bin:$PATH
26
-
27
- WORKDIR $HOME/app
28
-
29
- COPY --chown=user . $HOME/app
30
-
31
- ENTRYPOINT ["/entrypoint.sh"]
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
- python /code/check_token.py $HF_TOKEN
 
 
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.encoders import jsonable_encoder
9
- from fastapi.responses import StreamingResponse
10
- from fastapi.middleware.cors import CORSMiddleware
11
- from fastapi import FastAPI, UploadFile, File, Response, status
12
- from llama_index.readers import StringIterableReader, PDFReader, SimpleDirectoryReader
13
- from llama_index import (
14
- VectorStoreIndex,
15
- ServiceContext,
16
- set_global_service_context,
17
- )
18
- from pyngrok import ngrok
19
- import inference
20
-
21
-
22
- app = FastAPI()
23
-
24
- app.add_middleware(
25
- CORSMiddleware,
26
- allow_origins=["*"],
27
- allow_credentials=True,
28
- allow_methods=["*"],
29
- allow_headers=["*"],
30
- )
31
-
32
- class Message(BaseModel):
33
- content: str
34
-
35
- if not os.path.exists("tmp"):
36
- os.mkdir("tmp")
37
-
38
- vector_stores = {}
39
-
40
- @app.post("/retriveal/ingest")
41
- async def store_file(
42
- file: UploadFile = File(...)
43
- ):
44
-
45
- try:
46
-
47
- print(file.filename)
48
- id = str(uuid4())
49
- file_location = f"tmp/{id}"
50
-
51
- if not os.path.exists(file_location):
52
- os.mkdir(file_location)
53
-
54
- with open(f"{file_location}/{file.filename}", "wb+") as f:
55
- shutil.copyfileobj(file.file, f)
56
-
57
- pdf = SimpleDirectoryReader(f"tmp/{id}").load_data()
58
-
59
- vector_stores[id] = VectorStoreIndex.from_documents(pdf)
60
-
61
- return jsonable_encoder({"uuid": id})
62
-
63
- except Exception as e:
64
-
65
- # response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
66
- return jsonable_encoder({"error": str(e)})
67
-
68
- @app.post("/retriveal/ingest/{id}")
69
- async def store_file_with_id(
70
- id,
71
- file: UploadFile = File(...)
72
- ):
73
-
74
- try:
75
-
76
- print(file.filename)
77
-
78
- if(id == None or id == ""):
79
- raise Exception("Id is required")
80
-
81
- file_location = f"tmp/{id}"
82
-
83
- if not os.path.exists(file_location):
84
- os.mkdir(file_location)
85
-
86
- with open(f"{file_location}/{file.filename}", "wb+") as f:
87
- shutil.copyfileobj(file.file, f)
88
-
89
- pdf = SimpleDirectoryReader(f"tmp/{id}").load_data()
90
-
91
- vector_stores[id] = VectorStoreIndex.from_documents(pdf)
92
-
93
- return jsonable_encoder({"uuid": id})
94
-
95
- except Exception as e:
96
-
97
- # response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
98
- return jsonable_encoder({"error": str(e)})
99
-
100
- @app.delete("/session/{id}")
101
- async def delete_session(id):
102
- try:
103
- shutil.rmtree(f"tmp/{id}")
104
- return jsonable_encoder({"message": "ok"})
105
- except Exception as e:
106
- return jsonable_encoder({"error": str(e)})
107
-
108
- @app.post("/retriveal/{id}")
109
- async def inference(
110
- id,
111
- message: Message
112
- ):
113
-
114
- if(id == None or id == ""):
115
- raise Exception("Id is required")
116
-
117
- query = message.content
118
-
119
- query_engine = vector_stores[id].as_query_engine()
120
-
121
- inference = query_engine.query(query)
122
-
123
- return inference
124
-
125
-
126
- def stream_inference(gen):
127
- for token in gen:
128
- yield token
129
-
130
-
131
- @app.post("/retriveal/stream/{id}")
132
- async def inference(
133
- id,
134
- message: Message
135
- ):
136
-
137
- if(id == None or id == ""):
138
- raise Exception("Id is required")
139
-
140
- query = message.content
141
-
142
- query_engine = vector_stores[id].as_query_engine(streaming=True)
143
-
144
- gen = query_engine.query(query).response_gen
145
-
146
- return StreamingResponse(stream_inference(gen))
 
 
 
 
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
- torch==2.0.1
12
- transformers==4.37.2
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}. &quot;{source.pageContent}&quot;{
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
+ })