shamimjony1000 commited on
Commit
24dab17
1 Parent(s): 83fca7e

Upload 9 files

Browse files
Files changed (9) hide show
  1. app.py +6 -0
  2. database.py +97 -0
  3. gemini.py +84 -0
  4. memory.py +148 -0
  5. requests.db +0 -0
  6. requirements.txt +7 -0
  7. text_to_speech.py +11 -0
  8. ui.py +273 -0
  9. voice.py +50 -0
app.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from ui import create_ui
3
+
4
+ if __name__ == "__main__":
5
+ app = create_ui()
6
+ app.launch()
database.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sqlite3
2
+ from datetime import datetime
3
+ import time
4
+ from contextlib import contextmanager
5
+
6
+ class Database:
7
+ def __init__(self, db_name="requests.db"):
8
+ self.db_name = db_name
9
+ self.max_retries = 3
10
+ self.retry_delay = 1
11
+ self.initialize_database()
12
+
13
+ @contextmanager
14
+ def get_connection(self):
15
+ conn = sqlite3.connect(self.db_name)
16
+ try:
17
+ yield conn
18
+ finally:
19
+ conn.close()
20
+
21
+ def initialize_database(self):
22
+ for attempt in range(self.max_retries):
23
+ try:
24
+ with self.get_connection() as conn:
25
+ conn.execute('PRAGMA encoding="UTF-8"')
26
+ cursor = conn.cursor()
27
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='requests'")
28
+ if not cursor.fetchone():
29
+ self.create_table(conn)
30
+ else:
31
+ cursor.execute('PRAGMA table_info(requests)')
32
+ columns = [col[1] for col in cursor.fetchall()]
33
+ required_columns = ['id', 'timestamp', 'project_number', 'project_name', 'amount', 'reason', 'original_text']
34
+
35
+ if not all(col in columns for col in required_columns):
36
+ cursor.execute('ALTER TABLE requests RENAME TO requests_old')
37
+ self.create_table(conn)
38
+ cursor.execute('''
39
+ INSERT INTO requests (timestamp, project_number, project_name, amount, reason)
40
+ SELECT timestamp, project_number, project_name, amount, reason
41
+ FROM requests_old
42
+ ''')
43
+ cursor.execute('DROP TABLE requests_old')
44
+ conn.commit()
45
+ return
46
+ except sqlite3.OperationalError as e:
47
+ if attempt < self.max_retries - 1:
48
+ time.sleep(self.retry_delay)
49
+ continue
50
+ raise Exception(f"Could not initialize database after {self.max_retries} attempts: {str(e)}")
51
+
52
+ def create_table(self, conn):
53
+ cursor = conn.cursor()
54
+ cursor.execute('''
55
+ CREATE TABLE IF NOT EXISTS requests (
56
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
57
+ timestamp DATETIME,
58
+ project_number TEXT,
59
+ project_name TEXT,
60
+ amount REAL,
61
+ reason TEXT,
62
+ original_text TEXT
63
+ )
64
+ ''')
65
+ conn.commit()
66
+
67
+ def add_request(self, project_number, project_name, amount, reason, original_text=""):
68
+ for attempt in range(self.max_retries):
69
+ try:
70
+ with self.get_connection() as conn:
71
+ cursor = conn.cursor()
72
+ cursor.execute('''
73
+ INSERT INTO requests (timestamp, project_number, project_name, amount, reason, original_text)
74
+ VALUES (?, ?, ?, ?, ?, ?)
75
+ ''', (datetime.now(), project_number, project_name, amount, reason, original_text))
76
+ conn.commit()
77
+ return
78
+ except sqlite3.OperationalError as e:
79
+ if attempt < self.max_retries - 1:
80
+ time.sleep(self.retry_delay)
81
+ continue
82
+ raise Exception(f"Could not add request after {self.max_retries} attempts: {str(e)}")
83
+
84
+ def get_all_requests(self):
85
+ for attempt in range(self.max_retries):
86
+ try:
87
+ with self.get_connection() as conn:
88
+ cursor = conn.cursor()
89
+ cursor.execute('SELECT * FROM requests ORDER BY timestamp DESC')
90
+ columns = [description[0] for description in cursor.description]
91
+ results = cursor.fetchall()
92
+ return [dict(zip(columns, row)) for row in results]
93
+ except sqlite3.OperationalError as e:
94
+ if attempt < self.max_retries - 1:
95
+ time.sleep(self.retry_delay)
96
+ continue
97
+ raise Exception(f"Could not fetch requests after {self.max_retries} attempts: {str(e)}")
gemini.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ import os
3
+ import json
4
+ import re
5
+
6
+ class GeminiProcessor:
7
+ def __init__(self):
8
+ api_key = "AIzaSyCLyDgZNcE_v4wLMFF8SoimKga9bbLSun0"
9
+ if not api_key:
10
+ raise ValueError("GOOGLE_API_KEY not found in environment variables")
11
+ genai.configure(api_key=api_key)
12
+ self.model = genai.GenerativeModel('gemini-pro')
13
+ self.config = genai.GenerationConfig(temperature=0)
14
+
15
+ def is_arabic(self, text):
16
+ arabic_pattern = re.compile('[\u0600-\u06FF]')
17
+ return bool(arabic_pattern.search(text))
18
+
19
+ def translate_arabic_to_english(self, text):
20
+ prompt = f"""
21
+ Translate the following Arabic text to English. If the text is mixed (Arabic and English),
22
+ translate only the Arabic parts and keep the English parts as is.
23
+ Keep numbers in their original format.
24
+
25
+ Text to translate: {text}
26
+ """
27
+ try:
28
+ response = self.model.generate_content(prompt)
29
+ return response.text.strip()
30
+ except Exception as e:
31
+ print(f"Translation error: {e}")
32
+ return text
33
+
34
+ def extract_request_details(self, text, context=""):
35
+ full_text = f"{context} {text}".strip()
36
+ is_arabic_input = self.is_arabic(full_text)
37
+
38
+ if is_arabic_input:
39
+ translated_text = self.translate_arabic_to_english(full_text)
40
+ processing_text = translated_text
41
+ else:
42
+ processing_text = full_text
43
+
44
+ prompt = f"""
45
+ Extract the following information from this text and previous context.
46
+ The input has been translated from Arabic if it contained Arabic text.
47
+
48
+ If any information is missing, leave it empty.
49
+ Format the response exactly as a JSON object with these keys:
50
+ {{
51
+ "project_number": "extracted number or empty string",
52
+ "project_name": "extracted name or empty string",
53
+ "amount": extracted number or 0,
54
+ "reason": "extracted reason or empty string",
55
+ "missing_fields": ["list of missing required fields"],
56
+ "original_text": "the original input text"
57
+ }}
58
+
59
+ ##No preamble## Response in VALID JSON ONLY##
60
+
61
+ Text to analyze: {processing_text}
62
+ """
63
+
64
+ try:
65
+ response = self.model.generate_content(prompt, generation_config=self.config)
66
+ result = json.loads(response.text)
67
+
68
+ required_keys = ['project_number', 'project_name', 'amount', 'reason', 'missing_fields']
69
+ if not all(key in result for key in required_keys):
70
+ raise ValueError("Missing required keys in response")
71
+
72
+ result['amount'] = float(result.get('amount', 0))
73
+ result['original_text'] = full_text
74
+
75
+ if is_arabic_input:
76
+ result['translated_text'] = processing_text
77
+
78
+ return result
79
+ except json.JSONDecodeError as e:
80
+ print(f"JSON parsing error: {e}")
81
+ return None
82
+ except Exception as e:
83
+ print(f"Error processing request: {e}")
84
+ return None
memory.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime, timedelta
2
+ import json
3
+ from gtts import gTTS
4
+ import io
5
+
6
+ class MemoryHandler:
7
+ def __init__(self):
8
+ self.conversation_history = []
9
+ self.max_history = 5
10
+ self.context_timeout = timedelta(minutes=2)
11
+ self.last_interaction_time = None
12
+ self.partial_info = {
13
+ 'project_number': None,
14
+ 'project_name': None,
15
+ 'amount': None,
16
+ 'reason': None,
17
+ 'timestamp': None
18
+ }
19
+ self.confidence_scores = {
20
+ 'project_number': 0.0,
21
+ 'project_name': 0.0,
22
+ 'amount': 0.0,
23
+ 'reason': 0.0
24
+ }
25
+
26
+ def add_interaction(self, text: str, extracted_info: dict = None) -> None:
27
+ current_time = datetime.now()
28
+
29
+ if self.last_interaction_time and \
30
+ (current_time - self.last_interaction_time) > self.context_timeout:
31
+ self.clear_partial_info()
32
+
33
+ if text:
34
+ self.conversation_history.append({
35
+ 'text': text,
36
+ 'timestamp': current_time.isoformat(),
37
+ 'extracted_info': extracted_info
38
+ })
39
+ if len(self.conversation_history) > self.max_history:
40
+ self.conversation_history.pop(0)
41
+
42
+ if extracted_info:
43
+ self._update_partial_info(extracted_info, current_time)
44
+
45
+ self.last_interaction_time = current_time
46
+
47
+ def _update_partial_info(self, extracted_info: dict, current_time: datetime) -> None:
48
+ for key in self.partial_info:
49
+ if key in extracted_info and extracted_info[key]:
50
+ new_value = extracted_info[key]
51
+ current_value = self.partial_info[key]
52
+
53
+ if (current_value is None or
54
+ extracted_info.get(f'{key}_confidence', 0.5) >
55
+ self.confidence_scores.get(key, 0)):
56
+ self.partial_info[key] = new_value
57
+ self.confidence_scores[key] = extracted_info.get(f'{key}_confidence', 0.5)
58
+
59
+ self.partial_info['timestamp'] = current_time
60
+
61
+ def get_context(self) -> str:
62
+ context_parts = []
63
+
64
+ for entry in self.conversation_history:
65
+ timestamp = datetime.fromisoformat(entry['timestamp']).strftime('%H:%M:%S')
66
+ context_parts.append(f"[{timestamp}] {entry['text']}")
67
+
68
+ context = " ".join(context_parts)
69
+
70
+ partial_context = []
71
+ for key, value in self.partial_info.items():
72
+ if value and key != 'timestamp':
73
+ confidence = self.confidence_scores.get(key, 0)
74
+ partial_context.append(f"{key}: {value} (confidence: {confidence:.2f})")
75
+
76
+ if partial_context:
77
+ context += "\nPartial information: " + ", ".join(partial_context)
78
+
79
+ return context
80
+
81
+ def get_partial_info(self) -> dict:
82
+ info = {k: v for k, v in self.partial_info.items()
83
+ if k != 'timestamp' and v is not None}
84
+ info['confidence_scores'] = self.confidence_scores
85
+ return info
86
+
87
+ def merge_partial_info(self, new_info: dict) -> None:
88
+ for key in self.partial_info:
89
+ if key in new_info and new_info[key] is not None:
90
+ new_confidence = new_info.get(f'{key}_confidence', 0.5)
91
+ if (self.partial_info[key] is None or
92
+ new_confidence > self.confidence_scores.get(key, 0)):
93
+ self.partial_info[key] = new_info[key]
94
+ self.confidence_scores[key] = new_confidence
95
+
96
+ def clear_partial_info(self) -> None:
97
+ self.partial_info = {
98
+ 'project_number': None,
99
+ 'project_name': None,
100
+ 'amount': None,
101
+ 'reason': None,
102
+ 'timestamp': None
103
+ }
104
+ self.confidence_scores = {
105
+ 'project_number': 0.0,
106
+ 'project_name': 0.0,
107
+ 'amount': 0.0,
108
+ 'reason': 0.0
109
+ }
110
+
111
+ def clear_memory(self) -> None:
112
+ self.conversation_history = []
113
+ self.clear_partial_info()
114
+ self.last_interaction_time = None
115
+ return "Memory cleared!"
116
+
117
+ def get_missing_fields(self) -> list:
118
+ missing = []
119
+ confidence_threshold = 0.5
120
+
121
+ for field in ['project_number', 'project_name', 'amount', 'reason']:
122
+ if (self.partial_info.get(field) is None or
123
+ self.confidence_scores.get(field, 0) < confidence_threshold):
124
+ missing.append(field)
125
+ return missing
126
+
127
+ def get_prompt_for_missing_info(self) -> str:
128
+ missing = self.get_missing_fields()
129
+ if not missing:
130
+ return "All required information has been provided with sufficient confidence."
131
+
132
+ current_info = self.get_partial_info()
133
+ prompt = "Current information:\n"
134
+
135
+ for key, value in current_info.items():
136
+ if key != 'confidence_scores' and value is not None:
137
+ confidence = self.confidence_scores.get(key, 0)
138
+ prompt += f"- {key}: {value} (confidence: {confidence:.2f})\n"
139
+
140
+ prompt += "\nPlease provide or clarify the following information:\n"
141
+ for field in missing:
142
+ current_confidence = self.confidence_scores.get(field, 0)
143
+ if current_confidence > 0:
144
+ prompt += f"- {field} (current confidence: {current_confidence:.2f}, needs improvement)\n"
145
+ else:
146
+ prompt += f"- {field} (missing)\n"
147
+
148
+ return prompt
requests.db ADDED
Binary file (12.3 kB). View file
 
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio
2
+ pandas
3
+ google-generativeai
4
+ SpeechRecognition
5
+ pydub
6
+ gTTS
7
+ python-dotenv
text_to_speech.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from gtts import gTTS
2
+ import io
3
+
4
+ def play_text(text: str) -> tuple[str, str]:
5
+ try:
6
+ tts = gTTS(text=text, lang='en')
7
+ audio_path = "temp_audio.mp3"
8
+ tts.save(audio_path)
9
+ return audio_path, None
10
+ except Exception as e:
11
+ return None, f"Error generating audio: {str(e)}"
ui.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ from database import Database
4
+ from voice import VoiceHandler
5
+ from gemini import GeminiProcessor
6
+ from memory import MemoryHandler
7
+ from text_to_speech import play_text
8
+
9
+ def create_ui():
10
+ # Initialize components
11
+ db = Database()
12
+ voice_handler = VoiceHandler()
13
+ gemini_processor = GeminiProcessor()
14
+ memory_handler = MemoryHandler()
15
+
16
+ def validate_request(project_number, project_name, amount, reason):
17
+ if not project_number or not project_name or not amount or not reason:
18
+ missing_fields = []
19
+ if not project_number: missing_fields.append("project number")
20
+ if not project_name: missing_fields.append("project name")
21
+ if not amount: missing_fields.append("amount")
22
+ if not reason: missing_fields.append("reason")
23
+ return False, f"Please provide: {', '.join(missing_fields)}"
24
+ return True, ""
25
+
26
+ def process_text_input(text, language):
27
+ if not text:
28
+ return "Please enter some text first.", None, None, None, None
29
+
30
+ context = memory_handler.get_context()
31
+ details = gemini_processor.extract_request_details(text, context)
32
+
33
+ if not details:
34
+ return "Could not extract request details. Please try again.", None, None, None, None
35
+
36
+ memory_handler.add_interaction(text, details)
37
+ partial_info = memory_handler.get_partial_info()
38
+
39
+ return (
40
+ f"Text processed! {memory_handler.get_prompt_for_missing_info()}",
41
+ partial_info.get('project_number', ''),
42
+ partial_info.get('project_name', ''),
43
+ partial_info.get('amount', 0),
44
+ partial_info.get('reason', '')
45
+ )
46
+
47
+ def process_voice_input(audio_path, language):
48
+ if not audio_path:
49
+ return "No audio detected.", None, None, None, None
50
+
51
+ voice_text = voice_handler.process_audio_file(audio_path, language)
52
+ if voice_text.startswith("Error:"):
53
+ return voice_text, None, None, None, None
54
+
55
+ context = memory_handler.get_context()
56
+ details = gemini_processor.extract_request_details(voice_text, context)
57
+
58
+ if not details:
59
+ return "Could not extract request details. Please try again.", None, None, None, None
60
+
61
+ memory_handler.add_interaction(voice_text, details)
62
+ partial_info = memory_handler.get_partial_info()
63
+
64
+ return (
65
+ f"Voice processed! You said: {voice_text}\n\n{memory_handler.get_prompt_for_missing_info()}",
66
+ partial_info.get('project_number', ''),
67
+ partial_info.get('project_name', ''),
68
+ partial_info.get('amount', 0),
69
+ partial_info.get('reason', '')
70
+ )
71
+
72
+ def confirm_submission(project_number, project_name, amount, reason):
73
+ is_valid, message = validate_request(project_number, project_name, amount, reason)
74
+ if not is_valid:
75
+ return (
76
+ message, # confirmation_output
77
+ None, # confirmation_audio
78
+ gr.update(interactive=False), # submit_btn
79
+ gr.update(interactive=True), # confirm_btn
80
+ gr.update(interactive=True), # project_number
81
+ gr.update(interactive=True), # project_name
82
+ gr.update(interactive=True), # amount
83
+ gr.update(interactive=True) # reason
84
+ )
85
+
86
+ confirmation_text = f"Sir please ensure before submit project number: {project_number}, project name: {project_name}, amount: {amount} riyals, reason for request: {reason} are ok"
87
+ audio_path, error = play_text(confirmation_text)
88
+
89
+ if error:
90
+ return (
91
+ error, # confirmation_output
92
+ None, # confirmation_audio
93
+ gr.update(interactive=False), # submit_btn
94
+ gr.update(interactive=True), # confirm_btn
95
+ gr.update(interactive=True), # project_number
96
+ gr.update(interactive=True), # project_name
97
+ gr.update(interactive=True), # amount
98
+ gr.update(interactive=True) # reason
99
+ )
100
+
101
+ return (
102
+ "Please confirm the details you heard.", # confirmation_output
103
+ audio_path, # confirmation_audio
104
+ gr.update(interactive=True), # submit_btn
105
+ gr.update(interactive=False), # confirm_btn
106
+ gr.update(interactive=False), # project_number
107
+ gr.update(interactive=False), # project_name
108
+ gr.update(interactive=False), # amount
109
+ gr.update(interactive=False) # reason
110
+ )
111
+
112
+ def submit_request(project_number, project_name, amount, reason):
113
+ is_valid, message = validate_request(project_number, project_name, amount, reason)
114
+
115
+ if not is_valid:
116
+ return message, None
117
+
118
+ try:
119
+ db.add_request(project_number, project_name, float(amount), reason)
120
+ memory_handler.clear_memory()
121
+ return "Request successfully added!", get_requests_df()
122
+ except Exception as e:
123
+ return f"Error saving request: {str(e)}", None
124
+
125
+ def get_requests_df():
126
+ try:
127
+ requests = db.get_all_requests()
128
+ if requests:
129
+ df = pd.DataFrame(requests)
130
+ columns = ['timestamp', 'project_number', 'project_name', 'amount', 'reason']
131
+ df = df[columns]
132
+ headers = df.columns.tolist()
133
+ data = df.values.tolist()
134
+ return {"headers": headers, "data": data}
135
+ return {"headers": ['timestamp', 'project_number', 'project_name', 'amount', 'reason'], "data": []}
136
+ except Exception as e:
137
+ print(f"Error getting requests: {str(e)}")
138
+ return {"headers": ['timestamp', 'project_number', 'project_name', 'amount', 'reason'], "data": []}
139
+
140
+ def reset_form():
141
+ return (
142
+ gr.update(value=""), # project_number
143
+ gr.update(value=""), # project_name
144
+ gr.update(value=None), # amount
145
+ gr.update(value=""), # reason
146
+ gr.update(value=""), # confirmation_output
147
+ gr.update(value=None), # confirmation_audio
148
+ gr.update(interactive=False), # submit_btn
149
+ gr.update(interactive=True), # confirm_btn
150
+ gr.update(interactive=True), # project_number
151
+ gr.update(interactive=True), # project_name
152
+ gr.update(interactive=True), # amount
153
+ gr.update(interactive=True), # reason
154
+ gr.update(value=""), # text_input
155
+ gr.update(value=None), # audio_input
156
+ gr.update(value="") # process_output
157
+ )
158
+
159
+ # Create UI layout
160
+ with gr.Blocks(title="AI Agent Money Request System") as app:
161
+ gr.Markdown("# AI Agent Money Request System")
162
+
163
+ with gr.Tab("Input"):
164
+ language = gr.Dropdown(
165
+ choices=["English", "Arabic", "Mixed (Arabic/English)"],
166
+ value="English",
167
+ label="Select Language"
168
+ )
169
+
170
+ with gr.Tab("Voice Input"):
171
+ audio_input = gr.Audio(
172
+ label="Voice Input",
173
+ type="filepath",
174
+ sources=["microphone"]
175
+ )
176
+ voice_process_btn = gr.Button("Process Voice")
177
+
178
+ with gr.Tab("Text Input"):
179
+ text_input = gr.Textbox(
180
+ lines=3,
181
+ placeholder="Enter your request here...",
182
+ label="Text Input"
183
+ )
184
+ text_process_btn = gr.Button("Process Text")
185
+
186
+ process_output = gr.Textbox(label="Processing Result")
187
+
188
+ with gr.Group():
189
+ project_number = gr.Textbox(label="Project Number")
190
+ project_name = gr.Textbox(label="Project Name")
191
+ amount = gr.Number(label="Amount (in riyals)")
192
+ reason = gr.Textbox(label="Reason for Request")
193
+
194
+ with gr.Row():
195
+ confirm_btn = gr.Button("Confirm Details", variant="secondary")
196
+ submit_btn = gr.Button("Submit Request", variant="primary", interactive=False)
197
+
198
+ confirmation_output = gr.Textbox(label="Confirmation Message")
199
+ confirmation_audio = gr.Audio(label="Confirmation Audio", type="filepath")
200
+
201
+ result_text = gr.Textbox(label="Submission Result")
202
+
203
+ with gr.Tab("Existing Requests"):
204
+ requests_table = gr.DataFrame(
205
+ headers=["Timestamp", "Project Number", "Project Name", "Amount", "Reason"],
206
+ label="Existing Requests"
207
+ )
208
+ refresh_btn = gr.Button("Refresh")
209
+
210
+ # Event handlers
211
+ text_process_btn.click(
212
+ process_text_input,
213
+ inputs=[text_input, language],
214
+ outputs=[process_output, project_number, project_name, amount, reason]
215
+ )
216
+
217
+ voice_process_btn.click(
218
+ process_voice_input,
219
+ inputs=[audio_input, language],
220
+ outputs=[process_output, project_number, project_name, amount, reason]
221
+ )
222
+
223
+ # Confirm button handler with proper submit button and form field state management
224
+ confirm_btn.click(
225
+ confirm_submission,
226
+ inputs=[project_number, project_name, amount, reason],
227
+ outputs=[
228
+ confirmation_output,
229
+ confirmation_audio,
230
+ submit_btn,
231
+ confirm_btn,
232
+ project_number,
233
+ project_name,
234
+ amount,
235
+ reason
236
+ ]
237
+ )
238
+
239
+ # Submit button handler with form reset
240
+ submit_btn.click(
241
+ submit_request,
242
+ inputs=[project_number, project_name, amount, reason],
243
+ outputs=[result_text, requests_table]
244
+ ).then(
245
+ reset_form,
246
+ outputs=[
247
+ project_number,
248
+ project_name,
249
+ amount,
250
+ reason,
251
+ confirmation_output,
252
+ confirmation_audio,
253
+ submit_btn,
254
+ confirm_btn,
255
+ project_number,
256
+ project_name,
257
+ amount,
258
+ reason,
259
+ text_input,
260
+ audio_input,
261
+ process_output
262
+ ]
263
+ )
264
+
265
+ refresh_btn.click(
266
+ lambda: get_requests_df(),
267
+ outputs=[requests_table]
268
+ )
269
+
270
+ # Initialize requests table
271
+ requests_table.value = get_requests_df()
272
+
273
+ return app
voice.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import speech_recognition as sr
2
+ import os
3
+ from pydub import AudioSegment
4
+ import tempfile
5
+
6
+ class VoiceHandler:
7
+ def __init__(self):
8
+ self.recognizer = sr.Recognizer()
9
+ self.recognizer.energy_threshold = 20000
10
+ self.recognizer.dynamic_energy_threshold = False
11
+ self.recognizer.pause_threshold = 0.8
12
+
13
+ def process_audio_file(self, audio_path: str, language: str) -> str:
14
+ try:
15
+ if not audio_path.endswith('.wav'):
16
+ audio = AudioSegment.from_file(audio_path)
17
+ temp_wav = tempfile.NamedTemporaryFile(suffix='.wav', delete=False)
18
+ audio.export(temp_wav.name, format='wav')
19
+ audio_path = temp_wav.name
20
+
21
+ with sr.AudioFile(audio_path) as source:
22
+ audio = self.recognizer.record(source)
23
+
24
+ if language == "Arabic":
25
+ return self.recognizer.recognize_google(audio, language="ar-SA")
26
+ elif language == "Mixed (Arabic/English)":
27
+ try:
28
+ return self.recognizer.recognize_google(audio, language="ar-SA")
29
+ except sr.UnknownValueError:
30
+ return self.recognizer.recognize_google(audio, language="en-US")
31
+ else: # English
32
+ return self.recognizer.recognize_google(audio, language="en-US")
33
+
34
+ except sr.RequestError as e:
35
+ return f"Error: Could not request results from speech service: {str(e)}"
36
+ except sr.UnknownValueError:
37
+ return "Error: Could not understand audio. Please speak clearly and try again."
38
+ except Exception as e:
39
+ return f"Error: {str(e)}"
40
+ finally:
41
+ if 'temp_wav' in locals():
42
+ os.unlink(temp_wav.name)
43
+
44
+ def check_microphone_access(self) -> bool:
45
+ try:
46
+ with sr.Microphone() as source:
47
+ self.recognizer.adjust_for_ambient_noise(source, duration=0.1)
48
+ return True
49
+ except (OSError, AttributeError, sr.RequestError):
50
+ return False