Files
LLM-infa/api/route/llm.py
jze9 2697e01714 Рабочий пайплайн: YouTube -> транскрипция -> выжимка -> Postgres
- новый api/: /pipeline/process (yt-dlp, субтитры или Vosk, LLM/экстрактивная
  суммаризация), /videos, /llm; старый код перенесён в api_legacy/
- web/: Flet UI (flet 0.28.3 + flet-web, порт 8550)
- Dockerfile.api: ffmpeg слоем из mwader/static-ffmpeg, pip с кэш-маунтом
- requirements/pyproject: убраны moviepy, imageio-ffmpeg, битый asyncio
- README с инструкцией запуска и загрузкой Vosk-модели

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

96 lines
2.8 KiB
Python

"""Endpoints for inspecting and testing the external LLM hookup.
The LLM is not hosted here — the user supplies a base URL + model + key.
"""
from __future__ import annotations
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from api.lib.llm import (
LLMConfig,
LLMError,
chat_complete,
from_settings,
list_models,
ping,
)
router = APIRouter(prefix="/llm", tags=["llm"])
class LLMConfigIn(BaseModel):
base_url: str
api_key: str = ""
# model is optional here so /llm/models can be called before a model is picked
model: str = ""
timeout: int = 120
max_tokens: int = 1000
temperature: float = 0.3
extra_headers: dict[str, str] = {}
class LLMStatus(BaseModel):
configured: bool
base_url: str | None = None
model: str | None = None
class ChatIn(BaseModel):
config: LLMConfigIn | None = None
messages: list[dict]
@router.get("/config", response_model=LLMStatus)
async def get_status():
cfg = from_settings()
if cfg is None:
return LLMStatus(configured=False)
return LLMStatus(configured=True, base_url=cfg.base_url, model=cfg.model)
@router.post("/test")
async def test_connection(cfg: LLMConfigIn | None = None):
"""Send a tiny ping to verify the endpoint accepts our requests."""
config = LLMConfig(**cfg.model_dump()) if cfg else from_settings()
if config is None:
raise HTTPException(
status_code=400,
detail="No LLM config in body and none configured in environment",
)
try:
reply = ping(config)
except LLMError as e:
raise HTTPException(status_code=502, detail=str(e))
return {"ok": True, "model": config.model, "reply": reply}
@router.post("/models")
async def models(cfg: LLMConfigIn | None = None):
"""List available models from the configured (or supplied) endpoint."""
config = LLMConfig(**cfg.model_dump()) if cfg else from_settings()
if config is None:
raise HTTPException(
status_code=400,
detail="No LLM config in body and none configured in environment",
)
try:
items = list_models(config)
except LLMError as e:
raise HTTPException(status_code=502, detail=str(e))
return {"models": items, "count": len(items)}
@router.post("/chat")
async def chat(body: ChatIn):
"""Pass-through chat endpoint — useful for sanity-testing prompts."""
config = LLMConfig(**body.config.model_dump()) if body.config else from_settings()
if config is None:
raise HTTPException(status_code=400, detail="No LLM config supplied")
try:
reply = chat_complete(config, body.messages)
except LLMError as e:
raise HTTPException(status_code=502, detail=str(e))
return {"reply": reply, "model": config.model}