Spaces:
Paused
Paused
| # 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() |