import json import logging import re import os from groq import Groq from tools import get_current_datetime, search_web, ReminderManager logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") logger = logging.getLogger(__name__) class TelegramJadeAgent: def __init__(self, reminder_manager=None, config_path="telegram_jade/config.json"): # Simple config loading self.config = {} if os.path.exists(config_path): with open(config_path) as f: self.config = json.load(f) self.model = self.config.get("groq_model", "llama3-70b-8192") self.client = Groq(api_key=os.getenv("GROQ_API_KEY")) self.reminder_manager = reminder_manager if reminder_manager else ReminderManager() # We keep history per chat_id in memory for now (could be persisted later) self.histories = {} def _get_system_prompt(self): current_time = get_current_datetime() return f"""You are Jade, a 24h personal assistant on Telegram. Current Time: {current_time} Your goal is to be helpful, manage reminders, and search the web when needed. You have a personality: friendly, efficient, and slightly witty. AVAILABLE TOOLS: You can call tools by responding ONLY with a JSON block in this format: {{"tool": "tool_name", "args": {{"arg1": "value1"}}}} Tools: 1. get_current_datetime() -> Returns current date/time. 2. search_web(query: str) -> Search internet for info. 3. schedule_reminder(time_str: str, message: str) -> Schedule a reminder. - time_str MUST be in "YYYY-MM-DD HH:MM:SS" format. - If the user gives a relative time (e.g., "in 10 mins"), CALCULATE the absolute time based on Current Time. 4. list_reminders() -> List pending reminders for the user. 5. delete_reminder(reminder_id: int) -> Delete a reminder. RULES: - Always check the Current Time before scheduling. - If the user asks for a reminder, you MUST calculate the exact "YYYY-MM-DD HH:MM:SS" and use `schedule_reminder`. - If you need to answer a question about recent events, use `search_web`. - If you are just chatting, reply with plain text. """ def _get_history(self, chat_id): if chat_id not in self.histories: self.histories[chat_id] = [{"role": "system", "content": self._get_system_prompt()}] else: # Update system prompt with fresh time self.histories[chat_id][0]["content"] = self._get_system_prompt() return self.histories[chat_id] def _process_tool_call(self, response_text): try: match = re.search(r'\{.*\}', response_text, re.DOTALL) if not match: return None data = json.loads(match.group(0)) if "tool" in data and "args" in data: return data except: pass return None def _run_tool(self, tool_data, chat_id): name = tool_data["tool"] args = tool_data["args"] logger.info(f"Executing tool {name} for chat {chat_id}") if name == "get_current_datetime": return get_current_datetime() elif name == "search_web": return search_web(args.get("query")) elif name == "schedule_reminder": # We need to hook this into the actual scheduler in main.py. # Ideally, the Agent should return this intent to the Main loop, # or the ReminderManager handles the DB and Main loop watches the DB. # For simplicity: Agent updates DB via ReminderManager. # Main loop (Scheduler) should refresh or listen to changes. # BUT: `schedule_reminder` here just updates the JSON. # The Scheduler in `main.py` needs to be aware of new jobs. # We will return a special signal or just update the DB and assume Main reloads or we return a callback result. return self.reminder_manager.add_reminder(chat_id, args.get("time_str"), args.get("message")) elif name == "list_reminders": return self.reminder_manager.list_reminders(chat_id) elif name == "delete_reminder": return self.reminder_manager.delete_reminder(chat_id, int(args.get("reminder_id"))) else: return f"Unknown tool: {name}" def chat(self, chat_id, user_input): history = self._get_history(chat_id) history.append({"role": "user", "content": user_input}) # Simple ReAct loop for _ in range(5): try: completion = self.client.chat.completions.create( messages=history, model=self.model, temperature=0.5 ) response = completion.choices[0].message.content except Exception as e: return f"Error calling Groq: {e}" tool_data = self._process_tool_call(response) if tool_data: history.append({"role": "assistant", "content": response}) tool_result = self._run_tool(tool_data, chat_id) history.append({"role": "system", "content": f"TOOL_RESULT: {tool_result}"}) # If it was a reminder scheduling, we might want to inform the caller (main.py) # to update the scheduler. # For now, we just continue the conversation loop. else: history.append({"role": "assistant", "content": response}) # Limit history size if len(history) > 20: history = [history[0]] + history[-19:] self.histories[chat_id] = history return response return "I'm getting confused. Let's stop here." def generate_reminder_message(self, chat_id, reminder_message): """ Called when a scheduled reminder triggers. Generates a friendly notification message. """ prompt = [ {"role": "system", "content": "You are Jade. It is time to remind the user of something."}, {"role": "user", "content": f"The reminder is: '{reminder_message}'. Write a friendly message to send to the user now."} ] try: completion = self.client.chat.completions.create( messages=prompt, model=self.model, temperature=0.7 ) return completion.choices[0].message.content except Exception as e: return f"Reminder: {reminder_message}"