stivenDR14
commited on
Commit
·
67f0d18
0
Parent(s):
Initial commit
Browse files- .github/workflows/manual.yml +20 -0
- .gitignore +14 -0
- README.md +141 -0
- app.py +246 -0
- pdf_processor.py +353 -0
- utils.py +190 -0
.github/workflows/manual.yml
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Sync to Hugging Face hub
|
2 |
+
on:
|
3 |
+
push:
|
4 |
+
branches: [main]
|
5 |
+
|
6 |
+
# to run this workflow manually from the Actions tab
|
7 |
+
workflow_dispatch:
|
8 |
+
|
9 |
+
jobs:
|
10 |
+
sync-to-hub:
|
11 |
+
runs-on: ubuntu-latest
|
12 |
+
steps:
|
13 |
+
- uses: actions/checkout@v3
|
14 |
+
with:
|
15 |
+
fetch-depth: 0
|
16 |
+
lfs: true
|
17 |
+
- name: Push to hub
|
18 |
+
env:
|
19 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
20 |
+
run: git push https://HF_USERNAME:[email protected]/spaces/stiv14/pdf-multilanguage-qa-role main
|
.gitignore
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#ignore env variables
|
2 |
+
.env
|
3 |
+
|
4 |
+
#ignore pycache folder and all files in it
|
5 |
+
__pycache__
|
6 |
+
|
7 |
+
#ignore chroma_db folder
|
8 |
+
chroma_db
|
9 |
+
|
10 |
+
#ignore ollama folder
|
11 |
+
ollama
|
12 |
+
|
13 |
+
#ignore llama_index folder
|
14 |
+
llama_index
|
README.md
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 🤖 PDF AI Assistant
|
2 |
+
|
3 |
+
A multilingual PDF processing application that leverages various AI models to analyze, summarize, and interact with PDF documents. Built with Python, Gradio, and LangChain.
|
4 |
+
|
5 |
+
## 🌟 Features
|
6 |
+
|
7 |
+
- **Multiple AI Models Support**:
|
8 |
+
|
9 |
+
- OpenAI GPT-4
|
10 |
+
- IBM Granite 3.1
|
11 |
+
- Mistral Small 24B
|
12 |
+
- SmolLM2 1.7B
|
13 |
+
- Local Ollama models
|
14 |
+
|
15 |
+
- **Multilingual Interface**:
|
16 |
+
|
17 |
+
- English
|
18 |
+
- Español
|
19 |
+
- Deutsch
|
20 |
+
- Français
|
21 |
+
- Português
|
22 |
+
|
23 |
+
- **Core Functionalities**:
|
24 |
+
- 📝 Text extraction from PDFs
|
25 |
+
- 💬 Interactive Q&A with document content
|
26 |
+
- 📋 Document summarization
|
27 |
+
- 👨💼 Customizable specialist advisor
|
28 |
+
- 🔄 Dynamic chunk size and overlap settings
|
29 |
+
|
30 |
+
## 🛠️ Installation
|
31 |
+
|
32 |
+
1. Clone the repository:
|
33 |
+
|
34 |
+
```bash
|
35 |
+
git clone <repository-url>
|
36 |
+
cd pdf-ai-assistant
|
37 |
+
```
|
38 |
+
|
39 |
+
2. Install required dependencies:
|
40 |
+
|
41 |
+
```bash
|
42 |
+
pip install -r requirements.txt
|
43 |
+
```
|
44 |
+
|
45 |
+
3. Set up environment variables:
|
46 |
+
|
47 |
+
```bash
|
48 |
+
# Create .env file
|
49 |
+
touch .env
|
50 |
+
|
51 |
+
# Add your API keys (if using)
|
52 |
+
WATSONX_APIKEY=your_watsonx_api_key
|
53 |
+
WATSONX_PROJECT_ID=your_watsonx_project_id
|
54 |
+
```
|
55 |
+
|
56 |
+
## 📦 Dependencies
|
57 |
+
|
58 |
+
- gradio
|
59 |
+
- langchain
|
60 |
+
- chromadb
|
61 |
+
- PyPDF2
|
62 |
+
- ollama (for local models)
|
63 |
+
- python-dotenv
|
64 |
+
- requests
|
65 |
+
- ibm-watsonx-ai
|
66 |
+
|
67 |
+
## 🚀 Usage
|
68 |
+
|
69 |
+
1. Start the application:
|
70 |
+
|
71 |
+
```bash
|
72 |
+
python app.py
|
73 |
+
```
|
74 |
+
|
75 |
+
2. Open your web browser and navigate to the provided URL (usually http://localhost:7860)
|
76 |
+
|
77 |
+
3. Select your preferred:
|
78 |
+
|
79 |
+
- Language
|
80 |
+
- AI Model
|
81 |
+
- Model Type (Local/API)
|
82 |
+
|
83 |
+
4. Upload a PDF file and process it
|
84 |
+
|
85 |
+
5. Use any of the three main features:
|
86 |
+
- Ask questions about the document
|
87 |
+
- Generate a comprehensive summary
|
88 |
+
- Get specialized analysis using the custom advisor
|
89 |
+
|
90 |
+
## 💡 Features in Detail
|
91 |
+
|
92 |
+
### Q&A System
|
93 |
+
|
94 |
+
- Interactive chat interface
|
95 |
+
- Context-aware responses
|
96 |
+
- Source page references
|
97 |
+
|
98 |
+
### Summarization
|
99 |
+
|
100 |
+
- Chunk-based processing
|
101 |
+
- Configurable chunk sizes
|
102 |
+
- Comprehensive document overview
|
103 |
+
|
104 |
+
### Specialist Advisor
|
105 |
+
|
106 |
+
- Customizable expert roles
|
107 |
+
- Detailed analysis based on expertise
|
108 |
+
- Structured insights and recommendations
|
109 |
+
|
110 |
+
## 🔧 Configuration
|
111 |
+
|
112 |
+
The application supports various AI models:
|
113 |
+
|
114 |
+
- Local models via Ollama
|
115 |
+
- API-based models (OpenAI, IBM WatsonX)
|
116 |
+
- Hugging Face models
|
117 |
+
|
118 |
+
For Ollama local models, ensure:
|
119 |
+
|
120 |
+
```bash
|
121 |
+
ollama pull granite3.1-dense
|
122 |
+
ollama pull granite-embedding:278m
|
123 |
+
```
|
124 |
+
|
125 |
+
## 🌐 Language Support
|
126 |
+
|
127 |
+
The interface and AI responses are available in:
|
128 |
+
|
129 |
+
- English
|
130 |
+
- Spanish
|
131 |
+
- German
|
132 |
+
- French
|
133 |
+
- Portuguese
|
134 |
+
|
135 |
+
## 📝 License
|
136 |
+
|
137 |
+
[MIT License]
|
138 |
+
|
139 |
+
## 🤝 Contributing
|
140 |
+
|
141 |
+
Contributions, issues, and feature requests are welcome!
|
app.py
ADDED
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from pdf_processor import PDFProcessor
|
3 |
+
from utils import AI_MODELS, TRANSLATIONS
|
4 |
+
|
5 |
+
class PDFProcessorUI:
|
6 |
+
def __init__(self):
|
7 |
+
self.processor = PDFProcessor()
|
8 |
+
self.current_language = "English"
|
9 |
+
self.current_ai_model = "Huggingface / IBM granite granite 3.1 8b Instruct"
|
10 |
+
self.current_type_model = "Api Key"
|
11 |
+
|
12 |
+
def change_language(self, language):
|
13 |
+
self.current_language = language
|
14 |
+
self.processor.set_language(language)
|
15 |
+
|
16 |
+
# Retornamos todos los textos que necesitan ser actualizados
|
17 |
+
return [
|
18 |
+
TRANSLATIONS[language]["title"],
|
19 |
+
gr.update(label=TRANSLATIONS[language]["upload_pdf"]),
|
20 |
+
gr.update(label=TRANSLATIONS[language]["chunk_size"]),
|
21 |
+
gr.update(label=TRANSLATIONS[language]["chunk_overlap"]),
|
22 |
+
gr.update(value=TRANSLATIONS[language]["process_btn"]),
|
23 |
+
gr.update(label=TRANSLATIONS[language]["processing_status"]),
|
24 |
+
gr.update(label=TRANSLATIONS[language]["qa_tab"]),
|
25 |
+
gr.update(label=TRANSLATIONS[language]["summary_tab"]),
|
26 |
+
gr.update(label=TRANSLATIONS[language]["specialist_tab"]),
|
27 |
+
gr.update(label=TRANSLATIONS[language]["mini_summary_title"]),
|
28 |
+
gr.update(label=TRANSLATIONS[language]["mini_analysis_title"]),
|
29 |
+
gr.update(placeholder=TRANSLATIONS[language]["chat_placeholder"]),
|
30 |
+
TRANSLATIONS[language]["chat_title"],
|
31 |
+
gr.update(value=TRANSLATIONS[language]["chat_btn"]),
|
32 |
+
gr.update(value=TRANSLATIONS[language]["generate_summary"]),
|
33 |
+
gr.update(label=TRANSLATIONS[language]["summary_label"]),
|
34 |
+
gr.update(label=TRANSLATIONS[language]["ai_model"]),
|
35 |
+
TRANSLATIONS[language]["specialist_title"],
|
36 |
+
gr.update(label=TRANSLATIONS[language]["specialist_label"]),
|
37 |
+
gr.update(label=TRANSLATIONS[language]["specialist_output"]),
|
38 |
+
gr.update(value=TRANSLATIONS[language]["specialist_btn"])
|
39 |
+
]
|
40 |
+
|
41 |
+
def change_ai_model(self, ai_model):
|
42 |
+
self.current_ai_model = ai_model
|
43 |
+
if ai_model == "IBM Granite3.1 dense / Ollama local":
|
44 |
+
return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False, maximum=2048), gr.update(visible=False, maximum=200)
|
45 |
+
elif ai_model == "Open AI / GPT-4o-mini":
|
46 |
+
return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False, maximum=2048), gr.update(visible=False, maximum=200)
|
47 |
+
else:
|
48 |
+
return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False, maximum=500), gr.update(visible=False, maximum=100)
|
49 |
+
|
50 |
+
def change_type_model(self, type_model):
|
51 |
+
self.current_type_model = type_model
|
52 |
+
if type_model == "Api Key":
|
53 |
+
if self.current_ai_model == "IBM Granite3.1 dense / Ollama local":
|
54 |
+
return gr.update(visible=False), gr.update(visible=False)
|
55 |
+
else:
|
56 |
+
return gr.update(visible=True), gr.update(visible=False)
|
57 |
+
else:
|
58 |
+
return gr.update(visible=False), gr.update(visible=False)
|
59 |
+
|
60 |
+
def process_pdf(self, pdf_file, chunk_size, chunk_overlap, ai_model, type_model, api_key, project_id_watsonx):
|
61 |
+
return self.processor.process_pdf(pdf_file, chunk_size, chunk_overlap, ai_model, type_model, api_key, project_id_watsonx)
|
62 |
+
|
63 |
+
def qa_interface(self, message, history, ai_model, type_model, api_key, project_id_watsonx):
|
64 |
+
return self.processor.get_qa_response(message, history, ai_model, type_model, api_key, project_id_watsonx)
|
65 |
+
|
66 |
+
def summarize_interface(self, ai_model, type_model, api_key, project_id_watsonx):
|
67 |
+
return self.processor.get_summary(ai_model, type_model, api_key, project_id_watsonx)
|
68 |
+
|
69 |
+
def specialist_opinion(self, ai_model, type_model, api_key, project_id_watsonx, specialist_prompt):
|
70 |
+
return self.processor.get_specialist_opinion(ai_model, type_model, api_key, project_id_watsonx, specialist_prompt)
|
71 |
+
|
72 |
+
def upload_file(files):
|
73 |
+
file_paths = [file.name for file in files]
|
74 |
+
return file_paths[0]
|
75 |
+
|
76 |
+
def create_ui(self):
|
77 |
+
with gr.Blocks() as demo:
|
78 |
+
title = gr.Markdown(TRANSLATIONS[self.current_language]["title"])
|
79 |
+
|
80 |
+
with gr.Row():
|
81 |
+
language_dropdown = gr.Dropdown(
|
82 |
+
choices=list(TRANSLATIONS.keys()),
|
83 |
+
value=self.current_language,
|
84 |
+
label="Language/Idioma/Sprache/Langue/Língua",
|
85 |
+
key="language_dropdown"
|
86 |
+
)
|
87 |
+
ai_model_dropdown = gr.Dropdown(
|
88 |
+
choices=list(AI_MODELS.keys()),
|
89 |
+
value=self.current_ai_model,
|
90 |
+
label= TRANSLATIONS[self.current_language]["ai_model"],
|
91 |
+
key="ai_model_dropdown"
|
92 |
+
)
|
93 |
+
|
94 |
+
with gr.Row():
|
95 |
+
with gr.Column():
|
96 |
+
with gr.Row():
|
97 |
+
pdf_file = gr.File(
|
98 |
+
label=TRANSLATIONS[self.current_language]["upload_pdf"],
|
99 |
+
file_types=[".pdf"]
|
100 |
+
)
|
101 |
+
with gr.Column():
|
102 |
+
type_model=gr.Radio(choices=["Local", "Api Key"], label=TRANSLATIONS[self.current_language]["model_type"], visible=False, value="Api Key")
|
103 |
+
api_key_input = gr.Textbox(label="Api Key", placeholder=TRANSLATIONS[self.current_language]["api_key_placeholder"], visible=False)
|
104 |
+
project_id_watsonx = gr.Textbox(label="Project ID", placeholder=TRANSLATIONS[self.current_language]["project_id_placeholder"], visible=False)
|
105 |
+
chunk_size = gr.Slider(
|
106 |
+
value=250,
|
107 |
+
label=TRANSLATIONS[self.current_language]["chunk_size"],
|
108 |
+
minimum=100,
|
109 |
+
maximum=500,
|
110 |
+
step=10,
|
111 |
+
visible=False
|
112 |
+
)
|
113 |
+
chunk_overlap = gr.Slider(
|
114 |
+
value=25,
|
115 |
+
label=TRANSLATIONS[self.current_language]["chunk_overlap"],
|
116 |
+
minimum=10,
|
117 |
+
maximum=100,
|
118 |
+
step=5,
|
119 |
+
visible=False
|
120 |
+
)
|
121 |
+
process_btn = gr.Button(
|
122 |
+
TRANSLATIONS[self.current_language]["process_btn"]
|
123 |
+
)
|
124 |
+
process_output = gr.Textbox(
|
125 |
+
label=TRANSLATIONS[self.current_language]["processing_status"]
|
126 |
+
)
|
127 |
+
|
128 |
+
with gr.Tabs() as tabs:
|
129 |
+
qa_tab = gr.Tab(TRANSLATIONS[self.current_language]["qa_tab"])
|
130 |
+
summary_tab = gr.Tab(TRANSLATIONS[self.current_language]["summary_tab"])
|
131 |
+
specialist_tab = gr.Tab(TRANSLATIONS[self.current_language]["specialist_tab"])
|
132 |
+
with qa_tab:
|
133 |
+
chat_title = gr.Markdown(TRANSLATIONS[self.current_language]["chat_title"])
|
134 |
+
chat_placeholder = gr.Textbox(
|
135 |
+
placeholder=TRANSLATIONS[self.current_language]["chat_placeholder"],
|
136 |
+
container=False,
|
137 |
+
show_label=False
|
138 |
+
)
|
139 |
+
chat_btn = gr.Button(TRANSLATIONS[self.current_language]["chat_btn"])
|
140 |
+
chatbot = gr.Markdown(height=400)
|
141 |
+
|
142 |
+
with summary_tab:
|
143 |
+
with gr.Accordion(TRANSLATIONS[self.current_language]["mini_analysis_title"], open=False, visible=False):
|
144 |
+
minisummaries_output = gr.Textbox(
|
145 |
+
label=TRANSLATIONS[self.current_language]["mini_analysis_title"],
|
146 |
+
lines=10
|
147 |
+
)
|
148 |
+
summary_output = gr.Textbox(
|
149 |
+
label=TRANSLATIONS[self.current_language]["summary_label"],
|
150 |
+
lines=10
|
151 |
+
)
|
152 |
+
summarize_btn = gr.Button(
|
153 |
+
TRANSLATIONS[self.current_language]["generate_summary"]
|
154 |
+
)
|
155 |
+
|
156 |
+
with specialist_tab:
|
157 |
+
specialist_title = gr.Markdown(TRANSLATIONS[self.current_language]["specialist_title"])
|
158 |
+
specialist_placeholder = gr.Textbox(
|
159 |
+
label=TRANSLATIONS[self.current_language]["specialist_label"],
|
160 |
+
lines=10
|
161 |
+
)
|
162 |
+
with gr.Accordion(TRANSLATIONS[self.current_language]["mini_analysis_title"], open=False, visible=False):
|
163 |
+
minianalysis_output = gr.Textbox(
|
164 |
+
label=TRANSLATIONS[self.current_language]["mini_analysis_title"],
|
165 |
+
lines=10
|
166 |
+
)
|
167 |
+
specialist_output = gr.Textbox(label=TRANSLATIONS[self.current_language]["specialist_output"], lines=20)
|
168 |
+
specialist_btn = gr.Button(TRANSLATIONS[self.current_language]["specialist_btn"])
|
169 |
+
|
170 |
+
|
171 |
+
language_dropdown.change(
|
172 |
+
fn=self.change_language,
|
173 |
+
inputs=[language_dropdown],
|
174 |
+
outputs=[
|
175 |
+
title,
|
176 |
+
pdf_file,
|
177 |
+
chunk_size,
|
178 |
+
chunk_overlap,
|
179 |
+
process_btn,
|
180 |
+
process_output,
|
181 |
+
qa_tab,
|
182 |
+
summary_tab,
|
183 |
+
specialist_tab,
|
184 |
+
minisummaries_output,
|
185 |
+
minianalysis_output,
|
186 |
+
chat_placeholder,
|
187 |
+
chat_title,
|
188 |
+
chat_btn,
|
189 |
+
summarize_btn,
|
190 |
+
summary_output,
|
191 |
+
ai_model_dropdown,
|
192 |
+
specialist_title,
|
193 |
+
specialist_placeholder,
|
194 |
+
specialist_output,
|
195 |
+
specialist_btn
|
196 |
+
]
|
197 |
+
)
|
198 |
+
|
199 |
+
ai_model_dropdown.change(
|
200 |
+
fn=self.change_ai_model,
|
201 |
+
inputs=[ai_model_dropdown],
|
202 |
+
outputs=[type_model, api_key_input, project_id_watsonx, chunk_size, chunk_overlap]
|
203 |
+
)
|
204 |
+
|
205 |
+
type_model.change(
|
206 |
+
fn=self.change_type_model,
|
207 |
+
inputs=[type_model],
|
208 |
+
outputs=[api_key_input,project_id_watsonx]
|
209 |
+
)
|
210 |
+
|
211 |
+
chat_placeholder.submit(
|
212 |
+
fn=self.qa_interface,
|
213 |
+
inputs=[chat_placeholder, chatbot, ai_model_dropdown, type_model, api_key_input, project_id_watsonx],
|
214 |
+
outputs=[chatbot]
|
215 |
+
)
|
216 |
+
|
217 |
+
process_btn.click(
|
218 |
+
fn=self.process_pdf,
|
219 |
+
inputs=[pdf_file, chunk_size, chunk_overlap, ai_model_dropdown, type_model, api_key_input, project_id_watsonx],
|
220 |
+
outputs=[process_output]
|
221 |
+
)
|
222 |
+
|
223 |
+
summarize_btn.click(
|
224 |
+
fn=self.summarize_interface,
|
225 |
+
inputs=[ai_model_dropdown, type_model, api_key_input, project_id_watsonx],
|
226 |
+
outputs=[summary_output]
|
227 |
+
)
|
228 |
+
|
229 |
+
specialist_btn.click(
|
230 |
+
fn=self.specialist_opinion,
|
231 |
+
inputs=[ai_model_dropdown, type_model, api_key_input, project_id_watsonx, specialist_placeholder],
|
232 |
+
outputs=[specialist_output]
|
233 |
+
)
|
234 |
+
|
235 |
+
chat_btn.click(
|
236 |
+
fn=self.qa_interface,
|
237 |
+
inputs=[chat_placeholder, chatbot, ai_model_dropdown, type_model, api_key_input, project_id_watsonx],
|
238 |
+
outputs=[chatbot]
|
239 |
+
)
|
240 |
+
|
241 |
+
return demo
|
242 |
+
|
243 |
+
if __name__ == "__main__":
|
244 |
+
ui = PDFProcessorUI()
|
245 |
+
demo = ui.create_ui()
|
246 |
+
demo.launch()
|
pdf_processor.py
ADDED
@@ -0,0 +1,353 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import tempfile
|
3 |
+
from langchain_community.document_loaders import PyPDFLoader
|
4 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
5 |
+
from langchain_ollama import OllamaEmbeddings
|
6 |
+
from langchain_community.vectorstores import Chroma
|
7 |
+
from langchain_ollama import OllamaLLM
|
8 |
+
from langchain.chains import RetrievalQA
|
9 |
+
from langchain.prompts import PromptTemplate
|
10 |
+
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
|
11 |
+
from ibm_watsonx_ai.metanames import EmbedTextParamsMetaNames
|
12 |
+
from langchain_ibm import WatsonxLLM, WatsonxEmbeddings
|
13 |
+
from langchain_huggingface import HuggingFaceEndpoint, HuggingFaceEmbeddings
|
14 |
+
from ibm_watsonx_ai import APIClient, Credentials
|
15 |
+
from utils import AI_MODELS, TRANSLATIONS
|
16 |
+
import chromadb
|
17 |
+
import requests
|
18 |
+
import os
|
19 |
+
from dotenv import load_dotenv
|
20 |
+
|
21 |
+
OLLAMA_LLM = "granite3.1-dense"
|
22 |
+
OLLAMA_EMBEDDINGS = "granite-embedding:278m"
|
23 |
+
|
24 |
+
|
25 |
+
load_dotenv()
|
26 |
+
|
27 |
+
api_key_watsonx = os.getenv('WATSONX_APIKEY')
|
28 |
+
projectid_watsonx = os.getenv('WATSONX_PROJECT_ID')
|
29 |
+
endpoint_watsonx = "https://us-south.ml.cloud.ibm.com"
|
30 |
+
|
31 |
+
def set_up_watsonx():
|
32 |
+
token_watsonx = authenticate_watsonx(api_key_watsonx)
|
33 |
+
if token_watsonx == None:
|
34 |
+
return None
|
35 |
+
parameters = {
|
36 |
+
"max_new_tokens": 1500,
|
37 |
+
"min_new_tokens": 1,
|
38 |
+
"temperature": 0.7,
|
39 |
+
"top_k": 50,
|
40 |
+
"top_p": 1,
|
41 |
+
}
|
42 |
+
|
43 |
+
embed_params = {
|
44 |
+
EmbedTextParamsMetaNames.TRUNCATE_INPUT_TOKENS: 1,
|
45 |
+
EmbedTextParamsMetaNames.RETURN_OPTIONS: {"input_text": True},
|
46 |
+
}
|
47 |
+
|
48 |
+
credentials = Credentials(
|
49 |
+
url = endpoint_watsonx,
|
50 |
+
api_key = api_key_watsonx,
|
51 |
+
)
|
52 |
+
|
53 |
+
client = APIClient(credentials, project_id=projectid_watsonx)
|
54 |
+
|
55 |
+
client.set_token(token_watsonx)
|
56 |
+
|
57 |
+
watsonx_llm = WatsonxLLM(
|
58 |
+
model_id="ibm/granite-3-2-8b-instruct",
|
59 |
+
watsonx_client=client,
|
60 |
+
params = parameters
|
61 |
+
)
|
62 |
+
|
63 |
+
|
64 |
+
watsonx_embedding = WatsonxEmbeddings(
|
65 |
+
model_id="ibm/granite-embedding-278m-multilingual",
|
66 |
+
url=endpoint_watsonx,
|
67 |
+
project_id=projectid_watsonx,
|
68 |
+
params=embed_params,
|
69 |
+
)
|
70 |
+
|
71 |
+
return watsonx_llm, watsonx_embedding
|
72 |
+
|
73 |
+
def authenticate_watsonx(api_key):
|
74 |
+
url = "https://iam.cloud.ibm.com/identity/token"
|
75 |
+
headers = {
|
76 |
+
"Content-Type": "application/x-www-form-urlencoded"
|
77 |
+
}
|
78 |
+
data = {
|
79 |
+
"grant_type": "urn:ibm:params:oauth:grant-type:apikey",
|
80 |
+
"apikey": api_key
|
81 |
+
}
|
82 |
+
|
83 |
+
response = requests.post(url, headers=headers, data=data)
|
84 |
+
|
85 |
+
if response.status_code == 200:
|
86 |
+
token = response.json().get('access_token')
|
87 |
+
os.environ["WATSONX_TOKEN"] = token
|
88 |
+
return token
|
89 |
+
else:
|
90 |
+
print("Authentication failed. Status code:", response.status_code)
|
91 |
+
print("Response:", response.text)
|
92 |
+
return None
|
93 |
+
|
94 |
+
|
95 |
+
class PDFProcessor:
|
96 |
+
def __init__(self):
|
97 |
+
self.vectorstore = None
|
98 |
+
self.language = "English"
|
99 |
+
|
100 |
+
def set_language(self, language):
|
101 |
+
self.language = language
|
102 |
+
|
103 |
+
def set_llm(self, ai_model, type_model, api_key, project_id_watsonx):
|
104 |
+
if ai_model == "Open AI / GPT-4o-mini":
|
105 |
+
current_llm = ChatOpenAI(
|
106 |
+
model="gpt-4o",
|
107 |
+
temperature=0.5,
|
108 |
+
max_tokens=None,
|
109 |
+
timeout=None,
|
110 |
+
max_retries=2,
|
111 |
+
api_key=api_key,
|
112 |
+
)
|
113 |
+
embeding_model = OpenAIEmbeddings(
|
114 |
+
model="text-embedding-3-small",
|
115 |
+
api_key=api_key,
|
116 |
+
)
|
117 |
+
|
118 |
+
|
119 |
+
elif ai_model == "IBM Granite3.1 dense / Ollama local":
|
120 |
+
if type_model == "Local":
|
121 |
+
try:
|
122 |
+
# Verificar que Ollama está funcionando y el modelo está disponible
|
123 |
+
current_llm = OllamaLLM(model=OLLAMA_LLM)
|
124 |
+
# Intenta hacer un embedding de prueba
|
125 |
+
test_embedding = OllamaEmbeddings(model=OLLAMA_EMBEDDINGS)
|
126 |
+
test_embedding.embed_query("test")
|
127 |
+
embeding_model = test_embedding
|
128 |
+
except Exception as e:
|
129 |
+
print(f"Error with Ollama: {e}")
|
130 |
+
# Fallback a otro modelo o manejo de error
|
131 |
+
raise Exception("Please ensure Ollama is running and the models are pulled: \n" +
|
132 |
+
f"ollama pull {OLLAMA_LLM}\n" +
|
133 |
+
f"ollama pull {OLLAMA_EMBEDDINGS}")
|
134 |
+
else:
|
135 |
+
current_llm, embeding_model = set_up_watsonx()
|
136 |
+
else:
|
137 |
+
current_llm = HuggingFaceEndpoint(
|
138 |
+
repo_id= AI_MODELS[ai_model],
|
139 |
+
temperature=0.5,
|
140 |
+
)
|
141 |
+
embeding_model = HuggingFaceEmbeddings(
|
142 |
+
model_name="ibm-granite/granite-embedding-278m-multilingual",
|
143 |
+
)
|
144 |
+
return current_llm, embeding_model
|
145 |
+
|
146 |
+
|
147 |
+
def process_pdf(self, pdf_file, chunk_size, chunk_overlap, ai_model, type_model, api_key, project_id_watsonx):
|
148 |
+
defined_chunk_size = 1000
|
149 |
+
defined_chunk_overlap = 100
|
150 |
+
if (ai_model == "Open AI / GPT-4o-mini" and (api_key == "")) : #or (ai_model == "IBM Granite3.1 dense / Ollama local" and type_model == "Api Key" and (api_key == "" or project_id_watsonx == "")
|
151 |
+
return TRANSLATIONS[self.language]["api_key_required"]
|
152 |
+
if pdf_file is not None:
|
153 |
+
loader = PyPDFLoader(file_path=pdf_file.name)
|
154 |
+
documents = loader.load()
|
155 |
+
#delete empty page_content documents from documents
|
156 |
+
documents = [doc for doc in documents if doc.page_content]
|
157 |
+
if(ai_model == "Open AI / GPT-4o-mini" or ai_model == "IBM Granite3.1 dense / Ollama local"):
|
158 |
+
if type_model == "Api Key":
|
159 |
+
text_splitter = RecursiveCharacterTextSplitter(
|
160 |
+
chunk_size=defined_chunk_size,
|
161 |
+
chunk_overlap=defined_chunk_overlap,
|
162 |
+
separators=["\n\n", "\n"]
|
163 |
+
)
|
164 |
+
else:
|
165 |
+
text_splitter = RecursiveCharacterTextSplitter(
|
166 |
+
chunk_size=defined_chunk_size,
|
167 |
+
chunk_overlap=defined_chunk_overlap,
|
168 |
+
)
|
169 |
+
else:
|
170 |
+
text_splitter = RecursiveCharacterTextSplitter(
|
171 |
+
chunk_size=defined_chunk_size,
|
172 |
+
chunk_overlap=defined_chunk_overlap
|
173 |
+
)
|
174 |
+
|
175 |
+
#print(text_splitter)
|
176 |
+
texts = text_splitter.split_documents(documents)
|
177 |
+
_, embeddings = self.set_llm(ai_model, type_model, api_key, project_id_watsonx)
|
178 |
+
|
179 |
+
#delete all documents from the vectorstore
|
180 |
+
if self.vectorstore:
|
181 |
+
self.vectorstore.delete_collection()
|
182 |
+
|
183 |
+
new_client = chromadb.EphemeralClient()
|
184 |
+
|
185 |
+
self.vectorstore = Chroma.from_documents(
|
186 |
+
documents=texts,
|
187 |
+
embedding=embeddings,
|
188 |
+
client=new_client,
|
189 |
+
collection_name="pdf_collection"
|
190 |
+
#persist_directory="./chroma_db"
|
191 |
+
)
|
192 |
+
|
193 |
+
return TRANSLATIONS[self.language]["pdf_processed"] + f" ---- Chunks: {len(self.vectorstore.get()["documents"])}"
|
194 |
+
|
195 |
+
else:
|
196 |
+
return TRANSLATIONS[self.language]["load_pdf_first"]
|
197 |
+
|
198 |
+
|
199 |
+
def get_qa_response(self, message, history, ai_model, type_model, api_key, project_id_watsonx, k=4):
|
200 |
+
current_llm, _ = self.set_llm(ai_model, type_model, api_key, project_id_watsonx)
|
201 |
+
|
202 |
+
if not self.vectorstore:
|
203 |
+
return TRANSLATIONS[self.language]["load_pdf_first"]
|
204 |
+
|
205 |
+
retriever = self.vectorstore.as_retriever(search_kwargs={"k": k})
|
206 |
+
|
207 |
+
qa_chain = RetrievalQA.from_chain_type(
|
208 |
+
llm=current_llm,
|
209 |
+
chain_type="stuff",
|
210 |
+
retriever=retriever,
|
211 |
+
return_source_documents=True,
|
212 |
+
)
|
213 |
+
|
214 |
+
result = qa_chain.invoke({"query": f"{message}.\n You must answer it in {self.language}. Remember not to mention anything that is not in the text. Do not extend information that is not provided in the text. "})
|
215 |
+
|
216 |
+
unique_page_labels = {doc.metadata['page_label'] for doc in result["source_documents"]}
|
217 |
+
|
218 |
+
page_labels_text = " & ".join([f"Page: {page}" for page in sorted(unique_page_labels)])
|
219 |
+
|
220 |
+
return result["result"] + "\n\nSources: " + page_labels_text
|
221 |
+
|
222 |
+
|
223 |
+
def summarizer_by_k_top_n(self, ai_model, type_model, api_key, project_id_watsonx, k, summary_prompt, just_get_documments=False):
|
224 |
+
if not self.vectorstore:
|
225 |
+
return TRANSLATIONS[self.language]["load_pdf_first"]
|
226 |
+
|
227 |
+
current_llm, _ = self.set_llm(ai_model, type_model, api_key, project_id_watsonx)
|
228 |
+
# Get all documents from the vectorstore
|
229 |
+
retriever = self.vectorstore.as_retriever(search_kwargs={"k": k})
|
230 |
+
documents = retriever.invoke('Summary of the document and key points')
|
231 |
+
|
232 |
+
if just_get_documments:
|
233 |
+
return "\n".join([doc.page_content for doc in documents])
|
234 |
+
|
235 |
+
summary_chain = summary_prompt | current_llm
|
236 |
+
final_summary = summary_chain.invoke({"texts": "\n".join([doc.page_content for doc in documents]), "language": self.language})
|
237 |
+
return final_summary
|
238 |
+
|
239 |
+
# Get the top k documents by score
|
240 |
+
def get_summary(self, ai_model, type_model, api_key, project_id_watsonx, just_get_documments=False, k=10):
|
241 |
+
|
242 |
+
final_summary_prompt = PromptTemplate(
|
243 |
+
input_variables=["texts", "language"],
|
244 |
+
template="""
|
245 |
+
Combine the following texts into a cohesive and structured final summary:
|
246 |
+
------------
|
247 |
+
{texts}
|
248 |
+
------------
|
249 |
+
The final summary should be between 2 and 4 paragraphs.
|
250 |
+
Preserve the original meaning without adding external information or interpretations.
|
251 |
+
Ensure clarity, logical flow, and coherence between the combined points.
|
252 |
+
The summary must be in {language}.
|
253 |
+
"""
|
254 |
+
)
|
255 |
+
|
256 |
+
return self.summarizer_by_k_top_n(ai_model, type_model, api_key, project_id_watsonx, k, final_summary_prompt, just_get_documments)
|
257 |
+
|
258 |
+
|
259 |
+
|
260 |
+
def get_specialist_opinion(self, ai_model, type_model, api_key, project_id_watsonx, specialist_prompt):
|
261 |
+
questions_prompt = PromptTemplate(
|
262 |
+
input_variables=["text", "specialist_prompt", "language"],
|
263 |
+
template="""
|
264 |
+
* Act as a specialist based on the following instructions and behaviour that you will follow:
|
265 |
+
------------
|
266 |
+
{specialist_prompt}
|
267 |
+
------------
|
268 |
+
* Based on your role as specialist, create some different sintetized and concise aspects to ask to the knowledge base of the document about the following text:
|
269 |
+
------------
|
270 |
+
{text}
|
271 |
+
------------
|
272 |
+
* The key aspects and questions must be provided in JSON format with the following structure:
|
273 |
+
{{
|
274 |
+
"aspects": [
|
275 |
+
"Aspect 1",
|
276 |
+
"Aspect 2",
|
277 |
+
"Aspect 3",
|
278 |
+
"Aspect 4",
|
279 |
+
"Aspect 5",
|
280 |
+
"Aspect 6",
|
281 |
+
"Aspect 7",
|
282 |
+
"Aspect 8",
|
283 |
+
"Aspect 9",
|
284 |
+
"Aspect 10",
|
285 |
+
]
|
286 |
+
}}
|
287 |
+
------------
|
288 |
+
*Example of valid output:
|
289 |
+
{{
|
290 |
+
"aspects": [
|
291 |
+
"Finished date of the project",
|
292 |
+
"Payment of the project",
|
293 |
+
"Project extension"
|
294 |
+
]
|
295 |
+
}}
|
296 |
+
------------
|
297 |
+
* The aspects must be redacted in the language of {language}.
|
298 |
+
* The given structure must be followed strictly in front of the keys, just use the list of aspects, do not add any other key.
|
299 |
+
* Generate until 10 different aspects.
|
300 |
+
------------
|
301 |
+
Answer:
|
302 |
+
"""
|
303 |
+
)
|
304 |
+
if not self.vectorstore:
|
305 |
+
return TRANSLATIONS[self.language]["load_pdf_first"]
|
306 |
+
|
307 |
+
current_llm, _ = self.set_llm(ai_model, type_model, api_key, project_id_watsonx)
|
308 |
+
|
309 |
+
summary_text = self.get_summary(ai_model, type_model, api_key, project_id_watsonx, True, 10)
|
310 |
+
questions_chain = questions_prompt | current_llm
|
311 |
+
questions = questions_chain.invoke({"text": summary_text, "specialist_prompt": specialist_prompt, "language": self.language})
|
312 |
+
|
313 |
+
print(questions)
|
314 |
+
|
315 |
+
#clean the questions variable, delete all the text before the json and after the json
|
316 |
+
questions = questions.split("{")[1]
|
317 |
+
questions = questions.split("}")[0]
|
318 |
+
questions = questions.strip()
|
319 |
+
print(questions)
|
320 |
+
questions = json.loads(questions)
|
321 |
+
|
322 |
+
print(questions)
|
323 |
+
|
324 |
+
if len(questions["aspects"]) > 15:
|
325 |
+
questions["aspects"] = questions["aspects"][:15]
|
326 |
+
else:
|
327 |
+
questions["aspects"] = questions["aspects"]
|
328 |
+
|
329 |
+
aspects_text = "\n".join([f"* {aspect}: {self.get_qa_response(aspect, [], ai_model, type_model, api_key, project_id_watsonx, 2)}" for aspect in questions["aspects"]])
|
330 |
+
|
331 |
+
return aspects_text
|
332 |
+
|
333 |
+
|
334 |
+
""" Actúa como un abogado altamente experimentado en derecho civil y contractual.
|
335 |
+
|
336 |
+
Examina si existen cláusulas abusivas, desproporcionadas o contrarias a la normativa vigente, y explícalas con claridad.
|
337 |
+
Basa tu análisis en principios relevantes del derecho civil y contractual.
|
338 |
+
Ofrece un argumento estructurado y recomendaciones prácticas.
|
339 |
+
Si hay múltiples interpretaciones posibles, preséntalas de manera objetiva.
|
340 |
+
Mantén un tono profesional, preciso y fundamentado.
|
341 |
+
|
342 |
+
Basado en lo que analices, proporciona una evaluación legal detallada """
|
343 |
+
|
344 |
+
""" Actúa como un asesor e ingeniero financiero experto en lectura de reportes y análisis de datos.
|
345 |
+
|
346 |
+
Basado en los datos y conclusiones del reporte, proporciona una evaluación financiera detallada y posibles escenarios tanto negativos como positivos que se puedan presentar.
|
347 |
+
Establece el riesgo que se corre en cada escenario, la probabilidad de ocurrencia de cada uno y la magnitud del impacto en el recurso.
|
348 |
+
Si hay múltiples interpretaciones posibles, preséntalas de manera objetiva.
|
349 |
+
Realiza una hipótesis que pronostique el futuro de la situación o recurso analizado, teniendo en cuenta los datos y conclusiones del reporte.
|
350 |
+
Presenta tus hipotesis en 3 aspectos, corto, mediano y largo plazo.
|
351 |
+
Mantén un tono profesional, preciso y fundamentado.
|
352 |
+
|
353 |
+
Basado en lo que analices, proporciona una evaluación en detalle sobre los activos, reportes y/o recursos que se analizaron"""
|
utils.py
ADDED
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
AI_MODELS = {
|
2 |
+
"Huggingface / IBM granite granite 3.1 8b Instruct": "ibm-granite/granite-3.1-8b-instruct",
|
3 |
+
"Huggingface / Mistral Small 24B Instruct": "mistralai/Mistral-Small-24B-Instruct-2501",
|
4 |
+
"Huggingface / SmolLM2 1.7B Instruct": "HuggingFaceTB/SmolLM2-1.7B-Instruct",
|
5 |
+
"IBM Granite3.1 dense / Ollama local": "ollama",
|
6 |
+
"Open AI / GPT-4o-mini": "openai",
|
7 |
+
}
|
8 |
+
|
9 |
+
TRANSLATIONS = {
|
10 |
+
"Español": {
|
11 |
+
"title": "# 📚 Procesador de PDF con QA y Resumen",
|
12 |
+
"api_key_required": "Para usar este modelo, necesitas una clave de API.",
|
13 |
+
"model_type": "Tipo de modelo",
|
14 |
+
"api_key_placeholder": "Ingresa tu clave de API",
|
15 |
+
"project_id_placeholder": "Ingresa tu ID de proyecto",
|
16 |
+
"ai_model": "Modelo AI",
|
17 |
+
"upload_pdf": "Cargar PDF",
|
18 |
+
"upload_images": "Cargar imágenes",
|
19 |
+
"chunk_size": "Tamaño de chunk",
|
20 |
+
"chunk_overlap": "Superposición de chunk",
|
21 |
+
"process_btn": "Procesar",
|
22 |
+
"processing_status": "Estado del procesamiento",
|
23 |
+
"qa_tab": "Preguntas y Respuestas",
|
24 |
+
"summary_tab": "Resumen",
|
25 |
+
"chat_placeholder": "Haz una pregunta sobre el documento...",
|
26 |
+
"chat_title": "Pregunta al documento",
|
27 |
+
"chat_btn": "Preguntar",
|
28 |
+
"generate_summary": "Generar Resumen",
|
29 |
+
"summary_label": "Resumen del documento",
|
30 |
+
"pdf_processed": "PDF procesado y almacenado correctamente",
|
31 |
+
"load_pdf_first": "Por favor, carga un PDF primero.",
|
32 |
+
"map_prompt": """Escribe un resumen conciso del siguiente texto:
|
33 |
+
"{text}"
|
34 |
+
RESUMEN CONCISO:""",
|
35 |
+
"combine_prompt": """Escribe un resumen detallado basado en los siguientes resúmenes de diferentes secciones del texto:
|
36 |
+
"{text}"
|
37 |
+
RESUMEN DETALLADO:""",
|
38 |
+
"mini_summary_title": "Resúmenes de cada fragmento",
|
39 |
+
"mini_analysis_title": "Análisis de cada fragmento",
|
40 |
+
"specialist_tab": "Asesor a tu medida",
|
41 |
+
"specialist_title": "Asesor a tu medida",
|
42 |
+
"specialist_label": "Establece el comportamiento y rol de tu asesor. Ej: Eres un especialista de finanzas que ayuda a interpretar los datos de un reporte financiero. A partir del documento y tu basta experiencia cuéntame que oportunidades y riesgos ves al invertir en lo que te proponen.",
|
43 |
+
"specialist_output": "Respuesta de tu asesor",
|
44 |
+
"specialist_btn": "Generar Respuesta"
|
45 |
+
},
|
46 |
+
"English": {
|
47 |
+
"title": "# 📚 PDF Processor with QA and Summary",
|
48 |
+
"api_key_required": "To use this model, you need an API key.",
|
49 |
+
"model_type": "Model type",
|
50 |
+
"api_key_placeholder": "Enter your API key",
|
51 |
+
"project_id_placeholder": "Enter your project ID",
|
52 |
+
"ai_model": "AI Model",
|
53 |
+
"upload_pdf": "Upload PDF",
|
54 |
+
"upload_images": "Upload Images",
|
55 |
+
"chunk_size": "Chunk size",
|
56 |
+
"chunk_overlap": "Chunk overlap",
|
57 |
+
"process_btn": "Process",
|
58 |
+
"processing_status": "Processing status",
|
59 |
+
"qa_tab": "Questions and Answers",
|
60 |
+
"summary_tab": "Summary",
|
61 |
+
"chat_placeholder": "Ask a question about the document...",
|
62 |
+
"chat_title": "Question to document",
|
63 |
+
"chat_btn": "Ask",
|
64 |
+
"generate_summary": "Generate Summary",
|
65 |
+
"summary_label": "Document summary",
|
66 |
+
"pdf_processed": "PDF processed and stored successfully",
|
67 |
+
"load_pdf_first": "Please load a PDF first.",
|
68 |
+
"map_prompt": """Write a concise summary of the following text:
|
69 |
+
"{text}"
|
70 |
+
CONCISE SUMMARY:""",
|
71 |
+
"combine_prompt": """Write a detailed summary based on the following summaries from different sections of the text:
|
72 |
+
"{text}"
|
73 |
+
DETAILED SUMMARY:""",
|
74 |
+
"mini_summary_title": "Summaries of each fragment",
|
75 |
+
"mini_analysis_title": "Analysis of each fragment",
|
76 |
+
"specialist_tab": "Customized Advisor",
|
77 |
+
"specialist_title": "Customized Advisor",
|
78 |
+
"specialist_label": "Set the behavior and role of your advisor. Example: You are a financial expert who helps interpret the data of a financial report. Based on the document and your extensive experience, tell me what opportunities and risks you see in what they propose.",
|
79 |
+
"specialist_output": "Answer of your advisor",
|
80 |
+
"specialist_btn": "Generate Answer"
|
81 |
+
},
|
82 |
+
"Deutsch": {
|
83 |
+
"title": "# 📚 PDF-Prozessor mit Q&A und Zusammenfassung",
|
84 |
+
"model_type": "Modelltyp",
|
85 |
+
"api_key_required": "Um dieses Modell zu verwenden, benötigen Sie einen API-Schlüssel.",
|
86 |
+
"api_key_placeholder": "API-Schlüssel eingeben",
|
87 |
+
"project_id_placeholder": "Projekt-ID eingeben",
|
88 |
+
"ai_model": "AI-Modell",
|
89 |
+
"upload_pdf": "PDF hochladen",
|
90 |
+
"upload_images": "Bilder hochladen",
|
91 |
+
"chunk_size": "Chunk-Größe",
|
92 |
+
"chunk_overlap": "Chunk-Überlappung",
|
93 |
+
"process_btn": "PDF verarbe",
|
94 |
+
"processing_status": "Verarbeitungsstatus",
|
95 |
+
"qa_tab": "Fragen und Antworten",
|
96 |
+
"summary_tab": "Zusammenfassung",
|
97 |
+
"chat_placeholder": "Stellen Sie eine Frage zum Dokument...",
|
98 |
+
"chat_title": "Frage zum Dokument",
|
99 |
+
"chat_btn": "Fragen",
|
100 |
+
"generate_summary": "Zusammenfassung generieren",
|
101 |
+
"summary_label": "Dokumentzusammenfassung",
|
102 |
+
"pdf_processed": "PDF erfolgreich verarbeitet und gespeichert",
|
103 |
+
"load_pdf_first": "Bitte laden Sie zuerst ein PDF hoch.",
|
104 |
+
"map_prompt": """Schreiben Sie eine kurze Zusammenfassung des folgenden Textes:
|
105 |
+
"{text}"
|
106 |
+
KURZE ZUSAMMENFASSUNG:""",
|
107 |
+
"combine_prompt": """Schreiben Sie eine detaillierte Zusammenfassung basierend auf den folgenden Zusammenfassungen verschiedener Textabschnitte:
|
108 |
+
"{text}"
|
109 |
+
DETAILLIERTE ZUSAMMENFASSUNG:""",
|
110 |
+
"mini_summary_title": "Zusammenfassungen von jedem Fragment",
|
111 |
+
"mini_analysis_title": "Analyse von jedem Fragment",
|
112 |
+
"specialist_tab": "Anpassbarer Berater",
|
113 |
+
"specialist_title": "Anpassbarer Berater",
|
114 |
+
"specialist_label": "Setzen Sie das Verhalten und die Rolle Ihres Beraters fest. Beispiel: Sie sind ein Finanzexperte, der bei der Interpretation von Finanzdaten aus einem Bericht hilft. Basierend auf dem Dokument und Ihrer umfassenden Erfahrung, erzählen Sie mir, was Sie in dem sehen, was sie Ihnen vorschlagen.",
|
115 |
+
"specialist_output": "Antwort Ihres Beraters",
|
116 |
+
"specialist_btn": "Antwort generieren"
|
117 |
+
},
|
118 |
+
"Français": {
|
119 |
+
"title": "# 📚 Processeur PDF avec QR et Résumé",
|
120 |
+
"model_type": "Type de modèle",
|
121 |
+
"api_key_required": "Pour utiliser ce modèle, vous avez besoin d'une clé API.",
|
122 |
+
"api_key_placeholder": "Entrez votre clé API",
|
123 |
+
"project_id_placeholder": "Entrez votre ID de projet",
|
124 |
+
"ai_model": "Modèle AI",
|
125 |
+
"upload_pdf": "Charger PDF",
|
126 |
+
"upload_images": "Charger images",
|
127 |
+
"chunk_size": "Taille du chunk",
|
128 |
+
"chunk_overlap": "Chevauchement du chunk",
|
129 |
+
"process_btn": "Traiter le",
|
130 |
+
"processing_status": "État du traitement",
|
131 |
+
"qa_tab": "Questions et Réponses",
|
132 |
+
"summary_tab": "Résumé",
|
133 |
+
"chat_placeholder": "Posez une question sur le document...",
|
134 |
+
"chat_title": "Question au document",
|
135 |
+
"chat_btn": "Poser une question",
|
136 |
+
"generate_summary": "Générer le résumé",
|
137 |
+
"summary_label": "Résumé du document",
|
138 |
+
"pdf_processed": "PDF traité et enregistré avec succès",
|
139 |
+
"load_pdf_first": "Veuillez d'abord charger un PDF.",
|
140 |
+
"map_prompt": """Écrivez un résumé concis du texte suivant :
|
141 |
+
"{text}"
|
142 |
+
RÉSUMÉ CONCIS :""",
|
143 |
+
"combine_prompt": """Écrivez un résumé détaillé basé sur les résumés suivants de différentes sections du texte :
|
144 |
+
"{text}"
|
145 |
+
RÉSUMÉ DÉTAILLÉ :""",
|
146 |
+
"mini_summary_title": "Résumés de chaque fragment",
|
147 |
+
"mini_analysis_title": "Analyse de chaque fragment",
|
148 |
+
"specialist_tab": "Conseiller personnalisé",
|
149 |
+
"specialist_title": "Conseiller personnalisé",
|
150 |
+
"specialist_label": "Définissez le comportement et le rôle de votre conseiller. Exemple : Vous êtes un expert financier qui aide à interpréter les données d'un rapport financier. Basé sur le document et votre vaste expérience, partagez-moi ce que vous voyez dans ce qu'ils vous proposent.",
|
151 |
+
"specialist_output": "Réponse de votre conseiller",
|
152 |
+
"specialist_btn": "Générer la réponse"
|
153 |
+
},
|
154 |
+
"Português": {
|
155 |
+
"title": "# 📚 Processador de PDF com P&R e Resumo",
|
156 |
+
"model_type": "Tipo de modelo",
|
157 |
+
"api_key_required": "Para usar este modelo, necesitas una clave de API.",
|
158 |
+
"api_key_placeholder": "Digite sua chave API",
|
159 |
+
"project_id_placeholder": "Digite seu ID de projeto",
|
160 |
+
"ai_model": "Modelo AI",
|
161 |
+
"upload_pdf": "Carregar PDF",
|
162 |
+
"upload_images": "Carregar imagens",
|
163 |
+
"chunk_size": "Tamanho do chunk",
|
164 |
+
"chunk_overlap": "Sobreposição do chunk",
|
165 |
+
"process_btn": "Processar",
|
166 |
+
"processing_status": "Status do processamento",
|
167 |
+
"qa_tab": "Perguntas e Respostas",
|
168 |
+
"summary_tab": "Resumo",
|
169 |
+
"chat_placeholder": "Faça uma pergunta sobre o documento...",
|
170 |
+
"chat_title": "Pergunta ao documento",
|
171 |
+
"chat_btn": "Perguntar",
|
172 |
+
"generate_summary": "Gerar Resumo",
|
173 |
+
"summary_label": "Resumo do documento",
|
174 |
+
"pdf_processed": "PDF processado e armazenado com sucesso",
|
175 |
+
"load_pdf_first": "Por favor, carregue um PDF primeiro.",
|
176 |
+
"map_prompt": """Escreva um resumo conciso do seguinte texto:
|
177 |
+
"{text}"
|
178 |
+
RESUMO CONCISO:""",
|
179 |
+
"combine_prompt": """Escreva um resumo detalhado baseado nos seguintes resumos de diferentes seções do texto:
|
180 |
+
"{text}"
|
181 |
+
RESUMO DETALHADO:""",
|
182 |
+
"mini_summary_title": "Resúmenes de cada fragmento",
|
183 |
+
"mini_analysis_title": "Análisis de cada fragmento",
|
184 |
+
"specialist_tab": "Assistente Personalizado",
|
185 |
+
"specialist_title": "Assistente Personalizado",
|
186 |
+
"specialist_label": "Defina o comportamento e o papel do seu assistente. Exemplo: Você é um especialista em finanças que ajuda a interpretar os dados de um relatório financeiro. Com base no documento e em sua ampla experiência, compartilhe comigo o que você vê naquilo que eles lhe propõem.",
|
187 |
+
"specialist_output": "Resposta do seu assistente",
|
188 |
+
"specialist_btn": "Gerar Resposta"
|
189 |
+
}
|
190 |
+
}
|