File size: 6,710 Bytes
26dc96c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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}"