Initial commit for fresh VoiceInvoiceAI repo
This commit is contained in:
115
backend/core/utils.py
Normal file
115
backend/core/utils.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# backend/core/utils.py
|
||||
|
||||
import torch
|
||||
import gc
|
||||
import re
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, Any, Optional
|
||||
from backend.config import user_db, item_db # These are now mock DBs, kept for autofill logic
|
||||
from backend.models import InvoiceData, InvoiceItem, ValidationError # ValidationError for type hinting/catching
|
||||
|
||||
def clear_gpu_memory():
|
||||
"""Clears CUDA memory cache and runs garbage collection."""
|
||||
if torch.cuda.is_available():
|
||||
torch.cuda.empty_cache()
|
||||
gc.collect()
|
||||
|
||||
def autofill_invoice_data(invoice_data: InvoiceData) -> InvoiceData:
|
||||
"""
|
||||
Autofills invoice data based on simulated local databases (user_db, item_db).
|
||||
Fills in missing client address, item prices, and calculates totals/dates.
|
||||
|
||||
Args:
|
||||
invoice_data (InvoiceData): The invoice data Pydantic model to autofill.
|
||||
|
||||
Returns:
|
||||
InvoiceData: The updated invoice data with autofilled information.
|
||||
"""
|
||||
# Create a mutable copy to avoid modifying the original object in place
|
||||
# if this function is called on an object that might be reused or
|
||||
# that came directly from a FastAPI request body.
|
||||
invoice_data_copy = invoice_data.model_copy(deep=True)
|
||||
|
||||
if invoice_data_copy.client_name:
|
||||
normalized_client_name = invoice_data_copy.client_name.lower().strip()
|
||||
if normalized_client_name in user_db:
|
||||
client_info = user_db[normalized_client_name]
|
||||
if not invoice_data_copy.client_address:
|
||||
invoice_data_copy.client_address = client_info.get("address")
|
||||
# Only update tax_rate if it's not explicitly set in the incoming data
|
||||
if invoice_data_copy.tax_rate is None or invoice_data_copy.tax_rate == 0.08: # Default value
|
||||
invoice_data_copy.tax_rate = client_info.get("default_tax_rate", 0.08)
|
||||
|
||||
for item in invoice_data_copy.items:
|
||||
# If unit_price is missing but description exists, try to autofill
|
||||
if not item.unit_price and item.description:
|
||||
normalized_description = item.description.lower().strip()
|
||||
for db_item, db_info in item_db.items():
|
||||
if db_item in normalized_description: # Simple substring match
|
||||
item.unit_price = db_info.get("unit_price")
|
||||
# Break after finding the first match
|
||||
break
|
||||
# Ensure item total is calculated or re-calculated after autofill of unit_price
|
||||
if item.quantity is not None and item.unit_price is not None:
|
||||
item.total = round(item.quantity * item.unit_price, 2)
|
||||
elif item.total is None: # If no quantity/unit_price but total needed
|
||||
item.total = 0.0 # Default to 0 if not enough info
|
||||
|
||||
# Recalculate totals after autofilling item prices and ensure they are not None
|
||||
invoice_data_copy.subtotal = round(sum(item.total for item in invoice_data_copy.items if item.total is not None), 2)
|
||||
effective_tax_rate = invoice_data_copy.tax_rate if invoice_data_copy.tax_rate is not None else 0.0
|
||||
invoice_data_copy.tax_amount = round(invoice_data_copy.subtotal * effective_tax_rate, 2)
|
||||
invoice_data_copy.grand_total = round(invoice_data_copy.subtotal + invoice_data_copy.tax_amount, 2)
|
||||
|
||||
# Handle dates
|
||||
if not invoice_data_copy.invoice_date:
|
||||
invoice_data_copy.invoice_date = datetime.now().strftime("%Y-%m-%d")
|
||||
if not invoice_data_copy.due_date:
|
||||
try:
|
||||
invoice_date_dt = datetime.strptime(invoice_data_copy.invoice_date, "%Y-%m-%d")
|
||||
invoice_data_copy.due_date = (invoice_date_dt + timedelta(days=30)).strftime("%Y-%m-%d")
|
||||
except (ValueError, TypeError):
|
||||
# Fallback if invoice_date is invalid or not set
|
||||
invoice_data_copy.due_date = (datetime.now() + timedelta(days=30)).strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
return invoice_data_copy
|
||||
|
||||
def check_model_devices(qwen2_audio_model: Optional[Any], device: str) -> str:
|
||||
"""
|
||||
Checks the device placement and memory usage of the Qwen2-Audio model.
|
||||
"""
|
||||
status_output = []
|
||||
status_output.append("--- Checking Model Device Placement ---")
|
||||
|
||||
if qwen2_audio_model:
|
||||
try:
|
||||
if hasattr(qwen2_audio_model, 'hf_device_map') and qwen2_audio_model.hf_device_map:
|
||||
status_output.append(f"Qwen2-Audio model device map: {qwen2_audio_model.hf_device_map}")
|
||||
else:
|
||||
qwen_device = next(qwen2_audio_model.parameters()).device
|
||||
status_output.append(f"Qwen2-Audio model (first parameter) is on: {qwen_device}")
|
||||
|
||||
status_output.append(f"Qwen2-Audio model data type: {next(qwen2_audio_model.parameters()).dtype}")
|
||||
|
||||
if device == "cuda":
|
||||
status_output.append(f"Current GPU Memory Allocated: {torch.cuda.memory_allocated() / (1024**3):.2f} GB")
|
||||
status_output.append(f"Current GPU Memory Cached: {torch.cuda.memory_reserved() / (1024**3):.2f} GB")
|
||||
status_output.append(f"Max GPU Memory Allocated: {torch.cuda.max_memory_allocated() / (1024**3):.2f} GB")
|
||||
status_output.append(f"Max GPU Memory Cached: {torch.cuda.max_memory_reserved() / (1024**3):.2f} GB")
|
||||
except Exception as e:
|
||||
status_output.append(f"Error checking model device: {e}")
|
||||
else:
|
||||
status_output.append("Qwen2-Audio model is not loaded.")
|
||||
|
||||
status_output.append(f"System detected device: {device}")
|
||||
if device == "cuda":
|
||||
status_output.append(f"Number of CUDA devices: {torch.cuda.device_count()}")
|
||||
if torch.cuda.device_count() > 0:
|
||||
status_output.append(f"Current CUDA device: {torch.cuda.current_device()} ({torch.cuda.get_device_name(torch.cuda.current_device())})")
|
||||
status_output.append(f"Total GPU Memory on current device: {torch.cuda.get_device_properties(torch.cuda.current_device()).total_memory / (1024**3):.2f} GB")
|
||||
else:
|
||||
status_output.append("No CUDA devices detected. Running on CPU.")
|
||||
|
||||
return "\n".join(status_output)
|
||||
Reference in New Issue
Block a user