""" 🇬🇧 Module: comparer.py() Purpose: Compares two portfolios using LLM. Fetches metrics for both and builds a unified comparison prompt. 🇷🇺 Модуль: comparer.py Назначение: сравнение двух инвестиционных портфелей с помощью LLM. Получает метрики обоих портфелей, формирует промпт и возвращает потоковый результат. """ import asyncio from typing import Generator from services.output_api import extract_portfolio_id, fetch_metrics_async from services.llm_client import llm_service from prompts.system_prompts import COMPARISON_SYSTEM_PROMPT from prompts.reference_templates import REFERENCE_COMPARISON_PROMPT class PortfolioComparer: """Main use-case class for comparing two portfolios.""" def __init__(self, llm=llm_service, model_name: str = "meta-llama/Meta-Llama-3.1-8B-Instruct"): self.llm = llm self.model_name = model_name def run(self, text1: str, text2: str) -> Generator[str, None, None]: """Stream comparison results between two portfolios.""" id1 = extract_portfolio_id(text1) id2 = extract_portfolio_id(text2) if text1 == text2: yield "❗ Please, give me a two difference portfolio ID." return if not id1 or not id2: yield "❗ One of two portfolios is empty or incorrect." return yield "⏳ Working..." try: m1 = asyncio.run(fetch_metrics_async(id1)) m2 = asyncio.run(fetch_metrics_async(id2)) except Exception as e: yield f"❌ There are issue via collecting data: {e}" return if not m1 or not m2: yield "❗ One of two portfolios is empty or has incorrect data." return m1_text = ", ".join(f"{k}: {v}" for k, v in m1.items()) m2_text = ", ".join(f"{k}: {v}" for k, v in m2.items()) prompt = ( f"{REFERENCE_COMPARISON_PROMPT}\n" f"Используй эти данные для сравнения:\n" f"Портфель A: {m1_text}\n" f"Портфель B: {m2_text}" ) try: messages = [ {"role": "system", "content": COMPARISON_SYSTEM_PROMPT}, {"role": "user", "content": prompt}, ] partial = "" for delta in self.llm.stream_chat(messages=messages, model=self.model_name): partial += delta yield partial except Exception as e: yield f"❌ Ошибка при сравнении портфелей через LLM: {e}"