Guiyom commited on
Commit
5818aaa
Β·
verified Β·
1 Parent(s): 3e0283c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -112
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
- logging.basicConfig(level=logging.INFO)
9
- logger = logging.getLogger(__name__)
10
-
11
- OPENAI_API_KEY = os.getenv('openaikey')
12
- RAINDROP_TOKEN = os.getenv('raindroptoken')
13
 
14
- if not OPENAI_API_KEY or not RAINDROP_TOKEN:
15
- raise EnvironmentError(
16
- "Missing required environment variables. Please ensure 'openaikey' and 'raindroptoken' are set."
17
- )
 
 
18
 
19
  class RaindropSearchBot:
20
  def __init__(self):
21
- self.openai_api_key = OPENAI_API_KEY
22
- self.raindrop_api_token = RAINDROP_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 a tailored search query using OpenAI."""
27
- prompt = """
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
- Topic: {}
33
- """.format(user_request)
 
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
- query = response.choices[0].message.content.strip()
43
- logger.info(f"Generated query: {query}")
44
- return query
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 the generated query."""
 
 
51
  headers = {
52
  "Authorization": f"Bearer {self.raindrop_api_token}"
53
  }
54
 
55
- # First get all collections
56
- collections_response = requests.get(
57
- "https://api.raindrop.io/rest/v1/collections",
58
- headers=headers
59
- )
60
-
61
- if collections_response.status_code != 200:
62
- logger.error(f"Failed to get collections: {collections_response.text}")
 
 
 
63
  return []
64
-
65
- # Search parameters
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", # 0 means search all collections
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.text}")
89
  return []
90
-
91
  except Exception as e:
92
- logger.error(f"Error during search: {e}")
93
  return []
94
 
95
- def format_results(self, results: List[Dict]) -> str:
96
- """Format the search results and generate an AI response."""
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 AI response
110
- context = "Based on these sources:\n\n"
111
- for item in valid_results:
112
- context += f"Title: {item.get('title', 'No Title')}\n"
113
- context += f"Content: {item.get('excerpt') or item.get('description', 'No content')}\n\n"
 
 
 
 
114
 
115
- # Generate AI response
116
  try:
117
  prompt = f"""
118
- Based on the provided sources, create a comprehensive response about the topic.
119
- Synthesize the information and present key insights.
120
- Keep the response concise and informative.
 
 
 
 
 
121
 
122
- Sources:
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.7,
130
- max_tokens=500
131
  )
132
- ai_response = response.choices[0].message.content
 
 
133
  except Exception as e:
134
- logger.error(f"Error generating AI response: {e}")
135
- ai_response = "Unable to generate summary from the search results."
136
 
137
- # Format the final output
138
- formatted_output = f"{ai_response}\n\n"
139
- formatted_output += "-------\n\n"
140
- formatted_output += "πŸ” Source Links:\n\n"
141
-
142
- for idx, item in enumerate(valid_results, 1):
143
- # Title and link
144
- formatted_output += f"{idx}. {item.get('title', 'No Title')}\n"
145
- formatted_output += f" Link: {item.get('link', 'No Link')}\n"
146
-
147
- # Tags
148
- if item.get('tags'):
149
- formatted_output += f" Tags: {', '.join(item['tags'])}\n"
150
-
151
- # Description/excerpt if available
152
- if item.get('excerpt'):
153
- formatted_output += f" Description: {item['excerpt'][:200]}...\n"
154
-
155
- # Created date
156
- if item.get('created'):
157
- formatted_output += f" Created: {item['created'][:10]}\n"
158
-
159
- formatted_output += "\n"
160
-
161
- return formatted_output
162
 
163
  def process_request(self, user_request: str) -> str:
164
- """Process the user request and return formatted results with AI response."""
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
- # Format results and generate AI response
177
- return self.format_results(results)
 
 
 
178
 
179
  except Exception as e:
180
  logger.error(f"Error processing request: {e}", exc_info=True)
181
- return f"An error occurred: {str(e)}"
182
 
183
- # Initialize the bot
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 Gradio interface
191
- with gr.Blocks(title="Raindrop.io Link Search Assistant", theme=gr.themes.Soft()) as demo:
192
  gr.Markdown("""
193
- # πŸ” Raindrop.io Link Search Assistant
194
- Enter your search request in natural language, and I'll find relevant bookmarked links from your Raindrop.io collection.
195
  """)
196
 
197
  with gr.Row():
198
  input_text = gr.Textbox(
199
  label="What would you like to search for?",
200
- placeholder="Ex: blockchain technology",
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="Search Results",
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
  )