jzou19950715 commited on
Commit
26df3c0
Β·
verified Β·
1 Parent(s): 65ab430

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -195
app.py CHANGED
@@ -4,8 +4,6 @@ import requests
4
  import json
5
  from typing import List, Dict, Optional, Tuple
6
  import random
7
- import time
8
- from datetime import datetime
9
 
10
  class GifChatBot:
11
  def __init__(self):
@@ -14,182 +12,106 @@ class GifChatBot:
14
  self.chat_history = []
15
  self.is_initialized = False
16
  self.session = requests.Session()
17
-
18
- # Configure session for better performance
19
- self.session.headers.update({
20
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
21
- })
22
-
23
- # Parameters for GIF quality checking
24
- self.minimum_gif_size = 10000 # 10KB minimum
25
- self.maximum_gif_size = 2000000 # 2MB maximum
26
- self.minimum_dimension = 200
27
- self.maximum_dimension = 1000
28
- self.maximum_frames = 50
29
 
30
- def verify_gif_url(self, gif_url: str) -> bool:
31
- """Verify GIF URL with relaxed validation for GIPHY URLs"""
32
  try:
33
- # Special handling for GIPHY URLs: assume they are valid if they come from giphy.com/media
34
- if "giphy.com/media" in gif_url or "giphy.gif" in gif_url:
35
- return True
36
-
37
- # Use HEAD request with allow_redirects to properly follow URL redirects
38
- head_response = self.session.head(gif_url, timeout=3, allow_redirects=True)
39
- if head_response.status_code != 200:
40
- print(f"HEAD request failed with status {head_response.status_code}")
41
- return False
42
-
43
- content_type = head_response.headers.get('content-type', '').lower()
44
- if not ('gif' in content_type or 'image' in content_type):
45
- print("Invalid content type:", content_type)
46
- return False
47
-
48
- return True
49
-
50
  except Exception as error:
51
- print(f"GIF validation error: {error}")
52
- # Be more lenient if the URL is from GIPHY
53
- return "giphy.com/media" in gif_url
54
-
55
- def _is_good_quality_gif(self, gif: Dict) -> bool:
56
- """Comprehensive quality check for GIFs"""
57
- try:
58
- # Check required attributes
59
- if not gif.get("images") or not gif.get("images").get("original"):
60
- print("Missing image data")
61
- return False
62
-
63
- original = gif.get("images").get("original")
64
-
65
- # Check dimensions
66
- try:
67
- width = int(original.get("width", 0))
68
- height = int(original.get("height", 0))
69
-
70
- if width < self.minimum_dimension or height < self.minimum_dimension:
71
- print("GIF too small:", width, height)
72
- return False
73
- if width > self.maximum_dimension or height > self.maximum_dimension:
74
- print("GIF too large:", width, height)
75
- return False
76
- except (ValueError, TypeError):
77
- print("Invalid dimensions")
78
- return False
79
-
80
- # Check frame count
81
- try:
82
- frames = int(original.get("frames", self.maximum_frames))
83
- if frames > self.maximum_frames:
84
- print("Too many frames:", frames)
85
- return False
86
- except (ValueError, TypeError):
87
- print("Invalid frame count")
88
- return False
89
 
90
- # Check file size
91
- try:
92
- size = int(original.get("size", 0))
93
- if size < self.minimum_gif_size or size > self.maximum_gif_size:
94
- print("Invalid size:", size)
95
- return False
96
- except (ValueError, TypeError):
97
- print("Invalid size data")
98
- return False
99
 
100
- # Check if it's a newer GIF (longer ID generally means newer)
101
- gif_id = gif.get("id", "")
102
- if len(gif_id) < 10:
103
- print("Old GIF (short ID)")
104
- return False
 
 
 
 
 
105
 
106
- # Check trending status
107
- if gif.get("is_trending"):
108
- print("Currently trending")
 
 
109
  return True
110
 
111
- # Check recent trending status
112
- trending_datetime = gif.get("trending_datetime")
113
- if trending_datetime and trending_datetime != "0000-00-00 00:00:00":
114
- try:
115
- trending_date = datetime.strptime(trending_datetime, "%Y-%m-%d %H:%M:%S")
116
- days_ago = (datetime.now() - trending_date).days
117
- if days_ago < 90: # Recently trending
118
- print("Recently trending")
119
- return True
120
- except:
121
- pass
122
 
123
- return True
 
 
 
 
 
124
 
125
- except Exception as error:
126
- print(f"Quality check error: {error}")
127
  return False
128
 
129
  def get_gif(self, search_query: str) -> Optional[str]:
130
- """Search and validate GIPHY GIFs"""
131
  try:
132
- print(f"Searching for GIF: {search_query}")
133
-
134
- # Search parameters
135
- parameters = {
136
  'api_key': self.giphy_key,
137
  'q': search_query,
138
  'limit': 25,
139
  'rating': 'pg-13',
140
- 'bundle': 'messaging_non_clips',
141
- 'sort': 'relevant'
142
  }
143
 
144
  response = self.session.get(
145
  "https://api.giphy.com/v1/gifs/search",
146
- params=parameters,
147
  timeout=5
148
  )
149
 
150
  if response.status_code == 200:
151
  data = response.json()
152
- if data.get("data"):
153
- # Sort by ID length (newer GIFs first)
154
- gifs = sorted(
155
- data["data"],
156
- key=lambda x: len(x.get("id", "")),
157
- reverse=True
158
- )
159
-
160
- print(f"Found {len(gifs)} GIFs, testing quality...")
161
 
162
- # Try each GIF until we find a good one
163
  for gif in gifs:
164
  if not self._is_good_quality_gif(gif):
165
  continue
166
-
167
  gif_url = gif["images"]["original"]["url"]
168
- print(f"Testing GIF URL: {gif_url}")
169
-
170
  if self.verify_gif_url(gif_url):
171
- print("Found good GIF!")
172
  return gif_url
173
- else:
174
- print("GIF validation failed")
175
 
176
- print("No good GIFs found, trying trending...")
177
  return self._get_trending_gif()
178
- else:
179
- print("No GIFs found in search")
180
- else:
181
- print(f"Search failed with status {response.status_code}")
182
-
183
  return None
184
 
185
  except Exception as error:
186
- print(f"GIPHY search error: {error}")
187
  return None
188
 
189
  def _get_trending_gif(self) -> Optional[str]:
190
- """Get a verified trending GIF"""
191
  try:
192
- print("Fetching trending GIF...")
193
  response = self.session.get(
194
  "https://api.giphy.com/v1/gifs/trending",
195
  params={
@@ -203,63 +125,34 @@ class GifChatBot:
203
 
204
  if response.status_code == 200:
205
  data = response.json()
206
- if data.get("data"):
207
  gifs = list(data["data"])
208
  random.shuffle(gifs)
209
 
210
- print(f"Testing {len(gifs)} trending GIFs...")
211
  for gif in gifs:
212
- if not self._is_good_quality_gif(gif):
213
- continue
214
-
215
  gif_url = gif["images"]["original"]["url"]
216
- print(f"Testing trending GIF: {gif_url}")
217
-
218
  if self.verify_gif_url(gif_url):
219
- print("Found good trending GIF!")
220
  return gif_url
221
- else:
222
- print("Trending GIF validation failed")
223
-
224
- print("No good trending GIFs found")
225
- return None
226
 
227
  except Exception as error:
228
  print(f"Error getting trending GIF: {error}")
229
- return None
230
-
231
- def setup_keys(self, openai_key: str, giphy_key: str) -> str:
232
- """Initialize API clients with user's keys"""
233
- try:
234
- self.openai_client = OpenAI(api_key=openai_key)
235
- self.giphy_key = giphy_key
236
- self._test_giphy_key()
237
- self._test_openai_key()
238
- self.is_initialized = True
239
- return "βœ… Setup successful! Let's chat!"
240
- except Exception as error:
241
- self.is_initialized = False
242
- return f"❌ Error setting up: {str(error)}"
243
-
244
- def _test_giphy_key(self):
245
- """Test if GIPHY key is valid"""
246
- response = self.session.get(
247
- "https://api.giphy.com/v1/gifs/trending",
248
- params={"api_key": self.giphy_key, "limit": 1}
249
- )
250
- if response.status_code != 200:
251
- raise Exception("Invalid GIPHY API key")
252
 
253
- def _test_openai_key(self):
254
- """Test if OpenAI key is valid"""
255
  try:
256
- self.openai_client.chat.completions.create(
257
- model="gpt-4o-mini",
258
- messages=[{"role": "user", "content": "test"}],
259
- max_tokens=5
260
- )
261
- except Exception:
262
- raise Exception("Invalid OpenAI API key")
 
 
 
 
 
263
 
264
  def reset_chat(self) -> Tuple[List[Dict[str, str]], str]:
265
  """Reset the chat history"""
@@ -279,20 +172,26 @@ class GifChatBot:
279
  return message, history, ""
280
 
281
  try:
282
- # System message for the AI
283
  system_message = (
284
- "You are a supportive, empathetic friend who uses GIFs naturally and proactively in conversation regardless of "
285
- "whether the user requests it or not. When using GIFs, deduce what type of terms or keywords may be used to search "
286
- "for the GIF and pick a reliable one randomly."
 
 
 
 
 
 
 
 
 
287
  )
288
 
289
- # Prepare conversation history
290
  messages = [{"role": "system", "content": system_message}]
291
  for chat in history:
292
  messages.append({"role": chat["role"], "content": chat["content"]})
293
  messages.append({"role": "user", "content": message})
294
 
295
- # Get AI response
296
  response = self.openai_client.chat.completions.create(
297
  model="gpt-4o-mini",
298
  messages=messages,
@@ -300,7 +199,6 @@ class GifChatBot:
300
  max_tokens=150
301
  )
302
 
303
- # Process response and insert GIFs
304
  ai_message = response.choices[0].message.content
305
  final_response = ""
306
 
@@ -311,23 +209,17 @@ class GifChatBot:
311
  gif_desc_end = part.find("]")
312
  if gif_desc_end != -1:
313
  gif_desc = part[:gif_desc_end].strip()
314
- print(f"Looking for GIF: {gif_desc}")
315
  gif_url = self.get_gif(gif_desc)
316
  if gif_url:
317
  final_response += f"\n![GIF]({gif_url})\n"
318
- print(f"Added GIF: {gif_url}")
319
- else:
320
- print("No suitable GIF found")
321
  final_response += part[gif_desc_end + 1:]
322
 
323
- # Update history with new messages
324
  history.append(self.format_message("user", message))
325
  history.append(self.format_message("assistant", final_response))
326
  return "", history, ""
327
 
328
  except Exception as error:
329
  error_message = f"Oops! Something went wrong: {str(error)}"
330
- print(f"Chat error: {error}")
331
  return message, history, error_message
332
 
333
  def create_interface():
@@ -337,7 +229,7 @@ def create_interface():
337
  with gr.Blocks(theme=gr.themes.Soft()) as interface:
338
  gr.Markdown("""
339
  # 🎭 Friendly Chat Bot with GIFs
340
- Chat with an empathetic AI friend who expresses themselves through GIFs that are verified to work!
341
  Enter your API keys below to start.
342
  """)
343
 
@@ -400,7 +292,10 @@ def create_interface():
400
 
401
  gr.Markdown("""
402
  ### Tips:
403
- - Always check whether the GIF is broken or not before sending. Only non-broken GIFs will be used.
 
 
 
404
  """)
405
 
406
  return interface
 
4
  import json
5
  from typing import List, Dict, Optional, Tuple
6
  import random
 
 
7
 
8
  class GifChatBot:
9
  def __init__(self):
 
12
  self.chat_history = []
13
  self.is_initialized = False
14
  self.session = requests.Session()
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ def setup_keys(self, openai_key: str, giphy_key: str) -> str:
17
+ """Initialize API clients with user's keys"""
18
  try:
19
+ self.openai_client = OpenAI(api_key=openai_key)
20
+ self.giphy_key = giphy_key
21
+ self._test_giphy_key()
22
+ self._test_openai_key()
23
+ self.is_initialized = True
24
+ return "βœ… Setup successful! Let's chat!"
 
 
 
 
 
 
 
 
 
 
 
25
  except Exception as error:
26
+ self.is_initialized = False
27
+ return f"❌ Error setting up: {str(error)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ def _test_giphy_key(self):
30
+ """Test if GIPHY key is valid"""
31
+ response = self.session.get(
32
+ "https://api.giphy.com/v1/gifs/trending",
33
+ params={"api_key": self.giphy_key, "limit": 1}
34
+ )
35
+ if response.status_code != 200:
36
+ raise Exception("Invalid GIPHY API key")
 
37
 
38
+ def _test_openai_key(self):
39
+ """Test if OpenAI key is valid"""
40
+ try:
41
+ self.openai_client.chat.completions.create(
42
+ model="gpt-4o-mini",
43
+ messages=[{"role": "user", "content": "test"}],
44
+ max_tokens=5
45
+ )
46
+ except Exception:
47
+ raise Exception("Invalid OpenAI API key")
48
 
49
+ def verify_gif_url(self, gif_url: str) -> bool:
50
+ """Verify if a GIF URL is accessible"""
51
+ try:
52
+ # Assume URLs from GIPHY are valid.
53
+ if "giphy.com/media" in gif_url or "giphy.gif" in gif_url:
54
  return True
55
 
56
+ # First try a HEAD request with redirects allowed.
57
+ response = self.session.head(gif_url, timeout=3, allow_redirects=True)
58
+ if response.status_code == 200:
59
+ content_type = response.headers.get('content-type', '').lower()
60
+ if 'gif' in content_type or 'image' in content_type:
61
+ return True
 
 
 
 
 
62
 
63
+ # If HEAD did not succeed, try a GET request with streaming.
64
+ response = self.session.get(gif_url, timeout=5, stream=True, allow_redirects=True)
65
+ if response.status_code == 200:
66
+ content_type = response.headers.get('content-type', '').lower()
67
+ if 'gif' in content_type or 'image' in content_type:
68
+ return True
69
 
70
+ return False
71
+ except Exception:
72
  return False
73
 
74
  def get_gif(self, search_query: str) -> Optional[str]:
75
+ """Search GIPHY with validation for working GIFs"""
76
  try:
77
+ params = {
 
 
 
78
  'api_key': self.giphy_key,
79
  'q': search_query,
80
  'limit': 25,
81
  'rating': 'pg-13',
82
+ 'bundle': 'messaging_non_clips'
 
83
  }
84
 
85
  response = self.session.get(
86
  "https://api.giphy.com/v1/gifs/search",
87
+ params=params,
88
  timeout=5
89
  )
90
 
91
  if response.status_code == 200:
92
  data = response.json()
93
+ if data["data"]:
94
+ gifs = list(data["data"])
95
+ random.shuffle(gifs)
 
 
 
 
 
 
96
 
 
97
  for gif in gifs:
98
  if not self._is_good_quality_gif(gif):
99
  continue
100
+
101
  gif_url = gif["images"]["original"]["url"]
 
 
102
  if self.verify_gif_url(gif_url):
 
103
  return gif_url
 
 
104
 
 
105
  return self._get_trending_gif()
 
 
 
 
 
106
  return None
107
 
108
  except Exception as error:
109
+ print(f"GIPHY error: {error}")
110
  return None
111
 
112
  def _get_trending_gif(self) -> Optional[str]:
113
+ """Get a verified trending GIF as fallback"""
114
  try:
 
115
  response = self.session.get(
116
  "https://api.giphy.com/v1/gifs/trending",
117
  params={
 
125
 
126
  if response.status_code == 200:
127
  data = response.json()
128
+ if data["data"]:
129
  gifs = list(data["data"])
130
  random.shuffle(gifs)
131
 
 
132
  for gif in gifs:
 
 
 
133
  gif_url = gif["images"]["original"]["url"]
 
 
134
  if self.verify_gif_url(gif_url):
 
135
  return gif_url
 
 
 
 
 
136
 
137
  except Exception as error:
138
  print(f"Error getting trending GIF: {error}")
139
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
+ def _is_good_quality_gif(self, gif: Dict) -> bool:
142
+ """Check if a GIF meets quality criteria"""
143
  try:
144
+ if int(gif.get("images", {}).get("original", {}).get("frames", 50)) > 50:
145
+ return False
146
+
147
+ if int(gif.get("images", {}).get("original", {}).get("size", 0)) > 2000000:
148
+ return False
149
+
150
+ if not gif.get("images", {}).get("original", {}).get("url"):
151
+ return False
152
+
153
+ return True
154
+ except:
155
+ return False
156
 
157
  def reset_chat(self) -> Tuple[List[Dict[str, str]], str]:
158
  """Reset the chat history"""
 
172
  return message, history, ""
173
 
174
  try:
 
175
  system_message = (
176
+ "You are a supportive, empathetic friend who uses GIFs naturally in conversation. \n"
177
+ "When using GIFs, keep search terms simple and contextual:\n\n"
178
+ "Examples:\n"
179
+ "- User feeling hungry -> [GIF: hungry]\n"
180
+ "- User feeling sad -> [GIF: comforting hug]\n"
181
+ "- User celebrating -> [GIF: celebration]\n"
182
+ "- User confused -> [GIF: confused]\n\n"
183
+ "Keep your responses:\n"
184
+ "1. Empathetic and natural\n"
185
+ "2. Context-aware\n"
186
+ "3. Use GIFs that match the emotion\n\n"
187
+ "Use 0-1 GIFs per message unless the moment really calls for more."
188
  )
189
 
 
190
  messages = [{"role": "system", "content": system_message}]
191
  for chat in history:
192
  messages.append({"role": chat["role"], "content": chat["content"]})
193
  messages.append({"role": "user", "content": message})
194
 
 
195
  response = self.openai_client.chat.completions.create(
196
  model="gpt-4o-mini",
197
  messages=messages,
 
199
  max_tokens=150
200
  )
201
 
 
202
  ai_message = response.choices[0].message.content
203
  final_response = ""
204
 
 
209
  gif_desc_end = part.find("]")
210
  if gif_desc_end != -1:
211
  gif_desc = part[:gif_desc_end].strip()
 
212
  gif_url = self.get_gif(gif_desc)
213
  if gif_url:
214
  final_response += f"\n![GIF]({gif_url})\n"
 
 
 
215
  final_response += part[gif_desc_end + 1:]
216
 
 
217
  history.append(self.format_message("user", message))
218
  history.append(self.format_message("assistant", final_response))
219
  return "", history, ""
220
 
221
  except Exception as error:
222
  error_message = f"Oops! Something went wrong: {str(error)}"
 
223
  return message, history, error_message
224
 
225
  def create_interface():
 
229
  with gr.Blocks(theme=gr.themes.Soft()) as interface:
230
  gr.Markdown("""
231
  # 🎭 Friendly Chat Bot with GIFs
232
+ Chat with an empathetic AI friend who expresses themselves through GIFs!
233
  Enter your API keys below to start.
234
  """)
235
 
 
292
 
293
  gr.Markdown("""
294
  ### Tips:
295
+ - 🀝 Share how you're feeling - the AI responds empathetically
296
+ - πŸ’­ The conversation is context-aware
297
+ - 🎯 GIFs are chosen to match the emotion
298
+ - πŸ”„ Use 'Clear Chat' to start fresh
299
  """)
300
 
301
  return interface