41 lines
1.8 KiB
Python
41 lines
1.8 KiB
Python
"""Базовый интерфейс «мозга» (LLM) + результат запроса.
|
|
|
|
История диалога — простой список {"role": "user"|"assistant", "text": str},
|
|
чтобы провайдеры и будущая машина состояний не зависели от формата API.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass, field
|
|
from typing import List, Optional
|
|
|
|
DialogHistory = List[dict] # [{"role": "user"|"assistant", "text": str}, ...]
|
|
|
|
|
|
@dataclass
|
|
class LlmResult:
|
|
"""Ответ LLM: сырой текст + очищенный для озвучки."""
|
|
|
|
question: str
|
|
raw_text: str # как прислала модель (с разметкой, если была)
|
|
cleaned_text: str # после clean_for_speech() — именно это озвучиваем
|
|
generation_sec: float
|
|
model: str = ""
|
|
|
|
def __str__(self) -> str:
|
|
return (f"[{self.model}] {self.generation_sec:.2f} c, "
|
|
f"{len(self.cleaned_text)} симв.: {self.cleaned_text!r}")
|
|
|
|
|
|
class LlmProvider(ABC):
|
|
"""Интерфейс провайдера LLM (сейчас OpenRouter, при надобности — локальный ollama)."""
|
|
|
|
name: str = "base"
|
|
context_block: str = "" # доп. контекст (долговременная память); подставляется в system
|
|
|
|
@abstractmethod
|
|
def ask(self, question: str, history: Optional[DialogHistory] = None) -> LlmResult:
|
|
"""Задать вопрос, получить ответ. history — предыдущие реплики диалога."""
|
|
|
|
def close(self) -> None:
|
|
"""Освободить ресурсы (по умолчанию — ничего).""" |