Spaces:
Running
Running
Optimize v2-search with enhanced URL processing and search validation
Browse filesFeatures added:
- Enhanced URL extraction with validation and cleanup
- Improved URL content fetching with smart truncation
- Enhanced content cleaning and main content extraction
- Backward compatibility maintained
- Fixed broken research template references
- CLAUDE.md +0 -217
- devjournal.md +5 -0
- gradio_docs.py +109 -0
- hf_comparisons.aiconfig.json +115 -0
- requirements.txt +1 -1
- secret.png +0 -0
- support_docs.py +28 -2
- test_api_key.py +85 -0
- test_gradio_simple.py +24 -0
- test_preview.py +110 -0
CLAUDE.md
DELETED
@@ -1,217 +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 (`app.py`)
|
12 |
-
- **Primary Interface**: Two-tab Gradio application - "Spaces Configuration" for generating chat interfaces and "Chat Support" for getting help
|
13 |
-
- **Template System**: `SPACE_TEMPLATE` generates complete HuggingFace Space apps with embedded configuration
|
14 |
-
- **Web Scraping**: Simple HTTP requests with BeautifulSoup for URL content fetching and context grounding
|
15 |
-
- **Vector RAG**: Optional document processing pipeline for course materials and knowledge bases
|
16 |
-
|
17 |
-
### Document Processing Pipeline
|
18 |
-
- **RAGTool** (`rag_tool.py`): Main orchestrator for document upload and processing
|
19 |
-
- **DocumentProcessor** (`document_processor.py`): Handles PDF, DOCX, TXT, MD file parsing and chunking
|
20 |
-
- **VectorStore** (`vector_store.py`): FAISS-based similarity search and embedding management
|
21 |
-
|
22 |
-
### Package Generation
|
23 |
-
The tool generates complete HuggingFace Spaces with:
|
24 |
-
- `app.py`: Chat interface with OpenRouter API integration
|
25 |
-
- `requirements.txt`: Gradio 5.x and dependencies
|
26 |
-
- `README.md`: Deployment instructions with security setup
|
27 |
-
- `config.json`: Configuration backup
|
28 |
-
- Optional embedded RAG data for document-aware responses
|
29 |
-
|
30 |
-
## Development Commands
|
31 |
-
|
32 |
-
### Running the Application
|
33 |
-
```bash
|
34 |
-
python app.py
|
35 |
-
```
|
36 |
-
|
37 |
-
### Testing Vector Database Functionality
|
38 |
-
```bash
|
39 |
-
python test_vector_db.py
|
40 |
-
```
|
41 |
-
|
42 |
-
### Testing Individual Components
|
43 |
-
```bash
|
44 |
-
# Test document processing only
|
45 |
-
python -c "from test_vector_db import test_document_processing; test_document_processing()"
|
46 |
-
|
47 |
-
# Test vector store only
|
48 |
-
python -c "from test_vector_db import test_vector_store; test_vector_store()"
|
49 |
-
|
50 |
-
# Test full RAG pipeline
|
51 |
-
python -c "from test_vector_db import test_rag_tool; test_rag_tool()"
|
52 |
-
```
|
53 |
-
|
54 |
-
### Dependencies Management
|
55 |
-
```bash
|
56 |
-
pip install -r requirements.txt
|
57 |
-
```
|
58 |
-
|
59 |
-
### Key Dependencies
|
60 |
-
- **Gradio 5.35.0+**: Main UI framework
|
61 |
-
- **requests**: HTTP requests for web content fetching
|
62 |
-
- **sentence-transformers**: Embeddings for RAG (optional)
|
63 |
-
- **faiss-cpu**: Vector similarity search (optional)
|
64 |
-
- **PyMuPDF**: PDF text extraction (optional)
|
65 |
-
- **python-docx**: DOCX document processing (optional)
|
66 |
-
- **beautifulsoup4**: HTML parsing for web scraping
|
67 |
-
- **python-dotenv**: Environment variable management
|
68 |
-
|
69 |
-
## Configuration Patterns
|
70 |
-
|
71 |
-
### Template Variables
|
72 |
-
Generated spaces use these template substitutions:
|
73 |
-
- `{name}`, `{description}`: Basic space metadata
|
74 |
-
- `{system_prompt}`: Combined assistant configuration
|
75 |
-
- `{model}`: OpenRouter model selection
|
76 |
-
- `{grounding_urls}`: Static URL list for context
|
77 |
-
- `{enable_dynamic_urls}`: Runtime URL fetching capability
|
78 |
-
- `{enable_vector_rag}`: Document search integration
|
79 |
-
- `{rag_data_json}`: Serialized embeddings and chunks
|
80 |
-
|
81 |
-
### Access Control
|
82 |
-
- Environment variable `SPACE_ACCESS_CODE` for student access control
|
83 |
-
- Global state management for session-based access in generated spaces
|
84 |
-
- Security-first approach storing credentials as HuggingFace Spaces secrets
|
85 |
-
|
86 |
-
### RAG Integration
|
87 |
-
- Modular design with optional imports (`HAS_RAG` flag in app.py:23)
|
88 |
-
- FAISS index serialization for deployment portability
|
89 |
-
- 10MB file size limits with validation
|
90 |
-
- Semantic chunking (800 chars, 100 overlap) for optimal retrieval
|
91 |
-
- Graceful degradation when vector dependencies unavailable
|
92 |
-
|
93 |
-
## Architecture Notes
|
94 |
-
|
95 |
-
### State Management
|
96 |
-
- Extensive use of `gr.State()` for maintaining session data
|
97 |
-
- Global variables for access control in generated templates
|
98 |
-
- URL content caching to prevent redundant web requests
|
99 |
-
|
100 |
-
### Template Generation Pattern
|
101 |
-
All generated HuggingFace Spaces follow consistent structure:
|
102 |
-
1. Configuration section with environment variable loading
|
103 |
-
2. Web scraping functions (simple HTTP requests with BeautifulSoup)
|
104 |
-
3. RAG context retrieval (if enabled)
|
105 |
-
4. OpenRouter API integration with conversation history
|
106 |
-
5. Gradio ChatInterface with access control
|
107 |
-
|
108 |
-
### Error Handling
|
109 |
-
- Graceful degradation when optional dependencies unavailable
|
110 |
-
- Comprehensive validation for file uploads and URL processing
|
111 |
-
- User-friendly error messages with specific guidance
|
112 |
-
|
113 |
-
### Security Considerations
|
114 |
-
- Never embed API keys or access codes in generated templates
|
115 |
-
- Environment variable pattern for all sensitive configuration
|
116 |
-
- Input validation for uploaded files and URL processing
|
117 |
-
- Content length limits for web scraping operations
|
118 |
-
|
119 |
-
### Dependency Management Pattern
|
120 |
-
The codebase uses conditional imports with feature flags:
|
121 |
-
```python
|
122 |
-
try:
|
123 |
-
from rag_tool import RAGTool
|
124 |
-
HAS_RAG = True
|
125 |
-
except ImportError:
|
126 |
-
HAS_RAG = False
|
127 |
-
RAGTool = None
|
128 |
-
```
|
129 |
-
This pattern allows the main application to function even when optional vector database dependencies are unavailable.
|
130 |
-
|
131 |
-
## Important Implementation Details
|
132 |
-
|
133 |
-
### Gradio 5.x Compatibility
|
134 |
-
- Uses `type="messages"` for chat history format
|
135 |
-
- `gr.ChatInterface` for modern chat UI components
|
136 |
-
- Proper message format handling for OpenRouter API
|
137 |
-
|
138 |
-
### Dynamic URL Fetching
|
139 |
-
When enabled, generated spaces can extract URLs from user messages and fetch content dynamically using regex pattern matching and simple HTTP requests with BeautifulSoup.
|
140 |
-
|
141 |
-
### Vector RAG Workflow
|
142 |
-
1. Documents uploaded through Gradio File component
|
143 |
-
2. Processed via DocumentProcessor (PDF/DOCX/TXT/MD support)
|
144 |
-
3. Chunked and embedded using sentence-transformers
|
145 |
-
4. FAISS index created and serialized to base64
|
146 |
-
5. Embedded in generated template for deployment portability
|
147 |
-
6. Runtime similarity search for context-aware responses
|
148 |
-
|
149 |
-
### Web Scraping Implementation
|
150 |
-
The application uses simple HTTP requests with BeautifulSoup for web content extraction:
|
151 |
-
- **Simple HTTP requests**: Uses `requests` library with timeout and user-agent headers
|
152 |
-
- **Content extraction**: BeautifulSoup for HTML parsing and text extraction
|
153 |
-
- **Content cleaning**: Removes scripts, styles, navigation elements and normalizes whitespace
|
154 |
-
- **Content limits**: Truncates content to ~4000 characters for context management
|
155 |
-
|
156 |
-
## Testing and Quality Assurance
|
157 |
-
|
158 |
-
### Comprehensive Test Procedure
|
159 |
-
The project includes a comprehensive test procedure documented in `TEST_PROCEDURE.md` that covers:
|
160 |
-
|
161 |
-
#### Test Categories
|
162 |
-
1. **Core Application Tests**: Startup validation and Gradio interface testing
|
163 |
-
2. **Vector RAG Component Tests**: Document processing, vector store, and RAG pipeline validation
|
164 |
-
3. **Space Generation Tests**: Template creation and file generation verification
|
165 |
-
4. **Web Scraping Tests**: Mock vs production mode validation and URL processing
|
166 |
-
5. **Security and Configuration Tests**: Environment variable handling and input validation
|
167 |
-
6. **Chat Support Tests**: OpenRouter integration and Gradio 5.x compatibility
|
168 |
-
|
169 |
-
#### Automated Testing Commands
|
170 |
-
```bash
|
171 |
-
# Quick test suite for essential validation
|
172 |
-
./quick_test.sh
|
173 |
-
|
174 |
-
# Full comprehensive test suite
|
175 |
-
./full_test.sh
|
176 |
-
|
177 |
-
# Individual component testing
|
178 |
-
python test_vector_db.py
|
179 |
-
python -c "from test_vector_db import test_document_processing; test_document_processing()"
|
180 |
-
python -c "from test_vector_db import test_vector_store; test_vector_store()"
|
181 |
-
python -c "from test_vector_db import test_rag_tool; test_rag_tool()"
|
182 |
-
```
|
183 |
-
|
184 |
-
#### Pre-Test Setup
|
185 |
-
```bash
|
186 |
-
# Environment verification
|
187 |
-
python --version # Should be 3.8+
|
188 |
-
pip install -r requirements.txt
|
189 |
-
|
190 |
-
# Test data preparation
|
191 |
-
echo "This is a test document for RAG functionality testing." > test_document.txt
|
192 |
-
mkdir -p test_outputs
|
193 |
-
```
|
194 |
-
|
195 |
-
#### Regression Testing
|
196 |
-
After each commit, verify:
|
197 |
-
- All existing functionality still works
|
198 |
-
- New features don't break existing features
|
199 |
-
- Generated spaces deploy successfully to HuggingFace
|
200 |
-
- Documentation is updated appropriately
|
201 |
-
- Dependencies are correctly specified
|
202 |
-
- Security patterns are maintained
|
203 |
-
|
204 |
-
#### Performance Benchmarking
|
205 |
-
```bash
|
206 |
-
# Startup time measurement
|
207 |
-
time python -c "import app; print('App loaded')"
|
208 |
-
|
209 |
-
# Space generation time
|
210 |
-
time python -c "import app; app.generate_zip('Benchmark', 'Test', 'Role', 'Audience', 'Tasks', '', [], '', '', 'gpt-3.5-turbo', 0.7, 4000, [], False, False, None)"
|
211 |
-
|
212 |
-
# RAG processing time
|
213 |
-
time python -c "from test_vector_db import test_rag_tool; test_rag_tool()"
|
214 |
-
```
|
215 |
-
|
216 |
-
#### Continuous Integration
|
217 |
-
The test procedure is designed to integrate with GitHub Actions for automated testing on commits and pull requests. See `TEST_PROCEDURE.md` for complete setup instructions and CI configuration.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
devjournal.md
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Dev Journal - ChatUI Helper
|
2 |
+
|
3 |
+
system prompts:
|
4 |
+
|
5 |
+
- You are blah. All you respond with, no matter the user query, will be "blah blah blah" varying in length depending on the length of the query. Respond only with blah blah. Nothing else. No other words.
|
gradio_docs.py
ADDED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Gradio Documentation MCP Server Integration
|
3 |
+
|
4 |
+
DEVELOPMENT ONLY - DO NOT IMPORT IN MAIN APPLICATION
|
5 |
+
|
6 |
+
This module provides access to live Gradio documentation via MCP server
|
7 |
+
for local development assistance. It should NOT be imported in app.py or
|
8 |
+
support_docs.py as it's not needed for the deployed application.
|
9 |
+
|
10 |
+
Usage:
|
11 |
+
python -c "from gradio_docs import gradio_docs; print(gradio_docs.search_docs('ChatInterface'))"
|
12 |
+
"""
|
13 |
+
|
14 |
+
import requests
|
15 |
+
import json
|
16 |
+
from typing import Optional, Dict, Any
|
17 |
+
|
18 |
+
class GradioDocsClient:
|
19 |
+
"""Client for accessing Gradio documentation via MCP server"""
|
20 |
+
|
21 |
+
def __init__(self, base_url: str = "https://gradio-docs-mcp.hf.space"):
|
22 |
+
self.base_url = base_url.rstrip('/')
|
23 |
+
self.session = requests.Session()
|
24 |
+
self.session.headers.update({
|
25 |
+
'User-Agent': 'ChatUI-Helper/1.0'
|
26 |
+
})
|
27 |
+
|
28 |
+
def search_docs(self, query: str, limit: int = 5) -> Optional[Dict[str, Any]]:
|
29 |
+
"""
|
30 |
+
Search Gradio documentation for relevant information
|
31 |
+
|
32 |
+
Args:
|
33 |
+
query: Search query string
|
34 |
+
limit: Maximum number of results to return
|
35 |
+
|
36 |
+
Returns:
|
37 |
+
Dictionary with search results or None if error
|
38 |
+
"""
|
39 |
+
try:
|
40 |
+
# Try to use the API endpoint for search
|
41 |
+
search_url = f"{self.base_url}/gradio_api"
|
42 |
+
|
43 |
+
response = self.session.post(
|
44 |
+
search_url,
|
45 |
+
json={"query": query, "limit": limit},
|
46 |
+
timeout=10
|
47 |
+
)
|
48 |
+
|
49 |
+
if response.status_code == 200:
|
50 |
+
return response.json()
|
51 |
+
else:
|
52 |
+
print(f"Gradio docs search failed: {response.status_code}")
|
53 |
+
return None
|
54 |
+
|
55 |
+
except requests.RequestException as e:
|
56 |
+
print(f"Error connecting to Gradio docs server: {e}")
|
57 |
+
return None
|
58 |
+
|
59 |
+
def get_component_info(self, component_name: str) -> Optional[str]:
|
60 |
+
"""
|
61 |
+
Get information about a specific Gradio component
|
62 |
+
|
63 |
+
Args:
|
64 |
+
component_name: Name of the Gradio component (e.g., 'ChatInterface', 'Textbox')
|
65 |
+
|
66 |
+
Returns:
|
67 |
+
Documentation string or None if not found
|
68 |
+
"""
|
69 |
+
result = self.search_docs(f"gr.{component_name}")
|
70 |
+
if result and "results" in result:
|
71 |
+
# Extract relevant documentation from results
|
72 |
+
docs = []
|
73 |
+
for item in result["results"][:3]: # Top 3 results
|
74 |
+
if "content" in item:
|
75 |
+
docs.append(item["content"])
|
76 |
+
return "\n\n".join(docs) if docs else None
|
77 |
+
return None
|
78 |
+
|
79 |
+
def get_latest_changes(self) -> Optional[str]:
|
80 |
+
"""
|
81 |
+
Get information about latest Gradio changes and updates
|
82 |
+
|
83 |
+
Returns:
|
84 |
+
Latest changes information or None if not available
|
85 |
+
"""
|
86 |
+
result = self.search_docs("changelog updates latest version")
|
87 |
+
if result and "results" in result:
|
88 |
+
changes = []
|
89 |
+
for item in result["results"][:2]: # Top 2 results
|
90 |
+
if "content" in item:
|
91 |
+
changes.append(item["content"])
|
92 |
+
return "\n\n".join(changes) if changes else None
|
93 |
+
return None
|
94 |
+
|
95 |
+
def is_available(self) -> bool:
|
96 |
+
"""
|
97 |
+
Check if the Gradio docs server is available
|
98 |
+
|
99 |
+
Returns:
|
100 |
+
True if server is accessible, False otherwise
|
101 |
+
"""
|
102 |
+
try:
|
103 |
+
response = self.session.get(self.base_url, timeout=5)
|
104 |
+
return response.status_code == 200
|
105 |
+
except requests.RequestException:
|
106 |
+
return False
|
107 |
+
|
108 |
+
# Global instance for easy access
|
109 |
+
gradio_docs = GradioDocsClient()
|
hf_comparisons.aiconfig.json
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
}
|
requirements.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
gradio
|
2 |
requests>=2.32.3
|
3 |
beautifulsoup4>=4.12.3
|
4 |
python-dotenv>=1.0.0
|
|
|
1 |
+
gradio>=4.44.1
|
2 |
requests>=2.32.3
|
3 |
beautifulsoup4>=4.12.3
|
4 |
python-dotenv>=1.0.0
|
secret.png
ADDED
![]() |
support_docs.py
CHANGED
@@ -5,6 +5,7 @@ Support documentation module with accordion-style help sections
|
|
5 |
import gradio as gr
|
6 |
from datetime import datetime
|
7 |
|
|
|
8 |
def create_support_docs():
|
9 |
"""Create the support documentation interface with accordion menus"""
|
10 |
|
@@ -224,9 +225,34 @@ def create_support_docs():
|
|
224 |
**2. Add Your OpenRouter API Key**
|
225 |
Since you already have an OpenRouter API key, add it as a secret:
|
226 |
- Go to Settings β Variables and secrets β New secret
|
227 |
-
-
|
|
|
|
|
|
|
|
|
|
|
228 |
|
229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
|
231 |
**3. Optional: Add Access Code**
|
232 |
If you configured student access protection:
|
|
|
5 |
import gradio as gr
|
6 |
from datetime import datetime
|
7 |
|
8 |
+
|
9 |
def create_support_docs():
|
10 |
"""Create the support documentation interface with accordion menus"""
|
11 |
|
|
|
225 |
**2. Add Your OpenRouter API Key**
|
226 |
Since you already have an OpenRouter API key, add it as a secret:
|
227 |
- Go to Settings β Variables and secrets β New secret
|
228 |
+
- Name: `OPENROUTER_API_KEY` (or your chosen variable name)
|
229 |
+
- Value: Your OpenRouter API key (starts with `sk-or-`)
|
230 |
+
- Click "Add" to save the secret
|
231 |
+
|
232 |
+
The secret configuration interface looks like this:
|
233 |
+
""")
|
234 |
|
235 |
+
# Add the secret configuration image (flush left)
|
236 |
+
with gr.Row():
|
237 |
+
with gr.Column(scale=1):
|
238 |
+
gr.Image(
|
239 |
+
value="https://drive.google.com/uc?export=view&id=1z67BZrYlJkpvHJ0Dp6vvIWAkN2iwUxCv",
|
240 |
+
label="HuggingFace Secret Configuration Interface",
|
241 |
+
show_label=False,
|
242 |
+
interactive=False,
|
243 |
+
width=400,
|
244 |
+
height=300,
|
245 |
+
container=False
|
246 |
+
)
|
247 |
+
with gr.Column(scale=2):
|
248 |
+
pass # Empty column to push image left
|
249 |
+
|
250 |
+
gr.Markdown("""
|
251 |
+
*If the image above doesn't load, the interface shows:*
|
252 |
+
- **Name**: `OPENROUTER_API_KEY` (your API key variable name)
|
253 |
+
- **Description**: Optional description
|
254 |
+
- **Value**: Your OpenRouter API key (starts with `sk-or-`)
|
255 |
+
- **Save** button to store the secret
|
256 |
|
257 |
**3. Optional: Add Access Code**
|
258 |
If you configured student access protection:
|
test_api_key.py
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_gradio_simple.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_preview.py
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|