malek-messaoudii
commited on
Commit
·
45e145b
1
Parent(s):
997d57e
Add response models for new MCP tools: DetectStance, MatchKeypoint, TranscribeAudio, GenerateSpeech, and GenerateArgument
Browse files- models/__init__.py +10 -0
- models/mcp_models.py +77 -1
- routes/mcp_routes.py +143 -62
models/__init__.py
CHANGED
|
@@ -30,6 +30,11 @@ from .mcp_models import (
|
|
| 30 |
ToolListResponse,
|
| 31 |
ResourceInfo,
|
| 32 |
ResourceListResponse,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
)
|
| 34 |
|
| 35 |
__all__ = [
|
|
@@ -53,4 +58,9 @@ __all__ = [
|
|
| 53 |
"ToolListResponse",
|
| 54 |
"ResourceInfo",
|
| 55 |
"ResourceListResponse",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
]
|
|
|
|
| 30 |
ToolListResponse,
|
| 31 |
ResourceInfo,
|
| 32 |
ResourceListResponse,
|
| 33 |
+
DetectStanceResponse,
|
| 34 |
+
MatchKeypointResponse,
|
| 35 |
+
TranscribeAudioResponse,
|
| 36 |
+
GenerateSpeechResponse,
|
| 37 |
+
GenerateArgumentResponse,
|
| 38 |
)
|
| 39 |
|
| 40 |
__all__ = [
|
|
|
|
| 58 |
"ToolListResponse",
|
| 59 |
"ResourceInfo",
|
| 60 |
"ResourceListResponse",
|
| 61 |
+
"DetectStanceResponse",
|
| 62 |
+
"MatchKeypointResponse",
|
| 63 |
+
"TranscribeAudioResponse",
|
| 64 |
+
"GenerateSpeechResponse",
|
| 65 |
+
"GenerateArgumentResponse",
|
| 66 |
]
|
models/mcp_models.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from pydantic import BaseModel
|
| 2 |
from typing import Any, Dict, List, Optional
|
| 3 |
|
| 4 |
class ToolCallRequest(BaseModel):
|
|
@@ -13,6 +13,82 @@ class ToolCallResponse(BaseModel):
|
|
| 13 |
error: Optional[str] = None
|
| 14 |
tool_name: str
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
class ResourceInfo(BaseModel):
|
| 17 |
"""Information about an MCP resource"""
|
| 18 |
uri: str
|
|
|
|
| 1 |
+
from pydantic import BaseModel, Field, ConfigDict
|
| 2 |
from typing import Any, Dict, List, Optional
|
| 3 |
|
| 4 |
class ToolCallRequest(BaseModel):
|
|
|
|
| 13 |
error: Optional[str] = None
|
| 14 |
tool_name: str
|
| 15 |
|
| 16 |
+
# Response models for individual MCP tools
|
| 17 |
+
class DetectStanceResponse(BaseModel):
|
| 18 |
+
"""Response model for stance detection"""
|
| 19 |
+
model_config = ConfigDict(
|
| 20 |
+
json_schema_extra={
|
| 21 |
+
"example": {
|
| 22 |
+
"predicted_stance": "PRO",
|
| 23 |
+
"confidence": 0.9598,
|
| 24 |
+
"probability_con": 0.0402,
|
| 25 |
+
"probability_pro": 0.9598
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
predicted_stance: str = Field(..., description="PRO or CON")
|
| 31 |
+
confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence score")
|
| 32 |
+
probability_con: float = Field(..., ge=0.0, le=1.0)
|
| 33 |
+
probability_pro: float = Field(..., ge=0.0, le=1.0)
|
| 34 |
+
|
| 35 |
+
class MatchKeypointResponse(BaseModel):
|
| 36 |
+
"""Response model for keypoint matching"""
|
| 37 |
+
model_config = ConfigDict(
|
| 38 |
+
json_schema_extra={
|
| 39 |
+
"example": {
|
| 40 |
+
"prediction": 1,
|
| 41 |
+
"label": "apparie",
|
| 42 |
+
"confidence": 0.8157,
|
| 43 |
+
"probabilities": {
|
| 44 |
+
"non_apparie": 0.1843,
|
| 45 |
+
"apparie": 0.8157
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
prediction: int = Field(..., description="1 = apparie, 0 = non_apparie")
|
| 52 |
+
label: str = Field(..., description="apparie or non_apparie")
|
| 53 |
+
confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence score")
|
| 54 |
+
probabilities: Dict[str, float] = Field(..., description="Dictionary of class probabilities")
|
| 55 |
+
|
| 56 |
+
class TranscribeAudioResponse(BaseModel):
|
| 57 |
+
"""Response model for audio transcription"""
|
| 58 |
+
model_config = ConfigDict(
|
| 59 |
+
json_schema_extra={
|
| 60 |
+
"example": {
|
| 61 |
+
"text": "Hello, this is the transcribed text from the audio file."
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
text: str = Field(..., description="Transcribed text from audio")
|
| 67 |
+
|
| 68 |
+
class GenerateSpeechResponse(BaseModel):
|
| 69 |
+
"""Response model for speech generation"""
|
| 70 |
+
model_config = ConfigDict(
|
| 71 |
+
json_schema_extra={
|
| 72 |
+
"example": {
|
| 73 |
+
"audio_path": "temp_audio/tts_e9b78164.wav"
|
| 74 |
+
}
|
| 75 |
+
}
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
audio_path: str = Field(..., description="Path to generated audio file")
|
| 79 |
+
|
| 80 |
+
class GenerateArgumentResponse(BaseModel):
|
| 81 |
+
"""Response model for argument generation"""
|
| 82 |
+
model_config = ConfigDict(
|
| 83 |
+
json_schema_extra={
|
| 84 |
+
"example": {
|
| 85 |
+
"argument": "Climate change is a pressing issue that requires immediate action..."
|
| 86 |
+
}
|
| 87 |
+
}
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
argument: str = Field(..., description="Generated debate argument")
|
| 91 |
+
|
| 92 |
class ResourceInfo(BaseModel):
|
| 93 |
"""Information about an MCP resource"""
|
| 94 |
uri: str
|
routes/mcp_routes.py
CHANGED
|
@@ -7,7 +7,17 @@ import logging
|
|
| 7 |
import json
|
| 8 |
|
| 9 |
from services.mcp_service import mcp_server
|
| 10 |
-
from models.mcp_models import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
router = APIRouter(prefix="/api/v1/mcp", tags=["MCP"])
|
| 13 |
logger = logging.getLogger(__name__)
|
|
@@ -234,8 +244,8 @@ async def call_mcp_tool(request: ToolCallRequest):
|
|
| 234 |
|
| 235 |
# ===== Routes individuelles pour chaque outil (pour Swagger) =====
|
| 236 |
|
| 237 |
-
@router.post("/tools/detect-stance", summary="Détecter la stance d'un argument")
|
| 238 |
-
async def mcp_detect_stance(request: DetectStanceRequest)
|
| 239 |
"""Détecte si un argument est PRO ou CON pour un topic donné"""
|
| 240 |
try:
|
| 241 |
# Appeler directement via call_tool (async)
|
|
@@ -243,95 +253,144 @@ async def mcp_detect_stance(request: DetectStanceRequest) -> Dict[str, Any]:
|
|
| 243 |
"topic": request.topic,
|
| 244 |
"argument": request.argument
|
| 245 |
})
|
| 246 |
-
|
|
|
|
|
|
|
| 247 |
if isinstance(result, dict):
|
| 248 |
# Si le résultat contient une clé "result" avec une liste de ContentBlock
|
| 249 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 250 |
content_block = result["result"][0]
|
| 251 |
if hasattr(content_block, 'text') and content_block.text:
|
| 252 |
try:
|
| 253 |
-
|
| 254 |
except json.JSONDecodeError:
|
| 255 |
-
|
| 256 |
-
|
|
|
|
| 257 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
| 258 |
-
# Si c'est une liste de ContentBlock, extraire le contenu
|
| 259 |
if hasattr(result[0], 'text') and result[0].text:
|
| 260 |
try:
|
| 261 |
-
|
| 262 |
except json.JSONDecodeError:
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
except Exception as e:
|
| 267 |
logger.error(f"Error in detect_stance: {e}")
|
| 268 |
raise HTTPException(status_code=500, detail=f"Error executing tool detect_stance: {e}")
|
| 269 |
|
| 270 |
-
@router.post("/tools/match-keypoint", summary="Matcher un argument avec un keypoint")
|
| 271 |
-
async def mcp_match_keypoint(request: MatchKeypointRequest)
|
| 272 |
"""Détermine si un argument correspond à un keypoint"""
|
| 273 |
try:
|
| 274 |
result = await mcp_server.call_tool("match_keypoint_argument", {
|
| 275 |
"argument": request.argument,
|
| 276 |
"key_point": request.key_point
|
| 277 |
})
|
| 278 |
-
|
|
|
|
|
|
|
| 279 |
if isinstance(result, dict):
|
| 280 |
-
# Si le résultat contient une clé "result" avec une liste de ContentBlock
|
| 281 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 282 |
content_block = result["result"][0]
|
| 283 |
if hasattr(content_block, 'text') and content_block.text:
|
| 284 |
try:
|
| 285 |
-
|
| 286 |
except json.JSONDecodeError:
|
| 287 |
-
|
| 288 |
-
|
|
|
|
| 289 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
| 290 |
if hasattr(result[0], 'text') and result[0].text:
|
| 291 |
try:
|
| 292 |
-
|
| 293 |
except json.JSONDecodeError:
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
except Exception as e:
|
| 298 |
logger.error(f"Error in match_keypoint_argument: {e}")
|
| 299 |
raise HTTPException(status_code=500, detail=f"Error executing tool match_keypoint_argument: {e}")
|
| 300 |
|
| 301 |
-
@router.post("/tools/transcribe-audio", summary="Transcrire un audio en texte")
|
| 302 |
-
async def mcp_transcribe_audio(request: TranscribeAudioRequest)
|
| 303 |
"""Convertit un fichier audio en texte"""
|
| 304 |
try:
|
| 305 |
result = await mcp_server.call_tool("transcribe_audio", {
|
| 306 |
"audio_path": request.audio_path
|
| 307 |
})
|
| 308 |
-
|
|
|
|
|
|
|
| 309 |
if isinstance(result, dict):
|
| 310 |
-
# Si le résultat contient une clé "result" avec une liste de ContentBlock
|
| 311 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 312 |
content_block = result["result"][0]
|
| 313 |
if hasattr(content_block, 'text'):
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
return {"text": result["text"]}
|
| 318 |
-
return {"text": str(result)}
|
| 319 |
elif isinstance(result, str):
|
| 320 |
-
|
| 321 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
| 322 |
if hasattr(result[0], 'text'):
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
except FileNotFoundError as e:
|
| 327 |
logger.error(f"File not found in transcribe_audio: {e}")
|
| 328 |
raise HTTPException(status_code=500, detail=f"Error executing tool transcribe_audio: {e}")
|
|
|
|
|
|
|
| 329 |
except Exception as e:
|
| 330 |
logger.error(f"Error in transcribe_audio: {e}")
|
| 331 |
raise HTTPException(status_code=500, detail=f"Error executing tool transcribe_audio: {e}")
|
| 332 |
|
| 333 |
-
@router.post("/tools/generate-speech", summary="Générer de la parole à partir de texte")
|
| 334 |
-
async def mcp_generate_speech(request: GenerateSpeechRequest)
|
| 335 |
"""Convertit du texte en fichier audio"""
|
| 336 |
try:
|
| 337 |
result = await mcp_server.call_tool("generate_speech", {
|
|
@@ -339,54 +398,76 @@ async def mcp_generate_speech(request: GenerateSpeechRequest) -> Dict[str, str]:
|
|
| 339 |
"voice": request.voice,
|
| 340 |
"format": request.format
|
| 341 |
})
|
| 342 |
-
|
|
|
|
|
|
|
| 343 |
if isinstance(result, dict):
|
| 344 |
-
# Si le résultat contient une clé "result" avec une liste de ContentBlock
|
| 345 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 346 |
content_block = result["result"][0]
|
| 347 |
if hasattr(content_block, 'text'):
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
return {"audio_path": result["audio_path"]}
|
| 352 |
-
return {"audio_path": str(result)}
|
| 353 |
elif isinstance(result, str):
|
| 354 |
-
|
| 355 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
| 356 |
if hasattr(result[0], 'text'):
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 360 |
except Exception as e:
|
| 361 |
logger.error(f"Error in generate_speech: {e}")
|
| 362 |
raise HTTPException(status_code=500, detail=f"Error executing tool generate_speech: {e}")
|
| 363 |
|
| 364 |
-
@router.post("/tools/generate-argument", summary="Générer un argument de débat")
|
| 365 |
-
async def mcp_generate_argument(request: GenerateArgumentRequest)
|
| 366 |
"""Génère un argument de débat à partir d'un input utilisateur"""
|
| 367 |
try:
|
| 368 |
result = await mcp_server.call_tool("generate_argument", {
|
| 369 |
"user_input": request.user_input,
|
| 370 |
"conversation_id": request.conversation_id
|
| 371 |
})
|
| 372 |
-
|
|
|
|
|
|
|
| 373 |
if isinstance(result, dict):
|
| 374 |
-
# Si le résultat contient une clé "result" avec une liste de ContentBlock
|
| 375 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 376 |
content_block = result["result"][0]
|
| 377 |
if hasattr(content_block, 'text'):
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
return {"argument": result["argument"]}
|
| 382 |
-
return {"argument": str(result)}
|
| 383 |
elif isinstance(result, str):
|
| 384 |
-
|
| 385 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
| 386 |
if hasattr(result[0], 'text'):
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
except Exception as e:
|
| 391 |
logger.error(f"Error in generate_argument: {e}")
|
| 392 |
raise HTTPException(status_code=500, detail=f"Error executing tool generate_argument: {e}")
|
|
|
|
| 7 |
import json
|
| 8 |
|
| 9 |
from services.mcp_service import mcp_server
|
| 10 |
+
from models.mcp_models import (
|
| 11 |
+
ToolListResponse,
|
| 12 |
+
ToolInfo,
|
| 13 |
+
ToolCallRequest,
|
| 14 |
+
ToolCallResponse,
|
| 15 |
+
DetectStanceResponse,
|
| 16 |
+
MatchKeypointResponse,
|
| 17 |
+
TranscribeAudioResponse,
|
| 18 |
+
GenerateSpeechResponse,
|
| 19 |
+
GenerateArgumentResponse
|
| 20 |
+
)
|
| 21 |
|
| 22 |
router = APIRouter(prefix="/api/v1/mcp", tags=["MCP"])
|
| 23 |
logger = logging.getLogger(__name__)
|
|
|
|
| 244 |
|
| 245 |
# ===== Routes individuelles pour chaque outil (pour Swagger) =====
|
| 246 |
|
| 247 |
+
@router.post("/tools/detect-stance", response_model=DetectStanceResponse, summary="Détecter la stance d'un argument")
|
| 248 |
+
async def mcp_detect_stance(request: DetectStanceRequest):
|
| 249 |
"""Détecte si un argument est PRO ou CON pour un topic donné"""
|
| 250 |
try:
|
| 251 |
# Appeler directement via call_tool (async)
|
|
|
|
| 253 |
"topic": request.topic,
|
| 254 |
"argument": request.argument
|
| 255 |
})
|
| 256 |
+
|
| 257 |
+
# Extraire les données du résultat MCP
|
| 258 |
+
parsed_result = None
|
| 259 |
if isinstance(result, dict):
|
| 260 |
# Si le résultat contient une clé "result" avec une liste de ContentBlock
|
| 261 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 262 |
content_block = result["result"][0]
|
| 263 |
if hasattr(content_block, 'text') and content_block.text:
|
| 264 |
try:
|
| 265 |
+
parsed_result = json.loads(content_block.text)
|
| 266 |
except json.JSONDecodeError:
|
| 267 |
+
raise HTTPException(status_code=500, detail="Invalid JSON response from MCP tool")
|
| 268 |
+
else:
|
| 269 |
+
parsed_result = result
|
| 270 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
|
|
|
| 271 |
if hasattr(result[0], 'text') and result[0].text:
|
| 272 |
try:
|
| 273 |
+
parsed_result = json.loads(result[0].text)
|
| 274 |
except json.JSONDecodeError:
|
| 275 |
+
raise HTTPException(status_code=500, detail="Invalid JSON response from MCP tool")
|
| 276 |
+
else:
|
| 277 |
+
parsed_result = result
|
| 278 |
+
|
| 279 |
+
if not parsed_result:
|
| 280 |
+
raise HTTPException(status_code=500, detail="Empty response from MCP tool")
|
| 281 |
+
|
| 282 |
+
# Construire la réponse structurée
|
| 283 |
+
response = DetectStanceResponse(
|
| 284 |
+
predicted_stance=parsed_result["predicted_stance"],
|
| 285 |
+
confidence=parsed_result["confidence"],
|
| 286 |
+
probability_con=parsed_result["probability_con"],
|
| 287 |
+
probability_pro=parsed_result["probability_pro"]
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
logger.info(f"Stance prediction: {response.predicted_stance} (conf={response.confidence:.4f})")
|
| 291 |
+
return response
|
| 292 |
+
|
| 293 |
+
except HTTPException:
|
| 294 |
+
raise
|
| 295 |
except Exception as e:
|
| 296 |
logger.error(f"Error in detect_stance: {e}")
|
| 297 |
raise HTTPException(status_code=500, detail=f"Error executing tool detect_stance: {e}")
|
| 298 |
|
| 299 |
+
@router.post("/tools/match-keypoint", response_model=MatchKeypointResponse, summary="Matcher un argument avec un keypoint")
|
| 300 |
+
async def mcp_match_keypoint(request: MatchKeypointRequest):
|
| 301 |
"""Détermine si un argument correspond à un keypoint"""
|
| 302 |
try:
|
| 303 |
result = await mcp_server.call_tool("match_keypoint_argument", {
|
| 304 |
"argument": request.argument,
|
| 305 |
"key_point": request.key_point
|
| 306 |
})
|
| 307 |
+
|
| 308 |
+
# Extraire les données du résultat MCP
|
| 309 |
+
parsed_result = None
|
| 310 |
if isinstance(result, dict):
|
|
|
|
| 311 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 312 |
content_block = result["result"][0]
|
| 313 |
if hasattr(content_block, 'text') and content_block.text:
|
| 314 |
try:
|
| 315 |
+
parsed_result = json.loads(content_block.text)
|
| 316 |
except json.JSONDecodeError:
|
| 317 |
+
raise HTTPException(status_code=500, detail="Invalid JSON response from MCP tool")
|
| 318 |
+
else:
|
| 319 |
+
parsed_result = result
|
| 320 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
| 321 |
if hasattr(result[0], 'text') and result[0].text:
|
| 322 |
try:
|
| 323 |
+
parsed_result = json.loads(result[0].text)
|
| 324 |
except json.JSONDecodeError:
|
| 325 |
+
raise HTTPException(status_code=500, detail="Invalid JSON response from MCP tool")
|
| 326 |
+
else:
|
| 327 |
+
parsed_result = result
|
| 328 |
+
|
| 329 |
+
if not parsed_result:
|
| 330 |
+
raise HTTPException(status_code=500, detail="Empty response from MCP tool")
|
| 331 |
+
|
| 332 |
+
# Construire la réponse structurée
|
| 333 |
+
response = MatchKeypointResponse(
|
| 334 |
+
prediction=parsed_result["prediction"],
|
| 335 |
+
label=parsed_result["label"],
|
| 336 |
+
confidence=parsed_result["confidence"],
|
| 337 |
+
probabilities=parsed_result["probabilities"]
|
| 338 |
+
)
|
| 339 |
+
|
| 340 |
+
logger.info(f"Keypoint matching: {response.label} (conf={response.confidence:.4f})")
|
| 341 |
+
return response
|
| 342 |
+
|
| 343 |
+
except HTTPException:
|
| 344 |
+
raise
|
| 345 |
except Exception as e:
|
| 346 |
logger.error(f"Error in match_keypoint_argument: {e}")
|
| 347 |
raise HTTPException(status_code=500, detail=f"Error executing tool match_keypoint_argument: {e}")
|
| 348 |
|
| 349 |
+
@router.post("/tools/transcribe-audio", response_model=TranscribeAudioResponse, summary="Transcrire un audio en texte")
|
| 350 |
+
async def mcp_transcribe_audio(request: TranscribeAudioRequest):
|
| 351 |
"""Convertit un fichier audio en texte"""
|
| 352 |
try:
|
| 353 |
result = await mcp_server.call_tool("transcribe_audio", {
|
| 354 |
"audio_path": request.audio_path
|
| 355 |
})
|
| 356 |
+
|
| 357 |
+
# Extraire le texte du résultat MCP
|
| 358 |
+
transcribed_text = None
|
| 359 |
if isinstance(result, dict):
|
|
|
|
| 360 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 361 |
content_block = result["result"][0]
|
| 362 |
if hasattr(content_block, 'text'):
|
| 363 |
+
transcribed_text = content_block.text
|
| 364 |
+
elif "text" in result:
|
| 365 |
+
transcribed_text = result["text"]
|
|
|
|
|
|
|
| 366 |
elif isinstance(result, str):
|
| 367 |
+
transcribed_text = result
|
| 368 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
| 369 |
if hasattr(result[0], 'text'):
|
| 370 |
+
transcribed_text = result[0].text
|
| 371 |
+
else:
|
| 372 |
+
transcribed_text = str(result[0])
|
| 373 |
+
else:
|
| 374 |
+
transcribed_text = str(result)
|
| 375 |
+
|
| 376 |
+
if not transcribed_text:
|
| 377 |
+
raise HTTPException(status_code=500, detail="Empty transcription result from MCP tool")
|
| 378 |
+
|
| 379 |
+
response = TranscribeAudioResponse(text=transcribed_text)
|
| 380 |
+
logger.info(f"Audio transcribed: {len(transcribed_text)} characters")
|
| 381 |
+
return response
|
| 382 |
+
|
| 383 |
except FileNotFoundError as e:
|
| 384 |
logger.error(f"File not found in transcribe_audio: {e}")
|
| 385 |
raise HTTPException(status_code=500, detail=f"Error executing tool transcribe_audio: {e}")
|
| 386 |
+
except HTTPException:
|
| 387 |
+
raise
|
| 388 |
except Exception as e:
|
| 389 |
logger.error(f"Error in transcribe_audio: {e}")
|
| 390 |
raise HTTPException(status_code=500, detail=f"Error executing tool transcribe_audio: {e}")
|
| 391 |
|
| 392 |
+
@router.post("/tools/generate-speech", response_model=GenerateSpeechResponse, summary="Générer de la parole à partir de texte")
|
| 393 |
+
async def mcp_generate_speech(request: GenerateSpeechRequest):
|
| 394 |
"""Convertit du texte en fichier audio"""
|
| 395 |
try:
|
| 396 |
result = await mcp_server.call_tool("generate_speech", {
|
|
|
|
| 398 |
"voice": request.voice,
|
| 399 |
"format": request.format
|
| 400 |
})
|
| 401 |
+
|
| 402 |
+
# Extraire le chemin audio du résultat MCP
|
| 403 |
+
audio_path = None
|
| 404 |
if isinstance(result, dict):
|
|
|
|
| 405 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 406 |
content_block = result["result"][0]
|
| 407 |
if hasattr(content_block, 'text'):
|
| 408 |
+
audio_path = content_block.text
|
| 409 |
+
elif "audio_path" in result:
|
| 410 |
+
audio_path = result["audio_path"]
|
|
|
|
|
|
|
| 411 |
elif isinstance(result, str):
|
| 412 |
+
audio_path = result
|
| 413 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
| 414 |
if hasattr(result[0], 'text'):
|
| 415 |
+
audio_path = result[0].text
|
| 416 |
+
else:
|
| 417 |
+
audio_path = str(result[0])
|
| 418 |
+
else:
|
| 419 |
+
audio_path = str(result)
|
| 420 |
+
|
| 421 |
+
if not audio_path:
|
| 422 |
+
raise HTTPException(status_code=500, detail="Empty audio path from MCP tool")
|
| 423 |
+
|
| 424 |
+
response = GenerateSpeechResponse(audio_path=audio_path)
|
| 425 |
+
logger.info(f"Speech generated: {audio_path}")
|
| 426 |
+
return response
|
| 427 |
+
|
| 428 |
+
except HTTPException:
|
| 429 |
+
raise
|
| 430 |
except Exception as e:
|
| 431 |
logger.error(f"Error in generate_speech: {e}")
|
| 432 |
raise HTTPException(status_code=500, detail=f"Error executing tool generate_speech: {e}")
|
| 433 |
|
| 434 |
+
@router.post("/tools/generate-argument", response_model=GenerateArgumentResponse, summary="Générer un argument de débat")
|
| 435 |
+
async def mcp_generate_argument(request: GenerateArgumentRequest):
|
| 436 |
"""Génère un argument de débat à partir d'un input utilisateur"""
|
| 437 |
try:
|
| 438 |
result = await mcp_server.call_tool("generate_argument", {
|
| 439 |
"user_input": request.user_input,
|
| 440 |
"conversation_id": request.conversation_id
|
| 441 |
})
|
| 442 |
+
|
| 443 |
+
# Extraire l'argument du résultat MCP
|
| 444 |
+
generated_argument = None
|
| 445 |
if isinstance(result, dict):
|
|
|
|
| 446 |
if "result" in result and isinstance(result["result"], list) and len(result["result"]) > 0:
|
| 447 |
content_block = result["result"][0]
|
| 448 |
if hasattr(content_block, 'text'):
|
| 449 |
+
generated_argument = content_block.text
|
| 450 |
+
elif "argument" in result:
|
| 451 |
+
generated_argument = result["argument"]
|
|
|
|
|
|
|
| 452 |
elif isinstance(result, str):
|
| 453 |
+
generated_argument = result
|
| 454 |
elif isinstance(result, (list, tuple)) and len(result) > 0:
|
| 455 |
if hasattr(result[0], 'text'):
|
| 456 |
+
generated_argument = result[0].text
|
| 457 |
+
else:
|
| 458 |
+
generated_argument = str(result[0])
|
| 459 |
+
else:
|
| 460 |
+
generated_argument = str(result)
|
| 461 |
+
|
| 462 |
+
if not generated_argument:
|
| 463 |
+
raise HTTPException(status_code=500, detail="Empty argument from MCP tool")
|
| 464 |
+
|
| 465 |
+
response = GenerateArgumentResponse(argument=generated_argument)
|
| 466 |
+
logger.info(f"Argument generated: {len(generated_argument)} characters")
|
| 467 |
+
return response
|
| 468 |
+
|
| 469 |
+
except HTTPException:
|
| 470 |
+
raise
|
| 471 |
except Exception as e:
|
| 472 |
logger.error(f"Error in generate_argument: {e}")
|
| 473 |
raise HTTPException(status_code=500, detail=f"Error executing tool generate_argument: {e}")
|