Первоначальная версия VidConf
This commit is contained in:
70
backend/services/pipeline_producer.py
Normal file
70
backend/services/pipeline_producer.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""Постановка задачи `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
|
||||
)
|
||||
Reference in New Issue
Block a user