Init commit: migrate JarvisFlow from Tachi67/JarvisFlowModule to aiflows
Browse files- Controller_JarvisFlow.py +159 -0
- Controller_JarvisFlow.yaml +155 -0
- CtrlExMem_JarvisFlow.py +68 -0
- CtrlExMem_JarvisFlow.yaml +223 -0
- FinalAns_Jarvis.py +35 -0
- FinalAns_Jarvis.yaml +9 -0
- IntermediateAns_Jarvis.py +36 -0
- IntermediateAns_Jarvis.yaml +9 -0
- Introduction_to_Jarvis.md +328 -0
- JarvisFlow.py +32 -0
- JarvisFlow.yaml +182 -0
- Planner_JarvisFlow.py +64 -0
- Planner_JarvisFlow.yaml +117 -0
- README.md +234 -0
- UpdatePlanAtomicFlow.py +34 -0
- UpdatePlanAtomicFlow.yaml +9 -0
- __init__.py +23 -0
- pip_requirements.txt +15 -0
Controller_JarvisFlow.py
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
from copy import deepcopy
|
3 |
+
from typing import Any, Dict, List
|
4 |
+
|
5 |
+
from flow_modules.aiflows.ChatFlowModule import ChatAtomicFlow
|
6 |
+
|
7 |
+
|
8 |
+
from dataclasses import dataclass
|
9 |
+
|
10 |
+
|
11 |
+
@dataclass
|
12 |
+
class Command:
|
13 |
+
name: str
|
14 |
+
description: str
|
15 |
+
input_args: List[str]
|
16 |
+
|
17 |
+
# TODO: controller should be generalized
|
18 |
+
class Controller_JarvisFlow(ChatAtomicFlow):
|
19 |
+
"""This class is a controller for JarvisFlow, it takes the plan generated by the planner, logs of previous executions,
|
20 |
+
depending on the initial goal or the subsequent feedback from the branching executors (and the human), to decide which
|
21 |
+
executor to call next (or to exit by calling finish).
|
22 |
+
|
23 |
+
*Configuration Parameters*:
|
24 |
+
- `commands` (dict): a dictionary of commands that the controller can call, each command has a name, a description, and a list of input arguments.
|
25 |
+
The commands will be injected into the system message prompt template.
|
26 |
+
- `system_message_prompt_template` (str): the template for the system message prompt, there are several components needs to be injected into the
|
27 |
+
template, including the commands, plan, plan_file_location, logs, and the goal. The injection of commands is done then initalizing the flow,
|
28 |
+
the rest of the components are injected at the beginning of each run.
|
29 |
+
- `previous_messages` (int): a sliding window of previous messages that will be passed to the model. This is the central part of short-term memory management.
|
30 |
+
|
31 |
+
*Input Interface Non Initialized*:
|
32 |
+
- `goal` (str): the initial goal of the conversation, this is the input to the model.
|
33 |
+
- `memory_files` (dict): a dictionary of file locations that contains the plan, logs.
|
34 |
+
- `plan` (str): the plan generated by the planner, the plan will change (marked as done, or re-plan) as execution preceeds.
|
35 |
+
- `logs` (str): the logs of previous executions, the logs will be appended as execution preceeds.
|
36 |
+
|
37 |
+
*Input Interface Initialized*:
|
38 |
+
- `result` (str): the result of the previous execution, this is the input to the model.
|
39 |
+
- `memory_files` (dict): a dictionary of file locations that contains the plan, logs.
|
40 |
+
- `plan` (str): the plan generated by the planner, the plan will change (marked as done, or re-plan) as execution preceeds.
|
41 |
+
- `logs` (str): the logs of previous executions, the logs will be appended as execution preceeds.
|
42 |
+
- `goal` (str): the initial goal, this is kept because the goal is also injected into the system prompts so that Jarvis does not
|
43 |
+
forget what the goal is, when the memory sliding window is implemented.
|
44 |
+
|
45 |
+
*Output Interface*:
|
46 |
+
- `command` (str): the command to be executed by the executor.
|
47 |
+
- `command_args` (dict): the arguments of the command to be executed by the executor.
|
48 |
+
"""
|
49 |
+
def __init__(
|
50 |
+
self,
|
51 |
+
commands: List[Command],
|
52 |
+
**kwargs):
|
53 |
+
"""Initialize the flow, inject the commands into the system message prompt template."""
|
54 |
+
super().__init__(**kwargs)
|
55 |
+
self.system_message_prompt_template = self.system_message_prompt_template.partial(
|
56 |
+
commands=self._build_commands_manual(commands),
|
57 |
+
plan="no plans yet",
|
58 |
+
plan_file_location="no plan file location yet",
|
59 |
+
logs="no logs yet",
|
60 |
+
)
|
61 |
+
self.hint_for_model = """
|
62 |
+
Make sure your response is in the following format:
|
63 |
+
Response Format:
|
64 |
+
{
|
65 |
+
"command": "call one of the subordinates",
|
66 |
+
"command_args": {
|
67 |
+
"arg name": "value"
|
68 |
+
}
|
69 |
+
}
|
70 |
+
"""
|
71 |
+
|
72 |
+
def _get_content_file_location(self, input_data, content_name):
|
73 |
+
# get the location of the file that contains the content: plan, logs, code_library
|
74 |
+
assert "memory_files" in input_data, "memory_files not passed to Jarvis/Controller"
|
75 |
+
assert content_name in input_data["memory_files"], f"{content_name} not in memory files"
|
76 |
+
return input_data["memory_files"][content_name]
|
77 |
+
|
78 |
+
def _get_content(self, input_data, content_name):
|
79 |
+
# get the content of the file that contains the content: plan, logs, code_library
|
80 |
+
assert content_name in input_data, f"{content_name} not passed to Jarvis/Controller"
|
81 |
+
content = input_data[content_name]
|
82 |
+
if len(content) == 0:
|
83 |
+
content = f'No {content_name} yet'
|
84 |
+
return content
|
85 |
+
@staticmethod
|
86 |
+
def _build_commands_manual(commands: List[Command]) -> str:
|
87 |
+
"""Build the manual for the commands."""
|
88 |
+
ret = ""
|
89 |
+
for i, command in enumerate(commands):
|
90 |
+
command_input_json_schema = json.dumps(
|
91 |
+
{input_arg: f"YOUR_{input_arg.upper()}" for input_arg in command.input_args})
|
92 |
+
ret += f"{i + 1}. {command.name}: {command.description} Input arguments (given in the JSON schema): {command_input_json_schema}\n"
|
93 |
+
return ret
|
94 |
+
|
95 |
+
|
96 |
+
@classmethod
|
97 |
+
def instantiate_from_config(cls, config):
|
98 |
+
"""Setting up the flow from the config file. In particular, setting up the prompts, backend, and commands."""
|
99 |
+
flow_config = deepcopy(config)
|
100 |
+
|
101 |
+
kwargs = {"flow_config": flow_config}
|
102 |
+
|
103 |
+
# ~~~ Set up prompts ~~~
|
104 |
+
kwargs.update(cls._set_up_prompts(flow_config))
|
105 |
+
|
106 |
+
# ~~~Set up backend ~~~
|
107 |
+
kwargs.update(cls._set_up_backend(flow_config))
|
108 |
+
|
109 |
+
# ~~~ Set up commands ~~~
|
110 |
+
commands = flow_config["commands"]
|
111 |
+
commands = [
|
112 |
+
Command(name, command_conf["description"], command_conf["input_args"]) for name, command_conf in
|
113 |
+
commands.items()
|
114 |
+
]
|
115 |
+
kwargs.update({"commands": commands})
|
116 |
+
|
117 |
+
# ~~~ Instantiate flow ~~~
|
118 |
+
return cls(**kwargs)
|
119 |
+
|
120 |
+
def _update_prompts_and_input(self, input_data: Dict[str, Any]):
|
121 |
+
"""Hinting the model to output in json format, updating the plan, logs to the system prompts."""
|
122 |
+
if 'goal' in input_data:
|
123 |
+
input_data['goal'] += self.hint_for_model
|
124 |
+
if 'result' in input_data:
|
125 |
+
input_data['result'] += self.hint_for_model
|
126 |
+
plan_file_location = self._get_content_file_location(input_data, "plan")
|
127 |
+
plan_content = self._get_content(input_data, "plan")
|
128 |
+
logs_content = self._get_content(input_data, "logs")
|
129 |
+
self.system_message_prompt_template = self.system_message_prompt_template.partial(
|
130 |
+
plan_file_location=plan_file_location,
|
131 |
+
plan=plan_content,
|
132 |
+
logs=logs_content
|
133 |
+
)
|
134 |
+
|
135 |
+
def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
|
136 |
+
self._update_prompts_and_input(input_data)
|
137 |
+
|
138 |
+
# ~~~when conversation is initialized, append the updated system prompts to the chat history ~~~
|
139 |
+
if self._is_conversation_initialized():
|
140 |
+
updated_system_message_content = self._get_message(self.system_message_prompt_template, input_data)
|
141 |
+
self._state_update_add_chat_message(content=updated_system_message_content,
|
142 |
+
role=self.flow_config["system_name"])
|
143 |
+
|
144 |
+
# ~~~run the model, special mechanism to deal with situations where the output is not in json format. ~~~
|
145 |
+
while True:
|
146 |
+
api_output = super().run(input_data)["api_output"].strip()
|
147 |
+
try:
|
148 |
+
start = api_output.index("{")
|
149 |
+
end = api_output.rindex("}") + 1
|
150 |
+
json_str = api_output[start:end]
|
151 |
+
return json.loads(json_str)
|
152 |
+
except (ValueError, json.decoder.JSONDecodeError, json.JSONDecodeError):
|
153 |
+
updated_system_message_content = self._get_message(self.system_message_prompt_template, input_data)
|
154 |
+
self._state_update_add_chat_message(content=updated_system_message_content,
|
155 |
+
role=self.flow_config["system_name"])
|
156 |
+
new_goal = "The previous respond cannot be parsed with json.loads. Next time, do not provide any comments or code blocks. Make sure your next response is purely json parsable."
|
157 |
+
new_input_data = input_data.copy()
|
158 |
+
new_input_data['result'] = new_goal
|
159 |
+
input_data = new_input_data
|
Controller_JarvisFlow.yaml
ADDED
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: Tachi67.JarvisFlowModule.Controller_JarvisFlow.instantiate_from_default_config
|
2 |
+
name: "ControllerFlow_JarvisFlow"
|
3 |
+
description: "Proposes the next action to take towards achieving the goal, and prepares the input for the branching flow"
|
4 |
+
enable_cache: True
|
5 |
+
|
6 |
+
#######################################################
|
7 |
+
# Input keys
|
8 |
+
#######################################################
|
9 |
+
|
10 |
+
input_interface_non_initialized: # initial input keys
|
11 |
+
- "goal"
|
12 |
+
- "memory_files"
|
13 |
+
- "plan"
|
14 |
+
- "logs"
|
15 |
+
|
16 |
+
input_interface_initialized:
|
17 |
+
- "goal"
|
18 |
+
- "result"
|
19 |
+
- "memory_files"
|
20 |
+
- "plan"
|
21 |
+
- "logs"
|
22 |
+
|
23 |
+
#######################################################
|
24 |
+
# Output keys
|
25 |
+
#######################################################
|
26 |
+
|
27 |
+
output_interface:
|
28 |
+
- 'command'
|
29 |
+
- 'command_args'
|
30 |
+
|
31 |
+
backend:
|
32 |
+
api_infos: ???
|
33 |
+
model_name:
|
34 |
+
openai: gpt-4
|
35 |
+
azure: azure/gpt-4
|
36 |
+
|
37 |
+
commands:
|
38 |
+
Coder:
|
39 |
+
description: "Instruct the coder to write and run code to finish your given goal."
|
40 |
+
input_args: [ "goal" ]
|
41 |
+
re_plan:
|
42 |
+
description: "When something is wrong with current plan, draft another plan based on the old plan and information about why it's bad or how to refine it."
|
43 |
+
input_args: [ "goal" ]
|
44 |
+
finish:
|
45 |
+
description: "Signal that the objective has been satisfied, return the summary of what was done"
|
46 |
+
input_args: [ "summary" ]
|
47 |
+
manual_finish:
|
48 |
+
description: "The user demands to quit and terminate the current process"
|
49 |
+
input_args: [ ]
|
50 |
+
ask_user:
|
51 |
+
description: "Ask user a question for confirmation or assistance"
|
52 |
+
input_args: [ "question" ]
|
53 |
+
update_plan:
|
54 |
+
description: "When one step of the plan is done, pass the updated plan to edit plan file and override current plan"
|
55 |
+
input_args: [ "updated_plan" ]
|
56 |
+
intermediate_answer:
|
57 |
+
description: "When one step of the plan is done, pass the result from the step as an intermediate answer to the user"
|
58 |
+
input_args: [ "answer" ]
|
59 |
+
final_answer:
|
60 |
+
description: "When the goal is achieved, pass the result from the goal as a final answer to the user"
|
61 |
+
input_args: [ "answer" ]
|
62 |
+
|
63 |
+
|
64 |
+
system_message_prompt_template:
|
65 |
+
_target_: langchain.PromptTemplate
|
66 |
+
template: |2-
|
67 |
+
Your department is in charge of achieving a particular goal by writing and running code. You are the leader of the department.
|
68 |
+
|
69 |
+
Here is the goal you need to achieve:
|
70 |
+
{{goal}}
|
71 |
+
|
72 |
+
You work with several subordinates, they will be in charge of the specific tasks, including writing and running code to achieve a goal given specifically by you, re-planning, etc.
|
73 |
+
|
74 |
+
To call one of the subordinates, you need to call the corresponding command with necessary arguments, here are the commands and their descriptions:
|
75 |
+
{{commands}}
|
76 |
+
|
77 |
+
You are given a step-by-step plan to finish the goal, **make sure you follow the plan**, **notice that, if you are informed that the plan is overriden, this plan is the new plan you should stick to.** The plan is stored at {{plan_file_location}}. Here is the plan:
|
78 |
+
{{plan}}
|
79 |
+
|
80 |
+
You should execute the plan step-by-step, for each step, you should do the following workflows:
|
81 |
+
0.1 Whenever the user demands to quit or terminate the current process, call `manual_finish` command.
|
82 |
+
0.2 Whenever in doubt, or you have something to ask, or confirm to the user, call `ask_user` with your question.
|
83 |
+
0.3 During the execution of the plan, if something goes wrong, call the `re_plan` with detailed information about what was wrong.
|
84 |
+
1. If the current step of plan can be achieved by writing and running code, call `Coder` with the goal of the current step of plan.
|
85 |
+
2. Whenever you have done one step of the plan, do the following:
|
86 |
+
2.1 Reflect on what plan you are having right now.
|
87 |
+
2.2 Reflect on which step of the plan you just finished.
|
88 |
+
2.3 Generate a plan, **it is exactly the same as the plan you have now, but with the current step of plan marked as done**
|
89 |
+
2.4 Call `update_plan` with the plan you just generated.
|
90 |
+
3. After you finish one step of the plan and have called `update_plan`, call `intermediate_answer` with the result of the step you just finished.
|
91 |
+
4. The user will provide you with feedback on the last step executed, react accordingly.
|
92 |
+
5. If the user is happy with the intermediate result, proceed to the next step of the plan, go back to workflow 1.
|
93 |
+
5. When every step of the plan is done:
|
94 |
+
5.1 Call `final_answer` with the result of the goal.
|
95 |
+
5.2 React to the user's feedback, if the user is not happy, you may want to re-plan or give new instructions to the coder.
|
96 |
+
5.3 If the user is happy, call `finish` with a summary of what was done throughout the process.
|
97 |
+
|
98 |
+
**You MUST call `update_plan` whenever you realize one step of the plan is done.**
|
99 |
+
**You MUST call `finish` whenever everything is done and the user is happy.**
|
100 |
+
|
101 |
+
Here is an example of execution:
|
102 |
+
### Beginning of an example execution: ###
|
103 |
+
Plan: 1. Download the weather data from Paris on 19. Dec. 2021. 2. Send the weather data to an email address. 3. Give a final answer.
|
104 |
+
Your actions:
|
105 |
+
1. Call `Coder` with the goal of the first step of the plan. Code is written and run, the weather data is downloaded.
|
106 |
+
2. You call `update_plan` with the plan you just generated, it is exactly the same as the plan you have now, but with the current step of plan marked as done.
|
107 |
+
3. You call `intermediate_answer` with the result of the step you just finished.
|
108 |
+
4. The user is happy with the intermediate result, you proceed to the next step of the plan.
|
109 |
+
5. Call `Coder` with the goal of the second step of the plan. Code is written and run, the weather data is sent to an email address.
|
110 |
+
6. You call `update_plan` with the plan you just generated, it is exactly the same as the plan you have now, but with the current step of plan marked as done.
|
111 |
+
7. You call `intermediate_answer` with the result of the step you just finished.
|
112 |
+
8. The user is happy with the intermediate result, you proceed to the next step of the plan.
|
113 |
+
9. Call `final_answer` with the result of the goal.
|
114 |
+
10. The user is happy with the final result, you call `finish` with a summary of what was done throughout the process.
|
115 |
+
### End of an example of execution ###
|
116 |
+
|
117 |
+
Here is a list of history actions you have taken, for your reference:
|
118 |
+
{{logs}}
|
119 |
+
|
120 |
+
Constraints:
|
121 |
+
1. Exclusively use the commands listed in double quotes e.g. "command name"
|
122 |
+
|
123 |
+
Your response **MUST** be in the following format:
|
124 |
+
Response Format:
|
125 |
+
{
|
126 |
+
"command": "call one of the subordinates",
|
127 |
+
"command_args": {
|
128 |
+
"arg name": "value"
|
129 |
+
}
|
130 |
+
}
|
131 |
+
Ensure your responses can be parsed by Python json.loads
|
132 |
+
|
133 |
+
input_variables: ["commands", "plan", "logs", "plan_file_location", "goal"]
|
134 |
+
template_format: jinja2
|
135 |
+
|
136 |
+
human_message_prompt_template:
|
137 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
138 |
+
template: |2-
|
139 |
+
Here is the result of your previous action:
|
140 |
+
{{result}}
|
141 |
+
input_variables:
|
142 |
+
- "result"
|
143 |
+
template_format: jinja2
|
144 |
+
|
145 |
+
init_human_message_prompt_template:
|
146 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
147 |
+
template: |2-
|
148 |
+
Here is the goal you need to achieve, follow your plan to finish the goal:
|
149 |
+
{{goal}}
|
150 |
+
input_variables:
|
151 |
+
- "goal"
|
152 |
+
template_format: jinja2
|
153 |
+
|
154 |
+
previous_messages:
|
155 |
+
last_k: 3
|
CtrlExMem_JarvisFlow.py
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Dict, Any
|
2 |
+
|
3 |
+
from flow_modules.Tachi67.AbstractBossFlowModule import CtrlExMemFlow
|
4 |
+
from aiflows.base_flows import CircularFlow
|
5 |
+
|
6 |
+
|
7 |
+
class CtrlExMem_JarvisFlow(CtrlExMemFlow):
|
8 |
+
"""This class inherits from the CtrlExMemFlow class from AbstractBossFlowModule.
|
9 |
+
See: https://huggingface.co/Tachi67/AbstractBossFlowModule/blob/main/CtrlExMemFlow.py
|
10 |
+
|
11 |
+
Take notice that:
|
12 |
+
1. In the controller, we only keep the previous 3 messages for memory management, that will be:
|
13 |
+
a. The assistant message (controller's last command)
|
14 |
+
b. Manually updated new system prompt (new logs, new plans, etc.)
|
15 |
+
c. The user message (result, feedback)
|
16 |
+
2. Each time one executor from the branch is executed, the logs is updated, this means:
|
17 |
+
a. The logs file of Jarvis is updated.
|
18 |
+
b. After MemoryReading at the end of each run of the loop, the logs in the flow_state is updated.
|
19 |
+
c. The next time the controller is called, the updated logs is injected into the system prompts.
|
20 |
+
3. In the prompts of the controller, when the controller realizes one step of the plan is done,
|
21 |
+
we ask the controller to revise what was done and mark the current step as done. This means:
|
22 |
+
a. The plan file is updated.
|
23 |
+
b. The plan in the flow_state is updated.
|
24 |
+
c. The next time the controller is called, the updated plan is injected into the system prompts.
|
25 |
+
|
26 |
+
This is basically how the memory management works, to allow for more space for llm execution, and make sure the llm
|
27 |
+
does not forget important information.
|
28 |
+
"""
|
29 |
+
def _on_reach_max_round(self):
|
30 |
+
self._state_update_dict({
|
31 |
+
"result": "the maximum amount of rounds was reached before the Jarvis flow has done the job",
|
32 |
+
"summary": "JarvisFlow: the maximum amount of rounds was reached before the flow has done the job",
|
33 |
+
"status": "unfinished"
|
34 |
+
})
|
35 |
+
|
36 |
+
@CircularFlow.output_msg_payload_processor
|
37 |
+
def detect_finish_or_continue(self, output_payload: Dict[str, Any], src_flow) -> Dict[str, Any]:
|
38 |
+
command = output_payload["command"]
|
39 |
+
if command == "finish":
|
40 |
+
return {
|
41 |
+
"EARLY_EXIT": True,
|
42 |
+
"result": output_payload["command_args"]["summary"],
|
43 |
+
"summary": "Jarvis: " + output_payload["command_args"]["summary"],
|
44 |
+
"status": "finished"
|
45 |
+
}
|
46 |
+
elif command == "manual_finish":
|
47 |
+
# ~~~ return the manual quit status ~~~
|
48 |
+
return {
|
49 |
+
"EARLY_EXIT": True,
|
50 |
+
"result": "JarvisFlow was terminated explicitly by the user, process is unfinished",
|
51 |
+
"summary": "Jarvis: process terminated by the user explicitly, nothing generated",
|
52 |
+
"status": "unfinished"
|
53 |
+
}
|
54 |
+
elif command == "update_plan":
|
55 |
+
keys_to_fetch_from_state = ["memory_files"]
|
56 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
57 |
+
output_payload["command_args"]["memory_files"] = fetched_state["memory_files"]
|
58 |
+
return output_payload
|
59 |
+
|
60 |
+
elif command == "re_plan":
|
61 |
+
keys_to_fetch_from_state = ["plan", "memory_files"]
|
62 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
63 |
+
output_payload["command_args"]["plan_file_location"] = fetched_state["memory_files"]["plan"]
|
64 |
+
output_payload["command_args"]["plan"] = fetched_state["plan"]
|
65 |
+
return output_payload
|
66 |
+
|
67 |
+
else:
|
68 |
+
return output_payload
|
CtrlExMem_JarvisFlow.yaml
ADDED
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: Tachi67.JarvisFlowModule.CtrlExMem_JarvisFlow.instantiate_from_default_config
|
2 |
+
name: "CtrlExMem_JarvisFlow"
|
3 |
+
description: "MemorizedControllerExecutor flow for Jarvis flow"
|
4 |
+
|
5 |
+
input_interface:
|
6 |
+
- "plan"
|
7 |
+
- "logs"
|
8 |
+
- "memory_files"
|
9 |
+
- "goal"
|
10 |
+
|
11 |
+
subflows_config:
|
12 |
+
Controller:
|
13 |
+
_target_: Tachi67.JarvisFlowModule.Controller_JarvisFlow.instantiate_from_default_config
|
14 |
+
backend:
|
15 |
+
api_infos: ???
|
16 |
+
model_name:
|
17 |
+
openai: gpt-4
|
18 |
+
azure: azure/gpt-4
|
19 |
+
Executor:
|
20 |
+
_target_: aiflows.base_flows.BranchingFlow.instantiate_from_default_config
|
21 |
+
subflows_config:
|
22 |
+
Coder:
|
23 |
+
_target_: Tachi67.CoderFlowModule.CoderFlow.instantiate_from_default_config
|
24 |
+
memory_files: ???
|
25 |
+
subflows_config:
|
26 |
+
Planner:
|
27 |
+
#TODO remove this line
|
28 |
+
_target_: Tachi67.CoderFlowModule.Planner_CoderFlow.instantiate_from_default_config
|
29 |
+
subflows_config:
|
30 |
+
Controller:
|
31 |
+
backend:
|
32 |
+
api_infos: ???
|
33 |
+
model_name:
|
34 |
+
openai: gpt-4
|
35 |
+
azure: azure/gpt-4
|
36 |
+
Executor:
|
37 |
+
subflows_config:
|
38 |
+
write_plan:
|
39 |
+
subflows_config:
|
40 |
+
PlanGenerator:
|
41 |
+
backend:
|
42 |
+
api_infos: ???
|
43 |
+
model_name:
|
44 |
+
openai: gpt-4
|
45 |
+
azure: azure/gpt-4
|
46 |
+
|
47 |
+
CtrlExMem:
|
48 |
+
subflows_config:
|
49 |
+
Controller:
|
50 |
+
backend:
|
51 |
+
api_infos: ???
|
52 |
+
model_name:
|
53 |
+
openai: gpt-4
|
54 |
+
azure: azure/gpt-4
|
55 |
+
Executor:
|
56 |
+
subflows_config:
|
57 |
+
extend_library:
|
58 |
+
memory_files: ???
|
59 |
+
subflows_config:
|
60 |
+
Planner:
|
61 |
+
subflows_config:
|
62 |
+
Controller:
|
63 |
+
backend:
|
64 |
+
api_infos: ???
|
65 |
+
model_name:
|
66 |
+
openai: gpt-4
|
67 |
+
azure: azure/gpt-4
|
68 |
+
Executor:
|
69 |
+
subflows_config:
|
70 |
+
write_plan:
|
71 |
+
subflows_config:
|
72 |
+
PlanGenerator:
|
73 |
+
backend:
|
74 |
+
api_infos: ???
|
75 |
+
model_name:
|
76 |
+
openai: gpt-4
|
77 |
+
azure: azure/gpt-4
|
78 |
+
CtrlExMem:
|
79 |
+
subflows_config:
|
80 |
+
Controller:
|
81 |
+
backend:
|
82 |
+
api_infos: ???
|
83 |
+
model_name:
|
84 |
+
openai: gpt-4
|
85 |
+
azure: azure/gpt-4
|
86 |
+
Executor:
|
87 |
+
subflows_config:
|
88 |
+
write_code:
|
89 |
+
subflows_config:
|
90 |
+
Controller:
|
91 |
+
backend:
|
92 |
+
api_infos: ???
|
93 |
+
model_name:
|
94 |
+
openai: gpt-4
|
95 |
+
azure: azure/gpt-4
|
96 |
+
Executor:
|
97 |
+
subflows_config:
|
98 |
+
write_code:
|
99 |
+
memory_files: ???
|
100 |
+
subflows_config:
|
101 |
+
CodeGenerator:
|
102 |
+
backend:
|
103 |
+
api_infos: ???
|
104 |
+
model_name:
|
105 |
+
openai: gpt-4
|
106 |
+
azure: azure/gpt-4
|
107 |
+
test:
|
108 |
+
memory_files: ???
|
109 |
+
re_plan:
|
110 |
+
subflows_config:
|
111 |
+
Controller:
|
112 |
+
backend:
|
113 |
+
api_infos: ???
|
114 |
+
model_name:
|
115 |
+
openai: gpt-4
|
116 |
+
azure: azure/gpt-4
|
117 |
+
Executor:
|
118 |
+
subflows_config:
|
119 |
+
write_plan:
|
120 |
+
subflows_config:
|
121 |
+
PlanGenerator:
|
122 |
+
backend:
|
123 |
+
api_infos: ???
|
124 |
+
model_name:
|
125 |
+
openai: gpt-4
|
126 |
+
azure: azure/gpt-4
|
127 |
+
re_plan:
|
128 |
+
subflows_config:
|
129 |
+
Controller:
|
130 |
+
backend:
|
131 |
+
api_infos: ???
|
132 |
+
model_name:
|
133 |
+
openai: gpt-4
|
134 |
+
azure: azure/gpt-4
|
135 |
+
Executor:
|
136 |
+
subflows_config:
|
137 |
+
write_plan:
|
138 |
+
subflows_config:
|
139 |
+
PlanGenerator:
|
140 |
+
backend:
|
141 |
+
api_infos: ???
|
142 |
+
model_name:
|
143 |
+
openai: gpt-4
|
144 |
+
azure: azure/gpt-4
|
145 |
+
ask_user:
|
146 |
+
_target_: Tachi67.ExtendLibraryFlowModule.ExtLibAskUserFlow.instantiate_from_default_config
|
147 |
+
re_plan:
|
148 |
+
_target_: Tachi67.ReplanningFlowModule.ReplanningFlow.instantiate_from_default_config
|
149 |
+
subflows_config:
|
150 |
+
Controller:
|
151 |
+
backend:
|
152 |
+
api_infos: ???
|
153 |
+
model_name:
|
154 |
+
openai: gpt-4
|
155 |
+
azure: azure/gpt-4
|
156 |
+
Executor:
|
157 |
+
subflows_config:
|
158 |
+
write_plan:
|
159 |
+
subflows_config:
|
160 |
+
PlanGenerator:
|
161 |
+
backend:
|
162 |
+
api_infos: ???
|
163 |
+
model_name:
|
164 |
+
openai: gpt-4
|
165 |
+
azure: azure/gpt-4
|
166 |
+
update_plan:
|
167 |
+
_target_: Tachi67.JarvisFlowModule.UpdatePlanAtomicFlow.instantiate_from_default_config
|
168 |
+
intermediate_answer:
|
169 |
+
_target_: Tachi67.JarvisFlowModule.IntermediateAns_Jarvis.instantiate_from_default_config
|
170 |
+
final_answer:
|
171 |
+
_target_: Tachi67.JarvisFlowModule.FinalAns_Jarvis.instantiate_from_default_config
|
172 |
+
|
173 |
+
|
174 |
+
MemoryReading:
|
175 |
+
_target_: Tachi67.MemoryReadingFlowModule.MemoryReadingAtomicFlow.instantiate_from_default_config
|
176 |
+
output_interface:
|
177 |
+
- "plan"
|
178 |
+
- "logs"
|
179 |
+
|
180 |
+
topology:
|
181 |
+
- goal: "Select the next action and prepare the input for the executor."
|
182 |
+
input_interface:
|
183 |
+
_target_: aiflows.interfaces.KeyInterface
|
184 |
+
additional_transformations:
|
185 |
+
- _target_: aiflows.data_transformations.KeyMatchInput
|
186 |
+
flow: Controller
|
187 |
+
output_interface:
|
188 |
+
_target_: CtrlExMem_JarvisFlow.detect_finish_or_continue
|
189 |
+
reset: false
|
190 |
+
|
191 |
+
- goal: "Execute the action specified by the Controller."
|
192 |
+
input_interface:
|
193 |
+
_target_: aiflows.interfaces.KeyInterface
|
194 |
+
keys_to_rename:
|
195 |
+
command: branch
|
196 |
+
command_args: branch_input_data
|
197 |
+
keys_to_select: ["branch", "branch_input_data"]
|
198 |
+
flow: Executor
|
199 |
+
output_interface:
|
200 |
+
_target_: aiflows.interfaces.KeyInterface
|
201 |
+
keys_to_rename:
|
202 |
+
branch_output_data.plan: plan
|
203 |
+
branch_output_data.status: status
|
204 |
+
branch_output_data.summary: summary
|
205 |
+
branch_output_data.result: result
|
206 |
+
keys_to_delete: ["branch_output_data"]
|
207 |
+
reset: false
|
208 |
+
|
209 |
+
- goal: "Write memory to memory files"
|
210 |
+
input_interface:
|
211 |
+
_target_: aiflows.interfaces.KeyInterface
|
212 |
+
additional_transformations:
|
213 |
+
- _target_: aiflows.data_transformations.KeyMatchInput
|
214 |
+
flow: MemoryWriting
|
215 |
+
reset: false
|
216 |
+
|
217 |
+
- goal: "Read memory from memory files (flow_state)"
|
218 |
+
input_interface:
|
219 |
+
_target_: aiflows.interfaces.KeyInterface
|
220 |
+
additional_transformations:
|
221 |
+
- _target_: aiflows.data_transformations.KeyMatchInput
|
222 |
+
flow: MemoryReading
|
223 |
+
reset: false
|
FinalAns_Jarvis.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flow_modules.aiflows.HumanStandardInputFlowModule import HumanStandardInputFlow
|
2 |
+
|
3 |
+
from typing import Dict, Any
|
4 |
+
|
5 |
+
from aiflows.messages import UpdateMessage_Generic
|
6 |
+
|
7 |
+
from aiflows.utils import logging
|
8 |
+
|
9 |
+
log = logging.get_logger(f"aiflows.{__name__}")
|
10 |
+
|
11 |
+
|
12 |
+
class FinalAns_Jarvis(HumanStandardInputFlow):
|
13 |
+
"""This class inherits from the HumanStandardInputFlow class.
|
14 |
+
It is used to give the final answer to the user.
|
15 |
+
"""
|
16 |
+
def run(self,
|
17 |
+
input_data: Dict[str, Any]) -> Dict[str, Any]:
|
18 |
+
|
19 |
+
query_message = self._get_message(self.query_message_prompt_template, input_data)
|
20 |
+
state_update_message = UpdateMessage_Generic(
|
21 |
+
created_by=self.flow_config['name'],
|
22 |
+
updated_flow=self.flow_config["name"],
|
23 |
+
data={"query_message": query_message},
|
24 |
+
)
|
25 |
+
self._log_message(state_update_message)
|
26 |
+
|
27 |
+
log.info(query_message)
|
28 |
+
human_input = self._read_input()
|
29 |
+
|
30 |
+
answer = input_data["answer"]
|
31 |
+
response = {}
|
32 |
+
response["result"] = human_input
|
33 |
+
response["summary"] = f"Final answer provided: {answer}; feedback of user: {human_input}"
|
34 |
+
|
35 |
+
return response
|
FinalAns_Jarvis.yaml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: Tachi67.JarvisFlowModule.FinalAns_Jarvis.instantiate_from_default_config
|
2 |
+
request_multi_line_input_flag: False
|
3 |
+
end_of_input_string: EOI
|
4 |
+
|
5 |
+
query_message_prompt_template:
|
6 |
+
template: |2-
|
7 |
+
Jarvis has just achieved the goal, here is the final answer: {{answer}}
|
8 |
+
input_variables:
|
9 |
+
- "answer"
|
IntermediateAns_Jarvis.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flow_modules.aiflows.HumanStandardInputFlowModule import HumanStandardInputFlow
|
2 |
+
|
3 |
+
from typing import Dict, Any
|
4 |
+
|
5 |
+
from aiflows.messages import UpdateMessage_Generic
|
6 |
+
|
7 |
+
from aiflows.utils import logging
|
8 |
+
|
9 |
+
log = logging.get_logger(f"aiflows.{__name__}")
|
10 |
+
|
11 |
+
|
12 |
+
class IntermediateAns_Jarvis(HumanStandardInputFlow):
|
13 |
+
"""This class inherits from the HumanStandardInputFlow class.
|
14 |
+
It is used to give an intermediate answer to the user. The user is then able to provide feedback on the intermediate result.
|
15 |
+
Depending on the user's feedback, the controller will decide to do different things (e.g. continue, re-plan, etc.)
|
16 |
+
"""
|
17 |
+
def run(self,
|
18 |
+
input_data: Dict[str, Any]) -> Dict[str, Any]:
|
19 |
+
|
20 |
+
query_message = self._get_message(self.query_message_prompt_template, input_data)
|
21 |
+
state_update_message = UpdateMessage_Generic(
|
22 |
+
created_by=self.flow_config['name'],
|
23 |
+
updated_flow=self.flow_config["name"],
|
24 |
+
data={"query_message": query_message},
|
25 |
+
)
|
26 |
+
self._log_message(state_update_message)
|
27 |
+
|
28 |
+
log.info(query_message)
|
29 |
+
human_input = self._read_input()
|
30 |
+
|
31 |
+
answer = input_data["answer"]
|
32 |
+
response = {}
|
33 |
+
response["result"] = human_input
|
34 |
+
response["summary"] = f"Intermediate Answer provided: {answer}; feedback of user: {human_input}"
|
35 |
+
|
36 |
+
return response
|
IntermediateAns_Jarvis.yaml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: Tachi67.JarvisFlowModule.IntermediateAns_Jarvis.instantiate_from_default_config
|
2 |
+
request_multi_line_input_flag: False
|
3 |
+
end_of_input_string: EOI
|
4 |
+
|
5 |
+
query_message_prompt_template:
|
6 |
+
template: |2-
|
7 |
+
Jarvis has just finished an intermediate step of execution: {{answer}}
|
8 |
+
input_variables:
|
9 |
+
- "answer"
|
Introduction_to_Jarvis.md
ADDED
@@ -0,0 +1,328 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Introduction to Jarvis
|
2 |
+
|
3 |
+
Jarvis is a general purpose agent empowered by a hierarchical structure of large language models and tools including a code interpreter. At a high level, Jarvis takes in tasks in natural language, and achieve the task by making plans, writing and executing code.
|
4 |
+
|
5 |
+
|
6 |
+
|
7 |
+
To allow for extended the lifespan of Jarvis (constrained by token limitations of LLM APIs) and enable Jarvis to be able to utilize previously accomplished results, we implemented some interesting memory management mechanisms.
|
8 |
+
|
9 |
+
|
10 |
+
**Notice:**
|
11 |
+
The code interpreter of Jarvis (https://huggingface.co/Tachi67/InterpreterFlowModule) relies on **open-interpreter** (https://github.com/KillianLucas/open-interpreter). We are extracting the specific code from open-interpreter because the litellm version of open-interpreter is not compatible with that of the current version of aiflows (v.0.1.7).
|
12 |
+
|
13 |
+
|
14 |
+
|
15 |
+
### Structure of Jarvis
|
16 |
+
|
17 |
+
Let's cut things short, here is an illustration of the Jarvis architecture:
|
18 |
+
```
|
19 |
+
goal, memory_files (dict)
|
20 |
+
|
|
21 |
+
v
|
22 |
+
+-------------------+
|
23 |
+
| MemoryReading | Reads in the content of the memory files
|
24 |
+
| Flow |
|
25 |
+
+-------------------+
|
26 |
+
|
|
27 |
+
| (memory_content)
|
28 |
+
|
|
29 |
+
v
|
30 |
+
+-------------------+
|
31 |
+
| PlanWriter | Writes a step-by-step plan to achieve the goal
|
32 |
+
+-------------------+
|
33 |
+
|
|
34 |
+
| (plan)
|
35 |
+
|
|
36 |
+
v
|
37 |
+
+-------------------+
|
38 |
+
| CtrlExMemFlow | Illustrated below. Carries out the plan in an controller-executor fashion,
|
39 |
+
| | with memory management mechanisms.
|
40 |
+
+-------------------+
|
41 |
+
|
|
42 |
+
(summary, result)
|
43 |
+
|
44 |
+
```
|
45 |
+
|
46 |
+
Here is the structure of the `CtrlExMemFlow`:
|
47 |
+
```
|
48 |
+
plan, memory_files, memory_content, goal
|
49 |
+
|
|
50 |
+
v
|
51 |
+
+---------------+
|
52 |
+
| Controller | --------<<<<-----------+
|
53 |
+
+---------------+ |
|
54 |
+
| |
|
55 |
+
| (command, command args) |
|
56 |
+
| |
|
57 |
+
v |
|
58 |
+
+------------------+ |
|
59 |
+
| Executor | Each branch is an |
|
60 |
+
| (Tree Structure) | executor |
|
61 |
+
+------------------+ |
|
62 |
+
| ^
|
63 |
+
| (execution results) ^
|
64 |
+
| ^
|
65 |
+
v ^
|
66 |
+
+---------------+ ^
|
67 |
+
| MemWriteFlow | Updates memory files ^
|
68 |
+
+---------------+ ^
|
69 |
+
| ^
|
70 |
+
| (summary) |
|
71 |
+
| |
|
72 |
+
v |
|
73 |
+
+---------------+ |
|
74 |
+
| MemReadFlow | Reads updated memory |
|
75 |
+
+---------------+ |
|
76 |
+
| |
|
77 |
+
| (updated memory content) |
|
78 |
+
| |
|
79 |
+
+-> goes back to the Controller>-+
|
80 |
+
|
81 |
+
```
|
82 |
+
Structure of the Executors:
|
83 |
+
```
|
84 |
+
+-------------------+
|
85 |
+
| Branching |
|
86 |
+
| Executor |
|
87 |
+
+-------------------+
|
88 |
+
/ | | | | \
|
89 |
+
/ | | | | \
|
90 |
+
/ | | | | \
|
91 |
+
/ | | | | \
|
92 |
+
Coder ask_user re_plan update_plan intermediate_answer final_answer
|
93 |
+
|
94 |
+
```
|
95 |
+
|
96 |
+
In fact, this structure is defined as [AbstractBoss](https://huggingface.co/Tachi67/AbstractBossFlowModule), Jarvis inherits from this abstract flow. There are other components that
|
97 |
+
inherits from this structure. for example, [Coder](https://huggingface.co/Tachi67/CoderFlowModule), [ExtendLibrary](https://huggingface.co/Tachi67/ExtendLibraryFlowModule)
|
98 |
+
|
99 |
+
Let's go through some basic ideas of Jarvis and its components now.
|
100 |
+
- memory: Jarvis should remember certain important memory, they include the plan (`plan`), and the history of subflows execution results (`logs`). To do this, we put them in external files, and inject them into the system prompts to controllers and other corresponding roles. There are other classes that inherits from `AbstractBoss` and have other memories, e.g. `Coder` takes the memory of `code_libarary`, which is the function signatures and docstrings of the code written by the Coder and saved in the code library.
|
101 |
+
- memory_files: A dictionary: `{memory_name: memory_file_path}`
|
102 |
+
- [MemoryReadingFlow](https://huggingface.co/Tachi67/MemoryReadingFlowModule): Reads in the content of the memory files.
|
103 |
+
- [PlanWriter](https://huggingface.co/Tachi67/PlanWriterFlowModule): Writes a step-by-step plan to achieve the goal in an interactive fashion. During the process of writing a plan, the user has the chance to provide feedback to or directly edit the plan just written. The PlanWriter can comprehend the user's action and make adjustments to the plan accordingly.
|
104 |
+
- [CtrlExMem_JarvisFlow](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/CtrlExMem_JarvisFlow.py): Inherits from [CtrlExMemFlow](https://huggingface.co/Tachi67/AbstractBossFlowModule/blob/main/CtrlExMemFlow.py), it executes the step-by-step plan written by the PlanWriter in a controller-executor fashion, with memory management mechanisms.
|
105 |
+
- [Controller of CtrlExMem_JarvisFlow](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/Controller_JarvisFlow.py): The controller of the CtrlExMem_JarvisFlow. It inherits from [ChatFlowModule](https://huggingface.co/aiflows/ChatFlowModule) (that being said, an LLM API), it decides to either call one of the branching executors or to finish and exit the loop.
|
106 |
+
- [Coder](https://huggingface.co/Tachi67/CoderFlowModule): One branch of the executors, it writes functions that are reusable to the code library (Python), writes and run code scripts of execute logics like executing one function from the library, installing packages, etc.
|
107 |
+
- [re_plan](https://huggingface.co/Tachi67/ReplanningFlowModule): One branch of the executors, when something goes wrong, re-draft the plan.
|
108 |
+
- [update_plan](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/UpdatePlanAtomicFlow.py): One branch of the executors, when the controller realizes that one (or some, depending on the LLM's response) step of the plan is (are) done, it generates a new plan that marks the step(s) as done.
|
109 |
+
- [ask_user](https://huggingface.co/Tachi67/ExtendLibraryFlowModule/blob/main/ExtLibAskUserFlow.py): One branch of the executors, when the controller needs user assist, confirmation, etc., it calls this flow.
|
110 |
+
- [intermediate_answer](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/IntermediateAns_Jarvis.py): One branch of the executors, when one (or some, depending on the LLM's response) step of the plan is (are) done, the controller calls this flow to remind the user that the step(s) is (are) done, and ask for the user's feedback.
|
111 |
+
- [final_answer](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/FinalAns_Jarvis.py): One branch of the executors, when the plan is done, the controller calls this flow to remind the user that the plan is done, and ask for the user's feedback.
|
112 |
+
- [MemWriteFlow](https://huggingface.co/Tachi67/MemoryWritingFlowModule): Updates memory files (`logs`).
|
113 |
+
- [MemReadFlow](https://huggingface.co/Tachi67/MemoryReadingFlowModule): Reads updated memory files (`plan`,`logs`).
|
114 |
+
|
115 |
+
|
116 |
+
### Memory Management Mechanisms
|
117 |
+
Above all, the role separation into a hierarchy structure of LLMs largely increases the token upper limits.
|
118 |
+
|
119 |
+
For long-term memory, the goal is to refine the quality of LLM's chat history, to do this, we utilize memory files, for now there are three kinds of memory_files:
|
120 |
+
- `plan`: the step-by-step plan to achieve the goal, it will be updated by the `update_plan` executor when the controller realizes that one (or some, depending on the LLM's response) step of the plan is (are) done.
|
121 |
+
- `logs`: the history of subflows execution results, it will be updated by the `MemWriteFlow` executor whenever one branching executor is finished.
|
122 |
+
- `code_library`: the function signatures and docstrings of the code written by the Coder and saved in the code library, it will be updated by the `Coder` executor whenever the Coder writes a new function to the code library.
|
123 |
+
|
124 |
+
The memory contents are injected into the system prompts of the respective LLM chats.
|
125 |
+
|
126 |
+
We also applied a sliding window for history chat messages as a workaround for the token limitations of the LLM APIs. To do this:
|
127 |
+
- Whenever the chat that utilizes the sliding window is called, we inject a new system prompt with the newest memory (plan, logs, for `Coder` and some other flows, `code_library` is also injected) into the chat history.)
|
128 |
+
- We crop the chat history with a tailing window of length 3, that will include the assistant's previous call, the updated system prompt, the the user's new response.
|
129 |
+
|
130 |
+
When the `Coder` and `ExtendLibraryFlow` is finished, we reset the flows, that means the chat history is cleaned as well -- which is fine, because the plan and logs are preserved in the upper level of flow (`JarvisFlow` and `Coder`). By doing this, Jarvis is able to clear useless tokens.
|
131 |
+
|
132 |
+
As a result, Jarvis is able to run for quite a long time, and it's able to use the code written before.
|
133 |
+
|
134 |
+
### An Example run of Jarvis
|
135 |
+
WARNING: This section is quite lengthy. Also, due to the nondeterministic nature of the LLMs, the results may vary, the reader is suggested to make sure every plan and piece of code does the same thing as the example presents.
|
136 |
+
|
137 |
+
- user input goal = "Download tesla's stock prices from 2022-01-01 to 2022-06-01 from yfinance, plot the prices."
|
138 |
+
- Plan of Jarvis is generated, the user is able to edit the plan directly or give feedback.
|
139 |
+
|
140 |
+
Plan (Jarvis):
|
141 |
+
1. Write and run code to download Tesla's stock prices from 2022-01-01 to 2022-06-01 using python package yfinance.
|
142 |
+
2. Write and run code to plot the downloaded stock prices using matplotlib.
|
143 |
+
3. Give a final answer to the user.
|
144 |
+
- Controller of Jarvis calls Coder.
|
145 |
+
- Planner of Coder generates plan of Coder, the user is able to edit the plan directly or give feedback.
|
146 |
+
|
147 |
+
Plan (Coder):
|
148 |
+
1. Extend the code library with a function named `download_stock_data`. This function should take three parameters: `ticker_symbol` (a string representing the stock's ticker symbol), `start_date` (a string representing the start date in the format 'YYYY-MM-DD'), and `end_date` (a string representing the end date in the same format). The function should use the `yfinance` library to download the stock data for the given ticker symbol between the start and end dates, and return this data as a pandas DataFrame.
|
149 |
+
|
150 |
+
2. Run a code script to import the `yfinance` library. If the library is not already installed, the script should install it using pip.
|
151 |
+
|
152 |
+
3. Run a code script to import the `download_stock_data` function from the code library.
|
153 |
+
|
154 |
+
4. Run a code script to call the `download_stock_data` function with the inputs 'TSLA', '2022-01-01', and '2022-06-01'. The script should store the returned DataFrame in a variable.
|
155 |
+
|
156 |
+
5. Give a final answer: the data is stored in a variable, specify the variable name.
|
157 |
+
|
158 |
+
- The Controller of Coder calls ExtendLibraryFlow.
|
159 |
+
- Planner of ExtendLibrary generates plan of ExtendLibraryFlow, the user is able to edit the plan directly or give feedback.
|
160 |
+
|
161 |
+
Plan (ExtendLibrary):
|
162 |
+
1. Write a function named 'download_stock_data'. This function should take three parameters: 'ticker_symbol' (a string), 'start_date' (a string in the format 'YYYY-MM-DD'), and 'end_date' (a string in the same format). Inside the function, use the 'yfinance.download' method from the 'yfinance' library to download the stock data for the given ticker symbol between the start and end dates. The 'yfinance.download' method should be called with the 'ticker_symbol' as the first argument, 'start_date' as the second argument and 'end_date' as the third argument. The function should return the downloaded data as a pandas DataFrame.
|
163 |
+
|
164 |
+
|
165 |
+
- The Controller of ExtendLibrary calls write_code, inside of write_code flow:
|
166 |
+
- The code gets generated, and the user is able to provide feedback on the code or edit the code directly.
|
167 |
+
|
168 |
+
Code:
|
169 |
+
```python
|
170 |
+
def download_stock_data(ticker_symbol: str, start_date: str, end_date: str):
|
171 |
+
"""
|
172 |
+
Downloads the stock data for the given ticker symbol between the start and end dates.
|
173 |
+
|
174 |
+
Parameters:
|
175 |
+
ticker_symbol (str): The ticker symbol of the stock.
|
176 |
+
start_date (str): The start date in the format 'YYYY-MM-DD'.
|
177 |
+
end_date (str): The end date in the format 'YYYY-MM-DD'.
|
178 |
+
|
179 |
+
Returns:
|
180 |
+
pandas.DataFrame: The downloaded stock data.
|
181 |
+
"""
|
182 |
+
import yfinance
|
183 |
+
|
184 |
+
# Download the stock data
|
185 |
+
data = yfinance.download(ticker_symbol, start=start_date, end=end_date)
|
186 |
+
|
187 |
+
return data
|
188 |
+
```
|
189 |
+
- The code is tested, before testing the code, the user is able to provide test suites to test the code, otherwise only the syntax of the code is checked. Here we do not provide any other test suites.
|
190 |
+
- The Controller calls save_code to append the newly written function to the code library.
|
191 |
+
- The Controller of ExtendLibrary calls update_plan to mark the step of plan as done.
|
192 |
+
- The Controller of ExtendLibrary finishes ExtendLibraryFlow, going back to the coder.
|
193 |
+
- When finishing the ExtendLibraryFlow, it is reseted, setting its flow_state and the flow_state of its subflows as an empty dict.
|
194 |
+
- The Controller of Coder calls update_plan to mark step 1 of the plan as done.
|
195 |
+
- The Controller of Coder calls run_code to proceed step 2 of the plan. The code to run is generated by the Controller of Coder.
|
196 |
+
- Inside of run_code, the following is executed:
|
197 |
+
- The code is written to a temp file.
|
198 |
+
```shell
|
199 |
+
pip install yfinance
|
200 |
+
```
|
201 |
+
- The temp file is opened in a vscode session.
|
202 |
+
- The user is able to edit the code.
|
203 |
+
- Code is ran by an interpreter.
|
204 |
+
- The result is logged to the console, the user is able to provide feedback on the execution result.
|
205 |
+
- Exit back to Coder.
|
206 |
+
|
207 |
+
- The Controller of Coder calls run_code to proceed step 3 of the plan.
|
208 |
+
- Code to import function is executed, the user is able to provide feedback.
|
209 |
+
|
210 |
+
```python
|
211 |
+
import importlib
|
212 |
+
import library
|
213 |
+
importlib.reload(library)
|
214 |
+
from library import download_stock_data
|
215 |
+
```
|
216 |
+
- The Controller of Coder calls run_code to proceed step 4 of the plan.
|
217 |
+
- Code to download the data is executed. User is able to provide feedback on the results.
|
218 |
+
|
219 |
+
```python
|
220 |
+
stock_data = download_stock_data('TSLA', '2022-01-01', '2022-06-01')
|
221 |
+
```
|
222 |
+
- The Controller of Coder calls update_plan to mark the rest steps of the plan as done.
|
223 |
+
- The Controller finishes the Coder flow and return result to the JarvisFlow.
|
224 |
+
- When finishing, the CoderFlow resets, setting its flow_state and the flow_state of its subflows an an empty dict.
|
225 |
+
- The Controller of Jarvis calls update_plan to mark step 1 of the plan as done.
|
226 |
+
- The Controller of Jarvis calls Coder with the goal of the second step of plan.
|
227 |
+
- The Planner of Coder makes a plan:
|
228 |
+
|
229 |
+
Plan:
|
230 |
+
1. Extend the code library with a function to plot the stock prices. The function should take a pandas DataFrame (which is the output of the download_stock_data function) as input and plot the 'Close' prices against the dates. The function should not return anything. The function should be named 'plot_stock_prices'.
|
231 |
+
2. Import the 'download_stock_data' and 'plot_stock_prices' functions from the code library.
|
232 |
+
3. Run the 'download_stock_data' function with the desired ticker symbol, start date, and end date to get the stock data.
|
233 |
+
4. Run the 'plot_stock_prices' function with the DataFrame obtained from the previous step as input to plot the stock prices.
|
234 |
+
5. The final answer is the plot generated by the 'plot_stock_prices' function. function.
|
235 |
+
|
236 |
+
- The plan is passed to the Controller of Coder, the Controller of Coder calls ExtendLibrary.
|
237 |
+
- The Planner of ExtendLibrary makes the following plan:
|
238 |
+
|
239 |
+
Plan:
|
240 |
+
1. Write a function named 'plot_stock_prices'. This function should take in a pandas DataFrame as a parameter, which is the output of the 'download_stock_data' function. Inside the function, use matplotlib or any other plotting library to plot the 'Close' prices against the dates. The function should not return anything.
|
241 |
+
|
242 |
+
- The Controller of ExtendLibrary calls write_code to write and test the code. Still, we do not provide any more test suites other than checking syntax.
|
243 |
+
```python
|
244 |
+
def plot_stock_prices(df):
|
245 |
+
"""
|
246 |
+
Plots the 'Close' prices against the dates.
|
247 |
+
|
248 |
+
Parameters:
|
249 |
+
df (pandas.DataFrame): The DataFrame containing the stock data.
|
250 |
+
"""
|
251 |
+
import matplotlib.pyplot as plt
|
252 |
+
|
253 |
+
plt.figure(figsize=(10,5))
|
254 |
+
plt.plot(df.index, df['Close'], label='Close Price history')
|
255 |
+
plt.xlabel('Date')
|
256 |
+
plt.ylabel('Close Price')
|
257 |
+
plt.title('Close Price Trend')
|
258 |
+
plt.legend()
|
259 |
+
plt.show()
|
260 |
+
```
|
261 |
+
|
262 |
+
- The Controller of ExtendLibrary calls save_code to append the function to the code library.
|
263 |
+
- The Controller of ExtendLibrary calls update_plan to mark the step of plan as done.
|
264 |
+
- The Controller of ExtendLibrary finishes the flow and resets the flow and its subflows.
|
265 |
+
- The Controller of Coder calls update_plan to mark the first step of the plan as done.
|
266 |
+
- The Controller of Coder calls run_code to proceed to the second step of the plan.
|
267 |
+
|
268 |
+
```python
|
269 |
+
import importlib
|
270 |
+
import library
|
271 |
+
importlib.reload(library)
|
272 |
+
from library import download_stock_data, plot_stock_prices
|
273 |
+
```
|
274 |
+
- The Controller calls update_plan to mark the second step of plan as done.
|
275 |
+
- The Controller calls run_code to proceed the thrid step of the plan.
|
276 |
+
|
277 |
+
```python
|
278 |
+
stock_data = download_stock_data('TSLA', '2022-01-01', '2022-06-01')
|
279 |
+
```
|
280 |
+
- The Controller of Coder callss run_code to proceed the 4th step of the plan.
|
281 |
+
|
282 |
+
```python
|
283 |
+
plot_stock_prices(stock_data)
|
284 |
+
```
|
285 |
+
- Plot is generated.
|
286 |
+
- The Controller of Coder updates the rest of the plan as done.
|
287 |
+
- The Controller of Coder finishes the CoderFlow, reset the flow and go back to Jarvis.
|
288 |
+
- The Controller of Jarvis updates the plan to mark the second step of plan as done.
|
289 |
+
- The Controller of Jarvis calls final_ans to provide the final answer.
|
290 |
+
- The Controller of Jarvis finishes the Jarvis flow.
|
291 |
+
|
292 |
+
|
293 |
+
### Future Improvements
|
294 |
+
Due to time constraints, we were not able to refine Jarvis to the very best, here is a list of TODOs:
|
295 |
+
- When running code tests in [TestCodeFlow](https://huggingface.co/Tachi67/TestCodeFlowModule) we should show the test results to the user and parse for feedback. This can be done by adding a subflow to ask for human feedback (like [this](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/IntermediateAns_Jarvis.py)) to [TestCodeFlow](https://huggingface.co/Tachi67/TestCodeFlowModule/blob/main/TestCodeFlow.py).
|
296 |
+
|
297 |
+
- Consider refactoring:
|
298 |
+
- Controllers should be generalized:
|
299 |
+
- [Controller of JarvisFlow](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/Controller_JarvisFlow.py)
|
300 |
+
- [Controller of CoderFlow](https://huggingface.co/Tachi67/ExtendLibraryFlowModule/blob/main/ControllerFlow_ExtLib.py)
|
301 |
+
- [Controller of ExtendLibraryFlow](https://huggingface.co/Tachi67/CoderFlowModule/blob/main/Controller_CoderFlow.py)
|
302 |
+
- [Controller of PlanWriterFlow](https://huggingface.co/Tachi67/PlanWriterFlowModule/blob/main/PlanWriterCtrlFlow.py)
|
303 |
+
- [Controller of CodeWriterFlow](https://huggingface.co/Tachi67/CodeWriterFlowModule/blob/main/CodeWriterFlow.py)
|
304 |
+
- AskUserFlow should be generalized:
|
305 |
+
- [IntermediateAns of JarvisFlow](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/IntermediateAns_Jarvis.py)
|
306 |
+
- [FinalAns of JarvisFlow](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/FinalAns_Jarvis.py)
|
307 |
+
- AskUserFlow of each controller
|
308 |
+
- Planners should be generalized:
|
309 |
+
basically, modify [PlanWriterFlow](https://huggingface.co/Tachi67/PlanWriterFlowModule/tree/main) to make it adapted to:
|
310 |
+
- [Planner of JarvisFlow](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/Planner_JarvisFlow.py)
|
311 |
+
- [Planner of CoderFlow](https://huggingface.co/Tachi67/CoderFlowModule/blob/main/Planner_CoderFlow.py)
|
312 |
+
- UpdatePlanFlow should be generalized:
|
313 |
+
- [UpdatePlanFlow of JarvisFlow](https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/UpdatePlanAtomicFlow.py)
|
314 |
+
- [UpdatePlanFlow of CoderFlow](https://huggingface.co/Tachi67/CoderFlowModule/blob/main/UpdatePlanAtomicFlow.py)
|
315 |
+
- [UpdatePlanFlow of ExtendLibraryFlow](https://huggingface.co/Tachi67/ExtendLibraryFlowModule/blob/main/UpdatePlanAtomicFlow.py)
|
316 |
+
- Clear up prompts to use more examples instead of natural language instructions.
|
317 |
+
- During the execution of a flow, the user should be press `Crtl + C` to exit the current flow and go back to the caller flow. By doing this, the user should also be able to provide feedback to the controller of the caller flow to instruct what to do next.
|
318 |
+
- After replanning, the controller sometimes gets confused of the new plan, it may be because that we are simply overriding the plan injected to the system prompts. Consider providing the controller a user message informing it of the new plan (please take notice of the sliding window as it's kind of tricky.)
|
319 |
+
|
320 |
+
|
321 |
+
### Acknowledgement and some nonesense
|
322 |
+
**WARNING:** This section is not related to the design, implementaion nor usage of Jarvis, the reader can completely skip this section without losing any useful information.
|
323 |
+
|
324 |
+
Jarvis is done independently as a semester project by [Haolong Li](https://github.com/Tachi-67) ([haolong.li@epfl.ch](mailto:haolong.li@epfl.ch)) at EPFL, very special and strong thanks to his mentor: Martin Josifoski, and semi-mentor: Nicolas Baldwin (I'm not posting their contacts in case they get mad ;-) ) for their help and support during the design and implementation of Jarvis, love you!
|
325 |
+
|
326 |
+
Jarvis is built under the framework of [aiflows](https://github.com/epfl-dlab/aiflows), it's cool -- try it out!
|
327 |
+
|
328 |
+
Jarvis is not perfect, December in Switzerland (or EPFL? who knows) sucks, the poor author didn't got the energy to write super clean code, but it runs!! ;)
|
JarvisFlow.py
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flow_modules.Tachi67.AbstractBossFlowModule import AbstractBossFlow
|
2 |
+
|
3 |
+
class JarvisFlow(AbstractBossFlow):
|
4 |
+
"""JarvisFlow is a flow module for the boss Jarvis. It inherits from AbstractBossFlow. (
|
5 |
+
https://huggingface.co/Tachi67/AbstractBossFlowModule/tree/main). Jarvis is a general purpose agent empowered by
|
6 |
+
multiple large language models and tools including a code interpreter, to take task commands in natural language,
|
7 |
+
and make plans, write and run code in an interactive fashion to finish the task.
|
8 |
+
|
9 |
+
The highlight of Jarvis is that it integrates 17 large language models, each of them prompted differently to achieve
|
10 |
+
seamless inter-model communication and model-user interaction. The structure of Jarvis ensures that it is much more
|
11 |
+
robust, flexible and memory-efficient than previous agents empowered by one single model.
|
12 |
+
What's more, Jarvis integrates modules to allow for llm's memory management, ensuring persisted mid-long term memory
|
13 |
+
and efficient short-term memory management, making its life duration much longer than single-modeled agents,
|
14 |
+
and more powerful, in that it is able to accumulate important knowledge e.g. code library. Jarvis can also take
|
15 |
+
response from the user and the environment (e.g. code execution result), and spontaneously re-plan and re-execute
|
16 |
+
to make the execution more robust and reliable.
|
17 |
+
|
18 |
+
*Configuration Parameters*:
|
19 |
+
- `memory_files` (dict): mem_name-memfile_path pairs. mem_name is the name of the memory (plan, logs, code_library),
|
20 |
+
and memfile_path is the path to the corresponding memory file. Configure this either in the .yaml file, or override
|
21 |
+
the `memory_files` entry when running the flow.
|
22 |
+
- `subflows_config` (dict): configs for subflows.
|
23 |
+
- `MemoryReading`: Module used to read in memory (https://huggingface.co/Tachi67/MemoryReadingFlowModule), output interface
|
24 |
+
configured so that it outputs the neeed memory.
|
25 |
+
- `Planner`: Module used to interactively write plans for Jarvis, the planner is implemented in the JarvisFlow.
|
26 |
+
- `CtrlExMem`: Module used to execute the plan in a controller-executor manner, and update the memory. It is implemented
|
27 |
+
in the JarvisFlow.
|
28 |
+
|
29 |
+
**The code interpreter of Jarvis (https://huggingface.co/Tachi67/InterpreterFlowModule) relies on open-interpreter (https://github.com/KillianLucas/open-interpreter)
|
30 |
+
We are extracting the specific code from open-interpreter because the litellm version of open-interpreter is not compatible with that of the current version of aiflows(v.0.1.7).**
|
31 |
+
"""
|
32 |
+
pass
|
JarvisFlow.yaml
ADDED
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: JarvisFlow
|
2 |
+
description: Jarvis, an agent with structured llms and tools able to process user requests, write and execute code, with external memory mechanisms.
|
3 |
+
|
4 |
+
_target_: Tachi67.JarvisFlowModule.JarvisFlow.instantiate_from_default_config
|
5 |
+
|
6 |
+
memory_files: ???
|
7 |
+
|
8 |
+
subflows_config:
|
9 |
+
MemoryReading:
|
10 |
+
_target_: Tachi67.MemoryReadingFlowModule.MemoryReadingAtomicFlow.instantiate_from_default_config
|
11 |
+
output_interface:
|
12 |
+
- "plan"
|
13 |
+
- "logs"
|
14 |
+
|
15 |
+
Planner:
|
16 |
+
_target_: Tachi67.JarvisFlowModule.Planner_JarvisFlow.instantiate_from_default_config
|
17 |
+
subflows_config:
|
18 |
+
Controller:
|
19 |
+
backend:
|
20 |
+
api_infos: ???
|
21 |
+
model_name:
|
22 |
+
openai: gpt-4
|
23 |
+
azure: azure/gpt-4
|
24 |
+
Executor:
|
25 |
+
subflows_config:
|
26 |
+
write_plan:
|
27 |
+
subflows_config:
|
28 |
+
PlanGenerator:
|
29 |
+
backend:
|
30 |
+
api_infos: ???
|
31 |
+
model_name:
|
32 |
+
openai: gpt-4
|
33 |
+
azure: azure/gpt-4
|
34 |
+
|
35 |
+
CtrlExMem:
|
36 |
+
_target_: Tachi67.JarvisFlowModule.CtrlExMem_JarvisFlow.instantiate_from_default_config
|
37 |
+
subflows_config:
|
38 |
+
Controller:
|
39 |
+
backend:
|
40 |
+
api_infos: ???
|
41 |
+
model_name:
|
42 |
+
openai: gpt-4
|
43 |
+
azure: azure/gpt-4
|
44 |
+
Executor:
|
45 |
+
subflows_config:
|
46 |
+
Coder:
|
47 |
+
memory_files: ???
|
48 |
+
subflows_config:
|
49 |
+
Planner:
|
50 |
+
subflows_config:
|
51 |
+
Controller:
|
52 |
+
backend:
|
53 |
+
api_infos: ???
|
54 |
+
model_name:
|
55 |
+
openai: gpt-4
|
56 |
+
azure: azure/gpt-4
|
57 |
+
Executor:
|
58 |
+
subflows_config:
|
59 |
+
write_plan:
|
60 |
+
subflows_config:
|
61 |
+
PlanGenerator:
|
62 |
+
backend:
|
63 |
+
api_infos: ???
|
64 |
+
model_name:
|
65 |
+
openai: gpt-4
|
66 |
+
azure: azure/gpt-4
|
67 |
+
CtrlExMem:
|
68 |
+
subflows_config:
|
69 |
+
Controller:
|
70 |
+
backend:
|
71 |
+
api_infos: ???
|
72 |
+
model_name:
|
73 |
+
openai: gpt-4
|
74 |
+
azure: azure/gpt-4
|
75 |
+
Executor:
|
76 |
+
subflows_config:
|
77 |
+
extend_library:
|
78 |
+
memory_files: ???
|
79 |
+
subflows_config:
|
80 |
+
Planner:
|
81 |
+
subflows_config:
|
82 |
+
Controller:
|
83 |
+
backend:
|
84 |
+
api_infos: ???
|
85 |
+
model_name:
|
86 |
+
openai: gpt-4
|
87 |
+
azure: azure/gpt-4
|
88 |
+
Executor:
|
89 |
+
subflows_config:
|
90 |
+
write_plan:
|
91 |
+
subflows_config:
|
92 |
+
PlanGenerator:
|
93 |
+
backend:
|
94 |
+
api_infos: ???
|
95 |
+
model_name:
|
96 |
+
openai: gpt-4
|
97 |
+
azure: azure/gpt-4
|
98 |
+
CtrlExMem:
|
99 |
+
subflows_config:
|
100 |
+
Controller:
|
101 |
+
backend:
|
102 |
+
api_infos: ???
|
103 |
+
model_name:
|
104 |
+
openai: gpt-4
|
105 |
+
azure: azure/gpt-4
|
106 |
+
Executor:
|
107 |
+
subflows_config:
|
108 |
+
write_code:
|
109 |
+
subflows_config:
|
110 |
+
Controller:
|
111 |
+
backend:
|
112 |
+
api_infos: ???
|
113 |
+
model_name:
|
114 |
+
openai: gpt-4
|
115 |
+
azure: azure/gpt-4
|
116 |
+
Executor:
|
117 |
+
subflows_config:
|
118 |
+
write_code:
|
119 |
+
memory_files: ???
|
120 |
+
subflows_config:
|
121 |
+
CodeGenerator:
|
122 |
+
backend:
|
123 |
+
api_infos: ???
|
124 |
+
model_name:
|
125 |
+
openai: gpt-4
|
126 |
+
azure: azure/gpt-4
|
127 |
+
test:
|
128 |
+
memory_files: ???
|
129 |
+
re_plan:
|
130 |
+
subflows_config:
|
131 |
+
Controller:
|
132 |
+
backend:
|
133 |
+
api_infos: ???
|
134 |
+
model_name:
|
135 |
+
openai: gpt-4
|
136 |
+
azure: azure/gpt-4
|
137 |
+
Executor:
|
138 |
+
subflows_config:
|
139 |
+
write_plan:
|
140 |
+
subflows_config:
|
141 |
+
PlanGenerator:
|
142 |
+
backend:
|
143 |
+
api_infos: ???
|
144 |
+
model_name:
|
145 |
+
openai: gpt-4
|
146 |
+
azure: azure/gpt-4
|
147 |
+
re_plan:
|
148 |
+
subflows_config:
|
149 |
+
Controller:
|
150 |
+
backend:
|
151 |
+
api_infos: ???
|
152 |
+
model_name:
|
153 |
+
openai: gpt-4
|
154 |
+
azure: azure/gpt-4
|
155 |
+
Executor:
|
156 |
+
subflows_config:
|
157 |
+
write_plan:
|
158 |
+
subflows_config:
|
159 |
+
PlanGenerator:
|
160 |
+
backend:
|
161 |
+
api_infos: ???
|
162 |
+
model_name:
|
163 |
+
openai: gpt-4
|
164 |
+
azure: azure/gpt-4
|
165 |
+
re_plan:
|
166 |
+
subflows_config:
|
167 |
+
Controller:
|
168 |
+
backend:
|
169 |
+
api_infos: ???
|
170 |
+
model_name:
|
171 |
+
openai: gpt-4
|
172 |
+
azure: azure/gpt-4
|
173 |
+
Executor:
|
174 |
+
subflows_config:
|
175 |
+
write_plan:
|
176 |
+
subflows_config:
|
177 |
+
PlanGenerator:
|
178 |
+
backend:
|
179 |
+
api_infos: ???
|
180 |
+
model_name:
|
181 |
+
openai: gpt-4
|
182 |
+
azure: azure/gpt-4
|
Planner_JarvisFlow.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Dict, Any
|
2 |
+
import os
|
3 |
+
from flow_modules.Tachi67.PlanWriterFlowModule import PlanWriterFlow
|
4 |
+
from aiflows.base_flows import CircularFlow
|
5 |
+
|
6 |
+
class Planner_JarvisFlow(PlanWriterFlow):
|
7 |
+
"""This flow inherits from PlanWriterFlow (https://huggingface.co/Tachi67/PlanWriterFlowModule), and is used to generate a plan for Jarvis.
|
8 |
+
*Input Interface*:
|
9 |
+
- `goal` (str): the goal of the planner, the goal comes from the user's query when calling Jarvis.
|
10 |
+
- `memory_files` (dict): a dictionary of memory files, the keys are the names of the memory files, the values are the locations of the memory files.
|
11 |
+
|
12 |
+
*Output Interfaces*:
|
13 |
+
- `plan` (str): the generated plan, the plan string will be written to the plan file and returned to the flow state of the Jarvis flow.
|
14 |
+
- `summary` (str): the summary of the planner.
|
15 |
+
- `status` (str): the status of the planner, can be "finished" or "unfinished".
|
16 |
+
"""
|
17 |
+
# TODO: generalize this flow to avoid code duplication
|
18 |
+
@CircularFlow.output_msg_payload_processor
|
19 |
+
def detect_finish_or_continue(self, output_payload: Dict[str, Any], src_flow) -> Dict[str, Any]:
|
20 |
+
command = output_payload["command"]
|
21 |
+
if command == "finish":
|
22 |
+
# ~~~ fetch temp file location, plan content, memory file (of upper level flow e.g. ExtLib) from flow state
|
23 |
+
keys_to_fetch_from_state = ["temp_plan_file_location", "plan", "memory_files"]
|
24 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
25 |
+
temp_plan_file_location = fetched_state["temp_plan_file_location"]
|
26 |
+
plan_content = fetched_state["plan"]
|
27 |
+
plan_file_location = fetched_state["memory_files"]["plan"]
|
28 |
+
|
29 |
+
# ~~~ delete the temp plan file ~~~
|
30 |
+
if os.path.exists(temp_plan_file_location):
|
31 |
+
os.remove(temp_plan_file_location)
|
32 |
+
|
33 |
+
# ~~~ write plan content to plan file ~~~
|
34 |
+
with open(plan_file_location, 'w') as file:
|
35 |
+
file.write(plan_content)
|
36 |
+
|
37 |
+
# ~~~ return the plan content ~~~
|
38 |
+
return {
|
39 |
+
"EARLY_EXIT": True,
|
40 |
+
"plan": plan_content,
|
41 |
+
"summary": "Jarvis/PlanWriter: " + output_payload["command_args"]["summary"],
|
42 |
+
"status": "finished"
|
43 |
+
}
|
44 |
+
elif command == "manual_finish":
|
45 |
+
# ~~~ delete the temp plan file ~~~
|
46 |
+
keys_to_fetch_from_state = ["temp_plan_file_location"]
|
47 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
48 |
+
temp_plan_file_location = fetched_state["temp_plan_file_location"]
|
49 |
+
if os.path.exists(temp_plan_file_location):
|
50 |
+
os.remove(temp_plan_file_location)
|
51 |
+
# ~~~ return the manual quit status ~~~
|
52 |
+
return {
|
53 |
+
"EARLY_EXIT": True,
|
54 |
+
"plan": "no plan was generated",
|
55 |
+
"summary": "Jarvis/PlanWriter: PlanWriter was terminated explicitly by the user, process is unfinished",
|
56 |
+
"status": "unfinished"
|
57 |
+
}
|
58 |
+
elif command == "write_plan":
|
59 |
+
keys_to_fetch_from_state = ["memory_files"]
|
60 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
61 |
+
output_payload["command_args"]["plan_file_location"] = fetched_state["memory_files"]["plan"]
|
62 |
+
return output_payload
|
63 |
+
else:
|
64 |
+
return output_payload
|
Planner_JarvisFlow.yaml
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: "Planner/Jarvis"
|
2 |
+
description: "Generates plan with interactions with the user, used for the coder class"
|
3 |
+
|
4 |
+
_target_: Tachi67.JarvisFlowModule.Planner_JarvisFlow.instantiate_from_default_config
|
5 |
+
|
6 |
+
input_interface:
|
7 |
+
- "goal"
|
8 |
+
- "memory_files"
|
9 |
+
|
10 |
+
output_interface:
|
11 |
+
- "plan"
|
12 |
+
- "status"
|
13 |
+
- "summary"
|
14 |
+
|
15 |
+
### Subflows specification
|
16 |
+
subflows_config:
|
17 |
+
Controller:
|
18 |
+
_target_: Tachi67.PlanWriterFlowModule.PlanWriterCtrlFlow.instantiate_from_default_config
|
19 |
+
backend:
|
20 |
+
api_infos: ???
|
21 |
+
model_name:
|
22 |
+
openai: gpt-4
|
23 |
+
azure: azure/gpt-4
|
24 |
+
|
25 |
+
Executor:
|
26 |
+
_target_: aiflows.base_flows.BranchingFlow.instantiate_from_default_config
|
27 |
+
subflows_config:
|
28 |
+
write_plan:
|
29 |
+
subflows_config:
|
30 |
+
PlanGenerator:
|
31 |
+
backend:
|
32 |
+
api_infos: ???
|
33 |
+
model_name:
|
34 |
+
openai: gpt-4
|
35 |
+
azure: azure/gpt-4
|
36 |
+
system_message_prompt_template:
|
37 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
38 |
+
template: |2-
|
39 |
+
You are the planner for Jarvis, an intelligent agent able to achieve goals by writing and running code, you write step-by-step plans for Jarvis to follow and execute.
|
40 |
+
|
41 |
+
Jarvis is capable of:
|
42 |
+
1. Generating and running code, given a goal.
|
43 |
+
2. Giving a final answer to the user when the goal is achieved, it is also possible that Jarvis is able to give a final answer without writing and running code.
|
44 |
+
|
45 |
+
**Your task is: given a goal to achieve, decompose the goal into step-by-step plans for Jarvis, each step of your plan should ONLY involve one of the following:
|
46 |
+
1. Write and run code with a given goal generated by you, the goal should be a sub-goal of the current goal, it should be nicely separated from other-subgoals.
|
47 |
+
2. Give a final answer.**
|
48 |
+
|
49 |
+
Here are some criteria you should consider in order to write a good plan:
|
50 |
+
1. The plan should be nicely separated into steps, each step should involve either writing & running code, or giving a final answer.
|
51 |
+
2. Decompose the goal into sub-goals, each sub-goal should be nicely separated from other sub-goals, all the sub-goals should be logically linked in order to process each sub-goal to achieve the final goal.
|
52 |
+
|
53 |
+
Performance Evaluation:
|
54 |
+
1. Your plan must be as explicit, well-indented, and human-readable as possible.
|
55 |
+
2. Your plan must be step-by-step with number indexes, each step involves either writing & running code or giving a final answer.
|
56 |
+
3. You should make plans with as few steps as possible.
|
57 |
+
|
58 |
+
|
59 |
+
**Here is an example of a good plan:**
|
60 |
+
Goal: Send an email with the data of the weather in Paris of 19. Dec. 2021 to my friend.
|
61 |
+
Plan:
|
62 |
+
1. Write and run code to get the data of the weather in Paris of 19. Dec. 2021.
|
63 |
+
2. Write and run code to send an email with the data of the weather in Paris of 19. Dec. 2021 to my friend.
|
64 |
+
3. Give a final answer to the user.
|
65 |
+
|
66 |
+
**It's important that you should only respond in JSON format as described below:**
|
67 |
+
Response Format:
|
68 |
+
{
|
69 |
+
"plan": "A well indented string, containing a step-by-step plan to finish the given goal",
|
70 |
+
}
|
71 |
+
Ensure your responses can be parsed by Python json.loads
|
72 |
+
**Make sure that the plan you generate is a well-indented human readable string, with numbered indexes for each step.**
|
73 |
+
|
74 |
+
PlanFileEditor:
|
75 |
+
_target_: Tachi67.PlanFileEditFlowModule.PlanFileEditAtomicFlow.instantiate_from_default_config
|
76 |
+
|
77 |
+
ParseFeedback:
|
78 |
+
_target_: Tachi67.ParseFeedbackFlowModule.ParseFeedbackAtomicFlow.instantiate_from_default_config
|
79 |
+
input_interface:
|
80 |
+
- "temp_plan_file_location"
|
81 |
+
output_interface:
|
82 |
+
- "plan"
|
83 |
+
- "feedback"
|
84 |
+
|
85 |
+
ask_user:
|
86 |
+
_target_: Tachi67.PlanWriterFlowModule.PlanWriterAskUserFlow.instantiate_from_default_config
|
87 |
+
|
88 |
+
|
89 |
+
early_exit_key: "EARLY_EXIT"
|
90 |
+
|
91 |
+
topology:
|
92 |
+
- goal: "Select the next action and prepare the input for the executor."
|
93 |
+
input_interface:
|
94 |
+
_target_: aiflows.interfaces.KeyInterface
|
95 |
+
additional_transformations:
|
96 |
+
- _target_: aiflows.data_transformations.KeyMatchInput
|
97 |
+
flow: Controller
|
98 |
+
output_interface:
|
99 |
+
_target_: Planner_JarvisFlow.detect_finish_or_continue
|
100 |
+
reset: false
|
101 |
+
|
102 |
+
- goal: "Execute the action specified by the Controller."
|
103 |
+
input_interface:
|
104 |
+
_target_: aiflows.interfaces.KeyInterface
|
105 |
+
keys_to_rename:
|
106 |
+
command: branch
|
107 |
+
command_args: branch_input_data
|
108 |
+
keys_to_select: ["branch", "branch_input_data"]
|
109 |
+
flow: Executor
|
110 |
+
output_interface:
|
111 |
+
_target_: aiflows.interfaces.KeyInterface
|
112 |
+
keys_to_rename:
|
113 |
+
branch_output_data.plan: plan
|
114 |
+
branch_output_data.feedback: feedback
|
115 |
+
branch_output_data.temp_plan_file_location: temp_plan_file_location
|
116 |
+
keys_to_delete: ["branch_output_data"]
|
117 |
+
reset: false
|
README.md
ADDED
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Table of Contents
|
2 |
+
|
3 |
+
* [JarvisFlow](#JarvisFlow)
|
4 |
+
* [JarvisFlow](#JarvisFlow.JarvisFlow)
|
5 |
+
* [Controller\_JarvisFlow](#Controller_JarvisFlow)
|
6 |
+
* [Controller\_JarvisFlow](#Controller_JarvisFlow.Controller_JarvisFlow)
|
7 |
+
* [\_\_init\_\_](#Controller_JarvisFlow.Controller_JarvisFlow.__init__)
|
8 |
+
* [instantiate\_from\_config](#Controller_JarvisFlow.Controller_JarvisFlow.instantiate_from_config)
|
9 |
+
* [UpdatePlanAtomicFlow](#UpdatePlanAtomicFlow)
|
10 |
+
* [UpdatePlanAtomicFlow](#UpdatePlanAtomicFlow.UpdatePlanAtomicFlow)
|
11 |
+
* [Planner\_JarvisFlow](#Planner_JarvisFlow)
|
12 |
+
* [Planner\_JarvisFlow](#Planner_JarvisFlow.Planner_JarvisFlow)
|
13 |
+
* [run\_Jarvis](#run_Jarvis)
|
14 |
+
* [CtrlExMem\_JarvisFlow](#CtrlExMem_JarvisFlow)
|
15 |
+
* [CtrlExMem\_JarvisFlow](#CtrlExMem_JarvisFlow.CtrlExMem_JarvisFlow)
|
16 |
+
* [\_\_init\_\_](#__init__)
|
17 |
+
* [IntermediateAns\_Jarvis](#IntermediateAns_Jarvis)
|
18 |
+
* [IntermediateAns\_Jarvis](#IntermediateAns_Jarvis.IntermediateAns_Jarvis)
|
19 |
+
* [FinalAns\_Jarvis](#FinalAns_Jarvis)
|
20 |
+
* [FinalAns\_Jarvis](#FinalAns_Jarvis.FinalAns_Jarvis)
|
21 |
+
|
22 |
+
<a id="JarvisFlow"></a>
|
23 |
+
|
24 |
+
# JarvisFlow
|
25 |
+
|
26 |
+
<a id="JarvisFlow.JarvisFlow"></a>
|
27 |
+
|
28 |
+
## JarvisFlow Objects
|
29 |
+
|
30 |
+
```python
|
31 |
+
class JarvisFlow(AbstractBossFlow)
|
32 |
+
```
|
33 |
+
|
34 |
+
JarvisFlow is a flow module for the boss Jarvis. It inherits from AbstractBossFlow. (
|
35 |
+
https://huggingface.co/Tachi67/AbstractBossFlowModule/tree/main). Jarvis is a general purpose agent empowered by
|
36 |
+
multiple large language models and tools including a code interpreter, to take task commands in natural language,
|
37 |
+
and make plans, write and run code in an interactive fashion to finish the task.
|
38 |
+
|
39 |
+
The highlight of Jarvis is that it integrates 17 large language models, each of them prompted differently to achieve
|
40 |
+
seamless inter-model communication and model-user interaction. The structure of Jarvis ensures that it is much more
|
41 |
+
robust, flexible and memory-efficient than previous agents empowered by one single model.
|
42 |
+
What's more, Jarvis integrates modules to allow for llm's memory management, ensuring persisted mid-long term memory
|
43 |
+
and efficient short-term memory management, making its life duration much longer than single-modeled agents,
|
44 |
+
and more powerful, in that it is able to accumulate important knowledge e.g. code library. Jarvis can also take
|
45 |
+
response from the user and the environment (e.g. code execution result), and spontaneously re-plan and re-execute
|
46 |
+
to make the execution more robust and reliable.
|
47 |
+
|
48 |
+
*Configuration Parameters*:
|
49 |
+
- `memory_files` (dict): mem_name-memfile_path pairs. mem_name is the name of the memory (plan, logs, code_library),
|
50 |
+
and memfile_path is the path to the corresponding memory file. Configure this either in the .yaml file, or override
|
51 |
+
the `memory_files` entry when running the flow.
|
52 |
+
- `subflows_config` (dict): configs for subflows.
|
53 |
+
- `MemoryReading`: Module used to read in memory (https://huggingface.co/Tachi67/MemoryReadingFlowModule), output interface
|
54 |
+
configured so that it outputs the neeed memory.
|
55 |
+
- `Planner`: Module used to interactively write plans for Jarvis, the planner is implemented in the JarvisFlow.
|
56 |
+
- `CtrlExMem`: Module used to execute the plan in a controller-executor manner, and update the memory. It is implemented
|
57 |
+
in the JarvisFlow.
|
58 |
+
|
59 |
+
**The code interpreter of Jarvis (https://huggingface.co/Tachi67/InterpreterFlowModule) relies on open-interpreter (https://github.com/KillianLucas/open-interpreter)
|
60 |
+
We are extracting the specific code from open-interpreter because the litellm version of open-interpreter is not compatible with that of the current version of aiflows(v.0.1.7).**
|
61 |
+
|
62 |
+
<a id="Controller_JarvisFlow"></a>
|
63 |
+
|
64 |
+
# Controller\_JarvisFlow
|
65 |
+
|
66 |
+
<a id="Controller_JarvisFlow.Controller_JarvisFlow"></a>
|
67 |
+
|
68 |
+
## Controller\_JarvisFlow Objects
|
69 |
+
|
70 |
+
```python
|
71 |
+
class Controller_JarvisFlow(ChatAtomicFlow)
|
72 |
+
```
|
73 |
+
|
74 |
+
This class is a controller for JarvisFlow, it takes the plan generated by the planner, logs of previous executions,
|
75 |
+
depending on the initial goal or the subsequent feedback from the branching executors (and the human), to decide which
|
76 |
+
executor to call next (or to exit by calling finish).
|
77 |
+
|
78 |
+
*Configuration Parameters*:
|
79 |
+
- `commands` (dict): a dictionary of commands that the controller can call, each command has a name, a description, and a list of input arguments.
|
80 |
+
The commands will be injected into the system message prompt template.
|
81 |
+
- `system_message_prompt_template` (str): the template for the system message prompt, there are several components needs to be injected into the
|
82 |
+
template, including the commands, plan, plan_file_location, logs, and the goal. The injection of commands is done then initalizing the flow,
|
83 |
+
the rest of the components are injected at the beginning of each run.
|
84 |
+
- `previous_messages` (int): a sliding window of previous messages that will be passed to the model. This is the central part of short-term memory management.
|
85 |
+
|
86 |
+
*Input Interface Non Initialized*:
|
87 |
+
- `goal` (str): the initial goal of the conversation, this is the input to the model.
|
88 |
+
- `memory_files` (dict): a dictionary of file locations that contains the plan, logs.
|
89 |
+
- `plan` (str): the plan generated by the planner, the plan will change (marked as done, or re-plan) as execution preceeds.
|
90 |
+
- `logs` (str): the logs of previous executions, the logs will be appended as execution preceeds.
|
91 |
+
|
92 |
+
*Input Interface Initialized*:
|
93 |
+
- `result` (str): the result of the previous execution, this is the input to the model.
|
94 |
+
- `memory_files` (dict): a dictionary of file locations that contains the plan, logs.
|
95 |
+
- `plan` (str): the plan generated by the planner, the plan will change (marked as done, or re-plan) as execution preceeds.
|
96 |
+
- `logs` (str): the logs of previous executions, the logs will be appended as execution preceeds.
|
97 |
+
- `goal` (str): the initial goal, this is kept because the goal is also injected into the system prompts so that Jarvis does not
|
98 |
+
forget what the goal is, when the memory sliding window is implemented.
|
99 |
+
|
100 |
+
*Output Interface*:
|
101 |
+
- `command` (str): the command to be executed by the executor.
|
102 |
+
- `command_args` (dict): the arguments of the command to be executed by the executor.
|
103 |
+
|
104 |
+
<a id="Controller_JarvisFlow.Controller_JarvisFlow.__init__"></a>
|
105 |
+
|
106 |
+
#### \_\_init\_\_
|
107 |
+
|
108 |
+
```python
|
109 |
+
def __init__(commands: List[Command], **kwargs)
|
110 |
+
```
|
111 |
+
|
112 |
+
Initialize the flow, inject the commands into the system message prompt template.
|
113 |
+
|
114 |
+
<a id="Controller_JarvisFlow.Controller_JarvisFlow.instantiate_from_config"></a>
|
115 |
+
|
116 |
+
#### instantiate\_from\_config
|
117 |
+
|
118 |
+
```python
|
119 |
+
@classmethod
|
120 |
+
def instantiate_from_config(cls, config)
|
121 |
+
```
|
122 |
+
|
123 |
+
Setting up the flow from the config file. In particular, setting up the prompts, backend, and commands.
|
124 |
+
|
125 |
+
<a id="UpdatePlanAtomicFlow"></a>
|
126 |
+
|
127 |
+
# UpdatePlanAtomicFlow
|
128 |
+
|
129 |
+
<a id="UpdatePlanAtomicFlow.UpdatePlanAtomicFlow"></a>
|
130 |
+
|
131 |
+
## UpdatePlanAtomicFlow Objects
|
132 |
+
|
133 |
+
```python
|
134 |
+
class UpdatePlanAtomicFlow(AtomicFlow)
|
135 |
+
```
|
136 |
+
|
137 |
+
This class is used to update the plan file with the updated plan, called by the controlller,
|
138 |
+
when it realizes one step of the plan is done, and provide the updated plan, it is exactly the same
|
139 |
+
as the old plan, except the step that is done is marked as done.
|
140 |
+
|
141 |
+
<a id="Planner_JarvisFlow"></a>
|
142 |
+
|
143 |
+
# Planner\_JarvisFlow
|
144 |
+
|
145 |
+
<a id="Planner_JarvisFlow.Planner_JarvisFlow"></a>
|
146 |
+
|
147 |
+
## Planner\_JarvisFlow Objects
|
148 |
+
|
149 |
+
```python
|
150 |
+
class Planner_JarvisFlow(PlanWriterFlow)
|
151 |
+
```
|
152 |
+
|
153 |
+
This flow inherits from PlanWriterFlow (https://huggingface.co/Tachi67/PlanWriterFlowModule), and is used to generate a plan for Jarvis.
|
154 |
+
*Input Interface*:
|
155 |
+
- `goal` (str): the goal of the planner, the goal comes from the user's query when calling Jarvis.
|
156 |
+
- `memory_files` (dict): a dictionary of memory files, the keys are the names of the memory files, the values are the locations of the memory files.
|
157 |
+
|
158 |
+
*Output Interfaces*:
|
159 |
+
- `plan` (str): the generated plan, the plan string will be written to the plan file and returned to the flow state of the Jarvis flow.
|
160 |
+
- `summary` (str): the summary of the planner.
|
161 |
+
- `status` (str): the status of the planner, can be "finished" or "unfinished".
|
162 |
+
|
163 |
+
<a id="run_Jarvis"></a>
|
164 |
+
|
165 |
+
# run\_Jarvis
|
166 |
+
|
167 |
+
<a id="CtrlExMem_JarvisFlow"></a>
|
168 |
+
|
169 |
+
# CtrlExMem\_JarvisFlow
|
170 |
+
|
171 |
+
<a id="CtrlExMem_JarvisFlow.CtrlExMem_JarvisFlow"></a>
|
172 |
+
|
173 |
+
## CtrlExMem\_JarvisFlow Objects
|
174 |
+
|
175 |
+
```python
|
176 |
+
class CtrlExMem_JarvisFlow(CtrlExMemFlow)
|
177 |
+
```
|
178 |
+
|
179 |
+
This class inherits from the CtrlExMemFlow class from AbstractBossFlowModule.
|
180 |
+
See: https://huggingface.co/Tachi67/AbstractBossFlowModule/blob/main/CtrlExMemFlow.py
|
181 |
+
|
182 |
+
Take notice that:
|
183 |
+
1. In the controller, we only keep the previous 3 messages for memory management, that will be:
|
184 |
+
a. The assistant message (controller's last command)
|
185 |
+
b. Manually updated new system prompt (new logs, new plans, etc.)
|
186 |
+
c. The user message (result, feedback)
|
187 |
+
2. Each time one executor from the branch is executed, the logs is updated, this means:
|
188 |
+
a. The logs file of Jarvis is updated.
|
189 |
+
b. After MemoryReading at the end of each run of the loop, the logs in the flow_state is updated.
|
190 |
+
c. The next time the controller is called, the updated logs is injected into the system prompts.
|
191 |
+
3. In the prompts of the controller, when the controller realizes one step of the plan is done,
|
192 |
+
we ask the controller to revise what was done and mark the current step as done. This means:
|
193 |
+
a. The plan file is updated.
|
194 |
+
b. The plan in the flow_state is updated.
|
195 |
+
c. The next time the controller is called, the updated plan is injected into the system prompts.
|
196 |
+
|
197 |
+
This is basically how the memory management works, to allow for more space for llm execution, and make sure the llm
|
198 |
+
does not forget important information.
|
199 |
+
|
200 |
+
<a id="__init__"></a>
|
201 |
+
|
202 |
+
# \_\_init\_\_
|
203 |
+
|
204 |
+
<a id="IntermediateAns_Jarvis"></a>
|
205 |
+
|
206 |
+
# IntermediateAns\_Jarvis
|
207 |
+
|
208 |
+
<a id="IntermediateAns_Jarvis.IntermediateAns_Jarvis"></a>
|
209 |
+
|
210 |
+
## IntermediateAns\_Jarvis Objects
|
211 |
+
|
212 |
+
```python
|
213 |
+
class IntermediateAns_Jarvis(HumanStandardInputFlow)
|
214 |
+
```
|
215 |
+
|
216 |
+
This class inherits from the HumanStandardInputFlow class.
|
217 |
+
It is used to give an intermediate answer to the user. The user is then able to provide feedback on the intermediate result.
|
218 |
+
Depending on the user's feedback, the controller will decide to do different things (e.g. continue, re-plan, etc.)
|
219 |
+
|
220 |
+
<a id="FinalAns_Jarvis"></a>
|
221 |
+
|
222 |
+
# FinalAns\_Jarvis
|
223 |
+
|
224 |
+
<a id="FinalAns_Jarvis.FinalAns_Jarvis"></a>
|
225 |
+
|
226 |
+
## FinalAns\_Jarvis Objects
|
227 |
+
|
228 |
+
```python
|
229 |
+
class FinalAns_Jarvis(HumanStandardInputFlow)
|
230 |
+
```
|
231 |
+
|
232 |
+
This class inherits from the HumanStandardInputFlow class.
|
233 |
+
It is used to give the final answer to the user.
|
234 |
+
|
UpdatePlanAtomicFlow.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#TODO: generalize updateplanatomicflow with the one in extendlibrary
|
2 |
+
from typing import Dict, Any
|
3 |
+
from aiflows.base_flows.atomic import AtomicFlow
|
4 |
+
class UpdatePlanAtomicFlow(AtomicFlow):
|
5 |
+
"""This class is used to update the plan file with the updated plan, called by the controlller,
|
6 |
+
when it realizes one step of the plan is done, and provide the updated plan, it is exactly the same
|
7 |
+
as the old plan, except the step that is done is marked as done.
|
8 |
+
"""
|
9 |
+
def _check_input(self, input_data: Dict[str, Any]):
|
10 |
+
assert "memory_files" in input_data, "memory_files not passed to UpdatePlanAtomicFlow.yaml"
|
11 |
+
assert "plan" in input_data["memory_files"], "plan not in memory_files"
|
12 |
+
|
13 |
+
def _call(self, input_data: Dict[str, Any]):
|
14 |
+
try:
|
15 |
+
plan_file_location = input_data["memory_files"]["plan"]
|
16 |
+
plan_to_write = input_data["updated_plan"]
|
17 |
+
with open(plan_file_location, 'w') as file:
|
18 |
+
file.write(plan_to_write + "\n")
|
19 |
+
return {
|
20 |
+
"result": "updated plan saved to the plan file and has overriden the previous plan",
|
21 |
+
"summary": f"Jarvis/UpdatePlanFlow: updated plan saved to {plan_file_location}"
|
22 |
+
}
|
23 |
+
except Exception as e:
|
24 |
+
return {
|
25 |
+
"result": f"Error occurred: {str(e)}",
|
26 |
+
"summary": f"Jarvis/UpdatePlanFlow: error occurred while writing updated plan: {str(e)}"
|
27 |
+
}
|
28 |
+
|
29 |
+
def run(
|
30 |
+
self,
|
31 |
+
input_data: Dict[str, Any]
|
32 |
+
):
|
33 |
+
self._check_input(input_data)
|
34 |
+
return self._call(input_data)
|
UpdatePlanAtomicFlow.yaml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: Tachi67.JarvisFlowModule.UpdatePlanAtomicFlow.instantiate_from_default_config
|
2 |
+
name: "UpdatePlanAtomicFlow"
|
3 |
+
description: "Writes new plan to plan file"
|
4 |
+
|
5 |
+
input_interface:
|
6 |
+
- "updated_plan"
|
7 |
+
|
8 |
+
output_interface:
|
9 |
+
- "result"
|
__init__.py
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
dependencies = [
|
2 |
+
{"url": "Tachi67/AbstractBossFlowModule", "revision": "main"},
|
3 |
+
{"url": "Tachi67/MemoryReadingFlowModule", "revision": "main"},
|
4 |
+
{"url": "Tachi67/PlanWriterFlowModule", "revision": "main"},
|
5 |
+
{"url": "Tachi67/ExtendLibraryFlowModule", "revision": "main"},
|
6 |
+
{"url": "Tachi67/RunCodeFlowModule", "revision": "main"},
|
7 |
+
{"url": "Tachi67/ReplanningFlowModule", "revision": "main"},
|
8 |
+
{"url": "Tachi67/CoderFlowModule", "revision": "main"},
|
9 |
+
{"url": "aiflows/HumanStandardInputFlowModule", "revision": "4ff043522c89a964ea3a928ce09811c51a2b5b98"},
|
10 |
+
{"url": "aiflows/ChatFlowModule", "revision": "297c90d08087d9ff3139521f11d1a48d7dc63ed4"},
|
11 |
+
]
|
12 |
+
|
13 |
+
from aiflows import flow_verse
|
14 |
+
flow_verse.sync_dependencies(dependencies)
|
15 |
+
|
16 |
+
from .UpdatePlanAtomicFlow import UpdatePlanAtomicFlow
|
17 |
+
from .JarvisFlow import JarvisFlow
|
18 |
+
from .Controller_JarvisFlow import Controller_JarvisFlow
|
19 |
+
from .Planner_JarvisFlow import Planner_JarvisFlow
|
20 |
+
from .FinalAns_Jarvis import FinalAns_Jarvis
|
21 |
+
from .IntermediateAns_Jarvis import IntermediateAns_Jarvis
|
22 |
+
from .CtrlExMem_JarvisFlow import CtrlExMem_JarvisFlow
|
23 |
+
|
pip_requirements.txt
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
colorama==0.4.6
|
2 |
+
pytest==7.3.1
|
3 |
+
pytest-cov==4.1.0
|
4 |
+
hydra-core==1.3.2
|
5 |
+
hydra-colorlog==1.1.0
|
6 |
+
wrapt-timeout-decorator==1.3.12.2
|
7 |
+
diskcache==5.6.1
|
8 |
+
openai==1.0.0
|
9 |
+
huggingface_hub==0.19.4
|
10 |
+
jsonlines==3.1.0
|
11 |
+
jinja2==3.1.2
|
12 |
+
mock==5.0.2
|
13 |
+
rich==12.6.0
|
14 |
+
litellm==1.0.0
|
15 |
+
aiflows
|