CORVO-AI commited on
Commit
dff2bf0
·
verified ·
1 Parent(s): 1b58469

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +238 -542
app.py CHANGED
@@ -1,577 +1,273 @@
1
- from flask import Flask, request, jsonify
2
  import requests
3
- import random
4
- import string
5
  import time
6
  import os
7
- import json
8
- from datetime import datetime, timedelta
 
 
 
9
 
10
  app = Flask(__name__)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
- # Global variables to store workspace and bot IDs
13
- GLOBAL_WORKSPACE_ID = None
14
- GLOBAL_BOT_ID = None
15
-
16
- # Cookie value used in requests (should be updated with a valid cookie)
17
- AUTH_COOKIE = "pscd=try.botpress.com; _hjSessionUser_2931810=eyJpZCI6ImQ2MGMzYjhkLTlkMjQtNTA0OS1hMzlmLWEzNmI0NzA0NzUxNCIsImNyZWF0ZWQiOjE3MzU3MTg0MDcwNTAsImV4aXN0aW5nIjp0cnVlfQ==; hubspotutk=75739411a4d011b2164c4f3d944ecb94; intercom-device-id-bjzkw2xf=afd0a36b-b229-44e3-828e-60483c80c10c; _hjSessionUser_3339867=eyJpZCI6IjU4ODlmMTY4LWRkNGEtNTJhZS1hZTUzLWZlYWQwM2ZmMTVjNyIsImNyZWF0ZWQiOjE3MzU3MTg1ODM4MDgsImV4aXN0aW5nIjp0cnVlfQ==; __hstc=59821234.75739411a4d011b2164c4f3d944ecb94.1735718442141.1746194848836.1746538539919.88; mp_1195923e954ce61d822842b5832047cd_mixpanel=%7B%22distinct_id%22%3A%20%22d403ad7b-ea73-4d29-b977-5fd95afd585c%22%2C%22%24device_id%22%3A%20%22d403ad7b-ea73-4d29-b977-5fd95afd585c%22%2C%22%24initial_referrer%22%3A%20%22https%3A%2F%2Fapp.botpress.cloud%2F%22%2C%22%24initial_referring_domain%22%3A%20%22app.botpress.cloud%22%2C%22__mps%22%3A%20%7B%7D%2C%22__mpso%22%3A%20%7B%22%24initial_referrer%22%3A%20%22https%3A%2F%2Fapp.botpress.cloud%2F%22%2C%22%24initial_referring_domain%22%3A%20%22app.botpress.cloud%22%7D%2C%22__mpus%22%3A%20%7B%7D%2C%22__mpa%22%3A%20%7B%7D%2C%22__mpu%22%3A%20%7B%7D%2C%22__mpr%22%3A%20%5B%5D%2C%22__mpap%22%3A%20%5B%5D%2C%22%24user_id%22%3A%20%22d403ad7b-ea73-4d29-b977-5fd95afd585c%22%7D; intercom-session-bjzkw2xf=TTdnZGNWUC9xNXMreE80NXhRZFNZS0pyUEdsbkJRc2JMcXdGZmcveVRPYkxZTmVnVnhqMUhJTWlDcEpVcWljeDZVYVVSblN4YnV5S0xBdWxDd2swQjZiaUZTeWl5M1psRmtoUWJwUU9FSFE9LS1Bay9zNldJTmVhUFdwMFNReFRmcXB3PT0=--68abc5394d7aab99748f3e451637cab5d9152a4c; _ga=GA1.2.1726154447.1735718383; _gid=GA1.2.1619749406.1746821934; _gat_UA-226900660-1=1; _gat_UA-226900660-2=1; _ga_W6YT9YSNLH=GS2.2.s1746821948$o89$g0$t1746821948$j0$l0$h0; _ga_CYSS87Q508=GS2.2.s1746821949$o89$g0$t1746821949$j0$l0$h0; _hjSession_2931810=eyJpZCI6IjA1YTlkMzY3LWNiZWEtNGQ3OC04YzNiLTEzNTFjNjkxYzViZSIsImMiOjE3NDY4MjE5NTU2NjYsInMiOjAsInIiOjAsInNiIjowLCJzciI6MCwic2UiOjAsImZzIjowLCJzcCI6MH0=; _ga_PCC6TBWJY6=GS2.1.s1746821926$o120$g1$t1746821960$j0$l0$h0; _ga_HKHSWES9V9=GS2.1.s1746821929$o120$g1$t1746821960$j29$l0$h193244206; csrf_token_bd9ac21c34b9f0915e733c3e5305d737d0722c1168be7376b889426b5ec2a298=pIDxu4npODUGEpo7JHVQrKZ4GFaa3U+3BpgxaV5hcVw=; ory_kratos_session=MTc0NjgyMjAyOHxfUmdMaVlTQXVfSmxlT1lJSGpyU2FhbjVUTHg0R0ZsQWgtVm00M3pHcXZwVG9yNW1qRDJheUFGaFZvNmFEUVdBOThQR014RjJJbmhUMmhIV1I1ME5UVHZkTDNpMUMtQlRjZ1ZTbE55M19Pb2dHTF9vQlJoSGlBQnRRWUp0M1ZUdnVvcENLeVhOTllWNk1zMk11bFVPOWFrTzJMTTdxMmVteUozVVRDMWE5TVIxbDgzU3dUY2VQaDBRWDN4bDJUVm8yUkZQa19sb09GbzlFZHF2MDFQcVR6bVVWVVpDLXVoQ1lXMEh2LV9Sd2VNZXM1cjM4TGZPVTJqdW5xNTBETTBDYkppU0xNU2xicUk3Z2EyMnFkVmdyQT09fEYiv7pXcfXVnpIFi4JLGgDObQAchyJCoAwGDSkFkoX7; ajs_user_id=d403ad7b-ea73-4d29-b977-5fd95afd585c; ajs_anonymous_id=cda6139d-cb82-4906-bfac-adaea115b097"
18
-
19
- # -------------------------------------------------------------------
20
- # Helper functions for random bot/workspace names
21
- # -------------------------------------------------------------------
22
- def generate_random_name(length=5):
23
- """Generate a random name for workspace or bot"""
24
- return ''.join(random.choices(string.ascii_letters, k=length))
25
-
26
- # -------------------------------------------------------------------
27
- # Functions to create/delete workspaces and bots
28
- # -------------------------------------------------------------------
29
- def create_workspace():
30
- """Create a new workspace and return its ID"""
31
- ws_url = "https://api.botpress.cloud/v1/admin/workspaces"
32
- headers = {
33
- "User-Agent": "Mozilla/5.0",
34
- "Cookie": AUTH_COOKIE
35
  }
36
- payload = {"name": generate_random_name()}
37
 
38
- try:
39
- response = requests.post(ws_url, headers=headers, json=payload)
40
- if response.status_code == 200:
41
- response_json = response.json()
42
- workspace_id = response_json.get('id')
43
- print(f"Successfully created workspace: {workspace_id}")
44
- return workspace_id
45
- else:
46
- print(f"Workspace creation failed with: {response.status_code}, {response.text}")
47
- return None
48
- except Exception as e:
49
- print(f"Error creating workspace: {str(e)}")
50
- return None
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- def create_bot(workspace_id):
54
- """Create a new bot in the specified workspace and return its ID"""
55
- if not workspace_id:
56
- print("Cannot create bot: No workspace ID provided")
57
- return None
58
 
59
- bot_url = "https://api.botpress.cloud/v1/admin/bots"
60
- headers = {
61
- "User-Agent": "Mozilla/5.0",
62
- "x-workspace-id": workspace_id,
63
- "Cookie": AUTH_COOKIE,
64
- "Content-Type": "application/json"
65
  }
66
- payload = {"name": generate_random_name()}
67
 
68
  try:
69
- response = requests.post(bot_url, headers=headers, json=payload)
 
70
  if response.status_code == 200:
71
- response_json = response.json()
72
- bot_id = response_json.get("bot", {}).get("id")
73
- if not bot_id:
74
- print("Bot ID not found in the response.")
75
- return None
76
-
77
- print(f"Successfully created bot: {bot_id} in workspace: {workspace_id}")
78
-
79
- # Install integration for the new bot
80
- integration_success = install_bot_integration(bot_id, workspace_id)
81
- if integration_success:
82
- print(f"Successfully installed integration for bot {bot_id}")
83
- return bot_id
84
- else:
85
- print(f"Failed to install integration for bot {bot_id}")
86
- return bot_id # Still return the bot ID even if integration fails
87
  else:
88
- print(f"Bot creation failed with: {response.status_code}, {response.text}")
89
  return None
90
  except Exception as e:
91
- print(f"Error creating bot: {str(e)}")
92
  return None
93
 
94
-
95
- def install_bot_integration(bot_id, workspace_id):
96
- """Install required integration for the bot to function properly"""
97
- if not bot_id or not workspace_id:
98
- print("Cannot install integration: Missing bot ID or workspace ID")
99
- return False
100
-
101
- url = f"https://api.botpress.cloud/v1/admin/bots/{bot_id}"
102
- headers = {
103
- "User-Agent": "Mozilla/5.0",
104
- "Cookie": AUTH_COOKIE,
105
- "Content-Type": "application/json",
106
- "x-bot-id": bot_id,
107
- "x-workspace-id": workspace_id
108
- }
109
- # Integration payload
110
- payload = {
111
- "integrations": {
112
- "intver_01JSERFV00FYHW4SM6TEKZ9RWS": {
113
- "enabled": True
114
- }
115
- }
116
- }
117
-
118
  try:
119
- response = requests.put(url, headers=headers, json=payload)
120
- if response.status_code == 200:
121
- print(f"Successfully installed integration for bot {bot_id}")
122
- return True
123
- else:
124
- print(f"Failed to install integration: {response.status_code}, {response.text}")
125
- return False
126
- except Exception as e:
127
- print(f"Error installing integration: {str(e)}")
128
- return False
129
-
130
-
131
- def delete_bot(bot_id, workspace_id):
132
- """Delete a bot from the specified workspace"""
133
- if not bot_id or not workspace_id:
134
- print("Cannot delete bot: Missing bot ID or workspace ID")
135
- return False
136
-
137
- url = f"https://api.botpress.cloud/v1/admin/bots/{bot_id}"
138
- headers = {
139
- "User-Agent": "Mozilla/5.0",
140
- "x-workspace-id": workspace_id,
141
- "Cookie": AUTH_COOKIE
142
- }
143
-
144
- try:
145
- response = requests.delete(url, headers=headers)
146
- if response.status_code in [200, 204]:
147
- print(f"Successfully deleted bot: {bot_id}")
148
- return True
149
- else:
150
- print(f"Failed to delete bot: {response.status_code}, {response.text}")
151
- return False
152
- except Exception as e:
153
- print(f"Error deleting bot: {str(e)}")
154
- return False
155
-
156
-
157
- def delete_workspace(workspace_id):
158
- """Delete a workspace"""
159
- if not workspace_id:
160
- print("Cannot delete workspace: No workspace ID provided")
161
- return False
162
-
163
- url = f"https://api.botpress.cloud/v1/admin/workspaces/{workspace_id}"
164
- headers = {
165
- "User-Agent": "Mozilla/5.0",
166
- "Cookie": AUTH_COOKIE
167
- }
168
-
169
- try:
170
- response = requests.delete(url, headers=headers)
171
- if response.status_code in [200, 204]:
172
- print(f"Successfully deleted workspace: {workspace_id}")
173
- return True
174
- else:
175
- print(f"Failed to delete workspace: {response.status_code}, {response.text}")
176
- return False
177
- except Exception as e:
178
- print(f"Error deleting workspace: {str(e)}")
179
- return False
180
-
181
-
182
- # -------------------------------------------------------------------
183
- # Function to upload audio file and get URL
184
- # -------------------------------------------------------------------
185
- def upload_audio_file(file_path, bot_id, workspace_id):
186
- """Upload an audio file and return its URL"""
187
- global GLOBAL_WORKSPACE_ID, GLOBAL_BOT_ID
188
-
189
- # API endpoint
190
- url = "https://api.botpress.cloud/v1/files"
191
-
192
- # Get file name from path
193
- file_name = os.path.basename(file_path)
194
-
195
- # Get file size
196
- file_size = os.path.getsize(file_path)
197
-
198
- # Get file content type
199
- content_type = "audio/mpeg"
200
-
201
- # Calculate expiration date (3 days from now) with proper timezone format
202
- now = datetime.now()
203
- expires_at = (now + timedelta(days=3)).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "+02:00"
204
-
205
- # Prepare headers
206
- headers = {
207
- "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
208
- "x-bot-id": bot_id,
209
- "x-workspace-id": workspace_id,
210
- "Content-Type": "application/json",
211
- "cookie": AUTH_COOKIE
212
- }
213
 
214
- # Prepare payload
215
- payload = {
216
- "key": file_name,
217
- "tags": {
218
- "purpose": "emulator",
219
- "system": "true"
220
- },
221
- "size": file_size,
222
- "accessPolicies": ["public_content"],
223
- "contentType": content_type,
224
- "expiresAt": expires_at,
225
- "publicContentImmediatelyAccessible": True
226
- }
227
 
228
- # Make the PUT request to get the upload URL
229
- response = requests.put(url, headers=headers, data=json.dumps(payload))
230
-
231
- if response.status_code == 200:
232
- response_data = response.json()
233
-
234
- # Extract the URL and upload URL
235
- file_url = response_data.get("file", {}).get("url", "")
236
- upload_url = response_data.get("file", {}).get("uploadUrl", "")
237
-
238
- # Now upload the actual file to the upload URL
239
- if upload_url:
240
- # Read the binary content of the audio file
241
- with open(file_path, 'rb') as audio_file:
242
- file_content = audio_file.read()
243
-
244
- # Set headers for the upload request with all the headers you provided
245
- upload_headers = {
246
- "accept": "application/json, text/plain, */*",
247
- "accept-encoding": "gzip, deflate, br, zstd",
248
- "accept-language": "en-US,en;q=0.9,ar;q=0.8",
249
- "connection": "keep-alive",
250
- "content-length": str(file_size),
251
- "content-type": "audio/mpeg",
252
- "host": "s3.us-east-1.amazonaws.com",
253
- "origin": "https://studio.botpress.cloud",
254
- "referer": "https://studio.botpress.cloud/",
255
- "sec-ch-ua": '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
256
- "sec-ch-ua-mobile": "?0",
257
- "sec-ch-ua-platform": '"Windows"',
258
- "sec-fetch-dest": "empty",
259
- "sec-fetch-mode": "cors",
260
- "sec-fetch-site": "cross-site",
261
- "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
262
- "x-amz-tagging": "public=true"
263
- }
264
-
265
- # Make the PUT request to upload the file
266
- upload_response = requests.put(upload_url, headers=upload_headers, data=file_content)
267
-
268
- if upload_response.status_code == 200:
269
- return file_url, bot_id, workspace_id
270
- else:
271
- return f"Error uploading file: {upload_response.status_code} - {upload_response.text}", bot_id, workspace_id
272
- else:
273
- return "No upload URL provided in response", bot_id, workspace_id
274
- elif response.status_code == 403:
275
- # If we get a 403 error, we need to delete and recreate the bot and workspace
276
- print("Received 403 error during file upload. Recreating bot and workspace...")
277
-
278
- # Delete bot first, then workspace
279
- if bot_id and workspace_id:
280
- delete_bot(bot_id, workspace_id)
281
- delete_workspace(workspace_id)
282
-
283
- # Create new workspace and bot
284
- new_workspace_id = create_workspace()
285
- if not new_workspace_id:
286
- return "Failed to create a new workspace after 403 error", bot_id, workspace_id
287
-
288
- new_bot_id = create_bot(new_workspace_id)
289
- if not new_bot_id:
290
- return "Failed to create a new bot after 403 error", new_workspace_id, workspace_id
291
-
292
- # Update global variables
293
- GLOBAL_WORKSPACE_ID = new_workspace_id
294
- GLOBAL_BOT_ID = new_bot_id
295
-
296
- # Try again with the new IDs
297
- result, _, _ = upload_audio_file(file_path, new_bot_id, new_workspace_id)
298
- return result, new_bot_id, new_workspace_id
299
- else:
300
- return f"Error: {response.status_code} - {response.text}", bot_id, workspace_id
301
-
302
-
303
- # -------------------------------------------------------------------
304
- # Main function that calls the Botpress API endpoint for audio transcription
305
- # -------------------------------------------------------------------
306
- def transcribe_audio(file_url, prompt, bot_id, workspace_id):
307
- """
308
- Sends the audio file URL to the Botpress API endpoint for transcription,
309
- returns the transcription text and (possibly updated) bot/workspace IDs.
310
- """
311
- global GLOBAL_WORKSPACE_ID, GLOBAL_BOT_ID
312
-
313
- # Prepare the headers
314
- headers = {
315
- "User-Agent": "Mozilla/5.0",
316
- "x-bot-id": bot_id,
317
- "x-workspace-id": workspace_id,
318
- "Content-Type": "application/json",
319
- "Cookie": AUTH_COOKIE
320
- }
321
 
322
- # Prepare the payload for the API
323
- payload = {
324
- "type": "openai:transcribeAudio",
325
- "input": {
326
- "fileUrl": file_url,
327
- "prompt": prompt,
328
- "temperature": 0
329
  }
330
- }
331
-
332
- botpress_url = "https://api.botpress.cloud/v1/chat/actions"
333
- max_retries = 3
334
- timeout = 120 # Increased timeout for long audio files
335
 
336
- # Attempt to send the request
337
- for attempt in range(max_retries):
338
- try:
339
- print(f"Attempt {attempt+1}: Sending transcription request to Botpress API with bot_id={bot_id}, workspace_id={workspace_id}")
340
- response = requests.post(botpress_url, json=payload, headers=headers, timeout=timeout)
341
-
342
- # If successful (200)
343
- if response.status_code == 200:
344
- data = response.json()
345
-
346
- # Extract all text segments from the response
347
- transcription_text = ""
348
- segments = data.get('output', {}).get('segments', [])
349
- for segment in segments:
350
- segment_text = segment.get('text', '')
351
- if segment_text:
352
- transcription_text += segment_text + " "
353
-
354
- transcription_text = transcription_text.strip()
355
- print(f"Successfully received transcription from Botpress API")
356
- return transcription_text, bot_id, workspace_id
357
-
358
- # If we get a 403 error, delete and recreate workspace/bot
359
- elif response.status_code == 403:
360
- print(f"Received 403 error. Deleting and recreating workspace/bot...")
361
- break # Break out of the retry loop to handle 403 specially
362
-
363
- # Handle network errors or timeouts (just retry)
364
- elif response.status_code in [404, 408, 502, 503, 504]:
365
- print(f"Received error {response.status_code}. Retrying...")
366
- time.sleep(3) # Wait before retrying
367
- continue
368
-
369
- # Any other error status code
370
- else:
371
- print(f"Received unexpected error: {response.status_code}, {response.text}")
372
- if attempt < max_retries - 1:
373
- time.sleep(2)
374
- continue
375
- else:
376
- return f"Unable to transcribe the audio (Error {response.status_code}).", bot_id, workspace_id
377
 
378
- except requests.exceptions.Timeout:
379
- print(f"Request timed out. Retrying...")
380
- if attempt < max_retries - 1:
381
- time.sleep(2)
382
- continue
383
- else:
384
- return "The transcription is taking too long. Please try again with a shorter audio file.", bot_id, workspace_id
385
-
386
- except Exception as e:
387
- print(f"Error during request: {str(e)}")
388
- if attempt < max_retries - 1:
389
- time.sleep(2)
390
- continue
391
- else:
392
- return f"Unable to transcribe the audio: {str(e)}", bot_id, workspace_id
393
-
394
- # If we got a 403 error, delete and recreate resources
395
- # First delete the bot, then the workspace (in that order)
396
- if bot_id and workspace_id:
397
- print(f"Deleting bot {bot_id} first...")
398
- delete_success = delete_bot(bot_id, workspace_id)
399
- if delete_success:
400
- print(f"Successfully deleted bot {bot_id}")
401
- else:
402
- print(f"Failed to delete bot {bot_id}")
403
-
404
- print(f"Now deleting workspace {workspace_id}...")
405
- ws_delete_success = delete_workspace(workspace_id)
406
- if ws_delete_success:
407
- print(f"Successfully deleted workspace {workspace_id}")
408
- else:
409
- print(f"Failed to delete workspace {workspace_id}")
410
-
411
- # Create new workspace
412
- new_workspace_id = create_workspace()
413
- if not new_workspace_id:
414
- return "Failed to create a new workspace. Please try again later.", bot_id, workspace_id
415
-
416
- # Create new bot in the new workspace
417
- new_bot_id = create_bot(new_workspace_id)
418
- if not new_bot_id:
419
- return "Failed to create a new bot. Please try again later.", new_workspace_id, workspace_id
420
-
421
- # Update global variables
422
- GLOBAL_WORKSPACE_ID = new_workspace_id
423
- GLOBAL_BOT_ID = new_bot_id
424
 
425
- # Update headers with new bot ID and workspace ID
426
- headers["x-bot-id"] = new_bot_id
427
- headers["x-workspace-id"] = new_workspace_id
428
-
429
- # Try one more time with the new IDs
430
- try:
431
- print(f"Retrying with new bot_id={new_bot_id}, workspace_id={new_workspace_id}")
432
- retry_response = requests.post(botpress_url, json=payload, headers=headers, timeout=timeout)
433
-
434
- if retry_response.status_code == 200:
435
- data = retry_response.json()
436
-
437
- # Extract all text segments from the response
438
- transcription_text = ""
439
- segments = data.get('output', {}).get('segments', [])
440
- for segment in segments:
441
- segment_text = segment.get('text', '')
442
- if segment_text:
443
- transcription_text += segment_text + " "
444
-
445
- transcription_text = transcription_text.strip()
446
- print(f"Successfully received transcription with new IDs")
447
- return transcription_text, new_bot_id, new_workspace_id
448
- else:
449
- print(f"Failed with new IDs: {retry_response.status_code}, {retry_response.text}")
450
- return f"Unable to transcribe the audio with new credentials.", new_bot_id, new_workspace_id
451
 
 
452
  except Exception as e:
453
- print(f"Error with new IDs: {str(e)}")
454
- return f"Unable to transcribe the audio with new credentials: {str(e)}", new_bot_id, new_workspace_id
455
-
456
- # -------------------------------------------------------------------
457
- # Flask Endpoints
458
- # -------------------------------------------------------------------
459
- @app.route("/transcribe", methods=["POST"])
460
- def transcribe_endpoint():
461
- """
462
- Expects JSON with:
463
- {
464
- "file_url": "string",
465
- "prompt": "string"
466
- }
467
- Returns JSON with:
468
- {
469
- "transcription": "string"
470
- }
471
- """
472
- global GLOBAL_WORKSPACE_ID, GLOBAL_BOT_ID
473
-
474
- # Parse JSON from request
475
- data = request.get_json(force=True)
476
- file_url = data.get("file_url", "")
477
- prompt = data.get("prompt", "get all text with his lang and exatract (DON'T translate) .")
478
-
479
- if not file_url:
480
- return jsonify({"error": "Missing file_url parameter"}), 400
481
-
482
- # If we don't yet have a workspace or bot, create them
483
- if not GLOBAL_WORKSPACE_ID or not GLOBAL_BOT_ID:
484
- print("No existing IDs found. Creating new workspace and bot...")
485
- GLOBAL_WORKSPACE_ID = create_workspace()
486
- if GLOBAL_WORKSPACE_ID:
487
- GLOBAL_BOT_ID = create_bot(GLOBAL_WORKSPACE_ID)
488
-
489
- # If creation failed
490
- if not GLOBAL_WORKSPACE_ID or not GLOBAL_BOT_ID:
491
- return jsonify({"error": "I'm currently unavailable. Please try again later."}), 500
492
-
493
- # Call our function that interacts with Botpress API
494
- print(f"Sending transcription request with existing bot_id={GLOBAL_BOT_ID}, workspace_id={GLOBAL_WORKSPACE_ID}")
495
- transcription, updated_bot_id, updated_workspace_id = transcribe_audio(
496
- file_url,
497
- prompt,
498
- GLOBAL_BOT_ID,
499
- GLOBAL_WORKSPACE_ID
500
- )
501
-
502
- # Update global IDs if they changed
503
- if updated_bot_id != GLOBAL_BOT_ID or updated_workspace_id != GLOBAL_WORKSPACE_ID:
504
- print(f"Updating global IDs: bot_id={updated_bot_id}, workspace_id={updated_workspace_id}")
505
- GLOBAL_BOT_ID = updated_bot_id
506
- GLOBAL_WORKSPACE_ID = updated_workspace_id
507
-
508
- # Check if we got an error string back
509
- if isinstance(transcription, str) and (
510
- transcription.startswith("Failed") or
511
- transcription.startswith("Unable") or
512
- transcription.startswith("The transcription is taking too long")
513
- ):
514
- return jsonify({"error": transcription}), 500
515
-
516
- return jsonify({"transcription": transcription})
517
-
518
-
519
- @app.route("/upload", methods=["POST"])
520
- def upload_endpoint():
521
- """
522
- Endpoint to upload an audio file and get its URL
523
- Expects form data with a file field named 'audio'
524
- Returns JSON with the file URL
525
- """
526
- global GLOBAL_WORKSPACE_ID, GLOBAL_BOT_ID
527
-
528
- # Check if file was uploaded
529
  if 'audio' not in request.files:
530
- return jsonify({"error": "No audio file provided"}), 400
531
 
532
  audio_file = request.files['audio']
533
 
534
- # Check if filename is empty
535
- if audio_file.filename == '':
536
- return jsonify({"error": "No audio file selected"}), 400
 
537
 
538
- # If we don't yet have a workspace or bot, create them
539
- if not GLOBAL_WORKSPACE_ID or not GLOBAL_BOT_ID:
540
- print("No existing IDs found. Creating new workspace and bot...")
541
- GLOBAL_WORKSPACE_ID = create_workspace()
542
- if GLOBAL_WORKSPACE_ID:
543
- GLOBAL_BOT_ID = create_bot(GLOBAL_WORKSPACE_ID)
544
-
545
- # If creation failed
546
- if not GLOBAL_WORKSPACE_ID or not GLOBAL_BOT_ID:
547
- return jsonify({"error": "I'm currently unavailable. Please try again later."}), 500
548
-
549
- # Save the file temporarily
550
- temp_path = f"/tmp/{audio_file.filename}"
551
- audio_file.save(temp_path)
552
-
553
- # Upload the file
554
- file_url, updated_bot_id, updated_workspace_id = upload_audio_file(temp_path, GLOBAL_BOT_ID, GLOBAL_WORKSPACE_ID)
555
-
556
- # Remove the temporary file
557
- os.remove(temp_path)
558
-
559
- # Update global IDs if they changed
560
- if updated_bot_id != GLOBAL_BOT_ID or updated_workspace_id != GLOBAL_WORKSPACE_ID:
561
- print(f"Updating global IDs: bot_id={updated_bot_id}, workspace_id={updated_workspace_id}")
562
- GLOBAL_BOT_ID = updated_bot_id
563
- GLOBAL_WORKSPACE_ID = updated_workspace_id
564
-
565
- # Check if we got an error string back
566
- if isinstance(file_url, str) and (file_url.startswith("Error") or file_url.startswith("Failed") or file_url.startswith("No upload")):
567
- return jsonify({"error": file_url}), 500
568
-
569
- return jsonify({"file_url": file_url})
570
-
571
-
572
- # -------------------------------------------------------------------
573
- # Run the Flask app
574
- # -------------------------------------------------------------------
575
-
576
- if __name__ == "__main__":
577
- app.run(host="0.0.0.0", port=7860, debug=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, session
2
  import requests
3
+ import json
 
4
  import time
5
  import os
6
+ import uuid
7
+ import threading
8
+ import base64
9
+ from flask_cors import CORS
10
+ from flask_session import Session
11
 
12
  app = Flask(__name__)
13
+ CORS(app)
14
+
15
+ # Configure server-side session
16
+ app.config["SECRET_KEY"] = os.urandom(24)
17
+ app.config["SESSION_TYPE"] = "filesystem"
18
+ app.config["SESSION_PERMANENT"] = True
19
+ Session(app)
20
+
21
+ # Global variables
22
+ UPLOAD_FOLDER = 'temp_audio'
23
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
24
+
25
+ # API endpoints and headers
26
+ TTS_API_URL = "https://nanoxbot-voice.hf.space/synthesize"
27
+ TTS_HEADERS = {"Content-Type": "application/json"}
28
+ TTS_VOICE_ID = "PVL:db1ba8fe-9c80-4c13-b8b0-32c8163a581e" # Ana de Armas voice
29
+
30
+ STT_API_BASE_URL = "https://corvo-ai-transcript.hf.space"
31
+ STT_AUTH_COOKIE = "spaces-jwt=eyJhbGciOiJFZERTQSJ9.eyJyZWFkIjp0cnVlLCJwZXJtaXNzaW9ucyI6eyJyZXBvLmNvbnRlbnQucmVhZCI6dHJ1ZX0sIm9uQmVoYWxmT2YiOnsia2luZCI6InVzZXIiLCJfaWQiOiI2NzQ2ZTcwYzQ5MGM3M2EwOTdiMzBiMTAiLCJ1c2VyIjoiQ09SVk8tQUkiLCJzZXNzaW9uSWQiOiI2NzQ2ZTcwYzQ5MGM3M2EwOTdiMzBiMWQifSwiaWF0IjoxNzQ3NjUxMzk2LCJzdWIiOiIvc3BhY2VzL0NPUlZPLUFJL3RyYW5zY3JpcHQiLCJleHAiOjE3NDc3Mzc3OTYsImlzcyI6Imh0dHBzOi8vaHVnZ2luZ2ZhY2UuY28ifQ.yuFQOp57OzdyNw8GsJ7MUgxUTaaXPQKUWbW72LmxE3NYwC0wbSl2zvs9LB8lC8dLNwmrDd8GckhTMJIfCgMVAQ"
32
+ STT_HEADERS = {"Cookie": STT_AUTH_COOKIE}
33
+
34
+ AI_API_URL = "https://corvo-ai-xx-xy.hf.space/chat"
35
+ AI_HEADERS = {
36
+ "Content-Type": "application/json",
37
+ "cookie": "spaces-jwt=eyJhbGciOiJFZERTQSJ9.eyJyZWFkIjp0cnVlLCJwZXJtaXNzaW9ucyI6eyJyZXBvLmNvbnRlbnQucmVhZCI6dHJ1ZX0sIm9uQmVoYWxmT2YiOnsia2luZCI6InVzZXIiLCJfaWQiOiI2NzQ2ZTcwYzQ5MGM3M2EwOTdiMzBiMTAiLCJ1c2VyIjoiQ09SVk8tQUkiLCJzZXNzaW9uSWQiOiI2NzQ2ZTcwYzQ5MGM3M2EwOTdiMzBiMWQifSwiaWF0IjoxNzQ3NTc5NjM0LCJzdWIiOiIvc3BhY2VzL0NPUlZPLUFJL1hYLVhZIiwiZXhwIjoxNzQ3NjY2MDM0LCJpc3MiOiJodHRwczovL2h1Z2dpbmdmYWNlLmNvIn0.Yl67KHhOYDmY6atPWO01Q3sRNhal8BGWhgR8dR-CJGSTFKODs41GrB-_THHyuafodCnEb0_Yf5QnGltCsvUwBw"
38
+ }
39
+
40
+ # Helper function for thinking animation (not used in API but kept for reference)
41
+ def thinking_animation():
42
+ for _ in range(3):
43
+ print(".", end="", flush=True)
44
+ time.sleep(0.5)
45
+ print("\r", end="", flush=True)
46
+
47
+ # AI text generation function
48
+ def gpt4o_ai(user_input, chat_history, username):
49
+ """Function to interact with the AI API in a chat format"""
50
+
51
+ # Format chat history with roles
52
+ formatted_chat_history = []
53
+ for entry in chat_history:
54
+ formatted_chat_history.append({"role": entry['role'], "content": entry['content']})
55
+
56
+ # Append the current user input to the chat history
57
+ formatted_chat_history.append({"role": "user", "content": f"{username}: {user_input}"})
58
 
59
+ payload = {
60
+ "chat_history": formatted_chat_history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
 
62
 
63
+ max_retries = 5
64
+ retry_delay = 10
65
+ timeout = 600
 
 
 
 
 
 
 
 
 
 
66
 
67
+ for attempt in range(max_retries):
68
+ try:
69
+ print("AI THINKING...")
70
+ response = requests.post(AI_API_URL, headers=AI_HEADERS, data=json.dumps(payload), timeout=timeout)
71
+ response.raise_for_status()
72
+ assistant_response = response.json().get("assistant_response", "No response received.")
73
+
74
+ # Append the assistant's response to the chat history
75
+ formatted_chat_history.append({"role": "assistant", "content": assistant_response})
76
+ return assistant_response, formatted_chat_history
77
+ except requests.exceptions.Timeout:
78
+ print(f"Timeout on attempt {attempt + 1}, retrying...")
79
+ time.sleep(retry_delay)
80
+ except Exception as e:
81
+ print(f"Error on attempt {attempt + 1}: {e}, retrying...")
82
+ time.sleep(retry_delay)
83
 
84
+ return "Error processing request. Please try again.", formatted_chat_history
 
 
 
 
85
 
86
+ # Text-to-Speech function
87
+ def text_to_speech(text):
88
+ """Convert text to speech using the TTS API"""
89
+ payload = {
90
+ "text": text,
91
+ "voice_id": TTS_VOICE_ID
92
  }
 
93
 
94
  try:
95
+ response = requests.post(TTS_API_URL, headers=TTS_HEADERS, json=payload)
96
+
97
  if response.status_code == 200:
98
+ # Return the audio content as base64
99
+ return base64.b64encode(response.content).decode('utf-8')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  else:
101
+ print(f"Error getting TTS audio: {response.status_code}")
102
  return None
103
  except Exception as e:
104
+ print(f"Error in TTS API call: {str(e)}")
105
  return None
106
 
107
+ # Speech-to-Text function
108
+ def speech_to_text(audio_file_path):
109
+ """Convert speech to text using the STT API"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  try:
111
+ # Step 1: Upload the MP3 file
112
+ with open(audio_file_path, 'rb') as file:
113
+ files = {'audio': (os.path.basename(audio_file_path), file, 'audio/mpeg')}
114
+ upload_response = requests.post(
115
+ f"{STT_API_BASE_URL}/upload",
116
+ files=files,
117
+ headers=STT_HEADERS
118
+ )
119
+
120
+ # Check if upload was successful
121
+ if upload_response.status_code != 200:
122
+ print(f"Upload failed with status code {upload_response.status_code}")
123
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
+ # Get the file URL from the response
126
+ upload_data = upload_response.json()
127
+ file_url = upload_data.get('file_url')
 
 
 
 
 
 
 
 
 
 
128
 
129
+ if not file_url:
130
+ print("No file URL in response")
131
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
+ # Step 2: Send the file URL for transcription
134
+ transcribe_payload = {
135
+ "file_url": file_url,
136
+ "prompt": "get all text with his lang and extract (DON'T translate)."
 
 
 
137
  }
 
 
 
 
 
138
 
139
+ transcribe_response = requests.post(
140
+ f"{STT_API_BASE_URL}/transcribe",
141
+ json=transcribe_payload,
142
+ headers=STT_HEADERS
143
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
+ # Check if transcription was successful
146
+ if transcribe_response.status_code != 200:
147
+ print(f"Transcription failed with status code {transcribe_response.status_code}")
148
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
+ # Get the transcription from the response
151
+ transcribe_data = transcribe_response.json()
152
+ transcription = transcribe_data.get('transcription')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
+ return transcription
155
  except Exception as e:
156
+ print(f"Error in STT API call: {str(e)}")
157
+ return None
158
+
159
+ # Routes
160
+ @app.route('/api/start-session', methods=['POST'])
161
+ def start_session():
162
+ """Initialize a new session for a user"""
163
+ data = request.json
164
+ username = data.get('username', 'User')
165
+
166
+ # Initialize chat history for this user
167
+ session['username'] = username
168
+ session['chat_history'] = []
169
+
170
+ # Generate initial AI greeting
171
+ initial_prompt = "Hello! I'm your AI assistant. How can I help you today?"
172
+ ai_response, chat_history = gpt4o_ai(initial_prompt, [], username)
173
+ session['chat_history'] = chat_history
174
+
175
+ # Convert AI response to speech
176
+ audio_base64 = text_to_speech(ai_response)
177
+
178
+ return jsonify({
179
+ 'success': True,
180
+ 'message': 'Session started',
181
+ 'username': username,
182
+ 'ai_response': ai_response,
183
+ 'audio': audio_base64
184
+ })
185
+
186
+ @app.route('/api/send-text', methods=['POST'])
187
+ def send_text():
188
+ """Process text input from user and get AI response"""
189
+ data = request.json
190
+ user_input = data.get('text', '')
191
+
192
+ # Get session data
193
+ username = session.get('username', 'User')
194
+ chat_history = session.get('chat_history', [])
195
+
196
+ # Get AI response
197
+ ai_response, chat_history = gpt4o_ai(user_input, chat_history, username)
198
+ session['chat_history'] = chat_history
199
+
200
+ # Convert AI response to speech
201
+ audio_base64 = text_to_speech(ai_response)
202
+
203
+ return jsonify({
204
+ 'success': True,
205
+ 'ai_response': ai_response,
206
+ 'audio': audio_base64
207
+ })
208
+
209
+ @app.route('/api/send-audio', methods=['POST'])
210
+ def send_audio():
211
+ """Process audio input from user and get AI response"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  if 'audio' not in request.files:
213
+ return jsonify({'success': False, 'error': 'No audio file provided'})
214
 
215
  audio_file = request.files['audio']
216
 
217
+ # Save the audio file temporarily
218
+ filename = f"{uuid.uuid4()}.mp3"
219
+ file_path = os.path.join(UPLOAD_FOLDER, filename)
220
+ audio_file.save(file_path)
221
 
222
+ try:
223
+ # Convert speech to text
224
+ user_input = speech_to_text(file_path)
225
+
226
+ if not user_input:
227
+ return jsonify({'success': False, 'error': 'Failed to transcribe audio'})
228
+
229
+ # Get session data
230
+ username = session.get('username', 'User')
231
+ chat_history = session.get('chat_history', [])
232
+
233
+ # Get AI response
234
+ ai_response, chat_history = gpt4o_ai(user_input, chat_history, username)
235
+ session['chat_history'] = chat_history
236
+
237
+ # Convert AI response to speech
238
+ audio_base64 = text_to_speech(ai_response)
239
+
240
+ return jsonify({
241
+ 'success': True,
242
+ 'transcription': user_input,
243
+ 'ai_response': ai_response,
244
+ 'audio': audio_base64
245
+ })
246
+ finally:
247
+ # Clean up the temporary file
248
+ if os.path.exists(file_path):
249
+ os.remove(file_path)
250
+
251
+ @app.route('/api/interrupt', methods=['POST'])
252
+ def interrupt():
253
+ """Handle user interruption during AI speech"""
254
+ # This endpoint would be called when the user starts speaking while the AI is talking
255
+ # In a real implementation, you might need WebSockets for this kind of real-time interaction
256
+ return jsonify({
257
+ 'success': True,
258
+ 'message': 'AI speech interrupted'
259
+ })
260
+
261
+ @app.route('/api/end-session', methods=['POST'])
262
+ def end_session():
263
+ """End the current session"""
264
+ # Clear session data
265
+ session.clear()
266
+
267
+ return jsonify({
268
+ 'success': True,
269
+ 'message': 'Session ended'
270
+ })
271
+
272
+ if __name__ == '__main__':
273
+ app.run(debug=True, port=5000)