abhishekjoel commited on
Commit
dd7d315
·
verified ·
1 Parent(s): d8349f0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -165
app.py CHANGED
@@ -1,181 +1,131 @@
1
  import streamlit as st
2
- from streamlit_webrtc import webrtc_streamer, WebRtcMode, RTCConfiguration
3
  import requests
4
  import openai
5
  import tempfile
6
  import logging
7
- from twilio.rest import Client
8
- import asyncio
9
- import aiohttp
10
- import av
11
- import numpy as np
12
- from typing import Dict, Any
13
- import json
14
- from aiortc.contrib.media import MediaPlayer, MediaRecorder
15
- import threading
16
- from pathlib import Path
17
-
18
- # Enhanced logging configuration
19
- logging.basicConfig(
20
- level=logging.DEBUG, # Changed to DEBUG for more detailed logs
21
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
22
- )
23
- logger = logging.getLogger(__name__)
24
 
25
  # Load API keys from Streamlit secrets
26
- TAVUS_API_KEY = st.secrets["TAVUS_API_KEY"]
27
- OPENAI_API_KEY = st.secrets["OPENAI_API_KEY"]
28
- TWILIO_ACCOUNT_SID = st.secrets["TWILIO_ACCOUNT_SID"]
29
- TWILIO_AUTH_TOKEN = st.secrets["TWILIO_AUTH_TOKEN"]
30
 
31
- # Initialize API clients
32
  openai.api_key = OPENAI_API_KEY
33
- twilio_client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
34
-
35
- class WebRTCManager:
36
- def __init__(self):
37
- self.twilio_client = twilio_client
38
- self.lock = threading.Lock()
39
-
40
- def get_ice_servers(self):
41
- try:
42
- token = self.twilio_client.tokens.create()
43
- ice_servers = token.ice_servers
44
-
45
- # Add additional STUN servers for redundancy
46
- ice_servers.extend([
47
- {"urls": ["stun:stun1.l.google.com:19302"]},
48
- {"urls": ["stun:stun2.l.google.com:19302"]},
49
- {"urls": ["stun:stun3.l.google.com:19302"]},
50
- {"urls": ["stun:stun4.l.google.com:19302"]}
51
- ])
52
-
53
- return RTCConfiguration(iceServers=ice_servers)
54
- except Exception as e:
55
- logger.error(f"Failed to get Twilio ICE servers: {e}")
56
- # Fallback configuration with multiple STUN servers
57
- return RTCConfiguration(
58
- iceServers=[
59
- {"urls": ["stun:stun1.l.google.com:19302"]},
60
- {"urls": ["stun:stun2.l.google.com:19302"]},
61
- {"urls": ["stun:stun3.l.google.com:19302"]},
62
- {"urls": ["stun:stun4.l.google.com:19302"]}
63
- ]
64
- )
65
-
66
- def create_webrtc_context(self):
67
- try:
68
- rtc_configuration = self.get_ice_servers()
69
-
70
- def video_frame_callback(frame):
71
- try:
72
- with self.lock:
73
- img = frame.to_ndarray(format="bgr24")
74
- return av.VideoFrame.from_ndarray(img, format="bgr24")
75
- except Exception as e:
76
- logger.error(f"Error in video frame callback: {e}")
77
- return frame
78
-
79
- def audio_frame_callback(frame):
80
- try:
81
- with self.lock:
82
- return frame
83
- except Exception as e:
84
- logger.error(f"Error in audio frame callback: {e}")
85
- return frame
86
-
87
- return webrtc_streamer(
88
- key="user_stream",
89
- mode=WebRtcMode.SENDRECV,
90
- rtc_configuration=rtc_configuration,
91
- media_stream_constraints={
92
- "video": {
93
- "width": {"min": 640, "ideal": 1280, "max": 1920},
94
- "height": {"min": 480, "ideal": 720, "max": 1080},
95
- "frameRate": {"max": 30},
96
- },
97
- "audio": {
98
- "echoCancellation": True,
99
- "noiseSuppression": True,
100
- "autoGainControl": True,
101
- "sampleRate": 48000,
102
- "sampleSize": 16,
103
- "channelCount": 1,
104
- },
105
- },
106
- video_frame_callback=video_frame_callback,
107
- audio_frame_callback=audio_frame_callback,
108
- rtc_offer_options={
109
- "offerToReceiveAudio": True,
110
- "offerToReceiveVideo": True,
111
- },
112
- async_processing=True,
113
- video_html_attrs={
114
- "autoPlay": True,
115
- "controls": False,
116
- "muted": True,
117
- "playsinline": True,
118
- },
119
- sendback_audio=False, # Prevent audio feedback loops
120
- )
121
- except Exception as e:
122
- logger.error(f"WebRTC context creation failed: {e}")
123
- st.error("Failed to initialize video chat. Please refresh the page.")
124
- return None
125
-
126
- class AudioProcessor:
127
- def __init__(self):
128
- self.audio_buffer = []
129
- self.lock = threading.Lock()
130
-
131
- async def process_audio_frame(self, frame):
132
- try:
133
- with self.lock:
134
- self.audio_buffer.append(frame.to_ndarray())
135
- if len(self.audio_buffer) >= 10: # Process every 10 frames
136
- audio_data = np.concatenate(self.audio_buffer)
137
- self.audio_buffer = []
138
- return audio_data
139
- return None
140
- except Exception as e:
141
- logger.error(f"Error processing audio frame: {e}")
142
- return None
143
 
144
  def main():
145
  st.title("AI Video Chatbot for University Admissions")
146
-
147
- # Initialize managers
148
- webrtc_manager = WebRTCManager()
149
- audio_processor = AudioProcessor()
150
-
151
- # Create WebRTC context with error handling
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  try:
153
- webrtc_ctx = webrtc_manager.create_webrtc_context()
154
-
155
- if webrtc_ctx and webrtc_ctx.state.playing:
156
- st.write("Streaming is active...")
157
-
158
- # Initialize conversation if needed
159
- if 'conversation_history' not in st.session_state:
160
- st.session_state.conversation_history = [{
161
- "role": "assistant",
162
- "content": "Hello there, I'm Nathan and I'm going to help you with college admissions. How's it going?"
163
- }]
164
-
165
- # Process audio frames with additional error handling
166
- if webrtc_ctx.audio_receiver:
167
- try:
168
- audio_frames = webrtc_ctx.audio_receiver.get_frames(timeout=1)
169
- if audio_frames:
170
- # Process audio frames...
171
- pass
172
- except Exception as e:
173
- logger.error(f"Error receiving audio frames: {e}")
174
- st.warning("Audio connection interrupted. Please refresh the page if this persists.")
175
-
176
  except Exception as e:
177
- logger.error(f"Main loop error: {e}")
178
- st.error("An error occurred. Please refresh the page and try again.")
179
 
180
  if __name__ == "__main__":
181
- main()
 
1
  import streamlit as st
2
+ from streamlit_webrtc import webrtc_streamer, WebRtcMode
3
  import requests
4
  import openai
5
  import tempfile
6
  import logging
7
+
8
+ # Set logging level for debugging purposes
9
+ logging.basicConfig(level=logging.INFO)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  # Load API keys from Streamlit secrets
12
+ TAVUS_API_KEY = st.secrets.get("TAVUS_API_KEY")
13
+ OPENAI_API_KEY = st.secrets.get("OPENAI_API_KEY")
 
 
14
 
 
15
  openai.api_key = OPENAI_API_KEY
16
+
17
+ # Introduction prompt
18
+ initial_prompt = "Hello there, I'm Nathan and I'm going to help you with college admissions. How's it going?"
19
+
20
+ # Initialize conversation history if not present
21
+ if 'conversation_history' not in st.session_state:
22
+ st.session_state.conversation_history = [{"role": "assistant", "content": initial_prompt}]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  def main():
25
  st.title("AI Video Chatbot for University Admissions")
26
+
27
+ rtc_configuration = {
28
+ "iceServers": [
29
+ {"urls": ["stun:stun.l.google.com:19302"]},
30
+ # Add TURN server details if necessary for restrictive networks
31
+ ]
32
+ }
33
+
34
+ # Webrtc streamer without unsupported arguments
35
+ webrtc_ctx = webrtc_streamer(
36
+ key="user_stream",
37
+ mode=WebRtcMode.SENDRECV,
38
+ rtc_configuration=rtc_configuration,
39
+ media_stream_constraints={
40
+ "audio": True,
41
+ "video": True,
42
+ },
43
+ video_html_attrs={
44
+ "autoPlay": True,
45
+ "controls": False,
46
+ "muted": True,
47
+ "playsinline": True,
48
+ },
49
+ )
50
+
51
+ if webrtc_ctx and webrtc_ctx.state.playing:
52
+ st.write("Streaming...")
53
+
54
+ if len(st.session_state.conversation_history) == 1:
55
+ avatar_speak_tavus(initial_prompt)
56
+
57
+ if webrtc_ctx.audio_receiver:
58
+ try:
59
+ audio_frames = webrtc_ctx.audio_receiver.get_frames(timeout=1)
60
+ if audio_frames:
61
+ audio_data = b"".join(frame.to_ndarray().tobytes() for frame in audio_frames)
62
+ user_text = speech_to_text(audio_data)
63
+ if user_text:
64
+ st.write(f"**You said:** {user_text}")
65
+ st.session_state.conversation_history.append({"role": "user", "content": user_text})
66
+
67
+ response = get_chatbot_response(st.session_state.conversation_history)
68
+ st.session_state.conversation_history.append({"role": "assistant", "content": response})
69
+
70
+ avatar_speak_tavus(response)
71
+ except Exception as e:
72
+ st.error(f"Error in processing audio: {e}")
73
+
74
+ # Convert speech to text using OpenAI Whisper API
75
+ def speech_to_text(audio_data):
76
+ with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpfile:
77
+ tmpfile.write(audio_data)
78
+ tmpfile_path = tmpfile.name
79
+
80
+ try:
81
+ with open(tmpfile_path, "rb") as audio_file:
82
+ transcript = openai.Audio.transcribe("whisper-1", audio_file)
83
+ return transcript.get("text", "")
84
+ except Exception as e:
85
+ st.error(f"Error during transcription: {e}")
86
+ return ""
87
+
88
+ # Get chatbot response using GPT-3.5
89
+ def get_chatbot_response(conversation_history):
90
+ try:
91
+ response = openai.ChatCompletion.create(
92
+ model="gpt-3.5-turbo",
93
+ messages=conversation_history,
94
+ max_tokens=150,
95
+ )
96
+ return response.choices[0].message["content"]
97
+ except Exception as e:
98
+ st.error(f"Error in generating response from GPT-3.5: {e}")
99
+ return "Sorry, I'm having trouble understanding you at the moment."
100
+
101
+ # Make Tavus avatar speak the response
102
+ def avatar_speak_tavus(text):
103
  try:
104
+ url = "https://api.tavus.io/v2/conversations"
105
+ headers = {
106
+ "Authorization": f"Bearer {TAVUS_API_KEY}",
107
+ "Content-Type": "application/json",
108
+ }
109
+ payload = {
110
+ "replica_id": "r79e1c033f", # Replace with your Tavus replica ID
111
+ "persona_id": "p9a95912", # Replace with your Tavus persona ID
112
+ "conversation_name": "University Admissions Chat",
113
+ "conversational_context": text,
114
+ "properties": {
115
+ "enable_recording": True
116
+ }
117
+ }
118
+ response = requests.post(url, headers=headers, json=payload)
119
+ if response.status_code == 200:
120
+ video_url = response.json().get("conversation_url")
121
+ if video_url:
122
+ st.video(video_url)
123
+ else:
124
+ st.error("No video URL received from Tavus API.")
125
+ else:
126
+ st.error(f"Error from Tavus API: {response.status_code} {response.text}")
127
  except Exception as e:
128
+ st.error(f"Error in Tavus speech generation: {e}")
 
129
 
130
  if __name__ == "__main__":
131
+ main()