malek-messaoudii commited on
Commit
c45f0d6
·
1 Parent(s): 768f4da

Correct version

Browse files
main.py CHANGED
@@ -2,6 +2,8 @@ import sys
2
  from pathlib import Path
3
  import logging
4
  from contextlib import asynccontextmanager
 
 
5
 
6
  from fastapi import FastAPI
7
  from fastapi.middleware.cors import CORSMiddleware
@@ -24,51 +26,103 @@ from config import (
24
  HUGGINGFACE_API_KEY, HUGGINGFACE_STANCE_MODEL_ID, HUGGINGFACE_LABEL_MODEL_ID,
25
  HOST, PORT, RELOAD,
26
  CORS_ORIGINS, CORS_METHODS, CORS_HEADERS, CORS_CREDENTIALS,
27
- PRELOAD_MODELS_ON_STARTUP, LOAD_STANCE_MODEL, LOAD_KPA_MODEL
 
28
  )
29
 
30
- # --- Import des singletons de services ---
31
- from services.stance_model_manager import stance_model_manager
32
- from services.label_model_manager import kpa_model_manager
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- from services.stt_service import speech_to_text
35
- from services.tts_service import text_to_speech
 
 
 
 
 
 
 
36
 
37
  # --- Lifespan / startup API ---
38
  @asynccontextmanager
39
  async def lifespan(app: FastAPI):
40
  logger.info("="*60)
41
- logger.info("🚀 API STARTUP - Loading HuggingFace models...")
42
  logger.info("="*60)
43
-
 
 
 
 
 
 
 
 
 
 
 
 
44
  if PRELOAD_MODELS_ON_STARTUP:
45
-
46
  # Charger stance model
47
- if LOAD_STANCE_MODEL:
48
  try:
49
  stance_model_manager.load_model(HUGGINGFACE_STANCE_MODEL_ID, HUGGINGFACE_API_KEY)
50
- logger.info("✓ Stance model loaded")
51
  except Exception as e:
52
  logger.error(f"✗ Failed loading stance model: {e}")
53
 
54
  # Charger KPA model
55
- if LOAD_KPA_MODEL:
56
  try:
57
  kpa_model_manager.load_model(HUGGINGFACE_LABEL_MODEL_ID, HUGGINGFACE_API_KEY)
58
- logger.info("✓ KPA model loaded")
59
  except Exception as e:
60
  logger.error(f"✗ Failed loading KPA model: {e}")
61
 
62
- logger.info("✓ Startup complete. API ready.")
 
 
 
 
 
 
63
  yield
64
- logger.info("🛑 Shutting down...")
 
 
 
65
 
66
  # --- FastAPI app ---
67
  app = FastAPI(
68
  title=API_TITLE,
69
  description=API_DESCRIPTION,
70
  version=API_VERSION,
71
- lifespan=lifespan
 
 
 
72
  )
73
 
74
  # --- CORS ---
@@ -81,41 +135,98 @@ app.add_middleware(
81
  )
82
 
83
  # --- Routes ---
 
84
  try:
85
  from routes.stt_routes import router as stt_router
86
  app.include_router(stt_router, prefix="/api/v1/stt", tags=["Speech To Text"])
87
  logger.info("✓ STT route loaded (Groq Whisper)")
 
 
88
  except Exception as e:
89
  logger.warning(f"⚠ Failed loading STT route: {e}")
90
 
 
91
  try:
92
  from routes.tts_routes import router as tts_router
93
  app.include_router(tts_router, prefix="/api/v1/tts", tags=["Text To Speech"])
94
  logger.info("✓ TTS route loaded (Groq PlayAI TTS)")
 
 
95
  except Exception as e:
96
  logger.warning(f"⚠ Failed loading TTS route: {e}")
97
 
 
98
  try:
99
  from routes import api_router
100
  app.include_router(api_router)
101
  logger.info("✓ Main API routes loaded")
 
 
102
  except Exception as e:
103
  logger.warning(f"⚠ Failed loading main API routes: {e}")
104
 
105
  # --- Basic routes ---
106
- @app.get("/health")
107
  async def health():
108
- return {"status": "healthy", "service": "NLP Debater + Groq Voice"}
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- @app.get("/")
111
  async def root():
 
112
  return {
113
  "message": "NLP Debater API with Groq Voice Support",
114
- "docs": "/docs",
115
- "voice_stt": "/api/v1/stt",
116
- "voice_tts": "/api/v1/tts"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
 
119
  # --- Run server ---
120
  if __name__ == "__main__":
121
- uvicorn.run("main:app", host=HOST, port=PORT, reload=RELOAD)
 
 
 
 
 
 
 
 
 
 
 
 
2
  from pathlib import Path
3
  import logging
4
  from contextlib import asynccontextmanager
5
+ import atexit
6
+ import shutil
7
 
8
  from fastapi import FastAPI
9
  from fastapi.middleware.cors import CORSMiddleware
 
26
  HUGGINGFACE_API_KEY, HUGGINGFACE_STANCE_MODEL_ID, HUGGINGFACE_LABEL_MODEL_ID,
27
  HOST, PORT, RELOAD,
28
  CORS_ORIGINS, CORS_METHODS, CORS_HEADERS, CORS_CREDENTIALS,
29
+ PRELOAD_MODELS_ON_STARTUP, LOAD_STANCE_MODEL, LOAD_KPA_MODEL,
30
+ GROQ_API_KEY, GROQ_STT_MODEL, GROQ_TTS_MODEL, GROQ_CHAT_MODEL
31
  )
32
 
33
+ # --- Fonction de nettoyage ---
34
+ def cleanup_temp_files():
35
+ """Nettoyer les fichiers temporaires audio au démarrage"""
36
+ temp_dir = Path("temp_audio")
37
+ if temp_dir.exists():
38
+ try:
39
+ shutil.rmtree(temp_dir)
40
+ logger.info("✓ Cleaned up previous temp audio files")
41
+ except Exception as e:
42
+ logger.warning(f"⚠ Could not clean temp directory: {e}")
43
+
44
+ # Appeler au démarrage
45
+ cleanup_temp_files()
46
+
47
+ # Configurer le nettoyage à la fermeture
48
+ @atexit.register
49
+ def cleanup_on_exit():
50
+ temp_dir = Path("temp_audio")
51
+ if temp_dir.exists():
52
+ try:
53
+ shutil.rmtree(temp_dir)
54
+ except:
55
+ pass
56
 
57
+ # --- Import des singletons de services ---
58
+ try:
59
+ from services.stance_model_manager import stance_model_manager
60
+ from services.label_model_manager import kpa_model_manager
61
+ logger.info("✓ Model managers imported")
62
+ except ImportError as e:
63
+ logger.warning(f"⚠ Could not import model managers: {e}")
64
+ stance_model_manager = None
65
+ kpa_model_manager = None
66
 
67
  # --- Lifespan / startup API ---
68
  @asynccontextmanager
69
  async def lifespan(app: FastAPI):
70
  logger.info("="*60)
71
+ logger.info("🚀 API STARTUP - Loading models and checking APIs...")
72
  logger.info("="*60)
73
+
74
+ # Vérifier les clés API
75
+ if not GROQ_API_KEY:
76
+ logger.warning("⚠ GROQ_API_KEY is not set. STT/TTS features may not work.")
77
+ else:
78
+ logger.info("✓ GROQ_API_KEY is configured")
79
+
80
+ if not HUGGINGFACE_API_KEY:
81
+ logger.warning("⚠ HUGGINGFACE_API_KEY is not set. Local models may not work.")
82
+ else:
83
+ logger.info("✓ HUGGINGFACE_API_KEY is configured")
84
+
85
+ # Précharger les modèles Hugging Face si configuré
86
  if PRELOAD_MODELS_ON_STARTUP:
87
+
88
  # Charger stance model
89
+ if LOAD_STANCE_MODEL and stance_model_manager and HUGGINGFACE_STANCE_MODEL_ID:
90
  try:
91
  stance_model_manager.load_model(HUGGINGFACE_STANCE_MODEL_ID, HUGGINGFACE_API_KEY)
92
+ logger.info("✓ Stance model loaded successfully")
93
  except Exception as e:
94
  logger.error(f"✗ Failed loading stance model: {e}")
95
 
96
  # Charger KPA model
97
+ if LOAD_KPA_MODEL and kpa_model_manager and HUGGINGFACE_LABEL_MODEL_ID:
98
  try:
99
  kpa_model_manager.load_model(HUGGINGFACE_LABEL_MODEL_ID, HUGGINGFACE_API_KEY)
100
+ logger.info("✓ KPA model loaded successfully")
101
  except Exception as e:
102
  logger.error(f"✗ Failed loading KPA model: {e}")
103
 
104
+ logger.info("="*60)
105
+ logger.info("✓ Startup complete. API ready to receive requests.")
106
+ logger.info(f" STT Model: {GROQ_STT_MODEL}")
107
+ logger.info(f" TTS Model: {GROQ_TTS_MODEL}")
108
+ logger.info(f" Chat Model: {GROQ_CHAT_MODEL}")
109
+ logger.info("="*60)
110
+
111
  yield
112
+
113
+ logger.info("🛑 Shutting down API...")
114
+ # Nettoyage final
115
+ cleanup_on_exit()
116
 
117
  # --- FastAPI app ---
118
  app = FastAPI(
119
  title=API_TITLE,
120
  description=API_DESCRIPTION,
121
  version=API_VERSION,
122
+ lifespan=lifespan,
123
+ docs_url="/docs",
124
+ redoc_url="/redoc",
125
+ openapi_url="/openapi.json"
126
  )
127
 
128
  # --- CORS ---
 
135
  )
136
 
137
  # --- Routes ---
138
+ # STT Routes
139
  try:
140
  from routes.stt_routes import router as stt_router
141
  app.include_router(stt_router, prefix="/api/v1/stt", tags=["Speech To Text"])
142
  logger.info("✓ STT route loaded (Groq Whisper)")
143
+ except ImportError as e:
144
+ logger.warning(f"⚠ STT route not found: {e}")
145
  except Exception as e:
146
  logger.warning(f"⚠ Failed loading STT route: {e}")
147
 
148
+ # TTS Routes
149
  try:
150
  from routes.tts_routes import router as tts_router
151
  app.include_router(tts_router, prefix="/api/v1/tts", tags=["Text To Speech"])
152
  logger.info("✓ TTS route loaded (Groq PlayAI TTS)")
153
+ except ImportError as e:
154
+ logger.warning(f"⚠ TTS route not found: {e}")
155
  except Exception as e:
156
  logger.warning(f"⚠ Failed loading TTS route: {e}")
157
 
158
+ # Main API Routes
159
  try:
160
  from routes import api_router
161
  app.include_router(api_router)
162
  logger.info("✓ Main API routes loaded")
163
+ except ImportError as e:
164
+ logger.warning(f"⚠ Main API routes not found: {e}")
165
  except Exception as e:
166
  logger.warning(f"⚠ Failed loading main API routes: {e}")
167
 
168
  # --- Basic routes ---
169
+ @app.get("/health", tags=["Health"])
170
  async def health():
171
+ """Health check endpoint"""
172
+ health_status = {
173
+ "status": "healthy",
174
+ "service": "NLP Debater + Groq Voice",
175
+ "features": {
176
+ "stt": GROQ_STT_MODEL if GROQ_API_KEY else "disabled",
177
+ "tts": GROQ_TTS_MODEL if GROQ_API_KEY else "disabled",
178
+ "chat": GROQ_CHAT_MODEL if GROQ_API_KEY else "disabled",
179
+ "stance_model": "loaded" if (stance_model_manager and stance_model_manager.model is not None) else "not loaded",
180
+ "kpa_model": "loaded" if (kpa_model_manager and kpa_model_manager.model is not None) else "not loaded"
181
+ }
182
+ }
183
+ return health_status
184
 
185
+ @app.get("/", tags=["Root"])
186
  async def root():
187
+ """Root endpoint with API information"""
188
  return {
189
  "message": "NLP Debater API with Groq Voice Support",
190
+ "version": API_VERSION,
191
+ "endpoints": {
192
+ "docs": "/docs",
193
+ "redoc": "/redoc",
194
+ "health": "/health",
195
+ "stt": "/api/v1/stt/",
196
+ "tts": "/api/v1/tts/"
197
+ },
198
+ "models": {
199
+ "stt": GROQ_STT_MODEL,
200
+ "tts": GROQ_TTS_MODEL,
201
+ "chat": GROQ_CHAT_MODEL
202
+ }
203
+ }
204
+
205
+ # --- Error handlers ---
206
+ @app.exception_handler(404)
207
+ async def not_found_handler(request, exc):
208
+ return {
209
+ "error": "Not Found",
210
+ "message": f"The requested URL {request.url} was not found",
211
+ "available_endpoints": {
212
+ "GET /": "API information",
213
+ "GET /health": "Health check",
214
+ "POST /api/v1/stt/": "Speech to text",
215
+ "POST /api/v1/tts/": "Text to speech"
216
+ }
217
  }
218
 
219
  # --- Run server ---
220
  if __name__ == "__main__":
221
+ logger.info("="*60)
222
+ logger.info(f"Starting server on {HOST}:{PORT}")
223
+ logger.info(f"Reload mode: {RELOAD}")
224
+ logger.info("="*60)
225
+
226
+ uvicorn.run(
227
+ "main:app",
228
+ host=HOST,
229
+ port=PORT,
230
+ reload=RELOAD,
231
+ log_level="info"
232
+ )
models/stt.py CHANGED
@@ -2,3 +2,10 @@ from pydantic import BaseModel
2
 
3
  class STTResponse(BaseModel):
4
  text: str
 
 
 
 
 
 
 
 
2
 
3
  class STTResponse(BaseModel):
4
  text: str
5
+
6
+ class Config:
7
+ json_schema_extra = {
8
+ "example": {
9
+ "text": "Bonjour, comment allez-vous aujourd'hui ?"
10
+ }
11
+ }
models/tts.py CHANGED
@@ -1,6 +1,16 @@
1
- from pydantic import BaseModel
 
2
 
3
  class TTSRequest(BaseModel):
4
- text: str
5
- voice: str = "Aaliyah-PlayAI"
6
- format: str = "wav"
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ from typing import Optional
3
 
4
  class TTSRequest(BaseModel):
5
+ text: str = Field(..., min_length=1, max_length=5000)
6
+ voice: str = Field(default="Aaliyah-PlayAI")
7
+ format: str = Field(default="wav", pattern="^(wav|mp3)$")
8
+
9
+ class Config:
10
+ json_schema_extra = {
11
+ "example": {
12
+ "text": "Bonjour, ceci est un test de synthèse vocale.",
13
+ "voice": "Aaliyah-PlayAI",
14
+ "format": "wav"
15
+ }
16
+ }
requirements.txt CHANGED
@@ -2,31 +2,19 @@ fastapi==0.104.1
2
  uvicorn[standard]==0.24.0
3
  python-multipart==0.0.6
4
  python-dotenv==1.0.0
5
- gtts==2.3.2
6
  pydantic==2.5.0
7
- SpeechRecognition==3.10.0
8
- pyttsx3==2.90
9
 
10
- # Fix compatibilité NumPy + Torch + Transformers
11
- numpy==1.26.4
12
-
13
- # Transformers et dépendances compatibles
14
- transformers==4.35.0
15
- tokenizers==0.14.1
16
- huggingface_hub==0.16.4
17
- accelerate==0.20.3
18
- safetensors>=0.3.1
19
-
20
- # Modèle killer pour protobuf
21
- protobuf==3.20.0
22
 
23
- # Audio + STT
24
  soundfile==0.12.1
25
- requests==2.31.0
26
 
27
- # PyTorch CPU compatible NumPy<2
 
28
  torch==2.0.1+cpu
29
  --extra-index-url https://download.pytorch.org/whl/cpu
30
 
31
- # Groq SDK
32
- groq==0.9.0
 
2
  uvicorn[standard]==0.24.0
3
  python-multipart==0.0.6
4
  python-dotenv==1.0.0
 
5
  pydantic==2.5.0
 
 
6
 
7
+ # API Clients
8
+ requests==2.31.0
9
+ groq==0.9.0
 
 
 
 
 
 
 
 
 
10
 
11
+ # Audio processing (optionnel si vous avez besoin de traitement local)
12
  soundfile==0.12.1
 
13
 
14
+ # Hugging Face
15
+ transformers==4.35.0
16
  torch==2.0.1+cpu
17
  --extra-index-url https://download.pytorch.org/whl/cpu
18
 
19
+ # Autres dépendances
20
+ numpy==1.26.4
routes/stt_routes.py CHANGED
@@ -1,27 +1,42 @@
1
- from fastapi import APIRouter, UploadFile, File
 
2
  from services.stt_service import speech_to_text
3
  from models.stt import STTResponse
4
  import os
5
  import uuid
 
 
6
 
7
  router = APIRouter(prefix="/stt", tags=["Speech To Text"])
8
 
9
  @router.post("/", response_model=STTResponse)
10
- async def convert_stt(file: UploadFile = File(...)):
11
- # dossier temporaire
12
- os.makedirs("audio/temp", exist_ok=True)
 
 
 
 
13
 
14
- # nom temporaire unique
15
- temp_name = f"audio/temp/{uuid.uuid4()}_{file.filename}"
 
 
 
 
 
16
 
17
- # save file
18
- with open(temp_name, "wb") as f:
19
- f.write(await file.read())
20
-
21
- # STT conversion
22
- text = speech_to_text(temp_name)
23
-
24
- # cleanup
25
- os.remove(temp_name)
26
-
27
- return STTResponse(text=text)
 
 
 
 
1
+ from fastapi import APIRouter, UploadFile, File, HTTPException
2
+ from fastapi.responses import JSONResponse
3
  from services.stt_service import speech_to_text
4
  from models.stt import STTResponse
5
  import os
6
  import uuid
7
+ import tempfile
8
+ from pathlib import Path
9
 
10
  router = APIRouter(prefix="/stt", tags=["Speech To Text"])
11
 
12
  @router.post("/", response_model=STTResponse)
13
+ async def convert_speech_to_text(file: UploadFile = File(...)):
14
+ """
15
+ Convert uploaded audio file to text using Groq's Whisper API
16
+ """
17
+ # Vérifier le type de fichier
18
+ if not file.content_type or not file.content_type.startswith('audio/'):
19
+ raise HTTPException(status_code=400, detail="File must be an audio file")
20
 
21
+ # Créer un fichier temporaire unique
22
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_file:
23
+ temp_path = temp_file.name
24
+
25
+ # Écrire le contenu téléchargé
26
+ content = await file.read()
27
+ temp_file.write(content)
28
 
29
+ try:
30
+ # Convertir audio en texte
31
+ text = speech_to_text(temp_path)
32
+
33
+ # Nettoyer le fichier temporaire
34
+ os.unlink(temp_path)
35
+
36
+ return STTResponse(text=text)
37
+
38
+ except Exception as e:
39
+ # Nettoyer en cas d'erreur
40
+ if os.path.exists(temp_path):
41
+ os.unlink(temp_path)
42
+ raise HTTPException(status_code=500, detail=str(e))
routes/tts_routes.py CHANGED
@@ -1,22 +1,41 @@
1
- from fastapi import APIRouter
2
  from fastapi.responses import FileResponse
3
  from models.tts import TTSRequest
4
  from services.tts_service import text_to_speech
 
5
  from pathlib import Path
6
 
7
  router = APIRouter(prefix="/tts", tags=["Text To Speech"])
8
 
9
  @router.post("/")
10
- async def generate_tts(request: TTSRequest):
11
- output_path = text_to_speech(
12
- text=request.text,
13
- voice=request.voice,
14
- fmt=request.format
15
- )
16
-
17
-
18
- return FileResponse(
19
- output_path,
20
- filename=output_path.name,
21
- media_type="audio/wav"
22
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException
2
  from fastapi.responses import FileResponse
3
  from models.tts import TTSRequest
4
  from services.tts_service import text_to_speech
5
+ import os
6
  from pathlib import Path
7
 
8
  router = APIRouter(prefix="/tts", tags=["Text To Speech"])
9
 
10
  @router.post("/")
11
+ async def generate_speech(request: TTSRequest):
12
+ """
13
+ Convert text to speech and return audio file
14
+ """
15
+ try:
16
+ # Générer l'audio
17
+ audio_path = text_to_speech(
18
+ text=request.text,
19
+ voice=request.voice,
20
+ fmt=request.format
21
+ )
22
+
23
+ # Vérifier que le fichier existe
24
+ if not Path(audio_path).exists():
25
+ raise HTTPException(status_code=500, detail="Audio file generation failed")
26
+
27
+ # Déterminer le type MIME
28
+ media_type = "audio/wav" if request.format == "wav" else "audio/mpeg"
29
+
30
+ # Retourner le fichier audio
31
+ return FileResponse(
32
+ path=audio_path,
33
+ filename=f"speech.{request.format}",
34
+ media_type=media_type,
35
+ headers={
36
+ "Content-Disposition": f"attachment; filename=speech.{request.format}"
37
+ }
38
+ )
39
+
40
+ except Exception as e:
41
+ raise HTTPException(status_code=500, detail=str(e))
services/stt_service.py CHANGED
@@ -1,26 +1,40 @@
1
  import requests
2
  from config import GROQ_API_KEY, GROQ_STT_MODEL
 
 
3
 
4
  def speech_to_text(audio_file: str) -> str:
 
 
 
5
  if not GROQ_API_KEY:
6
  raise RuntimeError("GROQ_API_KEY is not set in config")
7
 
8
- url = "https://api.groq.ai/openai/v1/audio/transcriptions"
9
-
10
  headers = {
11
  "Authorization": f"Bearer {GROQ_API_KEY}"
12
  }
13
-
14
- with open(audio_file, "rb") as f:
 
15
  files = {
16
- "file": (audio_file, f, "audio/wav")
17
  }
18
  data = {
19
- "model": GROQ_STT_MODEL
 
 
20
  }
21
-
22
- response = requests.post(url, headers=headers, data=data, files=files)
23
- response.raise_for_status()
24
-
25
- result = response.json()
26
- return result.get("text", "")
 
 
 
 
 
 
 
1
  import requests
2
  from config import GROQ_API_KEY, GROQ_STT_MODEL
3
+ import tempfile
4
+ import os
5
 
6
  def speech_to_text(audio_file: str) -> str:
7
+ """
8
+ Convert audio file to text using Groq's Whisper API
9
+ """
10
  if not GROQ_API_KEY:
11
  raise RuntimeError("GROQ_API_KEY is not set in config")
12
 
13
+ url = "https://api.groq.com/openai/v1/audio/transcriptions"
14
+
15
  headers = {
16
  "Authorization": f"Bearer {GROQ_API_KEY}"
17
  }
18
+
19
+ # Lire le fichier audio
20
+ with open(audio_file, "rb") as audio_data:
21
  files = {
22
+ "file": (os.path.basename(audio_file), audio_data, "audio/wav")
23
  }
24
  data = {
25
+ "model": GROQ_STT_MODEL,
26
+ "temperature": 0,
27
+ "response_format": "json"
28
  }
29
+
30
+ try:
31
+ response = requests.post(url, headers=headers, files=files, data=data)
32
+ response.raise_for_status()
33
+
34
+ result = response.json()
35
+ return result.get("text", "")
36
+
37
+ except requests.exceptions.RequestException as e:
38
+ raise Exception(f"Groq STT API error: {str(e)}")
39
+ except Exception as e:
40
+ raise Exception(f"Unexpected error in speech_to_text: {str(e)}")
services/tts_service.py CHANGED
@@ -1,31 +1,54 @@
1
  import requests
2
  import uuid
 
 
3
  from config import GROQ_API_KEY, GROQ_TTS_MODEL
4
 
5
- def text_to_speech(text: str, voice: str, fmt: str):
 
 
 
 
6
  if not GROQ_API_KEY:
7
  raise RuntimeError("GROQ_API_KEY is not set in config")
8
-
9
- url = "https://api.groq.ai/openai/v1/audio/speech"
10
-
 
 
 
11
  headers = {
12
  "Authorization": f"Bearer {GROQ_API_KEY}",
13
  "Content-Type": "application/json"
14
  }
15
-
16
  payload = {
17
  "model": GROQ_TTS_MODEL,
 
18
  "voice": voice,
19
- "format": fmt,
20
- "input": text
21
  }
22
-
23
- output_file = f"audio_{uuid.uuid4()}.{fmt}"
24
-
25
- response = requests.post(url, headers=headers, json=payload)
26
- response.raise_for_status()
27
-
28
- with open(output_file, "wb") as f:
29
- f.write(response.content)
30
-
31
- return output_file
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import requests
2
  import uuid
3
+ import os
4
+ from pathlib import Path
5
  from config import GROQ_API_KEY, GROQ_TTS_MODEL
6
 
7
+ def text_to_speech(text: str, voice: str = "Aaliyah-PlayAI", fmt: str = "wav") -> str:
8
+ """
9
+ Convert text to speech using Groq's TTS API
10
+ Returns the path to the generated audio file
11
+ """
12
  if not GROQ_API_KEY:
13
  raise RuntimeError("GROQ_API_KEY is not set in config")
14
+
15
+ if not text or not text.strip():
16
+ raise ValueError("Text cannot be empty")
17
+
18
+ url = "https://api.groq.com/openai/v1/audio/speech"
19
+
20
  headers = {
21
  "Authorization": f"Bearer {GROQ_API_KEY}",
22
  "Content-Type": "application/json"
23
  }
24
+
25
  payload = {
26
  "model": GROQ_TTS_MODEL,
27
+ "input": text.strip(),
28
  "voice": voice,
29
+ "response_format": fmt
 
30
  }
31
+
32
+ try:
33
+ # Créer un répertoire temporaire pour les fichiers audio
34
+ temp_dir = Path("temp_audio")
35
+ temp_dir.mkdir(exist_ok=True)
36
+
37
+ # Nom de fichier unique
38
+ output_filename = f"tts_{uuid.uuid4().hex[:8]}.{fmt}"
39
+ output_path = temp_dir / output_filename
40
+
41
+ # Appel API Groq
42
+ response = requests.post(url, headers=headers, json=payload, timeout=30)
43
+ response.raise_for_status()
44
+
45
+ # Sauvegarder le fichier audio
46
+ with open(output_path, "wb") as f:
47
+ f.write(response.content)
48
+
49
+ return str(output_path)
50
+
51
+ except requests.exceptions.RequestException as e:
52
+ raise Exception(f"Groq TTS API error: {str(e)}")
53
+ except Exception as e:
54
+ raise Exception(f"Unexpected error in text_to_speech: {str(e)}")