Files
LLM-infa/api/route/pipeline.py
jze9 e87f6cd215 Фоновые задачи: плейлисты, длинные лекции, вкладка настроек
- таблица jobs + очередь с воркером: POST /jobs сразу отвечает, элементы
  плейлиста обрабатываются последовательно, есть отмена и живой прогресс
  (скачивание %, минуты распознанного Vosk-аудио)
- expand_url: плейлист раскрывается в список видео
- keep_video=false теперь качает только аудио (для 4-5ч лекций)
- настройки (LLM-ключ, модель, длина выжимки, хранение видео, путь Vosk)
  хранятся в Postgres: переживают перезапуск и перезагрузку страницы
- длина выжимки прокидывается в LLM-промпт ({n} предложений)
- UI: вкладки «Главная»/«Настройки», карточка задач с автообновлением
  каждые 3 сек, прогресс-бар, отмена, библиотека обновляется по мере
  готовности элементов

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:01:11 +05:00

81 lines
2.5 KiB
Python

"""Синхронный запуск пайплайна для одного видео (для длинных видео и
плейлистов используйте /jobs — этот эндпоинт держит HTTP-соединение открытым).
"""
from __future__ import annotations
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from api.lib.llm import LLMConfig
from api.lib.processing import PipelineError, process_single_video
router = APIRouter(prefix="/pipeline", tags=["pipeline"])
class LLMOverride(BaseModel):
base_url: str
api_key: str = ""
model: str
timeout: int = 120
max_tokens: int = 1000
temperature: float = 0.3
extra_headers: dict[str, str] = {}
system_prompt: str | None = None
user_template: str | None = None
class ProcessRequest(BaseModel):
url: str
model_path: str | None = None # None → из настроек
summary_max_sentences: int | None = None
keep_video: bool | None = None
section_id: int | None = None
# Если задано — используется этот LLM; иначе сохранённые настройки/env;
# иначе встроенная экстрактивная выжимка.
llm: LLMOverride | None = None
class ProcessResponse(BaseModel):
uuid: str
title: str
source_url: str
video_path: str
text_full_path: str
text_summary_path: str
transcription_method: str
summary_preview: str
@router.post("/process", response_model=ProcessResponse)
async def process_video(req: ProcessRequest):
llm_cfg = None
sys_p = user_t = None
if req.llm is not None:
llm_cfg = LLMConfig(
base_url=req.llm.base_url,
api_key=req.llm.api_key,
model=req.llm.model,
timeout=req.llm.timeout,
max_tokens=req.llm.max_tokens,
temperature=req.llm.temperature,
extra_headers=req.llm.extra_headers,
)
sys_p = req.llm.system_prompt
user_t = req.llm.user_template
try:
result = await process_single_video(
req.url,
model_path=req.model_path,
summary_max_sentences=req.summary_max_sentences,
keep_video=req.keep_video,
section_id=req.section_id,
llm_cfg=llm_cfg,
system_prompt=sys_p,
user_template=user_t,
)
except PipelineError as e:
raise HTTPException(status_code=e.status, detail=str(e))
return ProcessResponse(**result)