|
|
import json
|
|
|
import logging
|
|
|
import os
|
|
|
import sys
|
|
|
import time
|
|
|
import uuid
|
|
|
|
|
|
from groq import Groq
|
|
|
|
|
|
|
|
|
from .handlers import ImageHandler
|
|
|
from .tts import TTSPlayer
|
|
|
from .utils import slim_history
|
|
|
from .shorestone import ShoreStoneMemory
|
|
|
from .curator_heuristic import MemoryCuratorHeuristic
|
|
|
from .web_search import WebSearchHandler
|
|
|
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - JADE - %(levelname)s - %(message)s")
|
|
|
|
|
|
class JadeAgent:
|
|
|
def __init__(self, config_path="jade/config.json"):
|
|
|
|
|
|
|
|
|
try:
|
|
|
with open(config_path) as f:
|
|
|
self.cfg = json.load(f)
|
|
|
except FileNotFoundError:
|
|
|
|
|
|
base_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
config_path = os.path.join(base_dir, "config.json")
|
|
|
with open(config_path) as f:
|
|
|
self.cfg = json.load(f)
|
|
|
|
|
|
|
|
|
logging.info("Iniciando J.A.D.E. em modo API (Groq)...")
|
|
|
self.api_key = self._get_api_key()
|
|
|
self.client = Groq(api_key=self.api_key)
|
|
|
self.model_name = self.cfg.get("groq_model", "meta-llama/llama-4-maverick-17b-128e-instruct")
|
|
|
|
|
|
|
|
|
self.system_prompt = {"role": "system", "content": "Você é J.A.D.E., uma IA multimodal calma e inteligente. Seja direta. Responda de forma concisa e natural. NÃO explique seu processo de pensamento. Apenas responda à pergunta."}
|
|
|
|
|
|
|
|
|
logging.info("Carregando módulos de percepção e memória...")
|
|
|
|
|
|
|
|
|
self.image_handler = ImageHandler(self.cfg.get("caption_model", "Salesforce/blip-image-captioning-large"))
|
|
|
self.tts = TTSPlayer(lang=self.cfg.get("language", "pt"))
|
|
|
|
|
|
|
|
|
self.memory = ShoreStoneMemory()
|
|
|
|
|
|
self.memory.load_or_create_session("sessao_padrao_gabriel")
|
|
|
|
|
|
|
|
|
self.curator = MemoryCuratorHeuristic(shorestone_memory=self.memory)
|
|
|
self.response_count = 0
|
|
|
self.maintenance_interval = 10
|
|
|
|
|
|
|
|
|
self.web_search_handler = WebSearchHandler()
|
|
|
|
|
|
logging.info(f"J.A.D.E. pronta e conectada ao modelo {self.model_name}.")
|
|
|
|
|
|
def _get_api_key(self):
|
|
|
"""Recupera a chave da API do ambiente de forma segura."""
|
|
|
key = os.getenv("GROQ_API_KEY")
|
|
|
if not key:
|
|
|
logging.error("Chave GROQ_API_KEY não encontrada nas variáveis de ambiente.")
|
|
|
|
|
|
|
|
|
print("WARNING: GROQ_API_KEY not found.")
|
|
|
return key
|
|
|
|
|
|
def _chat(self, messages):
|
|
|
"""Envia as mensagens para a Groq e retorna a resposta."""
|
|
|
try:
|
|
|
chat = self.client.chat.completions.create(
|
|
|
messages=messages,
|
|
|
model=self.model_name,
|
|
|
temperature=0.7,
|
|
|
max_tokens=1024
|
|
|
)
|
|
|
return chat.choices[0].message.content.strip()
|
|
|
except Exception as e:
|
|
|
logging.error(f"Erro na comunicação com a Groq: {e}")
|
|
|
return "Desculpe, tive um problema ao me conectar com meu cérebro na nuvem."
|
|
|
|
|
|
def respond(self, history, user_input, user_id="default", vision_context=None, web_search=False, thinking_mode=False):
|
|
|
"""Processo principal de raciocínio: Buscar -> Lembrar -> Ver -> Pensar -> Responder -> Memorizar -> Manter."""
|
|
|
|
|
|
|
|
|
session_name = f"user_{user_id}"
|
|
|
self.memory.load_or_create_session(session_name)
|
|
|
|
|
|
messages = history[:]
|
|
|
|
|
|
|
|
|
if thinking_mode:
|
|
|
thinking_prompt = {
|
|
|
"role": "system",
|
|
|
"content": """MODO THINKING ATIVADO: Antes de dar sua resposta final, pense passo a passo.
|
|
|
Coloque todo seu raciocínio dentro de tags <thinking>...</thinking>.
|
|
|
Após fechar a tag </thinking>, dê sua resposta final de forma clara e direta.
|
|
|
Exemplo:
|
|
|
<thinking>
|
|
|
1. Primeiro, vou analisar...
|
|
|
2. Considerando que...
|
|
|
3. Portanto...
|
|
|
</thinking>
|
|
|
|
|
|
[Sua resposta final aqui]"""
|
|
|
}
|
|
|
messages.append(thinking_prompt)
|
|
|
|
|
|
|
|
|
if web_search and self.web_search_handler.is_available():
|
|
|
search_results = self.web_search_handler.search(user_input)
|
|
|
if search_results:
|
|
|
search_context = f"--- RESULTADOS DA BUSCA WEB ---\n{search_results}\n--- FIM DA BUSCA ---"
|
|
|
messages.append({"role": "system", "content": search_context})
|
|
|
|
|
|
|
|
|
memories = self.memory.remember(user_input)
|
|
|
if memories:
|
|
|
memory_context = f"--- MEMÓRIAS RELEVANTES (ShoreStone) ---\n{memories}\n--- FIM DAS MEMÓRIAS ---"
|
|
|
|
|
|
messages.append({"role": "system", "content": memory_context})
|
|
|
|
|
|
|
|
|
if vision_context:
|
|
|
messages.append({"role": "system", "content": f"Contexto visual da imagem que o usuário enviou: {vision_context}"})
|
|
|
|
|
|
|
|
|
history.append({"role": "user", "content": user_input})
|
|
|
messages.append({"role": "user", "content": user_input})
|
|
|
|
|
|
|
|
|
resposta = self._chat(messages)
|
|
|
|
|
|
|
|
|
history.append({"role": "assistant", "content": resposta})
|
|
|
history = slim_history(history, keep=self.cfg.get("max_context", 12))
|
|
|
|
|
|
|
|
|
self.memory.memorize(user_input, resposta)
|
|
|
|
|
|
print(f"\n🤖 J.A.D.E.: {resposta}")
|
|
|
|
|
|
|
|
|
audio_path = None
|
|
|
try:
|
|
|
|
|
|
audio_path = self.tts.save_audio_to_file(resposta)
|
|
|
except Exception as e:
|
|
|
logging.warning(f"TTS falhou (silenciado): {e}")
|
|
|
|
|
|
|
|
|
self.response_count += 1
|
|
|
if self.response_count % self.maintenance_interval == 0:
|
|
|
logging.info(f"Ciclo de manutenção agendado (interação {self.response_count}). Verificando saúde da memória...")
|
|
|
try:
|
|
|
self.curator.run_maintenance_cycle()
|
|
|
except Exception as e:
|
|
|
logging.error(f"Erro no Curador de Memória: {e}")
|
|
|
|
|
|
return resposta, audio_path, history
|
|
|
|