Spaces:
Sleeping
Sleeping
Commit
Β·
028ef27
1
Parent(s):
a213258
Refactor app.py to enhance paper fetching functionality and improve error handling. Update README.md to reflect new features and usage instructions. Remove dotenv dependency from requirements.txt.
Browse files- README.md +23 -13
- app.py +205 -256
- requirements.txt +0 -1
README.md
CHANGED
|
@@ -16,14 +16,13 @@ A modern conversational AI chatbot designed specifically for exploring and analy
|
|
| 16 |
|
| 17 |
## β¨ Latest Features
|
| 18 |
|
| 19 |
-
- π **
|
| 20 |
-
- π **
|
| 21 |
-
- π§ **
|
| 22 |
- π **Real-time Streaming**: Instant response streaming for better UX
|
| 23 |
- ποΈ **Multiple Model Selection**: Choose between GPT-4o, GPT-4o-mini, and GPT-3.5 Turbo
|
| 24 |
- βοΈ **Advanced Parameters**: Fine-tune temperature, max tokens, and top-p
|
| 25 |
- π¨ **Modern UI**: Responsive design with intuitive controls
|
| 26 |
-
- π§ **Customizable System Messages**: Define AI personality and behavior
|
| 27 |
- π‘οΈ **Robust Error Handling**: Clear error messages for common issues
|
| 28 |
- π± **Mobile Responsive**: Works great on all devices
|
| 29 |
|
|
@@ -45,11 +44,21 @@ pip install -r requirements.txt
|
|
| 45 |
### 3. Configure Environment
|
| 46 |
|
| 47 |
#### For Local Development
|
| 48 |
-
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
```bash
|
| 51 |
-
|
| 52 |
-
OPENAI_API_KEY=your_openai_api_key_here
|
| 53 |
```
|
| 54 |
|
| 55 |
#### For Hugging Face Spaces Deployment
|
|
@@ -78,10 +87,11 @@ The chatbot will be available at `http://localhost:7860`
|
|
| 78 |
## π― Usage Guide
|
| 79 |
|
| 80 |
### Basic Paper Exploration
|
| 81 |
-
1. **Ask about specific topics**: "
|
| 82 |
-
2. **Request full papers**: "Show me the full paper about
|
| 83 |
-
3. **Get detailed information**: "What's the conclusion of the pig disease paper?"
|
| 84 |
-
4. **
|
|
|
|
| 85 |
|
| 86 |
### Advanced Controls
|
| 87 |
|
|
@@ -172,7 +182,7 @@ Papers/
|
|
| 172 |
### Common Issues
|
| 173 |
|
| 174 |
**API Key Errors**
|
| 175 |
-
- Ensure your
|
| 176 |
- Check that the API key has sufficient credits
|
| 177 |
- For Hugging Face Spaces: Verify the secret is named `OPENAI_API_KEY`
|
| 178 |
|
|
@@ -194,7 +204,7 @@ Papers/
|
|
| 194 |
- Long conversations are automatically truncated
|
| 195 |
|
| 196 |
### Error Messages
|
| 197 |
-
- **"Invalid API key"**: Check your
|
| 198 |
- **"Quota exceeded"**: Add credits to your OpenAI account
|
| 199 |
- **"Rate limit"**: Wait and retry
|
| 200 |
- **"Paper not found"**: Check that the paper file exists in the Papers directory
|
|
|
|
| 16 |
|
| 17 |
## β¨ Latest Features
|
| 18 |
|
| 19 |
+
- π **Smart Function Calling**: Intelligent paper retrieval using OpenAI's function calling API
|
| 20 |
+
- π **Dynamic Paper Fetching**: Automatically fetches full paper texts when needed
|
| 21 |
+
- π§ **Contextual Conversation Memory**: Maintains chat history with intelligent truncation
|
| 22 |
- π **Real-time Streaming**: Instant response streaming for better UX
|
| 23 |
- ποΈ **Multiple Model Selection**: Choose between GPT-4o, GPT-4o-mini, and GPT-3.5 Turbo
|
| 24 |
- βοΈ **Advanced Parameters**: Fine-tune temperature, max tokens, and top-p
|
| 25 |
- π¨ **Modern UI**: Responsive design with intuitive controls
|
|
|
|
| 26 |
- π‘οΈ **Robust Error Handling**: Clear error messages for common issues
|
| 27 |
- π± **Mobile Responsive**: Works great on all devices
|
| 28 |
|
|
|
|
| 44 |
### 3. Configure Environment
|
| 45 |
|
| 46 |
#### For Local Development
|
| 47 |
+
Set your OpenAI API key as an environment variable:
|
| 48 |
|
| 49 |
+
**Windows (PowerShell):**
|
| 50 |
+
```powershell
|
| 51 |
+
$env:OPENAI_API_KEY="your_openai_api_key_here"
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
**Windows (Command Prompt):**
|
| 55 |
+
```cmd
|
| 56 |
+
set OPENAI_API_KEY=your_openai_api_key_here
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
**Linux/macOS:**
|
| 60 |
```bash
|
| 61 |
+
export OPENAI_API_KEY="your_openai_api_key_here"
|
|
|
|
| 62 |
```
|
| 63 |
|
| 64 |
#### For Hugging Face Spaces Deployment
|
|
|
|
| 87 |
## π― Usage Guide
|
| 88 |
|
| 89 |
### Basic Paper Exploration
|
| 90 |
+
1. **Ask about specific topics**: "What papers discuss AI's impact on employment?"
|
| 91 |
+
2. **Request full papers**: "Show me the full paper about AI companions"
|
| 92 |
+
3. **Get detailed information**: "What's the conclusion of the pig disease detection paper?"
|
| 93 |
+
4. **Compare findings**: "Compare findings on AI in education"
|
| 94 |
+
5. **Ask for specific details**: "What methodology did they use in the pig disease paper?"
|
| 95 |
|
| 96 |
### Advanced Controls
|
| 97 |
|
|
|
|
| 182 |
### Common Issues
|
| 183 |
|
| 184 |
**API Key Errors**
|
| 185 |
+
- Ensure your `OPENAI_API_KEY` environment variable is set correctly
|
| 186 |
- Check that the API key has sufficient credits
|
| 187 |
- For Hugging Face Spaces: Verify the secret is named `OPENAI_API_KEY`
|
| 188 |
|
|
|
|
| 204 |
- Long conversations are automatically truncated
|
| 205 |
|
| 206 |
### Error Messages
|
| 207 |
+
- **"Invalid API key"**: Check your environment variable or Hugging Face Spaces secrets
|
| 208 |
- **"Quota exceeded"**: Add credits to your OpenAI account
|
| 209 |
- **"Rate limit"**: Wait and retry
|
| 210 |
- **"Paper not found"**: Check that the paper file exists in the Papers directory
|
app.py
CHANGED
|
@@ -2,13 +2,9 @@ import gradio as gr
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
import re
|
| 5 |
-
from typing import Iterator, Dict, Any, List
|
| 6 |
from openai import OpenAI
|
| 7 |
from openai.types.chat import ChatCompletionChunk
|
| 8 |
-
from dotenv import load_dotenv
|
| 9 |
-
|
| 10 |
-
# Load environment variables
|
| 11 |
-
load_dotenv()
|
| 12 |
|
| 13 |
# Load abstracts content once at startup
|
| 14 |
def load_abstracts_content():
|
|
@@ -19,108 +15,81 @@ def load_abstracts_content():
|
|
| 19 |
except FileNotFoundError:
|
| 20 |
return "Abstracts database not found."
|
| 21 |
|
| 22 |
-
# Load
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
papers = {}
|
| 26 |
papers_dir = "Papers"
|
| 27 |
|
| 28 |
if not os.path.exists(papers_dir):
|
| 29 |
-
return
|
| 30 |
|
| 31 |
-
for filename in
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
try:
|
| 35 |
with open(filepath, "r", encoding="utf-8") as f:
|
| 36 |
-
|
| 37 |
-
# Extract title from filename (remove .txt extension)
|
| 38 |
-
title = filename[:-4]
|
| 39 |
-
papers[title] = content
|
| 40 |
except Exception as e:
|
| 41 |
-
|
|
|
|
|
|
|
| 42 |
|
| 43 |
return papers
|
| 44 |
|
| 45 |
-
# Load abstracts content globally
|
| 46 |
-
ABSTRACTS_CONTENT = load_abstracts_content()
|
| 47 |
-
PAPER_TEXTS = load_paper_texts()
|
| 48 |
-
|
| 49 |
-
def search_papers(query: str, papers: Dict[str, str]) -> List[tuple[str, str, str]]:
|
| 50 |
-
"""
|
| 51 |
-
Search through paper texts for relevant content.
|
| 52 |
-
Returns list of (title, content, relevance_score) tuples.
|
| 53 |
-
"""
|
| 54 |
-
results = []
|
| 55 |
-
query_lower = query.lower()
|
| 56 |
-
|
| 57 |
-
for title, content in papers.items():
|
| 58 |
-
# Simple keyword matching - can be enhanced with more sophisticated search
|
| 59 |
-
relevance_score = 0
|
| 60 |
-
|
| 61 |
-
# Check if query terms appear in title
|
| 62 |
-
if any(term in title.lower() for term in query_lower.split()):
|
| 63 |
-
relevance_score += 10
|
| 64 |
-
|
| 65 |
-
# Check if query terms appear in content
|
| 66 |
-
content_lower = content.lower()
|
| 67 |
-
for term in query_lower.split():
|
| 68 |
-
if term in content_lower:
|
| 69 |
-
relevance_score += content_lower.count(term)
|
| 70 |
-
|
| 71 |
-
if relevance_score > 0:
|
| 72 |
-
# For full paper requests, include more content
|
| 73 |
-
if any(keyword in query.lower() for keyword in ["full paper", "complete paper", "entire paper", "show me the paper", "read the paper"]):
|
| 74 |
-
# Include more content for full paper requests
|
| 75 |
-
truncated_content = content[:8000] + "..." if len(content) > 8000 else content
|
| 76 |
-
else:
|
| 77 |
-
# Truncate content to first 2000 characters for context
|
| 78 |
-
truncated_content = content[:2000] + "..." if len(content) > 2000 else content
|
| 79 |
-
results.append((title, truncated_content, relevance_score))
|
| 80 |
-
|
| 81 |
-
# Sort by relevance score
|
| 82 |
-
results.sort(key=lambda x: x[2], reverse=True)
|
| 83 |
-
return results[:3] # Return top 3 most relevant papers
|
| 84 |
-
|
| 85 |
-
def get_relevant_papers_content(user_query: str) -> str:
|
| 86 |
-
"""
|
| 87 |
-
Get relevant paper content based on user query.
|
| 88 |
-
"""
|
| 89 |
-
if not PAPER_TEXTS:
|
| 90 |
-
return ""
|
| 91 |
-
|
| 92 |
-
relevant_papers = search_papers(user_query, PAPER_TEXTS)
|
| 93 |
-
|
| 94 |
-
if not relevant_papers:
|
| 95 |
-
return ""
|
| 96 |
-
|
| 97 |
-
content = "\n\n=== FULL PAPER CONTENT ===\n"
|
| 98 |
-
for title, paper_content, score in relevant_papers:
|
| 99 |
-
content += f"\n--- {title} ---\n"
|
| 100 |
-
content += paper_content
|
| 101 |
-
content += "\n" + "="*50 + "\n"
|
| 102 |
-
|
| 103 |
-
return content
|
| 104 |
-
|
| 105 |
-
def get_full_paper_content(paper_title: str) -> str:
|
| 106 |
-
"""
|
| 107 |
-
Get the full content of a specific paper by title.
|
| 108 |
-
"""
|
| 109 |
-
if not PAPER_TEXTS:
|
| 110 |
-
return ""
|
| 111 |
-
|
| 112 |
-
# Try to find the paper by title (case-insensitive)
|
| 113 |
-
for title, content in PAPER_TEXTS.items():
|
| 114 |
-
if paper_title.lower() in title.lower() or title.lower() in paper_title.lower():
|
| 115 |
-
return f"\n\n=== FULL PAPER: {title} ===\n\n{content}"
|
| 116 |
-
|
| 117 |
-
return ""
|
| 118 |
-
|
| 119 |
def extract_conclusion_from_paper(content: str) -> str:
|
| 120 |
-
"""
|
| 121 |
-
Extract the conclusion section from a paper's content.
|
| 122 |
-
"""
|
| 123 |
-
# Look for conclusion sections with more specific patterns
|
| 124 |
conclusion_patterns = [
|
| 125 |
"conclusion and future works",
|
| 126 |
"conclusion and future work",
|
|
@@ -133,11 +102,9 @@ def extract_conclusion_from_paper(content: str) -> str:
|
|
| 133 |
lines = content.split('\n')
|
| 134 |
conclusion_start = -1
|
| 135 |
|
| 136 |
-
# First, try to find a proper conclusion section
|
| 137 |
for i, line in enumerate(lines):
|
| 138 |
line_lower = line.lower().strip()
|
| 139 |
if any(pattern in line_lower for pattern in conclusion_patterns):
|
| 140 |
-
# Check if it's a section header
|
| 141 |
if (line.isupper() or
|
| 142 |
line.strip().endswith(':') or
|
| 143 |
len(line.strip()) < 100 or
|
|
@@ -146,83 +113,29 @@ def extract_conclusion_from_paper(content: str) -> str:
|
|
| 146 |
break
|
| 147 |
|
| 148 |
if conclusion_start != -1:
|
| 149 |
-
# Extract from conclusion start to acknowledgments or references
|
| 150 |
conclusion_lines = []
|
| 151 |
for line in lines[conclusion_start:]:
|
| 152 |
line_stripped = line.strip()
|
| 153 |
-
# Stop at acknowledgments or references
|
| 154 |
if (line_stripped.lower().startswith('acknowledgments') or
|
| 155 |
line_stripped.lower().startswith('references') or
|
| 156 |
line_stripped.startswith('--- Page')):
|
| 157 |
break
|
| 158 |
conclusion_lines.append(line)
|
| 159 |
|
| 160 |
-
|
| 161 |
-
return conclusion_text
|
| 162 |
-
|
| 163 |
-
# If no conclusion section found, look for the final paragraphs
|
| 164 |
-
# Find the last substantial paragraph (usually before references or acknowledgments)
|
| 165 |
-
lines_reversed = list(reversed(lines))
|
| 166 |
-
final_content_start = -1
|
| 167 |
-
|
| 168 |
-
for i, line in enumerate(lines_reversed):
|
| 169 |
-
line_stripped = line.strip()
|
| 170 |
-
# Skip empty lines and page markers
|
| 171 |
-
if (line_stripped and
|
| 172 |
-
not line_stripped.startswith('--- Page') and
|
| 173 |
-
not line_stripped.startswith('References') and
|
| 174 |
-
not line_stripped.lower().startswith('acknowledgments')):
|
| 175 |
-
# Look for the last substantial paragraph
|
| 176 |
-
if len(line_stripped) > 50: # Substantial line
|
| 177 |
-
final_content_start = len(lines) - i
|
| 178 |
-
break
|
| 179 |
-
|
| 180 |
-
if final_content_start != -1:
|
| 181 |
-
# Get the last 1500 characters from the final content
|
| 182 |
-
final_content = '\n'.join(lines[final_content_start:])
|
| 183 |
-
return final_content[-1500:] if len(final_content) > 1500 else final_content
|
| 184 |
|
| 185 |
# Fallback: return the last 1000 characters
|
| 186 |
return content[-1000:] if len(content) > 1000 else content
|
| 187 |
|
| 188 |
-
# Get API key with better error handling
|
| 189 |
-
api_key = os.getenv("OPENAI_API_KEY")
|
| 190 |
-
if not api_key:
|
| 191 |
-
print("β οΈ Warning: OPENAI_API_KEY environment variable not set!")
|
| 192 |
-
print("Please set your OpenAI API key as an environment variable.")
|
| 193 |
-
print("For Hugging Face Spaces: Add OPENAI_API_KEY as a repository secret")
|
| 194 |
-
print("For local development: Create a .env file with OPENAI_API_KEY=your_key")
|
| 195 |
-
# Create a dummy client for UI to load (will show error when used)
|
| 196 |
-
client = None
|
| 197 |
-
else:
|
| 198 |
-
# Initialize OpenAI client with latest configuration
|
| 199 |
-
client = OpenAI(
|
| 200 |
-
api_key=api_key,
|
| 201 |
-
timeout=60.0, # 60 second timeout
|
| 202 |
-
max_retries=3 # Retry failed requests up to 3 times
|
| 203 |
-
)
|
| 204 |
-
|
| 205 |
-
# Available models
|
| 206 |
-
AVAILABLE_MODELS = {
|
| 207 |
-
"GPT-4o-mini": "gpt-4o-mini",
|
| 208 |
-
"GPT-4o": "gpt-4o",
|
| 209 |
-
"GPT-3.5 Turbo": "gpt-3.5-turbo"
|
| 210 |
-
}
|
| 211 |
-
|
| 212 |
def truncate_conversation_history(messages: list, max_tokens: int = 8000) -> list:
|
| 213 |
-
"""
|
| 214 |
-
|
| 215 |
-
Keeps the most recent messages and system message.
|
| 216 |
-
"""
|
| 217 |
-
if len(messages) <= 3: # System + 1 user + 1 assistant
|
| 218 |
return messages
|
| 219 |
|
| 220 |
-
# Always keep system message
|
| 221 |
system_message = messages[0]
|
| 222 |
conversation_messages = messages[1:]
|
| 223 |
|
| 224 |
-
|
| 225 |
-
while len(conversation_messages) > 6: # Keep last 3 exchanges
|
| 226 |
conversation_messages = conversation_messages[2:]
|
| 227 |
|
| 228 |
return [system_message] + conversation_messages
|
|
@@ -236,65 +149,39 @@ def respond(
|
|
| 236 |
top_p: float,
|
| 237 |
) -> Iterator[str]:
|
| 238 |
"""
|
| 239 |
-
Generate a response using OpenAI's
|
| 240 |
-
Maintains conversation history in the messages array with proper truncation.
|
| 241 |
"""
|
| 242 |
if not client:
|
| 243 |
-
yield "β Error: OpenAI API key not configured.
|
| 244 |
return
|
| 245 |
|
| 246 |
if not message.strip():
|
| 247 |
yield "Please enter a message to start the conversation."
|
| 248 |
return
|
| 249 |
|
| 250 |
-
#
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
# Check if user is asking for a specific paper (e.g., "show me the full paper about pigs")
|
| 254 |
-
specific_paper_content = ""
|
| 255 |
-
conclusion_content = ""
|
| 256 |
-
|
| 257 |
-
if any(keyword in message.lower() for keyword in ["full paper", "complete paper", "entire paper", "show me the paper", "read the paper"]):
|
| 258 |
-
# Try to find specific paper content
|
| 259 |
-
for title in PAPER_TEXTS.keys():
|
| 260 |
-
if any(term in title.lower() for term in message.lower().split()):
|
| 261 |
-
specific_paper_content = get_full_paper_content(title)
|
| 262 |
-
break
|
| 263 |
-
|
| 264 |
-
# Check if user is asking for conclusions specifically
|
| 265 |
-
if any(keyword in message.lower() for keyword in ["conclusion", "conclusions", "what's the conclusion", "what is the conclusion"]):
|
| 266 |
-
for title, content in PAPER_TEXTS.items():
|
| 267 |
-
if any(term in title.lower() for term in message.lower().split()):
|
| 268 |
-
conclusion_text = extract_conclusion_from_paper(content)
|
| 269 |
-
conclusion_content = f"\n\n=== CONCLUSION FROM {title} ===\n\n{conclusion_text}"
|
| 270 |
-
break
|
| 271 |
-
|
| 272 |
-
# Initialize messages array with system message
|
| 273 |
-
# Use the pre-loaded abstracts content and relevant full papers
|
| 274 |
-
system_prompt = f"""You are an AI chatbot designed to help users explore and analyze AI research papers. Your primary function is to retrieve relevant papers and answer questions about them based solely on the provided paper database.
|
| 275 |
|
| 276 |
-
|
|
|
|
|
|
|
| 277 |
|
|
|
|
| 278 |
{ABSTRACTS_CONTENT}
|
| 279 |
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
3. When asked for conclusions, look for sections titled "Conclusion", "Conclusions", or the final paragraphs of the paper.
|
| 290 |
-
4. When asked for quotes, provide the exact text from the paper content provided.
|
| 291 |
-
5. You can now access the complete text of papers and provide detailed information including conclusions, methodology, and specific quotes.
|
| 292 |
-
6. If a user asks for the "full paper" or "complete paper", provide a comprehensive summary including all major sections (abstract, introduction, methodology, results, conclusions).
|
| 293 |
-
7. When conclusion content is specifically provided, use that content to answer conclusion-related questions."""
|
| 294 |
|
| 295 |
messages = [{"role": "system", "content": system_prompt}]
|
| 296 |
|
| 297 |
-
# Add conversation history
|
| 298 |
for user_msg, assistant_msg in history:
|
| 299 |
if user_msg and user_msg.strip():
|
| 300 |
messages.append({"role": "user", "content": user_msg.strip()})
|
|
@@ -304,55 +191,134 @@ IMPORTANT INSTRUCTIONS:
|
|
| 304 |
# Add current user message
|
| 305 |
messages.append({"role": "user", "content": message.strip()})
|
| 306 |
|
| 307 |
-
# Truncate
|
| 308 |
messages = truncate_conversation_history(messages)
|
| 309 |
|
| 310 |
try:
|
| 311 |
-
# Get the actual model identifier
|
| 312 |
model = AVAILABLE_MODELS.get(model_name, "gpt-4o-mini")
|
| 313 |
|
| 314 |
-
#
|
| 315 |
response = client.chat.completions.create(
|
| 316 |
model=model,
|
| 317 |
messages=messages,
|
| 318 |
max_tokens=max_tokens,
|
| 319 |
temperature=temperature,
|
| 320 |
top_p=top_p,
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
frequency_penalty=0.0
|
| 325 |
)
|
| 326 |
|
| 327 |
-
#
|
| 328 |
-
|
|
|
|
|
|
|
|
|
|
| 329 |
for chunk in response:
|
| 330 |
-
if hasattr(chunk.choices[0], 'delta')
|
| 331 |
-
|
| 332 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
|
| 334 |
except Exception as e:
|
| 335 |
error_message = f"Error: {str(e)}"
|
| 336 |
if "api_key" in str(e).lower():
|
| 337 |
-
error_message = "Error: Invalid or missing OpenAI API key.
|
| 338 |
elif "quota" in str(e).lower():
|
| 339 |
-
error_message = "Error: API quota exceeded.
|
| 340 |
elif "rate" in str(e).lower():
|
| 341 |
-
error_message = "Error: Rate limit exceeded.
|
| 342 |
yield error_message
|
| 343 |
|
| 344 |
def chat_fn(message, history, model_name, max_tokens, temperature, top_p):
|
| 345 |
-
"""
|
| 346 |
-
Single function that handles the entire chat interaction.
|
| 347 |
-
This is the proper way to handle chatbot interfaces in Gradio.
|
| 348 |
-
"""
|
| 349 |
if not message.strip():
|
| 350 |
return history
|
| 351 |
|
| 352 |
-
# Add user message to history
|
| 353 |
history.append([message, ""])
|
| 354 |
|
| 355 |
-
# Generate response
|
| 356 |
for response in respond(message, history[:-1], model_name, max_tokens, temperature, top_p):
|
| 357 |
history[-1][1] = response
|
| 358 |
yield history
|
|
@@ -361,7 +327,7 @@ def clear_history() -> tuple:
|
|
| 361 |
"""Clear the conversation history."""
|
| 362 |
return [], ""
|
| 363 |
|
| 364 |
-
# Create the Gradio interface
|
| 365 |
with gr.Blocks(
|
| 366 |
title="π AI Research Paper Chatbot",
|
| 367 |
theme=gr.themes.Soft(),
|
|
@@ -376,21 +342,18 @@ with gr.Blocks(
|
|
| 376 |
"""
|
| 377 |
# π AI Research Paper Chatbot
|
| 378 |
|
| 379 |
-
Chat with an AI assistant
|
| 380 |
|
| 381 |
**Features:**
|
| 382 |
-
- π
|
| 383 |
-
- π¬
|
| 384 |
-
- π
|
| 385 |
- ποΈ Multiple model selection
|
| 386 |
-
- βοΈ Customizable parameters
|
| 387 |
-
- π¨ Modern, responsive UI
|
| 388 |
"""
|
| 389 |
)
|
| 390 |
|
| 391 |
with gr.Row():
|
| 392 |
with gr.Column(scale=3):
|
| 393 |
-
# Chat interface
|
| 394 |
chatbot = gr.Chatbot(
|
| 395 |
height=500,
|
| 396 |
show_label=False,
|
|
@@ -409,7 +372,6 @@ with gr.Blocks(
|
|
| 409 |
clear_btn = gr.Button("Clear", variant="secondary", scale=1)
|
| 410 |
|
| 411 |
with gr.Column(scale=1):
|
| 412 |
-
# Control panel
|
| 413 |
gr.Markdown("### βοΈ Settings")
|
| 414 |
|
| 415 |
model_dropdown = gr.Dropdown(
|
|
@@ -434,7 +396,7 @@ with gr.Blocks(
|
|
| 434 |
value=0.7,
|
| 435 |
step=0.1,
|
| 436 |
label="Temperature",
|
| 437 |
-
info="Creativity level
|
| 438 |
)
|
| 439 |
|
| 440 |
top_p_slider = gr.Slider(
|
|
@@ -446,22 +408,20 @@ with gr.Blocks(
|
|
| 446 |
info="Response diversity"
|
| 447 |
)
|
| 448 |
|
| 449 |
-
# Example messages
|
| 450 |
gr.Markdown("### π‘ Examples")
|
| 451 |
-
example_btn1 = gr.Button("
|
| 452 |
-
example_btn2 = gr.Button("
|
| 453 |
-
example_btn3 = gr.Button("
|
| 454 |
-
example_btn4 = gr.Button("
|
| 455 |
-
example_btn5 = gr.Button("What's the conclusion of the pig disease paper?", size="sm")
|
| 456 |
|
| 457 |
-
#
|
| 458 |
msg.submit(
|
| 459 |
chat_fn,
|
| 460 |
[msg, chatbot, model_dropdown, max_tokens_slider, temperature_slider, top_p_slider],
|
| 461 |
[chatbot],
|
| 462 |
show_progress=True
|
| 463 |
).then(
|
| 464 |
-
lambda: "",
|
| 465 |
outputs=[msg]
|
| 466 |
)
|
| 467 |
|
|
@@ -471,37 +431,26 @@ with gr.Blocks(
|
|
| 471 |
[chatbot],
|
| 472 |
show_progress=True
|
| 473 |
).then(
|
| 474 |
-
lambda: "",
|
| 475 |
outputs=[msg]
|
| 476 |
)
|
| 477 |
|
| 478 |
clear_btn.click(clear_history, outputs=[chatbot, msg])
|
| 479 |
|
| 480 |
-
# Example
|
| 481 |
-
example_btn1.click(lambda: "
|
| 482 |
-
example_btn2.click(lambda: "
|
| 483 |
-
example_btn3.click(lambda: "
|
| 484 |
-
example_btn4.click(lambda: "
|
| 485 |
-
example_btn5.click(lambda: "What's the conclusion of the pig disease paper?", outputs=msg)
|
| 486 |
|
| 487 |
if __name__ == "__main__":
|
| 488 |
-
# Check if API key is set
|
| 489 |
if not os.getenv("OPENAI_API_KEY"):
|
| 490 |
print("β οΈ Warning: OPENAI_API_KEY environment variable not set!")
|
| 491 |
-
print("Please set your OpenAI API key as an environment variable.")
|
| 492 |
-
print("For Hugging Face Spaces: Add OPENAI_API_KEY as a repository secret")
|
| 493 |
-
print("For local development: Create a .env file with OPENAI_API_KEY=your_key")
|
| 494 |
-
print("\nTo get an API key:")
|
| 495 |
-
print("1. Visit https://platform.openai.com/api-keys")
|
| 496 |
-
print("2. Sign in or create an account")
|
| 497 |
-
print("3. Generate a new API key")
|
| 498 |
-
print("4. Add it to your .env file or Hugging Face Spaces secrets")
|
| 499 |
|
| 500 |
-
# Launch with proper configuration for Hugging Face Spaces
|
| 501 |
demo.launch(
|
| 502 |
server_name="0.0.0.0",
|
| 503 |
server_port=7860,
|
| 504 |
-
share=False,
|
| 505 |
show_error=True,
|
| 506 |
quiet=False
|
| 507 |
)
|
|
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
import re
|
| 5 |
+
from typing import Iterator, Dict, Any, List, Optional
|
| 6 |
from openai import OpenAI
|
| 7 |
from openai.types.chat import ChatCompletionChunk
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
# Load abstracts content once at startup
|
| 10 |
def load_abstracts_content():
|
|
|
|
| 15 |
except FileNotFoundError:
|
| 16 |
return "Abstracts database not found."
|
| 17 |
|
| 18 |
+
# Load abstracts content globally
|
| 19 |
+
ABSTRACTS_CONTENT = load_abstracts_content()
|
| 20 |
+
|
| 21 |
+
# Get API key with better error handling
|
| 22 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
| 23 |
+
if not api_key:
|
| 24 |
+
print("β οΈ Warning: OPENAI_API_KEY environment variable not set!")
|
| 25 |
+
client = None
|
| 26 |
+
else:
|
| 27 |
+
client = OpenAI(
|
| 28 |
+
api_key=api_key,
|
| 29 |
+
timeout=60.0,
|
| 30 |
+
max_retries=3
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
# Available models
|
| 34 |
+
AVAILABLE_MODELS = {
|
| 35 |
+
"GPT-4o-mini": "gpt-4o-mini",
|
| 36 |
+
"GPT-4o": "gpt-4o",
|
| 37 |
+
"GPT-3.5 Turbo": "gpt-3.5-turbo"
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
# Define the tool for fetching papers
|
| 41 |
+
FETCH_PAPERS_TOOL = {
|
| 42 |
+
"type": "function",
|
| 43 |
+
"function": {
|
| 44 |
+
"name": "fetch_papers",
|
| 45 |
+
"description": "Fetch full text content of research papers by their filenames. Use this when you need detailed information, full text, conclusions, methodology, or specific quotes from papers.",
|
| 46 |
+
"parameters": {
|
| 47 |
+
"type": "object",
|
| 48 |
+
"properties": {
|
| 49 |
+
"filenames": {
|
| 50 |
+
"type": "array",
|
| 51 |
+
"items": {
|
| 52 |
+
"type": "string"
|
| 53 |
+
},
|
| 54 |
+
"description": "List of paper filenames to fetch (e.g., ['The Labor Market Effects of Generativ.txt', 'AI Companions Reduce Loneliness.txt'])"
|
| 55 |
+
}
|
| 56 |
+
},
|
| 57 |
+
"required": ["filenames"]
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
def fetch_papers(filenames: List[str]) -> Dict[str, str]:
|
| 63 |
+
"""
|
| 64 |
+
Fetch full paper texts by filenames.
|
| 65 |
+
Returns a dictionary mapping filename to content.
|
| 66 |
+
"""
|
| 67 |
papers = {}
|
| 68 |
papers_dir = "Papers"
|
| 69 |
|
| 70 |
if not os.path.exists(papers_dir):
|
| 71 |
+
return {"error": "Papers directory not found"}
|
| 72 |
|
| 73 |
+
for filename in filenames:
|
| 74 |
+
# Ensure .txt extension
|
| 75 |
+
if not filename.endswith('.txt'):
|
| 76 |
+
filename += '.txt'
|
| 77 |
+
|
| 78 |
+
filepath = os.path.join(papers_dir, filename)
|
| 79 |
+
|
| 80 |
+
if os.path.exists(filepath):
|
| 81 |
try:
|
| 82 |
with open(filepath, "r", encoding="utf-8") as f:
|
| 83 |
+
papers[filename] = f.read()
|
|
|
|
|
|
|
|
|
|
| 84 |
except Exception as e:
|
| 85 |
+
papers[filename] = f"Error loading paper: {str(e)}"
|
| 86 |
+
else:
|
| 87 |
+
papers[filename] = f"Paper not found: {filename}"
|
| 88 |
|
| 89 |
return papers
|
| 90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
def extract_conclusion_from_paper(content: str) -> str:
|
| 92 |
+
"""Extract the conclusion section from a paper's content."""
|
|
|
|
|
|
|
|
|
|
| 93 |
conclusion_patterns = [
|
| 94 |
"conclusion and future works",
|
| 95 |
"conclusion and future work",
|
|
|
|
| 102 |
lines = content.split('\n')
|
| 103 |
conclusion_start = -1
|
| 104 |
|
|
|
|
| 105 |
for i, line in enumerate(lines):
|
| 106 |
line_lower = line.lower().strip()
|
| 107 |
if any(pattern in line_lower for pattern in conclusion_patterns):
|
|
|
|
| 108 |
if (line.isupper() or
|
| 109 |
line.strip().endswith(':') or
|
| 110 |
len(line.strip()) < 100 or
|
|
|
|
| 113 |
break
|
| 114 |
|
| 115 |
if conclusion_start != -1:
|
|
|
|
| 116 |
conclusion_lines = []
|
| 117 |
for line in lines[conclusion_start:]:
|
| 118 |
line_stripped = line.strip()
|
|
|
|
| 119 |
if (line_stripped.lower().startswith('acknowledgments') or
|
| 120 |
line_stripped.lower().startswith('references') or
|
| 121 |
line_stripped.startswith('--- Page')):
|
| 122 |
break
|
| 123 |
conclusion_lines.append(line)
|
| 124 |
|
| 125 |
+
return '\n'.join(conclusion_lines)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
# Fallback: return the last 1000 characters
|
| 128 |
return content[-1000:] if len(content) > 1000 else content
|
| 129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
def truncate_conversation_history(messages: list, max_tokens: int = 8000) -> list:
|
| 131 |
+
"""Truncate conversation history to stay within token limits."""
|
| 132 |
+
if len(messages) <= 3:
|
|
|
|
|
|
|
|
|
|
| 133 |
return messages
|
| 134 |
|
|
|
|
| 135 |
system_message = messages[0]
|
| 136 |
conversation_messages = messages[1:]
|
| 137 |
|
| 138 |
+
while len(conversation_messages) > 6:
|
|
|
|
| 139 |
conversation_messages = conversation_messages[2:]
|
| 140 |
|
| 141 |
return [system_message] + conversation_messages
|
|
|
|
| 149 |
top_p: float,
|
| 150 |
) -> Iterator[str]:
|
| 151 |
"""
|
| 152 |
+
Generate a response using OpenAI's models with function calling.
|
|
|
|
| 153 |
"""
|
| 154 |
if not client:
|
| 155 |
+
yield "β Error: OpenAI API key not configured."
|
| 156 |
return
|
| 157 |
|
| 158 |
if not message.strip():
|
| 159 |
yield "Please enter a message to start the conversation."
|
| 160 |
return
|
| 161 |
|
| 162 |
+
# Initialize messages with a concise system prompt
|
| 163 |
+
system_prompt = f"""You are an AI chatbot designed to help users explore and analyze AI research papers.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
+
You have access to:
|
| 166 |
+
1. An abstracts database with summaries of research papers
|
| 167 |
+
2. A tool to fetch full paper texts when needed
|
| 168 |
|
| 169 |
+
ABSTRACTS DATABASE:
|
| 170 |
{ABSTRACTS_CONTENT}
|
| 171 |
|
| 172 |
+
INSTRUCTIONS:
|
| 173 |
+
- Answer questions using the abstracts when possible
|
| 174 |
+
- Use the fetch_papers tool when users ask for:
|
| 175 |
+
- Full papers or complete papers
|
| 176 |
+
- Specific details not in abstracts
|
| 177 |
+
- Conclusions, methodology, or quotes
|
| 178 |
+
- Any information requiring the full text
|
| 179 |
+
- When fetching papers, use the exact filename from the abstracts table
|
| 180 |
+
- Provide accurate, detailed responses based on the actual paper content"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
|
| 182 |
messages = [{"role": "system", "content": system_prompt}]
|
| 183 |
|
| 184 |
+
# Add conversation history
|
| 185 |
for user_msg, assistant_msg in history:
|
| 186 |
if user_msg and user_msg.strip():
|
| 187 |
messages.append({"role": "user", "content": user_msg.strip()})
|
|
|
|
| 191 |
# Add current user message
|
| 192 |
messages.append({"role": "user", "content": message.strip()})
|
| 193 |
|
| 194 |
+
# Truncate if needed
|
| 195 |
messages = truncate_conversation_history(messages)
|
| 196 |
|
| 197 |
try:
|
|
|
|
| 198 |
model = AVAILABLE_MODELS.get(model_name, "gpt-4o-mini")
|
| 199 |
|
| 200 |
+
# Initial response with tool support
|
| 201 |
response = client.chat.completions.create(
|
| 202 |
model=model,
|
| 203 |
messages=messages,
|
| 204 |
max_tokens=max_tokens,
|
| 205 |
temperature=temperature,
|
| 206 |
top_p=top_p,
|
| 207 |
+
tools=[FETCH_PAPERS_TOOL],
|
| 208 |
+
tool_choice="auto",
|
| 209 |
+
stream=True
|
|
|
|
| 210 |
)
|
| 211 |
|
| 212 |
+
# Collect the response and handle tool calls
|
| 213 |
+
full_response = ""
|
| 214 |
+
tool_calls = []
|
| 215 |
+
current_tool_call = None
|
| 216 |
+
|
| 217 |
for chunk in response:
|
| 218 |
+
if hasattr(chunk.choices[0], 'delta'):
|
| 219 |
+
delta = chunk.choices[0].delta
|
| 220 |
+
|
| 221 |
+
# Handle regular content
|
| 222 |
+
if delta.content is not None:
|
| 223 |
+
full_response += delta.content
|
| 224 |
+
yield full_response
|
| 225 |
+
|
| 226 |
+
# Handle tool calls
|
| 227 |
+
if delta.tool_calls:
|
| 228 |
+
for tool_call_chunk in delta.tool_calls:
|
| 229 |
+
if tool_call_chunk.id:
|
| 230 |
+
# New tool call
|
| 231 |
+
if current_tool_call:
|
| 232 |
+
tool_calls.append(current_tool_call)
|
| 233 |
+
current_tool_call = {
|
| 234 |
+
"id": tool_call_chunk.id,
|
| 235 |
+
"type": "function",
|
| 236 |
+
"function": {
|
| 237 |
+
"name": tool_call_chunk.function.name if tool_call_chunk.function else "",
|
| 238 |
+
"arguments": ""
|
| 239 |
+
}
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
if current_tool_call and tool_call_chunk.function:
|
| 243 |
+
if tool_call_chunk.function.arguments:
|
| 244 |
+
current_tool_call["function"]["arguments"] += tool_call_chunk.function.arguments
|
| 245 |
+
|
| 246 |
+
# Add final tool call if exists
|
| 247 |
+
if current_tool_call:
|
| 248 |
+
tool_calls.append(current_tool_call)
|
| 249 |
+
|
| 250 |
+
# Process tool calls if any
|
| 251 |
+
if tool_calls:
|
| 252 |
+
# Add the assistant's message with tool calls
|
| 253 |
+
messages.append({
|
| 254 |
+
"role": "assistant",
|
| 255 |
+
"content": full_response if full_response else None,
|
| 256 |
+
"tool_calls": tool_calls
|
| 257 |
+
})
|
| 258 |
+
|
| 259 |
+
# Execute tool calls
|
| 260 |
+
for tool_call in tool_calls:
|
| 261 |
+
function_name = tool_call["function"]["name"]
|
| 262 |
+
|
| 263 |
+
if function_name == "fetch_papers":
|
| 264 |
+
try:
|
| 265 |
+
# Parse arguments
|
| 266 |
+
arguments = json.loads(tool_call["function"]["arguments"])
|
| 267 |
+
filenames = arguments.get("filenames", [])
|
| 268 |
+
|
| 269 |
+
# Fetch papers
|
| 270 |
+
papers_content = fetch_papers(filenames)
|
| 271 |
+
|
| 272 |
+
# Add tool response to messages
|
| 273 |
+
tool_response = {
|
| 274 |
+
"role": "tool",
|
| 275 |
+
"tool_call_id": tool_call["id"],
|
| 276 |
+
"content": json.dumps(papers_content)
|
| 277 |
+
}
|
| 278 |
+
messages.append(tool_response)
|
| 279 |
+
|
| 280 |
+
except Exception as e:
|
| 281 |
+
tool_response = {
|
| 282 |
+
"role": "tool",
|
| 283 |
+
"tool_call_id": tool_call["id"],
|
| 284 |
+
"content": f"Error: {str(e)}"
|
| 285 |
+
}
|
| 286 |
+
messages.append(tool_response)
|
| 287 |
+
|
| 288 |
+
# Get final response with tool results
|
| 289 |
+
final_response = client.chat.completions.create(
|
| 290 |
+
model=model,
|
| 291 |
+
messages=messages,
|
| 292 |
+
max_tokens=max_tokens,
|
| 293 |
+
temperature=temperature,
|
| 294 |
+
top_p=top_p,
|
| 295 |
+
stream=True
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
# Stream the final response
|
| 299 |
+
final_text = ""
|
| 300 |
+
for chunk in final_response:
|
| 301 |
+
if hasattr(chunk.choices[0], 'delta') and chunk.choices[0].delta.content is not None:
|
| 302 |
+
final_text += chunk.choices[0].delta.content
|
| 303 |
+
yield full_response + "\n\n" + final_text if full_response else final_text
|
| 304 |
|
| 305 |
except Exception as e:
|
| 306 |
error_message = f"Error: {str(e)}"
|
| 307 |
if "api_key" in str(e).lower():
|
| 308 |
+
error_message = "Error: Invalid or missing OpenAI API key."
|
| 309 |
elif "quota" in str(e).lower():
|
| 310 |
+
error_message = "Error: API quota exceeded."
|
| 311 |
elif "rate" in str(e).lower():
|
| 312 |
+
error_message = "Error: Rate limit exceeded."
|
| 313 |
yield error_message
|
| 314 |
|
| 315 |
def chat_fn(message, history, model_name, max_tokens, temperature, top_p):
|
| 316 |
+
"""Handle the entire chat interaction."""
|
|
|
|
|
|
|
|
|
|
| 317 |
if not message.strip():
|
| 318 |
return history
|
| 319 |
|
|
|
|
| 320 |
history.append([message, ""])
|
| 321 |
|
|
|
|
| 322 |
for response in respond(message, history[:-1], model_name, max_tokens, temperature, top_p):
|
| 323 |
history[-1][1] = response
|
| 324 |
yield history
|
|
|
|
| 327 |
"""Clear the conversation history."""
|
| 328 |
return [], ""
|
| 329 |
|
| 330 |
+
# Create the Gradio interface
|
| 331 |
with gr.Blocks(
|
| 332 |
title="π AI Research Paper Chatbot",
|
| 333 |
theme=gr.themes.Soft(),
|
|
|
|
| 342 |
"""
|
| 343 |
# π AI Research Paper Chatbot
|
| 344 |
|
| 345 |
+
Chat with an AI assistant that can intelligently retrieve and analyze research papers.
|
| 346 |
|
| 347 |
**Features:**
|
| 348 |
+
- π Smart paper retrieval using function calling
|
| 349 |
+
- π¬ Contextual conversation memory
|
| 350 |
+
- π Efficient token usage
|
| 351 |
- ποΈ Multiple model selection
|
|
|
|
|
|
|
| 352 |
"""
|
| 353 |
)
|
| 354 |
|
| 355 |
with gr.Row():
|
| 356 |
with gr.Column(scale=3):
|
|
|
|
| 357 |
chatbot = gr.Chatbot(
|
| 358 |
height=500,
|
| 359 |
show_label=False,
|
|
|
|
| 372 |
clear_btn = gr.Button("Clear", variant="secondary", scale=1)
|
| 373 |
|
| 374 |
with gr.Column(scale=1):
|
|
|
|
| 375 |
gr.Markdown("### βοΈ Settings")
|
| 376 |
|
| 377 |
model_dropdown = gr.Dropdown(
|
|
|
|
| 396 |
value=0.7,
|
| 397 |
step=0.1,
|
| 398 |
label="Temperature",
|
| 399 |
+
info="Creativity level"
|
| 400 |
)
|
| 401 |
|
| 402 |
top_p_slider = gr.Slider(
|
|
|
|
| 408 |
info="Response diversity"
|
| 409 |
)
|
| 410 |
|
|
|
|
| 411 |
gr.Markdown("### π‘ Examples")
|
| 412 |
+
example_btn1 = gr.Button("What papers discuss AI's impact on employment?", size="sm")
|
| 413 |
+
example_btn2 = gr.Button("Show me the full paper about AI companions", size="sm")
|
| 414 |
+
example_btn3 = gr.Button("What's the conclusion of the pig disease detection paper?", size="sm")
|
| 415 |
+
example_btn4 = gr.Button("Compare findings on AI in education", size="sm")
|
|
|
|
| 416 |
|
| 417 |
+
# Event handlers
|
| 418 |
msg.submit(
|
| 419 |
chat_fn,
|
| 420 |
[msg, chatbot, model_dropdown, max_tokens_slider, temperature_slider, top_p_slider],
|
| 421 |
[chatbot],
|
| 422 |
show_progress=True
|
| 423 |
).then(
|
| 424 |
+
lambda: "",
|
| 425 |
outputs=[msg]
|
| 426 |
)
|
| 427 |
|
|
|
|
| 431 |
[chatbot],
|
| 432 |
show_progress=True
|
| 433 |
).then(
|
| 434 |
+
lambda: "",
|
| 435 |
outputs=[msg]
|
| 436 |
)
|
| 437 |
|
| 438 |
clear_btn.click(clear_history, outputs=[chatbot, msg])
|
| 439 |
|
| 440 |
+
# Example handlers
|
| 441 |
+
example_btn1.click(lambda: "What papers discuss AI's impact on employment?", outputs=msg)
|
| 442 |
+
example_btn2.click(lambda: "Show me the full paper about AI companions", outputs=msg)
|
| 443 |
+
example_btn3.click(lambda: "What's the conclusion of the pig disease detection paper?", outputs=msg)
|
| 444 |
+
example_btn4.click(lambda: "Compare findings on AI in education", outputs=msg)
|
|
|
|
| 445 |
|
| 446 |
if __name__ == "__main__":
|
|
|
|
| 447 |
if not os.getenv("OPENAI_API_KEY"):
|
| 448 |
print("β οΈ Warning: OPENAI_API_KEY environment variable not set!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
|
|
|
|
| 450 |
demo.launch(
|
| 451 |
server_name="0.0.0.0",
|
| 452 |
server_port=7860,
|
| 453 |
+
share=False,
|
| 454 |
show_error=True,
|
| 455 |
quiet=False
|
| 456 |
)
|
requirements.txt
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
openai>=1.98.0
|
| 2 |
gradio==4.44.0
|
| 3 |
-
python-dotenv>=1.0.0
|
| 4 |
pydantic==2.10.6
|
|
|
|
| 1 |
openai>=1.98.0
|
| 2 |
gradio==4.44.0
|
|
|
|
| 3 |
pydantic==2.10.6
|