Initial commit
Browse files- .gitignore +106 -0
- gradio_app.py +433 -0
- readme.md +96 -0
- requirements.txt +16 -0
.gitignore
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Python
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
*.so
|
6 |
+
.Python
|
7 |
+
build/
|
8 |
+
develop-eggs/
|
9 |
+
dist/
|
10 |
+
downloads/
|
11 |
+
eggs/
|
12 |
+
.eggs/
|
13 |
+
lib/
|
14 |
+
lib64/
|
15 |
+
parts/
|
16 |
+
sdist/
|
17 |
+
var/
|
18 |
+
wheels/
|
19 |
+
pip-wheel-metadata/
|
20 |
+
share/python-wheels/
|
21 |
+
*.egg-info/
|
22 |
+
.installed.cfg
|
23 |
+
*.egg
|
24 |
+
MANIFEST
|
25 |
+
|
26 |
+
# PyInstaller
|
27 |
+
*.manifest
|
28 |
+
*.spec
|
29 |
+
|
30 |
+
# Installer logs
|
31 |
+
pip-log.txt
|
32 |
+
pip-delete-this-directory.txt
|
33 |
+
|
34 |
+
# Unit test / coverage reports
|
35 |
+
htmlcov/
|
36 |
+
.tox/
|
37 |
+
.nox/
|
38 |
+
.coverage
|
39 |
+
.coverage.*
|
40 |
+
.cache
|
41 |
+
nosetests.xml
|
42 |
+
coverage.xml
|
43 |
+
*.cover
|
44 |
+
*.py,cover
|
45 |
+
.hypothesis/
|
46 |
+
.pytest_cache/
|
47 |
+
|
48 |
+
# Virtual environments
|
49 |
+
.env
|
50 |
+
.venv
|
51 |
+
env/
|
52 |
+
venv/
|
53 |
+
ENV/
|
54 |
+
env.bak/
|
55 |
+
venv.bak/
|
56 |
+
|
57 |
+
# IDEs
|
58 |
+
.vscode/
|
59 |
+
.idea/
|
60 |
+
*.swp
|
61 |
+
*.swo
|
62 |
+
*~
|
63 |
+
|
64 |
+
# OS
|
65 |
+
.DS_Store
|
66 |
+
.DS_Store?
|
67 |
+
._*
|
68 |
+
.Spotlight-V100
|
69 |
+
.Trashes
|
70 |
+
ehthumbs.db
|
71 |
+
Thumbs.db
|
72 |
+
|
73 |
+
# Gradio
|
74 |
+
gradio_cached_examples/
|
75 |
+
flagged/
|
76 |
+
|
77 |
+
# LlamaIndex
|
78 |
+
storage/
|
79 |
+
*.index
|
80 |
+
|
81 |
+
# Temporary files
|
82 |
+
tmp/
|
83 |
+
temp/
|
84 |
+
*.tmp
|
85 |
+
*.temp
|
86 |
+
|
87 |
+
# API keys and secrets
|
88 |
+
.env
|
89 |
+
.env.local
|
90 |
+
.env.*.local
|
91 |
+
config.json
|
92 |
+
secrets.json
|
93 |
+
|
94 |
+
# Large files
|
95 |
+
*.pdf
|
96 |
+
*.docx
|
97 |
+
*.doc
|
98 |
+
*.ppt
|
99 |
+
*.pptx
|
100 |
+
*.zip
|
101 |
+
*.tar.gz
|
102 |
+
*.rar
|
103 |
+
|
104 |
+
# Logs
|
105 |
+
*.log
|
106 |
+
logs/
|
gradio_app.py
ADDED
@@ -0,0 +1,433 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import os
|
3 |
+
import time
|
4 |
+
import tempfile
|
5 |
+
import shutil
|
6 |
+
from pathlib import Path
|
7 |
+
from collections import defaultdict
|
8 |
+
import json
|
9 |
+
|
10 |
+
# LlamaIndex imports
|
11 |
+
from llama_index.core import (
|
12 |
+
VectorStoreIndex,
|
13 |
+
SimpleDirectoryReader,
|
14 |
+
Settings,
|
15 |
+
StorageContext,
|
16 |
+
load_index_from_storage
|
17 |
+
)
|
18 |
+
from llama_index.core.node_parser import SentenceSplitter
|
19 |
+
from llama_index.llms.openrouter import OpenRouter
|
20 |
+
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
|
21 |
+
|
22 |
+
# Global variables
|
23 |
+
current_index = None
|
24 |
+
current_query_engine = None
|
25 |
+
query_stats = defaultdict(list)
|
26 |
+
temp_doc_dir = None
|
27 |
+
|
28 |
+
# Available models
|
29 |
+
AVAILABLE_MODELS = {
|
30 |
+
"GPT-4o": "openai/gpt-4o",
|
31 |
+
"GPT-4o Mini": "openai/gpt-4o-mini",
|
32 |
+
"Claude 3.5 Sonnet": "anthropic/claude-3.5-sonnet",
|
33 |
+
"Claude 3 Haiku": "anthropic/claude-3-haiku",
|
34 |
+
"Llama 3.1 70B": "meta-llama/llama-3.1-70b-instruct",
|
35 |
+
"Llama 3.1 8B": "meta-llama/llama-3.1-8b-instruct",
|
36 |
+
"Mistral Large": "mistralai/mistral-large",
|
37 |
+
"Gemini Pro": "google/gemini-pro"
|
38 |
+
}
|
39 |
+
|
40 |
+
def initialize_embeddings():
|
41 |
+
"""Initialize the embedding model"""
|
42 |
+
try:
|
43 |
+
Settings.embed_model = HuggingFaceEmbedding(
|
44 |
+
model_name="BAAI/bge-small-en-v1.5"
|
45 |
+
)
|
46 |
+
return "β Embedding model initialized successfully"
|
47 |
+
except Exception as e:
|
48 |
+
return f"β Error initializing embeddings: {str(e)}"
|
49 |
+
|
50 |
+
def setup_llm(api_key, model_name, temperature=0.1, max_tokens=512):
|
51 |
+
"""Setup the language model"""
|
52 |
+
try:
|
53 |
+
if not api_key:
|
54 |
+
return None, "β API key is required"
|
55 |
+
|
56 |
+
model_id = AVAILABLE_MODELS.get(model_name, "openai/gpt-4o")
|
57 |
+
|
58 |
+
llm = OpenRouter(
|
59 |
+
api_key=api_key,
|
60 |
+
max_tokens=max_tokens,
|
61 |
+
context_window=4096,
|
62 |
+
model=model_id,
|
63 |
+
temperature=temperature
|
64 |
+
)
|
65 |
+
|
66 |
+
# Test the connection
|
67 |
+
test_response = llm.complete("Hello")
|
68 |
+
return llm, f"β {model_name} configured successfully"
|
69 |
+
except Exception as e:
|
70 |
+
return None, f"β Error setting up LLM: {str(e)}"
|
71 |
+
|
72 |
+
def process_uploaded_files(files, progress=gr.Progress()):
|
73 |
+
"""Process uploaded files and create document index"""
|
74 |
+
global current_index, temp_doc_dir
|
75 |
+
|
76 |
+
if not files:
|
77 |
+
return "β No files uploaded", "", ""
|
78 |
+
|
79 |
+
try:
|
80 |
+
# Create temporary directory for documents
|
81 |
+
if temp_doc_dir and os.path.exists(temp_doc_dir):
|
82 |
+
shutil.rmtree(temp_doc_dir)
|
83 |
+
|
84 |
+
temp_doc_dir = tempfile.mkdtemp()
|
85 |
+
|
86 |
+
# Copy uploaded files to temp directory
|
87 |
+
file_info = []
|
88 |
+
progress(0, desc="Copying files...")
|
89 |
+
|
90 |
+
for i, file in enumerate(files):
|
91 |
+
if file is not None:
|
92 |
+
file_path = Path(file.name)
|
93 |
+
dest_path = os.path.join(temp_doc_dir, file_path.name)
|
94 |
+
shutil.copy2(file.name, dest_path)
|
95 |
+
file_info.append(f"β’ {file_path.name} ({file_path.suffix})")
|
96 |
+
progress((i + 1) / len(files) * 0.3, desc=f"Copying {file_path.name}...")
|
97 |
+
|
98 |
+
progress(0.3, desc="Loading documents...")
|
99 |
+
|
100 |
+
# Load documents
|
101 |
+
documents = SimpleDirectoryReader(
|
102 |
+
input_dir=temp_doc_dir,
|
103 |
+
exclude_hidden=True,
|
104 |
+
recursive=True
|
105 |
+
).load_data()
|
106 |
+
|
107 |
+
if not documents:
|
108 |
+
return "β No readable documents found", "", ""
|
109 |
+
|
110 |
+
progress(0.5, desc="Creating text chunks...")
|
111 |
+
|
112 |
+
# Configure text splitting
|
113 |
+
text_splitter = SentenceSplitter(
|
114 |
+
chunk_size=512,
|
115 |
+
chunk_overlap=50
|
116 |
+
)
|
117 |
+
|
118 |
+
progress(0.7, desc="Building vector index...")
|
119 |
+
|
120 |
+
# Create vector index
|
121 |
+
current_index = VectorStoreIndex.from_documents(
|
122 |
+
documents,
|
123 |
+
transformations=[text_splitter],
|
124 |
+
show_progress=False
|
125 |
+
)
|
126 |
+
|
127 |
+
progress(1.0, desc="Index created successfully!")
|
128 |
+
|
129 |
+
# Calculate statistics
|
130 |
+
total_chars = sum(len(doc.text) for doc in documents)
|
131 |
+
|
132 |
+
status = f"β Successfully processed {len(documents)} documents"
|
133 |
+
file_list = "\n".join(file_info)
|
134 |
+
stats = f"π Total content: ~{total_chars:,} characters\nπ Files processed: {len(files)}"
|
135 |
+
|
136 |
+
return status, file_list, stats
|
137 |
+
|
138 |
+
except Exception as e:
|
139 |
+
return f"β Error processing files: {str(e)}", "", ""
|
140 |
+
|
141 |
+
def create_query_engine(api_key, model_name, temperature, max_tokens, similarity_k):
|
142 |
+
"""Create query engine with current settings"""
|
143 |
+
global current_query_engine
|
144 |
+
|
145 |
+
if not current_index:
|
146 |
+
return None, "β No document index available. Please upload documents first."
|
147 |
+
|
148 |
+
llm, llm_status = setup_llm(api_key, model_name, temperature, max_tokens)
|
149 |
+
if not llm:
|
150 |
+
return None, llm_status
|
151 |
+
|
152 |
+
try:
|
153 |
+
current_query_engine = current_index.as_query_engine(
|
154 |
+
llm=llm,
|
155 |
+
similarity_top_k=similarity_k,
|
156 |
+
response_mode="tree_summarize",
|
157 |
+
verbose=False
|
158 |
+
)
|
159 |
+
return current_query_engine, f"β Query engine ready with {model_name}"
|
160 |
+
except Exception as e:
|
161 |
+
return None, f"β Error creating query engine: {str(e)}"
|
162 |
+
|
163 |
+
def query_documents(question, api_key, model_name, temperature, max_tokens, similarity_k, show_sources):
|
164 |
+
"""Query the document index"""
|
165 |
+
global query_stats
|
166 |
+
|
167 |
+
if not question.strip():
|
168 |
+
return "Please enter a question.", "", ""
|
169 |
+
|
170 |
+
if not current_index:
|
171 |
+
return "β No documents loaded. Please upload documents first.", "", ""
|
172 |
+
|
173 |
+
# Create/update query engine
|
174 |
+
query_engine, status = create_query_engine(api_key, model_name, temperature, max_tokens, similarity_k)
|
175 |
+
if not query_engine:
|
176 |
+
return status, "", ""
|
177 |
+
|
178 |
+
try:
|
179 |
+
start_time = time.time()
|
180 |
+
|
181 |
+
# Query the documents
|
182 |
+
response = query_engine.query(question)
|
183 |
+
query_time = time.time() - start_time
|
184 |
+
|
185 |
+
# Track statistics
|
186 |
+
query_stats['response_times'].append(query_time)
|
187 |
+
query_stats['questions'].append(question)
|
188 |
+
|
189 |
+
# Format response
|
190 |
+
answer = str(response)
|
191 |
+
|
192 |
+
# Format sources if requested
|
193 |
+
sources_text = ""
|
194 |
+
if show_sources and hasattr(response, 'source_nodes'):
|
195 |
+
sources_list = []
|
196 |
+
for i, node in enumerate(response.source_nodes, 1):
|
197 |
+
file_name = node.metadata.get('file_name', 'Unknown')
|
198 |
+
score = getattr(node, 'score', 0)
|
199 |
+
content_preview = node.text[:150] + "..." if len(node.text) > 150 else node.text
|
200 |
+
sources_list.append(f"**Source {i}:** {file_name} (relevance: {score:.3f})\n{content_preview}")
|
201 |
+
sources_text = "\n\n".join(sources_list)
|
202 |
+
|
203 |
+
# Performance info
|
204 |
+
perf_info = f"β±οΈ Response time: {query_time:.2f}s | Model: {model_name}"
|
205 |
+
|
206 |
+
return answer, sources_text, perf_info
|
207 |
+
|
208 |
+
except Exception as e:
|
209 |
+
return f"β Error during query: {str(e)}", "", ""
|
210 |
+
|
211 |
+
def get_performance_stats():
|
212 |
+
"""Get performance statistics"""
|
213 |
+
if not query_stats['response_times']:
|
214 |
+
return "No queries performed yet."
|
215 |
+
|
216 |
+
times = query_stats['response_times']
|
217 |
+
stats = f"""π **Performance Statistics** (based on {len(times)} queries)
|
218 |
+
|
219 |
+
β’ Average response time: {sum(times)/len(times):.2f}s
|
220 |
+
β’ Fastest response: {min(times):.2f}s
|
221 |
+
β’ Slowest response: {max(times):.2f}s
|
222 |
+
β’ Total queries: {len(times)}
|
223 |
+
"""
|
224 |
+
return stats
|
225 |
+
|
226 |
+
def clear_all_data():
|
227 |
+
"""Clear all data and reset the application"""
|
228 |
+
global current_index, current_query_engine, query_stats, temp_doc_dir
|
229 |
+
|
230 |
+
current_index = None
|
231 |
+
current_query_engine = None
|
232 |
+
query_stats = defaultdict(list)
|
233 |
+
|
234 |
+
if temp_doc_dir and os.path.exists(temp_doc_dir):
|
235 |
+
shutil.rmtree(temp_doc_dir)
|
236 |
+
temp_doc_dir = None
|
237 |
+
|
238 |
+
return "β All data cleared", "", "", "", ""
|
239 |
+
|
240 |
+
# Initialize embeddings on startup
|
241 |
+
embedding_status = initialize_embeddings()
|
242 |
+
|
243 |
+
# Create Gradio interface
|
244 |
+
with gr.Blocks(title="Document Q&A System", theme=gr.themes.Soft()) as app:
|
245 |
+
gr.Markdown("""
|
246 |
+
# π Document Q&A System
|
247 |
+
|
248 |
+
Upload your documents and ask questions about them using advanced AI models.
|
249 |
+
Built with LlamaIndex and powered by multiple LLM providers through OpenRouter.
|
250 |
+
""")
|
251 |
+
|
252 |
+
with gr.Tab("π€ Upload Documents"):
|
253 |
+
gr.Markdown("### Upload Your Documents")
|
254 |
+
gr.Markdown("Supported formats: PDF, TXT, DOCX, MD, and more")
|
255 |
+
|
256 |
+
file_upload = gr.File(
|
257 |
+
label="Choose files",
|
258 |
+
file_count="multiple",
|
259 |
+
file_types=[".pdf", ".txt", ".docx", ".md", ".csv", ".json"]
|
260 |
+
)
|
261 |
+
|
262 |
+
upload_btn = gr.Button("Process Documents", variant="primary")
|
263 |
+
|
264 |
+
with gr.Row():
|
265 |
+
with gr.Column():
|
266 |
+
upload_status = gr.Textbox(label="Status", interactive=False)
|
267 |
+
file_list = gr.Textbox(label="Uploaded Files", lines=5, interactive=False)
|
268 |
+
with gr.Column():
|
269 |
+
doc_stats = gr.Textbox(label="Document Statistics", lines=5, interactive=False)
|
270 |
+
|
271 |
+
with gr.Tab("π¬ Ask Questions"):
|
272 |
+
gr.Markdown("### Query Your Documents")
|
273 |
+
|
274 |
+
with gr.Row():
|
275 |
+
with gr.Column(scale=2):
|
276 |
+
question_input = gr.Textbox(
|
277 |
+
label="Your Question",
|
278 |
+
placeholder="What would you like to know about the documents?",
|
279 |
+
lines=2
|
280 |
+
)
|
281 |
+
|
282 |
+
with gr.Row():
|
283 |
+
query_btn = gr.Button("Ask Question", variant="primary")
|
284 |
+
clear_btn = gr.Button("Clear All Data", variant="stop")
|
285 |
+
|
286 |
+
answer_output = gr.Textbox(
|
287 |
+
label="Answer",
|
288 |
+
lines=10,
|
289 |
+
interactive=False
|
290 |
+
)
|
291 |
+
|
292 |
+
performance_info = gr.Textbox(
|
293 |
+
label="Performance Info",
|
294 |
+
interactive=False
|
295 |
+
)
|
296 |
+
|
297 |
+
with gr.Column(scale=1):
|
298 |
+
gr.Markdown("### Settings")
|
299 |
+
|
300 |
+
api_key_input = gr.Textbox(
|
301 |
+
label="OpenRouter API Key",
|
302 |
+
placeholder="Enter your API key or leave empty to use HF secret",
|
303 |
+
type="password"
|
304 |
+
)
|
305 |
+
|
306 |
+
model_dropdown = gr.Dropdown(
|
307 |
+
label="Model",
|
308 |
+
choices=list(AVAILABLE_MODELS.keys()),
|
309 |
+
value="GPT-4o Mini",
|
310 |
+
interactive=True
|
311 |
+
)
|
312 |
+
|
313 |
+
temperature_slider = gr.Slider(
|
314 |
+
label="Temperature",
|
315 |
+
minimum=0.0,
|
316 |
+
maximum=2.0,
|
317 |
+
value=0.1,
|
318 |
+
step=0.1
|
319 |
+
)
|
320 |
+
|
321 |
+
max_tokens_slider = gr.Slider(
|
322 |
+
label="Max Tokens",
|
323 |
+
minimum=100,
|
324 |
+
maximum=2000,
|
325 |
+
value=512,
|
326 |
+
step=50
|
327 |
+
)
|
328 |
+
|
329 |
+
similarity_k_slider = gr.Slider(
|
330 |
+
label="Sources to Retrieve",
|
331 |
+
minimum=1,
|
332 |
+
maximum=10,
|
333 |
+
value=5,
|
334 |
+
step=1
|
335 |
+
)
|
336 |
+
|
337 |
+
show_sources_checkbox = gr.Checkbox(
|
338 |
+
label="Show Sources",
|
339 |
+
value=True
|
340 |
+
)
|
341 |
+
|
342 |
+
with gr.Accordion("π Sources Used", open=False):
|
343 |
+
sources_output = gr.Textbox(
|
344 |
+
label="Source Documents",
|
345 |
+
lines=8,
|
346 |
+
interactive=False
|
347 |
+
)
|
348 |
+
|
349 |
+
with gr.Tab("π Performance"):
|
350 |
+
gr.Markdown("### Performance Statistics")
|
351 |
+
|
352 |
+
stats_btn = gr.Button("Refresh Stats")
|
353 |
+
performance_stats = gr.Textbox(
|
354 |
+
label="Statistics",
|
355 |
+
lines=10,
|
356 |
+
interactive=False
|
357 |
+
)
|
358 |
+
|
359 |
+
with gr.Tab("βΉοΈ Help"):
|
360 |
+
gr.Markdown("""
|
361 |
+
### How to Use This Application
|
362 |
+
|
363 |
+
1. **Upload Documents**: Go to the "Upload Documents" tab and select your files
|
364 |
+
2. **Process**: Click "Process Documents" to create the searchable index
|
365 |
+
3. **Ask Questions**: Use the "Ask Questions" tab to query your documents
|
366 |
+
4. **Adjust Settings**: Modify model parameters for different response styles
|
367 |
+
|
368 |
+
### Best Practices for Questions
|
369 |
+
|
370 |
+
- π― **Be specific**: "What does Smith say about feminist theology?" vs "Tell me about feminism"
|
371 |
+
- π **Ask about concepts**: "What is religious authority?" rather than just names
|
372 |
+
- π **Use comparative questions**: "How do different scholars approach this topic?"
|
373 |
+
- π **Request analysis**: "What are the main arguments presented?"
|
374 |
+
- ποΈ **Ask about methodology**: "What research methods are discussed?"
|
375 |
+
|
376 |
+
### API Key Setup
|
377 |
+
|
378 |
+
You can provide your OpenRouter API key in two ways:
|
379 |
+
1. Enter it directly in the "API Key" field
|
380 |
+
2. Set it as a Hugging Face Space secret named `OPENROUTER_API_KEY`
|
381 |
+
|
382 |
+
### Model Information
|
383 |
+
|
384 |
+
Different models have different strengths:
|
385 |
+
- **GPT-4o**: Best overall performance, most accurate
|
386 |
+
- **Claude 3.5 Sonnet**: Excellent reasoning and analysis
|
387 |
+
- **Llama models**: Open source, good performance
|
388 |
+
- **Mistral**: Strong multilingual capabilities
|
389 |
+
""")
|
390 |
+
|
391 |
+
# Event handlers
|
392 |
+
upload_btn.click(
|
393 |
+
fn=process_uploaded_files,
|
394 |
+
inputs=[file_upload],
|
395 |
+
outputs=[upload_status, file_list, doc_stats],
|
396 |
+
show_progress=True
|
397 |
+
)
|
398 |
+
|
399 |
+
query_btn.click(
|
400 |
+
fn=query_documents,
|
401 |
+
inputs=[
|
402 |
+
question_input, api_key_input, model_dropdown,
|
403 |
+
temperature_slider, max_tokens_slider, similarity_k_slider,
|
404 |
+
show_sources_checkbox
|
405 |
+
],
|
406 |
+
outputs=[answer_output, sources_output, performance_info]
|
407 |
+
)
|
408 |
+
|
409 |
+
clear_btn.click(
|
410 |
+
fn=clear_all_data,
|
411 |
+
inputs=[],
|
412 |
+
outputs=[upload_status, file_list, doc_stats, answer_output, sources_output]
|
413 |
+
)
|
414 |
+
|
415 |
+
stats_btn.click(
|
416 |
+
fn=get_performance_stats,
|
417 |
+
inputs=[],
|
418 |
+
outputs=[performance_stats]
|
419 |
+
)
|
420 |
+
|
421 |
+
# Auto-refresh API key from environment if not provided
|
422 |
+
def get_api_key():
|
423 |
+
return os.getenv("OPENROUTER_API_KEY", "")
|
424 |
+
|
425 |
+
app.load(
|
426 |
+
fn=get_api_key,
|
427 |
+
inputs=[],
|
428 |
+
outputs=[api_key_input]
|
429 |
+
)
|
430 |
+
|
431 |
+
# Launch the app
|
432 |
+
if __name__ == "__main__":
|
433 |
+
app.launch()
|
readme.md
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# π Document Q&A System
|
2 |
+
|
3 |
+
A powerful document question-answering system built with LlamaIndex and Gradio. Upload your documents and ask questions about them using state-of-the-art AI models.
|
4 |
+
|
5 |
+
## Features
|
6 |
+
|
7 |
+
π **Smart Document Processing**: Automatically processes various document formats (PDF, TXT, DOCX, MD, CSV, JSON)
|
8 |
+
|
9 |
+
π€ **Multiple AI Models**: Choose from GPT-4o, Claude 3.5 Sonnet, Llama 3.1, Mistral, and more
|
10 |
+
|
11 |
+
π **Performance Monitoring**: Track response times and query statistics
|
12 |
+
|
13 |
+
π― **Source Attribution**: See which document sections were used to generate answers
|
14 |
+
|
15 |
+
βοΈ **Customizable Settings**: Adjust temperature, token limits, and retrieval parameters
|
16 |
+
|
17 |
+
π **Secure API Key Management**: Use environment variables or direct input
|
18 |
+
|
19 |
+
## How to Use
|
20 |
+
|
21 |
+
### 1. Upload Documents
|
22 |
+
- Go to the "Upload Documents" tab
|
23 |
+
- Select your files (PDF, TXT, DOCX, MD, CSV, JSON)
|
24 |
+
- Click "Process Documents" to create the searchable index
|
25 |
+
|
26 |
+
### 2. Configure Settings
|
27 |
+
- Add your OpenRouter API key (or set as HF Space secret)
|
28 |
+
- Choose your preferred AI model
|
29 |
+
- Adjust parameters like temperature and max tokens
|
30 |
+
|
31 |
+
### 3. Ask Questions
|
32 |
+
- Enter your question in the "Ask Questions" tab
|
33 |
+
- Click "Ask Question" to get AI-powered answers
|
34 |
+
- View sources and performance metrics
|
35 |
+
|
36 |
+
## API Key Setup
|
37 |
+
|
38 |
+
You can provide your OpenRouter API key in two ways:
|
39 |
+
|
40 |
+
1. **Direct Input**: Enter it in the "API Key" field in the interface
|
41 |
+
2. **Environment Variable**: Set `OPENROUTER_API_KEY` as a Hugging Face Space secret
|
42 |
+
|
43 |
+
Get your API key from [OpenRouter](https://openrouter.ai/)
|
44 |
+
|
45 |
+
## Best Practices for Questions
|
46 |
+
|
47 |
+
- π― **Be specific**: "What does the author say about climate change?" vs "Tell me about climate"
|
48 |
+
- π **Ask about concepts**: "What is the main methodology discussed?"
|
49 |
+
- π **Use comparative questions**: "How do different studies approach this topic?"
|
50 |
+
- π **Request analysis**: "What are the key findings presented?"
|
51 |
+
- ποΈ **Ask about methodology**: "What research methods are used?"
|
52 |
+
|
53 |
+
## Available Models
|
54 |
+
|
55 |
+
- **GPT-4o**: Best overall performance, most accurate
|
56 |
+
- **GPT-4o Mini**: Faster, cost-effective option
|
57 |
+
- **Claude 3.5 Sonnet**: Excellent reasoning and analysis
|
58 |
+
- **Claude 3 Haiku**: Fast and efficient
|
59 |
+
- **Llama 3.1 70B/8B**: Open source, strong performance
|
60 |
+
- **Mistral Large**: Strong multilingual capabilities
|
61 |
+
- **Gemini Pro**: Google's advanced model
|
62 |
+
|
63 |
+
## Technical Details
|
64 |
+
|
65 |
+
Built with:
|
66 |
+
- **LlamaIndex**: Document indexing and retrieval
|
67 |
+
- **Gradio**: Web interface
|
68 |
+
- **OpenRouter**: Multi-model API access
|
69 |
+
- **HuggingFace Embeddings**: Text vectorization
|
70 |
+
- **BGE-small-en-v1.5**: Efficient embedding model
|
71 |
+
|
72 |
+
## Performance
|
73 |
+
|
74 |
+
- Vector-based semantic search for accurate retrieval
|
75 |
+
- Cached indexing for fast subsequent queries
|
76 |
+
- Configurable chunk sizes and overlap for optimal results
|
77 |
+
- Real-time performance monitoring
|
78 |
+
|
79 |
+
## Development
|
80 |
+
|
81 |
+
To run locally:
|
82 |
+
|
83 |
+
```bash
|
84 |
+
git clone <your-repo>
|
85 |
+
cd document-qa-system
|
86 |
+
pip install -r requirements.txt
|
87 |
+
python app.py
|
88 |
+
```
|
89 |
+
|
90 |
+
## License
|
91 |
+
|
92 |
+
This project is open source and available under the MIT License.
|
93 |
+
|
94 |
+
## Support
|
95 |
+
|
96 |
+
For issues or questions, please check the Help tab in the application or create an issue in the repository.
|
requirements.txt
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio>=4.0.0
|
2 |
+
llama-index>=0.10.0
|
3 |
+
llama-index-core>=0.10.0
|
4 |
+
llama-index-llms-openrouter>=0.1.0
|
5 |
+
llama-index-embeddings-huggingface>=0.2.0
|
6 |
+
sentence-transformers>=2.2.0
|
7 |
+
torch>=2.0.0
|
8 |
+
transformers>=4.30.0
|
9 |
+
numpy>=1.24.0
|
10 |
+
pandas>=2.0.0
|
11 |
+
python-dotenv>=1.0.0
|
12 |
+
pathlib
|
13 |
+
tempfile
|
14 |
+
shutil
|
15 |
+
json
|
16 |
+
collections
|