File size: 6,037 Bytes
3d30823 e8d999b 3d30823 e8d999b 61efe8b e8d999b 61efe8b 525c1e0 3d30823 a09a505 3d30823 e8d999b 3d30823 a09a505 e8d999b a09a505 e8d999b 3d30823 e8d999b eae83f7 e8d999b df56400 a09a505 df56400 a09a505 df56400 e8d999b 3d30823 0e3a95b 3d30823 0e3a95b e8d999b 3d30823 e8d999b 3d30823 a09a505 3d30823 0e3a95b e8d999b 3ac19a5 e8d999b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
import importlib
import importlib.util
import json
import os.path
from copy import deepcopy
from typing import Any, Dict, List
from flow_modules.aiflows.ChatFlowModule import ChatAtomicFlow
from dataclasses import dataclass
@dataclass
class Command:
name: str
description: str
input_args: List[str]
class ControllerAtomicFlow(ChatAtomicFlow):
"""
Refer to: https://huggingface.co/Tachi67/JarvisFlowModule/blob/main/Controller_JarvisFlow.py
iirc this flow is not really used.
"""
def __init__(
self,
commands: List[Command],
plan_file_location: str,
code_file_location: str,
plan_to_append: str,
function_signatures_to_append: str,
**kwargs):
super().__init__(**kwargs)
if os.path.isdir(plan_file_location):
plan_file_location = os.path.join(plan_file_location, "plan.txt")
self.plan_file_location = plan_file_location
self.code_file_location = code_file_location
self.plan_to_append = plan_to_append
self.function_signatures_to_append = function_signatures_to_append
self.system_message_prompt_template = self.system_message_prompt_template.partial(
commands=self._build_commands_manual(commands),
plan_file_location=self.plan_file_location,
code_file_location=self.code_file_location,
plan_to_append=self.plan_to_append,
function_signatures_to_append=self.function_signatures_to_append
)
self.hint_for_model = """
Make sure your response is in the following format:
Response Format:
{
"thought": "thought",
"reasoning": "reasoning",
"criticism": "constructive self-criticism",
"speak": "thoughts summary to say to user",
"command": "the python function you would like to call",
"command_args": {
"arg name": "value"
}
}
"""
@staticmethod
def _build_commands_manual(commands: List[Command]) -> str:
ret = ""
for i, command in enumerate(commands):
command_input_json_schema = json.dumps(
{input_arg: f"YOUR_{input_arg.upper()}" for input_arg in command.input_args})
ret += f"{i + 1}. {command.name}: {command.description} Input arguments (given in the JSON schema): {command_input_json_schema}\n"
return ret
@classmethod
def instantiate_from_config(cls, config):
flow_config = deepcopy(config)
kwargs = {"flow_config": flow_config}
# ~~~ Set up prompts ~~~
kwargs.update(cls._set_up_prompts(flow_config))
kwargs.update(cls._set_up_backend(flow_config))
# ~~~ Set up commands ~~~
commands = flow_config["commands"]
commands = [
Command(name, command_conf["description"], command_conf["input_args"]) for name, command_conf in
commands.items()
]
kwargs.update({"commands": commands})
# ~~~ Set up file location for plan and code ~~~
plan_file_location = flow_config["plan_file_location"]
code_file_location = flow_config["code_file_location"]
plan_to_append = flow_config["plan_to_append"]
function_signatures_to_append = flow_config["function_signatures_to_append"]
kwargs.update({"plan_file_location": plan_file_location})
kwargs.update({"code_file_location": code_file_location})
kwargs.update({"plan_to_append": plan_to_append})
kwargs.update({"function_signatures_to_append": function_signatures_to_append})
# ~~~ Instantiate flow ~~~
return cls(**kwargs)
def _get_library_function_signatures(self):
try:
spec = importlib.util.spec_from_file_location("code_library", self.code_file_location)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
ret = ''
import inspect
for name, obj in inspect.getmembers(module):
if inspect.isfunction(obj):
ret += f"{name}: {inspect.signature(obj)}\n"
return ret
except FileNotFoundError:
return 'There is no function available yet.'
def _get_plan(self):
try:
with open(self.plan_file_location, 'r') as file:
return file.read()
except FileNotFoundError:
return "There is no plan yet"
def _update_prompts_and_input(self, input_data: Dict[str, Any]):
if 'goal' in input_data:
input_data['goal'] += self.hint_for_model
if 'human_feedback' in input_data:
input_data['human_feedback'] += self.hint_for_model
self.plan_to_append = self._get_plan()
self.function_signatures_to_append = self._get_library_function_signatures()
self.system_message_prompt_template = self.system_message_prompt_template.partial(plan_to_append = self.plan_to_append, function_signatures_to_append = self.function_signatures_to_append)
def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
self._update_prompts_and_input(input_data)
api_output = super().run(input_data)["api_output"].strip()
try:
response = json.loads(api_output)
return response
except json.decoder.JSONDecodeError:
new_input_data = input_data.copy()
new_input_data['observation'] = ""
new_input_data['human_feedback'] = "The previous respond cannot be parsed with json.loads, it could be the backslashes used for escaping single quotes in the string arguments of the Python code are not properly escaped themselves within the JSON context. Make sure your next response is in JSON format."
new_api_output = super().run(new_input_data)["api_output"].strip()
return json.loads(new_api_output)
|