rasulbrur commited on
Commit
698c6c9
·
1 Parent(s): 460dc2c

Added mediarecorder for web recording

Browse files
Dockerfile CHANGED
@@ -3,7 +3,7 @@ FROM python:3.10-slim
3
 
4
  # Install system dependencies
5
  RUN apt-get update && \
6
- apt-get install -y wget unzip curl gcc portaudio19-dev && \
7
  rm -rf /var/lib/apt/lists/*
8
 
9
  # Set working directory
 
3
 
4
  # Install system dependencies
5
  RUN apt-get update && \
6
+ apt-get install -y wget ffmpeg unzip curl gcc portaudio19-dev && \
7
  rm -rf /var/lib/apt/lists/*
8
 
9
  # Set working directory
__pycache__/app.cpython-310.pyc ADDED
Binary file (4.62 kB). View file
 
__pycache__/main.cpython-310.pyc ADDED
Binary file (3.9 kB). View file
 
app.py CHANGED
@@ -1,15 +1,15 @@
1
- # app.py
2
- from fastapi import FastAPI, Request, Form
3
  from fastapi.responses import HTMLResponse, JSONResponse
4
  from fastapi.staticfiles import StaticFiles
5
  from fastapi.templating import Jinja2Templates
6
  from main import process_query
7
  from voice.speech_to_text import SpeechToText
8
  import os
9
- import asyncio
10
- import pyaudio
11
- import wave
12
  import logging
 
 
 
 
13
 
14
  # Set up logging
15
  logging.basicConfig(level=logging.INFO)
@@ -23,121 +23,104 @@ app.mount("/static", StaticFiles(directory="static"), name="static")
23
  # Set up templates
24
  templates = Jinja2Templates(directory="templates")
25
 
26
- # Vosk model path and audio file path
27
- vosk_model_path = "./vosk-model-small-en-us-0.15"
28
- audio_file_path = "voice/temp_audio.wav"
29
 
30
- # Ensure the voice directory exists
31
- os.makedirs("voice", exist_ok=True)
32
 
33
  # Initialize SpeechToText
34
  stt = SpeechToText(model_path=vosk_model_path)
35
 
36
- # Global variables for recording state
37
- recording = False
38
- audio_frames = []
39
- recording_task = None
40
-
41
- def save_audio_to_wav(frames, sample_rate=16000):
42
- """Save audio frames to a WAV file."""
43
  try:
44
- logger.info(f"Saving audio to {audio_file_path} with {len(frames)} frames")
45
- wf = wave.open(audio_file_path, 'wb')
46
- wf.setnchannels(1)
47
- wf.setsampwidth(2) # 16-bit
48
- wf.setframerate(sample_rate)
49
- wf.writeframes(b''.join(frames))
50
- wf.close()
51
- if os.path.exists(audio_file_path):
52
- logger.info(f"WAV file saved successfully: {os.path.getsize(audio_file_path)} bytes")
53
- else:
54
- logger.error("WAV file was not created")
55
- except Exception as e:
56
- logger.error(f"Error saving WAV file: {str(e)}")
57
- raise
58
-
59
- async def record_audio():
60
- """Background task to record audio."""
61
- global audio_frames
62
- p = pyaudio.PyAudio()
63
  try:
64
- stream = p.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True, frames_per_buffer=1024)
65
- stream.start_stream()
66
- logger.info("Recording started...")
67
-
68
- while recording:
69
- data = stream.read(1024, exception_on_overflow=False)
70
- audio_frames.append(data)
71
- await asyncio.sleep(0.01) # Small sleep to prevent blocking
72
-
73
- stream.stop_stream()
74
- stream.close()
75
- logger.info(f"Recording stopped, captured {len(audio_frames)} frames")
76
  except Exception as e:
77
- logger.error(f"Error during recording: {str(e)}")
78
- finally:
79
- p.terminate()
80
 
81
  @app.get("/", response_class=HTMLResponse)
82
  async def get_index(request: Request):
83
  return templates.TemplateResponse("index.html", {"request": request})
84
 
85
- @app.post("/start_recording", response_class=JSONResponse)
86
- async def start_recording():
87
- global recording, audio_frames, recording_task
88
- if not recording:
89
- recording = True
90
- audio_frames = []
91
- recording_task = asyncio.create_task(record_audio())
92
- logger.info("Started recording task")
93
- return {"status": "Recording started"}
94
- logger.warning("Recording already in progress")
95
- return {"status": "Already recording"}
96
-
97
- @app.post("/stop_recording", response_class=HTMLResponse)
98
- async def stop_recording(request: Request):
99
- global recording, recording_task
100
- if recording:
101
- recording = False
102
- if recording_task:
103
- await recording_task # Wait for the recording task to complete
104
- recording_task = None
105
-
106
- # Save the audio to WAV
107
- try:
108
- save_audio_to_wav(audio_frames)
109
- except Exception as e:
110
- logger.error(f"Failed to save audio: {str(e)}")
111
- return templates.TemplateResponse("index.html", {
112
- "request": request,
113
- "error": f"Failed to save audio: {str(e)}"
114
- })
115
-
116
- # Transcribe the saved audio
117
- try:
118
- text = stt.transcribe_audio(audio_file_path)
119
- logger.info(f"Transcription result: '{text}'")
120
- if not text:
121
- logger.warning("Transcription returned no text")
122
  return templates.TemplateResponse("index.html", {
123
  "request": request,
124
- "error": "Could not understand the audio."
 
 
 
 
 
 
 
 
 
125
  })
126
- return templates.TemplateResponse("index.html", {
127
- "request": request,
128
- "transcribed_text": text
129
- })
130
- except Exception as e:
131
- logger.error(f"Transcription error: {str(e)}")
132
- return templates.TemplateResponse("index.html", {
133
- "request": request,
134
- "error": f"Transcription error: {str(e)}"
135
- })
136
- logger.warning("No recording in progress")
137
- return templates.TemplateResponse("index.html", {
138
- "request": request,
139
- "error": "No recording in progress."
140
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
  @app.post("/query", response_class=HTMLResponse)
143
  async def handle_query(request: Request, query_text: str = Form(...), use_retriever: str = Form("no")):
@@ -154,4 +137,4 @@ async def handle_query(request: Request, query_text: str = Form(...), use_retrie
154
  "Web_Search_Response": result["web_search_response"],
155
  "Final_Response": result["final_response"],
156
  "Error": result["error"]
157
- })
 
1
+ from fastapi import FastAPI, Request, Form, File, UploadFile
 
2
  from fastapi.responses import HTMLResponse, JSONResponse
3
  from fastapi.staticfiles import StaticFiles
4
  from fastapi.templating import Jinja2Templates
5
  from main import process_query
6
  from voice.speech_to_text import SpeechToText
7
  import os
 
 
 
8
  import logging
9
+ import tempfile
10
+ import shutil
11
+ import wave
12
+ from pydub import AudioSegment
13
 
14
  # Set up logging
15
  logging.basicConfig(level=logging.INFO)
 
23
  # Set up templates
24
  templates = Jinja2Templates(directory="templates")
25
 
26
+ root_dir = "./voice"
 
 
27
 
28
+ # Vosk model path
29
+ vosk_model_path = "./vosk-model-small-en-us-0.15"
30
 
31
  # Initialize SpeechToText
32
  stt = SpeechToText(model_path=vosk_model_path)
33
 
34
+ def is_valid_wav(file_path):
35
+ """Check if the file is a valid WAV file."""
 
 
 
 
 
36
  try:
37
+ with wave.open(file_path, 'rb') as wf:
38
+ logger.info(f"WAV file validated: channels={wf.getnchannels()}, rate={wf.getframerate()}, frames={wf.getnframes()}")
39
+ return True
40
+ except wave.Error as e:
41
+ logger.error(f"Invalid WAV file: {str(e)}")
42
+ return False
43
+
44
+ def convert_to_wav(input_path, output_path):
45
+ """Convert input audio to WAV format (16kHz, mono, 16-bit PCM)."""
 
 
 
 
 
 
 
 
 
 
46
  try:
47
+ audio = AudioSegment.from_file(input_path)
48
+ audio = audio.set_channels(1).set_frame_rate(16000).set_sample_width(2) # 16-bit PCM
49
+ audio.export(output_path, format="wav")
50
+ logger.info(f"Converted audio to WAV: {output_path}, size: {os.path.getsize(output_path)} bytes")
51
+ return True
 
 
 
 
 
 
 
52
  except Exception as e:
53
+ logger.error(f"Failed to convert audio to WAV: {str(e)}")
54
+ return False
 
55
 
56
  @app.get("/", response_class=HTMLResponse)
57
  async def get_index(request: Request):
58
  return templates.TemplateResponse("index.html", {"request": request})
59
 
60
+ @app.post("/upload_audio", response_class=HTMLResponse)
61
+ async def upload_audio(request: Request, audio_file: UploadFile = File(...)):
62
+ # Use a temporary directory to store the uploaded audio file
63
+ try:
64
+ with tempfile.TemporaryDirectory() as temp_dir:
65
+ # Save the uploaded file (could be WebM, OGG, etc.)
66
+ input_file_path = os.path.join(temp_dir, "input_audio")
67
+ with open(input_file_path, "wb") as buffer:
68
+ shutil.copyfileobj(audio_file.file, buffer)
69
+ file_size = os.path.getsize(input_file_path) if os.path.exists(input_file_path) else 0
70
+ logger.info(f"Uploaded audio saved to {input_file_path}, size: {file_size} bytes")
71
+
72
+ # Verify file was saved
73
+ if not os.path.exists(input_file_path) or file_size == 0:
74
+ logger.error("Uploaded audio file was not saved correctly")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  return templates.TemplateResponse("index.html", {
76
  "request": request,
77
+ "error": "Failed to save uploaded audio file."
78
+ })
79
+
80
+ # Convert to WAV
81
+ wav_file_path = os.path.join(root_dir, "temp_audio.wav")
82
+ if not convert_to_wav(input_file_path, wav_file_path):
83
+ logger.error("Audio conversion to WAV failed")
84
+ return templates.TemplateResponse("index.html", {
85
+ "request": request,
86
+ "error": "Failed to convert audio to WAV format."
87
  })
88
+
89
+ # Validate WAV file
90
+ if not is_valid_wav(wav_file_path):
91
+ logger.error("Converted file is not a valid WAV file")
92
+ return templates.TemplateResponse("index.html", {
93
+ "request": request,
94
+ "error": "Converted audio is not a valid WAV file."
95
+ })
96
+
97
+ # Transcribe the WAV file
98
+ try:
99
+ text = stt.transcribe_audio(wav_file_path)
100
+ logger.info(f"Transcription result: '{text}'")
101
+ if not text:
102
+ logger.warning("Transcription returned no text")
103
+ return templates.TemplateResponse("index.html", {
104
+ "request": request,
105
+ "error": "Could not understand the audio. Please try speaking clearly."
106
+ })
107
+ return templates.TemplateResponse("index.html", {
108
+ "request": request,
109
+ "transcribed_text": text
110
+ })
111
+ except Exception as e:
112
+ logger.error(f"Transcription error: {str(e)}")
113
+ return templates.TemplateResponse("index.html", {
114
+ "request": request,
115
+ "error": f"Transcription error: {str(e)}"
116
+ })
117
+
118
+ except Exception as e:
119
+ logger.error(f"Error processing uploaded audio: {str(e)}")
120
+ return templates.TemplateResponse("index.html", {
121
+ "request": request,
122
+ "error": f"Error processing audio: {str(e)}"
123
+ })
124
 
125
  @app.post("/query", response_class=HTMLResponse)
126
  async def handle_query(request: Request, query_text: str = Form(...), use_retriever: str = Form("no")):
 
137
  "Web_Search_Response": result["web_search_response"],
138
  "Final_Response": result["final_response"],
139
  "Error": result["error"]
140
+ })
device_app.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ from fastapi import FastAPI, Request, Form
3
+ from fastapi.responses import HTMLResponse, JSONResponse
4
+ from fastapi.staticfiles import StaticFiles
5
+ from fastapi.templating import Jinja2Templates
6
+ from main import process_query
7
+ from voice.speech_to_text import SpeechToText
8
+ import os
9
+ import asyncio
10
+ import pyaudio
11
+ import wave
12
+ import logging
13
+
14
+ # Set up logging
15
+ logging.basicConfig(level=logging.INFO)
16
+ logger = logging.getLogger(__name__)
17
+
18
+ app = FastAPI()
19
+
20
+ # Mount static files (for CSS, JS, etc.)
21
+ app.mount("/static", StaticFiles(directory="static"), name="static")
22
+
23
+ # Set up templates
24
+ templates = Jinja2Templates(directory="templates")
25
+
26
+ # Vosk model path and audio file path
27
+ vosk_model_path = "./vosk-model-small-en-us-0.15"
28
+ audio_file_path = "voice/temp_audio.wav"
29
+
30
+ # Ensure the voice directory exists
31
+ os.makedirs("voice", exist_ok=True)
32
+
33
+ # Initialize SpeechToText
34
+ stt = SpeechToText(model_path=vosk_model_path)
35
+
36
+ # Global variables for recording state
37
+ recording = False
38
+ audio_frames = []
39
+ recording_task = None
40
+
41
+ def save_audio_to_wav(frames, sample_rate=16000):
42
+ """Save audio frames to a WAV file."""
43
+ try:
44
+ logger.info(f"Saving audio to {audio_file_path} with {len(frames)} frames")
45
+ wf = wave.open(audio_file_path, 'wb')
46
+ wf.setnchannels(1)
47
+ wf.setsampwidth(2) # 16-bit
48
+ wf.setframerate(sample_rate)
49
+ wf.writeframes(b''.join(frames))
50
+ wf.close()
51
+ if os.path.exists(audio_file_path):
52
+ logger.info(f"WAV file saved successfully: {os.path.getsize(audio_file_path)} bytes")
53
+ else:
54
+ logger.error("WAV file was not created")
55
+ except Exception as e:
56
+ logger.error(f"Error saving WAV file: {str(e)}")
57
+ raise
58
+
59
+ async def record_audio():
60
+ """Background task to record audio."""
61
+ global audio_frames
62
+ p = pyaudio.PyAudio()
63
+ try:
64
+ stream = p.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True, frames_per_buffer=1024)
65
+ stream.start_stream()
66
+ logger.info("Recording started...")
67
+
68
+ while recording:
69
+ data = stream.read(1024, exception_on_overflow=False)
70
+ audio_frames.append(data)
71
+ await asyncio.sleep(0.01) # Small sleep to prevent blocking
72
+
73
+ stream.stop_stream()
74
+ stream.close()
75
+ logger.info(f"Recording stopped, captured {len(audio_frames)} frames")
76
+ except Exception as e:
77
+ logger.error(f"Error during recording: {str(e)}")
78
+ finally:
79
+ p.terminate()
80
+
81
+ @app.get("/", response_class=HTMLResponse)
82
+ async def get_index(request: Request):
83
+ return templates.TemplateResponse("index.html", {"request": request})
84
+
85
+ @app.post("/start_recording", response_class=JSONResponse)
86
+ async def start_recording():
87
+ global recording, audio_frames, recording_task
88
+ if not recording:
89
+ recording = True
90
+ audio_frames = []
91
+ recording_task = asyncio.create_task(record_audio())
92
+ logger.info("Started recording task")
93
+ return {"status": "Recording started"}
94
+ logger.warning("Recording already in progress")
95
+ return {"status": "Already recording"}
96
+
97
+ @app.post("/stop_recording", response_class=HTMLResponse)
98
+ async def stop_recording(request: Request):
99
+ global recording, recording_task
100
+ if recording:
101
+ recording = False
102
+ if recording_task:
103
+ await recording_task # Wait for the recording task to complete
104
+ recording_task = None
105
+
106
+ # Save the audio to WAV
107
+ try:
108
+ save_audio_to_wav(audio_frames)
109
+ except Exception as e:
110
+ logger.error(f"Failed to save audio: {str(e)}")
111
+ return templates.TemplateResponse("index.html", {
112
+ "request": request,
113
+ "error": f"Failed to save audio: {str(e)}"
114
+ })
115
+
116
+ # Transcribe the saved audio
117
+ try:
118
+ text = stt.transcribe_audio(audio_file_path)
119
+ logger.info(f"Transcription result: '{text}'")
120
+ if not text:
121
+ logger.warning("Transcription returned no text")
122
+ return templates.TemplateResponse("index.html", {
123
+ "request": request,
124
+ "error": "Could not understand the audio."
125
+ })
126
+ return templates.TemplateResponse("index.html", {
127
+ "request": request,
128
+ "transcribed_text": text
129
+ })
130
+ except Exception as e:
131
+ logger.error(f"Transcription error: {str(e)}")
132
+ return templates.TemplateResponse("index.html", {
133
+ "request": request,
134
+ "error": f"Transcription error: {str(e)}"
135
+ })
136
+ logger.warning("No recording in progress")
137
+ return templates.TemplateResponse("index.html", {
138
+ "request": request,
139
+ "error": "No recording in progress."
140
+ })
141
+
142
+ @app.post("/query", response_class=HTMLResponse)
143
+ async def handle_query(request: Request, query_text: str = Form(...), use_retriever: str = Form("no")):
144
+ use_retriever = use_retriever.lower() in ["yes", "y"]
145
+ result = await process_query(vosk_model_path, query_text=query_text, use_retriever=use_retriever)
146
+
147
+ return templates.TemplateResponse("index.html", {
148
+ "request": request,
149
+ "User_Query": query_text,
150
+ "Intent": result["intent"],
151
+ "Entities": result["entities"],
152
+ "API_Response": result["base_response"],
153
+ "RAG_Response": result["retriever_response"],
154
+ "Web_Search_Response": result["web_search_response"],
155
+ "Final_Response": result["final_response"],
156
+ "Error": result["error"]
157
+ })
modules/__pycache__/get_financial_ratios.cpython-310.pyc CHANGED
Binary files a/modules/__pycache__/get_financial_ratios.cpython-310.pyc and b/modules/__pycache__/get_financial_ratios.cpython-310.pyc differ
 
modules/__pycache__/get_interest.cpython-310.pyc CHANGED
Binary files a/modules/__pycache__/get_interest.cpython-310.pyc and b/modules/__pycache__/get_interest.cpython-310.pyc differ
 
rag/__pycache__/sql_db.cpython-310.pyc CHANGED
Binary files a/rag/__pycache__/sql_db.cpython-310.pyc and b/rag/__pycache__/sql_db.cpython-310.pyc differ
 
rag/sql_db.py CHANGED
@@ -162,7 +162,7 @@ class SQL_Key_Pair:
162
  value = result[0]
163
  value_in_billions = value / 1_000_000_000
164
  return f"{metric} for {ticker}: ${value_in_billions:.2f} billion."
165
- return f"No {metric} data found for {ticker}."
166
  except Exception as e:
167
  print(f"Error querying database: {e}")
168
  return f"Error querying database: {str(e)}"
 
162
  value = result[0]
163
  value_in_billions = value / 1_000_000_000
164
  return f"{metric} for {ticker}: ${value_in_billions:.2f} billion."
165
+ return f"No relevant data found for {ticker}."
166
  except Exception as e:
167
  print(f"Error querying database: {e}")
168
  return f"Error querying database: {str(e)}"
requirements.txt CHANGED
@@ -31,7 +31,7 @@ platformdirs==4.3.7
31
  pluggy==1.5.0
32
  primp==0.14.0
33
  pandas
34
- pyaudio==0.2.14
35
  pydantic==2.11.2
36
  pydantic-core==2.33.1
37
  pydantic-settings==2.8.1
@@ -63,6 +63,7 @@ tomli==2.2.1
63
  tqdm==4.67.1
64
  typer==0.15.2
65
  typing-inspection==0.4.0
 
66
  uvicorn==0.34.0
67
  vosk==0.3.45
68
  wasabi==1.1.3
@@ -70,3 +71,8 @@ weasel==0.4.1
70
  yarl==1.19.0
71
  langchain
72
  langchain_community
 
 
 
 
 
 
31
  pluggy==1.5.0
32
  primp==0.14.0
33
  pandas
34
+ pydub==0.25.1
35
  pydantic==2.11.2
36
  pydantic-core==2.33.1
37
  pydantic-settings==2.8.1
 
63
  tqdm==4.67.1
64
  typer==0.15.2
65
  typing-inspection==0.4.0
66
+ transformers
67
  uvicorn==0.34.0
68
  vosk==0.3.45
69
  wasabi==1.1.3
 
71
  yarl==1.19.0
72
  langchain
73
  langchain_community
74
+ typing_extensions
75
+ urllib3
76
+ idna
77
+
78
+
templates/index.html CHANGED
@@ -1,4 +1,3 @@
1
- <!-- templates/index.html -->
2
  <!DOCTYPE html>
3
  <html lang="en">
4
  <head>
@@ -8,7 +7,7 @@
8
  <script src="https://cdn.tailwindcss.com"></script>
9
  <link rel="stylesheet" href="/static/css/styles.css">
10
  </head>
11
- <body class="bg-purple-100 font-sans !important">
12
  <div class="container mx-auto p-1">
13
  <h1 class="text-5xl font-bold text-center text-black mb-3 drop-shadow-lg">Financial AI Agent</h1>
14
 
@@ -16,10 +15,10 @@
16
  <div class="bg-white p-3 rounded-lg shadow-lg mb-6">
17
  <div class="flex justify-center space-x-4">
18
  <button type="button" id="startRecording" class="bg-green-500 text-white px-6 py-3 rounded-lg hover:bg-green-800 transition">
19
- <strong>Start Recording</strong>
20
  </button>
21
  <button type="button" id="stopRecording" class="bg-red-400 text-white px-6 py-3 rounded-lg hover:bg-red-700 transition hidden">
22
- <strong>Stop Recording</strong>
23
  </button>
24
  </div>
25
  </div>
@@ -34,18 +33,6 @@
34
  <button type="submit" id="submitButton" class="bg-indigo-600 text-white px-6 py-3 rounded-lg hover:bg-red-500 transition" disabled>
35
  <strong>Submit Query</strong>
36
  </button>
37
- <!-- Script to change button text on submit -->
38
- <script>
39
- document.addEventListener("DOMContentLoaded", function () {
40
- const form = document.querySelector("form");
41
- const submitButton = document.getElementById("submitButton");
42
-
43
- form.addEventListener("submit", function () {
44
- submitButton.innerText = "Processing...";
45
- submitButton.disabled = true;
46
- });
47
- });
48
- </script>
49
  </div>
50
  <div class="flex items-center space-x-2">
51
  <input type="checkbox" name="use_retriever" id="useRetriever" value="yes" class="h-5 w-5 text-indigo-600">
@@ -106,9 +93,122 @@
106
  </div>
107
  {% endif %}
108
  </div>
109
-
110
  </div>
111
 
112
- <script src="/static/js/script.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  </body>
114
  </html>
 
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
 
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="/static/css/styles.css">
9
  </head>
10
+ <body class="bg-purple-100 font-sans">
11
  <div class="container mx-auto p-1">
12
  <h1 class="text-5xl font-bold text-center text-black mb-3 drop-shadow-lg">Financial AI Agent</h1>
13
 
 
15
  <div class="bg-white p-3 rounded-lg shadow-lg mb-6">
16
  <div class="flex justify-center space-x-4">
17
  <button type="button" id="startRecording" class="bg-green-500 text-white px-6 py-3 rounded-lg hover:bg-green-800 transition">
18
+ <strong>Start Recording</strong>
19
  </button>
20
  <button type="button" id="stopRecording" class="bg-red-400 text-white px-6 py-3 rounded-lg hover:bg-red-700 transition hidden">
21
+ <strong>Stop Recording</strong>
22
  </button>
23
  </div>
24
  </div>
 
33
  <button type="submit" id="submitButton" class="bg-indigo-600 text-white px-6 py-3 rounded-lg hover:bg-red-500 transition" disabled>
34
  <strong>Submit Query</strong>
35
  </button>
 
 
 
 
 
 
 
 
 
 
 
 
36
  </div>
37
  <div class="flex items-center space-x-2">
38
  <input type="checkbox" name="use_retriever" id="useRetriever" value="yes" class="h-5 w-5 text-indigo-600">
 
93
  </div>
94
  {% endif %}
95
  </div>
 
96
  </div>
97
 
98
+ <script>
99
+ document.addEventListener("DOMContentLoaded", function () {
100
+ const startRecording = document.getElementById("startRecording");
101
+ const stopRecording = document.getElementById("stopRecording");
102
+ const queryText = document.getElementById("queryText");
103
+ const submitButton = document.getElementById("submitButton");
104
+ const form = document.getElementById("queryForm");
105
+ let mediaRecorder = null;
106
+ let audioStream = null;
107
+ let audioChunks = [];
108
+
109
+ // Request microphone permission and start recording
110
+ startRecording.addEventListener("click", async () => {
111
+ try {
112
+ audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
113
+ mediaRecorder = new MediaRecorder(audioStream);
114
+
115
+ mediaRecorder.ondataavailable = (event) => {
116
+ if (event.data.size > 0) {
117
+ audioChunks.push(event.data);
118
+ console.log("Audio chunk received, size:", event.data.size, "type:", event.data.type);
119
+ }
120
+ };
121
+
122
+ mediaRecorder.onstop = async () => {
123
+ // Stop all tracks to release the microphone
124
+ if (audioStream) {
125
+ audioStream.getTracks().forEach(track => {
126
+ track.stop();
127
+ console.log("Track stopped:", track);
128
+ });
129
+ audioStream = null;
130
+ }
131
+
132
+ // Create audio blob (use browser's default format)
133
+ const audioBlob = new Blob(audioChunks, { type: mediaRecorder.mimeType });
134
+ console.log("Audio blob created, size:", audioBlob.size, "type:", audioBlob.type);
135
+ const formData = new FormData();
136
+ formData.append("audio_file", audioBlob, "recorded_audio.webm");
137
+
138
+ try {
139
+ const response = await fetch("/upload_audio", {
140
+ method: "POST",
141
+ body: formData
142
+ });
143
+
144
+ const html = await response.text();
145
+ // Parse the response HTML to extract transcribed text or error
146
+ const parser = new DOMParser();
147
+ const doc = parser.parseFromString(html, "text/html");
148
+ const transcribedText = doc.querySelector("input[name='query_text']").value;
149
+ const error = doc.querySelector(".bg-red-100 p")?.textContent;
150
+
151
+ if (error) {
152
+ alert(error);
153
+ } else if (transcribedText) {
154
+ queryText.value = transcribedText;
155
+ queryText.readOnly = false; // Allow editing
156
+ submitButton.disabled = false; // Enable submit button
157
+ }
158
+
159
+ // Reset UI
160
+ startRecording.classList.remove("hidden");
161
+ stopRecording.classList.add("hidden");
162
+ audioChunks = [];
163
+ mediaRecorder = null;
164
+ } catch (error) {
165
+ console.error("Error uploading audio:", error);
166
+ alert("Failed to process audio: " + error.message);
167
+ startRecording.classList.remove("hidden");
168
+ stopRecording.classList.add("hidden");
169
+ }
170
+ };
171
+
172
+ mediaRecorder.onerror = (event) => {
173
+ console.error("MediaRecorder error:", event.error);
174
+ alert("Recording error: " + event.error.message);
175
+ };
176
+
177
+ mediaRecorder.start(100); // Collect chunks every 100ms
178
+ console.log("Recording started, MIME type:", mediaRecorder.mimeType);
179
+ startRecording.classList.add("hidden");
180
+ stopRecording.classList.remove("hidden");
181
+ } catch (error) {
182
+ console.error("Error accessing microphone:", error);
183
+ alert("Could not access microphone. Please check permissions.");
184
+ }
185
+ });
186
+
187
+ // Stop recording
188
+ stopRecording.addEventListener("click", () => {
189
+ if (mediaRecorder && mediaRecorder.state !== "inactive") {
190
+ console.log("Stopping recording, current state:", mediaRecorder.state);
191
+ mediaRecorder.stop();
192
+ } else {
193
+ console.warn("MediaRecorder is not active or not initialized");
194
+ }
195
+
196
+ // Ensure stream tracks are stopped even if MediaRecorder fails
197
+ if (audioStream) {
198
+ audioStream.getTracks().forEach(track => {
199
+ track.stop();
200
+ console.log("Track stopped:", track);
201
+ });
202
+ audioStream = null;
203
+ }
204
+ });
205
+
206
+ // Handle form submission
207
+ form.addEventListener("submit", () => {
208
+ submitButton.innerText = "Processing...";
209
+ submitButton.disabled = true;
210
+ });
211
+ });
212
+ </script>
213
  </body>
214
  </html>
voice/__pycache__/intent_classifier.cpython-310.pyc CHANGED
Binary files a/voice/__pycache__/intent_classifier.cpython-310.pyc and b/voice/__pycache__/intent_classifier.cpython-310.pyc differ