Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -3,35 +3,46 @@ from openai import OpenAI
|
|
3 |
import requests
|
4 |
import json
|
5 |
import os
|
6 |
-
from typing import Dict, List
|
7 |
import logging
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
OPENAI_API_KEY = os.getenv('openaikey')
|
12 |
-
RAINDROP_TOKEN = os.getenv('raindroptoken')
|
13 |
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
)
|
|
|
|
|
18 |
|
19 |
class RaindropSearchBot:
|
20 |
def __init__(self):
|
21 |
-
self.openai_api_key =
|
22 |
-
self.raindrop_api_token =
|
|
|
|
|
|
|
|
|
23 |
self.client = OpenAI(api_key=self.openai_api_key)
|
24 |
-
|
25 |
def generate_search_query(self, user_request: str) -> str:
|
26 |
-
"""Convert user request to
|
27 |
-
|
28 |
-
Create a search query for Raindrop.io bookmarks about this topic.
|
29 |
-
Use simple keywords and phrases. Don't use quotes or complex operators.
|
30 |
-
Only return the search terms themselves.
|
31 |
|
32 |
-
|
33 |
-
|
|
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
try:
|
36 |
response = self.client.chat.completions.create(
|
37 |
model="gpt-4o-mini",
|
@@ -39,129 +50,132 @@ class RaindropSearchBot:
|
|
39 |
temperature=0.3,
|
40 |
max_tokens=50
|
41 |
)
|
42 |
-
|
43 |
-
logger.info(f"Generated query: {
|
44 |
-
return
|
45 |
except Exception as e:
|
46 |
-
logger.error(f"Error generating query: {e}")
|
47 |
return user_request
|
48 |
|
49 |
def search_raindrop(self, search_query: str) -> List[Dict]:
|
50 |
-
"""Search Raindrop.io with
|
|
|
|
|
51 |
headers = {
|
52 |
"Authorization": f"Bearer {self.raindrop_api_token}"
|
53 |
}
|
54 |
|
55 |
-
#
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
|
|
|
|
|
|
63 |
return []
|
64 |
-
|
65 |
-
#
|
66 |
-
params = {
|
67 |
-
"search": search_query,
|
68 |
-
"perpage": 50, # Increased from 10
|
69 |
-
"sort": "-created",
|
70 |
-
"page": 0
|
71 |
-
}
|
72 |
-
|
73 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
response = requests.get(
|
75 |
-
"https://api.raindrop.io/rest/v1/raindrops/0",
|
76 |
headers=headers,
|
77 |
params=params
|
78 |
)
|
79 |
|
80 |
-
logger.info(f"Search API response status: {response.status_code}")
|
81 |
-
|
82 |
if response.status_code == 200:
|
83 |
data = response.json()
|
84 |
items = data.get("items", [])
|
85 |
logger.info(f"Found {len(items)} results")
|
86 |
return items
|
87 |
else:
|
88 |
-
logger.error(f"Search failed: {response.
|
89 |
return []
|
90 |
-
|
91 |
except Exception as e:
|
92 |
-
logger.error(f"
|
93 |
return []
|
94 |
|
95 |
-
def
|
96 |
-
"""
|
97 |
if not results:
|
98 |
-
return "No results found. Try modifying your search terms."
|
99 |
-
|
100 |
-
# Filter out broken links and empty content
|
101 |
-
valid_results = [
|
102 |
-
item for item in results
|
103 |
-
if item.get('link') and (item.get('excerpt') or item.get('description'))
|
104 |
-
]
|
105 |
-
|
106 |
-
if not valid_results:
|
107 |
-
return "Found results but no readable content to analyze. Try different search terms."
|
108 |
|
109 |
-
# Create context for
|
110 |
-
context = "Based on
|
111 |
-
|
112 |
-
|
113 |
-
|
|
|
|
|
|
|
|
|
114 |
|
115 |
-
# Generate AI response
|
116 |
try:
|
117 |
prompt = f"""
|
118 |
-
Based on
|
119 |
-
|
120 |
-
|
|
|
|
|
|
|
|
|
|
|
121 |
|
122 |
-
|
123 |
{context}
|
124 |
"""
|
125 |
-
|
126 |
response = self.client.chat.completions.create(
|
127 |
model="gpt-4o-mini",
|
128 |
messages=[{"role": "user", "content": prompt}],
|
129 |
-
temperature=0.
|
130 |
-
max_tokens=
|
131 |
)
|
132 |
-
|
|
|
|
|
133 |
except Exception as e:
|
134 |
-
logger.error(f"
|
135 |
-
|
136 |
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
#
|
148 |
-
if item.get('
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
return
|
162 |
|
163 |
def process_request(self, user_request: str) -> str:
|
164 |
-
"""Process the user request
|
165 |
try:
|
166 |
logger.info(f"Processing request: {user_request}")
|
167 |
|
@@ -173,31 +187,34 @@ class RaindropSearchBot:
|
|
173 |
results = self.search_raindrop(search_query)
|
174 |
logger.info(f"Found {len(results)} results")
|
175 |
|
176 |
-
#
|
177 |
-
|
|
|
|
|
|
|
178 |
|
179 |
except Exception as e:
|
180 |
logger.error(f"Error processing request: {e}", exc_info=True)
|
181 |
-
return f"An error occurred
|
182 |
|
183 |
-
# Initialize
|
184 |
bot = RaindropSearchBot()
|
185 |
|
186 |
# Create Gradio interface
|
187 |
def chatbot_interface(user_input: str) -> str:
|
188 |
return bot.process_request(user_input)
|
189 |
|
190 |
-
# Define and launch the
|
191 |
-
with gr.Blocks(title="Raindrop.io
|
192 |
gr.Markdown("""
|
193 |
-
# π Raindrop.io
|
194 |
-
Enter your search request in natural language, and I'll find relevant bookmarked
|
195 |
""")
|
196 |
|
197 |
with gr.Row():
|
198 |
input_text = gr.Textbox(
|
199 |
label="What would you like to search for?",
|
200 |
-
placeholder="
|
201 |
lines=2
|
202 |
)
|
203 |
|
@@ -206,7 +223,7 @@ with gr.Blocks(title="Raindrop.io Link Search Assistant", theme=gr.themes.Soft()
|
|
206 |
|
207 |
with gr.Row():
|
208 |
output_text = gr.Textbox(
|
209 |
-
label="
|
210 |
lines=15,
|
211 |
interactive=False
|
212 |
)
|
|
|
3 |
import requests
|
4 |
import json
|
5 |
import os
|
|
|
6 |
import logging
|
7 |
+
from typing import Dict, List
|
8 |
+
from datetime import datetime
|
|
|
|
|
|
|
9 |
|
10 |
+
# Set up logging
|
11 |
+
logging.basicConfig(
|
12 |
+
level=logging.INFO,
|
13 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
14 |
+
)
|
15 |
+
logger = logging.getLogger(__name__)
|
16 |
|
17 |
class RaindropSearchBot:
|
18 |
def __init__(self):
|
19 |
+
self.openai_api_key = os.getenv('openaikey')
|
20 |
+
self.raindrop_api_token = os.getenv('raindroptoken')
|
21 |
+
if not self.openai_api_key or not self.raindrop_api_token:
|
22 |
+
raise EnvironmentError(
|
23 |
+
"Missing required environment variables. Please ensure 'openaikey' and 'raindroptoken' are set."
|
24 |
+
)
|
25 |
self.client = OpenAI(api_key=self.openai_api_key)
|
26 |
+
|
27 |
def generate_search_query(self, user_request: str) -> str:
|
28 |
+
"""Convert user request to optimized search terms."""
|
29 |
+
logger.info(f"Generating search query for: {user_request}")
|
|
|
|
|
|
|
30 |
|
31 |
+
prompt = f"""
|
32 |
+
You are a search expert. Create a search query to find relevant documents about:
|
33 |
+
{user_request}
|
34 |
|
35 |
+
Guidelines:
|
36 |
+
- Focus on key concepts and synonyms
|
37 |
+
- Use simple keywords that would appear in titles or descriptions
|
38 |
+
- Avoid complex operators or special characters
|
39 |
+
- Return only the search terms, no explanation
|
40 |
+
- Include alternative phrasings
|
41 |
+
- Keep it concise (max 3-4 key terms/phrases)
|
42 |
+
|
43 |
+
Return only the search query terms.
|
44 |
+
"""
|
45 |
+
|
46 |
try:
|
47 |
response = self.client.chat.completions.create(
|
48 |
model="gpt-4o-mini",
|
|
|
50 |
temperature=0.3,
|
51 |
max_tokens=50
|
52 |
)
|
53 |
+
search_query = response.choices[0].message.content.strip()
|
54 |
+
logger.info(f"Generated search query: {search_query}")
|
55 |
+
return search_query
|
56 |
except Exception as e:
|
57 |
+
logger.error(f"Error generating search query: {e}")
|
58 |
return user_request
|
59 |
|
60 |
def search_raindrop(self, search_query: str) -> List[Dict]:
|
61 |
+
"""Search Raindrop.io with enhanced error handling and logging."""
|
62 |
+
logger.info(f"Searching Raindrop with query: {search_query}")
|
63 |
+
|
64 |
headers = {
|
65 |
"Authorization": f"Bearer {self.raindrop_api_token}"
|
66 |
}
|
67 |
|
68 |
+
# Test API connection first
|
69 |
+
try:
|
70 |
+
test_response = requests.get(
|
71 |
+
"https://api.raindrop.io/rest/v1/user",
|
72 |
+
headers=headers
|
73 |
+
)
|
74 |
+
if test_response.status_code != 200:
|
75 |
+
logger.error(f"API test failed: {test_response.status_code}")
|
76 |
+
return []
|
77 |
+
except Exception as e:
|
78 |
+
logger.error(f"API connection error: {e}")
|
79 |
return []
|
80 |
+
|
81 |
+
# Perform search
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
try:
|
83 |
+
params = {
|
84 |
+
"search": search_query,
|
85 |
+
"perpage": 50,
|
86 |
+
"sort": "-created",
|
87 |
+
"page": 0
|
88 |
+
}
|
89 |
+
|
90 |
response = requests.get(
|
91 |
+
"https://api.raindrop.io/rest/v1/raindrops/0",
|
92 |
headers=headers,
|
93 |
params=params
|
94 |
)
|
95 |
|
|
|
|
|
96 |
if response.status_code == 200:
|
97 |
data = response.json()
|
98 |
items = data.get("items", [])
|
99 |
logger.info(f"Found {len(items)} results")
|
100 |
return items
|
101 |
else:
|
102 |
+
logger.error(f"Search failed: {response.status_code}")
|
103 |
return []
|
|
|
104 |
except Exception as e:
|
105 |
+
logger.error(f"Search error: {e}")
|
106 |
return []
|
107 |
|
108 |
+
def analyze_results(self, results: List[Dict], user_query: str) -> str:
|
109 |
+
"""Generate an analysis of the search results."""
|
110 |
if not results:
|
111 |
+
return "No relevant results found. Try modifying your search terms."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
|
113 |
+
# Create context for analysis
|
114 |
+
context = f"Based on the search query: '{user_query}'\n\n"
|
115 |
+
context += "Analyze these relevant sources:\n\n"
|
116 |
+
|
117 |
+
for item in results:
|
118 |
+
context += f"Title: {item.get('title', 'No title')}\n"
|
119 |
+
if item.get('excerpt'):
|
120 |
+
context += f"Content: {item['excerpt'][:500]}...\n"
|
121 |
+
context += f"Created: {item.get('created', 'No date')}\n\n"
|
122 |
|
|
|
123 |
try:
|
124 |
prompt = f"""
|
125 |
+
Based on these search results, provide a comprehensive analysis of {user_query}.
|
126 |
+
|
127 |
+
Requirements:
|
128 |
+
1. Focus only on information directly related to the query
|
129 |
+
2. Organize the response by key themes or chronologically
|
130 |
+
3. Include only factual information from the sources
|
131 |
+
4. Highlight any significant developments or changes
|
132 |
+
5. Note any gaps in information
|
133 |
|
134 |
+
Context:
|
135 |
{context}
|
136 |
"""
|
137 |
+
|
138 |
response = self.client.chat.completions.create(
|
139 |
model="gpt-4o-mini",
|
140 |
messages=[{"role": "user", "content": prompt}],
|
141 |
+
temperature=0.5,
|
142 |
+
max_tokens=1000
|
143 |
)
|
144 |
+
|
145 |
+
analysis = response.choices[0].message.content
|
146 |
+
return analysis
|
147 |
except Exception as e:
|
148 |
+
logger.error(f"Analysis generation error: {e}")
|
149 |
+
return "Error generating analysis."
|
150 |
|
151 |
+
def format_results(self, results: List[Dict], analysis: str) -> str:
|
152 |
+
"""Format the search results with analysis."""
|
153 |
+
if not results:
|
154 |
+
return "No results found. Try modifying your search terms."
|
155 |
+
|
156 |
+
output = f"{analysis}\n\n"
|
157 |
+
output += "-------\n\n"
|
158 |
+
output += "π Source Links:\n\n"
|
159 |
+
|
160 |
+
for idx, item in enumerate(results, 1):
|
161 |
+
# Only include if there's at least a title or link
|
162 |
+
if item.get('title') or item.get('link'):
|
163 |
+
output += f"{idx}. {item.get('title', 'No Title')}\n"
|
164 |
+
if item.get('link'):
|
165 |
+
output += f" Link: {item['link']}\n"
|
166 |
+
if item.get('tags'):
|
167 |
+
output += f" Tags: {', '.join(item['tags'])}\n"
|
168 |
+
if item.get('excerpt'):
|
169 |
+
output += f" Description: {item['excerpt'][:200]}...\n"
|
170 |
+
if item.get('created'):
|
171 |
+
created_date = item['created'][:10] # Get just the date part
|
172 |
+
output += f" Created: {created_date}\n"
|
173 |
+
output += "\n"
|
174 |
+
|
175 |
+
return output
|
176 |
|
177 |
def process_request(self, user_request: str) -> str:
|
178 |
+
"""Process the user request with enhanced error handling."""
|
179 |
try:
|
180 |
logger.info(f"Processing request: {user_request}")
|
181 |
|
|
|
187 |
results = self.search_raindrop(search_query)
|
188 |
logger.info(f"Found {len(results)} results")
|
189 |
|
190 |
+
# Generate analysis
|
191 |
+
analysis = self.analyze_results(results, user_request)
|
192 |
+
|
193 |
+
# Format and return results
|
194 |
+
return self.format_results(results, analysis)
|
195 |
|
196 |
except Exception as e:
|
197 |
logger.error(f"Error processing request: {e}", exc_info=True)
|
198 |
+
return f"An error occurred while processing your request. Please try again."
|
199 |
|
200 |
+
# Initialize bot
|
201 |
bot = RaindropSearchBot()
|
202 |
|
203 |
# Create Gradio interface
|
204 |
def chatbot_interface(user_input: str) -> str:
|
205 |
return bot.process_request(user_input)
|
206 |
|
207 |
+
# Define and launch the interface
|
208 |
+
with gr.Blocks(title="Raindrop.io Search Assistant", theme=gr.themes.Soft()) as demo:
|
209 |
gr.Markdown("""
|
210 |
+
# π Raindrop.io Search Assistant
|
211 |
+
Enter your search request in natural language, and I'll find and analyze relevant bookmarked information.
|
212 |
""")
|
213 |
|
214 |
with gr.Row():
|
215 |
input_text = gr.Textbox(
|
216 |
label="What would you like to search for?",
|
217 |
+
placeholder="Enter your search query here...",
|
218 |
lines=2
|
219 |
)
|
220 |
|
|
|
223 |
|
224 |
with gr.Row():
|
225 |
output_text = gr.Textbox(
|
226 |
+
label="Analysis and Results",
|
227 |
lines=15,
|
228 |
interactive=False
|
229 |
)
|