shamimjony1000 commited on
Commit
df9db76
·
verified ·
1 Parent(s): a8ee38f

Upload 8 files

Browse files
Files changed (8) hide show
  1. .env +1 -0
  2. app.py +214 -0
  3. database.py +103 -0
  4. gemini_processor.py +86 -0
  5. memory_handler.py +15 -0
  6. requests.db +0 -0
  7. requirements.txt +6 -0
  8. voice_handler.py +50 -0
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ GOOGLE_API_KEY=AIzaSyCLyDgZNcE_v4wLMFF8SoimKga9bbLSun0
app.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ from database import Database
4
+ from voice_handler import VoiceHandler
5
+ from gemini_processor import GeminiProcessor
6
+ from memory_handler import MemoryHandler
7
+ import os
8
+ from dotenv import load_dotenv
9
+
10
+ load_dotenv()
11
+
12
+ # Initialize components
13
+ try:
14
+ db = Database()
15
+ voice_handler = VoiceHandler()
16
+ gemini_processor = GeminiProcessor()
17
+ if 'memory_handler' not in st.session_state:
18
+ st.session_state.memory_handler = MemoryHandler()
19
+ except Exception as e:
20
+ st.error(f"Error initializing components: {str(e)}")
21
+ st.stop()
22
+
23
+ def validate_request(project_number, project_name, amount, reason):
24
+ if not project_number or not project_name or not amount or not reason:
25
+ missing_fields = []
26
+ if not project_number: missing_fields.append("project number")
27
+ if not project_name: missing_fields.append("project name")
28
+ if not amount: missing_fields.append("amount")
29
+ if not reason: missing_fields.append("reason")
30
+ return False, f"Please provide the following missing information: {', '.join(missing_fields)}"
31
+ return True, ""
32
+
33
+ st.title("AI Agent Money Request System")
34
+ st.markdown("---")
35
+
36
+ # Language Selection
37
+ if 'language' not in st.session_state:
38
+ st.session_state.language = "en-US" # Default to English
39
+
40
+ st.sidebar.title("Settings")
41
+ language_option = st.sidebar.selectbox(
42
+ "Select Input Language",
43
+ options=["English", "Arabic", "Mixed (Arabic/English)"],
44
+ index=0,
45
+ key="language_select"
46
+ )
47
+
48
+ # Map selection to language codes
49
+ language_mapping = {
50
+ "English": "en-US",
51
+ "Arabic": "ar-SA",
52
+ "Mixed (Arabic/English)": "mixed"
53
+ }
54
+
55
+ # Input Method Tabs
56
+ tab1, tab2 = st.tabs(["Voice Input", "Text Input"])
57
+
58
+ # Voice Input Tab
59
+ with tab1:
60
+ col1, col2, col3 = st.columns([3, 1, 1])
61
+ with col1:
62
+ if language_option == "Arabic":
63
+ st.markdown("""
64
+ جرب أن تقول شيئاً مثل:
65
+ > "أحتاج إلى طلب 500 ريال للمشروع 223 المسمى جامعة أبها لشراء بعض الأدوات"
66
+ """)
67
+ else:
68
+ st.markdown("""
69
+ Try saying something like:
70
+ > "I need to request 500 riyals for project 223 named Abha University to buy some tools"
71
+ """)
72
+ with col2:
73
+ if st.button("🎤 Start Voice Input"):
74
+ with st.spinner("Listening... Please speak clearly"):
75
+ voice_text = voice_handler.listen_for_voice(language_mapping[language_option])
76
+ if not voice_text.startswith("Error") and not voice_text.startswith("Could not"):
77
+ st.success("Voice captured!")
78
+ st.write("You said:", voice_text)
79
+
80
+ # Add to memory
81
+ st.session_state.memory_handler.add_interaction(voice_text)
82
+
83
+ with st.spinner("Processing voice input..."):
84
+ context = st.session_state.memory_handler.get_context()
85
+ details = gemini_processor.extract_request_details(voice_text, context)
86
+ if details:
87
+ st.session_state['voice_details'] = details
88
+ if 'translated_text' in details:
89
+ st.info(f"Translated text: {details['translated_text']}")
90
+ if details.get('missing_fields'):
91
+ missing = ", ".join(details['missing_fields'])
92
+ st.warning(f"Please provide the following missing information: {missing}")
93
+ else:
94
+ st.success("Voice input processed! Please verify the details below.")
95
+ else:
96
+ st.error("Could not extract request details. Please try again or use manual input.")
97
+ else:
98
+ st.error(voice_text)
99
+ with col3:
100
+ if st.button("🗑️ Clear Memory"):
101
+ st.session_state.memory_handler.clear_memory()
102
+ if 'voice_details' in st.session_state:
103
+ del st.session_state['voice_details']
104
+ st.success("Memory cleared!")
105
+
106
+ # Text Input Tab
107
+ with tab2:
108
+ if language_option == "Arabic":
109
+ st.markdown("""
110
+ أدخل طلبك في شكل نص. مثال:
111
+ > "أحتاج إلى طلب 500 ريال للمشروع 223 المسمى جامعة أبها لشراء بعض الأدوات"
112
+ """)
113
+ else:
114
+ st.markdown("""
115
+ Enter your request in text format. Example:
116
+ > "I need to request 500 riyals for project 223 named Abha University to buy some tools"
117
+ """)
118
+
119
+ text_input = st.text_area("Enter your request:", height=100)
120
+ col1, col2 = st.columns([1, 4])
121
+
122
+ with col1:
123
+ if st.button("Process Text"):
124
+ if text_input:
125
+ with st.spinner("Processing text input..."):
126
+ context = st.session_state.memory_handler.get_context()
127
+ details = gemini_processor.extract_request_details(text_input, context)
128
+ if details:
129
+ st.session_state['voice_details'] = details
130
+ if 'translated_text' in details:
131
+ st.info(f"Translated text: {details['translated_text']}")
132
+ if details.get('missing_fields'):
133
+ missing = ", ".join(details['missing_fields'])
134
+ st.warning(f"Please provide the following missing information: {missing}")
135
+ else:
136
+ st.success("Text processed! Please verify the details below.")
137
+ else:
138
+ st.error("Could not extract request details. Please try again.")
139
+ else:
140
+ st.warning("Please enter some text first.")
141
+
142
+ # Show conversation history
143
+ if st.session_state.memory_handler.conversation_history:
144
+ with st.expander("Previous Inputs"):
145
+ for i, text in enumerate(st.session_state.memory_handler.conversation_history, 1):
146
+ st.text(f"{i}. {text}")
147
+
148
+ # Input form
149
+ st.markdown("---")
150
+ st.subheader("Submit Money Request")
151
+ with st.form("request_form"):
152
+ voice_details = st.session_state.get('voice_details', {})
153
+
154
+ project_number = st.text_input("Project Number", value=voice_details.get('project_number', ''))
155
+ project_name = st.text_input("Project Name", value=voice_details.get('project_name', ''))
156
+ amount = st.number_input("Amount (in riyals)",
157
+ value=float(voice_details.get('amount', 0)),
158
+ min_value=0.0,
159
+ step=100.0)
160
+ reason = st.text_input("Reason for Request", value=voice_details.get('reason', ''))
161
+
162
+ col1, col2 = st.columns(2)
163
+ with col1:
164
+ submitted = st.form_submit_button("Submit Request")
165
+ with col2:
166
+ confirmed = st.form_submit_button("Confirm & Save")
167
+
168
+ if submitted:
169
+ is_valid, message = validate_request(project_number, project_name, amount, reason)
170
+ if is_valid:
171
+ st.session_state.show_confirmation = True
172
+ confirmation_text = f"""
173
+ Please review the following request and click 'Confirm & Save' to proceed:
174
+
175
+ - Project Number: {project_number}
176
+ - Project Name: {project_name}
177
+ - Amount: {amount} riyals
178
+ - Reason: {reason}
179
+ """
180
+ st.info(confirmation_text)
181
+ else:
182
+ st.error(message)
183
+
184
+ if confirmed:
185
+ is_valid, message = validate_request(project_number, project_name, amount, reason)
186
+ if is_valid:
187
+ try:
188
+ original_text = voice_details.get('original_text', '') if 'voice_details' in st.session_state else ''
189
+ db.add_request(project_number, project_name, amount, reason, original_text)
190
+ st.success("Request successfully added to the database!")
191
+ if 'voice_details' in st.session_state:
192
+ del st.session_state['voice_details']
193
+ st.session_state.show_confirmation = False
194
+ st.session_state.memory_handler.clear_memory()
195
+ except Exception as e:
196
+ st.error(f"Error saving request: {str(e)}")
197
+ else:
198
+ st.error(message)
199
+
200
+ # Display existing requests
201
+ st.markdown("---")
202
+ st.subheader("Existing Requests")
203
+ try:
204
+ requests = db.get_all_requests()
205
+ if requests:
206
+ df = pd.DataFrame(requests)
207
+ # Reorder columns to show original text
208
+ columns = ['timestamp', 'project_number', 'project_name', 'amount', 'reason', 'original_text']
209
+ df = df[columns]
210
+ st.dataframe(df, use_container_width=True)
211
+ else:
212
+ st.info("No requests submitted yet.")
213
+ except Exception as e:
214
+ st.error(f"Error loading requests: {str(e)}")
database.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sqlite3
2
+ from datetime import datetime
3
+ import os
4
+ import time
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
+ def initialize_database(self):
14
+ for attempt in range(self.max_retries):
15
+ try:
16
+
17
+ self.conn = sqlite3.connect(self.db_name)
18
+
19
+ self.conn.execute('PRAGMA encoding="UTF-8"')
20
+
21
+
22
+ cursor = self.conn.cursor()
23
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='requests'")
24
+ if not cursor.fetchone():
25
+ self.create_table()
26
+ else:
27
+ # Verify columns
28
+ cursor.execute('PRAGMA table_info(requests)')
29
+ columns = [col[1] for col in cursor.fetchall()]
30
+ required_columns = ['id', 'timestamp', 'project_number', 'project_name', 'amount', 'reason', 'original_text']
31
+
32
+ if not all(col in columns for col in required_columns):
33
+ # Backup existing data
34
+ cursor.execute('ALTER TABLE requests RENAME TO requests_old')
35
+ self.create_table()
36
+ # Copy data from old table
37
+ cursor.execute('''
38
+ INSERT INTO requests (timestamp, project_number, project_name, amount, reason)
39
+ SELECT timestamp, project_number, project_name, amount, reason
40
+ FROM requests_old
41
+ ''')
42
+ cursor.execute('DROP TABLE requests_old')
43
+ self.conn.commit()
44
+
45
+ return # Success
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
+ except Exception as e:
52
+ raise Exception(f"Database initialization error: {str(e)}")
53
+
54
+ def create_table(self):
55
+ cursor = self.conn.cursor()
56
+ cursor.execute('''
57
+ CREATE TABLE IF NOT EXISTS requests (
58
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
59
+ timestamp DATETIME,
60
+ project_number TEXT,
61
+ project_name TEXT,
62
+ amount REAL,
63
+ reason TEXT,
64
+ original_text TEXT
65
+ )
66
+ ''')
67
+ self.conn.commit()
68
+
69
+ def add_request(self, project_number, project_name, amount, reason, original_text=""):
70
+ for attempt in range(self.max_retries):
71
+ try:
72
+ cursor = self.conn.cursor()
73
+ cursor.execute('''
74
+ INSERT INTO requests (timestamp, project_number, project_name, amount, reason, original_text)
75
+ VALUES (?, ?, ?, ?, ?, ?)
76
+ ''', (datetime.now(), project_number, project_name, amount, reason, original_text))
77
+ self.conn.commit()
78
+ return # Success
79
+ except sqlite3.OperationalError as e:
80
+ if attempt < self.max_retries - 1:
81
+ time.sleep(self.retry_delay)
82
+ continue
83
+ raise Exception(f"Could not add request after {self.max_retries} attempts: {str(e)}")
84
+
85
+ def get_all_requests(self):
86
+ for attempt in range(self.max_retries):
87
+ try:
88
+ cursor = self.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)}")
98
+
99
+ def __del__(self):
100
+ try:
101
+ self.conn.close()
102
+ except:
103
+ pass
gemini_processor.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ import os
3
+ from dotenv import load_dotenv
4
+ import json
5
+ import re
6
+
7
+ load_dotenv()
8
+
9
+ class GeminiProcessor:
10
+ def __init__(self):
11
+ api_key = os.getenv('GOOGLE_API_KEY')
12
+ if not api_key:
13
+ raise ValueError("GOOGLE_API_KEY not found in environment variables")
14
+ genai.configure(api_key=api_key)
15
+ self.model = genai.GenerativeModel('gemini-pro')
16
+
17
+ def is_arabic(self, text):
18
+ arabic_pattern = re.compile('[\u0600-\u06FF]')
19
+ return bool(arabic_pattern.search(text))
20
+
21
+ def translate_arabic_to_english(self, text):
22
+ prompt = f"""
23
+ Translate the following Arabic text to English. If the text is mixed (Arabic and English),
24
+ translate only the Arabic parts and keep the English parts as is.
25
+ Keep numbers in their original format.
26
+
27
+ Text to translate: {text}
28
+ """
29
+ try:
30
+ response = self.model.generate_content(prompt)
31
+ return response.text.strip()
32
+ except Exception as e:
33
+ print(f"Translation error: {e}")
34
+ return text
35
+
36
+ def extract_request_details(self, text, context=""):
37
+ full_text = f"{context} {text}".strip()
38
+ is_arabic_input = self.is_arabic(full_text)
39
+
40
+ # Translate if Arabic text is detected
41
+ if is_arabic_input:
42
+ translated_text = self.translate_arabic_to_english(full_text)
43
+ processing_text = translated_text
44
+ else:
45
+ processing_text = full_text
46
+
47
+ prompt = f"""
48
+ Extract the following information from this text and previous context.
49
+ The input has been translated from Arabic if it contained Arabic text.
50
+
51
+ If any information is missing, leave it empty.
52
+ Format the response exactly as a JSON object with these keys:
53
+ {{
54
+ "project_number": "extracted number or empty string",
55
+ "project_name": "extracted name or empty string",
56
+ "amount": extracted number or 0,
57
+ "reason": "extracted reason or empty string",
58
+ "missing_fields": ["list of missing required fields"],
59
+ "original_text": "the original input text"
60
+ }}
61
+
62
+ Text to analyze: {processing_text}
63
+ """
64
+
65
+ try:
66
+ response = self.model.generate_content(prompt)
67
+ result = json.loads(response.text)
68
+
69
+ required_keys = ['project_number', 'project_name', 'amount', 'reason', 'missing_fields']
70
+ if not all(key in result for key in required_keys):
71
+ raise ValueError("Missing required keys in response")
72
+
73
+ result['amount'] = float(result.get('amount', 0))
74
+ result['original_text'] = full_text # Keep the original Arabic text
75
+
76
+ # Add translation if it was performed
77
+ if is_arabic_input:
78
+ result['translated_text'] = processing_text
79
+
80
+ return result
81
+ except json.JSONDecodeError as e:
82
+ print(f"JSON parsing error: {e}")
83
+ return None
84
+ except Exception as e:
85
+ print(f"Error processing request: {e}")
86
+ return None
memory_handler.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class MemoryHandler:
2
+ def __init__(self):
3
+ self.conversation_history = []
4
+ self.max_history = 5 # Keep last 5 interactions
5
+
6
+ def add_interaction(self, text):
7
+ self.conversation_history.append(text)
8
+ if len(self.conversation_history) > self.max_history:
9
+ self.conversation_history.pop(0)
10
+
11
+ def get_context(self):
12
+ return " ".join(self.conversation_history)
13
+
14
+ def clear_memory(self):
15
+ self.conversation_history = []
requests.db ADDED
Binary file (12.3 kB). View file
 
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ streamlit
2
+ pandas
3
+ SpeechRecognition
4
+ google-generativeai
5
+ pyaudio
6
+ python-dotenv
voice_handler.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import speech_recognition as sr
2
+
3
+ class VoiceHandler:
4
+ def __init__(self):
5
+ self.recognizer = sr.Recognizer()
6
+ # Adjust recognition settings for better accuracy
7
+ self.recognizer.energy_threshold = 4000
8
+ self.recognizer.dynamic_energy_threshold = True
9
+ self.recognizer.pause_threshold = 0.8
10
+
11
+ def listen_for_voice(self, language="mixed"):
12
+ """
13
+ Listen for voice input in specified language.
14
+ language can be:
15
+ - "ar-SA" for Arabic
16
+ - "en-US" for English
17
+ - "mixed" for both Arabic and English
18
+ """
19
+ try:
20
+ with sr.Microphone() as source:
21
+ print("Adjusting for ambient noise...")
22
+ self.recognizer.adjust_for_ambient_noise(source, duration=1)
23
+ print("Listening...")
24
+ audio = self.recognizer.listen(source, timeout=5, phrase_time_limit=10)
25
+ print("Processing speech...")
26
+
27
+ # Try Arabic first if mixed or Arabic is specified
28
+ if language in ["ar-SA", "mixed"]:
29
+ try:
30
+ text = self.recognizer.recognize_google(audio, language="ar-SA")
31
+ return text
32
+ except sr.UnknownValueError:
33
+ if language == "mixed":
34
+ # If Arabic fails and mixed is specified, try English
35
+ text = self.recognizer.recognize_google(audio, language="en-US")
36
+ return text
37
+ raise
38
+ else:
39
+ # English only
40
+ text = self.recognizer.recognize_google(audio, language="en-US")
41
+ return text
42
+
43
+ except sr.RequestError as e:
44
+ return f"Could not request results from speech service: {str(e)}"
45
+ except sr.UnknownValueError:
46
+ return "Could not understand audio. Please speak clearly and try again."
47
+ except sr.WaitTimeoutError:
48
+ return "Listening timed out. Please try again."
49
+ except Exception as e:
50
+ return f"Error: {str(e)}"