Spaces:
Sleeping
Sleeping
File size: 9,124 Bytes
ef287e1 4e138bb ef287e1 26c0cd1 ef287e1 26c0cd1 ef287e1 26c0cd1 ef287e1 26c0cd1 ef287e1 26c0cd1 ef287e1 26c0cd1 ef287e1 26c0cd1 ef287e1 26c0cd1 ef287e1 26c0cd1 ef287e1 4e138bb ef287e1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb 26c0cd1 4e138bb |
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 |
import os
import json
from pathlib import Path
from google import genai
from google.genai import types
from django.conf import settings
class GeminiService:
def __init__(self):
# Try to load from environment first
self.api_key = os.environ.get("GEMINI_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('GEMINI_API_KEY='):
self.api_key = line.split('=', 1)[1].strip()
break
if not self.api_key or self.api_key == 'your-gemini-api-key-here':
raise ValueError("GEMINI_API_KEY not found or invalid. Please set it in backend/.env file")
self.client = genai.Client(api_key=self.api_key)
# Use free tier model with generous quotas
self.model = "gemini-flash-latest" # Free tier: 15 RPM, 1M tokens/day
def process_voice_command(self, audio_bytes, mime_type="audio/mp3", context_products=None):
"""
Process audio bytes to extract transaction details.
Returns a dictionary with transcription and structured data.
"""
if context_products is None:
context_products = []
prompt = f"""
You are an AI assistant for a financial app called Akompta.
Your task is to listen to the user's voice command and extract transaction details.
The user might say things like:
- "J'ai vendu la tomate pour 500FCFA le Kilo" (Income)
- "J'ai payé un ordinateur à 300000FCFA" (Expense)
Here is the list of products currently in the user's inventory:
{json.dumps(context_products)}
If the user mentions a product from this list but doesn't specify the price, use the price from the list to calculate the total amount (amount = quantity * price).
Please perform the following:
1. Transcribe the audio exactly as spoken (in French).
2. Analyze the intent and extract structured data.
Return ONLY a JSON object with the following structure:
{{
"transcription": "The exact transcription",
"intent": "create_transaction",
"data": {{
"type": "income" or "expense",
"amount": number (e.g. 500),
"currency": "FCFA" or other,
"category": "Category name (e.g. Vente, Alimentation, Transport, Technologie)",
"name": "Description of the item or service (e.g. 'Vente de 3 Mangues')",
"date": "YYYY-MM-DD" or null if not specified (assume today if null)
}}
}}
Important: If a product price is found in the inventory list, ALWAYS calculate: amount = quantity * unit_price.
If the audio is not clear or not related to a transaction, return:
{{
"transcription": "...",
"intent": "unknown",
"error": "Reason"
}}
"""
try:
response = self.client.models.generate_content(
model=self.model,
contents=[
types.Content(
parts=[
types.Part.from_bytes(data=audio_bytes, mime_type=mime_type),
types.Part.from_text(text=prompt)
]
)
],
config=types.GenerateContentConfig(
response_mime_type="application/json"
)
)
result = json.loads(response.text)
print(f"Gemini AI Voice Result: {result}")
return result
except Exception as e:
print(f"Error calling Gemini: {e}")
return {
"transcription": "",
"intent": "error",
"error": str(e)
}
def process_text_command(self, text, context_products=None):
"""
Process text input to extract transaction details.
"""
if context_products is None:
context_products = []
prompt = f"""
You are an AI assistant for a financial app called Akompta.
Your task is to analyze the user's text command and extract transaction details.
The user might say things like:
- "J'ai vendu la tomate pour 500FCFA le Kilo" (Income)
- "J'ai payé un ordinateur à 300000FCFA" (Expense)
- "Ajoute un produit Tomate à 200FCFA le bol, j'en ai 30 en stock" (Create Product)
Here is the list of products currently in the user's inventory:
{json.dumps(context_products)}
If the user mentions a product from this list but doesn't specify the price, use the price from the list to calculate the total amount (amount = quantity * price).
Example: If "Mangue" is in the list at 100 FCFA and the user says "vendu 3 mangues", the amount should be 300.
Please perform the following:
1. Analyze the intent and extract structured data.
Return ONLY a JSON object with the following structure:
For transactions:
{{
"transcription": "The input text",
"intent": "create_transaction",
"data": {{
"type": "income" or "expense",
"amount": number,
"currency": "FCFA",
"category": "Category name",
"name": "Description (e.g. 'Vente de 3 Mangues')",
"date": "YYYY-MM-DD"
}}
}}
Important: If a product price is found in the inventory list, ALWAYS calculate: amount = quantity * unit_price.
For products/inventory:
{{
"transcription": "The input text",
"intent": "create_product",
"data": {{
"name": "Product name",
"price": number,
"unit": "Unit (e.g. bol, kg, unit)",
"description": "Short description",
"category": "vente" or "depense" or "stock",
"stock_status": "ok" or "low" or "rupture"
}}
}}
If the text is not clear, return:
{{
"transcription": "...",
"intent": "unknown",
"error": "Reason"
}}
"""
try:
response = self.client.models.generate_content(
model=self.model,
contents=[
types.Content(
parts=[
types.Part.from_text(text=f"User command: {text}"),
types.Part.from_text(text=prompt)
]
)
],
config=types.GenerateContentConfig(
response_mime_type="application/json"
)
)
result = json.loads(response.text)
print(f"Gemini AI Result: {result}")
# Ensure transcription is the input text if not provided by AI
if not result.get('transcription'):
result['transcription'] = text
return result
except Exception as e:
print(f"Error calling Gemini: {e}")
return {
"transcription": text,
"intent": "error",
"error": str(e)
}
def process_insights(self, context_data):
"""
Generate financial insights based on context data.
"""
prompt = f"""
Tu es un analyste financier expert pour l'application Akompta.
Analyse les données suivantes (JSON) :
{json.dumps(context_data)}
Génère exactement 3 insights courts et percutants (max 1 phrase chacun) en Français :
1. Une observation sur les ventes ou revenus.
2. Une observation sur les dépenses.
3. Une alerte sur le stock ou une recommandation.
Format de réponse attendu : Une liste simple de 3 phrases séparées par des sauts de ligne. Pas de markdown complexe, pas de titres.
"""
try:
response = self.client.models.generate_content(
model=self.model,
contents=prompt
)
text = response.text or ""
items = [line.strip() for line in text.split('\n') if line.strip()]
return items[:3]
except Exception as e:
print(f"Error calling Gemini for insights: {e}")
return [
"Analyse des ventes en cours...",
"Vérification des stocks...",
"Calcul des marges..."
]
|