rairo commited on
Commit
48417e8
·
verified ·
1 Parent(s): 222c9f0

Update utility.py

Browse files
Files changed (1) hide show
  1. utility.py +71 -38
utility.py CHANGED
@@ -106,38 +106,76 @@ def generateResponse(prompt: str) -> str:
106
  if not model:
107
  return '{"error": "Model not available"}'
108
 
109
- # --- CHANGE 1: Final, aggressive system prompt ---
 
110
  system_prompt = """
111
- Analyze the user's request for business transaction management. Your goal is to extract structured information and output it as a valid JSON list.
112
-
113
- **1. Critical Rule: Is it a Sale or a Service Definition?**
114
- This is the most important rule. You must analyze the user's verb.
115
- - A **SALE** is a **completed action**. If the user says they **did** something (e.g., "styled a client," "wired a house," "completed a project," "sold"), the `transaction_type` **MUST** be `sale`. You **MUST** also include a `quantity`, defaulting to `1` if not specified.
116
- - A **SERVICE OFFERING** is a **statement of capability**. If the user says they **can do** something (e.g., "I offer styling," "add wiring to my services"), the `transaction_type` **MUST** be `service_offering`.
117
-
118
- **2. Key Naming Conventions for `details`:**
119
- - **Sales/Inventory:** Use `"item"`, `"quantity"`, and `"price"`.
120
- - **Expenses:** Use `"amount"`, `"description"`.
121
- - **Assets:** Use `"value"`, `"name"`.
122
- - **Liabilities:** Use `"amount"`, `"creditor"`.
123
-
124
- **3. Examples to Learn From:**
125
-
126
- **Example A: Defining a Service (Inventory Update)**
127
- - **Input:** "I now offer styling for $120"
128
- - **Output:** `[{"intent": "create", "transaction_type": "service_offering", "details": {"item": "styling", "price": 120}}]`
129
-
130
- **Example B: Selling a Service (Sale Event - No Price Mentioned)**
131
- - **Input:** "I just styled a client"
132
- - **Output:** `[{"intent": "create", "transaction_type": "sale", "details": {"item": "styling", "quantity": 1}}]`
133
-
134
- **Example C: Selling a Service (Sale Event - With Price)**
135
- - **Input:** "I wired a house for the new construction project for $2500"
136
- - **Output:** `[{"intent": "create", "transaction_type": "sale", "details": {"item": "wiring", "quantity": 1, "price": 2500}}]`
137
-
138
- **Example D: A Simple Query**
139
- - **Input:** "what were my sales yesterday?"
140
- - **Output:** `[{"intent": "read", "transaction_type": "query", "details": {"query": "what were my sales yesterday?"}}]`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  """
142
  try:
143
  full_prompt = [system_prompt, prompt]
@@ -180,7 +218,6 @@ def _get_canonical_info(user_phone: str, item_name: str) -> Dict[str, Any]:
180
  """
181
  Finds the canonical version of an item using an "exact match first" hybrid approach.
182
  """
183
- # --- CHANGE 2: "Exact Match First" Fuzzy Logic ---
184
  inventory_ref = db.collection("users").document(user_phone).collection("inventory_and_services")
185
  name_lower = item_name.lower().strip()
186
 
@@ -208,7 +245,6 @@ def _get_canonical_info(user_phone: str, item_name: str) -> Dict[str, Any]:
208
  singular = name_lower
209
 
210
  return {'doc': None, 'name': singular}
211
- # --- END OF CHANGE 2 ---
212
 
213
 
214
  def create_or_update_inventory_or_service_offering(user_phone: str, transaction_data: List[Dict]) -> tuple[bool, str]:
@@ -305,12 +341,10 @@ def create_sale(user_phone: str, transaction_data: List[Dict]) -> tuple[bool, st
305
  elif last_selling_price is not None:
306
  selling_price = last_selling_price
307
  else:
308
- # For services sold without a price, we can default to 0 or another value
309
- # For goods, we must require a price.
310
  if item_type == 'good':
311
  return f"Sale failed for new item '{canonical_name}': You must specify a price for the first sale."
312
  else:
313
- selling_price = 0 # Default price for services sold without one
314
 
315
  if not isinstance(selling_price, (int, float)): selling_price = 0
316
 
@@ -555,7 +589,6 @@ def read_datalake(user_phone: str, query: str) -> str:
555
  """
556
  Handles queries with contextual pruning, temporal awareness, and robust error handling.
557
  """
558
- # --- CHANGE 3: Definitive Temporal Fix and Conversational Flag ---
559
  try:
560
  all_dfs_with_names = _fetch_all_collections_as_dfs(user_phone)
561
  if not all_dfs_with_names:
@@ -588,7 +621,7 @@ def read_datalake(user_phone: str, query: str) -> str:
588
  lake = SmartDatalake(datalake_dfs, config={
589
  "llm": llm, "response_parser": FlaskResponse,
590
  "save_charts_path": user_defined_path, "enable_cache": False,
591
- "conversational": True # Added conversational flag
592
  })
593
 
594
  today_str = datetime.now(timezone.utc).strftime('%Y-%m-%d')
 
106
  if not model:
107
  return '{"error": "Model not available"}'
108
 
109
+ # --- CORRECTED SYSTEM PROMPT ---
110
+ # This is the restored, comprehensive prompt that correctly classifies multiple transaction types.
111
  system_prompt = """
112
+ Analyze the user's request for business transaction management. Your goal is to extract structured information about one or more transactions and output it as a valid JSON list.
113
+
114
+ **1. Output Format:**
115
+ You MUST output your response as a valid JSON list `[]` containing one or more transaction objects `{}`.
116
+
117
+ **2. Transaction Object Structure:**
118
+ Each transaction object MUST have the following keys:
119
+ - `"intent"`: The user's goal (e.g., "create", "read", "update", "delete").
120
+ - `"transaction_type"`: The category of the transaction (e.g., "sale", "purchase", "inventory", "expense", "asset", "liability", "query", "service_offering").
121
+ - `"details"`: An object containing key-value pairs extracted from the request.
122
+
123
+ **3. Key Naming Conventions for the `details` Object:**
124
+ - **For Expenses:** Use `"amount"`, `"description"`, and `"category"`.
125
+ - **For Assets:** Use `"value"` for the monetary worth and `"name"` for the item's name.
126
+ - **For Liabilities:** Use `"amount"` and `"creditor"`.
127
+ - **For Sales/Inventory:** Use `"item"`, `"quantity"`, and `"price"`.
128
+ - **For all financial transactions:** If a currency symbol or code is present (e.g., $, £, €, ZAR, R), include a `"currency"` key.
129
+
130
+ **4. Important Rules:**
131
+ - **Rule for Queries:** For "read" intents or general questions, set `transaction_type` to "query" and the `details` object MUST contain a single key `"query"` with the user's full, original question as the value.
132
+ - **Rule for Multiple Items:** If the user's request contains multiple distinct transactions (e.g., recording an expense AND an asset), create a separate JSON object for each one within the main list.
133
+ - **Rule for Expense Normalization:** For "create" intents with `transaction_type` "expense", analyze the `description`. If it contains common keywords, normalize it to a single word. For example, if the description is "paid for fuel for the delivery truck", the normalized `description` in the JSON should be "fuel". If it's "office electricity bill", normalize it to "electricity".
134
+
135
+ **5. Examples:**
136
+
137
+ **Example 1: Simple Query**
138
+ - **Input:** "what are my assets?"
139
+ - **Output:**
140
+ [
141
+ {
142
+ "intent": "read",
143
+ "transaction_type": "query",
144
+ "details": {
145
+ "query": "what are my assets?"
146
+ }
147
+ }
148
+ ]
149
+
150
+ **Example 2: Creating a Normalized Expense**
151
+ - **Input:** "I paid R250 for fuel for work"
152
+ - **Output:**
153
+ [
154
+ {
155
+ "intent": "create",
156
+ "transaction_type": "expense",
157
+ "details": {
158
+ "description": "fuel",
159
+ "amount": 250,
160
+ "currency": "R"
161
+ }
162
+ }
163
+ ]
164
+
165
+ **Example 3: Creating an Asset**
166
+ - **Input:** "just bought a new company laptop for $1500"
167
+ - **Output:**
168
+ [
169
+ {
170
+ "intent": "create",
171
+ "transaction_type": "asset",
172
+ "details": {
173
+ "name": "new company laptop",
174
+ "value": 1500,
175
+ "currency": "$"
176
+ }
177
+ }
178
+ ]
179
  """
180
  try:
181
  full_prompt = [system_prompt, prompt]
 
218
  """
219
  Finds the canonical version of an item using an "exact match first" hybrid approach.
220
  """
 
221
  inventory_ref = db.collection("users").document(user_phone).collection("inventory_and_services")
222
  name_lower = item_name.lower().strip()
223
 
 
245
  singular = name_lower
246
 
247
  return {'doc': None, 'name': singular}
 
248
 
249
 
250
  def create_or_update_inventory_or_service_offering(user_phone: str, transaction_data: List[Dict]) -> tuple[bool, str]:
 
341
  elif last_selling_price is not None:
342
  selling_price = last_selling_price
343
  else:
 
 
344
  if item_type == 'good':
345
  return f"Sale failed for new item '{canonical_name}': You must specify a price for the first sale."
346
  else:
347
+ selling_price = 0
348
 
349
  if not isinstance(selling_price, (int, float)): selling_price = 0
350
 
 
589
  """
590
  Handles queries with contextual pruning, temporal awareness, and robust error handling.
591
  """
 
592
  try:
593
  all_dfs_with_names = _fetch_all_collections_as_dfs(user_phone)
594
  if not all_dfs_with_names:
 
621
  lake = SmartDatalake(datalake_dfs, config={
622
  "llm": llm, "response_parser": FlaskResponse,
623
  "save_charts_path": user_defined_path, "enable_cache": False,
624
+ "conversational": True
625
  })
626
 
627
  today_str = datetime.now(timezone.utc).strftime('%Y-%m-%d')