Athspi commited on
Commit
764eab2
Β·
verified Β·
1 Parent(s): 0683728

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -30
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  from flask import Flask, request, jsonify, send_from_directory, make_response
2
  import google.generativeai as genai
3
  from dotenv import load_dotenv
@@ -12,14 +13,14 @@ import uuid
12
  # Load environment variables
13
  load_dotenv()
14
 
15
- # Directories
16
  AUDIO_FOLDER = os.path.join('static', 'audio')
17
  os.makedirs(AUDIO_FOLDER, exist_ok=True)
18
 
19
  app = Flask(__name__, static_folder='static')
20
  CORS(app)
21
 
22
- # Gemini AI Configuration
23
  system_instruction = """
24
  You are a helpful AI assistant named Athspi. When responding:
25
  1. Never mention "audio" or technical terms
@@ -28,11 +29,12 @@ You are a helpful AI assistant named Athspi. When responding:
28
  3. Keep responses natural and friendly
29
  4. Decide automatically when to include audio based on the content type
30
  5. For stories, always include audio version
31
- 6. If a URL is present in the user's message, respond with:
32
  [SHORTEN]original_url|short_url[/SHORTEN]
33
- Example:
34
- I've shortened your link for convenience:
35
  [SHORTEN]https://example.com|https://spoo.me/abc123[/SHORTEN]
 
 
36
  """
37
 
38
  genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
@@ -42,28 +44,37 @@ model = genai.GenerativeModel('gemini-2.5-flash', system_instruction=system_inst
42
  chat_sessions = {}
43
 
44
  def convert_markdown_to_html(text):
 
45
  html = markdown2.markdown(text, extras=["fenced-code-blocks", "tables"])
46
  html = re.sub(r'<pre><code(.*?)>', r'<pre class="code-block"><code\1>', html)
47
  return html
48
 
49
  def process_response(full_response):
50
- """Extract visible text, audio content, and short links"""
 
51
  audio_match = re.search(r'\[AUDIO\](.*?)\[/AUDIO\]', full_response, re.DOTALL)
52
  audio_content = audio_match.group(1).strip() if audio_match else None
53
  visible_text = re.sub(r'\[/?AUDIO\]', '', full_response)
54
-
 
55
  short_link_match = re.search(r'\[SHORTEN\](.*?)\|([^|]*?)\[/SHORTEN\]', visible_text, re.DOTALL)
56
  if short_link_match:
57
  original_url = short_link_match.group(1).strip()
58
  short_url = short_link_match.group(2).strip()
59
- link_html = f'<p><strong>πŸ”— Original:</strong> <a href="{original_url}" target="_blank">{original_url}</a></p>' \
60
- f'<p><strong>βœ‚οΈ Shortened:</strong> <a href="{short_url}" target="_blank">{short_url}</a></p>'
 
 
 
 
 
 
61
  visible_text = re.sub(r'\[SHORTEN\].*?\[/SHORTEN\]', link_html, visible_text)
62
 
63
  return visible_text, audio_content
64
 
65
  def generate_audio(text):
66
- """Generate audio file from text"""
67
  text = re.sub(r'[^\w\s.,!?\-]', '', text)
68
  filename = f"audio_{uuid.uuid4()}.mp3"
69
  filepath = os.path.join(AUDIO_FOLDER, filename)
@@ -72,37 +83,48 @@ def generate_audio(text):
72
  return filename
73
 
74
  def shorten_url_with_spoo_me(url):
75
- """Shorten URL using spoo.me API and clean output"""
76
  try:
 
77
  clean_url = url.strip()
78
  if not clean_url.startswith(('http://', 'https://')):
79
  clean_url = 'https://' + clean_url
80
 
81
- payload = {"url": clean_url}
 
 
82
  headers = {
83
  "Accept": "application/json",
84
  "Content-Type": "application/x-www-form-urlencoded"
85
  }
86
 
87
- response = requests.post("https://spoo.me/", data=payload, headers=headers, timeout=10)
 
 
 
 
 
88
 
89
  if response.status_code == 200:
90
  try:
 
91
  result = response.json()
92
- original_url = result.get("original_url", "").strip()
93
  short_url = result.get("short_url", "").strip()
94
- if short_url:
95
- print(f"Spoo.me returned: {original_url} β†’ {short_url}")
96
  return short_url
97
  except Exception as json_error:
98
- print("JSON parse error:", json_error)
99
- return None
 
 
 
100
 
101
- print(f"Spoo.me error: {response.status_code} - {response.text}")
102
  return None
103
 
104
  except Exception as e:
105
- print("Request failed:", e)
106
  return None
107
 
108
  @app.route('/chat', methods=['POST'])
@@ -120,19 +142,37 @@ def chat():
120
  chat_sessions[session_id] = model.start_chat(history=[])
121
  chat_session = chat_sessions[session_id]
122
 
123
- # AI response
124
- response = chat_session.send_message(user_message)
125
- full_text = response.text
126
-
127
- # Detect and shorten first URL
128
  url_match = re.search(r'https?://[^\s<>"{}|\\^`\[\]]+', user_message)
 
 
 
129
  if url_match:
130
  original_url = url_match.group(0).strip()
131
  short_url = shorten_url_with_spoo_me(original_url)
132
- if short_url and "[SHORTEN]" not in full_text:
 
 
133
  short_tag = f"[SHORTEN]{original_url}|{short_url}[/SHORTEN]"
134
- full_text += f"\n\n{short_tag}"
135
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  visible_text, audio_content = process_response(full_text)
137
  html_response = convert_markdown_to_html(visible_text)
138
 
@@ -141,12 +181,13 @@ def chat():
141
  "has_audio": False
142
  }
143
 
 
144
  if audio_content:
145
  audio_filename = generate_audio(audio_content)
146
  result["audio_filename"] = audio_filename
147
  result["has_audio"] = True
148
 
149
- # Set session ID in cookie
150
  resp = make_response(jsonify(result))
151
  resp.set_cookie('session_id', session_id, max_age=3600, httponly=True, samesite='Lax')
152
  return resp
@@ -157,6 +198,7 @@ def chat():
157
 
158
  @app.route('/new-chat', methods=['POST'])
159
  def new_chat():
 
160
  session_id = str(uuid.uuid4())
161
  resp = make_response(jsonify({"status": "new chat started"}))
162
  resp.set_cookie('session_id', session_id, max_age=3600, httponly=True, samesite='Lax')
@@ -178,4 +220,4 @@ def serve_static(path):
178
  return send_from_directory('static', path)
179
 
180
  if __name__ == '__main__':
181
- app.run(host="0.0.0.0", port=7860)
 
1
+ # app.py - Flask Backend with AI URL Shortening System
2
  from flask import Flask, request, jsonify, send_from_directory, make_response
3
  import google.generativeai as genai
4
  from dotenv import load_dotenv
 
13
  # Load environment variables
14
  load_dotenv()
15
 
16
+ # Configure paths
17
  AUDIO_FOLDER = os.path.join('static', 'audio')
18
  os.makedirs(AUDIO_FOLDER, exist_ok=True)
19
 
20
  app = Flask(__name__, static_folder='static')
21
  CORS(app)
22
 
23
+ # AI Configuration - Critical for URL detection and formatting
24
  system_instruction = """
25
  You are a helpful AI assistant named Athspi. When responding:
26
  1. Never mention "audio" or technical terms
 
29
  3. Keep responses natural and friendly
30
  4. Decide automatically when to include audio based on the content type
31
  5. For stories, always include audio version
32
+ 6. CRITICAL: When you detect a URL in the user's message, respond with:
33
  [SHORTEN]original_url|short_url[/SHORTEN]
34
+ Example format ONLY:
 
35
  [SHORTEN]https://example.com|https://spoo.me/abc123[/SHORTEN]
36
+ 7. DO NOT add any extra text inside the SHORTEN tags
37
+ 8. ONLY use this format when you have a shortened URL to provide
38
  """
39
 
40
  genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
 
44
  chat_sessions = {}
45
 
46
  def convert_markdown_to_html(text):
47
+ """Convert markdown to HTML with code formatting"""
48
  html = markdown2.markdown(text, extras=["fenced-code-blocks", "tables"])
49
  html = re.sub(r'<pre><code(.*?)>', r'<pre class="code-block"><code\1>', html)
50
  return html
51
 
52
  def process_response(full_response):
53
+ """Extract visible text, audio content, and short links from AI response"""
54
+ # Extract audio content
55
  audio_match = re.search(r'\[AUDIO\](.*?)\[/AUDIO\]', full_response, re.DOTALL)
56
  audio_content = audio_match.group(1).strip() if audio_match else None
57
  visible_text = re.sub(r'\[/?AUDIO\]', '', full_response)
58
+
59
+ # Process short links - CRITICAL PART
60
  short_link_match = re.search(r'\[SHORTEN\](.*?)\|([^|]*?)\[/SHORTEN\]', visible_text, re.DOTALL)
61
  if short_link_match:
62
  original_url = short_link_match.group(1).strip()
63
  short_url = short_link_match.group(2).strip()
64
+
65
+ # Create clean HTML for display
66
+ link_html = f'<div class="link-container">' \
67
+ f'<p><strong>πŸ”— Original:</strong> <a href="{original_url}" target="_blank" rel="noopener noreferrer">{original_url}</a></p>' \
68
+ f'<p><strong>βœ‚οΈ Shortened:</strong> <a href="{short_url}" target="_blank" rel="noopener noreferrer">{short_url}</a></p>' \
69
+ f'</div>'
70
+
71
+ # Replace the SHORTEN tags with our clean HTML
72
  visible_text = re.sub(r'\[SHORTEN\].*?\[/SHORTEN\]', link_html, visible_text)
73
 
74
  return visible_text, audio_content
75
 
76
  def generate_audio(text):
77
+ """Generate audio file from text, cleaning special characters"""
78
  text = re.sub(r'[^\w\s.,!?\-]', '', text)
79
  filename = f"audio_{uuid.uuid4()}.mp3"
80
  filepath = os.path.join(AUDIO_FOLDER, filename)
 
83
  return filename
84
 
85
  def shorten_url_with_spoo_me(url):
86
+ """Shorten URL using spoo.me API with proper error handling"""
87
  try:
88
+ # Clean the input URL
89
  clean_url = url.strip()
90
  if not clean_url.startswith(('http://', 'https://')):
91
  clean_url = 'https://' + clean_url
92
 
93
+ payload = {
94
+ "url": clean_url
95
+ }
96
  headers = {
97
  "Accept": "application/json",
98
  "Content-Type": "application/x-www-form-urlencoded"
99
  }
100
 
101
+ response = requests.post(
102
+ "https://spoo.me/",
103
+ data=payload,
104
+ headers=headers,
105
+ timeout=10
106
+ )
107
 
108
  if response.status_code == 200:
109
  try:
110
+ # Parse the JSON response
111
  result = response.json()
112
+ # Extract short_url and strip whitespace
113
  short_url = result.get("short_url", "").strip()
114
+ if short_url and short_url.startswith('http'):
 
115
  return short_url
116
  except Exception as json_error:
117
+ print("JSON parse error:", str(json_error))
118
+ # Fallback for plain text response
119
+ text_response = response.text.strip()
120
+ if text_response.startswith('http'):
121
+ return text_response
122
 
123
+ print(f"spoo.me error: {response.status_code} - {response.text}")
124
  return None
125
 
126
  except Exception as e:
127
+ print("Request failed:", str(e))
128
  return None
129
 
130
  @app.route('/chat', methods=['POST'])
 
142
  chat_sessions[session_id] = model.start_chat(history=[])
143
  chat_session = chat_sessions[session_id]
144
 
145
+ # Check if user message contains a URL
 
 
 
 
146
  url_match = re.search(r'https?://[^\s<>"{}|\\^`\[\]]+', user_message)
147
+ ai_response = None
148
+ full_text = ""
149
+
150
  if url_match:
151
  original_url = url_match.group(0).strip()
152
  short_url = shorten_url_with_spoo_me(original_url)
153
+
154
+ if short_url:
155
+ # Format the SHORTEN tag exactly as required
156
  short_tag = f"[SHORTEN]{original_url}|{short_url}[/SHORTEN]"
157
+
158
+ # Send message to AI with instruction to include short link
159
+ ai_response = chat_session.send_message(
160
+ f"User sent this message: '{user_message}'\n\n"
161
+ f"I found a URL: {original_url}\n"
162
+ f"Shortened URL: {short_url}\n\n"
163
+ f"Respond naturally and include this exact format:\n{short_tag}"
164
+ )
165
+ full_text = ai_response.text
166
+ else:
167
+ # If shortening failed, just send normal message
168
+ ai_response = chat_session.send_message(user_message)
169
+ full_text = ai_response.text
170
+ else:
171
+ # No URL found, normal chat
172
+ ai_response = chat_session.send_message(user_message)
173
+ full_text = ai_response.text
174
+
175
+ # Process final response
176
  visible_text, audio_content = process_response(full_text)
177
  html_response = convert_markdown_to_html(visible_text)
178
 
 
181
  "has_audio": False
182
  }
183
 
184
+ # Generate audio if needed
185
  if audio_content:
186
  audio_filename = generate_audio(audio_content)
187
  result["audio_filename"] = audio_filename
188
  result["has_audio"] = True
189
 
190
+ # Return response + set session cookie
191
  resp = make_response(jsonify(result))
192
  resp.set_cookie('session_id', session_id, max_age=3600, httponly=True, samesite='Lax')
193
  return resp
 
198
 
199
  @app.route('/new-chat', methods=['POST'])
200
  def new_chat():
201
+ """Reset chat memory"""
202
  session_id = str(uuid.uuid4())
203
  resp = make_response(jsonify({"status": "new chat started"}))
204
  resp.set_cookie('session_id', session_id, max_age=3600, httponly=True, samesite='Lax')
 
220
  return send_from_directory('static', path)
221
 
222
  if __name__ == '__main__':
223
+ app.run(host="0.0.0.0", port=7860)