# aduc_framework/engineers/neura_link.py # # Versão 2.0.0 (Interface Neural com Controle de Memória) # # Este componente substitui o antigo 'Composer'. Ele atua como o hub central # de comunicação com o LLM, abstraindo a formatação de prompts, o envio # de requisições e o parsing de respostas. Agora inclui um método `reset_memory` # para dar ao orquestrador controle explícito sobre o ciclo de vida da conversa. import logging import json import re import yaml import os from PIL import Image from typing import List, Dict, Any, Optional # --- Importando os managers de LLM de baixo nível e o motor de prompt --- from ..managers.gemini_manager import gemini_manager_singleton from ..managers.llama_multimodal_manager import llama_multimodal_manager_singleton from .prompt_engine import prompt_engine_singleton logger = logging.getLogger(__name__) def robust_json_parser(raw_text: str) -> dict: """ Analisa um objeto JSON de uma string que pode conter texto extra, como blocos de código Markdown ou explicações do LLM. """ logger.debug(f"Neura_Link(JSON_PARSER): Tentando parsear JSON (primeiros 500 chars):\n---\n{raw_text[:500]}\n---") match = re.search(r'```json\s*(\{.*?\})\s*```', raw_text, re.DOTALL) if match: json_str = match.group(1); logger.debug("JSON explícito encontrado.") return json.loads(json_str) try: start_index = raw_text.find('{'); end_index = raw_text.rfind('}') if start_index != -1 and end_index != -1 and end_index > start_index: json_str = raw_text[start_index : end_index + 1]; logger.debug("JSON por delimitadores '{...}' encontrado.") return json.loads(json_str) except json.JSONDecodeError: pass try: return json.loads(raw_text) except json.JSONDecodeError as e: logger.error(f"Falha CRÍTICA no parser de JSON. O LLM retornou um texto inválido. Resposta bruta: \n{raw_text}") raise ValueError(f"O LLM retornou um formato JSON inválido que não pôde ser corrigido. Erro: {e}") class NeuraLink: """ Neura_Link é a interface de comunicação com a IA. Ele carrega templates, formata prompts, envia requisições e processa as respostas, com controle sobre a memória da sessão. """ def __init__(self): logger.info("Neura_Link: Lendo config.yaml para selecionar o LLM Engine...") with open("config.yaml", 'r') as f: config = yaml.safe_load(f) self.provider = config.get('specialists', {}).get('llm_engine', {}).get('provider', 'gemini') if self.provider == 'llama_multimodal': self.llm_manager = llama_multimodal_manager_singleton logger.info("Neura_Link: Motor de LLM configurado para usar 'Llama'.") else: self.llm_manager = gemini_manager_singleton logger.info("Neura_Link: Motor de LLM configurado para usar 'Gemini' (padrão).") prompt_engine_singleton.set_provider(self.provider) self.task_templates = self._load_task_templates() logger.info(f"Neura_Link inicializado com {len(self.task_templates)} templates de tarefa.") def _load_task_templates(self) -> Dict[str, str]: """Carrega todos os arquivos .txt do diretório de templates de tarefa.""" templates = {} try: template_dir = os.path.join(os.path.dirname(__file__), '..', 'prompts', 'task_templates') for task_file in os.listdir(template_dir): if task_file.endswith(".txt"): task_id = os.path.splitext(task_file)[0] with open(os.path.join(template_dir, task_file), 'r', encoding='utf-8') as f: templates[task_id] = f.read() except FileNotFoundError: raise FileNotFoundError(f"Diretório de templates de tarefa não encontrado. Verifique a estrutura do projeto.") return templates def reset_memory(self): """ Envia um comando para o manager de LLM subjacente reiniciar sua sessão de chat, garantindo um contexto limpo para uma nova operação. """ logger.info("Neura_Link: Solicitando reinicialização da memória da sessão de chat.") # Chama o manager com o gatilho `restart_session` e um prompt vazio. # O manager está programado para não fazer uma chamada à API se o prompt for vazio. self.llm_manager.process_turn(prompt_text="", restart_session=True) def execute_task(self, task_id: str, template_data: Dict[str, Any], image_paths: Optional[List[str]] = None, expected_format: str = "text", use_memory: bool = False) -> Any: """ Executa uma única tarefa cognitiva, orquestrando todo o processo. """ logger.info(f"Neura_Link: Executando tarefa '{task_id}'...") generic_template = self.task_templates.get(task_id) if not generic_template: raise ValueError(f"Neura_Link: Template para a tarefa '{task_id}' não foi encontrado.") prompt_content = generic_template for key, value in template_data.items(): prompt_content = prompt_content.replace(f"{{{key}}}", str(value)) images = [Image.open(p) for p in image_paths] if image_paths else None final_model_prompt = prompt_engine_singleton.translate( generic_prompt_content=prompt_content, has_image=bool(images) ) logger.info(f"Neura_Link: Enviando prompt finalizado para o provedor '{self.provider}'. Use Memory: {use_memory}") response_raw = self.llm_manager.process_turn( prompt_text=final_model_prompt, image_list=images, use_memory=use_memory ) logger.info(f"Neura_Link: Resposta bruta recebida do provedor '{self.provider}'.") if expected_format == "json": return robust_json_parser(response_raw) else: return response_raw.strip() # --- Instância Singleton --- neura_link_singleton = NeuraLink()