"""Постановка задачи `run_pipeline` в очередь Celery `transcription`. Отправляет задачу по имени через голый Celery-клиент (`broker_url` без `backend`) — backend не импортирует пакет `workers` (инвариант разделения слоёв: API-процесс не должен тянуть зависимости воркеров, в т.ч. `faster-whisper` через транзитивный импорт `workers.tasks.pipeline`). Задача регистрируется и обрабатывается в `workers/tasks/pipeline.py` (блок C). """ import uuid from celery import Celery from kombu.exceptions import OperationalError from core.config import get_settings RUN_PIPELINE_TASK_NAME = "workers.tasks.pipeline.run_pipeline" TRANSCRIPTION_QUEUE = "transcription" def enqueue_pipeline(session_id: uuid.UUID) -> None: """Поставить в очередь `transcription` запуск AI-пайплайна для сеанса `session_id`. Идемпотентно на стороне задачи (`run_pipeline` продолжает с последнего успешного шага) — повторная постановка (например, из `_on_room_finished` при повторной обработке) безопасна. """ settings = get_settings() client = Celery("vidconf-producer", broker=settings.redis_url) client.send_task(RUN_PIPELINE_TASK_NAME, args=[str(session_id)], queue=TRANSCRIPTION_QUEUE) def transcription_queue_served(timeout: float = 1.0) -> bool: """Проверить, обслуживается ли очередь `transcription` хотя бы одним воркером Celery. Детект доступности уровня AI (`services.ai_levels.detect_ai_levels`) смотрит только на железо и файлы моделей на диске, но не видит, запущен ли вообще воркер транскрибации, — админка (`GET /admin/settings`) использует эту функцию, чтобы предупредить «AI включён, но обработка недоступна». `app.control.inspect(timeout=...).active_queues()` — блокирующий вызов (ждёт ответа брокера/воркеров); возвращает `{hostname: [{"name": ...}, ...]}` для ответивших воркеров либо `None`, если за `timeout` не ответил НИ ОДИН (нет запущенных воркеров либо брокер Redis недоступен) — оба случая здесь трактуются как «очередь не обслуживается». Вызывающая сторона (API-хендлер) должна оборачивать в `anyio.to_thread.run_sync`, чтобы не блокировать event loop. Полностью недоступный брокер (Redis лежит/не резолвится) — отдельный случай: `kombu`/`redis-py` не возвращают `None`, а бросают исключение, которое Celery оборачивает в `kombu.exceptions.OperationalError` («Recoverable message transport connection error» — проверено эмпирически: недоступный/несуществующий хост даёт именно этот тип). По контракту «нет воркеров ИЛИ брокер недоступен → `False`» это тоже трактуется как «очередь не обслуживается», а не пробрасывается 500-кой наружу в `GET`/`PUT /admin/settings`. """ settings = get_settings() client = Celery("vidconf-producer", broker=settings.redis_url) try: active_queues = client.control.inspect(timeout=timeout).active_queues() except OperationalError: return False if not active_queues: return False return any( queue.get("name") == TRANSCRIPTION_QUEUE for queues in active_queues.values() for queue in queues )