File size: 6,771 Bytes
cab4c60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# agent.py
import os
from typing import List, Dict, Optional
from openai import OpenAI
from strategy import StrategyFactory, ExecutionStrategy
from persistence import AgentPersistence
from datetime import datetime

class Agent:
    def __init__(self, name: str, persistence: Optional[AgentPersistence] = None):
        """
        Initialize an agent with a name and optional persistence manager.
        If no persistence manager is provided, a default one will be created.
        """
        self._name = name
        self._persona = ""
        self._instruction = ""
        self._task = ""
        self._api_key = os.getenv('OPENAI_API_KEY', '')
        self._model = "gpt-4o-mini"
        self._history: List[Dict[str, str]] = []
        self._strategy: Optional[ExecutionStrategy] = None
        self._persistence = persistence or AgentPersistence()
        
        # Try to load existing state
        self._persistence.load_agent_state(self)

    @property
    def name(self) -> str:
        """Get the agent's name."""
        return self._name

    @property
    def persona(self) -> str:
        """Get the agent's persona."""
        return self._persona

    @persona.setter
    def persona(self, value: str):
        """Set the agent's persona."""
        self._persona = value

    @property
    def instruction(self) -> str:
        """Get the agent's global instruction."""
        return self._instruction

    @instruction.setter
    def instruction(self, value: str):
        """Set the agent's global instruction."""
        self._instruction = value

    @property
    def task(self) -> str:
        """Get the current task."""
        return self._task

    @task.setter
    def task(self, value: str):
        """Set the current task."""
        self._task = value

    @property
    def strategy(self) -> Optional[ExecutionStrategy]:
        """Get the current execution strategy."""
        return self._strategy

    @strategy.setter
    def strategy(self, strategy_name: str):
        """Set the execution strategy by name."""
        self._strategy = StrategyFactory.create_strategy(strategy_name)

    @property
    def history(self) -> List[Dict[str, str]]:
        """Get the conversation history."""
        return self._history

    def get_history_states(self, limit: int = 10) -> List[Dict]:
        """
        Retrieve the last N states with their timestamps.
        """
        return self._persistence.get_agent_history(self.name, limit)

    def _build_messages(self, task: Optional[str] = None) -> List[Dict[str, str]]:
        """Build the messages list including persona, instruction, and history."""
        messages = [{"role": "system", "content": self.persona}]
        
        if self.instruction:
            messages.append({
                "role": "user", 
                "content": f"Global Instruction: {self.instruction}"
            })
        
        # Add conversation history
        messages.extend(self._history)
        
        # Use provided task or stored task
        current_task = task if task is not None else self._task
        
        # Apply strategy if set
        if self._strategy and current_task:
            current_task = self._strategy.build_prompt(current_task, self.instruction)
        
        # Add the current task if it exists
        if current_task:
            messages.append({"role": "user", "content": current_task})
            
        return messages

    def execute(self, task: Optional[str] = None) -> str:
        """Execute a task using the configured LLM."""
        if task is not None:
            self._task = task
        
        if not self._api_key:
            return "API key not found. Please set the OPENAI_API_KEY environment variable."

        if not self._task:
            return "No task specified. Please provide a task to execute."

        client = OpenAI(api_key=self._api_key)
        messages = self._build_messages()

        try:
            response = client.chat.completions.create(
                model=self._model,
                messages=messages
            )
            
            response_content = response.choices[0].message.content
            
            # Process response through strategy if set
            if self._strategy:
                response_content = self._strategy.process_response(response_content)
            
            # Store the interaction in history
            self._history.append({"role": "user", "content": self._task})
            self._history.append({
                "role": "assistant",
                "content": response_content
            })
            
            # Save state after successful execution
            self.save_state()
            
            # Clear the task after execution
            self._task = ""
            
            return response_content
        except Exception as e:
            return f"An error occurred: {str(e)}"

    def save_state(self) -> bool:
        """Save the current state of the agent."""
        return self._persistence.save_agent_state(self)
    
    def load_state(self, agent_name: Optional[str] = None) -> bool:
        """Load a saved state into the agent."""
        return self._persistence.load_agent_state(self, agent_name)
    
    def clear_history(self, keep_last: int = 0):
        """
        Clear the conversation history, optionally keeping the last N states.
        If keep_last > 0, it will clean up old states but retain the specified number.
        If keep_last = 0, it clears all history.
        """
        if keep_last > 0:
            self._persistence.cleanup_old_states(self.name, keep_last)
            # Reload the state to get the kept history
            self.load_state()
        else:
            self._history = []
            self.save_state()

    def pause(self) -> bool:
        """Pause the agent by saving its current state."""
        return self.save_state()
    
    def resume(self, agent_name: Optional[str] = None) -> bool:
        """Resume the agent by loading its saved state."""
        return self.load_state(agent_name)

    def available_strategies(self) -> List[str]:
        """Return a list of available strategy names."""
        return StrategyFactory.available_strategies()
    
    def delete_agent(self) -> bool:
        """Delete all data for this agent from the database."""
        return self._persistence.delete_agent_state(self.name)

    @staticmethod
    def list_saved_agents() -> Dict[str, datetime]:
        """
        List all saved agents and their last update times.
        Returns a dictionary of agent names mapped to their last update timestamps.
        """
        persistence = AgentPersistence()
        return persistence.list_saved_agents()