Guiyom commited on
Commit
e1e6f2f
·
verified ·
1 Parent(s): da70a42

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -140
app.py CHANGED
@@ -3,26 +3,17 @@ from openai import OpenAI
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.DEBUG,
13
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
14
- handlers=[
15
- logging.FileHandler(f'raindrop_search_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'),
16
- logging.StreamHandler()
17
- ]
18
- )
19
- logger = logging.getLogger('RaindropSearchBot')
20
 
21
  OPENAI_API_KEY = os.getenv('openaikey')
22
  RAINDROP_TOKEN = os.getenv('raindroptoken')
23
 
24
  if not OPENAI_API_KEY or not RAINDROP_TOKEN:
25
- logger.error("Missing environment variables")
26
  raise EnvironmentError(
27
  "Missing required environment variables. Please ensure 'openaikey' and 'raindroptoken' are set."
28
  )
@@ -32,195 +23,142 @@ class RaindropSearchBot:
32
  self.openai_api_key = OPENAI_API_KEY
33
  self.raindrop_api_token = RAINDROP_TOKEN
34
  self.client = OpenAI(api_key=self.openai_api_key)
35
- logger.info("RaindropSearchBot initialized")
36
 
37
  def generate_search_query(self, user_request: str) -> str:
38
  """Convert user request to a tailored search query using OpenAI."""
39
- logger.info(f"Generating search query for request: {user_request}")
40
-
41
- prompt = f"""
42
- Convert this request into a simple search query for finding bookmarks about this topic.
43
- Use quotes for exact phrases and OR between alternatives.
44
- Only return the search query itself, no explanations.
45
- Keep it concise and focused.
46
 
47
- Request: {user_request}
48
- """
49
 
50
  try:
51
  response = self.client.chat.completions.create(
52
  model="gpt-4o-mini",
53
  messages=[{"role": "user", "content": prompt}],
54
- temperature=0.3, # Lower temperature for more focused results
55
- max_tokens=100 # Reduced tokens since we only need the query
56
  )
57
- search_query = response.choices[0].message.content.strip()
58
- logger.info(f"Generated search query: {search_query}")
59
- return search_query
60
  except Exception as e:
61
- logger.error(f"Error generating search query: {str(e)}")
62
- # Fallback to a simple search if query generation fails
63
  return user_request
64
 
65
  def search_raindrop(self, search_query: str) -> List[Dict]:
66
  """Search Raindrop.io with the generated query."""
67
- logger.info(f"Original search query: {search_query}")
 
 
68
 
69
- # Extract the actual search terms from between the backticks
70
- try:
71
- if "```" in search_query:
72
- search_terms = search_query.split("```")[1].strip()
73
- else:
74
- search_terms = search_query.strip()
75
-
76
- # Clean up the search terms
77
- search_terms = search_terms.replace('\n', ' ').strip()
78
- logger.info(f"Cleaned search terms: {search_terms}")
79
- except Exception as e:
80
- logger.error(f"Error processing search query: {e}")
81
- search_terms = search_query
82
 
83
- headers = {
84
- "Authorization": f"Bearer {self.raindrop_api_token}",
85
- "Content-Type": "application/json"
 
 
 
 
 
 
 
86
  }
87
 
88
- # Test the API connection first
89
  try:
90
- test_response = requests.get(
91
- "https://api.raindrop.io/rest/v1/user",
92
- headers=headers
93
- )
94
- logger.info(f"API test response status: {test_response.status_code}")
95
- if test_response.status_code != 200:
96
- logger.error(f"API test failed: {test_response.text}")
97
- return []
98
-
99
- # Search parameters
100
- params = {
101
- "search": search_terms,
102
- "page": 0,
103
- "perpage": 50,
104
- "sort": "-created"
105
- }
106
-
107
- logger.info(f"Making request to Raindrop API with params: {params}")
108
-
109
- # Make the search request
110
  response = requests.get(
111
- "https://api.raindrop.io/rest/v1/raindrops/0",
112
  headers=headers,
113
  params=params
114
  )
115
 
116
- logger.info(f"Search response status code: {response.status_code}")
117
 
118
  if response.status_code == 200:
119
  data = response.json()
120
  items = data.get("items", [])
121
  logger.info(f"Found {len(items)} results")
122
- logger.debug(f"Response data: {json.dumps(data, indent=2)}")
123
  return items
124
  else:
125
- logger.error(f"Search failed with status {response.status_code}: {response.text}")
126
  return []
127
 
128
  except Exception as e:
129
- logger.error(f"Error during Raindrop search: {str(e)}", exc_info=True)
130
  return []
131
 
132
- def generate_summary(self, item: Dict) -> str:
133
- logger.info(f"Generating summary for item: {item.get('title', 'No Title')}")
134
- content = f"""
135
- Title: {item.get('title', 'No Title')}
136
- Description: {item.get('excerpt', '')}
137
- Tags: {', '.join(item.get('tags', []))}
138
- Type: {item.get('type', 'unknown')}
139
- """
140
-
141
- prompt = f"""
142
- Please provide a brief, informative summary of this bookmarked content:
143
- {content}
144
- Keep the summary concise but include key points and relevance.
145
- """
146
-
147
- try:
148
- response = self.client.chat.completions.create(
149
- model="gpt-4o-mini",
150
- messages=[{"role": "user", "content": prompt}],
151
- temperature=0.3,
152
- max_tokens=150
153
- )
154
- summary = response.choices[0].message.content
155
- logger.info(f"Generated summary: {summary}")
156
- return summary
157
- except Exception as e:
158
- logger.error(f"Error generating summary: {str(e)}")
159
- return "Summary generation failed"
160
-
161
  def format_results(self, results: List[Dict]) -> str:
162
- logger.info(f"Formatting {len(results)} results")
163
-
164
  if not results:
165
- logger.warning("No results to format")
166
- return "No results found."
167
 
168
  formatted_output = "🔍 Search Results:\n\n"
169
  for idx, item in enumerate(results, 1):
170
- logger.debug(f"Formatting item {idx}: {item.get('title', 'No Title')}")
171
-
172
  formatted_output += f"{idx}. {item.get('title', 'No Title')}\n"
173
  formatted_output += f" Link: {item.get('link', 'No Link')}\n"
174
 
 
175
  if item.get('tags'):
176
  formatted_output += f" Tags: {', '.join(item['tags'])}\n"
 
 
 
 
 
 
 
 
 
 
177
 
178
- formatted_output += f" Type: {item.get('type', 'unknown')} | Created: {item.get('created', 'unknown')}\n"
179
-
180
- summary = self.generate_summary(item)
181
- formatted_output += f" Summary: {summary}\n\n"
182
-
183
- logger.info("Results formatting completed")
184
  return formatted_output
185
-
186
  def process_request(self, user_request: str) -> str:
187
- logger.info(f"Processing new request: {user_request}")
188
  try:
 
 
 
189
  search_query = self.generate_search_query(user_request)
190
  logger.info(f"Using search query: {search_query}")
191
 
 
192
  results = self.search_raindrop(search_query)
193
  logger.info(f"Found {len(results)} results")
194
 
195
- formatted_results = self.format_results(results)
196
- logger.info("Request processing completed")
197
- return formatted_results
198
 
199
  except Exception as e:
200
- logger.error(f"Error processing request: {str(e)}", exc_info=True)
201
- return f"An error occurred: {str(e)}\nPlease check the logs for more details."
202
 
203
  # Initialize the bot
204
  bot = RaindropSearchBot()
205
 
206
  # Create Gradio interface
207
  def chatbot_interface(user_input: str) -> str:
208
- logger.info(f"New interface request received: {user_input}")
209
- result = bot.process_request(user_input)
210
- logger.info("Interface request completed")
211
- return result
212
 
213
  # Define and launch the Gradio interface
214
  with gr.Blocks(title="Raindrop.io Link Search Assistant", theme=gr.themes.Soft()) as demo:
215
  gr.Markdown("""
216
  # 🔍 Raindrop.io Link Search Assistant
217
- Enter your search request in natural language, and I'll find and summarize relevant bookmarked links from your Raindrop.io collection.
218
  """)
219
 
220
  with gr.Row():
221
  input_text = gr.Textbox(
222
  label="What would you like to search for?",
223
- placeholder="Ex: Give me the list of all the links related to blockchain in Hong Kong",
224
  lines=2
225
  )
226
 
@@ -239,20 +177,7 @@ with gr.Blocks(title="Raindrop.io Link Search Assistant", theme=gr.themes.Soft()
239
  inputs=input_text,
240
  outputs=output_text
241
  )
242
-
243
- gr.Markdown("""
244
- ### How to use:
245
- 1. Enter your search request in natural language
246
- 2. Click the Search button
247
- 3. View the results and summaries from your Raindrop.io bookmarks
248
-
249
- The assistant will:
250
- - Convert your request into an optimized search query
251
- - Search across all your collections
252
- - Generate summaries for each result
253
- - Present the findings with relevant metadata
254
- """)
255
 
 
256
  if __name__ == "__main__":
257
- logger.info("Starting Gradio interface")
258
  demo.launch(share=True)
 
3
  import requests
4
  import json
5
  import os
 
6
  from typing import Dict, List
7
+ import logging
8
 
9
  # Set up logging
10
+ logging.basicConfig(level=logging.INFO)
11
+ logger = logging.getLogger(__name__)
 
 
 
 
 
 
 
12
 
13
  OPENAI_API_KEY = os.getenv('openaikey')
14
  RAINDROP_TOKEN = os.getenv('raindroptoken')
15
 
16
  if not OPENAI_API_KEY or not RAINDROP_TOKEN:
 
17
  raise EnvironmentError(
18
  "Missing required environment variables. Please ensure 'openaikey' and 'raindroptoken' are set."
19
  )
 
23
  self.openai_api_key = OPENAI_API_KEY
24
  self.raindrop_api_token = RAINDROP_TOKEN
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 a tailored search query using OpenAI."""
29
+ prompt = """
30
+ Create a search query for Raindrop.io bookmarks about this topic.
31
+ Use simple keywords and phrases. Don't use quotes or complex operators.
32
+ Only return the search terms themselves.
 
 
 
33
 
34
+ Topic: {}
35
+ """.format(user_request)
36
 
37
  try:
38
  response = self.client.chat.completions.create(
39
  model="gpt-4o-mini",
40
  messages=[{"role": "user", "content": prompt}],
41
+ temperature=0.3,
42
+ max_tokens=50
43
  )
44
+ query = response.choices[0].message.content.strip()
45
+ logger.info(f"Generated query: {query}")
46
+ return query
47
  except Exception as e:
48
+ logger.error(f"Error generating query: {e}")
 
49
  return user_request
50
 
51
  def search_raindrop(self, search_query: str) -> List[Dict]:
52
  """Search Raindrop.io with the generated query."""
53
+ headers = {
54
+ "Authorization": f"Bearer {self.raindrop_api_token}"
55
+ }
56
 
57
+ # First get all collections
58
+ collections_response = requests.get(
59
+ "https://api.raindrop.io/rest/v1/collections",
60
+ headers=headers
61
+ )
 
 
 
 
 
 
 
 
62
 
63
+ if collections_response.status_code != 200:
64
+ logger.error(f"Failed to get collections: {collections_response.text}")
65
+ return []
66
+
67
+ # Search parameters
68
+ params = {
69
+ "search": search_query,
70
+ "perpage": 50, # Increased from 10
71
+ "sort": "-created",
72
+ "page": 0
73
  }
74
 
 
75
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  response = requests.get(
77
+ "https://api.raindrop.io/rest/v1/raindrops/0", # 0 means search all collections
78
  headers=headers,
79
  params=params
80
  )
81
 
82
+ logger.info(f"Search API response status: {response.status_code}")
83
 
84
  if response.status_code == 200:
85
  data = response.json()
86
  items = data.get("items", [])
87
  logger.info(f"Found {len(items)} results")
 
88
  return items
89
  else:
90
+ logger.error(f"Search failed: {response.text}")
91
  return []
92
 
93
  except Exception as e:
94
+ logger.error(f"Error during search: {e}")
95
  return []
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  def format_results(self, results: List[Dict]) -> str:
98
+ """Format the search results into a readable string."""
 
99
  if not results:
100
+ return "No results found. Try modifying your search terms."
 
101
 
102
  formatted_output = "🔍 Search Results:\n\n"
103
  for idx, item in enumerate(results, 1):
104
+ # Title and link
 
105
  formatted_output += f"{idx}. {item.get('title', 'No Title')}\n"
106
  formatted_output += f" Link: {item.get('link', 'No Link')}\n"
107
 
108
+ # Tags
109
  if item.get('tags'):
110
  formatted_output += f" Tags: {', '.join(item['tags'])}\n"
111
+
112
+ # Description/excerpt if available
113
+ if item.get('excerpt'):
114
+ formatted_output += f" Description: {item['excerpt'][:200]}...\n"
115
+
116
+ # Created date
117
+ if item.get('created'):
118
+ formatted_output += f" Created: {item['created'][:10]}\n"
119
+
120
+ formatted_output += "\n"
121
 
 
 
 
 
 
 
122
  return formatted_output
123
+
124
  def process_request(self, user_request: str) -> str:
125
+ """Process the user request and return formatted results."""
126
  try:
127
+ logger.info(f"Processing request: {user_request}")
128
+
129
+ # Generate search query
130
  search_query = self.generate_search_query(user_request)
131
  logger.info(f"Using search query: {search_query}")
132
 
133
+ # Search Raindrop.io
134
  results = self.search_raindrop(search_query)
135
  logger.info(f"Found {len(results)} results")
136
 
137
+ # Format and return results
138
+ return self.format_results(results)
 
139
 
140
  except Exception as e:
141
+ logger.error(f"Error processing request: {e}")
142
+ return f"An error occurred: {str(e)}"
143
 
144
  # Initialize the bot
145
  bot = RaindropSearchBot()
146
 
147
  # Create Gradio interface
148
  def chatbot_interface(user_input: str) -> str:
149
+ return bot.process_request(user_input)
 
 
 
150
 
151
  # Define and launch the Gradio interface
152
  with gr.Blocks(title="Raindrop.io Link Search Assistant", theme=gr.themes.Soft()) as demo:
153
  gr.Markdown("""
154
  # 🔍 Raindrop.io Link Search Assistant
155
+ Enter your search request in natural language, and I'll find relevant bookmarked links from your Raindrop.io collection.
156
  """)
157
 
158
  with gr.Row():
159
  input_text = gr.Textbox(
160
  label="What would you like to search for?",
161
+ placeholder="Ex: blockchain technology",
162
  lines=2
163
  )
164
 
 
177
  inputs=input_text,
178
  outputs=output_text
179
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
+ # Launch the interface
182
  if __name__ == "__main__":
 
183
  demo.launch(share=True)