Tachi67 commited on
Commit
962d35d
1 Parent(s): 69fa46c

Init commit: migrate JarvisFlow from Tachi67/JarvisFlowModule to aiflows

Browse files
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