""" API-роуты. GET /api/profiles — список доступных LLM-профилей. POST /api/speech/ — принять аудио, распознать, отправить в LLM, вернуть ответ. """ import logging from flask import Blueprint, request, jsonify from services.whisper import whisper_service from services.llm import llm_service log = logging.getLogger(__name__) api_bp = Blueprint("api", __name__, url_prefix="/api") @api_bp.route("/profiles") def get_profiles(): """Возвращает список LLM-профилей для отображения на фронтенде.""" return jsonify(llm_service.profile_list) @api_bp.route("/speech/", methods=["POST"]) def speech_to_llm(llm_id: str): """ Основной рабочий процесс: 1. Принимает аудиофайл из формы (поле "audio"). 2. Отправляет в Whisper → получает распознанный текст. 3. Отправляет текст в выбранный LLM-профиль → получает ответ. 4. Возвращает JSON: { transcription, response, llm }. Args: llm_id: Идентификатор LLM-профиля (ddm, qwen, deepseek, ...). """ # --- Валидация входных данных --- if "audio" not in request.files: return jsonify({"error": "Поле 'audio' обязательно"}), 400 audio_file = request.files["audio"] audio_bytes = audio_file.read() mime_type = audio_file.mimetype or "audio/webm" if len(audio_bytes) == 0: return jsonify({"error": "Аудиофайл пуст"}), 400 # --- Проверка существования LLM-профиля --- profile = llm_service.get_profile(llm_id) if not profile: return jsonify({ "error": f"Неизвестный LLM: '{llm_id}'", "available": [p["id"] for p in llm_service.profile_list], }), 400 try: # Шаг 1: Whisper — распознавание речи log.info("API /speech/%s: получено %d байт аудио", llm_id, len(audio_bytes)) transcription = whisper_service.transcribe(audio_bytes, mime_type) if not transcription: return jsonify({ "error": "Не удалось распознать речь", "transcription": "", }), 400 # Шаг 2: LLM — отправка распознанного текста llm_answer = llm_service.chat(llm_id, transcription) # Шаг 3: Ответ return jsonify({ "transcription": transcription, "response": llm_answer, "llm": { "id": profile["id"], "name": profile["name"], }, }) except ValueError as e: log.warning("API /speech/%s: bad request — %s", llm_id, e) return jsonify({"error": str(e)}), 400 except RuntimeError as e: log.error("API /speech/%s: runtime error — %s", llm_id, e) return jsonify({"error": str(e)}), 500 except Exception as e: log.exception("API /speech/%s: unexpected error", llm_id) return jsonify({"error": f"Внутренняя ошибка: {e}"}), 500