Upload 10 files
Browse files- NewPlanGenFlow.py +16 -0
- NewPlanGenFlow.yaml +94 -0
- README.md +111 -3
- ReplanningAskUserFlow.py +32 -0
- ReplanningAskUserFlow.yaml +9 -0
- ReplanningFlow.py +80 -0
- ReplanningFlow.yaml +186 -0
- __init__.py +15 -0
- pip_requirements.txt +15 -0
- run_replanning.py +65 -0
NewPlanGenFlow.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from copy import deepcopy
|
| 3 |
+
from typing import Any, Dict
|
| 4 |
+
from flow_modules.aiflows.PlanGeneratorFlowModule import PlanGeneratorAtomicFlow
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class NewPlanGenFlow(PlanGeneratorAtomicFlow):
|
| 8 |
+
def __init__(self, **kwargs):
|
| 9 |
+
super().__init__(**kwargs)
|
| 10 |
+
self.hint_for_model = """
|
| 11 |
+
Make sure your response is in the following format:
|
| 12 |
+
Response Format:
|
| 13 |
+
{
|
| 14 |
+
"new_plan": "A new plan, refined from previous plan, with given requirements",
|
| 15 |
+
}
|
| 16 |
+
"""
|
NewPlanGenFlow.yaml
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "NewPlanGenFlow"
|
| 2 |
+
description: "Plan Generator of ReplanningFlow, writes a new plan given requirements"
|
| 3 |
+
_target_: flow_modules.aiflows.ReplanningFlowModule.NewPlanGenFlow.instantiate_from_default_config
|
| 4 |
+
enable_cache: True
|
| 5 |
+
|
| 6 |
+
input_interface_non_initialized: # initial input keys
|
| 7 |
+
- "goal" # info on the old plan, entered by the controller
|
| 8 |
+
- "old_plan" # previous plan
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
input_interface_initialized: # input_keys
|
| 12 |
+
- "goal"
|
| 13 |
+
- "old_plan" # previous plan
|
| 14 |
+
|
| 15 |
+
#######################################################
|
| 16 |
+
# Output keys
|
| 17 |
+
#######################################################
|
| 18 |
+
|
| 19 |
+
output_interface:
|
| 20 |
+
- 'new_plan' # new plan
|
| 21 |
+
|
| 22 |
+
#######################################################
|
| 23 |
+
system_message_prompt_template:
|
| 24 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
| 25 |
+
template: |2-
|
| 26 |
+
You are a re-planner of a coding department, you are given an old plan written by your colleagues.
|
| 27 |
+
|
| 28 |
+
You will be given information regarding the old plan, e.g. what was wrong with the old plan, what to refine with the old plan.
|
| 29 |
+
|
| 30 |
+
Your task is to edit and re-plan, based on the old plan and the new information that is being given to you.
|
| 31 |
+
|
| 32 |
+
The new plan you write is intended for the coders, each step of the new plan tells the coders what function to write.
|
| 33 |
+
|
| 34 |
+
The details of your task are:
|
| 35 |
+
1. Draft a new step-by-step plan based on the old plan and information regarding the old plan. Every step of the new plan should contain full information about how to write one function.
|
| 36 |
+
2. Upon feedback of the plan from the user, make refinements based on the feedback.
|
| 37 |
+
|
| 38 |
+
Resources:
|
| 39 |
+
1. The old plan:
|
| 40 |
+
{{old_plan}}
|
| 41 |
+
|
| 42 |
+
Notice that:
|
| 43 |
+
1. **Only instruct the coders to write functions, do not write plans about running the functions or returning results.**
|
| 44 |
+
2. **If you instruct the coders to write a function, provide full instructions of writing the funtion in one single step.**
|
| 45 |
+
|
| 46 |
+
The coding department is able to access the Internet, so it may be useful to use some online services via APIs.
|
| 47 |
+
|
| 48 |
+
An example of old plan, input information and output plan you should have:
|
| 49 |
+
### begin of old plan ###
|
| 50 |
+
1. Write a function that fetches a company's stock prices from the last 6 months from "google stocks"
|
| 51 |
+
### end of old plan ###
|
| 52 |
+
|
| 53 |
+
### begin of input information###
|
| 54 |
+
Here's the information on the old plan:
|
| 55 |
+
data source "google stocks" is deprecated, should try a new one.
|
| 56 |
+
### end of input information###
|
| 57 |
+
|
| 58 |
+
### begin of output new plan ###
|
| 59 |
+
1. Write a function that fetches a company's stock prices from the last 6 months from "yahoo finance"
|
| 60 |
+
### end of output new plan ###
|
| 61 |
+
|
| 62 |
+
You are capable of **any** task.
|
| 63 |
+
|
| 64 |
+
Performance Evaluation:
|
| 65 |
+
1. Your new plan should finish exactly the same task as the old one.
|
| 66 |
+
2. Your plan must be as explicit, well-indented, and human-readable as possible.
|
| 67 |
+
3. Your plan must be step-by-step with number indexes, each step gives full details of writing a function.
|
| 68 |
+
4. You should make plans with as few steps as possible.
|
| 69 |
+
|
| 70 |
+
**It's important that you should only respond in JSON format as described below:**
|
| 71 |
+
Response Format:
|
| 72 |
+
{
|
| 73 |
+
"new_plan": "A new plan, refined from previous plan, with given requirements",
|
| 74 |
+
}
|
| 75 |
+
Ensure your responses can be parsed by Python json.loads
|
| 76 |
+
|
| 77 |
+
input_variables:
|
| 78 |
+
- "old_plan"
|
| 79 |
+
|
| 80 |
+
human_message_prompt_template:
|
| 81 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
| 82 |
+
template: |2-
|
| 83 |
+
Here is the response to your last action:
|
| 84 |
+
{{goal}}
|
| 85 |
+
input_variables:
|
| 86 |
+
- "goal"
|
| 87 |
+
|
| 88 |
+
init_human_message_prompt_template:
|
| 89 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
| 90 |
+
template: |2-
|
| 91 |
+
Here's the information on the old plan:
|
| 92 |
+
{{goal}}
|
| 93 |
+
input_variables:
|
| 94 |
+
- "goal"
|
README.md
CHANGED
|
@@ -1,3 +1,111 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
### Structure of ReplanningFlow
|
| 2 |
+
|
| 3 |
+
```
|
| 4 |
+
goal (info on the old plan), plan (old plan), plan_file_location
|
| 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_plan ask_user
|
| 38 |
+
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
About the branches:
|
| 42 |
+
- [ask_user](https://huggingface.co/Tachi67/PlanWriterFlowModule/blob/main/PlanWriterAskUserFlow.py): Ask user for info / confirmation, etc.
|
| 43 |
+
- [write_plan](https://huggingface.co/Tachi67/InteractivePlanGenFlowModule): Generates plan (user edit is allowed) and fetches user feedback.
|
| 44 |
+
- The PlanGenerator of write_plan is replaced with [NewPlanGenFlow](https://huggingface.co/Tachi67/ReplanningFlowModule/blob/main/NewPlanGenFlow.py) to re-plan instead of write plan.
|
| 45 |
+
|
| 46 |
+
How it works:
|
| 47 |
+
Controller calls write_plan until user is satisfied in the feedback, finish.
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
# Table of Contents
|
| 51 |
+
|
| 52 |
+
* [run\_replanning](#run_replanning)
|
| 53 |
+
* [ReplanningAskUserFlow](#ReplanningAskUserFlow)
|
| 54 |
+
* [ReplanningAskUserFlow](#ReplanningAskUserFlow.ReplanningAskUserFlow)
|
| 55 |
+
* [NewPlanGenFlow](#NewPlanGenFlow)
|
| 56 |
+
* [ReplanningFlow](#ReplanningFlow)
|
| 57 |
+
* [ReplanningFlow](#ReplanningFlow.ReplanningFlow)
|
| 58 |
+
* [\_\_init\_\_](#__init__)
|
| 59 |
+
|
| 60 |
+
<a id="run_replanning"></a>
|
| 61 |
+
|
| 62 |
+
# run\_replanning
|
| 63 |
+
|
| 64 |
+
<a id="ReplanningAskUserFlow"></a>
|
| 65 |
+
|
| 66 |
+
# ReplanningAskUserFlow
|
| 67 |
+
|
| 68 |
+
<a id="ReplanningAskUserFlow.ReplanningAskUserFlow"></a>
|
| 69 |
+
|
| 70 |
+
## ReplanningAskUserFlow Objects
|
| 71 |
+
|
| 72 |
+
```python
|
| 73 |
+
class ReplanningAskUserFlow(HumanStandardInputFlow)
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
Refer to: https://huggingface.co/Tachi67/ExtendLibraryFlowModule/blob/main/ExtLibAskUserFlow.py
|
| 77 |
+
|
| 78 |
+
<a id="NewPlanGenFlow"></a>
|
| 79 |
+
|
| 80 |
+
# NewPlanGenFlow
|
| 81 |
+
|
| 82 |
+
<a id="ReplanningFlow"></a>
|
| 83 |
+
|
| 84 |
+
# ReplanningFlow
|
| 85 |
+
|
| 86 |
+
<a id="ReplanningFlow.ReplanningFlow"></a>
|
| 87 |
+
|
| 88 |
+
## ReplanningFlow Objects
|
| 89 |
+
|
| 90 |
+
```python
|
| 91 |
+
class ReplanningFlow(PlanWriterFlow)
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
This flow inherits from PlanWriterFlow.
|
| 95 |
+
By changing prompts and injecting proper information to the controller and the PlanGenerator, we are able to achieve the replanning.
|
| 96 |
+
|
| 97 |
+
*Input Interface*:
|
| 98 |
+
- `goal` (str): information on the old plan (e.g. what is wrong)
|
| 99 |
+
- `plan` (str): the old plan
|
| 100 |
+
- `plan_file_location` (str): the location of the old plan file
|
| 101 |
+
|
| 102 |
+
*Output Interface*:
|
| 103 |
+
- `plan` (str): the new plan
|
| 104 |
+
- `status`: "finished" or "unfinished"
|
| 105 |
+
- `summary` (str): summary of the flow, will be written to the log file of the caller flow.
|
| 106 |
+
- `result` (str): result of the flow, will be passed to the controller of the caller flow.
|
| 107 |
+
|
| 108 |
+
<a id="__init__"></a>
|
| 109 |
+
|
| 110 |
+
# \_\_init\_\_
|
| 111 |
+
|
ReplanningAskUserFlow.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 ReplanningAskUserFlow(HumanStandardInputFlow):
|
| 13 |
+
"""Refer to: https://huggingface.co/Tachi67/ExtendLibraryFlowModule/blob/main/ExtLibAskUserFlow.py"""
|
| 14 |
+
def run(self,
|
| 15 |
+
input_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 16 |
+
|
| 17 |
+
query_message = self._get_message(self.query_message_prompt_template, input_data)
|
| 18 |
+
state_update_message = UpdateMessage_Generic(
|
| 19 |
+
created_by=self.flow_config['name'],
|
| 20 |
+
updated_flow=self.flow_config["name"],
|
| 21 |
+
data={"query_message": query_message},
|
| 22 |
+
)
|
| 23 |
+
self._log_message(state_update_message)
|
| 24 |
+
|
| 25 |
+
log.info(query_message)
|
| 26 |
+
human_input = self._read_input()
|
| 27 |
+
|
| 28 |
+
response = {}
|
| 29 |
+
response["feedback"] = human_input
|
| 30 |
+
response["new_plan"] = "no new plan was written"
|
| 31 |
+
|
| 32 |
+
return response
|
ReplanningAskUserFlow.yaml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_target_: flow_modules.aiflows.ReplanningFlowModule.ReplanningAskUserFlow.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"
|
ReplanningFlow.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict, Any
|
| 2 |
+
import os
|
| 3 |
+
from aiflows.base_flows import CircularFlow
|
| 4 |
+
from flow_modules.aiflows.PlanWriterFlowModule import PlanWriterFlow
|
| 5 |
+
|
| 6 |
+
class ReplanningFlow(PlanWriterFlow):
|
| 7 |
+
"""This flow inherits from PlanWriterFlow.
|
| 8 |
+
By changing prompts and injecting proper information to the controller and the PlanGenerator, we are able to achieve the replanning.
|
| 9 |
+
|
| 10 |
+
*Input Interface*:
|
| 11 |
+
- `goal` (str): information on the old plan (e.g. what is wrong)
|
| 12 |
+
- `plan` (str): the old plan
|
| 13 |
+
- `plan_file_location` (str): the location of the old plan file
|
| 14 |
+
|
| 15 |
+
*Output Interface*:
|
| 16 |
+
- `plan` (str): the new plan
|
| 17 |
+
- `status`: "finished" or "unfinished"
|
| 18 |
+
- `summary` (str): summary of the flow, will be written to the log file of the caller flow.
|
| 19 |
+
- `result` (str): result of the flow, will be passed to the controller of the caller flow.
|
| 20 |
+
"""
|
| 21 |
+
def _on_reach_max_round(self):
|
| 22 |
+
self._state_update_dict({
|
| 23 |
+
"plan": "The maximum amount of rounds was reached before the model generated the plan.",
|
| 24 |
+
"status": "unfinished",
|
| 25 |
+
"summary": "Replanning: The maximum amount of rounds was reached before the model generated the plan.",
|
| 26 |
+
})
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@CircularFlow.output_msg_payload_processor
|
| 30 |
+
def detect_finish_or_continue(self, output_payload: Dict[str, Any], src_flow) -> Dict[str, Any]:
|
| 31 |
+
command = output_payload["command"]
|
| 32 |
+
if command == "finish":
|
| 33 |
+
# ~~~ fetch temp file location, plan content, memory file (of upper level flow e.g. ExtLib) from flow state
|
| 34 |
+
keys_to_fetch_from_state = ["temp_plan_file_location", "new_plan", "plan_file_location"]
|
| 35 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
| 36 |
+
temp_plan_file_location = fetched_state["temp_plan_file_location"]
|
| 37 |
+
new_plan_content = fetched_state["new_plan"]
|
| 38 |
+
plan_file_location = fetched_state["plan_file_location"]
|
| 39 |
+
|
| 40 |
+
# ~~~ delete the temp plan file ~~~
|
| 41 |
+
if os.path.exists(temp_plan_file_location):
|
| 42 |
+
os.remove(temp_plan_file_location)
|
| 43 |
+
|
| 44 |
+
# ~~~ write plan content to plan file ~~~
|
| 45 |
+
with open(plan_file_location, 'w') as file:
|
| 46 |
+
file.write(new_plan_content)
|
| 47 |
+
|
| 48 |
+
# ~~~ return the plan content ~~~
|
| 49 |
+
return {
|
| 50 |
+
"EARLY_EXIT": True,
|
| 51 |
+
"plan": new_plan_content,
|
| 52 |
+
"result": "New plan was generated and has overriden the previous plan",
|
| 53 |
+
"summary": "ReplanningFlow: " + output_payload["command_args"]["summary"] + f" new plan is written to {plan_file_location}",
|
| 54 |
+
"status": "finished"
|
| 55 |
+
}
|
| 56 |
+
elif command == "manual_finish":
|
| 57 |
+
# ~~~ delete the temp plan file ~~~
|
| 58 |
+
keys_to_fetch_from_state = ["temp_plan_file_location"]
|
| 59 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
| 60 |
+
temp_plan_file_location = fetched_state["temp_plan_file_location"]
|
| 61 |
+
if os.path.exists(temp_plan_file_location):
|
| 62 |
+
os.remove(temp_plan_file_location)
|
| 63 |
+
# ~~~ return the manual quit status ~~~
|
| 64 |
+
return {
|
| 65 |
+
"EARLY_EXIT": True,
|
| 66 |
+
"new_plan": "no new plan was generated",
|
| 67 |
+
"result": "The replanner was explicitly terminated by the user, no new plan was generated.",
|
| 68 |
+
"summary": "ReplanningFlow: Replanner was terminated explicitly by the user, process is unfinished",
|
| 69 |
+
"status": "unfinished"
|
| 70 |
+
}
|
| 71 |
+
elif command == "write_plan":
|
| 72 |
+
keys_to_fetch_from_state = ["plan", "plan_file_location"]
|
| 73 |
+
fetched_state = self._fetch_state_attributes_by_keys(keys=keys_to_fetch_from_state)
|
| 74 |
+
old_plan = fetched_state["plan"]
|
| 75 |
+
plan_file_location = fetched_state["plan_file_location"]
|
| 76 |
+
output_payload["command_args"]["old_plan"] = old_plan
|
| 77 |
+
output_payload["command_args"]["plan_file_location"] = plan_file_location
|
| 78 |
+
return output_payload
|
| 79 |
+
else:
|
| 80 |
+
return output_payload
|
ReplanningFlow.yaml
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "ReplanningFlow"
|
| 2 |
+
description: "Re-plan given old plan and new information"
|
| 3 |
+
|
| 4 |
+
_target_: flow_modules.aiflows.ReplanningFlowModule.ReplanningFlow.instantiate_from_default_config
|
| 5 |
+
|
| 6 |
+
input_interface:
|
| 7 |
+
- "goal" # info on the old plan
|
| 8 |
+
- "plan" # the old plan
|
| 9 |
+
- "plan_file_location"
|
| 10 |
+
|
| 11 |
+
output_interface:
|
| 12 |
+
- "plan"
|
| 13 |
+
- "status"
|
| 14 |
+
- "summary"
|
| 15 |
+
- "result"
|
| 16 |
+
|
| 17 |
+
### Subflows specification
|
| 18 |
+
subflows_config:
|
| 19 |
+
Controller:
|
| 20 |
+
_target_: flow_modules.aiflows.PlanWriterFlowModule.PlanWriterCtrlFlow.instantiate_from_default_config
|
| 21 |
+
backend:
|
| 22 |
+
api_infos: ???
|
| 23 |
+
model_name:
|
| 24 |
+
openai: gpt-4
|
| 25 |
+
azure: azure/gpt-4
|
| 26 |
+
input_interface_initialized:
|
| 27 |
+
- "new_plan"
|
| 28 |
+
- "feedback"
|
| 29 |
+
system_message_prompt_template:
|
| 30 |
+
_target_: langchain.PromptTemplate
|
| 31 |
+
template: |2-
|
| 32 |
+
You are in charge of a department of rewriting plans to solve a certain goal. You work with a re-planner, who does all the re-planning job.
|
| 33 |
+
|
| 34 |
+
You are not given the old plan, the planner is aware of the old plan, you do not need to care about the details of it.
|
| 35 |
+
|
| 36 |
+
Your **ONLY** task is to take the user's information about the old plan for you, to decide whether to call the re-planner to write or refine the plan, or to finish the current task.
|
| 37 |
+
|
| 38 |
+
When you need to call the plan writer, call the `write_plan` command with the goal specified.
|
| 39 |
+
When the plan is written and the user is satisfied, call the `finish` command to terminate the current process with a summary of what was done in one sentence.
|
| 40 |
+
Whenever you are in doubt, or need to confirm something to the user, call `ask_user` with the question.
|
| 41 |
+
|
| 42 |
+
You **must not** write plans yourself. You only decide whether to call the planner with specified goals or to finish.
|
| 43 |
+
|
| 44 |
+
Your workflow:
|
| 45 |
+
0. Whenever the user demands to quit or terminate the current process, call `manual_finish` command.
|
| 46 |
+
1. Upon user request, call the `write_plan` with the information given.
|
| 47 |
+
2. The planner will write the plan. The user will examine the plan, and provide feedback.
|
| 48 |
+
3. Depending on the feedback of the user:
|
| 49 |
+
3.1. The user provides feedback on how to change the plan, **call the planner with user's specific requirements again, to ask the planner to refine the plan**. Go back to step 2.
|
| 50 |
+
3.2. The user does not provide details about refining the plan, for example, just stating the fact that the user has updated the plan, **this means the user is satisfied with the plan written, call the `finish` command.**
|
| 51 |
+
3.3. The user is satisfied with the plan, **call the `finish` command with a summary of what was done**
|
| 52 |
+
|
| 53 |
+
If you have completed all your tasks, make sure to use the "finish" command, with a summary of what was done.
|
| 54 |
+
|
| 55 |
+
Constraints:
|
| 56 |
+
1. Exclusively use the commands listed in double quotes e.g. "command name"
|
| 57 |
+
|
| 58 |
+
Your response **MUST** be in the following format:
|
| 59 |
+
Response Format:
|
| 60 |
+
{
|
| 61 |
+
"command": "call plan writer, or to finish",
|
| 62 |
+
"command_args": {
|
| 63 |
+
"arg name": "value"
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
Ensure your responses can be parsed by Python json.loads
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
Available Functions:
|
| 70 |
+
{{commands}}
|
| 71 |
+
input_variables: [ "commands" ]
|
| 72 |
+
template_format: jinja2
|
| 73 |
+
|
| 74 |
+
human_message_prompt_template:
|
| 75 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
| 76 |
+
template: |2-
|
| 77 |
+
Here is the new plan written by the planner, it might have been updated by the user, depending on the user's feedback:
|
| 78 |
+
{{new_plan}}
|
| 79 |
+
Here is the feedback from the user:
|
| 80 |
+
{{feedback}}
|
| 81 |
+
input_variables:
|
| 82 |
+
- "new_plan"
|
| 83 |
+
- "feedback"
|
| 84 |
+
template_format: jinja2
|
| 85 |
+
|
| 86 |
+
init_human_message_prompt_template:
|
| 87 |
+
_target_: aiflows.prompt_template.JinjaPrompt
|
| 88 |
+
template: |2-
|
| 89 |
+
Here is the information about the old plan:
|
| 90 |
+
{{goal}}
|
| 91 |
+
input_variables:
|
| 92 |
+
- "goal"
|
| 93 |
+
template_format: jinja2
|
| 94 |
+
|
| 95 |
+
Executor:
|
| 96 |
+
_target_: aiflows.base_flows.BranchingFlow.instantiate_from_default_config
|
| 97 |
+
subflows_config:
|
| 98 |
+
write_plan:
|
| 99 |
+
_target_: flow_modules.aiflows.InteractivePlanGenFlowModule.InteractivePlanGenFlow.instantiate_from_default_config
|
| 100 |
+
output_interface:
|
| 101 |
+
- "new_plan"
|
| 102 |
+
- "feedback"
|
| 103 |
+
- "temp_plan_file_location"
|
| 104 |
+
subflows_config:
|
| 105 |
+
PlanGenerator:
|
| 106 |
+
_target_: flow_modules.aiflows.ReplanningFlowModule.NewPlanGenFlow.instantiate_from_default_config
|
| 107 |
+
backend:
|
| 108 |
+
api_infos: ???
|
| 109 |
+
model_name:
|
| 110 |
+
openai: gpt-4
|
| 111 |
+
azure: azure/gpt-4
|
| 112 |
+
PlanFileEditor:
|
| 113 |
+
_target_: flow_modules.aiflows.PlanFileEditFlowModule.PlanFileEditAtomicFlow.instantiate_from_default_config
|
| 114 |
+
input_interface:
|
| 115 |
+
- "new_plan"
|
| 116 |
+
- "plan_file_location"
|
| 117 |
+
ParseFeedback:
|
| 118 |
+
_target_: flow_modules.aiflows.ParseFeedbackFlowModule.ParseFeedbackAtomicFlow.instantiate_from_default_config
|
| 119 |
+
input_interface:
|
| 120 |
+
- "temp_plan_file_location"
|
| 121 |
+
output_interface:
|
| 122 |
+
- "new_plan"
|
| 123 |
+
- "feedback"
|
| 124 |
+
|
| 125 |
+
topology:
|
| 126 |
+
- goal: "Generate plan to achieve the task."
|
| 127 |
+
input_interface:
|
| 128 |
+
_target_: aiflows.interfaces.KeyInterface
|
| 129 |
+
additional_transformations:
|
| 130 |
+
- _target_: aiflows.data_transformations.KeyMatchInput
|
| 131 |
+
flow: PlanGenerator
|
| 132 |
+
reset: false
|
| 133 |
+
|
| 134 |
+
- goal: "Write the plan generated to a temp file with instructions to the user"
|
| 135 |
+
input_interface:
|
| 136 |
+
_target_: aiflows.interfaces.KeyInterface
|
| 137 |
+
additional_transformations:
|
| 138 |
+
- _target_: aiflows.data_transformations.KeyMatchInput
|
| 139 |
+
flow: PlanFileEditor
|
| 140 |
+
reset: false
|
| 141 |
+
|
| 142 |
+
- goal: "Parse user feedback from the temp file"
|
| 143 |
+
input_interface:
|
| 144 |
+
_target_: aiflows.interfaces.KeyInterface
|
| 145 |
+
additional_transformations:
|
| 146 |
+
- _target_: aiflows.data_transformations.KeyMatchInput
|
| 147 |
+
flow: ParseFeedback
|
| 148 |
+
output_interface:
|
| 149 |
+
_target_: aiflows.interfaces.KeyInterface
|
| 150 |
+
keys_to_rename:
|
| 151 |
+
plan: new_plan
|
| 152 |
+
reset: false
|
| 153 |
+
|
| 154 |
+
ask_user:
|
| 155 |
+
_target_: flow_modules.aiflows.ReplanningFlowModule.ReplanningAskUserFlow.instantiate_from_default_config
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
early_exit_key: "EARLY_EXIT"
|
| 159 |
+
|
| 160 |
+
topology:
|
| 161 |
+
- goal: "Select the next action and prepare the input for the executor."
|
| 162 |
+
input_interface:
|
| 163 |
+
_target_: aiflows.interfaces.KeyInterface
|
| 164 |
+
additional_transformations:
|
| 165 |
+
- _target_: aiflows.data_transformations.KeyMatchInput
|
| 166 |
+
flow: Controller
|
| 167 |
+
output_interface:
|
| 168 |
+
_target_: ReplanningFlow.detect_finish_or_continue
|
| 169 |
+
reset: false
|
| 170 |
+
|
| 171 |
+
- goal: "Execute the action specified by the Controller."
|
| 172 |
+
input_interface:
|
| 173 |
+
_target_: aiflows.interfaces.KeyInterface
|
| 174 |
+
keys_to_rename:
|
| 175 |
+
command: branch
|
| 176 |
+
command_args: branch_input_data
|
| 177 |
+
keys_to_select: ["branch", "branch_input_data"]
|
| 178 |
+
flow: Executor
|
| 179 |
+
output_interface:
|
| 180 |
+
_target_: aiflows.interfaces.KeyInterface
|
| 181 |
+
keys_to_rename:
|
| 182 |
+
branch_output_data.new_plan: new_plan
|
| 183 |
+
branch_output_data.feedback: feedback
|
| 184 |
+
branch_output_data.temp_plan_file_location: temp_plan_file_location
|
| 185 |
+
keys_to_delete: ["branch_output_data"]
|
| 186 |
+
reset: false
|
__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dependencies = [
|
| 2 |
+
{"url": "aiflows/PlanGeneratorFlowModule", "revision": "main"},
|
| 3 |
+
{"url": "aiflows/InteractivePlanGenFlowModule", "revision": "main"},
|
| 4 |
+
{"url": "aiflows/PlanWriterFlowModule", "revision": "main"},
|
| 5 |
+
{"url": "aiflows/PlanFileEditFlowModule", "revision": "main"},
|
| 6 |
+
{"url": "aiflows/ParseFeedbackFlowModule", "revision": "main"},
|
| 7 |
+
{"url": "aiflows/HumanStandardInputFlowModule", "revision": "4ff043522c89a964ea3a928ce09811c51a2b5b98"}
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
from aiflows import flow_verse
|
| 11 |
+
flow_verse.sync_dependencies(dependencies)
|
| 12 |
+
|
| 13 |
+
from .NewPlanGenFlow import NewPlanGenFlow
|
| 14 |
+
from .ReplanningFlow import ReplanningFlow
|
| 15 |
+
from .ReplanningAskUserFlow import ReplanningAskUserFlow
|
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_replanning.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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/ReplanningFlowModule", "revision": "main"},
|
| 20 |
+
{"url": "aiflows/PlanGeneratorFlowModule", "revision": "main"},
|
| 21 |
+
{"url": "aiflows/InteractivePlanGenFlowModule", "revision": "main"},
|
| 22 |
+
{"url": "aiflows/PlanWriterFlowModule", "revision": "main"},
|
| 23 |
+
{"url": "aiflows/PlanFileEditFlowModule", "revision": "main"},
|
| 24 |
+
{"url": "aiflows/ParseFeedbackFlowModule", "revision": "main"},
|
| 25 |
+
{"url": "aiflows/HumanStandardInputFlowModule", "revision": "4ff043522c89a964ea3a928ce09811c51a2b5b98"}
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
+
from aiflows import flow_verse
|
| 29 |
+
|
| 30 |
+
flow_verse.sync_dependencies(dependencies)
|
| 31 |
+
|
| 32 |
+
if __name__ == "__main__":
|
| 33 |
+
# ~~~ make sure to set the openai api key in the envs ~~~
|
| 34 |
+
key = os.getenv("OPENAI_API_KEY")
|
| 35 |
+
api_information = [ApiInfo(backend_used="openai", api_key=os.getenv("OPENAI_API_KEY"))]
|
| 36 |
+
path_to_output_file = None
|
| 37 |
+
|
| 38 |
+
# ~~~ setting api information into config ~~~
|
| 39 |
+
current_dir = os.getcwd()
|
| 40 |
+
cfg_path = os.path.join(current_dir, "ReplanningFlow.yaml")
|
| 41 |
+
cfg = read_yaml_file(cfg_path)
|
| 42 |
+
quick_load(cfg, api_information)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# ~~~ instantiating the flow and input data ~~~
|
| 46 |
+
ReplanningFlow = hydra.utils.instantiate(cfg, _recursive_=False, _convert_="partial")
|
| 47 |
+
|
| 48 |
+
# ~~~ creating the plan file location (of the upper level flow e.g. ExtLib)
|
| 49 |
+
plan_file_location = os.path.join(current_dir, "ExtLib_plan.txt")
|
| 50 |
+
with open(plan_file_location, 'w') as file:
|
| 51 |
+
pass
|
| 52 |
+
|
| 53 |
+
input_data = {
|
| 54 |
+
"goal": "data source OpenWeatherMap is deprecated",
|
| 55 |
+
"plan": "1. Write a function that fetches today's weather at Lausanne from OpenWeatherMap. \n 2. Write a funtion that prints the fetched data in a formatted way.",
|
| 56 |
+
"plan_file_location": plan_file_location
|
| 57 |
+
}
|
| 58 |
+
input_message = InputMessage.build(
|
| 59 |
+
data_dict=input_data,
|
| 60 |
+
src_flow="Launcher",
|
| 61 |
+
dst_flow=ReplanningFlow.name
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
# ~~~ calling the flow ~~~
|
| 65 |
+
output_message = ReplanningFlow(input_message)
|