Upload 10 files
Browse files- CodeWriterAskUserFlow.py +43 -0
- CodeWriterAskUserFlow.yaml +9 -0
- CodeWriterCtrlFlow.py +96 -0
- CodeWriterCtrlFlow.yaml +123 -0
- CodeWriterFlow.py +88 -0
- CodeWriterFlow.yaml +80 -0
- README.md +145 -3
- __init__.py +14 -0
- pip_requirements.txt +15 -0
- run_codewriter.py +61 -0
CodeWriterAskUserFlow.py
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 CodeWriterAskUserFlow(HumanStandardInputFlow):
|
13 |
+
"""This class is used to ask for user feedback whenever the controller is unsure of something, or need confirmation, etc.
|
14 |
+
*Expected Input*:
|
15 |
+
- `question`: The question asked by the controller
|
16 |
+
*Expected Behaviour*:
|
17 |
+
- The question is displayed, and the user gives feedback by inputing string.
|
18 |
+
*Expected Ouput*:
|
19 |
+
- `feedback`: The input of the user.
|
20 |
+
- `code`: No code was written.
|
21 |
+
"""
|
22 |
+
def run(self,
|
23 |
+
input_data: Dict[str, Any]) -> Dict[str, Any]:
|
24 |
+
"""Run the flow.
|
25 |
+
:param input_data: The input data.
|
26 |
+
:return: The output data.
|
27 |
+
"""
|
28 |
+
query_message = self._get_message(self.query_message_prompt_template, input_data)
|
29 |
+
state_update_message = UpdateMessage_Generic(
|
30 |
+
created_by=self.flow_config['name'],
|
31 |
+
updated_flow=self.flow_config["name"],
|
32 |
+
data={"query_message": query_message},
|
33 |
+
)
|
34 |
+
self._log_message(state_update_message)
|
35 |
+
|
36 |
+
log.info(query_message)
|
37 |
+
human_input = self._read_input()
|
38 |
+
|
39 |
+
response = {}
|
40 |
+
response["feedback"] = human_input
|
41 |
+
response["code"] = "no code was written"
|
42 |
+
|
43 |
+
return response
|
CodeWriterAskUserFlow.yaml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: flow_modules.aiflows.CodeWriterFlowModule.CodeWriterAskUserFlow.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 |
+
{{question}}
|
8 |
+
input_variables:
|
9 |
+
- "question"
|
CodeWriterCtrlFlow.py
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
from dataclasses import dataclass
|
8 |
+
|
9 |
+
|
10 |
+
@dataclass
|
11 |
+
class Command:
|
12 |
+
name: str
|
13 |
+
description: str
|
14 |
+
input_args: List[str]
|
15 |
+
|
16 |
+
class CodeWriterCtrlFlow(ChatAtomicFlow):
|
17 |
+
"""refer to https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/Controller_JarvisFlow.py"""
|
18 |
+
def __init__(
|
19 |
+
self,
|
20 |
+
commands: List[Command],
|
21 |
+
**kwargs):
|
22 |
+
super().__init__(**kwargs)
|
23 |
+
self.system_message_prompt_template = self.system_message_prompt_template.partial(
|
24 |
+
commands=self._build_commands_manual(commands),
|
25 |
+
)
|
26 |
+
self.hint_for_model = """
|
27 |
+
Make sure your response is in the following format:
|
28 |
+
Response Format:
|
29 |
+
{
|
30 |
+
"command": "call code writer, the tester, or to finish",
|
31 |
+
"command_args": {
|
32 |
+
"arg name": "value"
|
33 |
+
}
|
34 |
+
}
|
35 |
+
"""
|
36 |
+
|
37 |
+
@staticmethod
|
38 |
+
def _build_commands_manual(commands: List[Command]) -> str:
|
39 |
+
ret = ""
|
40 |
+
for i, command in enumerate(commands):
|
41 |
+
command_input_json_schema = json.dumps(
|
42 |
+
{input_arg: f"YOUR_{input_arg.upper()}" for input_arg in command.input_args})
|
43 |
+
ret += f"{i + 1}. {command.name}: {command.description} Input arguments (given in the JSON schema): {command_input_json_schema}\n"
|
44 |
+
return ret
|
45 |
+
|
46 |
+
@classmethod
|
47 |
+
def instantiate_from_config(cls, config):
|
48 |
+
flow_config = deepcopy(config)
|
49 |
+
|
50 |
+
kwargs = {"flow_config": flow_config}
|
51 |
+
|
52 |
+
# ~~~ Set up prompts ~~~
|
53 |
+
kwargs.update(cls._set_up_prompts(flow_config))
|
54 |
+
|
55 |
+
# ~~~Set up backend ~~~
|
56 |
+
kwargs.update(cls._set_up_backend(flow_config))
|
57 |
+
|
58 |
+
# ~~~ Set up commands ~~~
|
59 |
+
commands = flow_config["commands"]
|
60 |
+
commands = [
|
61 |
+
Command(name, command_conf["description"], command_conf["input_args"]) for name, command_conf in
|
62 |
+
commands.items()
|
63 |
+
]
|
64 |
+
kwargs.update({"commands": commands})
|
65 |
+
|
66 |
+
# ~~~ Instantiate flow ~~~
|
67 |
+
return cls(**kwargs)
|
68 |
+
|
69 |
+
def _update_prompts_and_input(self, input_data: Dict[str, Any]):
|
70 |
+
if 'goal' in input_data:
|
71 |
+
input_data['goal'] += self.hint_for_model
|
72 |
+
if 'feedback' in input_data:
|
73 |
+
input_data['feedback'] += self.hint_for_model
|
74 |
+
|
75 |
+
def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
|
76 |
+
self._update_prompts_and_input(input_data)
|
77 |
+
|
78 |
+
# ~~~when conversation is initialized, append the updated system prompts to the chat history ~~~
|
79 |
+
if self._is_conversation_initialized():
|
80 |
+
updated_system_message_content = self._get_message(self.system_message_prompt_template, input_data)
|
81 |
+
self._state_update_add_chat_message(content=updated_system_message_content,
|
82 |
+
role=self.flow_config["system_name"])
|
83 |
+
|
84 |
+
while True:
|
85 |
+
api_output = super().run(input_data)["api_output"].strip()
|
86 |
+
try:
|
87 |
+
response = json.loads(api_output)
|
88 |
+
return response
|
89 |
+
except (json.decoder.JSONDecodeError, json.JSONDecodeError):
|
90 |
+
updated_system_message_content = self._get_message(self.system_message_prompt_template, input_data)
|
91 |
+
self._state_update_add_chat_message(content=updated_system_message_content,
|
92 |
+
role=self.flow_config["system_name"])
|
93 |
+
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."
|
94 |
+
new_input_data = input_data.copy()
|
95 |
+
new_input_data['feedback'] = new_goal
|
96 |
+
input_data = new_input_data
|
CodeWriterCtrlFlow.yaml
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
_target_: flow_modules.aiflows.CodeWriterFlowModule.CodeWriterCtrlFlow.instantiate_from_default_config
|
2 |
+
name: "CodeWriterControllerFlow"
|
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 |
+
|
13 |
+
input_interface_initialized:
|
14 |
+
- "goal"
|
15 |
+
- "code"
|
16 |
+
- "feedback"
|
17 |
+
|
18 |
+
#######################################################
|
19 |
+
# Output keys
|
20 |
+
#######################################################
|
21 |
+
|
22 |
+
output_interface:
|
23 |
+
- 'command'
|
24 |
+
- 'command_args'
|
25 |
+
|
26 |
+
backend:
|
27 |
+
api_infos: ???
|
28 |
+
model_name:
|
29 |
+
openai: gpt-4
|
30 |
+
azure: azure/gpt-4
|
31 |
+
|
32 |
+
commands:
|
33 |
+
write_code:
|
34 |
+
description: "Write code to finish the goal with user interaction"
|
35 |
+
input_args: ["goal"]
|
36 |
+
finish:
|
37 |
+
description: "Signal that the objective has been satisfied, return the summary of what functions are written, and what are their uses"
|
38 |
+
input_args: ["summary"]
|
39 |
+
manual_finish:
|
40 |
+
description: "The user demands to quit and terminate the current process"
|
41 |
+
input_args: []
|
42 |
+
ask_user:
|
43 |
+
description: "Ask user a question for confirmation or assistance"
|
44 |
+
input_args: ["question"]
|
45 |
+
test:
|
46 |
+
description: "test the code generated from write_code"
|
47 |
+
input_args: []
|
48 |
+
|
49 |
+
system_message_prompt_template:
|
50 |
+
_target_: langchain.PromptTemplate
|
51 |
+
template: |2-
|
52 |
+
You are in charge of a department of writing code to solve a certain goal. You work with a coder, who does all the coding job; and a code tester, who does all the testing job.
|
53 |
+
|
54 |
+
Your **ONLY** task is to take the user's goal for you, to decide whether to call the coder to write or re-write the code, to call the tester to test the code, or to finish the current task.
|
55 |
+
|
56 |
+
Here is the goal you need to achieve:
|
57 |
+
{{goal}}
|
58 |
+
|
59 |
+
When you need to call the code writer, call the `write_code` command with the goal specified.
|
60 |
+
When you need to call the code tester, call the `test` command to test the code written.
|
61 |
+
When the code is written and the user is satisfied, call the `finish` command to terminate the current process with a summary of what functions are written, and what are their uses.
|
62 |
+
Whenever you are in doubt, or need to confirm something to the user, call `ask_user` with the question.
|
63 |
+
|
64 |
+
The coder will only write one function per goal, make sure you are not asking the coder to write more than one function.
|
65 |
+
|
66 |
+
You **must not** write code yourself. You only decide whether to call the coder with specified goals or to finish.
|
67 |
+
|
68 |
+
Your workflow:
|
69 |
+
0. Whenever the user demands to quit or terminate the current process, call `manual_finish` command.
|
70 |
+
1. Upon user request, call the `write_code` with the goal given.
|
71 |
+
2. The coder will write code, which is a function. The user will examine the code, and provide feedback.
|
72 |
+
3. Depending on the feedback of the user:
|
73 |
+
3.1. The user provides feedback on how to change the code, **call the coder with user's specific requirements again, to ask the coder to refine the code**. Go back to step 2.
|
74 |
+
3.2. The user does not provide details about refining the code, for example, just stating the fact that the user has updated the code, **this means the user is satisfied with the code written, call the `finish` command.**
|
75 |
+
4. If the user is satisfied with the code, call `test` to test the code
|
76 |
+
5. Depending on the result of the test:
|
77 |
+
5.1 Test passes, terminate the current process with the `finish` command, with a summary of what functions are written, and what are their uses.
|
78 |
+
5.2 Test fails, **call the coder with details of the test results to instruct the coder to refine the code**, go back to step 2.
|
79 |
+
|
80 |
+
If you have completed all your tasks, make sure to use the "finish" command, with a summary of what functions are written, and what are their uses.
|
81 |
+
|
82 |
+
Constraints:
|
83 |
+
1. Exclusively use the commands listed in double quotes e.g. "command name"
|
84 |
+
|
85 |
+
Your response **MUST** be in the following format:
|
86 |
+
Response Format:
|
87 |
+
{
|
88 |
+
"command": "call code writer, the tester, or to finish",
|
89 |
+
"command_args": {
|
90 |
+
"arg name": "value"
|
91 |
+
}
|
92 |
+
}
|
93 |
+
Ensure your responses can be parsed by Python json.loads
|
94 |
+
|
95 |
+
|
96 |
+
Available Functions:
|
97 |
+
{{commands}}
|
98 |
+
input_variables: ["commands", "goal"]
|
99 |
+
template_format: jinja2
|
100 |
+
|
101 |
+
human_message_prompt_template:
|
102 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
103 |
+
template: |2-
|
104 |
+
Here is the code written by the coder, it might have been updated by the user, depending on the user's feedback:
|
105 |
+
{{code}}
|
106 |
+
Here is the feedback, depending on the last command you called, it either came from the user or the tester:
|
107 |
+
{{feedback}}
|
108 |
+
input_variables:
|
109 |
+
- "code"
|
110 |
+
- "feedback"
|
111 |
+
template_format: jinja2
|
112 |
+
|
113 |
+
init_human_message_prompt_template:
|
114 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
115 |
+
template: |2-
|
116 |
+
Here is the goal you need to achieve:
|
117 |
+
{{goal}}
|
118 |
+
input_variables:
|
119 |
+
- "goal"
|
120 |
+
template_format: jinja2
|
121 |
+
|
122 |
+
previous_messages:
|
123 |
+
last_k: 3
|
CodeWriterFlow.py
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Dict, Any
|
2 |
+
import os
|
3 |
+
|
4 |
+
from flow_modules.aiflows.ContentWriterFlowModule import ContentWriterFlow
|
5 |
+
from aiflows.base_flows import CircularFlow
|
6 |
+
|
7 |
+
|
8 |
+
class CodeWriterFlow(ContentWriterFlow):
|
9 |
+
"""This flow inherits from ContentWriterFlow, it is used to write code in an interactive way.
|
10 |
+
In the subflow of the executor, we specify an InteractiveCodeGenFlow (https://huggingface.co/Tachi67/InteractiveCodeGenFlowModule)
|
11 |
+
|
12 |
+
*Input Interface*:
|
13 |
+
- `goal`
|
14 |
+
|
15 |
+
*Output Interface*:
|
16 |
+
- `code`
|
17 |
+
- `result`
|
18 |
+
- `summary`
|
19 |
+
- `status`
|
20 |
+
"""
|
21 |
+
def _on_reach_max_round(self):
|
22 |
+
self._state_update_dict({
|
23 |
+
"code": "The maximum amount of rounds was reached before the model generated the code.",
|
24 |
+
"status": "unfinished"
|
25 |
+
})
|
26 |
+
|
27 |
+
@CircularFlow.output_msg_payload_processor
|
28 |
+
def detect_finish_or_continue(self, output_payload: Dict[str, Any], src_flow) -> Dict[str, Any]:
|
29 |
+
command = output_payload["command"]
|
30 |
+
if command == "finish":
|
31 |
+
# ~~~ delete the temp code file ~~~
|
32 |
+
keys_to_fetch_from_state = ["temp_code_file_location", "code"]
|
33 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
34 |
+
temp_code_file_location = fetched_state["temp_code_file_location"]
|
35 |
+
code_content = fetched_state["code"]
|
36 |
+
if os.path.exists(temp_code_file_location):
|
37 |
+
os.remove(temp_code_file_location)
|
38 |
+
# ~~~ return the code content ~~~
|
39 |
+
return {
|
40 |
+
"EARLY_EXIT": True,
|
41 |
+
"code": code_content,
|
42 |
+
"result": output_payload["command_args"]["summary"],
|
43 |
+
"summary": "ExtendLibrary/CodeWriter: " + output_payload["command_args"]["summary"],
|
44 |
+
"status": "finished"
|
45 |
+
}
|
46 |
+
elif command == "manual_finish":
|
47 |
+
# ~~~ delete the temp code file ~~~
|
48 |
+
keys_to_fetch_from_state = ["temp_code_file_location"]
|
49 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
50 |
+
temp_code_file_location = fetched_state["temp_code_file_location"]
|
51 |
+
if os.path.exists(temp_code_file_location):
|
52 |
+
os.remove(temp_code_file_location)
|
53 |
+
# ~~~ return the manual quit status ~~~
|
54 |
+
return {
|
55 |
+
"EARLY_EXIT": True,
|
56 |
+
"code": "no code was generated",
|
57 |
+
"result": "CodeWriter was terminated explicitly by the user, process is unfinished",
|
58 |
+
"summary": "ExtendLibrary/CodeWriter: CodeWriter was terminated explicitly by the user, process is unfinished",
|
59 |
+
"status": "unfinished"
|
60 |
+
}
|
61 |
+
elif command == "test":
|
62 |
+
# ~~~ fetch code string from flow state ~~~
|
63 |
+
keys_to_fetch_from_state = ["code"]
|
64 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
65 |
+
|
66 |
+
# ~~~ add code content to the command args (branch input data) ~~~
|
67 |
+
code_content = fetched_state["code"]
|
68 |
+
output_payload["command_args"]["code"] = code_content
|
69 |
+
|
70 |
+
return output_payload
|
71 |
+
else:
|
72 |
+
return output_payload
|
73 |
+
|
74 |
+
def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
|
75 |
+
# ~~~ sets the input_data in the flow_state dict ~~~
|
76 |
+
self._state_update_dict(update_data=input_data)
|
77 |
+
|
78 |
+
max_rounds = self.flow_config.get("max_rounds", 1)
|
79 |
+
if max_rounds is None:
|
80 |
+
log.info(f"Running {self.flow_config['name']} without `max_rounds` until the early exit condition is met.")
|
81 |
+
|
82 |
+
self._sequential_run(max_rounds=max_rounds)
|
83 |
+
|
84 |
+
output = self._get_output_from_state()
|
85 |
+
|
86 |
+
self.reset(full_reset=True, recursive=True, src_flow=self)
|
87 |
+
|
88 |
+
return output
|
CodeWriterFlow.yaml
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: "CodeWriter"
|
2 |
+
description: "Generates code with interactions with the user"
|
3 |
+
|
4 |
+
_target_: flow_modules.aiflows.CodeWriterFlowModule.CodeWriterFlow.instantiate_from_default_config
|
5 |
+
|
6 |
+
output_interface:
|
7 |
+
- "code"
|
8 |
+
- "status"
|
9 |
+
- "summary"
|
10 |
+
- "result"
|
11 |
+
|
12 |
+
### Subflows specification
|
13 |
+
subflows_config:
|
14 |
+
Controller:
|
15 |
+
_target_: flow_modules.aiflows.CodeWriterFlowModule.CodeWriterCtrlFlow.instantiate_from_default_config
|
16 |
+
backend:
|
17 |
+
api_infos: ???
|
18 |
+
model_name:
|
19 |
+
openai: gpt-4
|
20 |
+
azure: azure/gpt-4
|
21 |
+
|
22 |
+
Executor:
|
23 |
+
_target_: aiflows.base_flows.BranchingFlow.instantiate_from_default_config
|
24 |
+
subflows_config:
|
25 |
+
write_code:
|
26 |
+
_target_: flow_modules.aiflows.InteractiveCodeGenFlowModule.InteractiveCodeGenFlow.instantiate_from_default_config
|
27 |
+
subflows_config:
|
28 |
+
MemoryReading:
|
29 |
+
_target_: flow_modules.aiflows.MemoryReadingFlowModule.MemoryReadingAtomicFlow.instantiate_from_default_config
|
30 |
+
|
31 |
+
CodeGenerator:
|
32 |
+
_target_: flow_modules.aiflows.CodeGeneratorFlowModule.CodeGeneratorAtomicFlow.instantiate_from_default_config
|
33 |
+
backend:
|
34 |
+
api_infos: ???
|
35 |
+
model_name:
|
36 |
+
openai: gpt-4
|
37 |
+
azure: azure/gpt-4
|
38 |
+
|
39 |
+
CodeFileEditor:
|
40 |
+
_target_: flow_modules.aiflows.CodeFileEditFlowModule.CodeFileEditAtomicFlow.instantiate_from_default_config
|
41 |
+
|
42 |
+
ParseFeedback:
|
43 |
+
_target_: flow_modules.aiflows.ParseFeedbackFlowModule.ParseFeedbackAtomicFlow.instantiate_from_default_config
|
44 |
+
|
45 |
+
ask_user:
|
46 |
+
_target_: flow_modules.aiflows.CodeWriterFlowModule.CodeWriterAskUserFlow.instantiate_from_default_config
|
47 |
+
|
48 |
+
test:
|
49 |
+
_target_: flow_modules.aiflows.TestCodeFlowModule.TestCodeFlow.instantiate_from_default_config
|
50 |
+
|
51 |
+
|
52 |
+
early_exit_key: "EARLY_EXIT"
|
53 |
+
|
54 |
+
topology:
|
55 |
+
- goal: "Select the next action and prepare the input for the executor."
|
56 |
+
input_interface:
|
57 |
+
_target_: aiflows.interfaces.KeyInterface
|
58 |
+
additional_transformations:
|
59 |
+
- _target_: aiflows.data_transformations.KeyMatchInput
|
60 |
+
flow: Controller
|
61 |
+
output_interface:
|
62 |
+
_target_: CodeWriterFlow.detect_finish_or_continue
|
63 |
+
reset: false
|
64 |
+
|
65 |
+
- goal: "Execute the action specified by the Controller."
|
66 |
+
input_interface:
|
67 |
+
_target_: aiflows.interfaces.KeyInterface
|
68 |
+
keys_to_rename:
|
69 |
+
command: branch
|
70 |
+
command_args: branch_input_data
|
71 |
+
keys_to_select: ["branch", "branch_input_data"]
|
72 |
+
flow: Executor
|
73 |
+
output_interface:
|
74 |
+
_target_: aiflows.interfaces.KeyInterface
|
75 |
+
keys_to_rename:
|
76 |
+
branch_output_data.code: code
|
77 |
+
branch_output_data.feedback: feedback
|
78 |
+
branch_output_data.temp_code_file_location: temp_code_file_location
|
79 |
+
keys_to_delete: ["branch_output_data"]
|
80 |
+
reset: false
|
README.md
CHANGED
@@ -1,3 +1,145 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
### Structure of CodeWriterFlow
|
2 |
+
|
3 |
+
```
|
4 |
+
goal
|
5 |
+
|
|
6 |
+
v
|
7 |
+
+---------------+
|
8 |
+
| Controller | --------<<<<-----------+
|
9 |
+
+---------------+ |
|
10 |
+
| |
|
11 |
+
| (command, command args) |
|
12 |
+
| |
|
13 |
+
v |
|
14 |
+
+------------------+ |
|
15 |
+
| Executor | Each branch is an |
|
16 |
+
| (Tree Structure) | executor |
|
17 |
+
+------------------+ |
|
18 |
+
| ^
|
19 |
+
| (summary) |
|
20 |
+
| |
|
21 |
+
v |
|
22 |
+
| |
|
23 |
+
+-> goes back to the Controller>-+
|
24 |
+
|
25 |
+
```
|
26 |
+
|
27 |
+
Structure of the Executors:
|
28 |
+
```
|
29 |
+
+-------------------+
|
30 |
+
| Branching |
|
31 |
+
| Executor |
|
32 |
+
+-------------------+
|
33 |
+
/ | \
|
34 |
+
/ | \
|
35 |
+
/ | \
|
36 |
+
/ | \
|
37 |
+
write_code ask_user test
|
38 |
+
|
39 |
+
```
|
40 |
+
|
41 |
+
About the branches:
|
42 |
+
- [ask_user](https://huggingface.co/Tachi67/CodeWriterFlowModule/blob/main/CodeWriterAskUserFlow.py): Ask user for info / confirmation, etc.
|
43 |
+
- [write_code](https://huggingface.co/Tachi67/InteractiveCodeGenFlowModule): Generates code (user edit is allowed) and fetches user feedback.
|
44 |
+
- [test](https://huggingface.co/Tachi67/TestCodeFlowModule): Test the code, user can provide test suites, if nothing is provided, syntax of the code is checked.
|
45 |
+
|
46 |
+
How it works:
|
47 |
+
Controller calls write_code until user is satisfied in the feedback, then controller calls test to test the code, if test passes, finish.
|
48 |
+
|
49 |
+
# Table of Contents
|
50 |
+
|
51 |
+
* [run\_codewriter](#run_codewriter)
|
52 |
+
* [CodeWriterCtrlFlow](#CodeWriterCtrlFlow)
|
53 |
+
* [CodeWriterCtrlFlow](#CodeWriterCtrlFlow.CodeWriterCtrlFlow)
|
54 |
+
* [CodeWriterAskUserFlow](#CodeWriterAskUserFlow)
|
55 |
+
* [CodeWriterAskUserFlow](#CodeWriterAskUserFlow.CodeWriterAskUserFlow)
|
56 |
+
* [run](#CodeWriterAskUserFlow.CodeWriterAskUserFlow.run)
|
57 |
+
* [CodeWriterFlow](#CodeWriterFlow)
|
58 |
+
* [CodeWriterFlow](#CodeWriterFlow.CodeWriterFlow)
|
59 |
+
* [\_\_init\_\_](#__init__)
|
60 |
+
|
61 |
+
<a id="run_codewriter"></a>
|
62 |
+
|
63 |
+
# run\_codewriter
|
64 |
+
|
65 |
+
<a id="CodeWriterCtrlFlow"></a>
|
66 |
+
|
67 |
+
# CodeWriterCtrlFlow
|
68 |
+
|
69 |
+
<a id="CodeWriterCtrlFlow.CodeWriterCtrlFlow"></a>
|
70 |
+
|
71 |
+
## CodeWriterCtrlFlow Objects
|
72 |
+
|
73 |
+
```python
|
74 |
+
class CodeWriterCtrlFlow(ChatAtomicFlow)
|
75 |
+
```
|
76 |
+
|
77 |
+
refer to https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/Controller_JarvisFlow.py
|
78 |
+
|
79 |
+
<a id="CodeWriterAskUserFlow"></a>
|
80 |
+
|
81 |
+
# CodeWriterAskUserFlow
|
82 |
+
|
83 |
+
<a id="CodeWriterAskUserFlow.CodeWriterAskUserFlow"></a>
|
84 |
+
|
85 |
+
## CodeWriterAskUserFlow Objects
|
86 |
+
|
87 |
+
```python
|
88 |
+
class CodeWriterAskUserFlow(HumanStandardInputFlow)
|
89 |
+
```
|
90 |
+
|
91 |
+
This class is used to ask for user feedback whenever the controller is unsure of something, or need confirmation, etc.
|
92 |
+
*Expected Input*:
|
93 |
+
- `question`: The question asked by the controller
|
94 |
+
*Expected Behaviour*:
|
95 |
+
- The question is displayed, and the user gives feedback by inputing string.
|
96 |
+
*Expected Ouput*:
|
97 |
+
- `feedback`: The input of the user.
|
98 |
+
- `code`: No code was written.
|
99 |
+
|
100 |
+
<a id="CodeWriterAskUserFlow.CodeWriterAskUserFlow.run"></a>
|
101 |
+
|
102 |
+
#### run
|
103 |
+
|
104 |
+
```python
|
105 |
+
def run(input_data: Dict[str, Any]) -> Dict[str, Any]
|
106 |
+
```
|
107 |
+
|
108 |
+
Run the flow.
|
109 |
+
|
110 |
+
**Arguments**:
|
111 |
+
|
112 |
+
- `input_data`: The input data.
|
113 |
+
|
114 |
+
**Returns**:
|
115 |
+
|
116 |
+
The output data.
|
117 |
+
|
118 |
+
<a id="CodeWriterFlow"></a>
|
119 |
+
|
120 |
+
# CodeWriterFlow
|
121 |
+
|
122 |
+
<a id="CodeWriterFlow.CodeWriterFlow"></a>
|
123 |
+
|
124 |
+
## CodeWriterFlow Objects
|
125 |
+
|
126 |
+
```python
|
127 |
+
class CodeWriterFlow(ContentWriterFlow)
|
128 |
+
```
|
129 |
+
|
130 |
+
This flow inherits from ContentWriterFlow, it is used to write code in an interactive way.
|
131 |
+
In the subflow of the executor, we specify an InteractiveCodeGenFlow (https://huggingface.co/Tachi67/InteractiveCodeGenFlowModule)
|
132 |
+
|
133 |
+
*Input Interface*:
|
134 |
+
- `goal`
|
135 |
+
|
136 |
+
*Output Interface*:
|
137 |
+
- `code`
|
138 |
+
- `result`
|
139 |
+
- `summary`
|
140 |
+
- `status`
|
141 |
+
|
142 |
+
<a id="__init__"></a>
|
143 |
+
|
144 |
+
# \_\_init\_\_
|
145 |
+
|
__init__.py
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
dependencies = [
|
2 |
+
{"url": "aiflows/ContentWriterFlowModule", "revision": "main"},
|
3 |
+
{"url": "aiflows/InteractiveCodeGenFlowModule", "revision": "main"},
|
4 |
+
{"url": "aiflows/TestCodeFlowModule", "revision": "main"},
|
5 |
+
{"url": "aiflows/ChatFlowModule", "revision": "297c90d08087d9ff3139521f11d1a48d7dc63ed4"},
|
6 |
+
{"url": "aiflows/HumanStandardInputFlowModule", "revision": "4ff043522c89a964ea3a928ce09811c51a2b5b98"}
|
7 |
+
]
|
8 |
+
from aiflows import flow_verse
|
9 |
+
|
10 |
+
flow_verse.sync_dependencies(dependencies)
|
11 |
+
|
12 |
+
from .CodeWriterFlow import CodeWriterFlow
|
13 |
+
from .CodeWriterCtrlFlow import CodeWriterCtrlFlow
|
14 |
+
from .CodeWriterAskUserFlow import CodeWriterAskUserFlow
|
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
|
run_codewriter.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
import hydra
|
4 |
+
|
5 |
+
from aiflows.backends.api_info import ApiInfo
|
6 |
+
from aiflows.messages import InputMessage
|
7 |
+
from aiflows.utils.general_helpers import read_yaml_file, quick_load
|
8 |
+
|
9 |
+
from aiflows import logging
|
10 |
+
from aiflows.flow_cache import CACHING_PARAMETERS, clear_cache
|
11 |
+
|
12 |
+
CACHING_PARAMETERS.do_caching = False # Set to True in order to disable caching
|
13 |
+
# clear_cache() # Uncomment this line to clear the cache
|
14 |
+
|
15 |
+
logging.set_verbosity_debug()
|
16 |
+
logging.auto_set_dir()
|
17 |
+
|
18 |
+
dependencies = [
|
19 |
+
{"url": "aiflows/ContentWriterFlowModule", "revision": "main"},
|
20 |
+
{"url": "aiflows/InteractiveCodeGenFlowModule", "revision": "main"},
|
21 |
+
{"url": "aiflows/CodeWriterFlowModule", "revision": "main"},
|
22 |
+
{"url": "aiflows/ChatFlowModule", "revision": "297c90d08087d9ff3139521f11d1a48d7dc63ed4"},
|
23 |
+
]
|
24 |
+
|
25 |
+
from aiflows import flow_verse
|
26 |
+
|
27 |
+
flow_verse.sync_dependencies(dependencies)
|
28 |
+
|
29 |
+
if __name__ == "__main__":
|
30 |
+
# ~~~ make sure to set the openai api key in the envs ~~~
|
31 |
+
key = os.getenv("OPENAI_API_KEY")
|
32 |
+
api_information = [ApiInfo(backend_used="openai", api_key=os.getenv("OPENAI_API_KEY"))]
|
33 |
+
path_to_output_file = None
|
34 |
+
|
35 |
+
# ~~~ setting api information into config ~~~
|
36 |
+
current_dir = os.getcwd()
|
37 |
+
cfg_path = os.path.join(current_dir, "CodeWriterFlow.yaml")
|
38 |
+
cfg = read_yaml_file(cfg_path)
|
39 |
+
quick_load(cfg, api_information)
|
40 |
+
|
41 |
+
|
42 |
+
# ~~~ setting code library into config ~~~
|
43 |
+
code_lib_file_loc = os.path.join(current_dir, "library.py")
|
44 |
+
with open(code_lib_file_loc, 'w') as file:
|
45 |
+
pass
|
46 |
+
cfg["subflows_config"]["Executor"]["subflows_config"]["write_code"]["memory_files"] = {"code_library": code_lib_file_loc}
|
47 |
+
cfg["subflows_config"]["Executor"]["subflows_config"]["test"]["memory_files"] = {"code_library": code_lib_file_loc}
|
48 |
+
|
49 |
+
# ~~~ instantiating the flow and input data ~~~
|
50 |
+
CodeWriterFlow = hydra.utils.instantiate(cfg, _recursive_=False, _convert_="partial")
|
51 |
+
input_data = {
|
52 |
+
"goal": "create a function that adds two numbers and returns the result",
|
53 |
+
}
|
54 |
+
input_message = InputMessage.build(
|
55 |
+
data_dict=input_data,
|
56 |
+
src_flow="Launcher",
|
57 |
+
dst_flow=CodeWriterFlow.name
|
58 |
+
)
|
59 |
+
|
60 |
+
# ~~~ calling the flow ~~~
|
61 |
+
output_message = CodeWriterFlow(input_message)
|