version fix

frdel committed Jul 14, 2024 at 22:14 UTC aebd266154c0f19bde1cb2a45e9a3e1f3f6c4fba
7 files changed +165 -135
agent.py
+44 -67
@@ -1,19 +1,14 @@
1 +from dataclasses import dataclass, field
2 import time, importlib, inspect, os, json
2 -import traceback
3 -from typing import Optional, Dict, TypedDict
4 -from tools.helpers import extract_tools, rate_limiter, files, errors
5 -from tools.helpers.print_style import PrintStyle
3 +from typing import Any, Optional, Dict
4 +from python.helpers import extract_tools, rate_limiter, files, errors
5 +from python.helpers.print_style import PrintStyle
6 from langchain.schema import AIMessage
7 from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
8 from langchain_core.messages import HumanMessage, SystemMessage
9 from langchain_core.language_models.chat_models import BaseChatModel
10 from langchain_core.embeddings import Embeddings
11 -from tools.helpers.rate_limiter import RateLimiter
11
13 -# rate_limit = rate_limiter.rate_limiter(30,160000) #TODO! implement properly
14 -
15 -<<<<<<< Updated upstream
16 -=======
12 @dataclass
13 class AgentConfig:
14 chat_model:BaseChatModel
@@ -43,48 +38,20 @@ class AgentConfig:
38 code_exec_ssh_pass: str = "toor"
39 additional: Dict[str, Any] = field(default_factory=dict)
40
46 ->>>>>>> Stashed changes
41
42 class Agent:
43
44 paused=False
45 streaming_agent=None
46
53 - def __init__(self,
54 - agent_number: int,
55 - chat_model:BaseChatModel,
56 - embeddings_model:Embeddings,
57 - memory_subdir: str = "",
58 - auto_memory_count: int = 3,
59 - auto_memory_skip: int = 2,
60 - rate_limit_seconds: int = 60,
61 - rate_limit_requests: int = 30,
62 - rate_limit_input_tokens: int = 0,
63 - rate_limit_output_tokens: int = 0,
64 - msgs_keep_max: int = 25,
65 - msgs_keep_start: int = 5,
66 - msgs_keep_end: int = 10,
67 - max_tool_response_length: int = 3000,
68 - **kwargs):
69 -
70 - # agent config
71 - self.agent_number = agent_number
72 - self.chat_model = chat_model
73 - self.embeddings_model = embeddings_model
74 - self.memory_subdir = memory_subdir
75 - self.auto_memory_count = auto_memory_count
76 - self.auto_memory_skip = auto_memory_skip
77 - self.rate_limit_seconds = rate_limit_seconds
78 - self.rate_limit_requests = rate_limit_requests
79 - self.rate_limit_input_tokens = rate_limit_input_tokens
80 - self.rate_limit_output_tokens = rate_limit_output_tokens
81 - self.msgs_keep_max = msgs_keep_max
82 - self.msgs_keep_start = msgs_keep_start
83 - self.msgs_keep_end = msgs_keep_end
84 - self.max_tool_response_length = max_tool_response_length
47 + def __init__(self, number:int, config: AgentConfig):
48 +
49 + # agent config
50 + self.config = config
51
52 # non-config vars
87 - self.agent_name = f"Agent {self.agent_number}"
53 + self.number = number
54 + self.agent_name = f"Agent {self.number}"
55
56 self.system_prompt = files.read_file("./prompts/agent.system.md").replace("{", "{{").replace("}", "}}")
57 self.tools_prompt = files.read_file("./prompts/agent.tools.md").replace("{", "{{").replace("}", "}}")
@@ -93,7 +60,7 @@ class Agent:
60 self.last_message = ""
61 self.intervention_message = ""
62 self.intervention_status = False
96 - self.rate_limiter = RateLimiter(max_calls=rate_limit_requests,max_input_tokens=rate_limit_input_tokens,max_output_tokens=rate_limit_output_tokens,window_seconds=rate_limit_seconds)
63 + self.rate_limiter = rate_limiter.RateLimiter(max_calls=self.config.rate_limit_requests,max_input_tokens=self.config.rate_limit_input_tokens,max_output_tokens=self.config.rate_limit_output_tokens,window_seconds=self.config.rate_limit_seconds)
64 self.data = {} # free data object all the tools can use
65
66 os.chdir(files.get_abs_path("./work_dir")) #change CWD to work_dir
@@ -122,7 +89,7 @@ class Agent:
89 MessagesPlaceholder(variable_name="messages") ])
90
91 inputs = {"messages": self.history}
125 - chain = prompt | self.chat_model
92 + chain = prompt | self.config.chat_model
93
94 formatted_inputs = prompt.format(messages=self.history)
95 tokens = int(len(formatted_inputs)/4)
@@ -179,7 +146,7 @@ class Agent:
146 else:
147 new_message = HumanMessage(content=msg) if human else AIMessage(content=msg)
148 self.history.append(new_message)
182 - self.cleanup_history(self.msgs_keep_max, self.msgs_keep_start, self.msgs_keep_end)
149 + self.cleanup_history(self.config.msgs_keep_max, self.config.msgs_keep_start, self.config.msgs_keep_end)
150 if message_type=="ai":
151 self.last_message = msg
152
@@ -191,7 +158,7 @@ class Agent:
158 SystemMessage(content=system),
159 HumanMessage(content=msg)])
160
194 - chain = prompt | self.chat_model
161 + chain = prompt | self.config.utility_model
162 response = ""
163 printer = None
164
@@ -266,29 +233,35 @@ class Agent:
233 def process_tools(self, msg: str):
234 # search for tool usage requests in agent message
235 tool_request = extract_tools.json_parse_dirty(msg)
269 - tool_name = tool_request.get("tool_name", "")
270 - tool_args = tool_request.get("tool_args", {})
236
272 - tool = self.get_tool(
273 - tool_name,
274 - tool_args,
275 - msg)
237 + if tool_request is not None:
238 + tool_name = tool_request.get("tool_name", "")
239 + tool_args = tool_request.get("tool_args", {})
240 +
241 + tool = self.get_tool(
242 + tool_name,
243 + tool_args,
244 + msg)
245 +
246 + if self.handle_intervention(): return # wait if paused and handle intervention message if needed
247
277 - if self.handle_intervention(): return # wait if paused and handle intervention message if needed
278 -
279 - tool.before_execution(**tool_args)
280 - response = tool.execute(**tool_args)
281 - tool.after_execution(response)
282 - if response.break_loop: return response.message
248 + tool.before_execution(**tool_args)
249 + response = tool.execute(**tool_args)
250 + tool.after_execution(response)
251 + if response.break_loop: return response.message
252 + else:
253 + msg = files.read_file("prompts/fw.msg_misformat.md")
254 + self.append_message(msg, human=True)
255 + PrintStyle(font_color="red", padding=True).print(msg)
256
257
258 def get_tool(self, name: str, args: dict, message: str, **kwargs):
286 - from tools.unknown import Unknown
287 - from tools.helpers.tool import Tool
259 + from python.tools.unknown import Unknown
260 + from python.helpers.tool import Tool
261
262 tool_class = Unknown
290 - if files.exists("tools",f"{name}.py"):
291 - module = importlib.import_module("tools." + name) # Import the module
263 + if files.exists("python/tools",f"{name}.py"):
264 + module = importlib.import_module("python.tools." + name) # Import the module
265 class_list = inspect.getmembers(module, inspect.isclass) # Get all functions in the module
266
267 for cls in class_list:
@@ -299,20 +272,24 @@ class Agent:
272 return tool_class(agent=self, name=name, args=args, message=message, **kwargs)
273
274 def fetch_memories(self,reset_skip=False):
275 + if self.config.auto_memory_count<=0: return ""
276 if reset_skip: self.memory_skip_counter = 0
277
278 if self.memory_skip_counter > 0:
279 self.memory_skip_counter-=1
280 return ""
281 else:
308 - self.memory_skip_counter = self.auto_memory_skip
309 - from tools import memory_tool
282 + self.memory_skip_counter = self.config.auto_memory_skip
283 + from python.tools import memory_tool
284 messages = self.concat_messages(self.history)
311 - memories = memory_tool.search(messages)
285 + memories = memory_tool.search(self,messages)
286 input = {
287 "conversation_history" : messages,
288 "raw_memories": memories
289 }
290 cleanup_prompt = files.read_file("./prompts/msg.memory_cleanup.md").replace("{", "{{")
291 clean_memories = self.send_adhoc_message(cleanup_prompt,json.dumps(input), output_label="Memory injection")
318 - return clean_memories
\ No newline at end of file
292 + return clean_memories
293 +
294 + def call_extension(self, name: str, **kwargs) -> Any:
295 + pass
\ No newline at end of file
main.py
+60 -46
@@ -1,21 +1,20 @@
1 -import threading, sys, time, readline, models, os
1 +import threading, time, models, os
2 from ansio import application_keypad, mouse_input, raw_input
3 from ansio.input import InputEvent, get_input_event
4 -from agent import Agent
5 -from tools.helpers.print_style import PrintStyle
6 -from tools.helpers.files import read_file
7 -from pytimedinput import timedInput as timed_input
8 -from tools.helpers import files
4 +from agent import Agent, AgentConfig
5 +from python.helpers.print_style import PrintStyle
6 +from python.helpers.files import read_file
7 +from python.helpers import files
8
9
10 input_lock = threading.Lock()
12 -
11 os.chdir(files.get_abs_path("./work_dir")) #change CWD to work_dir
12
15 -# Main conversation loop
16 -def chat():
13
18 - # chat model used for agents
14 +def initialize():
15 +
16 + # main chat model used by agents (smarter, more accurate)
17 +
18 # chat_llm = models.get_groq_llama70b(temperature=0.2)
19 # chat_llm = models.get_groq_llama70b_json(temperature=0.2)
20 # chat_llm = models.get_groq_llama8b(temperature=0.2)
@@ -23,15 +22,20 @@ def chat():
22 # chat_llm = models.get_openai_gpt4o(temperature=0)
23 # chat_llm = models.get_anthropic_opus(temperature=0)
24 # chat_llm = models.get_anthropic_sonnet(temperature=0)
26 - chat_llm = models.get_anthropic_sonnet_35(temperature=0)
25 + # chat_llm = models.get_anthropic_sonnet_35(temperature=0)
26 # chat_llm = models.get_anthropic_haiku(temperature=0)
27 # chat_llm = models.get_ollama_dolphin()
28 + # chat_llm = models.get_ollama(model_name="gemma2:27b")
29 + # chat_llm = models.get_ollama(model_name="llama3:8b-text-fp16")
30 + # chat_llm = models.get_ollama(model_name="gemma2:latest")
31 + # chat_llm = models.get_ollama(model_name="qwen:14b")
32 + chat_llm = models.get_google_chat()
33 +
34 +
35 + # utility model used for helper functions (cheaper, faster)
36 + utility_llm = models.get_anthropic_haiku(temperature=0)
37
38 # embedding model used for memory
31 -<<<<<<< Updated upstream
32 - # embedding_llm = models.get_embedding_openai()
33 - embedding_llm = models.get_embedding_hf()
34 -=======
39 embedding_llm = models.get_embedding_openai()
40 # embedding_llm = models.get_embedding_hf()
41
@@ -50,7 +54,6 @@ def chat():
54 # msgs_keep_max = 25,
55 # msgs_keep_start = 5,
56 # msgs_keep_end = 10,
53 - # response_timeout_seconds = 60,
57 # max_tool_response_length = 3000,
58 code_exec_docker_enabled = True,
59 # code_exec_docker_name = "agent-zero-exe",
@@ -64,58 +67,53 @@ def chat():
67 # code_exec_ssh_pass = "toor",
68 # additional = {},
69 )
67 ->>>>>>> Stashed changes
70
71 # create the first agent
70 - agent0 = Agent( agent_number=0,
71 - chat_model=chat_llm,
72 - embeddings_model=embedding_llm,
73 - # memory_subdir = "",
74 - # auto_memory_count = 3,
75 - # auto_memory_skip = 2,
76 - # rate_limit_seconds = 60,
77 - rate_limit_requests = 30,
78 - rate_limit_input_tokens = 160000,
79 - rate_limit_output_tokens = 8000,
80 - # msgs_keep_max = 25,
81 - # msgs_keep_start = 5,
82 - # msgs_keep_end = 10,
83 - # max_tool_response_length = 3000,
84 - )
72 + agent0 = Agent( number = 0, config = config )
73 +
74 + # start the chat loop
75 + chat(agent0)
76
77 +
78 +# Main conversation loop
79 +def chat(agent:Agent):
80 +
81 # start the conversation loop
82 while True:
83 # ask user for message
84 with input_lock:
90 - timeout = agent0.get_data("timeout") # how long the agent is willing to wait
85 + timeout = agent.get_data("timeout") # how long the agent is willing to wait
86 if not timeout: # if agent wants to wait for user input forever
92 - PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User message ('exit' to leave):")
87 + PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User message ('e' to leave):")
88 + import readline # this fixes arrow keys in terminal
89 user_input = input("> ")
90 PrintStyle(font_color="white", padding=False, log_only=True).print(f"> {user_input}")
91
92 else: # otherwise wait for user input with a timeout
97 - PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User message ({timeout}s timeout, 'wait' to wait, 'exit' to leave):")
98 - user_input = timed_input("> ", timeout=timeout)
93 + PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User message ({timeout}s timeout, 'w' to wait, 'e' to leave):")
94 + import readline # this fixes arrow keys in terminal
95 + # user_input = timed_input("> ", timeout=timeout)
96 + user_input = timeout_input("> ", timeout=timeout)
97
100 - if user_input[1]:
98 + if not user_input:
99 user_input = read_file("prompts/fw.msg_timeout.md")
100 PrintStyle(font_color="white", padding=False).stream(f"{user_input}")
101 else:
104 - user_input = user_input[0].strip()
105 - if user_input.lower()=="wait": # the user needs more time
102 + user_input = user_input.strip()
103 + if user_input.lower()=="w": # the user needs more time
104 user_input = input("> ").strip()
105 PrintStyle(font_color="white", padding=False, log_only=True).print(f"> {user_input}")
106
107
108
109 # exit the conversation when the user types 'exit'
112 - if user_input.lower() == 'exit': break
110 + if user_input.lower() == 'e': break
111
112 # send message to agent0,
115 - assistant_response = agent0.message_loop(user_input)
113 + assistant_response = agent.message_loop(user_input)
114
115 # print agent0 response
118 - PrintStyle(font_color="white",background_color="#1D8348", bold=True, padding=True).print(f"{agent0.agent_name}: reponse:")
116 + PrintStyle(font_color="white",background_color="#1D8348", bold=True, padding=True).print(f"{agent.agent_name}: reponse:")
117 PrintStyle(font_color="white").print(f"{assistant_response}")
118
119
@@ -123,13 +121,13 @@ def chat():
121 def intervention():
122 if Agent.streaming_agent and not Agent.paused:
123 Agent.paused = True # stop agent streaming
126 - PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User intervention ('exit' to leave, empty to continue):")
124 + PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User intervention ('e' to leave, empty to continue):")
125
128 - import readline
126 + import readline # this fixes arrow keys in terminal
127 user_input = input("> ").strip()
128 PrintStyle(font_color="white", padding=False, log_only=True).print(f"> {user_input}")
129
132 - if user_input.lower() == 'exit': os._exit(0) # exit the conversation when the user types 'exit'
130 + if user_input.lower() == 'e': os._exit(0) # exit the conversation when the user types 'exit'
131 if user_input: Agent.streaming_agent.intervention_message = user_input # set intervention message if non-empty
132 Agent.paused = False # continue agent streaming
133
@@ -151,6 +149,22 @@ def capture_keys():
149 intervent=True
150 continue
151
152 +# User input with timeout
153 +def timeout_input(prompt, timeout=10):
154 + result = [""]
155 +
156 + def get_input():
157 + result[0] = input(prompt)
158 +
159 + input_thread = threading.Thread(target=get_input)
160 + input_thread.start()
161 + input_thread.join(timeout)
162 +
163 + if input_thread.is_alive():
164 + return ""
165 + else:
166 + return result[0]
167 +
168 if __name__ == "__main__":
169 print("Initializing framework...")
170
@@ -158,4 +172,4 @@ if __name__ == "__main__":
172 threading.Thread(target=capture_keys, daemon=True).start()
173
174 # Start the chat
161 - chat()
\ No newline at end of file
175 + initialize()
\ No newline at end of file
models.py
prompts/agent.tools.md
+61 -14
@@ -62,11 +62,6 @@ Always verify memory by online.
62 }
63 ~~~
64
65 -<<<<<<< Updated upstream
66 -### memorize:
67 -Save information to persistent memory.
68 -Memories can help you remember important details and later reuse them.
69 -=======
65 ### memory_tool:
66 Manage long term memories. Allowed arguments are "query", "memorize", "forget" and "delete".
67 Memories can help you remember important details and later reuse them.
@@ -74,9 +69,22 @@ When querying, provide a "query" argument to search for. You will retrieve IDs a
69 When memorizing, provide enough information in "memorize" argument for future reuse.
70 When deleting, provide memory IDs from loaded memories separated by commas in "delete" argument.
71 When forgetting, provide query and optionally threshold like you would for querying, corresponding memories will be deleted.
77 ->>>>>>> Stashed changes
72 Provide a title, short summary and and all the necessary information to help you later solve similiar tasks including details like code executed, libraries used etc.
73 **Example usages**:
74 +1. load:
75 +~~~json
76 +{
77 + "thoughts": [
78 + "Let's search my memory for...",
79 + ],
80 + "tool_name": "memory_tool",
81 + "tool_args": {
82 + "query": "File compression library for...",
83 + "threshold": 0.1
84 + }
85 +}
86 +~~~
87 +2. save:
88 ~~~json
89 {
90 "thoughts": [
@@ -84,11 +92,8 @@ Provide a title, short summary and and all the necessary information to help you
92 "Details of this process will be valuable...",
93 "Let's save tools and code used...",
94 ],
87 - "tool_name": "memorize",
95 + "tool_name": "memory_tool",
96 "tool_args": {
89 -<<<<<<< Updated upstream
90 - "memory": "# How to...",
91 -=======
97 "memorize": "# How to...",
98 }
99 }
@@ -114,7 +119,6 @@ Provide a title, short summary and and all the necessary information to help you
119 "tool_name": "memory_tool",
120 "tool_args": {
121 "forget": "User's contact information",
117 ->>>>>>> Stashed changes
122 }
123 }
124 ~~~
@@ -124,13 +128,14 @@ Execute provided terminal commands, python code or nodejs code.
128 This tool can be used to achieve any task that requires computation, or any other software related activity.
129 Place your code escaped and properly indented in the "code" argument.
130 Select the corresponding runtime with "runtime" argument. Possible values are "terminal", "python" and "nodejs".
131 +Sometimes a dialogue can occur in output, questions like Y/N, in that case use the "teminal" runtime in the next step and send your answer.
132 You can use pip, npm and apt-get in terminal runtime to install any required packages.
133 IMPORTANT: Never use implicit print or implicit output, it does not work! If you need output of your code, you MUST use print() or console.log() to output selected variables.
134 When tool outputs error, you need to change your code accordingly before trying again. knowledge_tool can help analyze errors.
130 -Keep in mind that current working directory CWD automatically resets before every tool call.
135 IMPORTANT!: Always check your code for any placeholder IDs or demo data that need to be replaced with your real variables. Do not simply reuse code snippets from tutorials.
136 Do not use in combination with other tools except for thoughts. Wait for response before using other tools.
133 -**Example usage**:
137 +**Example usages:**
138 +1. Execute python code
139 ~~~json
140 {
141 "thoughts": [
@@ -138,10 +143,52 @@ Do not use in combination with other tools except for thoughts. Wait for respons
143 "I can use library...",
144 "Then I can...",
145 ],
141 - "tool_name": "memory_tool",
146 + "tool_name": "code_execution_tool",
147 "tool_args": {
148 "runtime": "python",
149 "code": "import os\nreturn os.getcwd()",
150 }
151 }
152 +~~~
153 +
154 +2. Execute terminal command
155 +~~~json
156 +{
157 + "thoughts": [
158 + "I need to do...",
159 + "I need to install...",
160 + ],
161 + "tool_name": "code_execution_tool",
162 + "tool_args": {
163 + "runtime": "terminal",
164 + "code": "apt-get install zip",
165 + }
166 +}
167 +~~~
168 +
169 +2. 1. Wait for terminal and check output with long running scripts
170 +~~~json
171 +{
172 + "thoughts": [
173 + "I will wait for the program to finish...",
174 + ],
175 + "tool_name": "code_execution_tool",
176 + "tool_args": {
177 + "runtime": "output",
178 + }
179 +}
180 +~~~
181 +
182 +2. 2. Answer terminal dialog
183 +~~~json
184 +{
185 + "thoughts": [
186 + "Program needs confirmation...",
187 + ],
188 + "tool_name": "code_execution_tool",
189 + "tool_args": {
190 + "runtime": "terminal",
191 + "code": "Y",
192 + }
193 +}
194 ~~~
\ No newline at end of file
python/tools/task_done.py
-3
@@ -10,11 +10,8 @@ from python.helpers.print_style import PrintStyle
10 class TaskDone(Tool):
11
12 def execute(self,**kwargs):
13 - # superior = self.agent.get_data("superior")
14 - # if superior:
13 self.agent.set_data("timeout", 0)
14 return Response(message=self.args["text"], break_loop=True)
17 - # else:
15
16 def after_execution(self, response, **kwargs):
17 pass # do add anything to the history or output
\ No newline at end of file
test.py deleted
-5
@@ -1,5 +0,0 @@
1 -from models import get_google_chat
2 -
3 -llm = get_google_chat()
4 -result = llm.invoke("Write a ballad about LangChain")
5 -print(result.content)
work_dir/.gitkeep