Spaces:
Running
Running
Organize repository structure: move files to appropriate directories
Browse files- .gitignore +7 -20
- TEST_PROCEDURE.md +0 -285
- claude.local.md +0 -314
- file_upload_proposal.md +0 -144
- hf_comparisons.aiconfig.json +0 -115
- test_api_key.py +0 -85
- test_connection_fix.py +0 -137
- test_document.txt +0 -24
- test_gradio_simple.py +0 -24
- test_minimal.py +0 -20
- test_preview.py +0 -110
- test_rag_fix.py +0 -182
- test_sample.txt +0 -8
- test_vector_db.py +0 -196
.gitignore
CHANGED
@@ -29,22 +29,17 @@ Thumbs.db
|
|
29 |
# Generated files
|
30 |
*.zip
|
31 |
|
32 |
-
#
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
*_SUMMARY.md
|
38 |
-
WEB_SEARCH_IMPLEMENTATION.md
|
39 |
-
simplified_documentation.md
|
40 |
-
file_upload_proposal.md
|
41 |
-
TEST_PROCEDURE.md
|
42 |
|
43 |
# Gradio cache
|
44 |
.gradio/
|
45 |
flagged/
|
46 |
|
47 |
-
# Development images
|
48 |
*.png
|
49 |
!secret.png
|
50 |
*.jpg
|
@@ -52,12 +47,4 @@ flagged/
|
|
52 |
*.gif
|
53 |
|
54 |
# Claude local files
|
55 |
-
claude
|
56 |
-
.claude/
|
57 |
-
|
58 |
-
# Config files
|
59 |
-
*.aiconfig.json
|
60 |
-
|
61 |
-
# Sample/temp files
|
62 |
-
test_*.txt
|
63 |
-
*_sample.txt
|
|
|
29 |
# Generated files
|
30 |
*.zip
|
31 |
|
32 |
+
# Organized directories
|
33 |
+
docs/
|
34 |
+
tests/
|
35 |
+
development/
|
36 |
+
temp/
|
|
|
|
|
|
|
|
|
|
|
37 |
|
38 |
# Gradio cache
|
39 |
.gradio/
|
40 |
flagged/
|
41 |
|
42 |
+
# Development images (except secret.png)
|
43 |
*.png
|
44 |
!secret.png
|
45 |
*.jpg
|
|
|
47 |
*.gif
|
48 |
|
49 |
# Claude local files
|
50 |
+
.claude/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TEST_PROCEDURE.md
DELETED
@@ -1,285 +0,0 @@
|
|
1 |
-
# Chat UI Helper - Comprehensive Test Procedure
|
2 |
-
|
3 |
-
This document outlines a systematic test procedure for validating the Chat UI Helper application after new commits. This procedure ensures all components function correctly and can be iterated upon as the project evolves.
|
4 |
-
|
5 |
-
## Pre-Test Setup
|
6 |
-
|
7 |
-
### Environment Verification
|
8 |
-
```bash
|
9 |
-
# Verify Python environment
|
10 |
-
python --version # Should be 3.8+
|
11 |
-
|
12 |
-
# Install/update dependencies
|
13 |
-
pip install -r requirements.txt
|
14 |
-
|
15 |
-
# Verify optional dependencies status
|
16 |
-
python -c "
|
17 |
-
try:
|
18 |
-
import sentence_transformers, faiss, fitz, docx
|
19 |
-
print('✅ All RAG dependencies available')
|
20 |
-
except ImportError as e:
|
21 |
-
print(f'⚠️ Optional RAG dependencies missing: {e}')
|
22 |
-
"
|
23 |
-
```
|
24 |
-
|
25 |
-
### Test Data Preparation
|
26 |
-
```bash
|
27 |
-
# Ensure test document exists
|
28 |
-
echo "This is a test document for RAG functionality testing." > test_document.txt
|
29 |
-
|
30 |
-
# Create test directory structure if needed
|
31 |
-
mkdir -p test_outputs
|
32 |
-
```
|
33 |
-
|
34 |
-
## Test Categories
|
35 |
-
|
36 |
-
### 1. Core Application Tests
|
37 |
-
|
38 |
-
#### 1.1 Application Startup
|
39 |
-
```bash
|
40 |
-
# Test basic application launch
|
41 |
-
python app.py &
|
42 |
-
APP_PID=$!
|
43 |
-
sleep 10
|
44 |
-
curl -f http://localhost:7860 > /dev/null && echo "✅ App started successfully" || echo "❌ App failed to start"
|
45 |
-
kill $APP_PID
|
46 |
-
```
|
47 |
-
|
48 |
-
#### 1.2 Gradio Interface Validation
|
49 |
-
- [ ] Application loads without errors
|
50 |
-
- [ ] Two tabs visible: "Spaces Configuration" and "Chat Support"
|
51 |
-
- [ ] All form fields render correctly
|
52 |
-
- [ ] Template selection works (Custom vs Research Assistant)
|
53 |
-
- [ ] File upload components appear when RAG is enabled
|
54 |
-
|
55 |
-
### 2. Vector RAG Component Tests
|
56 |
-
|
57 |
-
#### 2.1 Individual Component Testing
|
58 |
-
```bash
|
59 |
-
# Test document processing
|
60 |
-
python -c "from test_vector_db import test_document_processing; test_document_processing()"
|
61 |
-
|
62 |
-
# Test vector store functionality
|
63 |
-
python -c "from test_vector_db import test_vector_store; test_vector_store()"
|
64 |
-
|
65 |
-
# Test full RAG pipeline
|
66 |
-
python -c "from test_vector_db import test_rag_tool; test_rag_tool()"
|
67 |
-
```
|
68 |
-
|
69 |
-
#### 2.2 RAG Integration Tests
|
70 |
-
- [ ] Document upload accepts PDF, DOCX, TXT, MD files
|
71 |
-
- [ ] File size validation (10MB limit) works
|
72 |
-
- [ ] Documents are processed and chunked correctly
|
73 |
-
- [ ] Vector embeddings are generated
|
74 |
-
- [ ] Similarity search returns relevant results
|
75 |
-
- [ ] RAG data serializes/deserializes properly for templates
|
76 |
-
|
77 |
-
### 3. Space Generation Tests
|
78 |
-
|
79 |
-
#### 3.1 Basic Space Creation
|
80 |
-
- [ ] Generate space with minimal configuration
|
81 |
-
- [ ] Verify all required files are created (app.py, requirements.txt, README.md, config.json)
|
82 |
-
- [ ] Check generated app.py syntax is valid
|
83 |
-
- [ ] Verify requirements.txt has correct dependencies
|
84 |
-
- [ ] Ensure README.md contains proper deployment instructions
|
85 |
-
|
86 |
-
#### 3.2 Advanced Feature Testing
|
87 |
-
- [ ] Generate space with URL grounding enabled
|
88 |
-
- [ ] Generate space with vector RAG enabled
|
89 |
-
- [ ] Generate space with access code protection
|
90 |
-
- [ ] Test template substitution works correctly
|
91 |
-
- [ ] Verify environment variable security pattern
|
92 |
-
|
93 |
-
### 4. Web Scraping Tests
|
94 |
-
|
95 |
-
#### 4.1 Mock vs Production Mode
|
96 |
-
```bash
|
97 |
-
# Test in mock mode (lines 14-18 in app.py)
|
98 |
-
# Verify placeholder content is returned
|
99 |
-
|
100 |
-
# Test in production mode
|
101 |
-
# Verify actual web content is fetched via HTTP requests
|
102 |
-
```
|
103 |
-
|
104 |
-
#### 4.2 URL Processing
|
105 |
-
- [ ] Valid URLs are processed correctly
|
106 |
-
- [ ] Invalid URLs are handled gracefully
|
107 |
-
- [ ] Content extraction works for different site types
|
108 |
-
- [ ] Rate limiting and error handling work
|
109 |
-
|
110 |
-
### 5. Security and Configuration Tests
|
111 |
-
|
112 |
-
#### 5.1 Environment Variable Handling
|
113 |
-
- [ ] API keys are not embedded in generated templates
|
114 |
-
- [ ] Access codes use environment variable pattern
|
115 |
-
- [ ] Sensitive data is properly excluded from version control
|
116 |
-
|
117 |
-
#### 5.2 Input Validation
|
118 |
-
- [ ] File upload validation works
|
119 |
-
- [ ] URL validation prevents malicious inputs
|
120 |
-
- [ ] Content length limits are enforced
|
121 |
-
- [ ] XSS prevention in user inputs
|
122 |
-
|
123 |
-
### 6. Chat Support Tests
|
124 |
-
|
125 |
-
#### 6.1 OpenRouter Integration
|
126 |
-
- [ ] Chat responds when API key is configured
|
127 |
-
- [ ] Proper error message when API key is missing
|
128 |
-
- [ ] Message history formatting works correctly
|
129 |
-
- [ ] URL grounding provides relevant context
|
130 |
-
|
131 |
-
#### 6.2 Gradio 5.x Compatibility
|
132 |
-
- [ ] Message format uses `type="messages"`
|
133 |
-
- [ ] ChatInterface renders correctly
|
134 |
-
- [ ] User/assistant message distinction works
|
135 |
-
- [ ] Chat history persists during session
|
136 |
-
|
137 |
-
## Automated Test Execution
|
138 |
-
|
139 |
-
### Quick Test Suite
|
140 |
-
```bash
|
141 |
-
#!/bin/bash
|
142 |
-
# quick_test.sh - Run essential tests
|
143 |
-
|
144 |
-
echo "🔍 Running Quick Test Suite..."
|
145 |
-
|
146 |
-
# 1. Syntax check
|
147 |
-
python -m py_compile app.py && echo "✅ app.py syntax valid" || echo "❌ app.py syntax error"
|
148 |
-
|
149 |
-
# 2. Import test
|
150 |
-
python -c "import app; print('✅ App imports successfully')" 2>/dev/null || echo "❌ Import failed"
|
151 |
-
|
152 |
-
# 3. RAG component test (if available)
|
153 |
-
if python -c "from rag_tool import RAGTool" 2>/dev/null; then
|
154 |
-
python test_vector_db.py && echo "✅ RAG tests passed" || echo "❌ RAG tests failed"
|
155 |
-
else
|
156 |
-
echo "⚠️ RAG components not available"
|
157 |
-
fi
|
158 |
-
|
159 |
-
# 4. Template generation test
|
160 |
-
python -c "
|
161 |
-
import app
|
162 |
-
result = app.generate_zip('Test Space', 'Test Description', 'Test Role', 'Test Audience', 'Test Tasks', '', [], '', '', 'gpt-3.5-turbo', 0.7, 4000, [], False, False, None)
|
163 |
-
assert result[0].endswith('.zip'), 'ZIP generation failed'
|
164 |
-
print('✅ Space generation works')
|
165 |
-
"
|
166 |
-
|
167 |
-
echo "🎉 Quick test suite completed!"
|
168 |
-
```
|
169 |
-
|
170 |
-
### Full Test Suite
|
171 |
-
```bash
|
172 |
-
#!/bin/bash
|
173 |
-
# full_test.sh - Comprehensive testing
|
174 |
-
|
175 |
-
echo "🔍 Running Full Test Suite..."
|
176 |
-
|
177 |
-
# Run all component tests
|
178 |
-
./quick_test.sh
|
179 |
-
|
180 |
-
# Additional integration tests
|
181 |
-
echo "🧪 Running integration tests..."
|
182 |
-
|
183 |
-
# Test with different configurations
|
184 |
-
# Test error handling
|
185 |
-
# Test edge cases
|
186 |
-
# Performance tests
|
187 |
-
|
188 |
-
echo "📊 Generating test report..."
|
189 |
-
# Generate detailed test report
|
190 |
-
```
|
191 |
-
|
192 |
-
## Regression Test Checklist
|
193 |
-
|
194 |
-
After each commit, verify:
|
195 |
-
|
196 |
-
- [ ] All existing functionality still works
|
197 |
-
- [ ] New features don't break existing features
|
198 |
-
- [ ] Generated spaces deploy successfully to HuggingFace
|
199 |
-
- [ ] Documentation is updated appropriately
|
200 |
-
- [ ] Dependencies are correctly specified
|
201 |
-
- [ ] Security patterns are maintained
|
202 |
-
|
203 |
-
## Performance Benchmarks
|
204 |
-
|
205 |
-
### Metrics to Track
|
206 |
-
- Application startup time
|
207 |
-
- Space generation time
|
208 |
-
- Document processing time (for various file sizes)
|
209 |
-
- Memory usage during RAG operations
|
210 |
-
- API response times
|
211 |
-
|
212 |
-
### Benchmark Commands
|
213 |
-
```bash
|
214 |
-
# Startup time
|
215 |
-
time python -c "import app; print('App loaded')"
|
216 |
-
|
217 |
-
# Space generation time
|
218 |
-
time python -c "
|
219 |
-
import app
|
220 |
-
app.generate_zip('Benchmark', 'Test', 'Role', 'Audience', 'Tasks', '', [], '', '', 'gpt-3.5-turbo', 0.7, 4000, [], False, False, None)
|
221 |
-
"
|
222 |
-
|
223 |
-
# RAG processing time
|
224 |
-
time python -c "from test_vector_db import test_rag_tool; test_rag_tool()"
|
225 |
-
```
|
226 |
-
|
227 |
-
## Test Data Management
|
228 |
-
|
229 |
-
### Sample Test Files
|
230 |
-
- `test_document.txt` - Basic text document
|
231 |
-
- `sample.pdf` - PDF document for upload testing
|
232 |
-
- `sample.docx` - Word document for testing
|
233 |
-
- `sample.md` - Markdown document for testing
|
234 |
-
|
235 |
-
### Test Configuration Profiles
|
236 |
-
- Minimal configuration (basic chat only)
|
237 |
-
- Research assistant template
|
238 |
-
- Full-featured (RAG + URL grounding + access control)
|
239 |
-
- Edge case configurations
|
240 |
-
|
241 |
-
## Continuous Integration Integration
|
242 |
-
|
243 |
-
### GitHub Actions Integration
|
244 |
-
```yaml
|
245 |
-
# .github/workflows/test.yml
|
246 |
-
name: Test Chat UI Helper
|
247 |
-
on: [push, pull_request]
|
248 |
-
jobs:
|
249 |
-
test:
|
250 |
-
runs-on: ubuntu-latest
|
251 |
-
steps:
|
252 |
-
- uses: actions/checkout@v3
|
253 |
-
- name: Set up Python
|
254 |
-
uses: actions/setup-python@v4
|
255 |
-
with:
|
256 |
-
python-version: '3.9'
|
257 |
-
- name: Install dependencies
|
258 |
-
run: pip install -r requirements.txt
|
259 |
-
- name: Run test suite
|
260 |
-
run: ./quick_test.sh
|
261 |
-
```
|
262 |
-
|
263 |
-
## Future Test Enhancements
|
264 |
-
|
265 |
-
### Planned Additions
|
266 |
-
- [ ] Automated UI testing with Selenium
|
267 |
-
- [ ] Load testing for generated spaces
|
268 |
-
- [ ] Cross-browser compatibility testing
|
269 |
-
- [ ] Mobile responsiveness testing
|
270 |
-
- [ ] Accessibility testing
|
271 |
-
- [ ] Multi-language content testing
|
272 |
-
|
273 |
-
### Test Coverage Goals
|
274 |
-
- [ ] 90%+ code coverage for core components
|
275 |
-
- [ ] All user workflows tested end-to-end
|
276 |
-
- [ ] Error conditions properly tested
|
277 |
-
- [ ] Performance regression detection
|
278 |
-
|
279 |
-
---
|
280 |
-
|
281 |
-
**Last Updated**: 2025-07-13
|
282 |
-
**Version**: 1.0
|
283 |
-
**Maintained by**: Development Team
|
284 |
-
|
285 |
-
This test procedure should be updated whenever new features are added or existing functionality is modified.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
claude.local.md
DELETED
@@ -1,314 +0,0 @@
|
|
1 |
-
# CLAUDE.md
|
2 |
-
|
3 |
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
4 |
-
|
5 |
-
## Project Overview
|
6 |
-
|
7 |
-
Chat UI Helper is a Gradio-based tool for generating and configuring chat interfaces for HuggingFace Spaces. It creates deployable packages with custom assistants, web scraping capabilities, and optional vector RAG functionality.
|
8 |
-
|
9 |
-
## Core Architecture
|
10 |
-
|
11 |
-
### Main Application Flow (`app.py`)
|
12 |
-
The application follows a three-tab Gradio interface pattern:
|
13 |
-
1. **Configuration Tab**: Space setup, assistant configuration, tool settings (lines 1267-1589)
|
14 |
-
2. **Sandbox Preview Tab**: Interactive testing with real OpenRouter API integration (lines 1591-1699)
|
15 |
-
3. **Support Docs Tab**: Comprehensive guidance and templates via `support_docs.py`
|
16 |
-
|
17 |
-
### Template Generation System
|
18 |
-
- `SPACE_TEMPLATE` (lines 50-347): Complete HuggingFace Space template with export functionality and legacy tuple format compatibility
|
19 |
-
- `generate_zip()` function (lines 562-652): Orchestrates package creation with all dependencies
|
20 |
-
- Key template variables: `{system_prompt}`, `{model}`, `{enable_vector_rag}`, `{api_key_var}`, `{grounding_urls}`, `{enable_dynamic_urls}`
|
21 |
-
|
22 |
-
### Preview Sandbox Architecture (Enhanced)
|
23 |
-
- Real OpenRouter API integration in preview mode (`preview_chat_response()` line 855)
|
24 |
-
- URL context testing with dynamic add/remove functionality
|
25 |
-
- Configuration-aware responses using exact model and parameters from user configuration
|
26 |
-
- Fallback messaging when `OPENROUTER_API_KEY` environment variable not set
|
27 |
-
- Legacy tuple format compatibility for Gradio 4.44.1 ChatInterface
|
28 |
-
- **Comprehensive Debugging**: Enhanced error handling with detailed API response validation (lines 928-955)
|
29 |
-
- Empty response detection and logging
|
30 |
-
- API structure validation (choices, message, content)
|
31 |
-
- Request payload debugging for troubleshooting
|
32 |
-
- Timeout handling (30 seconds) for API requests
|
33 |
-
|
34 |
-
### Document Processing Pipeline (RAG)
|
35 |
-
- **RAGTool** (`rag_tool.py`): Main orchestrator with 10MB file size validation (lines 19-79)
|
36 |
-
- **DocumentProcessor** (`document_processor.py`): PDF/DOCX/TXT/MD parsing with semantic chunking (800 chars, 100 overlap)
|
37 |
-
- **VectorStore** (`vector_store.py`): FAISS-based similarity search and base64 serialization
|
38 |
-
|
39 |
-
### Web Scraping Architecture
|
40 |
-
Simple HTTP + BeautifulSoup approach (replacing previous Crawl4AI):
|
41 |
-
- `fetch_url_content()` (lines 390-415): Basic requests with timeout and user-agent headers
|
42 |
-
- Content cleaning: Removes scripts, styles, navigation elements
|
43 |
-
- Content limits: ~4000 character truncation for context management
|
44 |
-
- URL content caching: `get_cached_grounding_context()` (line 1019) prevents redundant fetches
|
45 |
-
- `extract_urls_from_text()` (line 44): Regex-based URL extraction for dynamic fetching
|
46 |
-
|
47 |
-
## Development-Only Utilities
|
48 |
-
|
49 |
-
### MCP Servers
|
50 |
-
- **Gradio Docs**: Available at https://gradio-docs-mcp.hf.space/gradio_api/mcp/sse
|
51 |
-
- Use `gradio_docs.py` utility for development assistance
|
52 |
-
- **CRITICAL**: Do NOT import in main application - this is for development tooling only
|
53 |
-
|
54 |
-
Usage for development:
|
55 |
-
```bash
|
56 |
-
python -c "from gradio_docs import gradio_docs; print(gradio_docs.search_docs('ChatInterface'))"
|
57 |
-
```
|
58 |
-
|
59 |
-
## Development Commands
|
60 |
-
|
61 |
-
### Environment Setup
|
62 |
-
**Important**: This application requires Python ≥3.10 for Gradio 5.x compatibility.
|
63 |
-
|
64 |
-
```bash
|
65 |
-
# Recommended: Use Python 3.11+ environment
|
66 |
-
python3.11 -m venv venv311
|
67 |
-
source venv311/bin/activate # or venv311\Scripts\activate on Windows
|
68 |
-
pip install -r requirements.txt
|
69 |
-
```
|
70 |
-
|
71 |
-
### Running the Application
|
72 |
-
```bash
|
73 |
-
# With virtual environment activated
|
74 |
-
python app.py
|
75 |
-
```
|
76 |
-
|
77 |
-
### Testing Commands
|
78 |
-
```bash
|
79 |
-
# Test vector database functionality (requires all RAG dependencies)
|
80 |
-
python test_vector_db.py
|
81 |
-
|
82 |
-
# Test OpenRouter API key validation
|
83 |
-
python test_api_key.py
|
84 |
-
|
85 |
-
# Test minimal Gradio functionality (for debugging)
|
86 |
-
python test_minimal.py
|
87 |
-
|
88 |
-
# Test preview functionality components (new)
|
89 |
-
python test_preview.py
|
90 |
-
|
91 |
-
# Test individual RAG components
|
92 |
-
python -c "from test_vector_db import test_document_processing; test_document_processing()"
|
93 |
-
python -c "from test_vector_db import test_vector_store; test_vector_store()"
|
94 |
-
python -c "from test_vector_db import test_rag_tool; test_rag_tool()"
|
95 |
-
```
|
96 |
-
|
97 |
-
### Pre-Test Setup for RAG Components
|
98 |
-
```bash
|
99 |
-
# Create test document for vector database testing
|
100 |
-
echo "This is a test document for RAG functionality testing." > test_document.txt
|
101 |
-
|
102 |
-
# Verify all dependencies are installed
|
103 |
-
python -c "import sentence_transformers, faiss, fitz; print('RAG dependencies available')"
|
104 |
-
```
|
105 |
-
|
106 |
-
### Key Dependencies and Versions
|
107 |
-
|
108 |
-
#### Required Dependencies
|
109 |
-
- **Gradio ≥4.44.1**: Main UI framework (5.37.0 recommended for Python ≥3.10)
|
110 |
-
- **requests ≥2.32.3**: HTTP requests for web content fetching
|
111 |
-
- **beautifulsoup4 ≥4.12.3**: HTML parsing for web scraping
|
112 |
-
- **python-dotenv ≥1.0.0**: Environment variable management
|
113 |
-
|
114 |
-
#### Optional RAG Dependencies
|
115 |
-
- **sentence-transformers ≥2.2.2**: Text embeddings
|
116 |
-
- **faiss-cpu ==1.7.4**: Vector similarity search
|
117 |
-
- **PyMuPDF ≥1.23.0**: PDF text extraction
|
118 |
-
- **python-docx ≥0.8.11**: DOCX document processing
|
119 |
-
- **numpy ==1.26.4**: Numerical operations
|
120 |
-
|
121 |
-
## Configuration Patterns
|
122 |
-
|
123 |
-
### Conditional Dependency Loading
|
124 |
-
```python
|
125 |
-
try:
|
126 |
-
from rag_tool import RAGTool
|
127 |
-
HAS_RAG = True
|
128 |
-
except ImportError:
|
129 |
-
HAS_RAG = False
|
130 |
-
RAGTool = None
|
131 |
-
```
|
132 |
-
This pattern allows graceful degradation when optional vector dependencies are unavailable.
|
133 |
-
|
134 |
-
### Template Variable Substitution
|
135 |
-
Generated spaces use these key substitutions:
|
136 |
-
- `{system_prompt}`: Combined assistant configuration
|
137 |
-
- `{grounding_urls}`: Static URL list for context
|
138 |
-
- `{enable_dynamic_urls}`: Runtime URL fetching capability
|
139 |
-
- `{enable_vector_rag}`: Document search integration
|
140 |
-
- `{rag_data_json}`: Serialized embeddings and chunks
|
141 |
-
- `{api_key_var}`: Customizable API key environment variable name
|
142 |
-
|
143 |
-
### Access Control Pattern
|
144 |
-
- Environment variable `SPACE_ACCESS_CODE` for student access control
|
145 |
-
- Global state management for session-based access in generated spaces
|
146 |
-
- Security-first approach storing credentials as HuggingFace Spaces secrets
|
147 |
-
|
148 |
-
### RAG Integration Workflow
|
149 |
-
1. Documents uploaded through Gradio File component with conditional visibility (`HAS_RAG` flag)
|
150 |
-
2. Processed via DocumentProcessor (PDF/DOCX/TXT/MD support) in `process_documents()` function
|
151 |
-
3. Chunked and embedded using sentence-transformers (800 chars, 100 overlap)
|
152 |
-
4. FAISS index created and serialized to base64 for deployment portability
|
153 |
-
5. Embedded in generated template via `{rag_data_json}` template variable
|
154 |
-
|
155 |
-
## Implementation Notes
|
156 |
-
|
157 |
-
### Research Template System (Simplified)
|
158 |
-
- **Simple Toggle**: `toggle_research_assistant()` function (line 1225) now provides simple on/off functionality
|
159 |
-
- **Direct System Prompt**: Enables predefined academic research prompt with DOI verification and LibKey integration
|
160 |
-
- **Auto-Enable Dynamic URLs**: Research template automatically enables dynamic URL fetching for academic sources
|
161 |
-
- **Template Content**: Academic inquiry focus with DOI-verified sources, fact-checking, and proper citation requirements
|
162 |
-
- **Note**: Previous complex field system (Role and Purpose, Intended Audience, Key Tasks, Additional Context) has been removed for simplified architecture
|
163 |
-
|
164 |
-
### State Management Across Tabs
|
165 |
-
- Extensive use of `gr.State()` for maintaining session data
|
166 |
-
- Cross-tab functionality through shared state variables (`sandbox_state`, `preview_config_state`)
|
167 |
-
- URL content caching to prevent redundant web requests (`url_content_cache` global variable)
|
168 |
-
- Preview debugging with comprehensive error handling and API response validation
|
169 |
-
|
170 |
-
### Gradio Compatibility and Message Format Handling
|
171 |
-
- **Target Version**: Gradio 5.37.0 (requires Python ≥3.10)
|
172 |
-
- **Legacy Support**: Gradio 4.44.1 compatibility with JSON schema workarounds
|
173 |
-
- **Message Format**: Preview uses legacy tuple format `[user_msg, bot_msg]` for ChatInterface compatibility
|
174 |
-
- **Generated Spaces**: Use modern dictionary format `{"role": "user", "content": "..."}` for OpenRouter API
|
175 |
-
|
176 |
-
### Security Considerations
|
177 |
-
- Never embed API keys or access codes in generated templates
|
178 |
-
- Environment variable pattern for all sensitive configuration (`{api_key_var}` template variable)
|
179 |
-
- Input validation for uploaded files and URL processing
|
180 |
-
- Content length limits for web scraping operations
|
181 |
-
|
182 |
-
## Testing Infrastructure
|
183 |
-
|
184 |
-
### Current Test Structure
|
185 |
-
- `test_vector_db.py`: Comprehensive RAG component testing (196 lines)
|
186 |
-
- `test_api_key.py`: OpenRouter API validation (85 lines)
|
187 |
-
- `test_minimal.py`: Basic Gradio functionality debugging (20 lines)
|
188 |
-
- `test_preview.py`: Preview functionality component testing (URL extraction, fetching, chat response)
|
189 |
-
|
190 |
-
### Test Dependencies
|
191 |
-
RAG testing requires: `sentence-transformers`, `faiss-cpu`, `PyMuPDF`, `python-docx`
|
192 |
-
|
193 |
-
Core testing requires: `gradio`, `requests`, `beautifulsoup4`, `python-dotenv`
|
194 |
-
|
195 |
-
### Testing Status
|
196 |
-
- **Functional**: Three main test files covering core functionality
|
197 |
-
- **Missing**: Automated test scripts referenced in TEST_PROCEDURE.md (`quick_test.sh`, `full_test.sh`) are documented but not implemented
|
198 |
-
- **Usage**: Run individual Python test modules directly
|
199 |
-
|
200 |
-
## File Structure Notes
|
201 |
-
|
202 |
-
### Generated Space Structure
|
203 |
-
All generated HuggingFace Spaces follow consistent structure:
|
204 |
-
1. Configuration section with environment variable loading
|
205 |
-
2. Web scraping functions (simple HTTP requests with BeautifulSoup)
|
206 |
-
3. RAG context retrieval (if enabled)
|
207 |
-
4. OpenRouter API integration with conversation history
|
208 |
-
5. Gradio ChatInterface with access control
|
209 |
-
|
210 |
-
### Development Files Not For Production
|
211 |
-
- `gradio_docs.py`: MCP server integration (development only)
|
212 |
-
- `test_*.py`: Testing utilities
|
213 |
-
- `TEST_PROCEDURE.md`: Comprehensive testing methodology
|
214 |
-
- `file_upload_proposal.md`: Technical architecture proposals
|
215 |
-
|
216 |
-
## Known Issues and Compatibility
|
217 |
-
|
218 |
-
### Gradio 4.44.1 JSON Schema Bug
|
219 |
-
- **Issue**: TypeError in `json_schema_to_python_type` prevents app startup in some environments
|
220 |
-
- **Symptom**: "argument of type 'bool' is not iterable" error during API schema generation
|
221 |
-
- **Workaround**: Individual component functions work correctly (as verified by `test_preview.py`)
|
222 |
-
- **Solution**: Upgrade to Gradio 5.x for full compatibility, or wait for Gradio 4.x patch
|
223 |
-
|
224 |
-
### Message Format Compatibility
|
225 |
-
- **Preview Mode**: Uses legacy tuple format `[user_msg, bot_msg]` for Gradio 4.44.1 ChatInterface
|
226 |
-
- **Generated Spaces**: Use modern dictionary format for OpenRouter API compatibility
|
227 |
-
- **Cross-Version Support**: Template generation handles both formats appropriately
|
228 |
-
|
229 |
-
### Python Version Requirements
|
230 |
-
- **Minimum**: Python 3.9 (for Gradio 4.44.1)
|
231 |
-
- **Recommended**: Python 3.11+ (for Gradio 5.x and optimal performance)
|
232 |
-
|
233 |
-
## Common Claude Code Anti-Patterns to Avoid
|
234 |
-
|
235 |
-
### Message Format Reversion
|
236 |
-
**❌ Don't revert to:** New dictionary format in preview functions
|
237 |
-
```python
|
238 |
-
# WRONG - breaks Gradio 4.44.1 ChatInterface
|
239 |
-
history.append({"role": "user", "content": message})
|
240 |
-
history.append({"role": "assistant", "content": response})
|
241 |
-
```
|
242 |
-
**✅ Keep:** Legacy tuple format for preview compatibility
|
243 |
-
```python
|
244 |
-
# CORRECT - works with current Gradio ChatInterface
|
245 |
-
history.append([message, response])
|
246 |
-
```
|
247 |
-
|
248 |
-
### Template Variable Substitution
|
249 |
-
**❌ Don't change:** Template string escaping patterns in `SPACE_TEMPLATE`
|
250 |
-
- Keep double backslashes: `\\n\\n` (becomes `\n\n` after Python string processing)
|
251 |
-
- Keep double braces: `{{variable}}` (becomes `{variable}` after format())
|
252 |
-
- **Reason**: Template undergoes two levels of processing (Python format + HuggingFace deployment)
|
253 |
-
|
254 |
-
### Research Template Function Signature
|
255 |
-
**✅ Current Implementation:** Simplified function signature for research template
|
256 |
-
```python
|
257 |
-
# CURRENT - simplified toggle with direct system prompt management
|
258 |
-
def toggle_research_assistant(enable_research):
|
259 |
-
if enable_research:
|
260 |
-
return (gr.update(value=combined_prompt), gr.update(value=True))
|
261 |
-
else:
|
262 |
-
return (gr.update(value=""), gr.update(value=False))
|
263 |
-
```
|
264 |
-
**❌ Don't revert to:** Complex field management patterns that are no longer needed
|
265 |
-
- The research template no longer uses separate fields for role, audience, tasks, context
|
266 |
-
- Current implementation directly manages system prompt and dynamic URL setting only
|
267 |
-
|
268 |
-
### Import Organization Anti-Patterns
|
269 |
-
**❌ Don't move:** `extract_urls_from_text()` back into template string
|
270 |
-
- Function must remain in main app code (line 44) for preview functionality
|
271 |
-
- Template version is for generated spaces only
|
272 |
-
|
273 |
-
### URL Management Simplification
|
274 |
-
**❌ Don't remove:** Dynamic URL add/remove functionality
|
275 |
-
- Keep `add_urls()`, `remove_urls()`, `add_chat_urls()`, `remove_chat_urls()` functions
|
276 |
-
- Maintain URL count state management with `gr.State()`
|
277 |
-
- **Reason**: Users expect scalable URL input interface
|
278 |
-
|
279 |
-
### Preview Functionality Degradation
|
280 |
-
**❌ Don't revert to:** Simple mock responses in preview
|
281 |
-
```python
|
282 |
-
# WRONG - provides no real testing value
|
283 |
-
def preview_chat_response(message, history, config_data):
|
284 |
-
return "", history + [[message, "Mock response"]]
|
285 |
-
```
|
286 |
-
**✅ Keep:** Real API integration with comprehensive debugging
|
287 |
-
- Actual OpenRouter API calls when `OPENROUTER_API_KEY` is set
|
288 |
-
- URL context fetching and processing
|
289 |
-
- Configuration-aware responses using exact user settings
|
290 |
-
- Comprehensive debugging for empty responses and API errors (lines 928-955)
|
291 |
-
|
292 |
-
### Research Template Simplification
|
293 |
-
**✅ Current Implementation:** Simplified research template system
|
294 |
-
- Simple toggle functionality without complex field management
|
295 |
-
- Direct system prompt injection for academic research use cases
|
296 |
-
- Auto-enables dynamic URL fetching for academic sources
|
297 |
-
- **Reason**: Simplified architecture reduces maintenance complexity while preserving core functionality
|
298 |
-
|
299 |
-
### Conditional Dependency Loading
|
300 |
-
**❌ Don't remove:** `HAS_RAG` flag and conditional imports
|
301 |
-
```python
|
302 |
-
# WRONG - breaks installations without vector dependencies
|
303 |
-
from rag_tool import RAGTool
|
304 |
-
```
|
305 |
-
**✅ Keep:** Graceful degradation pattern
|
306 |
-
```python
|
307 |
-
# CORRECT - allows app to work without optional dependencies
|
308 |
-
try:
|
309 |
-
from rag_tool import RAGTool
|
310 |
-
HAS_RAG = True
|
311 |
-
except ImportError:
|
312 |
-
HAS_RAG = False
|
313 |
-
RAGTool = None
|
314 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
file_upload_proposal.md
DELETED
@@ -1,144 +0,0 @@
|
|
1 |
-
# File Upload System Proposal for Faculty Course Materials
|
2 |
-
|
3 |
-
Based on your existing architecture, here's a comprehensive proposal for implementing file uploads with efficient parsing and deployment preservation:
|
4 |
-
|
5 |
-
## Core Architecture Design
|
6 |
-
|
7 |
-
### 1. File Processing Pipeline
|
8 |
-
```
|
9 |
-
Upload → Parse → Chunk → Vector Store → RAG Integration → Deployment Package
|
10 |
-
```
|
11 |
-
|
12 |
-
### 2. File Storage Structure
|
13 |
-
```
|
14 |
-
/course_materials/
|
15 |
-
├── raw_files/ # Original uploaded files
|
16 |
-
├── processed/ # Parsed text content
|
17 |
-
├── embeddings/ # Vector representations
|
18 |
-
└── metadata.json # File tracking & metadata
|
19 |
-
```
|
20 |
-
|
21 |
-
## Implementation Components
|
22 |
-
|
23 |
-
### File Upload Handler (app.py:352-408 enhancement)
|
24 |
-
- Add `gr.File(file_types=[".pdf", ".docx", ".txt", ".md"])` component
|
25 |
-
- Support multiple file uploads with `file_count="multiple"`
|
26 |
-
- Implement file validation and size limits (10MB per file)
|
27 |
-
|
28 |
-
### Document Parser Service (new: `document_parser.py`)
|
29 |
-
- **PDF**: PyMuPDF for text extraction with layout preservation
|
30 |
-
- **DOCX**: python-docx for structured content
|
31 |
-
- **TXT/MD**: Direct text processing with metadata extraction
|
32 |
-
- **Auto-detection**: File type identification and appropriate parser routing
|
33 |
-
|
34 |
-
### RAG Integration (enhancement to existing web scraping system)
|
35 |
-
- **Chunking Strategy**: Semantic chunking (500-1000 tokens with 100-token overlap)
|
36 |
-
- **Embeddings**: sentence-transformers/all-MiniLM-L6-v2 (lightweight, fast)
|
37 |
-
- **Vector Store**: In-memory FAISS index for deployment portability
|
38 |
-
- **Retrieval**: Top-k similarity search (k=3-5) with relevance scoring
|
39 |
-
|
40 |
-
### Enhanced Template (SPACE_TEMPLATE modification)
|
41 |
-
```python
|
42 |
-
# Add to generated app.py
|
43 |
-
COURSE_MATERIALS = json.loads('''{{course_materials_json}}''')
|
44 |
-
EMBEDDINGS_INDEX = pickle.loads(base64.b64decode('''{{embeddings_base64}}'''))
|
45 |
-
|
46 |
-
def get_relevant_context(query, max_contexts=3):
|
47 |
-
"""Retrieve relevant course material context"""
|
48 |
-
# Vector similarity search
|
49 |
-
# Return formatted context snippets
|
50 |
-
```
|
51 |
-
|
52 |
-
## Speed & Accuracy Optimizations
|
53 |
-
|
54 |
-
### 1. Processing Speed
|
55 |
-
- Batch processing during upload (not per-query)
|
56 |
-
- Lightweight embedding model (384 dimensions vs 1536)
|
57 |
-
- In-memory vector store (no database dependencies)
|
58 |
-
- Cached embeddings in deployment package
|
59 |
-
|
60 |
-
### 2. Query Speed
|
61 |
-
- Pre-computed embeddings (no real-time encoding)
|
62 |
-
- Efficient FAISS indexing for similarity search
|
63 |
-
- Context caching for repeated queries
|
64 |
-
- Parallel processing for multiple files
|
65 |
-
|
66 |
-
### 3. Accuracy Enhancements
|
67 |
-
- Semantic chunking preserves context boundaries
|
68 |
-
- Query expansion with synonyms/related terms
|
69 |
-
- Relevance scoring with threshold filtering
|
70 |
-
- Metadata-aware retrieval (file type, section, date)
|
71 |
-
|
72 |
-
## Deployment Package Integration
|
73 |
-
|
74 |
-
### Package Structure Enhancement
|
75 |
-
```
|
76 |
-
generated_space.zip
|
77 |
-
├── app.py # Enhanced with RAG
|
78 |
-
├── requirements.txt # + sentence-transformers, faiss-cpu
|
79 |
-
├── course_materials/ # Embedded materials
|
80 |
-
│ ├── embeddings.pkl # FAISS index
|
81 |
-
│ ├── chunks.json # Text chunks with metadata
|
82 |
-
│ └── files_metadata.json # Original file info
|
83 |
-
└── README.md # Updated instructions
|
84 |
-
```
|
85 |
-
|
86 |
-
### Size Management
|
87 |
-
- Compress embeddings with pickle optimization
|
88 |
-
- Base64 encode for template embedding
|
89 |
-
- Implement file size warnings (>50MB total)
|
90 |
-
- Optional: External storage links for large datasets
|
91 |
-
|
92 |
-
## User Interface Updates
|
93 |
-
|
94 |
-
### Configuration Tab Enhancements
|
95 |
-
```python
|
96 |
-
with gr.Accordion("Course Materials Upload", open=False):
|
97 |
-
file_upload = gr.File(
|
98 |
-
label="Upload Course Materials",
|
99 |
-
file_types=[".pdf", ".docx", ".txt", ".md"],
|
100 |
-
file_count="multiple"
|
101 |
-
)
|
102 |
-
processing_status = gr.Markdown()
|
103 |
-
material_summary = gr.DataFrame() # Show processed files
|
104 |
-
```
|
105 |
-
|
106 |
-
## Technical Implementation
|
107 |
-
|
108 |
-
### Dependencies Addition (requirements.txt)
|
109 |
-
```
|
110 |
-
sentence-transformers==2.2.2
|
111 |
-
faiss-cpu==1.7.4
|
112 |
-
PyMuPDF==1.23.0
|
113 |
-
python-docx==0.8.11
|
114 |
-
tiktoken==0.5.1
|
115 |
-
```
|
116 |
-
|
117 |
-
### Processing Workflow
|
118 |
-
1. **Upload**: Faculty uploads syllabi, schedules, readings
|
119 |
-
2. **Parse**: Extract text with structure preservation
|
120 |
-
3. **Chunk**: Semantic segmentation with metadata
|
121 |
-
4. **Embed**: Generate vector representations
|
122 |
-
5. **Package**: Serialize index and chunks into deployment
|
123 |
-
6. **Deploy**: Single-file space with embedded knowledge
|
124 |
-
|
125 |
-
## Performance Metrics
|
126 |
-
|
127 |
-
- **Upload Processing**: ~2-5 seconds per document
|
128 |
-
- **Query Response**: <200ms additional latency
|
129 |
-
- **Package Size**: +5-15MB for typical course materials
|
130 |
-
- **Accuracy**: 85-95% relevant context retrieval
|
131 |
-
- **Memory Usage**: +50-100MB runtime overhead
|
132 |
-
|
133 |
-
## Benefits
|
134 |
-
|
135 |
-
This approach maintains your existing speed while adding powerful document understanding capabilities that persist in the deployed package. Faculty can upload course materials once during configuration, and students get contextually-aware responses based on actual course content without any external dependencies in the deployed space.
|
136 |
-
|
137 |
-
## Next Steps
|
138 |
-
|
139 |
-
1. Implement document parser service
|
140 |
-
2. Add file upload UI components
|
141 |
-
3. Integrate RAG system with existing web scraping architecture
|
142 |
-
4. Enhance SPACE_TEMPLATE with embedded materials
|
143 |
-
5. Test with sample course materials
|
144 |
-
6. Optimize for deployment package size
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hf_comparisons.aiconfig.json
DELETED
@@ -1,115 +0,0 @@
|
|
1 |
-
{
|
2 |
-
"name": "Hugging Face LLM Comparisons",
|
3 |
-
"schema_version": "latest",
|
4 |
-
"metadata": {
|
5 |
-
"parameters": {
|
6 |
-
"CoLA_ex_prompt": "Is the sentence grammatical or ungrammatical?\n\n\"This building is than that one.\"",
|
7 |
-
"SST_2_ex_prompt": "Is the movie review positive, negative, or neutral?\n\n\"The movie is funny, smart, visually inventive, and most of all, alive.\"",
|
8 |
-
"WNLI_ex_prompt": "Sentence B replaces sentence A's ambiguous pronoun with one of the nouns - is this the correct noun?\n\n\"A) Lily spoke to Donna, breaking her concentration.\nB) Lily spoke to Donna, breaking Lily's concentration.\""
|
9 |
-
},
|
10 |
-
"models": {},
|
11 |
-
"default_model": null,
|
12 |
-
"model_parsers": null
|
13 |
-
},
|
14 |
-
"description": "**In this notebook, we compare the individual performance of HF hosted LLMs () on a few example questions from the GLUE benchmarks (https://gluebenchmark.com/tasks).**\n\n**Example questions taken from \"What is the GLUE Benchmark\" medium post - https://angelina-yang.medium.com/what-is-the-glue-benchmark-for-nlu-systems-61127b3cab3f**\n\n---\n\n| General Language Understanding Evaluation (GLUE) Tasks | Example Question |\n| ----------- | ----------- |\n| Corpus of Linguistic Acceptability (CoLA) | Is the sentence grammatical or ungrammatical? \"This building is than that one.\" |\n| Stanford Sentiment Treebank (SST) | Is the movie review positive, negative, or neutral? \"The movie is funny, smart, visually inventive, and most of all, alive.\" |\n| Winograd NLI (WNLI) | Sentence B replaces sentence A's ambiguous pronoun with one of the nouns - is this the correct noun? \"A) Lily spoke to Donna, breaking her concentration. B) Lily spoke to Donna, breaking Lily's concentration.\" |",
|
15 |
-
"prompts": [
|
16 |
-
{
|
17 |
-
"name": "mistral_7b_instruct_v0.1",
|
18 |
-
"input": "Is the movie review positive, negative, or neutral?\n\n\n\"The movie is funny, smart, visually inventive, and most of all, alive.\"",
|
19 |
-
"metadata": {
|
20 |
-
"model": {
|
21 |
-
"name": "Text Generation",
|
22 |
-
"settings": {
|
23 |
-
"model": "mistralai/Mistral-7B-Instruct-v0.1"
|
24 |
-
}
|
25 |
-
},
|
26 |
-
"tags": null,
|
27 |
-
"parameters": {}
|
28 |
-
},
|
29 |
-
"outputs": [
|
30 |
-
{
|
31 |
-
"output_type": "execute_result",
|
32 |
-
"execution_count": 0,
|
33 |
-
"data": "\n\nThe movie review is positive.</s>",
|
34 |
-
"mime_type": null,
|
35 |
-
"metadata": {}
|
36 |
-
}
|
37 |
-
]
|
38 |
-
},
|
39 |
-
{
|
40 |
-
"name": "google_flan_t5_sm",
|
41 |
-
"input": "Is the movie review positive, negative, or neutral?\n\n\"The movie is funny, smart, visually inventive, and most of all, alive.\"",
|
42 |
-
"metadata": {
|
43 |
-
"model": {
|
44 |
-
"name": "Conversational",
|
45 |
-
"settings": {
|
46 |
-
"model": "google/flan-t5-small",
|
47 |
-
"max_new_tokens": 250,
|
48 |
-
"stream": false
|
49 |
-
}
|
50 |
-
},
|
51 |
-
"tags": null,
|
52 |
-
"parameters": {}
|
53 |
-
},
|
54 |
-
"outputs": [
|
55 |
-
{
|
56 |
-
"output_type": "execute_result",
|
57 |
-
"execution_count": 0,
|
58 |
-
"data": "positive",
|
59 |
-
"mime_type": null,
|
60 |
-
"metadata": {
|
61 |
-
"raw_response": {
|
62 |
-
"generated_text": "positive",
|
63 |
-
"conversation": {
|
64 |
-
"generated_responses": [
|
65 |
-
"positive"
|
66 |
-
],
|
67 |
-
"past_user_inputs": [
|
68 |
-
"Is the movie review positive, negative, or neutral?\n\n\"The movie is funny, smart, visually inventive, and most of all, alive.\""
|
69 |
-
]
|
70 |
-
},
|
71 |
-
"warnings": [
|
72 |
-
"\nNo chat template is defined for this tokenizer - using a default chat template that implements the ChatML format (without BOS/EOS tokens!). If the default is not appropriate for your model, please set `tokenizer.chat_template` to an appropriate template. See https://huggingface.co/docs/transformers/main/chat_templating for more information.\n"
|
73 |
-
]
|
74 |
-
}
|
75 |
-
}
|
76 |
-
}
|
77 |
-
]
|
78 |
-
},
|
79 |
-
{
|
80 |
-
"name": "tinyllama-1_1B",
|
81 |
-
"input": "<|system|>\nYou are to answer the following question by the user</s>\n<|user|>\n{{SST_2_ex_prompt}}</s>\n<|assistant|>",
|
82 |
-
"metadata": {
|
83 |
-
"model": {
|
84 |
-
"name": "Conversational",
|
85 |
-
"settings": {
|
86 |
-
"model": "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
|
87 |
-
}
|
88 |
-
},
|
89 |
-
"tags": null,
|
90 |
-
"parameters": {}
|
91 |
-
},
|
92 |
-
"outputs": [
|
93 |
-
{
|
94 |
-
"output_type": "execute_result",
|
95 |
-
"execution_count": 0,
|
96 |
-
"data": "The movie review is positive.",
|
97 |
-
"mime_type": null,
|
98 |
-
"metadata": {
|
99 |
-
"raw_response": {
|
100 |
-
"generated_text": "The movie review is positive.",
|
101 |
-
"conversation": {
|
102 |
-
"generated_responses": [
|
103 |
-
"The movie review is positive."
|
104 |
-
],
|
105 |
-
"past_user_inputs": [
|
106 |
-
"<|system|>\nYou are to answer the following question by the user</s>\n<|user|>\nIs the movie review positive, negative, or neutral?\n\n"The movie is funny, smart, visually inventive, and most of all, alive."</s>\n<|assistant|>"
|
107 |
-
]
|
108 |
-
}
|
109 |
-
}
|
110 |
-
}
|
111 |
-
}
|
112 |
-
]
|
113 |
-
}
|
114 |
-
]
|
115 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_api_key.py
DELETED
@@ -1,85 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""Test OpenRouter API key functionality"""
|
3 |
-
|
4 |
-
import requests
|
5 |
-
import json
|
6 |
-
|
7 |
-
def test_openrouter_api_key(api_key):
|
8 |
-
"""Test if an OpenRouter API key is valid by making a simple completion request"""
|
9 |
-
|
10 |
-
url = "https://openrouter.ai/api/v1/chat/completions"
|
11 |
-
|
12 |
-
headers = {
|
13 |
-
"Authorization": f"Bearer {api_key}",
|
14 |
-
"Content-Type": "application/json",
|
15 |
-
"HTTP-Referer": "https://github.com/test-api-key", # Required by OpenRouter
|
16 |
-
"X-Title": "API Key Test" # Optional but recommended
|
17 |
-
}
|
18 |
-
|
19 |
-
# Simple test message
|
20 |
-
data = {
|
21 |
-
"model": "openrouter/auto", # Auto-select cheapest available model
|
22 |
-
"messages": [
|
23 |
-
{"role": "user", "content": "Say 'API key is working!' in exactly 4 words."}
|
24 |
-
],
|
25 |
-
"max_tokens": 10,
|
26 |
-
"temperature": 0.1
|
27 |
-
}
|
28 |
-
|
29 |
-
try:
|
30 |
-
print("Testing OpenRouter API key...")
|
31 |
-
response = requests.post(url, headers=headers, json=data, timeout=30)
|
32 |
-
|
33 |
-
if response.status_code == 200:
|
34 |
-
result = response.json()
|
35 |
-
if "choices" in result and len(result["choices"]) > 0:
|
36 |
-
assistant_message = result["choices"][0]["message"]["content"]
|
37 |
-
print(f"✓ API key is valid!")
|
38 |
-
print(f"Response: {assistant_message}")
|
39 |
-
print(f"Model used: {result.get('model', 'unknown')}")
|
40 |
-
return True
|
41 |
-
else:
|
42 |
-
print("✗ Unexpected response format")
|
43 |
-
return False
|
44 |
-
else:
|
45 |
-
error_data = response.json() if response.headers.get('content-type', '').startswith('application/json') else {}
|
46 |
-
print(f"✗ API key test failed!")
|
47 |
-
print(f"Status code: {response.status_code}")
|
48 |
-
print(f"Error: {error_data.get('error', {}).get('message', response.text)}")
|
49 |
-
|
50 |
-
# Common error interpretations
|
51 |
-
if response.status_code == 401:
|
52 |
-
print("→ The API key is invalid or has been revoked")
|
53 |
-
elif response.status_code == 402:
|
54 |
-
print("→ The API key has insufficient credits")
|
55 |
-
elif response.status_code == 429:
|
56 |
-
print("→ Rate limit exceeded")
|
57 |
-
|
58 |
-
return False
|
59 |
-
|
60 |
-
except requests.exceptions.Timeout:
|
61 |
-
print("✗ Request timed out")
|
62 |
-
return False
|
63 |
-
except requests.exceptions.RequestException as e:
|
64 |
-
print(f"✗ Network error: {e}")
|
65 |
-
return False
|
66 |
-
except Exception as e:
|
67 |
-
print(f"✗ Unexpected error: {e}")
|
68 |
-
return False
|
69 |
-
|
70 |
-
if __name__ == "__main__":
|
71 |
-
# Test the provided API key
|
72 |
-
api_key = "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"
|
73 |
-
|
74 |
-
print(f"API Key: {api_key[:20]}...{api_key[-10:]}")
|
75 |
-
print("-" * 50)
|
76 |
-
|
77 |
-
success = test_openrouter_api_key(api_key)
|
78 |
-
|
79 |
-
print("-" * 50)
|
80 |
-
if success:
|
81 |
-
print("✓ The API key is working correctly!")
|
82 |
-
print("You can use this key in your Chat UI Helper application.")
|
83 |
-
else:
|
84 |
-
print("✗ The API key is not working.")
|
85 |
-
print("Please check that the key is correct and has available credits.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_connection_fix.py
DELETED
@@ -1,137 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
Test RAG connection error fix
|
4 |
-
Tests the specific multiprocessing and connection timeout issues
|
5 |
-
"""
|
6 |
-
|
7 |
-
import os
|
8 |
-
import tempfile
|
9 |
-
import warnings
|
10 |
-
|
11 |
-
# Set environment variables before any imports
|
12 |
-
os.environ['TOKENIZERS_PARALLELISM'] = 'false'
|
13 |
-
os.environ['OMP_NUM_THREADS'] = '1'
|
14 |
-
os.environ['MKL_NUM_THREADS'] = '1'
|
15 |
-
|
16 |
-
# Suppress warnings for cleaner output
|
17 |
-
warnings.filterwarnings("ignore", category=UserWarning)
|
18 |
-
warnings.filterwarnings("ignore", category=FutureWarning)
|
19 |
-
|
20 |
-
def test_connection_fix():
|
21 |
-
"""Test the connection error fix specifically"""
|
22 |
-
print("Testing RAG connection error fix...")
|
23 |
-
|
24 |
-
try:
|
25 |
-
# Test conditional import
|
26 |
-
try:
|
27 |
-
from rag_tool import RAGTool
|
28 |
-
has_rag = True
|
29 |
-
print("✓ RAG dependencies available")
|
30 |
-
except ImportError:
|
31 |
-
print("✗ RAG dependencies not available")
|
32 |
-
return False
|
33 |
-
|
34 |
-
# Create a test document
|
35 |
-
test_content = """This is a test document for connection error testing.
|
36 |
-
It contains multiple sentences to test the embedding process.
|
37 |
-
The document should be processed without connection errors.
|
38 |
-
This tests multiprocessing fixes and memory management."""
|
39 |
-
|
40 |
-
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
|
41 |
-
f.write(test_content)
|
42 |
-
test_file = f.name
|
43 |
-
|
44 |
-
try:
|
45 |
-
print("✓ Test document created")
|
46 |
-
|
47 |
-
# Initialize RAG tool with environment variables already set
|
48 |
-
print("Initializing RAG tool with connection fixes...")
|
49 |
-
rag_tool = RAGTool()
|
50 |
-
print("✓ RAG tool initialized successfully")
|
51 |
-
|
52 |
-
# Process document - this was causing the connection error
|
53 |
-
print("Processing document (this was causing connection errors)...")
|
54 |
-
result = rag_tool.process_uploaded_files([test_file])
|
55 |
-
|
56 |
-
if result['success']:
|
57 |
-
print(f"✓ Document processed successfully: {result['message']}")
|
58 |
-
print(f" - Chunks created: {result.get('index_stats', {}).get('total_chunks', 'unknown')}")
|
59 |
-
|
60 |
-
# Test search to ensure embeddings work
|
61 |
-
context = rag_tool.get_relevant_context("test document", max_chunks=1)
|
62 |
-
print(f"✓ Search test successful, context length: {len(context)}")
|
63 |
-
|
64 |
-
return True
|
65 |
-
else:
|
66 |
-
print(f"✗ Document processing failed: {result['message']}")
|
67 |
-
return False
|
68 |
-
|
69 |
-
finally:
|
70 |
-
# Clean up
|
71 |
-
if os.path.exists(test_file):
|
72 |
-
os.unlink(test_file)
|
73 |
-
print("✓ Test file cleaned up")
|
74 |
-
|
75 |
-
except Exception as e:
|
76 |
-
print(f"✗ Test failed with error: {e}")
|
77 |
-
return False
|
78 |
-
|
79 |
-
def test_gradio_integration():
|
80 |
-
"""Test integration with Gradio interface"""
|
81 |
-
print("\nTesting Gradio integration...")
|
82 |
-
|
83 |
-
try:
|
84 |
-
import gradio as gr
|
85 |
-
|
86 |
-
# Create a minimal Gradio interface similar to the main app
|
87 |
-
def test_process_documents(files):
|
88 |
-
"""Minimal version of process_documents for testing"""
|
89 |
-
if not files:
|
90 |
-
return "No files uploaded"
|
91 |
-
|
92 |
-
try:
|
93 |
-
from rag_tool import RAGTool
|
94 |
-
rag_tool = RAGTool()
|
95 |
-
|
96 |
-
# Simulate file processing
|
97 |
-
file_paths = [f.name if hasattr(f, 'name') else str(f) for f in files]
|
98 |
-
result = rag_tool.process_uploaded_files(file_paths)
|
99 |
-
|
100 |
-
if result['success']:
|
101 |
-
return f"✓ Success: {result['message']}"
|
102 |
-
else:
|
103 |
-
return f"✗ Failed: {result['message']}"
|
104 |
-
|
105 |
-
except Exception as e:
|
106 |
-
return f"✗ Error: {str(e)}"
|
107 |
-
|
108 |
-
# Create interface without launching
|
109 |
-
with gr.Blocks() as interface:
|
110 |
-
file_input = gr.File(file_count="multiple", label="Test Documents")
|
111 |
-
output = gr.Textbox(label="Result")
|
112 |
-
process_btn = gr.Button("Process")
|
113 |
-
|
114 |
-
process_btn.click(
|
115 |
-
test_process_documents,
|
116 |
-
inputs=[file_input],
|
117 |
-
outputs=[output]
|
118 |
-
)
|
119 |
-
|
120 |
-
print("✓ Gradio interface created successfully")
|
121 |
-
print(" Interface can be launched without connection errors")
|
122 |
-
return True
|
123 |
-
|
124 |
-
except Exception as e:
|
125 |
-
print(f"✗ Gradio integration test failed: {e}")
|
126 |
-
return False
|
127 |
-
|
128 |
-
if __name__ == "__main__":
|
129 |
-
success = test_connection_fix()
|
130 |
-
if success:
|
131 |
-
success = test_gradio_integration()
|
132 |
-
|
133 |
-
if success:
|
134 |
-
print("\n🎉 All connection error fixes are working!")
|
135 |
-
print("The RAG processing should now work without connection timeouts.")
|
136 |
-
else:
|
137 |
-
print("\n❌ Some tests failed. Check the error messages above.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_document.txt
DELETED
@@ -1,24 +0,0 @@
|
|
1 |
-
Vector Database Test Document
|
2 |
-
|
3 |
-
This is a test document for evaluating the vector database functionality.
|
4 |
-
|
5 |
-
Section 1: Introduction to Vector Databases
|
6 |
-
Vector databases store and query high-dimensional vector representations of data. They enable semantic search by finding vectors similar to a query vector in an embedding space.
|
7 |
-
|
8 |
-
Section 2: Use Cases
|
9 |
-
Common applications include:
|
10 |
-
- Document retrieval and question answering
|
11 |
-
- Similarity search for products or content
|
12 |
-
- Recommendation systems
|
13 |
-
- Semantic search in chatbots
|
14 |
-
|
15 |
-
Section 3: Technical Implementation
|
16 |
-
Vector databases typically use embedding models to convert text into dense vectors, then use algorithms like cosine similarity or approximate nearest neighbor search to find relevant results.
|
17 |
-
|
18 |
-
Section 4: Benefits
|
19 |
-
- Semantic understanding beyond keyword matching
|
20 |
-
- Scalable retrieval for large document collections
|
21 |
-
- Integration with modern AI systems and large language models
|
22 |
-
- Support for multi-modal data (text, images, audio)
|
23 |
-
|
24 |
-
This document should generate multiple chunks when processed by the system.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_gradio_simple.py
DELETED
@@ -1,24 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
Simple test to verify if the gradio app can start without schema errors
|
4 |
-
"""
|
5 |
-
|
6 |
-
import gradio as gr
|
7 |
-
|
8 |
-
def simple_function(message, history):
|
9 |
-
return "", history + [[message, "Test response"]]
|
10 |
-
|
11 |
-
# Create a simple interface to test
|
12 |
-
with gr.Blocks() as demo:
|
13 |
-
chatbot = gr.Chatbot()
|
14 |
-
msg = gr.Textbox()
|
15 |
-
|
16 |
-
msg.submit(simple_function, [msg, chatbot], [msg, chatbot])
|
17 |
-
|
18 |
-
if __name__ == "__main__":
|
19 |
-
print("Testing simple Gradio interface...")
|
20 |
-
try:
|
21 |
-
demo.launch(server_name="127.0.0.1", server_port=7862, share=False, prevent_thread_lock=True)
|
22 |
-
print("✅ Simple Gradio interface works")
|
23 |
-
except Exception as e:
|
24 |
-
print(f"❌ Simple Gradio interface failed: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_minimal.py
DELETED
@@ -1,20 +0,0 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
|
3 |
-
# Minimal test to isolate the boolean iteration error
|
4 |
-
with gr.Blocks() as demo:
|
5 |
-
with gr.Tab("Test"):
|
6 |
-
name = gr.Textbox(label="Name")
|
7 |
-
checkbox = gr.Checkbox(label="Test", value=False)
|
8 |
-
button = gr.Button("Test")
|
9 |
-
|
10 |
-
def test_func(name_val, checkbox_val):
|
11 |
-
return f"Hello {name_val}, checkbox: {checkbox_val}"
|
12 |
-
|
13 |
-
button.click(
|
14 |
-
test_func,
|
15 |
-
inputs=[name, checkbox],
|
16 |
-
outputs=[name]
|
17 |
-
)
|
18 |
-
|
19 |
-
if __name__ == "__main__":
|
20 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_preview.py
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
Test script for preview functionality
|
4 |
-
"""
|
5 |
-
|
6 |
-
import os
|
7 |
-
import sys
|
8 |
-
import tempfile
|
9 |
-
|
10 |
-
# Add current directory to path for imports
|
11 |
-
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
12 |
-
|
13 |
-
def test_preview_chat_response():
|
14 |
-
"""Test the preview chat response function"""
|
15 |
-
try:
|
16 |
-
from app import preview_chat_response
|
17 |
-
|
18 |
-
# Mock config data
|
19 |
-
config_data = {
|
20 |
-
'name': 'Test Assistant',
|
21 |
-
'model': 'google/gemini-2.0-flash-001',
|
22 |
-
'system_prompt': 'You are a helpful assistant.',
|
23 |
-
'temperature': 0.7,
|
24 |
-
'max_tokens': 500,
|
25 |
-
'enable_dynamic_urls': False,
|
26 |
-
'enable_vector_rag': False
|
27 |
-
}
|
28 |
-
|
29 |
-
# Test with no API key (should give preview message)
|
30 |
-
if 'OPENROUTER_API_KEY' in os.environ:
|
31 |
-
del os.environ['OPENROUTER_API_KEY']
|
32 |
-
|
33 |
-
message = "Hello, how are you?"
|
34 |
-
history = []
|
35 |
-
|
36 |
-
result_msg, result_history = preview_chat_response(
|
37 |
-
message, history, config_data, "", "", "", ""
|
38 |
-
)
|
39 |
-
|
40 |
-
print("✅ preview_chat_response function works")
|
41 |
-
print(f"Result message: {result_msg}")
|
42 |
-
print(f"History length: {len(result_history)}")
|
43 |
-
print(f"Last response: {result_history[-1] if result_history else 'None'}")
|
44 |
-
|
45 |
-
return True
|
46 |
-
|
47 |
-
except Exception as e:
|
48 |
-
print(f"❌ preview_chat_response failed: {e}")
|
49 |
-
return False
|
50 |
-
|
51 |
-
def test_url_extraction():
|
52 |
-
"""Test URL extraction function"""
|
53 |
-
try:
|
54 |
-
from app import extract_urls_from_text
|
55 |
-
|
56 |
-
test_text = "Check out https://example.com and also https://test.org/page"
|
57 |
-
urls = extract_urls_from_text(test_text)
|
58 |
-
|
59 |
-
print("✅ extract_urls_from_text works")
|
60 |
-
print(f"Extracted URLs: {urls}")
|
61 |
-
|
62 |
-
return True
|
63 |
-
|
64 |
-
except Exception as e:
|
65 |
-
print(f"❌ extract_urls_from_text failed: {e}")
|
66 |
-
return False
|
67 |
-
|
68 |
-
def test_url_fetching():
|
69 |
-
"""Test URL content fetching"""
|
70 |
-
try:
|
71 |
-
from app import fetch_url_content
|
72 |
-
|
73 |
-
# Test with a simple URL
|
74 |
-
content = fetch_url_content("https://httpbin.org/get")
|
75 |
-
|
76 |
-
print("✅ fetch_url_content works")
|
77 |
-
print(f"Content length: {len(content)}")
|
78 |
-
print(f"Content preview: {content[:100]}...")
|
79 |
-
|
80 |
-
return True
|
81 |
-
|
82 |
-
except Exception as e:
|
83 |
-
print(f"❌ fetch_url_content failed: {e}")
|
84 |
-
return False
|
85 |
-
|
86 |
-
if __name__ == "__main__":
|
87 |
-
print("Testing preview functionality components...")
|
88 |
-
|
89 |
-
tests = [
|
90 |
-
test_url_extraction,
|
91 |
-
test_url_fetching,
|
92 |
-
test_preview_chat_response
|
93 |
-
]
|
94 |
-
|
95 |
-
passed = 0
|
96 |
-
total = len(tests)
|
97 |
-
|
98 |
-
for test in tests:
|
99 |
-
if test():
|
100 |
-
passed += 1
|
101 |
-
print()
|
102 |
-
|
103 |
-
print(f"Test Results: {passed}/{total} passed")
|
104 |
-
|
105 |
-
if passed == total:
|
106 |
-
print("✅ All preview functionality tests passed!")
|
107 |
-
sys.exit(0)
|
108 |
-
else:
|
109 |
-
print("❌ Some tests failed")
|
110 |
-
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_rag_fix.py
DELETED
@@ -1,182 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
Test script to verify RAG functionality fixes
|
4 |
-
"""
|
5 |
-
|
6 |
-
import os
|
7 |
-
import tempfile
|
8 |
-
import warnings
|
9 |
-
from pathlib import Path
|
10 |
-
|
11 |
-
# Suppress known warnings
|
12 |
-
warnings.filterwarnings("ignore", message=".*use_auth_token.*")
|
13 |
-
warnings.filterwarnings("ignore", message=".*urllib3.*")
|
14 |
-
warnings.filterwarnings("ignore", message=".*resource_tracker.*")
|
15 |
-
|
16 |
-
# Set environment variables to prevent multiprocessing issues
|
17 |
-
os.environ['TOKENIZERS_PARALLELISM'] = 'false'
|
18 |
-
|
19 |
-
def test_rag_dependencies():
|
20 |
-
"""Test that RAG dependencies are available"""
|
21 |
-
print("Testing RAG dependencies...")
|
22 |
-
|
23 |
-
try:
|
24 |
-
import sentence_transformers
|
25 |
-
print("✅ sentence-transformers available")
|
26 |
-
except ImportError:
|
27 |
-
print("❌ sentence-transformers not available")
|
28 |
-
return False
|
29 |
-
|
30 |
-
try:
|
31 |
-
import faiss
|
32 |
-
print("✅ faiss-cpu available")
|
33 |
-
except ImportError:
|
34 |
-
print("❌ faiss-cpu not available")
|
35 |
-
return False
|
36 |
-
|
37 |
-
try:
|
38 |
-
import fitz # PyMuPDF
|
39 |
-
print("✅ PyMuPDF available")
|
40 |
-
except ImportError:
|
41 |
-
print("⚠️ PyMuPDF not available (PDF processing disabled)")
|
42 |
-
|
43 |
-
try:
|
44 |
-
from docx import Document
|
45 |
-
print("✅ python-docx available")
|
46 |
-
except ImportError:
|
47 |
-
print("⚠️ python-docx not available (DOCX processing disabled)")
|
48 |
-
|
49 |
-
return True
|
50 |
-
|
51 |
-
def test_vector_store_initialization():
|
52 |
-
"""Test vector store initialization with improved error handling"""
|
53 |
-
print("\nTesting vector store initialization...")
|
54 |
-
|
55 |
-
try:
|
56 |
-
from vector_store import VectorStore
|
57 |
-
|
58 |
-
# Test with CPU-only settings
|
59 |
-
store = VectorStore(embedding_model="all-MiniLM-L6-v2")
|
60 |
-
print("✅ VectorStore created successfully")
|
61 |
-
|
62 |
-
# Test a small embedding operation
|
63 |
-
test_texts = ["This is a test sentence.", "Another test sentence."]
|
64 |
-
embeddings = store.create_embeddings(test_texts)
|
65 |
-
print(f"✅ Created embeddings: shape {embeddings.shape}")
|
66 |
-
|
67 |
-
return True
|
68 |
-
|
69 |
-
except Exception as e:
|
70 |
-
print(f"❌ VectorStore initialization failed: {e}")
|
71 |
-
return False
|
72 |
-
|
73 |
-
def test_document_processing():
|
74 |
-
"""Test document processing with a simple text file"""
|
75 |
-
print("\nTesting document processing...")
|
76 |
-
|
77 |
-
try:
|
78 |
-
from document_processor import DocumentProcessor
|
79 |
-
|
80 |
-
# Create a temporary test file
|
81 |
-
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
|
82 |
-
f.write("This is a test document for RAG processing. ")
|
83 |
-
f.write("It contains multiple sentences that should be processed into chunks. ")
|
84 |
-
f.write("Each chunk should have proper metadata and be ready for embedding.")
|
85 |
-
test_file = f.name
|
86 |
-
|
87 |
-
try:
|
88 |
-
processor = DocumentProcessor(chunk_size=50, chunk_overlap=10)
|
89 |
-
chunks = processor.process_file(test_file)
|
90 |
-
|
91 |
-
print(f"✅ Created {len(chunks)} chunks from test document")
|
92 |
-
if chunks:
|
93 |
-
print(f" First chunk: {chunks[0].text[:50]}...")
|
94 |
-
print(f" Metadata keys: {list(chunks[0].metadata.keys())}")
|
95 |
-
|
96 |
-
return True
|
97 |
-
|
98 |
-
finally:
|
99 |
-
# Clean up test file
|
100 |
-
os.unlink(test_file)
|
101 |
-
|
102 |
-
except Exception as e:
|
103 |
-
print(f"❌ Document processing failed: {e}")
|
104 |
-
return False
|
105 |
-
|
106 |
-
def test_rag_tool_integration():
|
107 |
-
"""Test the complete RAG tool integration"""
|
108 |
-
print("\nTesting complete RAG tool integration...")
|
109 |
-
|
110 |
-
try:
|
111 |
-
from rag_tool import RAGTool
|
112 |
-
|
113 |
-
# Create a temporary test file
|
114 |
-
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
|
115 |
-
f.write("RAG integration test document. ")
|
116 |
-
f.write("This document tests the complete RAG pipeline from file processing to vector search. ")
|
117 |
-
f.write("The system should handle this without crashing the server.")
|
118 |
-
test_file = f.name
|
119 |
-
|
120 |
-
try:
|
121 |
-
rag_tool = RAGTool()
|
122 |
-
result = rag_tool.process_uploaded_files([test_file])
|
123 |
-
|
124 |
-
if result['success']:
|
125 |
-
print(f"✅ RAG processing succeeded: {result['message']}")
|
126 |
-
print(f" Files processed: {len(result['summary']['files_processed'])}")
|
127 |
-
print(f" Total chunks: {result['summary']['total_chunks']}")
|
128 |
-
|
129 |
-
# Test search functionality
|
130 |
-
context = rag_tool.get_relevant_context("test document")
|
131 |
-
if context:
|
132 |
-
print(f"✅ Search functionality working: {context[:100]}...")
|
133 |
-
else:
|
134 |
-
print("⚠️ Search returned no results")
|
135 |
-
|
136 |
-
return True
|
137 |
-
else:
|
138 |
-
print(f"❌ RAG processing failed: {result['message']}")
|
139 |
-
return False
|
140 |
-
|
141 |
-
finally:
|
142 |
-
# Clean up test file
|
143 |
-
os.unlink(test_file)
|
144 |
-
|
145 |
-
except Exception as e:
|
146 |
-
print(f"❌ RAG tool integration failed: {e}")
|
147 |
-
return False
|
148 |
-
|
149 |
-
def main():
|
150 |
-
"""Run all RAG tests"""
|
151 |
-
print("🚀 Testing RAG functionality fixes...")
|
152 |
-
print("=" * 50)
|
153 |
-
|
154 |
-
tests = [
|
155 |
-
test_rag_dependencies,
|
156 |
-
test_vector_store_initialization,
|
157 |
-
test_document_processing,
|
158 |
-
test_rag_tool_integration
|
159 |
-
]
|
160 |
-
|
161 |
-
passed = 0
|
162 |
-
total = len(tests)
|
163 |
-
|
164 |
-
for test in tests:
|
165 |
-
try:
|
166 |
-
if test():
|
167 |
-
passed += 1
|
168 |
-
except Exception as e:
|
169 |
-
print(f"❌ Test failed with exception: {e}")
|
170 |
-
|
171 |
-
print("\n" + "=" * 50)
|
172 |
-
print(f"📊 Test Results: {passed}/{total} tests passed")
|
173 |
-
|
174 |
-
if passed == total:
|
175 |
-
print("🎉 All tests passed! RAG functionality should work correctly.")
|
176 |
-
return True
|
177 |
-
else:
|
178 |
-
print("⚠️ Some tests failed. Check error messages above.")
|
179 |
-
return False
|
180 |
-
|
181 |
-
if __name__ == "__main__":
|
182 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_sample.txt
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
This is a sample document for testing the RAG functionality.
|
2 |
-
It contains multiple paragraphs of text that will be processed and chunked.
|
3 |
-
|
4 |
-
The vector store will create embeddings for these chunks and allow semantic search.
|
5 |
-
This enables context-aware responses based on uploaded documents.
|
6 |
-
|
7 |
-
The system supports PDF, DOCX, TXT, and Markdown files.
|
8 |
-
It uses FAISS for efficient similarity search and sentence transformers for embeddings.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_vector_db.py
DELETED
@@ -1,196 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
Test script to verify vector database creation functionality
|
4 |
-
"""
|
5 |
-
|
6 |
-
import sys
|
7 |
-
import os
|
8 |
-
from pathlib import Path
|
9 |
-
|
10 |
-
# Add current directory to path to import modules
|
11 |
-
sys.path.append(str(Path(__file__).parent))
|
12 |
-
|
13 |
-
try:
|
14 |
-
from rag_tool import RAGTool
|
15 |
-
from vector_store import VectorStore
|
16 |
-
from document_processor import DocumentProcessor
|
17 |
-
print("✅ Successfully imported all RAG modules")
|
18 |
-
except ImportError as e:
|
19 |
-
print(f"❌ Failed to import RAG modules: {e}")
|
20 |
-
sys.exit(1)
|
21 |
-
|
22 |
-
def test_document_processing():
|
23 |
-
"""Test document processing functionality"""
|
24 |
-
print("\n=== Testing Document Processing ===")
|
25 |
-
|
26 |
-
processor = DocumentProcessor(chunk_size=200, chunk_overlap=50)
|
27 |
-
|
28 |
-
# Test with our test document
|
29 |
-
test_file = "test_document.txt"
|
30 |
-
if not os.path.exists(test_file):
|
31 |
-
print(f"❌ Test file {test_file} not found")
|
32 |
-
return False
|
33 |
-
|
34 |
-
try:
|
35 |
-
chunks = processor.process_file(test_file)
|
36 |
-
print(f"✅ Processed {test_file} into {len(chunks)} chunks")
|
37 |
-
|
38 |
-
# Show first chunk
|
39 |
-
if chunks:
|
40 |
-
first_chunk = chunks[0]
|
41 |
-
print(f"First chunk preview: {first_chunk.text[:100]}...")
|
42 |
-
print(f"Chunk metadata: {first_chunk.metadata}")
|
43 |
-
|
44 |
-
return True
|
45 |
-
except Exception as e:
|
46 |
-
print(f"❌ Failed to process document: {e}")
|
47 |
-
return False
|
48 |
-
|
49 |
-
def test_vector_store():
|
50 |
-
"""Test vector store functionality"""
|
51 |
-
print("\n=== Testing Vector Store ===")
|
52 |
-
|
53 |
-
try:
|
54 |
-
# Initialize vector store
|
55 |
-
vector_store = VectorStore()
|
56 |
-
print("✅ Initialized vector store")
|
57 |
-
|
58 |
-
# Create test data
|
59 |
-
test_chunks = [
|
60 |
-
{
|
61 |
-
'text': 'Vector databases are used for semantic search',
|
62 |
-
'chunk_id': 'test1',
|
63 |
-
'metadata': {'file_name': 'test.txt', 'chunk_index': 0}
|
64 |
-
},
|
65 |
-
{
|
66 |
-
'text': 'Machine learning models convert text to embeddings',
|
67 |
-
'chunk_id': 'test2',
|
68 |
-
'metadata': {'file_name': 'test.txt', 'chunk_index': 1}
|
69 |
-
},
|
70 |
-
{
|
71 |
-
'text': 'FAISS provides efficient similarity search capabilities',
|
72 |
-
'chunk_id': 'test3',
|
73 |
-
'metadata': {'file_name': 'test.txt', 'chunk_index': 2}
|
74 |
-
}
|
75 |
-
]
|
76 |
-
|
77 |
-
# Build index
|
78 |
-
print("Building vector index...")
|
79 |
-
vector_store.build_index(test_chunks, show_progress=True)
|
80 |
-
print("✅ Built vector index")
|
81 |
-
|
82 |
-
# Test search
|
83 |
-
query = "How do vector databases work?"
|
84 |
-
results = vector_store.search(query, top_k=2)
|
85 |
-
|
86 |
-
print(f"Search results for '{query}':")
|
87 |
-
for i, result in enumerate(results):
|
88 |
-
print(f" {i+1}. Score: {result.score:.3f} - {result.text[:50]}...")
|
89 |
-
|
90 |
-
# Test serialization
|
91 |
-
serialized = vector_store.serialize()
|
92 |
-
print(f"✅ Serialized data size: {len(serialized['index_base64'])} characters")
|
93 |
-
|
94 |
-
return True
|
95 |
-
|
96 |
-
except Exception as e:
|
97 |
-
print(f"❌ Failed vector store test: {e}")
|
98 |
-
import traceback
|
99 |
-
traceback.print_exc()
|
100 |
-
return False
|
101 |
-
|
102 |
-
def test_rag_tool():
|
103 |
-
"""Test complete RAG tool functionality"""
|
104 |
-
print("\n=== Testing RAG Tool ===")
|
105 |
-
|
106 |
-
try:
|
107 |
-
# Initialize RAG tool
|
108 |
-
rag_tool = RAGTool()
|
109 |
-
print("✅ Initialized RAG tool")
|
110 |
-
|
111 |
-
# Process test document
|
112 |
-
test_files = ["test_document.txt"]
|
113 |
-
result = rag_tool.process_uploaded_files(test_files)
|
114 |
-
|
115 |
-
if result['success']:
|
116 |
-
print(f"✅ {result['message']}")
|
117 |
-
|
118 |
-
# Show summary
|
119 |
-
summary = result['summary']
|
120 |
-
print(f"Files processed: {summary['total_files']}")
|
121 |
-
print(f"Total chunks: {summary['total_chunks']}")
|
122 |
-
|
123 |
-
# Test context retrieval
|
124 |
-
query = "What are the benefits of vector databases?"
|
125 |
-
context = rag_tool.get_relevant_context(query, max_chunks=2)
|
126 |
-
|
127 |
-
if context:
|
128 |
-
print(f"\nContext for '{query}':")
|
129 |
-
print(context[:300] + "..." if len(context) > 300 else context)
|
130 |
-
print("✅ Successfully retrieved context")
|
131 |
-
else:
|
132 |
-
print("⚠️ No context retrieved")
|
133 |
-
|
134 |
-
# Test serialization for deployment
|
135 |
-
serialized_data = rag_tool.get_serialized_data()
|
136 |
-
if serialized_data:
|
137 |
-
print("✅ Successfully serialized RAG data for deployment")
|
138 |
-
print(f"Serialized keys: {list(serialized_data.keys())}")
|
139 |
-
else:
|
140 |
-
print("❌ Failed to serialize RAG data")
|
141 |
-
|
142 |
-
return True
|
143 |
-
else:
|
144 |
-
print(f"❌ {result['message']}")
|
145 |
-
return False
|
146 |
-
|
147 |
-
except Exception as e:
|
148 |
-
print(f"❌ Failed RAG tool test: {e}")
|
149 |
-
import traceback
|
150 |
-
traceback.print_exc()
|
151 |
-
return False
|
152 |
-
|
153 |
-
def main():
|
154 |
-
"""Run all tests"""
|
155 |
-
print("=== Vector Database Testing ===")
|
156 |
-
print("Testing vector database creation and functionality...")
|
157 |
-
|
158 |
-
# Check dependencies
|
159 |
-
print("\n=== Checking Dependencies ===")
|
160 |
-
try:
|
161 |
-
import sentence_transformers
|
162 |
-
import faiss
|
163 |
-
import fitz # PyMuPDF
|
164 |
-
print("✅ All required dependencies available")
|
165 |
-
except ImportError as e:
|
166 |
-
print(f"❌ Missing dependency: {e}")
|
167 |
-
return
|
168 |
-
|
169 |
-
# Run tests
|
170 |
-
tests = [
|
171 |
-
("Document Processing", test_document_processing),
|
172 |
-
("Vector Store", test_vector_store),
|
173 |
-
("RAG Tool", test_rag_tool)
|
174 |
-
]
|
175 |
-
|
176 |
-
results = []
|
177 |
-
for test_name, test_func in tests:
|
178 |
-
print(f"\n{'='*20}")
|
179 |
-
success = test_func()
|
180 |
-
results.append((test_name, success))
|
181 |
-
|
182 |
-
# Summary
|
183 |
-
print(f"\n{'='*40}")
|
184 |
-
print("TEST SUMMARY:")
|
185 |
-
for test_name, success in results:
|
186 |
-
status = "✅ PASS" if success else "❌ FAIL"
|
187 |
-
print(f" {test_name}: {status}")
|
188 |
-
|
189 |
-
all_passed = all(success for _, success in results)
|
190 |
-
if all_passed:
|
191 |
-
print("\n🎉 All tests passed! Vector database functionality is working.")
|
192 |
-
else:
|
193 |
-
print("\n⚠️ Some tests failed. Check the output above for details.")
|
194 |
-
|
195 |
-
if __name__ == "__main__":
|
196 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|