Spaces:
Sleeping
Sleeping
File size: 14,201 Bytes
d813e31 630a5df beab094 1519cac 630a5df d813e31 2e932f1 d813e31 53b9009 d2d2e8d 53b9009 4e56772 630a5df d2d2e8d 630a5df d813e31 4e56772 d813e31 4e56772 d813e31 4e56772 5dd7fbf 4e56772 5dd7fbf 4e56772 5dd7fbf 4e56772 d2d2e8d d813e31 5dd7fbf d813e31 5dd7fbf d813e31 5dd7fbf d813e31 4e56772 d813e31 4e56772 d813e31 5dd7fbf d813e31 4e56772 d813e31 4e56772 d813e31 f281e50 d813e31 4e56772 d813e31 4589222 d813e31 4589222 d813e31 4e56772 d813e31 4589222 d813e31 4e56772 d813e31 4589222 d813e31 4e56772 d813e31 4589222 d813e31 4e56772 d813e31 decf374 630a5df decf374 5dd7fbf decf374 630a5df 8f20afd d7a4e43 630a5df decf374 36cedf9 d813e31 4e56772 36cedf9 d813e31 36cedf9 4e56772 36cedf9 e6ff214 36cedf9 4e56772 36cedf9 4e56772 d813e31 4acd89f d813e31 4acd89f d813e31 f281e50 630a5df d813e31 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
import re
import os
from datetime import datetime
import openai
from google.cloud import firestore
from dotenv import load_dotenv
from pandasai import SmartDatalake
from pandasai import Agent
from pandasai.responses.response_parser import ResponseParser
import pandas as pd
from pandasai.llm import OpenAI
#from langchain.llms.sambanova import Sambaverse
#from langchain_community.llms.sambanova import Sambaverse
from langchain_community.chat_models.sambanova import ChatSambaNovaCloud
import ast
import json
# Make API connection
load_dotenv()
# gemini_api_key = os.environ['Gemini']
# o_api_key = os.getenv("openai_api_key")
# openai.api_key = o_api_key
# Authenticate to Firesotre with the JSON account key
db = firestore.Client.from_service_account_json("firestore-key.json")
client = openai.OpenAI(
api_key=os.environ.get("SAMBANOVA_API_KEY"),
base_url="https://api.sambanova.ai/v1",
)
sambaverse_api_key = os.environ.get("SAMBANOVA_API_KEY")
llm = ChatSambaNovaCloud(
model="Meta-Llama-3.1-70B-Instruct",
max_tokens=1024,
temperature=0.7,
top_k=1,
top_p=0.01,
)
print(llm.invoke("Why should I use open source models?"))
class FlaskResponse(ResponseParser):
def __init__(self, context) -> None:
super().__init__(context)
def format_dataframe(self, result):
return result['value'].to_html()
def format_plot(self, result):
# Save the plot using savefig
try:
img_path = result['value']
except ValueError:
img_path = str(result['value'])
print("value error!", img_path)
print("response_class_path:", img_path)
return img_path
def format_other(self, result):
return str(result['value'])
# Generate AI response from user input
def generateResponse(prompt,model='Meta-Llama-3.1-70B-Instruct'):
#----- Call API to classify and extract relevant transaction information
# These templates help provide a unified response format for use as context clues when
# parsing the AI generated response into a structured data format
relevant_info_template = """
Intent: The CRUD operation, one of create, read, update, or delete
Transaction Type: The type of transaction such as purchases, sales
Details: as a sublist of the key details like name of item, quantity, cost price, currency, unit for the quantity, description, among other details you are able to extract.
"""
sample_single_transaction_template = """
The information provided indicates that you want to *create/record* a new transaction.
*Extracted Information*:
*Intent*: Create
*Transaction Type*: Purchase
*Details*:
- Item: Car
- Purpose: Business
- Quantity: 1
- Unit: None
- Cost: 10000
- Tax: 200
- Currency: USD
- Note: A new car for business
"""
sample_multi_transaction_template = """
The information provided indicates that you want to *create/record* a new transaction.
*Extracted Information*:
*Intent*: Create
Transaction 1:
*Transaction Type*: Purchase
*Details*:
- Item: Car
- Purpose: Business
- Quantity: 1
- Unit: None
- Cost: 10000
- Tax: 200
- Currency: USD
- Note: A new car for business
Transaction 2:
*Transaction Type*: Expense
*Details*:
- Item: Office Chair
- Quantity: 2
- Unit: None
- Cost: 300
- Currency: USD
- Category: Furniture
"""
response = client.chat.completions.create(
model = model,
# model="gpt-4o",
messages=[
{"role": "system", "content": f"You are a helpful assistant that classifies transactions written in natural language into CRUD operations (Create, Read, Update, and Delete) and extracts relevant information. For update and delete queries, the transaction type should either be sales or inventory. You should be able to recognize the currency being used and any quantity units into separate fields. Format the relevant information extracted from the transaction text in this format: {relevant_info_template}. A sample response for a single transaction could look like this: {sample_single_transaction_template}, while multiple transactions could look like this: {sample_multi_transaction_template}. There should be only one intent even in the case of multiple transactions."},
{"role": "user", "content": prompt}
]
)
#----- Process response
try:
response = response.choices[0].message.content
except Exception as e:
print(f'An error occurred: {str(e)}')
response = None
return response
def parse_value(value):
"""
Parses a value string into the appropriate data type and detects currency.
Handles various currencies, percentages, numbers, and text.
"""
value = value.strip()
try:
# Match currency codes or symbols dynamically
currency_match = re.search(r"([A-Z]{3}|\$|€|£)", value)
currency = currency_match.group(1) if currency_match else None
# Remove currency symbols or codes for numeric conversion
cleaned_value = re.sub(r"[A-Z]{3}|\$|€|£", "", value).replace(",", "").strip()
# Handle percentages
if "%" in cleaned_value:
return float(cleaned_value.replace("%", "")), currency
# Handle plain numbers (integers or floats)
elif cleaned_value.replace(".", "", 1).isdigit():
return float(cleaned_value) if "." in cleaned_value else int(cleaned_value), currency
# Return as text if no numeric parsing is possible
return value, currency
except ValueError:
# Fallback to original value if parsing fails
return value, None
def extract_transaction_details(text):
"""
Extracts transaction details from a given text input.
Handles both bold (**field**) and non-bold (field) formats.
"""
details = {}
transaction_currency = None # Default currency field
# Regex to match key-value pairs
detail_matches = re.findall(
r"-\s*\*{0,2}([\w\s]+)\*{0,2}:\s*([\w\s,.$%-]+?)(?:\s*[\n]|$)", # Stop matching before newline or end of string
text,
re.DOTALL
)
# print("Detail matches:", detail_matches) # Debugging
for field, value in detail_matches:
# Standardize the field name (convert to snake_case)
field = field.strip().lower().replace(" ", "_")
# Parse the value and dynamically detect currency
parsed_value, detected_currency = parse_value(value)
if detected_currency and not transaction_currency:
transaction_currency = detected_currency # Set the transaction-level currency if not already set
details[field] = parsed_value
# Add currency as a separate field
if transaction_currency:
details["currency"] = transaction_currency
return details
# Parsing single transactions
def parse_ai_response(response_text):
# Initialize the structured data dictionary
data = {
"intent": None,
"transaction_type": None,
"details": {},
"created_at": datetime.now().isoformat() # Add current date and time
}
# Extract the intent
intent_match = re.search(r"\*Intent\*:\s*(\w+)", response_text)
if intent_match:
data["intent"] = intent_match.group(1)
# Extract the transaction type
transaction_type_match = re.search(r"\*Transaction Type\*:\s*(\w+)", response_text)
if transaction_type_match:
data["transaction_type"] = transaction_type_match.group(1)
# Store details in the structured data
data["details"] = extract_transaction_details(response_text)
return data
def parse_multiple_transactions(response_text):
transactions = []
# Split the response into transaction sections based on keyword 'Transaction #'
transaction_sections = re.split(r"Transaction \d+:", response_text, flags=re.IGNORECASE)
# Adjust regex to handle variations in "Transaction X:"
# transaction_sections = re.split(r"(?i)(?<=\n)transaction\s+\d+:", response_text)
transaction_sections = [section.strip() for section in transaction_sections if section.strip()]
# Remove the first section if it's not a valid transaction
if not re.search(r"\*Transaction Type\*", transaction_sections[0], re.IGNORECASE):
transaction_sections.pop(0)
# Extract intent: with support for a single intent per user prompt
intent_match = re.search(r"\*Intent\*:\s*(\w+)", response_text)
if intent_match:
intent = intent_match.group(1)
for section in transaction_sections:
# Initialize transaction data
transaction_data = {
"intent": intent, # global intent
"transaction_type": None,
"details": {},
"created_at": datetime.now().isoformat()
}
# Extract transaction type
transaction_type_match = re.search(r"\*Transaction Type\*:\s*(\w+)", section)
if transaction_type_match:
transaction_data["transaction_type"] = transaction_type_match.group(1)
# Extract details
transaction_data["details"] = extract_transaction_details(section)
transactions.append(transaction_data)
return transactions
def read_datalake(user_phone, user_question):
inventory_ref = db.collection("users").document(user_phone).collection("inventory")
sales_ref = db.collection("users").document(user_phone).collection('sales')
inventory_list = [doc.to_dict() for doc in inventory_ref.stream()]
sales_list = [doc.to_dict() for doc in sales_ref.stream()]
inventory_df = pd.DataFrame(inventory_list)
sales_df = pd.DataFrame(sales_list)
lake = SmartDatalake([inventory_df, sales_df], config={"llm": llm, "custom_whitelisted_dependencies":["ast"], "response_parser": FlaskResponse, "enable_cache": False, "save_logs": False})
response = lake.chat(user_question)
return response
def create_inventory(user_phone, transaction_data):
for transaction in transaction_data:
item_name = transaction['details']['item'] # assumes unique item name
doc_ref = db.collection("users").document(user_phone).collection("inventory").document(item_name)
# transaction_data['transaction_id'] # let's default to random generated document ids
doc_ref.set(transaction)
# print("Transaction created successfully!")
return True
def create_sale(user_phone, transaction_data):
for transaction in transaction_data:
item_name = transaction['details']['item'] # assumes item names are unique per transactions
# fetch the inventory
inventory = fetch_transaction(user_phone, item_name)
# Do the sales calculations
new_stock_level = inventory['details']['quantity'] - transaction['details']['quantity']
inventory['details']['quantity'] = new_stock_level
# update the inventory
doc_ref = db.collection("users").document(user_phone).collection("inventory").document(item_name)
doc_ref.set(inventory)
# Create the sale
doc_ref = db.collection("users").document(user_phone).collection("sales").document(item_name)
doc_ref.set(transaction)
# print("Transaction created successfully!")
return True
# Update logic:
# -
def update_transaction(user_phone, transaction_id, update_data):
doc_ref = db.collection("users").document(user_phone).collection("transactions").document(transaction_id)
doc_ref.update(update_data)
# print("Transaction updated successfully!")
return True
def fetch_transaction(user_phone, transaction_id=None):
if transaction_id:
doc_ref = db.collection("users").document(user_phone).collection("inventory").document(transaction_id)
transaction = doc_ref.get()
if transaction.exists:
return transaction.to_dict()
else:
# print("Transaction not found.")
return None
else:
collection_ref = db.collection("users").document(user_phone).collection("inventory")
transactions = [doc.to_dict() for doc in collection_ref.stream()]
return transactions
# Delete fields or an entire transaction document.
# Delete specific fields
def delete_transaction_fields(user_phone, transaction_id, fields_to_delete):
doc_ref = db.collection("users").document(user_phone).collection("transactions").document(transaction_id)
updates = {field: firestore.DELETE_FIELD for field in fields_to_delete}
doc_ref.update(updates)
print("Fields deleted successfully!")
# Delete an entire transaction
def delete_transaction(user_phone, transaction_id):
doc_ref = db.collection("users").document(user_phone).collection("transactions").document(transaction_id)
doc_ref.delete()
print("Transaction deleted successfully!")
# Delete an entire transaction
def delete_transaction(user_phone, transaction_data):
# the transaction id currently defaults to the item name
transaction_id = transaction_data[0]['details']['item']
transaction_type = transaction_data[0]['transaction_type'].lower()
doc_ref = db.collection("users").document(user_phone).collection(transaction_type).document(transaction_id)
item_doc = doc_ref.get()
if item_doc.exists:
doc_ref.delete()
return True
else:
return False
# Example usage
# response_text = """
# The information provided indicates that you want to **create/record** a new transaction.
# **Extracted Information**:
# **Intent**: Create
# **Transaction Type**: Purchase
# **Details**:
# - Item: Car
# - Purpose: Business
# - Amount: 100000 USD
# - Tax: 1000 USD
# """
# parsed_data = parse_ai_response(response_text)
# print(parsed_data)
|