"""`LLMTasks` on top of any `LLMProvider`: builds prompts, delegates transport and validation.""" from app.core.types import ScrubbedText from app.llm import prompts from app.llm.base import LLMProvider from app.llm.schemas import CheckinFacts, ItemScore, RiskVerdict class PromptedTasks: def __init__(self, provider: LLMProvider, name: str = "gigachat") -> None: self._provider = provider self.name = name async def extract_item( self, scale_item_id: str, item_text: str, max_score: int, answer: ScrubbedText ) -> ItemScore | None: system = prompts.extract_item_system(scale_item_id, item_text, max_score) result = await self._provider.complete_json( system, answer, ItemScore, validation_context={"max_score": max_score} ) if result is not None and result.item != scale_item_id: # The model sometimes echoes the question instead of the id; the id is ours, not its. result = result.model_copy(update={"item": scale_item_id}) return result async def extract_checkin(self, text: ScrubbedText) -> CheckinFacts | None: return await self._provider.complete_json(prompts.extract_checkin_system(), text, CheckinFacts) async def risk_classify(self, text: ScrubbedText) -> RiskVerdict | None: return await self._provider.complete_json(prompts.risk_system(), text, RiskVerdict) async def phrase(self, intent: str, payload: ScrubbedText, base_text: str) -> str | None: # `payload` already carries `base_text` (built by the gateway), so the model sees the meaning to keep. return await self._provider.complete_text(prompts.phrase_system(intent), payload) async def summarize_for_doctor(self, structured: ScrubbedText, base_text: str) -> str | None: return await self._provider.complete_text(prompts.summary_system(), structured)