AkomptaBackend / api /groq_service.py
rinogeek's picture
Initial commit of backend code to Hugging Face Space
385a349
import os
import json
from pathlib import Path
from groq import Groq
from django.conf import settings
class GroqService:
def __init__(self):
# Try to load from environment first
self.api_key = os.environ.get("GROQ_API_KEY")
# If not in environment, try loading from .env file directly
if not self.api_key:
env_path = Path(__file__).resolve().parent.parent / '.env'
if env_path.exists():
with open(env_path, 'r') as f:
for line in f:
line = line.strip()
if line.startswith('GROQ_API_KEY='):
self.api_key = line.split('=', 1)[1].strip()
break
if not self.api_key or self.api_key == 'your-groq-api-key-here':
# Note: We fallback to 'your-groq-api-key-here' to avoid crashing if it's in .env as a placeholder
print("Warning: GROQ_API_KEY not found or invalid.")
self.client = Groq(api_key=self.api_key)
self.model = "whisper-large-v3-turbo"
def transcribe(self, audio_file, language=None):
"""
Transcribe audio file using Groq's Whisper API.
audio_file can be a file-like object or a path.
"""
try:
# Groq expects a file object or a tuple (filename, content, content_type)
# For Django's UploadedFile, passing (file.name, file.read()) works best
file_tuple = (audio_file.name, audio_file.read())
# Context prompt to help Whisper with terminology and language detection
context_prompt = "Ceci est une commande vocale pour l'application financière Akompta. L'utilisateur enregistre ses ventes, ses achats ou ses stocks. Ex: 'J'ai vendu 2 kilos de tomates', 'Paiement fournisseur', 'Ajouter du sucre au stock'."
params = {
"file": file_tuple,
"model": self.model,
"response_format": "json",
"temperature": 0.0,
"prompt": context_prompt
}
# Use 'fr' by default if no language is specified, to avoid wrong auto-detection
if language:
params["language"] = language.lower()[:2] # e.g. 'fr' or 'en'
else:
params["language"] = "fr" # Default to French for this app context
transcription = self.client.audio.transcriptions.create(**params)
return transcription.text
except Exception as e:
print(f"Error calling Groq STT: {e}")
return None
def process_text_command(self, text, context_products=None, model="llama-3.3-70b-versatile"):
"""
Process text command using Groq's LLM models.
"""
if context_products is None:
context_products = []
system_prompt = f"""
You are an AI assistant for Akompta, a financial and inventory management app.
Your task is to identify if the user wants to record a financial transaction (income/expense) or manage their inventory (create/update a product).
RULES:
1. If the user reports a SALE or PURCHASE of an item, it's a 'create_transaction'.
2. If the user says they want to ADD, REGISTER, or CREATE an item in their catalog/stock, it's a 'create_product'.
3. For 'create_transaction':
- type: 'income' for sales, 'expense' for purchases/costs.
- category: Use a descriptive name like 'Vente', 'Achat', 'Nourriture', etc.
- name: A SHORT and DESCRIPTIVE name of the transaction (ex: 'Vente de Savon', 'Achat de Sac de Riz').
4. For 'create_product':
- name: The name of the product.
- category: MUST BE exactly one of: 'vente', 'depense', 'stock'.
- stock_status: MUST BE exactly one of: 'ok', 'low', 'rupture'.
Inventory Context (Existing Products):
{json.dumps(context_products)}
Return ONLY a JSON object with this EXACT structure:
If intent is 'create_transaction':
{{
"transcription": "...",
"intent": "create_transaction",
"data": {{
"type": "income" or "expense",
"amount": number,
"currency": "FCFA",
"category": "Descriptive category",
"name": "Descriptive name",
"date": "YYYY-MM-DD"
}}
}}
If intent is 'create_product':
{{
"transcription": "...",
"intent": "create_product",
"data": {{
"name": "Product name",
"price": number,
"unit": "Kg, Unit, etc.",
"description": "...",
"category": "vente" or "depense" or "stock",
"stock_status": "ok" or "low" or "rupture"
}}
}}
"""
try:
chat_completion = self.client.chat.completions.create(
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": text}
],
model=model,
response_format={"type": "json_object"},
temperature=0.0
)
result_text = chat_completion.choices[0].message.content
return json.loads(result_text)
except Exception as e:
print(f"Error calling Groq LLM ({model}): {e}")
return None