first load

frdel committed Jun 10, 2024 at 11:10 UTC 8cef5e1e359f057e197a9a6d52278ce9773dba55
51 files changed +1080 -1
.gitignore new
+14
@@ -0,0 +1,14 @@
1 +/.DS_Store
2 +/.env
3 +/__pycache__
4 +
5 +
6 +# Ignore all contents of the directory "work_dir"
7 +work_dir/*
8 +# But do not ignore the directory itself
9 +!work_dir/
10 +
11 +# Ignore all contents of the directory "memory"
12 +memory/*
13 +# But do not ignore the directory itself
14 +!memory/
\ No newline at end of file
.vscode/extensions.json new
+7
@@ -0,0 +1,7 @@
1 +{
2 + "recommendations": [
3 + "usernamehw.errorlens",
4 + "ms-python.debugpy",
5 + "ms-python.python"
6 + ]
7 +}
\ No newline at end of file
.vscode/launch.json new
+19
@@ -0,0 +1,19 @@
1 +{
2 + "version": "0.2.0",
3 + "configurations": [
4 + {
5 + "name": "Debug main.py",
6 + "type": "debugpy",
7 + "request": "launch",
8 + "program": "./main.py",
9 + "console": "integratedTerminal",
10 + },
11 + {
12 + "name": "Debug current file",
13 + "type": "debugpy",
14 + "request": "launch",
15 + "program": "${file}",
16 + "console": "integratedTerminal",
17 + }
18 + ]
19 +}
\ No newline at end of file
.vscode/settings.json new
+3
@@ -0,0 +1,3 @@
1 +{
2 + "python.analysis.typeCheckingMode": "standard"
3 +}
\ No newline at end of file
README.md
+3 -1
@@ -1,2 +1,4 @@
1 -# agent-zero
1 +# Agent Zero
2 Agent Zero AI framework
3 +
4 +![Agent Zero](docs/splash.webp)
\ No newline at end of file
agent.py new
+180
@@ -0,0 +1,180 @@
1 +import os, json, contextlib, time, importlib, inspect
2 +from io import StringIO
3 +from typing import Optional, Dict
4 +from tools.helpers import extract_tools, rate_limiter, files
5 +from tools.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
9 +from langchain_core.language_models.chat_models import BaseChatModel
10 +
11 +
12 +rate_limit = rate_limiter.rate_limiter(30,80000)
13 +
14 +class Agent:
15 +
16 + paused=False
17 + streaming_agent=None
18 +
19 + @staticmethod
20 + def configure(model_chat, model_embedding, memory_subdir="", memory_results=3):
21 +
22 + #save configuration
23 + Agent.model_chat = model_chat
24 +
25 + # initialize memory tool
26 + from tools import memory_tool
27 + memory_tool.initialize(
28 + embeddings_model=model_embedding,
29 + messages_returned=memory_results,
30 + subdir=memory_subdir )
31 +
32 + def __init__(self, system_prompt:Optional[str]=None, tools_prompt:Optional[str]=None, superior:Optional['Agent']=None, number=0):
33 +
34 + self.number = number
35 + self.name = f"Agent {self.number}"
36 +
37 + if system_prompt is None: system_prompt = files.read_file("./prompts/agent.system.md")
38 + if tools_prompt is None: tools_prompt = files.read_file("./prompts/agent.tools.md")
39 + self.system_prompt = system_prompt.replace("{", "{{").replace("}", "}}")
40 + self.tools_prompt = tools_prompt.replace("{", "{{").replace("}", "}}")
41 +
42 + self.superior: Optional['Agent'] = superior
43 +
44 + self.subordinate: Optional['Agent'] = None
45 + self.history = []
46 + self.last_message = ""
47 + self.message_for_superior = ""
48 + self.intervention_message = ""
49 + self.intervention_status = False
50 +
51 + self.prompt = ChatPromptTemplate.from_messages([
52 + ("system", self.system_prompt + "\n\n" + self.tools_prompt),
53 + MessagesPlaceholder(variable_name="messages") ])
54 +
55 + def process_message(self, msg: str):
56 + try:
57 + self.append_message(msg, human=True) # Append the user's input to the history
58 + printer = PrintStyle(italic=True, font_color="#b3ffd9", padding=False)
59 +
60 + while True: # let the agent iterate on his thoughts until he stops by using a tool
61 + Agent.streaming_agent = self #mark self as current streamer
62 + agent_response = ""
63 + self.intervention_status = False # reset interventon status
64 + try:
65 +
66 + inputs = {"input": msg,"messages": self.history}
67 + chain = self.prompt | Agent.model_chat
68 + formatted_inputs = self.prompt.format(**inputs)
69 +
70 + rate_limit(len(formatted_inputs)/4) #wait for rate limiter - A helpful rule of thumb is that one token generally corresponds to ~4 characters of text for common English text. This translates to roughly ¾ of a word (so 100 tokens ~= 75 words).
71 +
72 + # output that the agent is starting
73 + PrintStyle(bold=True, font_color="green", padding=True, background_color="white").print(f"{self.name}: Starting a message:")
74 +
75 + for chunk in chain.stream(inputs):
76 +
77 + if self.handle_intervention(agent_response): break # wait for intervention and handle it, if paused
78 +
79 + if chunk.content is not None and chunk.content != '':
80 + printer.stream(chunk.content) # output the agent response stream
81 + agent_response += chunk.content # type: ignore | concatenate stream into the response
82 +
83 + if not self.handle_intervention(agent_response):
84 + #if assistant_response is the same as last message in history, let him know
85 + if self.last_message == agent_response:
86 + agent_response = files.read_file("./prompts/fw.msg_repeat.md")
87 + PrintStyle(font_color="orange", padding=True).print(agent_response)
88 + self.last_message = agent_response
89 +
90 + self.append_message(agent_response) # Append the assistant's response to the history
91 +
92 + self.process_tools(agent_response)
93 +
94 + #break the execution if there is a message for superior agent
95 + if self.message_for_superior and not self.intervention_status:
96 + msg = self.message_for_superior
97 + self.message_for_superior = ""
98 + return msg
99 +
100 + # Forward errors to the LLM, maybe he can fix them
101 + except Exception as e:
102 + msg_response = files.read_file("./prompts/fw.error.md", error=str(e)) # error message template
103 + self.append_message(msg_response, human=True)
104 + PrintStyle(font_color="red", padding=True).print(msg_response)
105 + finally:
106 + Agent.streaming_agent = None # unset current streamer
107 +
108 + def append_message(self, msg: str, human: bool = False):
109 + message_type = "human" if human else "ai"
110 + if self.history and self.history[-1].type == message_type:
111 + self.history[-1].content += "\n\n" + msg
112 + else:
113 + new_message = HumanMessage(content=msg) if human else AIMessage(content=msg)
114 + self.history.append(new_message)
115 + self.cleanup_history(5, 10)
116 + if message_type=="ai":
117 + self.last_message = msg
118 +
119 + def cleanup_history(self,x, y):
120 + if len(self.history) <= x + y:
121 + return self.history
122 +
123 + first_x = self.history[:x]
124 + last_y = self.history[-y:]
125 +
126 + cleanup_prompt = files.read_file("./prompts/fw.msg_cleanup.md")
127 + middle_values = [AIMessage(content=cleanup_prompt)]
128 +
129 + self.history = first_x + middle_values + last_y
130 +
131 + def handle_intervention(self, progress:str="") -> bool:
132 + while self.paused: time.sleep(0.1) # wait if paused
133 + if self.intervention_message and not self.intervention_status: # if there is an intervention message, but not yet processed
134 + if progress.strip(): self.append_message(progress) # append the response generated so far
135 + user_msg = files.read_file("./prompts/fw.intervention.md", user_message=self.intervention_message) # format the user intervention template
136 + self.append_message(user_msg,human=True) # append the intervention message
137 + self.intervention_message = "" # reset the intervention message
138 + self.intervention_status = True
139 + return self.intervention_status # return intervention status
140 +
141 + def process_tools(self, msg: str):
142 + # search for tool usage requests in agent message
143 + tool_requests = extract_tools.extract_tool_requests(msg)
144 +
145 + for tool_request in tool_requests:
146 +
147 + if self.handle_intervention(): break # wait if paused and handle intervention message if needed
148 +
149 + tool_name = tool_request["name"]
150 + tool_function = self.get_tool(tool_name)
151 +
152 + if callable(tool_function):
153 + short_params = {k: v for k, v in tool_request.items() if k != "name" and k != "body"} # only extra parameters to output to console
154 +
155 + PrintStyle(font_color="#1B4F72", padding=True, background_color="white", bold=True).print(f"{self.name}: Using tool {tool_name}:")
156 + PrintStyle(font_color="#85C1E9").print(short_params, tool_request["body"], sep="\n") if short_params else PrintStyle(font_color="#85C1E9").print(tool_request["body"])
157 +
158 + tool_response = tool_function(self, tool_request["body"], **tool_request) or "" # call tool function with all parameters, body parameter separated for convenience
159 + Agent.streaming_agent = self # mark self as current streamer again, it may have changed during tool use
160 +
161 + if self.handle_intervention(): break # wait if paused and handle intervention message if needed
162 +
163 + msg_response = files.read_file("./prompts/fw.tool_response.md", tool_name=tool_name, tool_response=tool_response)
164 + self.append_message(msg_response, human=True)
165 +
166 + PrintStyle(font_color="#1B4F72", background_color="white", padding=True, bold=True).print(f"{self.name}: Response from {tool_name}:")
167 + PrintStyle(font_color="#85C1E9").print(tool_response)
168 + else:
169 + if self.handle_intervention(): break # wait if paused and handle intervention message if needed
170 + msg_response = files.read_file("./prompts/fw.tool_not_found.md", tool_name=tool_name, tools_prompt=self.tools_prompt)
171 + self.append_message(msg_response,True)
172 + PrintStyle(font_color="orange", padding=True).print(msg_response)
173 +
174 + def get_tool(self, name: str):
175 + module = importlib.import_module("tools." + name) # Import the module
176 + functions_list = {name: func for name, func in inspect.getmembers(module, inspect.isfunction)} # Get all functions in the module
177 +
178 + if "execute" in functions_list: return functions_list["execute"] # Check if the module contains a function named "execute"
179 + if functions_list: return next(iter(functions_list.values())) # Return the first function if no "execute" function is found
180 + return None # Return None if no functions are found
\ No newline at end of file
docs/splash.webp
Binary files /dev/null and b/docs/splash.webp differ
example.env new
+4
@@ -0,0 +1,4 @@
1 +API_KEY_OPENAI=
2 +API_KEY_ANTHROPIC=
3 +API_KEY_GROQ=
4 +API_KEY_PERPLEXITY=
\ No newline at end of file
main.py new
+89
@@ -0,0 +1,89 @@
1 +import threading, sys, time, readline, models
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 +
7 +input_lock = threading.Lock()
8 +
9 +# Main conversation loop
10 +def chat():
11 +
12 + # chat model used for agents
13 + # chat_llm = models.get_groq_llama70b(temperature=0.2)
14 + chat_llm = models.get_openai_gpt35()
15 + # chat_llm = models.get_openai_gpt4o()
16 + # chat_llm = models.get_anthropic_sonnet()
17 + # chat_llm = models.get_anthropic_haiku()
18 + # chat_llm = models.get_ollama_dolphin()
19 +
20 + # embedding model used for memory
21 + embedding_llm = models.get_embedding_openai()
22 +
23 + # initial configuration
24 + Agent.configure(
25 + model_chat = chat_llm,
26 + model_embedding = embedding_llm,
27 + #memory_subdir=""
28 + #memory_results=3
29 + )
30 +
31 + # create the first agent
32 + agent0 = Agent()
33 +
34 + # start the conversation loop
35 + while True:
36 + # ask user for message
37 + PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User message ('exit' to leave):")
38 + # while input_lock: time.sleep(0.1)
39 + with input_lock:
40 + user_input = input("> ").strip()
41 +
42 + # exit the conversation when the user types 'exit'
43 + if user_input.lower() == 'exit': break
44 +
45 + # send message to agent0,
46 + assistant_response = agent0.process_message(user_input)
47 +
48 + # print agent0 response
49 + PrintStyle(font_color="white",background_color="#1D8348", bold=True, padding=True).print(f"{agent0.name}: reponse:")
50 + PrintStyle(font_color="white").print(f"{assistant_response}")
51 +
52 +
53 +# User intervention during agent streaming
54 +def intervention():
55 + if Agent.streaming_agent and not Agent.paused:
56 + Agent.paused = True # stop agent streaming
57 + PrintStyle(background_color="#6C3483", font_color="white", bold=True, padding=True).print(f"User intervention ('exit' to leave, empty to continue):")
58 +
59 + import readline
60 + user_input = input("> ").strip()
61 + if user_input.lower() == 'exit': sys.exit() # exit the conversation when the user types 'exit'
62 + if user_input: Agent.streaming_agent.intervention_message = user_input # set intervention message if non-empty
63 + Agent.paused = False # continue agent streaming
64 +
65 +
66 +# Capture keyboard input to trigger user intervention
67 +def capture_keys():
68 + global input_lock
69 + intervent=False
70 + while True:
71 + if intervent: intervention()
72 + intervent = False
73 +
74 + if Agent.streaming_agent:
75 + # with raw_input, application_keypad, mouse_input:
76 + with input_lock, raw_input, application_keypad:
77 + event: InputEvent | None = get_input_event(timeout=0.1)
78 + if event and (event.shortcut.isalpha() or event.shortcut.isspace()):
79 + intervent=True
80 + continue
81 +
82 +if __name__ == "__main__":
83 + print("Initializing framework...")
84 +
85 + # Start the key capture thread for user intervention during agent streaming
86 + threading.Thread(target=capture_keys, daemon=True).start()
87 +
88 + # Start the chat
89 + chat()
\ No newline at end of file
models.py new
+76
@@ -0,0 +1,76 @@
1 +import os
2 +from dotenv import load_dotenv
3 +from langchain_community.llms import Ollama
4 +from langchain_openai import ChatOpenAI, OpenAI, OpenAIEmbeddings
5 +from langchain_anthropic import ChatAnthropic
6 +from langchain_groq import ChatGroq
7 +from langchain_community.embeddings import HuggingFaceEmbeddings
8 +
9 +
10 +# Load environment variables
11 +load_dotenv()
12 +
13 +# Configuration
14 +DEFAULT_TEMPERATURE = 0.0
15 +
16 +# Utility function to get API keys from environment variables
17 +def get_api_key(service):
18 + return os.getenv(f"API_KEY_{service.upper()}")
19 +
20 +# Factory functions for each model type
21 +def get_anthropic_haiku(api_key=None, temperature=DEFAULT_TEMPERATURE):
22 + api_key = api_key or get_api_key("anthropic")
23 + return ChatAnthropic(model_name="claude-3-haiku-20240307", temperature=temperature, api_key=api_key) # type: ignore
24 +
25 +def get_anthropic_sonnet(api_key=None, temperature=DEFAULT_TEMPERATURE):
26 + api_key = api_key or get_api_key("anthropic")
27 + return ChatAnthropic(model_name="claude-3-sonnet-20240229", temperature=temperature, api_key=api_key) # type: ignore
28 +
29 +def get_anthropic_opus(api_key=None, temperature=DEFAULT_TEMPERATURE):
30 + api_key = api_key or get_api_key("anthropic")
31 + return ChatAnthropic(model_name="claude-3-opus-20240229", temperature=temperature, api_key=api_key) # type: ignore
32 +
33 +def get_openai_gpt35(api_key=None, temperature=DEFAULT_TEMPERATURE):
34 + api_key = api_key or get_api_key("openai")
35 + return ChatOpenAI(model_name="gpt-3.5-turbo", temperature=temperature, api_key=api_key) # type: ignore
36 +
37 +def get_openai_gpt35_instruct(api_key=None, temperature=DEFAULT_TEMPERATURE):
38 + api_key = api_key or get_api_key("openai")
39 + return OpenAI(model_name="gpt-3.5-turbo-instruct", temperature=temperature, api_key=api_key) # type: ignore
40 +
41 +def get_openai_gpt4(api_key=None, temperature=DEFAULT_TEMPERATURE):
42 + api_key = api_key or get_api_key("openai")
43 + return ChatOpenAI(model_name="gpt-4-0125-preview", temperature=temperature, api_key=api_key) # type: ignore
44 +
45 +def get_openai_gpt4o(api_key=None, temperature=DEFAULT_TEMPERATURE):
46 + api_key = api_key or get_api_key("openai")
47 + return ChatOpenAI(model_name="gpt-4o", temperature=temperature, api_key=api_key) # type: ignore
48 +
49 +def get_groq_mixtral7b(api_key=None, temperature=DEFAULT_TEMPERATURE):
50 + api_key = api_key or get_api_key("groq")
51 + return ChatGroq(model_name="mixtral-8x7b-32768", temperature=temperature, api_key=api_key) # type: ignore
52 +
53 +def get_groq_llama70b(api_key=None, temperature=DEFAULT_TEMPERATURE):
54 + api_key = api_key or get_api_key("groq")
55 + return ChatGroq(model_name="llama3-70b-8192", temperature=temperature, api_key=api_key) # type: ignore
56 +
57 +def get_groq_llama8b(api_key=None, temperature=DEFAULT_TEMPERATURE):
58 + api_key = api_key or get_api_key("groq")
59 + return ChatGroq(model_name="Llama3-8b-8192", temperature=temperature, api_key=api_key) # type: ignore
60 +
61 +def get_groq_gemma(api_key=None, temperature=DEFAULT_TEMPERATURE):
62 + api_key = api_key or get_api_key("groq")
63 + return ChatGroq(model_name="gemma-7b-it", temperature=temperature, api_key=api_key) # type: ignore
64 +
65 +def get_ollama_dolphin(api_key=None, temperature=DEFAULT_TEMPERATURE):
66 + return Ollama(model="dolphin-llama3:8b-256k-v2.9-fp16")
67 +
68 +def get_ollama_phi(api_key=None, temperature=DEFAULT_TEMPERATURE):
69 + return Ollama(model="phi3:3.8b-mini-instruct-4k-fp16")
70 +
71 +def get_embedding_hf(model_name="sentence-transformers/all-MiniLM-L6-v2"):
72 + return HuggingFaceEmbeddings(model_name=model_name)
73 +
74 +def get_embedding_openai(api_key=None):
75 + api_key = api_key or get_api_key("openai")
76 + return OpenAIEmbeddings(api_key=api_key)
prompts/agent.system.md new
+75
@@ -0,0 +1,75 @@
1 +# Your role
2 +- You are a fully autonomous, highly inteligent AI agent.
3 +- You are given a task by a superior agent: instance of the same AI agent.
4 +- Your superior agent acts as a USER in your message history.
5 +- You must complete the task either yourself or with help of subordinate AI agents.
6 +- Tasks can be simple (questions, calculations, writing), or complex (code execution, data processing, etc.)
7 + - When you are given a question, solution is the response to superior using speak_to_superior tool. Nothing more needs to be done.
8 + - When you are given a complex task, solution can consist of both actions and a response.
9 +- Do not overprocess tasks, do only what you are asked and provide meaningful response.
10 +
11 +# Step by step instruction manual to problem solving
12 +- Once you are given a task, follow these instructions step by step.
13 +- Use reasoning skills and explain your steps.
14 +- Always first conduct reasoning, respond with your thoughts and breakdowns first. Do no immediately use tools.
15 +- Subordinates can help you solve tasks you struggle with.
16 +
17 +
18 +1. Search for solution outline
19 + 1. 1. Figure out the most straight forward solution to the problem, consider simplicity and reliability.
20 + 1. 2. Always perform multiple searches with different approaches to be able to compare the pros and cons, focus on simple and straightforward solutions.
21 + 1. 3. Always check your memory, it may already contain solution to your task.
22 + 1. 4. If a solution has been chosen, proceed to Task breakdown and evaluation.
23 +3. Task breakdown and evaluation
24 + 3. 1. If the task consists of multiple steps that can be self contained and delegated, break it down into subtasks and delegate them to subordinates.
25 +4. Processing task/subtasks
26 + 4. 1. Process all necessary subtasks in a step-by-step manner. Explain your reasoning and only progress to the next step when the previous is complete.
27 + 4. 2. Self contained subtasks should be delegated to subordinate agents using the speak_to_subordinate tool.
28 + 4. 3. Validate and verify results, expect the worst.
29 +5. Completing the task
30 + 5. 1. Once all subtasks are completed, validate and verify the correctness of the full result.
31 + 5. 2. If you managed to solve a task that required some online search, multiple attemps or advanced programming, save the information about solution into your memory, so you can later reuse it.
32 +6. Reporting back to superior agent
33 + 6. 1. Report back with the full status of your task processing. Include all details, that might be relevant for the superior agent to proceed with his workflow like actions done, persistent changes made etc. You don't need to include details that are not relevant for further processing.
34 +
35 +# General operation manual
36 +- Use your reasoning and process each problem in a step-by-step manner.
37 +- To keep track of your chain of thought process, use your response messages containing your thoughts. You will be prompted again to continue with more thoughts or function calls until you are satisfied, so do not rush with function calls, first process your thoughts.
38 +- Your chat history is private for you and only contains your thoughts, tool usage and instructions from superior agent.
39 +- Prevent loping thoughts. Always consider your previous messages and check that you are not repeating yourself in circles.
40 +- Always double check information and code found if it contains placeholders or demo data that need to be replaced with your real variables. Never use code still containing placeholders, you have to fill that information in.
41 +- Avoid solutions that require credentials.
42 +- When asked about your memory, it means your long term memory. Use your memory tool.
43 +
44 +# Tips and tricks
45 +- Focus on python libraries when searching for solutions. You can use them with your tools and make solutions easy.
46 +- Use online knowledge search tools in a smart way to make it help you, try different prompts, be very specific, ask exactly what you need to know, not just vague query, ask for details, alternatives.
47 +- Do not get too deep into complicated solutions, try if your tools can give you the answer right away.
48 +- Prefer python code to console commands using subprocess. This way you get more meaningful output.
49 +- Sometimes you don't need tools, some things can be determined.
50 +- Make a good use of your memory tool.
51 +
52 +
53 +# Communication instructions
54 +- When you want a message to be visible to your superior agent (eg. when responding), use the speak_to_superior tool.
55 +- Messages without 'speak_to_superior' tool will only be visible to you for further processing.
56 +- When communicating with the superior agent or subordinate agents, be sure to include context relevant for the information you are sending.
57 +- When delegating new subtask to subordinate, use the 'reset' parameter set to True to reset subordinate's context and start fresh. When sending followup questions or instructions, do not set the flag to keep his previous context.
58 +- No other agent or user can read your responses, so use them to process your thoughts, do not use them to respond to superior agent.
59 +- When ready to finish the task, use the speak_to_superior tool to report back to the superior agent.
60 +- Never speak to superior agent just to let him know you work on something, only contact him when you are done or need something.
61 +- When superior agent refuses help, you have to manage on your own. Do your best to solve the problem.
62 +
63 +# Tool usage instructions
64 +- Tools can be used to communicate with superior and subordinate and to solve problems.
65 +- To use a tool, include pair XML tags <tool$> and </tool$> in your response. Use with attribute "name" of the tool and poteontial other attributes the tool accepts. The main input data (message, code, question) for the tool goes between <tool$> and </tool$> tags.
66 +- No escaping of the tool input is wanted.
67 +- In the following message you will receive output from that tool.
68 +- Only perform tool function calls after you have processed your thoughts and planned your actions step by step.
69 +- Only use tools provided in Available tools section, do not try to use any tool name you have not been instructed to.
70 +- You can only exetute Python code, so only search for solutions utilizing Python tools.
71 +
72 +## Tool usage generic example:
73 +<tool$ name="speak_to_subordinate" reset="false">
74 +Hello...
75 +</tool$>
\ No newline at end of file
prompts/agent.tools.md new
+74
@@ -0,0 +1,74 @@
1 +## Tools available:
2 +
3 +### speak_to_superior:
4 +Send message to your superior agent.
5 +Put the message for your superior (response, report, question) in between the tags.
6 +No additional arguments.
7 +Superior agent has context of your previous conversations.
8 +**Example usage**:
9 +<tool$ name="speak_to_superior">
10 +I am done.
11 +</tool$>
12 +
13 +### speak_to_subordinate:
14 +Send message to your subordinate and get a response.
15 +Use the tag body to enter a text message to your subordinate containing task description or follow-up instructions.
16 +The response will be returned in following message.
17 +Subordinate agent will have context of your previous conversation.
18 +When delegating a new task to subordinate, use the 'reset' argument set to true to reset subordinate's context and start fresh.
19 +**Example usage**:
20 +<tool$ name="speak_to_subordinate" reset="false">
21 +Try again.
22 +</tool$>
23 +
24 +### online_knowledge_tool:
25 +Provide question and get online response.
26 +Use the tag body to send a text message.
27 +The response will be returned in following message.
28 +This tool utilizes Perplexity AI search engine internally to get relevant response summarized from online sources.
29 +Be specific with your question, do not input vague queries.
30 +**Example usage**:
31 +<tool$ name="online_knowledge_tool">
32 +How to get current directory in Python?
33 +</tool$>
34 +
35 +### memory_tool:
36 +Access your persistent memory to load or save memories.
37 +Memories can help you to remember important information and later reuse it.
38 +With this you are able to learn and improve.
39 +Put the memory you need to load or save in the tag body.
40 +Use argument "action" with value "load", "save" or "delete", based on what you want to do.
41 +When loading memories using action "load", provide keywords or question relevant to your current task.
42 +The response will be returned in following message.
43 +When saving memories using action "save", provide a title, short summary and and all the necessary information to help you later solve similiar tasks.
44 +When deleting memories using action "delete", provide a prompt to search memories to delete.
45 +Be specific with your question, do not input vague queries.
46 +**Example usages**:
47 +<tool$ name="memory_tool" action="load">
48 +How to get current working directory in python?
49 +</tool$>
50 +
51 +<tool$ name="memory_tool" action="save">
52 +# How to get current working directory in python:
53 +Here is a python code to get current working directory:
54 +
55 +import os
56 +return os.getcwd()
57 +</tool$>
58 +
59 +
60 +
61 +### code_execution_tool:
62 +Execute provided python code.
63 +This tool can be used to achieve any task that requires computation, communication, processing or any other software related activity.
64 +Place your python code into the tag body. No escaping is wanted, maintain proper indentation.
65 +Using subprocess, you can install python packages and use any external library required.
66 +If you need to return output from this tool, you must use the "return" keyword in the code along with the output variable name. Otherwise nothing is returned.
67 +When tool outputs error, you need to change your code accordingly before trying again.
68 +Do not wrap code in any markdown or other formatting. Only provide plain python code.
69 +IMPORTANT!: Always check your code if it contains placeholders or demo data that need to be replaced with your real variables. Do not simply reuse code snippets from tutorials.
70 +**Example usage**:
71 +<tool$ name="code_execution_tool">
72 +import os
73 +return os.getcwd()
74 +</tool$>
prompts/fw.code_no_output.md new
+3
@@ -0,0 +1,3 @@
1 +No output or error was returned.
2 +If you require output from the tool, you have to use return [variable name] in your code.
3 +Otherwise proceed.
\ No newline at end of file
prompts/fw.error.md new
+2
@@ -0,0 +1,2 @@
1 +An error has occured due to your last message:
2 +{{error}}
\ No newline at end of file
prompts/fw.intervention.md new
+1
@@ -0,0 +1 @@
1 +INTERVENTION: {{user_message}}
\ No newline at end of file
prompts/fw.memories_deleted.md new
+1
@@ -0,0 +1 @@
1 +Memories deleted: {{memories}}
\ No newline at end of file
prompts/fw.memories_not_found.md new
+1
@@ -0,0 +1 @@
1 +No memories found for specified query: {{query}}
\ No newline at end of file
prompts/fw.memory_saved.md new
+1
@@ -0,0 +1 @@
1 +Memory has been saved.
\ No newline at end of file
prompts/fw.msg_cleanup.md new
+1
@@ -0,0 +1 @@
1 +NOTICE: Some messages here have been removed to save memory.
\ No newline at end of file
prompts/fw.msg_repeat.md new
+1
@@ -0,0 +1 @@
1 +I tried the same response twice. I have to do something else.
\ No newline at end of file
prompts/fw.msg_sent.md new
+1
@@ -0,0 +1 @@
1 +Message sent, wait for response.
\ No newline at end of file
prompts/fw.tool_not_found.md new
+2
@@ -0,0 +1,2 @@
1 +Tool {{tool_name}} not found. Available tools:
2 +{{tools_prompt}}
\ No newline at end of file
prompts/fw.tool_response.md new
+2
@@ -0,0 +1,2 @@
1 +Response from {{tool_name}} tool:
2 +{{tool_response}}
\ No newline at end of file
tools/.DS_Store
Binary files /dev/null and b/tools/.DS_Store differ
tools/__init__.py
tools/__pycache__/__init__.cpython-312.pyc
Binary files /dev/null and b/tools/__pycache__/__init__.cpython-312.pyc differ
tools/__pycache__/code_execution_tool.cpython-312.pyc
Binary files /dev/null and b/tools/__pycache__/code_execution_tool.cpython-312.pyc differ
tools/__pycache__/memory_tool.cpython-312.pyc
Binary files /dev/null and b/tools/__pycache__/memory_tool.cpython-312.pyc differ
tools/__pycache__/online_knowledge_tool.cpython-312.pyc
Binary files /dev/null and b/tools/__pycache__/online_knowledge_tool.cpython-312.pyc differ
tools/__pycache__/print_style.cpython-312.pyc
Binary files /dev/null and b/tools/__pycache__/print_style.cpython-312.pyc differ
tools/__pycache__/speak_to_subordinate.cpython-312.pyc
Binary files /dev/null and b/tools/__pycache__/speak_to_subordinate.cpython-312.pyc differ
tools/__pycache__/speak_to_superior.cpython-312.pyc
Binary files /dev/null and b/tools/__pycache__/speak_to_superior.cpython-312.pyc differ
tools/__pycache__/tool_perplexity_search.cpython-312.pyc
Binary files /dev/null and b/tools/__pycache__/tool_perplexity_search.cpython-312.pyc differ
tools/__pycache__/tools.cpython-312.pyc
Binary files /dev/null and b/tools/__pycache__/tools.cpython-312.pyc differ
tools/code_execution_tool.py new
+88
@@ -0,0 +1,88 @@
1 +import os, json, contextlib
2 +from io import StringIO
3 +from tools.helpers import files
4 +import ast
5 +from agent import Agent
6 +
7 +
8 +def execute(agent:Agent ,code_text:str, **kwargs):
9 +
10 + return execute_user_code(code_text)
11 +
12 + def execute_code(code_string, input=None):
13 + os.chdir(files.get_abs_path("./work_dir")) #change CWD to work_dir
14 + buffer = StringIO()
15 + local_vars = {} # {"input": input}
16 +
17 + with contextlib.redirect_stdout(buffer):
18 + try:
19 + indented_code = "\n ".join(code_string.strip().split("\n"))
20 + wrapped_code = f"""def isolate(input):\n {indented_code}"""
21 + exec(wrapped_code, None, local_vars) # exec(code_string, {"__builtins__": __builtins__}, local_vars)
22 + return local_vars.get('isolate', lambda: None)(input) # type: ignore # calling main if defined to get its return value
23 +
24 + except Exception as e:
25 + import traceback
26 + error_info = traceback.format_exc()
27 + return json.dumps({"error": str(e), "details": error_info})
28 + # return local_vars.get("output", buffer.getvalue())
29 +
30 + result = json.dumps(output) if (output := execute_code(code_text)) else files.read_file("./prompts/fw.code_no_output.md")
31 + return result
32 +
33 +
34 +
35 +def wrap_code_with_return_and_function(code):
36 + # Parse the code into an AST
37 + parsed_code = ast.parse(code)
38 + # Filter out only executable statements, ignoring comments and empty lines
39 + executable_statements = [stmt for stmt in parsed_code.body if not isinstance(stmt, ast.Pass)]
40 +
41 + if not executable_statements:
42 + raise Exception("There are no executable statements in the code.")
43 + else:
44 + # Get the last executable statement in the code
45 + last_statement = executable_statements[-1]
46 +
47 + # Check if the last statement is an expression (including function calls)
48 + if isinstance(last_statement, ast.Expr):
49 + # Convert the expression into a return statement
50 + return_stmt = ast.Return(value=last_statement.value)
51 + return_stmt.lineno = last_statement.lineno
52 + return_stmt.col_offset = last_statement.col_offset
53 + parsed_code.body[parsed_code.body.index(last_statement)] = return_stmt
54 +
55 + # Wrap the entire code in a function definition
56 + function_def = ast.FunctionDef(
57 + name="isolate",
58 + args=ast.arguments(
59 + posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[]
60 + ),
61 + body=parsed_code.body,
62 + decorator_list=[],
63 + lineno=1,
64 + col_offset=0
65 + ) # type: ignore
66 +
67 + # Create a new module with the function definition
68 + module = ast.Module(body=[function_def], type_ignores=[])
69 +
70 + # Convert the AST back to source code
71 + wrapped_code = compile(module, filename="<ast>", mode="exec")
72 + return wrapped_code
73 +def execute_user_code(code):
74 + try:
75 + wrapped_code = wrap_code_with_return_and_function(code)
76 + exec_globals = {}
77 + exec_locals = {}
78 + exec(wrapped_code, exec_globals, exec_locals)
79 + os.chdir(files.get_abs_path("./work_dir")) #change CWD to work_dir
80 + try:
81 + return exec_locals.get('isolate', lambda: None)()
82 + except Exception as e:
83 + import traceback
84 + error_info = traceback.format_exc()
85 + return json.dumps({"error": str(e), "details": error_info})
86 +
87 + except Exception as e:
88 + return "Error: " + str(e)
\ No newline at end of file
tools/helpers/__pycache__/extract_tools.cpython-312.pyc
Binary files /dev/null and b/tools/helpers/__pycache__/extract_tools.cpython-312.pyc differ
tools/helpers/__pycache__/files.cpython-312.pyc
Binary files /dev/null and b/tools/helpers/__pycache__/files.cpython-312.pyc differ
tools/helpers/__pycache__/perplexity_search.cpython-312.pyc
Binary files /dev/null and b/tools/helpers/__pycache__/perplexity_search.cpython-312.pyc differ
tools/helpers/__pycache__/print_style.cpython-312.pyc
Binary files /dev/null and b/tools/helpers/__pycache__/print_style.cpython-312.pyc differ
tools/helpers/__pycache__/rate_limiter.cpython-312.pyc
Binary files /dev/null and b/tools/helpers/__pycache__/rate_limiter.cpython-312.pyc differ
tools/helpers/__pycache__/vector_db.cpython-312.pyc
Binary files /dev/null and b/tools/helpers/__pycache__/vector_db.cpython-312.pyc differ
tools/helpers/extract_tools.py new
+86
@@ -0,0 +1,86 @@
1 +import re
2 +
3 +def extract_tool_requests(response):
4 + # Regex to match the tool blocks
5 + pattern = r'<tool\$(.*?)>(.*?)</tool\$>'
6 + matches = re.findall(pattern, response, re.DOTALL)
7 +
8 + tool_usages = []
9 +
10 + for match in matches:
11 + attributes, body = match
12 + tool_dict = {}
13 + # Parse attributes
14 + for attr in re.findall(r'(\w+)="([^"]+)"', attributes):
15 + tool_dict[attr[0]] = attr[1]
16 + # Add body content
17 + tool_dict["body"] = body.strip()
18 + tool_usages.append(tool_dict)
19 +
20 + return tool_usages
21 +
22 +# import re
23 +# from xml.etree import ElementTree as ET
24 +
25 +# def extract_tool_usages_advanced(response):
26 +# tool_usages = []
27 +# pattern = re.compile(r'<tool.*?>', re.DOTALL)
28 +
29 +# start_pos = 0
30 +# while start_pos < len(response):
31 +# match = pattern.search(response, start_pos)
32 +# if not match:
33 +# break
34 +
35 +# tag_start = match.start()
36 +# tag_end = match.end()
37 +# end_tag = '</tool>'
38 +
39 +# # To find the corresponding end tag correctly handling nested tags
40 +# depth = 1
41 +# search_pos = tag_end
42 +
43 +# while depth > 0:
44 +# next_open = response.find('<tool', search_pos)
45 +# next_close = response.find(end_tag, search_pos)
46 +
47 +# if next_close == -1:
48 +# break
49 +
50 +# if next_open != -1 and next_open < next_close:
51 +# depth += 1
52 +# search_pos = next_open + len('<tool')
53 +# else:
54 +# depth -= 1
55 +# search_pos = next_close + len(end_tag)
56 +
57 +# end_tag_end = search_pos
58 +
59 +# # Extract the whole tool block
60 +# tool_block = response[tag_start:end_tag_end]
61 +
62 +# try:
63 +# element = ET.fromstring(tool_block)
64 +# tool_dict = element.attrib
65 +# tool_dict["body"] = ET.tostring(element, encoding='unicode', method='xml').split('>', 1)[1].rsplit('<', 1)[0].strip()
66 +# tool_usages.append(tool_dict)
67 +# except ET.ParseError:
68 +# # In case of parsing error, fall back to including entire content between the tags
69 +# body_content = response[tag_end:end_tag_end - len(end_tag)].strip()
70 +# tool_dict = {"name": re.search(r'name="(.*?)"', match.group(0)).group(1), "body": body_content}
71 +# tool_usages.append(tool_dict)
72 +
73 +# start_pos = end_tag_end
74 +
75 +# return tool_usages
76 +
77 +# # Example usage with the given input
78 +# response = """
79 +# <tool name="code_execution_tool">
80 +# #comment <tool<tool name="abc><tool><loot><tool>"
81 +# print(text)
82 +# </tool>
83 +# """
84 +
85 +# tool_usages = extract_tool_usages(response)
86 +# print(tool_usages)
\ No newline at end of file
tools/helpers/files.py new
+25
@@ -0,0 +1,25 @@
1 +import os, re, sys
2 +
3 +def read_file(relative_path, **kwargs):
4 + absolute_path = get_abs_path(relative_path) # Construct the absolute path to the target file
5 +
6 + with open(absolute_path) as f:
7 + content = f.read()
8 +
9 + # Replace placeholders with values from kwargs
10 + for key, value in kwargs.items():
11 + placeholder = "{{" + key + "}}"
12 + strval = str(value)
13 + # strval = strval.encode('unicode_escape').decode('utf-8')
14 + # content = re.sub(re.escape(placeholder), strval, content)
15 + content = content.replace(placeholder, strval)
16 +
17 + return content
18 +
19 +def get_abs_path(*relative_paths):
20 + return os.path.join(get_base_dir(), *relative_paths)
21 +
22 +def get_base_dir():
23 + # Get the base directory from the current file path
24 + base_dir = os.path.dirname(os.path.abspath(os.path.join(__file__,"../../")))
25 + return base_dir
\ No newline at end of file
tools/helpers/perplexity_search.py new
+100
@@ -0,0 +1,100 @@
1 +
2 +import requests
3 +
4 +from langchain.llms import BaseLLM # type: ignore
5 +from langchain_core.callbacks import CallbackManagerForLLMRun
6 +from langchain_core.outputs.llm_result import LLMResult
7 +from typing import List, Optional, Any
8 +from openai import OpenAI
9 +import os
10 +
11 +
12 +api_key_from_env = os.getenv("API_KEY_PERPLEXITY")
13 +
14 +class PerplexityCrewLLM(BaseLLM):
15 + api_key: str
16 + model_name: str
17 +
18 + def call_perplexity_ai(self, prompt: str) -> LLMResult:
19 + url = "https://api.perplexity.ai/chat/completions"
20 +
21 + payload = {
22 + "model": self.model_name,
23 + "messages": [
24 + {
25 + "role": "system",
26 + "content": "Be precise and concise."
27 + },
28 + {
29 + "role": "user",
30 + "content": prompt
31 + }
32 + ]
33 + }
34 + headers = {
35 + "Authorization": f"Bearer {self.api_key}",
36 + "accept": "application/json",
37 + "content-type": "application/json"
38 + }
39 +
40 + response = requests.post(url, json=payload, headers=headers)
41 +
42 + # Convert the response JSON to dictionary
43 + json_response = response.json()
44 +
45 + return json_response
46 +
47 + def _generate(self, prompts: List[str], stop: Optional[List[str]] = None,
48 + run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any) -> LLMResult:
49 + generations = []
50 + for prompt in prompts:
51 + generations.append([self._call(prompt, stop=stop, **kwargs)])
52 + return LLMResult.construct(generations=generations)
53 +
54 + def _call(self, prompt: str, stop: Optional[List[str]] = None, max_tokens: Optional[int] = None) -> LLMResult:
55 + response_data = self.call_perplexity_ai(prompt)
56 + model = LLMResult.construct(text=response_data['choices'][0]["message"]["content"]) # type: ignore
57 +
58 + return model
59 +
60 + @property
61 + def _llm_type(self) -> str:
62 + return "PerplexityAI"
63 +
64 +
65 +def PerplexitySearchLLM(api_key,model_name="sonar-medium-online",base_url="https://api.perplexity.ai"):
66 + client = OpenAI(api_key=api_key_from_env, base_url=base_url)
67 +
68 + def call_model(query:str):
69 + messages = [
70 + #It is recommended to use only single-turn conversations and avoid system prompts for the online LLMs (sonar-small-online and sonar-medium-online).
71 +
72 + # {
73 + # "role": "system",
74 + # "content": (
75 + # "You are an artificial intelligence assistant and you need to "
76 + # "engage in a helpful, detailed, polite conversation with a user."
77 + # ),
78 + # },
79 + {
80 + "role": "user",
81 + "content": (
82 + query
83 + ),
84 + },
85 + ]
86 +
87 + response = client.chat.completions.create(
88 + model=model_name,
89 + messages=messages, # type: ignore
90 + )
91 + result = response.choices[0].message.content #only the text is returned
92 + return result
93 +
94 + return call_model
95 +
96 +
97 +call_llm = PerplexitySearchLLM(api_key=api_key_from_env,model_name="sonar-medium-online")
98 +
99 +def perplexity_search(search_query: str):
100 + return call_llm(search_query)
\ No newline at end of file
tools/helpers/print_style.py new
+68
@@ -0,0 +1,68 @@
1 +import webcolors
2 +
3 +class PrintStyle:
4 + last_endline=True
5 +
6 + def __init__(self, bold=False, italic=False, underline=False, font_color="default", background_color="default", padding=False):
7 + self.bold = bold
8 + self.italic = italic
9 + self.underline = underline
10 + self.font_color = font_color
11 + self.background_color = background_color
12 + self.padding = padding
13 + self.padding_added = False # Flag to track if padding was added
14 +
15 + def _get_rgb_color_code(self, color, is_background=False):
16 + try:
17 + if color.startswith("#") and len(color) == 7:
18 + # Convert hex color to RGB
19 + r = int(color[1:3], 16)
20 + g = int(color[3:5], 16)
21 + b = int(color[5:7], 16)
22 + else:
23 + # Convert named color to RGB
24 + rgb_color = webcolors.name_to_rgb(color)
25 + r, g, b = rgb_color.red, rgb_color.green, rgb_color.blue
26 +
27 + if is_background:
28 + return f"\033[48;2;{r};{g};{b}m"
29 + else:
30 + return f"\033[38;2;{r};{g};{b}m"
31 + except ValueError:
32 + # Fallback to default color
33 + return "\033[49m" if is_background else "\033[39m"
34 +
35 + def _get_styled_text(self, text):
36 + start = ""
37 + end = "\033[0m" # Reset ANSI code
38 + if self.bold:
39 + start += "\033[1m"
40 + if self.italic:
41 + start += "\033[3m"
42 + if self.underline:
43 + start += "\033[4m"
44 + start += self._get_rgb_color_code(self.font_color)
45 + start += self._get_rgb_color_code(self.background_color,True)
46 + return start + text + end
47 +
48 + def _add_padding_if_needed(self):
49 + if self.padding and not self.padding_added:
50 + print() # Print an empty line for padding
51 + if not PrintStyle.last_endline: print() # add one more if last print was streamed
52 + self.padding_added = True
53 +
54 + def get(self, *args, sep=' ', **kwargs):
55 + text = sep.join(map(str, args))
56 + return self._get_styled_text(text)
57 +
58 + def print(self, *args, sep=' ', **kwargs):
59 + self._add_padding_if_needed()
60 + styled_text = self.get(*args, sep=sep, **kwargs)
61 + print(styled_text, end='\n', flush=True)
62 + PrintStyle.last_endline = True
63 +
64 + def stream(self, *args, sep=' ', **kwargs):
65 + self._add_padding_if_needed()
66 + styled_text = self.get(*args, sep=sep, **kwargs)
67 + print(styled_text, end='', flush=True)
68 + PrintStyle.last_endline = False
\ No newline at end of file
tools/helpers/rate_limiter.py new
+36
@@ -0,0 +1,36 @@
1 +import time
2 +from collections import deque
3 +from .print_style import PrintStyle
4 +
5 +def rate_limiter(max_requests_per_minute, max_tokens_per_minute):
6 + execution_times = deque()
7 + token_counts = deque()
8 +
9 + def limit(tokens):
10 + if tokens > max_tokens_per_minute:
11 + raise ValueError("Number of tokens exceeds the maximum allowed per minute.")
12 +
13 + current_time = time.time()
14 +
15 + # Cleanup old execution times and token counts
16 + while execution_times and current_time - execution_times[0] > 60:
17 + execution_times.popleft()
18 + token_counts.popleft()
19 +
20 + total_tokens = sum(token_counts)
21 +
22 + if len(execution_times) < max_requests_per_minute and total_tokens + tokens <= max_tokens_per_minute:
23 + execution_times.append(current_time)
24 + token_counts.append(tokens)
25 + else:
26 + sleep_time = max(
27 + 60 - (current_time - execution_times[0]),
28 + 60 - (current_time - execution_times[0]) if total_tokens + tokens > max_tokens_per_minute else 0
29 + )
30 + PrintStyle(font_color="yellow", padding=True).print(f"Rate limiter: sleeping for {sleep_time} seconds...")
31 + time.sleep(sleep_time)
32 + current_time = time.time()
33 + execution_times.append(current_time)
34 + token_counts.append(tokens)
35 +
36 + return limit
\ No newline at end of file
tools/helpers/vector_db.py new
+67
@@ -0,0 +1,67 @@
1 +from langchain.storage import InMemoryByteStore, LocalFileStore
2 +from langchain.embeddings import CacheBackedEmbeddings
3 +from langchain_chroma import Chroma
4 +from . import files
5 +from langchain_core.documents import Document
6 +import uuid
7 +
8 +
9 +class VectorDB:
10 +
11 + def __init__(self, embeddings_model, in_memory=False, cache_dir="./cache"):
12 + print("Initializing VectorDB...")
13 + self.embeddings_model = embeddings_model
14 +
15 + em_cache = files.get_abs_path(cache_dir,"embeddings")
16 + db_cache = files.get_abs_path(cache_dir,"database")
17 +
18 + if in_memory:
19 + self.store = InMemoryByteStore()
20 + else:
21 + self.store = LocalFileStore(em_cache)
22 +
23 +
24 + #here we setup the embeddings model with the chosen cache storage
25 + self.embedder = CacheBackedEmbeddings.from_bytes_store(
26 + embeddings_model,
27 + self.store,
28 + namespace=getattr(embeddings_model, 'model', getattr(embeddings_model, 'model_name', "default")) )
29 +
30 + self.db = Chroma(embedding_function=self.embedder,persist_directory=db_cache)
31 +
32 + def search_similarity(self, query, results=3):
33 + return self.db.similarity_search(query,results)
34 +
35 + def search_max_rel(self, query, results=3):
36 + return self.db.max_marginal_relevance_search(query,results)
37 +
38 + def delete_documents(self, query):
39 + score_limit = 1
40 + k = 2
41 + tot = 0
42 + while True:
43 + # Perform similarity search with score
44 + docs = self.db.similarity_search_with_score(query, k=k)
45 +
46 + # Extract document IDs and filter based on score
47 + document_ids = [result[0].metadata["id"] for result in docs if result[1] < score_limit]
48 +
49 + # Delete documents with IDs over the threshold score
50 + if document_ids:
51 + fnd = self.db.get(where={"id": {"$in": document_ids}})
52 + if fnd["ids"]: self.db.delete(ids=fnd["ids"])
53 + tot += len(fnd["ids"])
54 +
55 + # If fewer than K document IDs, break the loop
56 + if len(document_ids) < k:
57 + break
58 +
59 + return tot
60 +
61 + def insert_document(self, data):
62 + id = str(uuid.uuid4())
63 + self.db.add_documents(documents=[ Document(data, metadata={"id": id}) ])
64 + return id
65 +
66 +
67 +
tools/memory_tool.py new
+32
@@ -0,0 +1,32 @@
1 +from agent import Agent
2 +from tools.helpers.vector_db import VectorDB, Document
3 +from tools.helpers import files
4 +import os, json
5 +
6 +db: VectorDB
7 +result_count = 3
8 +
9 +def initialize(embeddings_model,messages_returned=3, subdir=""):
10 + global db, result_count
11 + dir = os.path.join("memory",subdir)
12 + db = VectorDB(embeddings_model=embeddings_model, in_memory=False, cache_dir=dir)
13 + result_count = messages_returned
14 +
15 +def execute(agent:Agent, message: str, action: str = "load", **kwargs):
16 + if action.strip().lower() == "save":
17 + id = db.insert_document(message)
18 + return files.read_file("./prompts/fw.memory_saved.md")
19 +
20 + elif action.strip().lower() == "delete":
21 + deleted = db.delete_documents(message)
22 + return files.read_file("./prompts/fw.memories_deleted.md", count=deleted)
23 +
24 + else:
25 + results=[]
26 + docs = db.search_max_rel(message,result_count)
27 + if len(docs)==0: return files.read_file("./prompts/fw.memories_not_found.md", query=message)
28 + for doc in docs:
29 + results.append({ "meta": doc.metadata, "content": doc.page_content })
30 + return json.dumps(results)
31 +
32 +
tools/online_knowledge_tool.py new
+6
@@ -0,0 +1,6 @@
1 +from agent import Agent
2 +from tools.helpers import perplexity_search
3 +
4 +def execute(agent:Agent, question:str, **kwargs):
5 + return perplexity_search.perplexity_search(question)
6 +
\ No newline at end of file
tools/speak_to_subordinate.py new
+6
@@ -0,0 +1,6 @@
1 +from agent import Agent
2 +
3 +def execute(agent:Agent, message: str, reset: str = "false", **kwargs):
4 + if agent.subordinate is None or reset.lower() == "true":
5 + agent.subordinate = Agent(superior=agent, system_prompt=agent.system_prompt, tools_prompt=agent.tools_prompt, number=agent.number+1)
6 + return agent.subordinate.process_message(message)
\ No newline at end of file
tools/speak_to_superior.py new
+6
@@ -0,0 +1,6 @@
1 +from agent import Agent
2 +from tools.helpers import files
3 +
4 +def execute(agent:Agent, message: str, **kwargs):
5 + agent.message_for_superior = message
6 + return files.read_file("./prompts/fw.msg_sent.md")
\ No newline at end of file