Update app.py
Browse files
app.py
CHANGED
@@ -4,6 +4,7 @@ import requests
|
|
4 |
import json
|
5 |
from typing import List, Dict, Optional, Tuple
|
6 |
import random
|
|
|
7 |
|
8 |
class GifChatBot:
|
9 |
def __init__(self):
|
@@ -13,46 +14,68 @@ class GifChatBot:
|
|
13 |
self.is_initialized = False
|
14 |
self.session = requests.Session()
|
15 |
|
16 |
-
#
|
17 |
-
self.
|
18 |
-
|
19 |
-
|
20 |
|
21 |
-
def
|
22 |
-
"""
|
23 |
try:
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
if not content_length:
|
32 |
-
return False
|
33 |
-
|
34 |
-
file_size = int(content_length)
|
35 |
-
return self.MINIMUM_FILE_SIZE <= file_size <= self.MAXIMUM_FILE_SIZE
|
36 |
-
|
37 |
except Exception as error:
|
38 |
-
|
39 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
|
41 |
def get_gif(self, search_query: str) -> Optional[str]:
|
42 |
-
"""
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
try:
|
45 |
-
# Calculate offset
|
46 |
-
offset =
|
47 |
|
48 |
params = {
|
49 |
'api_key': self.giphy_key,
|
50 |
'q': search_query,
|
51 |
-
'limit':
|
52 |
'offset': offset,
|
53 |
'rating': 'pg-13'
|
54 |
}
|
55 |
|
|
|
|
|
56 |
response = self.session.get(
|
57 |
"https://api.giphy.com/v1/gifs/search",
|
58 |
params=params,
|
@@ -61,32 +84,52 @@ class GifChatBot:
|
|
61 |
|
62 |
if response.status_code == 200:
|
63 |
data = response.json()
|
64 |
-
if data
|
65 |
-
# Shuffle results for variety
|
66 |
-
gifs = list(data["data"])
|
67 |
-
random.shuffle(gifs)
|
68 |
-
|
69 |
-
# Try each GIF until we find one meeting size requirements
|
70 |
-
for gif in gifs:
|
71 |
-
gif_url = gif["images"]["original"]["url"]
|
72 |
-
if self.verify_gif_size(gif_url):
|
73 |
-
print(f"Found valid GIF on attempt {attempt + 1}")
|
74 |
-
return gif_url
|
75 |
-
|
76 |
-
print(f"No valid GIFs found in attempt {attempt + 1}, retrying...")
|
77 |
-
else:
|
78 |
print("No GIFs found in search")
|
79 |
break
|
80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
except Exception as error:
|
82 |
-
print(f"Error in attempt {
|
83 |
-
|
84 |
|
85 |
# If we get here, try trending as last resort
|
|
|
86 |
return self._get_trending_gif()
|
87 |
|
88 |
def _get_trending_gif(self) -> Optional[str]:
|
89 |
-
"""Get a trending GIF
|
|
|
|
|
90 |
try:
|
91 |
params = {
|
92 |
'api_key': self.giphy_key,
|
@@ -94,6 +137,8 @@ class GifChatBot:
|
|
94 |
'rating': 'pg-13'
|
95 |
}
|
96 |
|
|
|
|
|
97 |
response = self.session.get(
|
98 |
"https://api.giphy.com/v1/gifs/trending",
|
99 |
params=params,
|
@@ -102,53 +147,39 @@ class GifChatBot:
|
|
102 |
|
103 |
if response.status_code == 200:
|
104 |
data = response.json()
|
105 |
-
if data
|
106 |
-
|
107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
|
109 |
-
|
110 |
-
|
111 |
-
if self.verify_gif_size(gif_url):
|
112 |
-
print("Found valid trending GIF")
|
113 |
-
return gif_url
|
114 |
-
|
115 |
except Exception as error:
|
116 |
print(f"Trending GIF error: {error}")
|
|
|
|
|
117 |
return None
|
118 |
|
119 |
-
def setup_keys(self, openai_key: str, giphy_key: str) -> str:
|
120 |
-
"""Initialize API clients with user's keys"""
|
121 |
-
try:
|
122 |
-
self.openai_client = OpenAI(api_key=openai_key)
|
123 |
-
self.giphy_key = giphy_key
|
124 |
-
self._test_giphy_key()
|
125 |
-
self._test_openai_key()
|
126 |
-
self.is_initialized = True
|
127 |
-
return "✅ Setup successful! Let's chat!"
|
128 |
-
except Exception as error:
|
129 |
-
self.is_initialized = False
|
130 |
-
return f"❌ Error setting up: {str(error)}"
|
131 |
-
|
132 |
-
def _test_giphy_key(self):
|
133 |
-
"""Test if GIPHY key is valid"""
|
134 |
-
response = self.session.get(
|
135 |
-
"https://api.giphy.com/v1/gifs/trending",
|
136 |
-
params={"api_key": self.giphy_key, "limit": 1}
|
137 |
-
)
|
138 |
-
if response.status_code != 200:
|
139 |
-
raise Exception("Invalid GIPHY API key")
|
140 |
-
|
141 |
-
def _test_openai_key(self):
|
142 |
-
"""Test if OpenAI key is valid"""
|
143 |
-
try:
|
144 |
-
self.openai_client.chat.completions.create(
|
145 |
-
model="gpt-4o-mini",
|
146 |
-
messages=[{"role": "user", "content": "test"}],
|
147 |
-
max_tokens=5
|
148 |
-
)
|
149 |
-
except Exception:
|
150 |
-
raise Exception("Invalid OpenAI API key")
|
151 |
-
|
152 |
def reset_chat(self) -> Tuple[List[Dict[str, str]], str]:
|
153 |
"""Reset the chat history"""
|
154 |
self.chat_history = []
|
@@ -167,7 +198,7 @@ class GifChatBot:
|
|
167 |
return message, history, ""
|
168 |
|
169 |
try:
|
170 |
-
# System message
|
171 |
system_message = """You are a supportive, empathetic friend who uses GIFs naturally in conversation.
|
172 |
When using GIFs, keep search terms simple and contextual:
|
173 |
|
@@ -179,10 +210,11 @@ class GifChatBot:
|
|
179 |
|
180 |
Keep your responses:
|
181 |
1. Empathetic and natural
|
182 |
-
2. Context-aware (reference previous messages)
|
183 |
-
3. Use GIFs that match the
|
184 |
|
185 |
-
Use 0-1 GIFs per message unless the moment really calls for more.
|
|
|
186 |
|
187 |
# Prepare conversation history
|
188 |
messages = [{"role": "system", "content": system_message}]
|
@@ -202,6 +234,7 @@ class GifChatBot:
|
|
202 |
ai_message = response.choices[0].message.content
|
203 |
final_response = ""
|
204 |
|
|
|
205 |
parts = ai_message.split("[GIF:")
|
206 |
final_response += parts[0]
|
207 |
|
@@ -209,19 +242,23 @@ class GifChatBot:
|
|
209 |
gif_desc_end = part.find("]")
|
210 |
if gif_desc_end != -1:
|
211 |
gif_desc = part[:gif_desc_end].strip()
|
212 |
-
print(f"
|
213 |
gif_url = self.get_gif(gif_desc)
|
214 |
if gif_url:
|
215 |
final_response += f"\n\n"
|
216 |
-
print(f"
|
|
|
|
|
217 |
final_response += part[gif_desc_end + 1:]
|
218 |
|
|
|
219 |
history.append(self.format_message("user", message))
|
220 |
history.append(self.format_message("assistant", final_response))
|
221 |
return "", history, ""
|
222 |
|
223 |
except Exception as error:
|
224 |
error_message = f"Oops! Something went wrong: {str(error)}"
|
|
|
225 |
return message, history, error_message
|
226 |
|
227 |
def create_interface():
|
@@ -258,7 +295,7 @@ def create_interface():
|
|
258 |
label="Chat",
|
259 |
bubble_full_width=False,
|
260 |
height=450,
|
261 |
-
type="messages"
|
262 |
)
|
263 |
|
264 |
with gr.Row():
|
@@ -298,6 +335,8 @@ def create_interface():
|
|
298 |
- 💭 The conversation is context-aware
|
299 |
- 🎯 GIFs are chosen to match the emotion
|
300 |
- 🔄 Use 'Clear Chat' to start fresh
|
|
|
|
|
301 |
""")
|
302 |
|
303 |
return interface
|
|
|
4 |
import json
|
5 |
from typing import List, Dict, Optional, Tuple
|
6 |
import random
|
7 |
+
import time
|
8 |
|
9 |
class GifChatBot:
|
10 |
def __init__(self):
|
|
|
14 |
self.is_initialized = False
|
15 |
self.session = requests.Session()
|
16 |
|
17 |
+
# Configure session for better performance
|
18 |
+
self.session.headers.update({
|
19 |
+
'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'
|
20 |
+
})
|
21 |
|
22 |
+
def setup_keys(self, openai_key: str, giphy_key: str) -> str:
|
23 |
+
"""Initialize API clients with user's keys"""
|
24 |
try:
|
25 |
+
self.openai_client = OpenAI(api_key=openai_key)
|
26 |
+
self.giphy_key = giphy_key
|
27 |
+
# Test both keys
|
28 |
+
self._test_giphy_key()
|
29 |
+
self._test_openai_key()
|
30 |
+
self.is_initialized = True
|
31 |
+
return "✅ Setup successful! Let's chat!"
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
except Exception as error:
|
33 |
+
self.is_initialized = False
|
34 |
+
return f"❌ Error setting up: {str(error)}"
|
35 |
+
|
36 |
+
def _test_giphy_key(self):
|
37 |
+
"""Test if GIPHY key is valid"""
|
38 |
+
response = self.session.get(
|
39 |
+
"https://api.giphy.com/v1/gifs/trending",
|
40 |
+
params={"api_key": self.giphy_key, "limit": 1}
|
41 |
+
)
|
42 |
+
if response.status_code != 200:
|
43 |
+
raise Exception("Invalid GIPHY API key")
|
44 |
+
|
45 |
+
def _test_openai_key(self):
|
46 |
+
"""Test if OpenAI key is valid"""
|
47 |
+
try:
|
48 |
+
self.openai_client.chat.completions.create(
|
49 |
+
model="gpt-4o-mini",
|
50 |
+
messages=[{"role": "user", "content": "test"}],
|
51 |
+
max_tokens=5
|
52 |
+
)
|
53 |
+
except Exception:
|
54 |
+
raise Exception("Invalid OpenAI API key")
|
55 |
|
56 |
def get_gif(self, search_query: str) -> Optional[str]:
|
57 |
+
"""Search for a GIF with strict size requirements"""
|
58 |
+
total_attempts = 0
|
59 |
+
max_total_attempts = 3 # Maximum pagination attempts
|
60 |
+
minimum_size_bytes = 500000 # 500KB in bytes
|
61 |
+
|
62 |
+
print(f"\nSearching for GIF: {search_query}")
|
63 |
+
|
64 |
+
while total_attempts < max_total_attempts:
|
65 |
try:
|
66 |
+
# Calculate offset for pagination
|
67 |
+
offset = total_attempts * 25
|
68 |
|
69 |
params = {
|
70 |
'api_key': self.giphy_key,
|
71 |
'q': search_query,
|
72 |
+
'limit': 25,
|
73 |
'offset': offset,
|
74 |
'rating': 'pg-13'
|
75 |
}
|
76 |
|
77 |
+
print(f"Attempt {total_attempts + 1} with offset {offset}")
|
78 |
+
|
79 |
response = self.session.get(
|
80 |
"https://api.giphy.com/v1/gifs/search",
|
81 |
params=params,
|
|
|
84 |
|
85 |
if response.status_code == 200:
|
86 |
data = response.json()
|
87 |
+
if not data.get("data"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
print("No GIFs found in search")
|
89 |
break
|
90 |
+
|
91 |
+
print(f"Found {len(data['data'])} GIFs, filtering by size...")
|
92 |
+
|
93 |
+
# Filter GIFs by size first
|
94 |
+
valid_gifs = []
|
95 |
+
for gif in data["data"]:
|
96 |
+
try:
|
97 |
+
# Get size from original image data
|
98 |
+
size_str = gif["images"]["original"]["size"]
|
99 |
+
if size_str and size_str.isdigit():
|
100 |
+
size_bytes = int(size_str)
|
101 |
+
if size_bytes >= minimum_size_bytes:
|
102 |
+
valid_gifs.append(gif)
|
103 |
+
print(f"Found valid GIF: {size_bytes / 1024:.2f}KB")
|
104 |
+
except (KeyError, ValueError) as e:
|
105 |
+
print(f"Size parsing error for GIF: {e}")
|
106 |
+
continue
|
107 |
+
|
108 |
+
if valid_gifs:
|
109 |
+
# Randomly select from valid GIFs
|
110 |
+
chosen_gif = random.choice(valid_gifs)
|
111 |
+
url = chosen_gif["images"]["original"]["url"]
|
112 |
+
size = int(chosen_gif["images"]["original"]["size"])
|
113 |
+
print(f"Selected GIF URL: {url}")
|
114 |
+
print(f"Selected GIF size: {size / 1024:.2f}KB")
|
115 |
+
return url
|
116 |
+
|
117 |
+
print(f"No GIFs >= 500KB found in attempt {total_attempts + 1}")
|
118 |
+
|
119 |
+
total_attempts += 1
|
120 |
+
|
121 |
except Exception as error:
|
122 |
+
print(f"Error in attempt {total_attempts + 1}: {error}")
|
123 |
+
total_attempts += 1
|
124 |
|
125 |
# If we get here, try trending as last resort
|
126 |
+
print("No suitable GIFs found in search, trying trending...")
|
127 |
return self._get_trending_gif()
|
128 |
|
129 |
def _get_trending_gif(self) -> Optional[str]:
|
130 |
+
"""Get a trending GIF with size verification"""
|
131 |
+
minimum_size_bytes = 500000 # 500KB in bytes
|
132 |
+
|
133 |
try:
|
134 |
params = {
|
135 |
'api_key': self.giphy_key,
|
|
|
137 |
'rating': 'pg-13'
|
138 |
}
|
139 |
|
140 |
+
print("Fetching trending GIFs...")
|
141 |
+
|
142 |
response = self.session.get(
|
143 |
"https://api.giphy.com/v1/gifs/trending",
|
144 |
params=params,
|
|
|
147 |
|
148 |
if response.status_code == 200:
|
149 |
data = response.json()
|
150 |
+
if data.get("data"):
|
151 |
+
print(f"Found {len(data['data'])} trending GIFs, filtering by size...")
|
152 |
+
|
153 |
+
# Filter by size first
|
154 |
+
valid_gifs = []
|
155 |
+
for gif in data["data"]:
|
156 |
+
try:
|
157 |
+
size_str = gif["images"]["original"]["size"]
|
158 |
+
if size_str and size_str.isdigit():
|
159 |
+
size_bytes = int(size_str)
|
160 |
+
if size_bytes >= minimum_size_bytes:
|
161 |
+
valid_gifs.append(gif)
|
162 |
+
print(f"Found valid trending GIF: {size_bytes / 1024:.2f}KB")
|
163 |
+
except (KeyError, ValueError) as e:
|
164 |
+
print(f"Size parsing error for trending GIF: {e}")
|
165 |
+
continue
|
166 |
+
|
167 |
+
if valid_gifs:
|
168 |
+
chosen_gif = random.choice(valid_gifs)
|
169 |
+
url = chosen_gif["images"]["original"]["url"]
|
170 |
+
size = int(chosen_gif["images"]["original"]["size"])
|
171 |
+
print(f"Selected trending GIF URL: {url}")
|
172 |
+
print(f"Selected trending GIF size: {size / 1024:.2f}KB")
|
173 |
+
return url
|
174 |
|
175 |
+
print("No valid trending GIFs found")
|
176 |
+
|
|
|
|
|
|
|
|
|
177 |
except Exception as error:
|
178 |
print(f"Trending GIF error: {error}")
|
179 |
+
|
180 |
+
print("Failed to find any suitable GIFs")
|
181 |
return None
|
182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
def reset_chat(self) -> Tuple[List[Dict[str, str]], str]:
|
184 |
"""Reset the chat history"""
|
185 |
self.chat_history = []
|
|
|
198 |
return message, history, ""
|
199 |
|
200 |
try:
|
201 |
+
# System message for the AI
|
202 |
system_message = """You are a supportive, empathetic friend who uses GIFs naturally in conversation.
|
203 |
When using GIFs, keep search terms simple and contextual:
|
204 |
|
|
|
210 |
|
211 |
Keep your responses:
|
212 |
1. Empathetic and natural
|
213 |
+
2. Context-aware (reference previous messages when relevant)
|
214 |
+
3. Use GIFs that match the emotional context
|
215 |
|
216 |
+
Use 0-1 GIFs per message unless the moment really calls for more.
|
217 |
+
Keep search terms simple and universal for better GIF matching."""
|
218 |
|
219 |
# Prepare conversation history
|
220 |
messages = [{"role": "system", "content": system_message}]
|
|
|
234 |
ai_message = response.choices[0].message.content
|
235 |
final_response = ""
|
236 |
|
237 |
+
# Split by GIF markers and process
|
238 |
parts = ai_message.split("[GIF:")
|
239 |
final_response += parts[0]
|
240 |
|
|
|
242 |
gif_desc_end = part.find("]")
|
243 |
if gif_desc_end != -1:
|
244 |
gif_desc = part[:gif_desc_end].strip()
|
245 |
+
print(f"\nProcessing GIF request: {gif_desc}")
|
246 |
gif_url = self.get_gif(gif_desc)
|
247 |
if gif_url:
|
248 |
final_response += f"\n\n"
|
249 |
+
print(f"Successfully added GIF: {gif_url}")
|
250 |
+
else:
|
251 |
+
print("Failed to find suitable GIF")
|
252 |
final_response += part[gif_desc_end + 1:]
|
253 |
|
254 |
+
# Update history with new messages
|
255 |
history.append(self.format_message("user", message))
|
256 |
history.append(self.format_message("assistant", final_response))
|
257 |
return "", history, ""
|
258 |
|
259 |
except Exception as error:
|
260 |
error_message = f"Oops! Something went wrong: {str(error)}"
|
261 |
+
print(f"Chat error: {error}")
|
262 |
return message, history, error_message
|
263 |
|
264 |
def create_interface():
|
|
|
295 |
label="Chat",
|
296 |
bubble_full_width=False,
|
297 |
height=450,
|
298 |
+
type="messages" # Use new message format
|
299 |
)
|
300 |
|
301 |
with gr.Row():
|
|
|
335 |
- 💭 The conversation is context-aware
|
336 |
- 🎯 GIFs are chosen to match the emotion
|
337 |
- 🔄 Use 'Clear Chat' to start fresh
|
338 |
+
|
339 |
+
Note: GIFs are carefully validated to ensure quality (minimum 500KB size).
|
340 |
""")
|
341 |
|
342 |
return interface
|