Yassine Mhirsi
commited on
Commit
·
16153ee
1
Parent(s):
2c1862b
feat: Introduce individual GROQ API keys for Topic, STT, TTS, and Chat services, enhancing configuration flexibility and service initialization checks across the application.
Browse files- config.py +6 -0
- main.py +24 -12
- services/chat_service.py +5 -5
- services/stt_service.py +4 -4
- services/topic_service.py +4 -4
- services/tts_service.py +4 -4
config.py
CHANGED
|
@@ -28,6 +28,12 @@ GENERATE_MODEL_ID = HUGGINGFACE_GENERATE_MODEL_ID
|
|
| 28 |
# ============ GROQ MODELS ============
|
| 29 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY", "")
|
| 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
# ============ GOOGLE MODELS ============
|
| 32 |
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "")
|
| 33 |
|
|
|
|
| 28 |
# ============ GROQ MODELS ============
|
| 29 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY", "")
|
| 30 |
|
| 31 |
+
# Individual API keys for different services (fallback to GROQ_API_KEY if not set)
|
| 32 |
+
GROQ_TOPIC_API_KEY = os.getenv("GROQ_TOPIC_API_KEY", GROQ_API_KEY)
|
| 33 |
+
GROQ_TTS_API_KEY = os.getenv("GROQ_TTS_API_KEY", GROQ_API_KEY)
|
| 34 |
+
GROQ_STT_API_KEY = os.getenv("GROQ_STT_API_KEY", GROQ_API_KEY)
|
| 35 |
+
GROQ_CHAT_API_KEY = os.getenv("GROQ_CHAT_API_KEY", GROQ_API_KEY)
|
| 36 |
+
|
| 37 |
# ============ GOOGLE MODELS ============
|
| 38 |
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "")
|
| 39 |
|
main.py
CHANGED
|
@@ -23,7 +23,8 @@ from config import (
|
|
| 23 |
HOST, PORT, RELOAD,
|
| 24 |
CORS_ORIGINS, CORS_METHODS, CORS_HEADERS, CORS_CREDENTIALS,
|
| 25 |
PRELOAD_MODELS_ON_STARTUP, LOAD_STANCE_MODEL, LOAD_KPA_MODEL,
|
| 26 |
-
|
|
|
|
| 27 |
)
|
| 28 |
|
| 29 |
# --- Fonction de nettoyage ---
|
|
@@ -106,7 +107,7 @@ async def lifespan(app: FastAPI):
|
|
| 106 |
logger.error("⚠️ Generation endpoints will not work!")
|
| 107 |
|
| 108 |
# Initialize Topic Extraction service (uses Groq LLM)
|
| 109 |
-
if topic_similarity_service and
|
| 110 |
try:
|
| 111 |
logger.info("Initializing Topic Extraction service (Groq LLM)...")
|
| 112 |
topic_similarity_service.initialize()
|
|
@@ -114,17 +115,28 @@ async def lifespan(app: FastAPI):
|
|
| 114 |
except Exception as e:
|
| 115 |
logger.error(f"✗ Failed to initialize Topic Extraction service: {str(e)}")
|
| 116 |
logger.error("⚠️ Topic extraction endpoints will not work!")
|
| 117 |
-
elif not
|
| 118 |
-
logger.warning("⚠
|
| 119 |
|
| 120 |
logger.info("✓ API startup complete")
|
| 121 |
logger.info("https://nlp-debater-project-fastapi-backend-models.hf.space/docs")
|
| 122 |
|
| 123 |
# Vérifier les clés API
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
else:
|
| 127 |
-
logger.info("✓
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
if not HUGGINGFACE_API_KEY:
|
| 130 |
logger.warning("⚠ HUGGINGFACE_API_KEY non configurée. Modèles locaux désactivés.")
|
|
@@ -164,7 +176,7 @@ async def lifespan(app: FastAPI):
|
|
| 164 |
logger.info(f" TTS Model: {GROQ_TTS_MODEL}")
|
| 165 |
logger.info(f" Chat Model: {GROQ_CHAT_MODEL}")
|
| 166 |
logger.info(f" Topic Extraction: {'Initialized' if (topic_similarity_service and topic_similarity_service.initialized) else 'Not initialized'}")
|
| 167 |
-
logger.info(f" Voice Chat: {'Available' if
|
| 168 |
logger.info(f" MCP: {'Activé' if MCP_ENABLED else 'Désactivé'}")
|
| 169 |
if MCP_ENABLED:
|
| 170 |
logger.info(f" - Tools: detect_stance, match_keypoint_argument, transcribe_audio, generate_speech, generate_argument, extract_topic, voice_chat, health_check")
|
|
@@ -277,11 +289,11 @@ async def health():
|
|
| 277 |
"service": "NLP Debater + Groq Voice",
|
| 278 |
"version": API_VERSION,
|
| 279 |
"features": {
|
| 280 |
-
"stt": GROQ_STT_MODEL if
|
| 281 |
-
"tts": GROQ_TTS_MODEL if
|
| 282 |
-
"chat": GROQ_CHAT_MODEL if
|
| 283 |
"topic_extraction": "initialized" if (topic_similarity_service and hasattr(topic_similarity_service, 'initialized') and topic_similarity_service.initialized) else "not initialized",
|
| 284 |
-
"voice_chat": "available" if
|
| 285 |
"stance_model": "loaded" if (stance_model_manager and hasattr(stance_model_manager, 'model_loaded') and stance_model_manager.model_loaded) else "not loaded",
|
| 286 |
"kpa_model": "loaded" if (kpa_model_manager and hasattr(kpa_model_manager, 'model_loaded') and kpa_model_manager.model_loaded) else "not loaded",
|
| 287 |
"generate_model": "loaded" if (generate_model_manager and hasattr(generate_model_manager, 'model_loaded') and generate_model_manager.model_loaded) else "not loaded",
|
|
|
|
| 23 |
HOST, PORT, RELOAD,
|
| 24 |
CORS_ORIGINS, CORS_METHODS, CORS_HEADERS, CORS_CREDENTIALS,
|
| 25 |
PRELOAD_MODELS_ON_STARTUP, LOAD_STANCE_MODEL, LOAD_KPA_MODEL,
|
| 26 |
+
GROQ_TOPIC_API_KEY, GROQ_STT_API_KEY, GROQ_TTS_API_KEY, GROQ_CHAT_API_KEY,
|
| 27 |
+
GROQ_STT_MODEL, GROQ_TTS_MODEL, GROQ_CHAT_MODEL
|
| 28 |
)
|
| 29 |
|
| 30 |
# --- Fonction de nettoyage ---
|
|
|
|
| 107 |
logger.error("⚠️ Generation endpoints will not work!")
|
| 108 |
|
| 109 |
# Initialize Topic Extraction service (uses Groq LLM)
|
| 110 |
+
if topic_similarity_service and GROQ_TOPIC_API_KEY:
|
| 111 |
try:
|
| 112 |
logger.info("Initializing Topic Extraction service (Groq LLM)...")
|
| 113 |
topic_similarity_service.initialize()
|
|
|
|
| 115 |
except Exception as e:
|
| 116 |
logger.error(f"✗ Failed to initialize Topic Extraction service: {str(e)}")
|
| 117 |
logger.error("⚠️ Topic extraction endpoints will not work!")
|
| 118 |
+
elif not GROQ_TOPIC_API_KEY:
|
| 119 |
+
logger.warning("⚠ GROQ_TOPIC_API_KEY not configured. Topic extraction service will not be available.")
|
| 120 |
|
| 121 |
logger.info("✓ API startup complete")
|
| 122 |
logger.info("https://nlp-debater-project-fastapi-backend-models.hf.space/docs")
|
| 123 |
|
| 124 |
# Vérifier les clés API
|
| 125 |
+
groq_services_configured = any([
|
| 126 |
+
GROQ_TOPIC_API_KEY,
|
| 127 |
+
GROQ_STT_API_KEY,
|
| 128 |
+
GROQ_TTS_API_KEY,
|
| 129 |
+
GROQ_CHAT_API_KEY
|
| 130 |
+
])
|
| 131 |
+
|
| 132 |
+
if not groq_services_configured:
|
| 133 |
+
logger.warning("⚠ Aucune clé GROQ API configurée. Fonctions STT/TTS/Chat/Topic désactivées.")
|
| 134 |
else:
|
| 135 |
+
logger.info("✓ Clés GROQ API configurées:")
|
| 136 |
+
if GROQ_TOPIC_API_KEY: logger.info(" - Topic Extraction: ✓")
|
| 137 |
+
if GROQ_STT_API_KEY: logger.info(" - Speech-to-Text: ✓")
|
| 138 |
+
if GROQ_TTS_API_KEY: logger.info(" - Text-to-Speech: ✓")
|
| 139 |
+
if GROQ_CHAT_API_KEY: logger.info(" - Chat/Voice Chat: ✓")
|
| 140 |
|
| 141 |
if not HUGGINGFACE_API_KEY:
|
| 142 |
logger.warning("⚠ HUGGINGFACE_API_KEY non configurée. Modèles locaux désactivés.")
|
|
|
|
| 176 |
logger.info(f" TTS Model: {GROQ_TTS_MODEL}")
|
| 177 |
logger.info(f" Chat Model: {GROQ_CHAT_MODEL}")
|
| 178 |
logger.info(f" Topic Extraction: {'Initialized' if (topic_similarity_service and topic_similarity_service.initialized) else 'Not initialized'}")
|
| 179 |
+
logger.info(f" Voice Chat: {'Available' if GROQ_CHAT_API_KEY else 'Disabled (no GROQ_CHAT_API_KEY)'}")
|
| 180 |
logger.info(f" MCP: {'Activé' if MCP_ENABLED else 'Désactivé'}")
|
| 181 |
if MCP_ENABLED:
|
| 182 |
logger.info(f" - Tools: detect_stance, match_keypoint_argument, transcribe_audio, generate_speech, generate_argument, extract_topic, voice_chat, health_check")
|
|
|
|
| 289 |
"service": "NLP Debater + Groq Voice",
|
| 290 |
"version": API_VERSION,
|
| 291 |
"features": {
|
| 292 |
+
"stt": GROQ_STT_MODEL if GROQ_STT_API_KEY else "disabled",
|
| 293 |
+
"tts": GROQ_TTS_MODEL if GROQ_TTS_API_KEY else "disabled",
|
| 294 |
+
"chat": GROQ_CHAT_MODEL if GROQ_CHAT_API_KEY else "disabled",
|
| 295 |
"topic_extraction": "initialized" if (topic_similarity_service and hasattr(topic_similarity_service, 'initialized') and topic_similarity_service.initialized) else "not initialized",
|
| 296 |
+
"voice_chat": "available" if GROQ_CHAT_API_KEY else "disabled",
|
| 297 |
"stance_model": "loaded" if (stance_model_manager and hasattr(stance_model_manager, 'model_loaded') and stance_model_manager.model_loaded) else "not loaded",
|
| 298 |
"kpa_model": "loaded" if (kpa_model_manager and hasattr(kpa_model_manager, 'model_loaded') and kpa_model_manager.model_loaded) else "not loaded",
|
| 299 |
"generate_model": "loaded" if (generate_model_manager and hasattr(generate_model_manager, 'model_loaded') and generate_model_manager.model_loaded) else "not loaded",
|
services/chat_service.py
CHANGED
|
@@ -4,7 +4,7 @@ from datetime import datetime
|
|
| 4 |
import requests
|
| 5 |
import json
|
| 6 |
|
| 7 |
-
from config import
|
| 8 |
|
| 9 |
# In-memory conversation storage
|
| 10 |
conversation_store: Dict[str, List[Dict]] = {}
|
|
@@ -56,7 +56,7 @@ def generate_chat_response(
|
|
| 56 |
messages.append({"role": msg["role"], "content": msg["content"]})
|
| 57 |
|
| 58 |
# 6. Call Groq Chat API
|
| 59 |
-
if not
|
| 60 |
# Fallback if no API key
|
| 61 |
response_text = f"Hello! You said: '{user_input}'. I'm a voice assistant configured to respond in English."
|
| 62 |
else:
|
|
@@ -87,13 +87,13 @@ def call_groq_chat_api(messages: List[Dict]) -> str:
|
|
| 87 |
"""
|
| 88 |
Call Groq Chat API
|
| 89 |
"""
|
| 90 |
-
if not
|
| 91 |
-
raise RuntimeError("
|
| 92 |
|
| 93 |
url = "https://api.groq.com/openai/v1/chat/completions"
|
| 94 |
|
| 95 |
headers = {
|
| 96 |
-
"Authorization": f"Bearer {
|
| 97 |
"Content-Type": "application/json"
|
| 98 |
}
|
| 99 |
|
|
|
|
| 4 |
import requests
|
| 5 |
import json
|
| 6 |
|
| 7 |
+
from config import GROQ_CHAT_API_KEY, GROQ_CHAT_MODEL
|
| 8 |
|
| 9 |
# In-memory conversation storage
|
| 10 |
conversation_store: Dict[str, List[Dict]] = {}
|
|
|
|
| 56 |
messages.append({"role": msg["role"], "content": msg["content"]})
|
| 57 |
|
| 58 |
# 6. Call Groq Chat API
|
| 59 |
+
if not GROQ_CHAT_API_KEY:
|
| 60 |
# Fallback if no API key
|
| 61 |
response_text = f"Hello! You said: '{user_input}'. I'm a voice assistant configured to respond in English."
|
| 62 |
else:
|
|
|
|
| 87 |
"""
|
| 88 |
Call Groq Chat API
|
| 89 |
"""
|
| 90 |
+
if not GROQ_CHAT_API_KEY:
|
| 91 |
+
raise RuntimeError("GROQ_CHAT_API_KEY is not configured")
|
| 92 |
|
| 93 |
url = "https://api.groq.com/openai/v1/chat/completions"
|
| 94 |
|
| 95 |
headers = {
|
| 96 |
+
"Authorization": f"Bearer {GROQ_CHAT_API_KEY}",
|
| 97 |
"Content-Type": "application/json"
|
| 98 |
}
|
| 99 |
|
services/stt_service.py
CHANGED
|
@@ -1,17 +1,17 @@
|
|
| 1 |
import requests
|
| 2 |
-
from config import
|
| 3 |
|
| 4 |
def speech_to_text(audio_file: str) -> str:
|
| 5 |
"""
|
| 6 |
Convert audio file to text using Groq's Whisper API (English only)
|
| 7 |
"""
|
| 8 |
-
if not
|
| 9 |
-
raise RuntimeError("
|
| 10 |
|
| 11 |
url = "https://api.groq.com/openai/v1/audio/transcriptions"
|
| 12 |
|
| 13 |
headers = {
|
| 14 |
-
"Authorization": f"Bearer {
|
| 15 |
}
|
| 16 |
|
| 17 |
with open(audio_file, "rb") as audio_data:
|
|
|
|
| 1 |
import requests
|
| 2 |
+
from config import GROQ_STT_API_KEY, GROQ_STT_MODEL
|
| 3 |
|
| 4 |
def speech_to_text(audio_file: str) -> str:
|
| 5 |
"""
|
| 6 |
Convert audio file to text using Groq's Whisper API (English only)
|
| 7 |
"""
|
| 8 |
+
if not GROQ_STT_API_KEY:
|
| 9 |
+
raise RuntimeError("GROQ_STT_API_KEY is not set in config")
|
| 10 |
|
| 11 |
url = "https://api.groq.com/openai/v1/audio/transcriptions"
|
| 12 |
|
| 13 |
headers = {
|
| 14 |
+
"Authorization": f"Bearer {GROQ_STT_API_KEY}"
|
| 15 |
}
|
| 16 |
|
| 17 |
with open(audio_file, "rb") as audio_data:
|
services/topic_service.py
CHANGED
|
@@ -7,7 +7,7 @@ from langchain_groq import ChatGroq
|
|
| 7 |
from pydantic import BaseModel, Field
|
| 8 |
from langsmith import traceable
|
| 9 |
|
| 10 |
-
from config import
|
| 11 |
|
| 12 |
logger = logging.getLogger(__name__)
|
| 13 |
|
|
@@ -31,8 +31,8 @@ class TopicService:
|
|
| 31 |
logger.info("Topic service already initialized")
|
| 32 |
return
|
| 33 |
|
| 34 |
-
if not
|
| 35 |
-
raise ValueError("
|
| 36 |
|
| 37 |
if model_name:
|
| 38 |
self.model_name = model_name
|
|
@@ -42,7 +42,7 @@ class TopicService:
|
|
| 42 |
|
| 43 |
llm = ChatGroq(
|
| 44 |
model=self.model_name,
|
| 45 |
-
api_key=
|
| 46 |
temperature=0.0,
|
| 47 |
max_tokens=512,
|
| 48 |
)
|
|
|
|
| 7 |
from pydantic import BaseModel, Field
|
| 8 |
from langsmith import traceable
|
| 9 |
|
| 10 |
+
from config import GROQ_TOPIC_API_KEY
|
| 11 |
|
| 12 |
logger = logging.getLogger(__name__)
|
| 13 |
|
|
|
|
| 31 |
logger.info("Topic service already initialized")
|
| 32 |
return
|
| 33 |
|
| 34 |
+
if not GROQ_TOPIC_API_KEY:
|
| 35 |
+
raise ValueError("GROQ_TOPIC_API_KEY not found in environment variables")
|
| 36 |
|
| 37 |
if model_name:
|
| 38 |
self.model_name = model_name
|
|
|
|
| 42 |
|
| 43 |
llm = ChatGroq(
|
| 44 |
model=self.model_name,
|
| 45 |
+
api_key=GROQ_TOPIC_API_KEY,
|
| 46 |
temperature=0.0,
|
| 47 |
max_tokens=512,
|
| 48 |
)
|
services/tts_service.py
CHANGED
|
@@ -2,7 +2,7 @@ import requests
|
|
| 2 |
import uuid
|
| 3 |
import os
|
| 4 |
from pathlib import Path
|
| 5 |
-
from config import
|
| 6 |
|
| 7 |
def text_to_speech(
|
| 8 |
text: str,
|
|
@@ -12,8 +12,8 @@ def text_to_speech(
|
|
| 12 |
"""
|
| 13 |
Convert text to speech using Groq's TTS API (English only)
|
| 14 |
"""
|
| 15 |
-
if not
|
| 16 |
-
raise RuntimeError("
|
| 17 |
|
| 18 |
if not text or not text.strip():
|
| 19 |
raise ValueError("Text cannot be empty")
|
|
@@ -21,7 +21,7 @@ def text_to_speech(
|
|
| 21 |
url = "https://api.groq.com/openai/v1/audio/speech"
|
| 22 |
|
| 23 |
headers = {
|
| 24 |
-
"Authorization": f"Bearer {
|
| 25 |
"Content-Type": "application/json"
|
| 26 |
}
|
| 27 |
|
|
|
|
| 2 |
import uuid
|
| 3 |
import os
|
| 4 |
from pathlib import Path
|
| 5 |
+
from config import GROQ_TTS_API_KEY, GROQ_TTS_MODEL
|
| 6 |
|
| 7 |
def text_to_speech(
|
| 8 |
text: str,
|
|
|
|
| 12 |
"""
|
| 13 |
Convert text to speech using Groq's TTS API (English only)
|
| 14 |
"""
|
| 15 |
+
if not GROQ_TTS_API_KEY:
|
| 16 |
+
raise RuntimeError("GROQ_TTS_API_KEY is not set in config")
|
| 17 |
|
| 18 |
if not text or not text.strip():
|
| 19 |
raise ValueError("Text cannot be empty")
|
|
|
|
| 21 |
url = "https://api.groq.com/openai/v1/audio/speech"
|
| 22 |
|
| 23 |
headers = {
|
| 24 |
+
"Authorization": f"Bearer {GROQ_TTS_API_KEY}",
|
| 25 |
"Content-Type": "application/json"
|
| 26 |
}
|
| 27 |
|