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):
|
@@ -11,6 +12,7 @@ class GifChatBot:
|
|
11 |
self.giphy_key = None
|
12 |
self.chat_history = []
|
13 |
self.is_initialized = False
|
|
|
14 |
|
15 |
def setup_keys(self, openai_key: str, giphy_key: str) -> str:
|
16 |
"""Initialize API clients with user's keys"""
|
@@ -25,16 +27,16 @@ class GifChatBot:
|
|
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 =
|
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:
|
@@ -46,78 +48,111 @@ class GifChatBot:
|
|
46 |
except Exception:
|
47 |
raise Exception("Invalid OpenAI API key")
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
def get_gif(self, search_query: str) -> Optional[str]:
|
50 |
-
"""Search GIPHY with
|
51 |
try:
|
52 |
-
# Keep the search query simple and contextual
|
53 |
params = {
|
54 |
'api_key': self.giphy_key,
|
55 |
'q': search_query,
|
56 |
-
'limit':
|
57 |
'rating': 'pg-13',
|
58 |
'bundle': 'messaging_non_clips'
|
59 |
}
|
60 |
|
61 |
-
response =
|
62 |
"https://api.giphy.com/v1/gifs/search",
|
63 |
-
params=params
|
|
|
64 |
)
|
65 |
|
66 |
if response.status_code == 200:
|
67 |
data = response.json()
|
68 |
if data["data"]:
|
69 |
-
#
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
return self._get_trending_gif()
|
80 |
|
81 |
except Exception as error:
|
82 |
print(f"GIPHY error: {error}")
|
83 |
return None
|
84 |
|
85 |
-
def _is_good_quality_gif(self, gif: Dict) -> bool:
|
86 |
-
"""Check if a GIF meets quality criteria"""
|
87 |
-
try:
|
88 |
-
# Avoid very long GIFs
|
89 |
-
if int(gif.get("images", {}).get("original", {}).get("frames", 50)) > 50:
|
90 |
-
return False
|
91 |
-
|
92 |
-
# Check if size is reasonable (under 2MB)
|
93 |
-
if int(gif.get("images", {}).get("original", {}).get("size", 0)) > 2000000:
|
94 |
-
return False
|
95 |
-
|
96 |
-
return True
|
97 |
-
except:
|
98 |
-
return True
|
99 |
-
|
100 |
def _get_trending_gif(self) -> Optional[str]:
|
101 |
-
"""Get a trending GIF as fallback"""
|
102 |
try:
|
103 |
-
response =
|
104 |
"https://api.giphy.com/v1/gifs/trending",
|
105 |
params={
|
106 |
'api_key': self.giphy_key,
|
107 |
'limit': 25,
|
108 |
'rating': 'pg-13',
|
109 |
'bundle': 'messaging_non_clips'
|
110 |
-
}
|
|
|
111 |
)
|
112 |
|
113 |
if response.status_code == 200:
|
114 |
data = response.json()
|
115 |
if data["data"]:
|
116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
except Exception as error:
|
118 |
print(f"Error getting trending GIF: {error}")
|
119 |
return None
|
120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
def reset_chat(self) -> Tuple[str, List[List[str]]]:
|
122 |
"""Reset the chat history"""
|
123 |
self.chat_history = []
|
@@ -132,24 +167,22 @@ class GifChatBot:
|
|
132 |
return message, history, ""
|
133 |
|
134 |
try:
|
135 |
-
# System message emphasizing natural GIF
|
136 |
system_message = """You are a supportive, empathetic friend who uses GIFs naturally in conversation.
|
137 |
-
When using GIFs, keep search terms simple and contextual
|
138 |
|
139 |
-
Examples
|
140 |
-
-
|
141 |
-
-
|
142 |
-
-
|
143 |
-
-
|
144 |
-
- When agreeing strongly: [GIF: absolutely yes]
|
145 |
|
146 |
Keep your responses:
|
147 |
-
1. Empathetic and
|
148 |
-
2. Context-aware
|
149 |
-
3. Use GIFs that match the
|
150 |
|
151 |
-
Use 0-1 GIFs per message unless the
|
152 |
-
The GIF should enhance your message, not replace it."""
|
153 |
|
154 |
# Prepare conversation history
|
155 |
messages = [{"role": "system", "content": system_message}]
|
@@ -192,6 +225,10 @@ class GifChatBot:
|
|
192 |
error_message = f"Oops! Something went wrong: {str(error)}"
|
193 |
return message, history, error_message
|
194 |
|
|
|
|
|
|
|
|
|
195 |
def create_interface():
|
196 |
"""Create the Gradio interface"""
|
197 |
bot = GifChatBot()
|
@@ -262,11 +299,18 @@ def create_interface():
|
|
262 |
|
263 |
gr.Markdown("""
|
264 |
### Tips:
|
265 |
-
- π€ Share how you're feeling - the AI
|
266 |
- π The conversation is context-aware
|
267 |
-
- π― GIFs are chosen to match the
|
268 |
- π Use 'Clear Chat' to start fresh
|
269 |
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
270 |
|
271 |
return interface
|
272 |
|
|
|
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):
|
|
|
12 |
self.giphy_key = None
|
13 |
self.chat_history = []
|
14 |
self.is_initialized = False
|
15 |
+
self.session = requests.Session() # Use session for better performance
|
16 |
|
17 |
def setup_keys(self, openai_key: str, giphy_key: str) -> str:
|
18 |
"""Initialize API clients with user's keys"""
|
|
|
27 |
except Exception as error:
|
28 |
self.is_initialized = False
|
29 |
return f"β Error setting up: {str(error)}"
|
30 |
+
|
31 |
def _test_giphy_key(self):
|
32 |
"""Test if GIPHY key is valid"""
|
33 |
+
response = self.session.get(
|
34 |
"https://api.giphy.com/v1/gifs/trending",
|
35 |
params={"api_key": self.giphy_key, "limit": 1}
|
36 |
)
|
37 |
if response.status_code != 200:
|
38 |
raise Exception("Invalid GIPHY API key")
|
39 |
+
|
40 |
def _test_openai_key(self):
|
41 |
"""Test if OpenAI key is valid"""
|
42 |
try:
|
|
|
48 |
except Exception:
|
49 |
raise Exception("Invalid OpenAI API key")
|
50 |
|
51 |
+
def verify_gif_url(self, gif_url: str) -> bool:
|
52 |
+
"""Verify if a GIF URL is accessible"""
|
53 |
+
try:
|
54 |
+
# Head request is faster than full GET
|
55 |
+
response = self.session.head(gif_url, timeout=3)
|
56 |
+
|
57 |
+
# Check if status is OK and content type is correct
|
58 |
+
if response.status_code == 200:
|
59 |
+
content_type = response.headers.get('content-type', '').lower()
|
60 |
+
return 'gif' in content_type or 'image' in content_type
|
61 |
+
return False
|
62 |
+
except:
|
63 |
+
return False
|
64 |
+
|
65 |
def get_gif(self, search_query: str) -> Optional[str]:
|
66 |
+
"""Search GIPHY with validation for working GIFs"""
|
67 |
try:
|
|
|
68 |
params = {
|
69 |
'api_key': self.giphy_key,
|
70 |
'q': search_query,
|
71 |
+
'limit': 25, # Get more options
|
72 |
'rating': 'pg-13',
|
73 |
'bundle': 'messaging_non_clips'
|
74 |
}
|
75 |
|
76 |
+
response = self.session.get(
|
77 |
"https://api.giphy.com/v1/gifs/search",
|
78 |
+
params=params,
|
79 |
+
timeout=5
|
80 |
)
|
81 |
|
82 |
if response.status_code == 200:
|
83 |
data = response.json()
|
84 |
if data["data"]:
|
85 |
+
# Shuffle results for variety
|
86 |
+
gifs = list(data["data"])
|
87 |
+
random.shuffle(gifs)
|
88 |
+
|
89 |
+
# Try GIFs until we find a good one
|
90 |
+
for gif in gifs:
|
91 |
+
if not self._is_good_quality_gif(gif):
|
92 |
+
continue
|
93 |
+
|
94 |
+
gif_url = gif["images"]["original"]["url"]
|
95 |
+
if self.verify_gif_url(gif_url):
|
96 |
+
return gif_url
|
97 |
|
98 |
+
# If no GIFs passed validation, try trending
|
99 |
+
return self._get_trending_gif()
|
100 |
+
|
101 |
+
return None
|
|
|
102 |
|
103 |
except Exception as error:
|
104 |
print(f"GIPHY error: {error}")
|
105 |
return None
|
106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
def _get_trending_gif(self) -> Optional[str]:
|
108 |
+
"""Get a verified trending GIF as fallback"""
|
109 |
try:
|
110 |
+
response = self.session.get(
|
111 |
"https://api.giphy.com/v1/gifs/trending",
|
112 |
params={
|
113 |
'api_key': self.giphy_key,
|
114 |
'limit': 25,
|
115 |
'rating': 'pg-13',
|
116 |
'bundle': 'messaging_non_clips'
|
117 |
+
},
|
118 |
+
timeout=5
|
119 |
)
|
120 |
|
121 |
if response.status_code == 200:
|
122 |
data = response.json()
|
123 |
if data["data"]:
|
124 |
+
# Try each trending GIF until we find a working one
|
125 |
+
gifs = list(data["data"])
|
126 |
+
random.shuffle(gifs)
|
127 |
+
|
128 |
+
for gif in gifs:
|
129 |
+
gif_url = gif["images"]["original"]["url"]
|
130 |
+
if self.verify_gif_url(gif_url):
|
131 |
+
return gif_url
|
132 |
+
|
133 |
except Exception as error:
|
134 |
print(f"Error getting trending GIF: {error}")
|
135 |
return None
|
136 |
|
137 |
+
def _is_good_quality_gif(self, gif: Dict) -> bool:
|
138 |
+
"""Check if a GIF meets quality criteria"""
|
139 |
+
try:
|
140 |
+
# Skip GIFs that are too long
|
141 |
+
if int(gif.get("images", {}).get("original", {}).get("frames", 50)) > 50:
|
142 |
+
return False
|
143 |
+
|
144 |
+
# Skip GIFs that are too large
|
145 |
+
if int(gif.get("images", {}).get("original", {}).get("size", 0)) > 2000000: # 2MB
|
146 |
+
return False
|
147 |
+
|
148 |
+
# Skip if no image data
|
149 |
+
if not gif.get("images", {}).get("original", {}).get("url"):
|
150 |
+
return False
|
151 |
+
|
152 |
+
return True
|
153 |
+
except:
|
154 |
+
return False
|
155 |
+
|
156 |
def reset_chat(self) -> Tuple[str, List[List[str]]]:
|
157 |
"""Reset the chat history"""
|
158 |
self.chat_history = []
|
|
|
167 |
return message, history, ""
|
168 |
|
169 |
try:
|
170 |
+
# System message emphasizing natural GIF usage
|
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 |
|
174 |
+
Examples:
|
175 |
+
- User feeling hungry -> [GIF: hungry]
|
176 |
+
- User feeling sad -> [GIF: comforting hug]
|
177 |
+
- User celebrating -> [GIF: celebration]
|
178 |
+
- User confused -> [GIF: confused]
|
|
|
179 |
|
180 |
Keep your responses:
|
181 |
+
1. Empathetic and natural
|
182 |
+
2. Context-aware
|
183 |
+
3. Use GIFs that match the emotion
|
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}]
|
|
|
225 |
error_message = f"Oops! Something went wrong: {str(error)}"
|
226 |
return message, history, error_message
|
227 |
|
228 |
+
def cleanup(self):
|
229 |
+
"""Cleanup resources"""
|
230 |
+
self.session.close()
|
231 |
+
|
232 |
def create_interface():
|
233 |
"""Create the Gradio interface"""
|
234 |
bot = GifChatBot()
|
|
|
299 |
|
300 |
gr.Markdown("""
|
301 |
### Tips:
|
302 |
+
- π€ Share how you're feeling - the AI responds empathetically
|
303 |
- π The conversation is context-aware
|
304 |
+
- π― GIFs are chosen to match the emotion
|
305 |
- π Use 'Clear Chat' to start fresh
|
306 |
""")
|
307 |
+
|
308 |
+
# Cleanup on page unload
|
309 |
+
interface.load(lambda: None, [], [], _js="""() => {
|
310 |
+
window.addEventListener('unload', function() {
|
311 |
+
// Cleanup resources
|
312 |
+
})
|
313 |
+
}""")
|
314 |
|
315 |
return interface
|
316 |
|