rairo commited on
Commit
1ed88b4
·
1 Parent(s): 2cd4dfe

Update utility.py

Browse files
Files changed (1) hide show
  1. utility.py +100 -206
utility.py CHANGED
@@ -1,20 +1,16 @@
1
- import re
2
- import os
3
  import json
 
4
  from datetime import datetime
5
- import openai
6
  from google.cloud import firestore
7
- from dotenv import load_dotenv
8
  import pandas as pd
9
- from pandasai.responses.response_parser import ResponseParser
10
  from langchain_google_genai import ChatGoogleGenerativeAI
11
  import google.generativeai as genai
 
 
 
12
 
13
- # Load environment variables and configure Firestore
14
- load_dotenv()
15
  db = firestore.Client.from_service_account_json("firestore-key.json")
16
 
17
- # Initialize the Gemini LLM client
18
  llm = ChatGoogleGenerativeAI(
19
  model="gemini-2.0-flash-thinking-exp",
20
  max_output_tokens=1024,
@@ -23,32 +19,21 @@ llm = ChatGoogleGenerativeAI(
23
  top_p=0.01,
24
  )
25
 
26
- class FlaskResponse(ResponseParser):
27
- def __init__(self, context) -> None:
28
- super().__init__(context)
29
-
30
- def format_dataframe(self, result):
31
- return result['value'].to_html()
32
-
33
- def format_plot(self, result):
34
- try:
35
- img_path = result['value']
36
- except ValueError:
37
- img_path = str(result['value'])
38
- return img_path
39
 
40
- def format_other(self, result):
41
- return str(result['value'])
 
 
 
 
42
 
43
- def generateResponse(prompt, model='gemini-2.0-flash-thinking-exp'):
44
- relevant_info_template = """
45
- Intent: The CRUD operation (create, read, update, delete)
46
- Transaction Type: e.g. Purchase, Sale, Inventory
47
- Details: A list of key detail fields extracted.
48
- """
49
  genai.configure(api_key=os.environ.get("GOOGLE_API_KEY"))
50
- model_instance = genai.GenerativeModel(
51
- model_name=model,
52
  generation_config={
53
  "temperature": 0.1,
54
  "top_p": 0.01,
@@ -57,75 +42,53 @@ Details: A list of key detail fields extracted.
57
  }
58
  )
59
 
60
- system_prompt = (
61
- f"You are a transaction classification assistant. Format your output with: "
62
- f"{relevant_info_template}"
63
- )
64
-
65
  try:
66
- response = model_instance.generate_content([
67
- {"role": "user", "parts": [system_prompt, prompt]}
68
- ])
69
  return response.text
70
  except Exception as e:
71
- logger.error(f'Error generating response: {str(e)}')
72
  return None
73
 
74
  def parse_multiple_transactions(response_text):
 
 
 
75
  try:
 
76
  parsed = json.loads(response_text)
77
  if isinstance(parsed, dict):
78
- parsed["created_at"] = datetime.now().isoformat()
79
- return [parsed]
80
- elif isinstance(parsed, list):
81
- for item in parsed:
82
- item["created_at"] = datetime.now().isoformat()
83
- return parsed
84
- except Exception:
85
- data = {}
86
- lines = response_text.splitlines()
87
- for line in lines:
88
- if "*Intent*:" in line:
89
- data["intent"] = line.split(":", 1)[1].strip()
90
- elif "*Transaction Type*:" in line:
91
- data["transaction_type"] = line.split(":", 1)[1].strip()
92
- elif "*Details*:" in line:
93
- details_line = line.split(":", 1)[1].strip()
94
- details = {}
95
- parts = details_line.split(",-")
96
- for part in parts:
97
- part = part.strip(" -")
98
- if ":" in part:
99
- key, value = part.split(":", 1)
100
- details[key.strip().lower()] = value.strip().strip(",")
101
- data["details"] = details
102
- if data:
103
- data["created_at"] = datetime.now().isoformat()
104
- return [data]
105
- return []
106
 
107
- def read_datalake(user_phone, user_question):
108
- inventory_ref = db.collection("users").document(user_phone).collection("inventory")
109
- sales_ref = db.collection("users").document(user_phone).collection("sales")
110
- inventory_list = [doc.to_dict() for doc in inventory_ref.stream()]
111
- sales_list = [doc.to_dict() for doc in sales_ref.stream()]
112
- inventory_df = pd.DataFrame(inventory_list)
113
- sales_df = pd.DataFrame(sales_list)
114
- from pandasai import SmartDatalake
115
- lake = SmartDatalake(
116
- [inventory_df, sales_df],
117
- config={
118
- "llm": llm,
119
- "custom_whitelisted_dependencies": ["ast"],
120
- "response_parser": FlaskResponse,
121
- "enable_cache": False,
122
- "save_logs": False
123
- }
124
- )
125
- response = lake.chat(user_question)
126
- if isinstance(response, dict):
127
- return json.dumps(response)
128
- return str(response)
 
 
 
 
 
129
 
130
  def create_inventory(user_phone, transaction_data):
131
  for transaction in transaction_data:
@@ -167,132 +130,63 @@ def create_sale(user_phone, transaction_data):
167
  sale_ref.set(transaction)
168
  return True
169
 
170
- def update_transaction(user_phone, transaction_id, update_data, transaction_type="inventory"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  try:
172
- doc_ref = db.collection("users").document(user_phone).collection(transaction_type).document(transaction_id)
173
- if not doc_ref.get().exists:
174
- return False
175
-
176
- updates = {}
177
- for key, value in update_data.items():
178
- updates[f'details.{key}'] = value
179
-
180
- doc_ref.update(updates)
181
- return True
182
  except Exception as e:
183
- logger.error(f"Update failed: {str(e)}")
184
- return False
185
-
186
- def fetch_transaction(user_phone, transaction_id=None, transaction_type="inventory"):
187
- if transaction_id:
188
- doc_ref = db.collection("users").document(user_phone).collection(transaction_type).document(transaction_id)
189
- transaction = doc_ref.get()
190
- if transaction.exists:
191
- return transaction.to_dict()
192
- return None
193
- else:
194
- collection_ref = db.collection("users").document(user_phone).collection(transaction_type)
195
- return [doc.to_dict() for doc in collection_ref.stream()]
196
-
197
- def delete_transaction(user_phone, transaction_data):
198
- transaction_id = transaction_data[0]['details'].get('item')
199
- transaction_type = transaction_data[0].get('transaction_type', 'inventory').lower()
200
- doc_ref = db.collection("users").document(user_phone).collection(transaction_type).document(transaction_id)
201
- if doc_ref.get().exists:
202
- doc_ref.delete()
203
- return True
204
- return False
205
 
206
  def persist_temporary_transaction(transactions, mobile):
207
- temp_ref = db.collection("users").document(mobile).collection("temp_transactions").document('pending-user-action')
208
- data = {
209
- "transactions": transactions,
210
- "status": "pending",
211
- "created_at": datetime.now().isoformat()
212
- }
213
- temp_ref.set(data)
214
- return True
 
215
 
216
  def format_transaction_response(transactions):
217
  if not transactions:
218
- return "No transaction data found."
219
 
220
  if isinstance(transactions, dict):
221
  transactions = [transactions]
222
- elif not isinstance(transactions, list):
223
- return str(transactions)
224
 
225
- result = []
226
- for idx, trans in enumerate(transactions, 1):
227
- if not isinstance(trans, dict):
228
- continue
229
-
230
- intent = trans.get('intent', 'Unknown')
231
- trans_type = trans.get('transaction_type', 'Unknown')
232
- details = trans.get('details', {})
233
-
234
- if len(transactions) > 1:
235
- result.append(f"Transaction {idx}:")
236
-
237
- result.append(f"Intent: {intent}")
238
- result.append(f"Type: {trans_type}")
239
-
240
- if details:
241
- result.append("Details:")
242
- for key, value in details.items():
243
- if key == 'currency':
244
- continue
245
- if 'price' in key or 'cost' in key or 'amount' in key:
246
- currency = details.get('currency', '$')
247
- result.append(f" - {key.replace('_', ' ').title()}: {currency}{value}")
248
- else:
249
- result.append(f" - {key.replace('_', ' ').title()}: {value}")
250
-
251
- result.append("")
252
 
253
- return "\n".join(result)
254
 
255
- def process_intent(parsed_trans_data, mobile):
256
- intent = parsed_trans_data[0]['intent'].lower()
257
- trans_type = parsed_trans_data[0].get('transaction_type', '').lower()
258
- transaction_summary = format_transaction_response(parsed_trans_data)
259
-
260
- if intent == 'create':
261
- if trans_type in ('purchase', 'inventory'):
262
- if create_inventory(mobile, parsed_trans_data):
263
- return f"Transaction recorded successfully!\n\n{transaction_summary}"
264
- return f"Failed to record transaction!\n\n{transaction_summary}"
265
- elif trans_type == 'sale':
266
- if create_sale(mobile, parsed_trans_data):
267
- return f"Sale recorded successfully!\n\n{transaction_summary}"
268
- return f"Insufficient stock or invalid item!\n\n{transaction_summary}"
269
- return f"Unsupported transaction type: {trans_type}\n\n{transaction_summary}"
270
-
271
- elif intent == 'update':
272
- item = parsed_trans_data[0]['details'].get('item')
273
- update_details = {k:v for k,v in parsed_trans_data[0]['details'].items() if k != 'item'}
274
-
275
- if update_transaction(mobile, item, update_details, trans_type):
276
- return f"Updated {item} successfully!\n\n{transaction_summary}"
277
- return f"Failed to update {item}!\n\n{transaction_summary}"
278
-
279
- elif intent == 'delete':
280
- item = parsed_trans_data[0]['details'].get('item', 'item')
281
- if delete_transaction(mobile, parsed_trans_data):
282
- return f"Deleted {item} successfully!\n\n{transaction_summary}"
283
- return f"Failed to delete {item}!\n\n{transaction_summary}"
284
-
285
- elif intent == 'read':
286
- if 'item' in parsed_trans_data[0]['details']:
287
- item = parsed_trans_data[0]['details']['item']
288
- transaction = fetch_transaction(mobile, item)
289
- if transaction:
290
- return f"Details for {item}:\n\n{format_transaction_response(transaction)}"
291
- return f"Item {item} not found!"
292
- else:
293
- transactions = fetch_transaction(mobile, transaction_type=trans_type if trans_type else "inventory")
294
- if transactions:
295
- return f"All {trans_type if trans_type else 'inventory'} items:\n\n{format_transaction_response(transactions)}"
296
- return "No items found!"
297
-
298
- return f"Unsupported intent: {intent}\n\n{transaction_summary}"
 
 
 
1
  import json
2
+ import os
3
  from datetime import datetime
 
4
  from google.cloud import firestore
 
5
  import pandas as pd
 
6
  from langchain_google_genai import ChatGoogleGenerativeAI
7
  import google.generativeai as genai
8
+ import logging
9
+
10
+ logger = logging.getLogger(__name__)
11
 
 
 
12
  db = firestore.Client.from_service_account_json("firestore-key.json")
13
 
 
14
  llm = ChatGoogleGenerativeAI(
15
  model="gemini-2.0-flash-thinking-exp",
16
  max_output_tokens=1024,
 
19
  top_p=0.01,
20
  )
21
 
22
+ def generateResponse(prompt):
23
+ system_prompt = """You MUST format responses EXACTLY like this:
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ *Intent*: [create/read/update/delete]
26
+ *Transaction Type*: [purchase/sale/inventory/etc]
27
+ *Details*:
28
+ - Field1: Value1
29
+ - Field2: Value2
30
+ - Field3: Value3
31
 
32
+ For multiple transactions repeat this pattern exactly."""
33
+
 
 
 
 
34
  genai.configure(api_key=os.environ.get("GOOGLE_API_KEY"))
35
+ model = genai.GenerativeModel(
36
+ 'gemini-2.0-flash-thinking-exp',
37
  generation_config={
38
  "temperature": 0.1,
39
  "top_p": 0.01,
 
42
  }
43
  )
44
 
 
 
 
 
 
45
  try:
46
+ response = model.generate_content([system_prompt, prompt])
 
 
47
  return response.text
48
  except Exception as e:
49
+ logger.error(f"Generation error: {str(e)}")
50
  return None
51
 
52
  def parse_multiple_transactions(response_text):
53
+ transactions = []
54
+ current_trans = {}
55
+
56
  try:
57
+ # First try JSON parsing
58
  parsed = json.loads(response_text)
59
  if isinstance(parsed, dict):
60
+ return [add_timestamp(parsed)]
61
+ return [add_timestamp(t) for t in parsed]
62
+ except json.JSONDecodeError:
63
+ pass # Proceed to text parsing
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
+ lines = [line.strip() for line in response_text.split('\n') if line.strip()]
66
+
67
+ for line in lines:
68
+ if line.startswith('*Intent*:'):
69
+ if current_trans:
70
+ transactions.append(add_timestamp(current_trans))
71
+ current_trans = {}
72
+ current_trans['intent'] = line.split(':', 1)[1].strip().lower()
73
+ elif line.startswith('*Transaction Type*:'):
74
+ current_trans['transaction_type'] = line.split(':', 1)[1].strip().lower()
75
+ elif line.startswith('*Details*:'):
76
+ current_trans['details'] = {}
77
+ elif line.startswith('-') and 'details' in current_trans:
78
+ key_value = line[1:].strip().split(':', 1)
79
+ if len(key_value) == 2:
80
+ key = key_value[0].strip().lower()
81
+ value = key_value[1].strip()
82
+ current_trans['details'][key] = value
83
+
84
+ if current_trans:
85
+ transactions.append(add_timestamp(current_trans))
86
+
87
+ return transactions if transactions else []
88
+
89
+ def add_timestamp(trans):
90
+ trans['created_at'] = datetime.now().isoformat()
91
+ return trans
92
 
93
  def create_inventory(user_phone, transaction_data):
94
  for transaction in transaction_data:
 
130
  sale_ref.set(transaction)
131
  return True
132
 
133
+ def read_datalake(user_phone, user_question):
134
+ inventory_ref = db.collection("users").document(user_phone).collection("inventory")
135
+ sales_ref = db.collection("users").document(user_phone).collection("sales")
136
+ inventory_list = [doc.to_dict() for doc in inventory_ref.stream()]
137
+ sales_list = [doc.to_dict() for doc in sales_ref.stream()]
138
+
139
+ if not inventory_list and not sales_list:
140
+ return "No data found"
141
+
142
+ inventory_df = pd.DataFrame(inventory_list)
143
+ sales_df = pd.DataFrame(sales_list)
144
+
145
+ from pandasai import SmartDatalake
146
+ lake = SmartDatalake(
147
+ [inventory_df, sales_df],
148
+ config={"llm": llm}
149
+ )
150
+
151
  try:
152
+ return str(lake.chat(user_question))
 
 
 
 
 
 
 
 
 
153
  except Exception as e:
154
+ logger.error(f"Datalake error: {str(e)}")
155
+ return "Error generating response"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  def persist_temporary_transaction(transactions, mobile):
158
+ try:
159
+ db.collection("users").document(mobile).collection("temp_transactions").document("pending").set({
160
+ "transactions": transactions,
161
+ "timestamp": datetime.now().isoformat()
162
+ })
163
+ return True
164
+ except Exception as e:
165
+ logger.error(f"Temp save error: {str(e)}")
166
+ return False
167
 
168
  def format_transaction_response(transactions):
169
  if not transactions:
170
+ return "No transaction data"
171
 
172
  if isinstance(transactions, dict):
173
  transactions = [transactions]
 
 
174
 
175
+ output = []
176
+ for trans in transactions:
177
+ output.append(f"Intent: {trans.get('intent', 'unknown')}")
178
+ output.append(f"Type: {trans.get('transaction_type', 'unknown')}")
179
+ if 'details' in trans:
180
+ output.append("Details:")
181
+ for k, v in trans['details'].items():
182
+ output.append(f"- {k}: {v}")
183
+ output.append("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
+ return "\n".join(output)
186
 
187
+ def fetch_transaction(user_phone, item_name=None):
188
+ if item_name:
189
+ doc = db.collection("users").document(user_phone).collection("inventory").document(item_name).get()
190
+ return doc.to_dict() if doc.exists else None
191
+ else:
192
+ return [doc.to_dict() for doc in db.collection("users").document(user_phone).collection("inventory").stream()]