Upload 9 files
Browse files- MemoryReadingAtomicFlow.py +124 -0
- MemoryReadingAtomicFlow.yaml +11 -0
- README.md +34 -3
- __init__.py +1 -0
- example_library.py +7 -0
- example_logs.txt +1 -0
- example_plan.txt +2 -0
- pip_requirements.txt +15 -0
- run.py +48 -0
MemoryReadingAtomicFlow.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict, Any
|
| 2 |
+
import os
|
| 3 |
+
import ast
|
| 4 |
+
import importlib
|
| 5 |
+
import inspect
|
| 6 |
+
from aiflows.base_flows import AtomicFlow
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class MemoryReadingAtomicFlow(AtomicFlow):
|
| 10 |
+
"""A flow to read memory from given files.
|
| 11 |
+
|
| 12 |
+
Any composite flow that uses this flow should have
|
| 13 |
+
memory_files: Dict[str, str] which maps memory name to its memory file location in the flow_state
|
| 14 |
+
|
| 15 |
+
*Input Interface*:
|
| 16 |
+
- `memory_files` : name of the Dict which maps the memory name to its file location e.g.
|
| 17 |
+
{"plan": "examples/JARVIS/plan.txt"}
|
| 18 |
+
|
| 19 |
+
*Output_Interface*:
|
| 20 |
+
- corresponding memory content, for example, `code_library`. There could be multiple memory content returned.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
def __init__(self, **kwargs):
|
| 24 |
+
super().__init__(**kwargs)
|
| 25 |
+
self.supported_mem_name = ["plan", "logs", "code_library"]
|
| 26 |
+
|
| 27 |
+
def _check_input_data(self, input_data: Dict[str, Any]):
|
| 28 |
+
"""input data sanity check"""
|
| 29 |
+
assert "memory_files" in input_data, "memory_files not passed to MemoryReadingAtomicFlow"
|
| 30 |
+
|
| 31 |
+
for mem_name, mem_path in input_data["memory_files"].items():
|
| 32 |
+
assert mem_name in self.supported_mem_name, (f"{mem_name} is not supported in MemoryReadingAtomicFlow, "
|
| 33 |
+
f"supported names are: {self.supported_mem_name}")
|
| 34 |
+
assert os.path.exists(mem_path), f"{mem_path} does not exist."
|
| 35 |
+
assert os.path.isfile(mem_path), f"{mem_path} is not a file."
|
| 36 |
+
|
| 37 |
+
def _read_text(self, file_location):
|
| 38 |
+
with open(file_location, 'r', encoding='utf-8') as file:
|
| 39 |
+
content = file.read()
|
| 40 |
+
return content
|
| 41 |
+
|
| 42 |
+
def _get_pyfile_functions_metadata_from_file(self, file_location):
|
| 43 |
+
def python_file_path_to_module_name(file_path):
|
| 44 |
+
return os.path.basename(file_path).replace('.py', '')
|
| 45 |
+
|
| 46 |
+
def extract_top_level_function_names(python_file_path):
|
| 47 |
+
with open(python_file_path, 'r') as file:
|
| 48 |
+
file_content = file.read()
|
| 49 |
+
tree = ast.parse(file_content)
|
| 50 |
+
functions = filter(lambda node: isinstance(node, ast.FunctionDef), ast.iter_child_nodes(tree))
|
| 51 |
+
return [node.name for node in functions]
|
| 52 |
+
|
| 53 |
+
def load_module_from_file(file_path):
|
| 54 |
+
module_name = python_file_path_to_module_name(file_path)
|
| 55 |
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
| 56 |
+
module = importlib.util.module_from_spec(spec)
|
| 57 |
+
spec.loader.exec_module(module)
|
| 58 |
+
return module
|
| 59 |
+
|
| 60 |
+
def get_function_from_name(function_name, module):
|
| 61 |
+
return getattr(module, function_name)
|
| 62 |
+
|
| 63 |
+
def function_to_dict(function):
|
| 64 |
+
if not callable(function):
|
| 65 |
+
raise ValueError("Provided object is not a function.")
|
| 66 |
+
|
| 67 |
+
function_dict = {
|
| 68 |
+
"name": function.__name__,
|
| 69 |
+
"doc": function.__doc__,
|
| 70 |
+
"args": []
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
signature = inspect.signature(function)
|
| 74 |
+
for name, param in signature.parameters.items():
|
| 75 |
+
arg_info = {
|
| 76 |
+
"name": name,
|
| 77 |
+
"default": param.default if param.default is not inspect.Parameter.empty else None,
|
| 78 |
+
"type": str(param.annotation) if param.annotation is not inspect.Parameter.empty else "unknown"
|
| 79 |
+
}
|
| 80 |
+
function_dict["args"].append(arg_info)
|
| 81 |
+
|
| 82 |
+
return function_dict
|
| 83 |
+
|
| 84 |
+
function_names = extract_top_level_function_names(file_location)
|
| 85 |
+
module = load_module_from_file(file_location)
|
| 86 |
+
functions = [get_function_from_name(name, module) for name in function_names]
|
| 87 |
+
return [function_to_dict(function) for function in functions]
|
| 88 |
+
|
| 89 |
+
def _format_metadata(self, metadata):
|
| 90 |
+
lines = []
|
| 91 |
+
for function_data in metadata:
|
| 92 |
+
lines.append(f"Function: {function_data['name']}")
|
| 93 |
+
lines.append(f"Documentation: {function_data['doc']}")
|
| 94 |
+
|
| 95 |
+
args = function_data.get('args', [])
|
| 96 |
+
if args:
|
| 97 |
+
lines.append("Arguments:")
|
| 98 |
+
for arg in args:
|
| 99 |
+
default = f" (default: {arg['default']})" if arg['default'] is not None else ""
|
| 100 |
+
lines.append(f" - {arg['name']} (type: {arg['type']}){default}")
|
| 101 |
+
else:
|
| 102 |
+
lines.append("Arguments: None")
|
| 103 |
+
|
| 104 |
+
lines.append("#########")
|
| 105 |
+
|
| 106 |
+
return '\n'.join(lines)
|
| 107 |
+
def _read_py_code_library(self, file_location):
|
| 108 |
+
metadata = self._get_pyfile_functions_metadata_from_file(file_location)
|
| 109 |
+
if len(metadata) == 0:
|
| 110 |
+
return "No functions yet."
|
| 111 |
+
formatted_metadata = self._format_metadata(metadata)
|
| 112 |
+
return formatted_metadata
|
| 113 |
+
|
| 114 |
+
def run(
|
| 115 |
+
self,
|
| 116 |
+
input_data: Dict[str, Any]):
|
| 117 |
+
self._check_input_data(input_data)
|
| 118 |
+
response = {}
|
| 119 |
+
for mem_name, mem_path in input_data["memory_files"].items():
|
| 120 |
+
if mem_name in ['plan', 'logs']:
|
| 121 |
+
response[mem_name] = self._read_text(mem_path)
|
| 122 |
+
elif mem_name == 'code_library' and mem_path.endswith('.py'):
|
| 123 |
+
response[mem_name] = self._read_py_code_library(mem_path)
|
| 124 |
+
return response
|
MemoryReadingAtomicFlow.yaml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "MemoryReadingAtomicFlow"
|
| 2 |
+
description: "A flow that reads memory from given file locations"
|
| 3 |
+
_target_: flow_modules.aiflows.MemoryReadingFlowModule.MemoryReadingAtomicFlow.instantiate_from_default_config
|
| 4 |
+
|
| 5 |
+
input_interface:
|
| 6 |
+
- "memory_files"
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
# should be overriden if the memory reading is to read other memories
|
| 10 |
+
output_interface:
|
| 11 |
+
- "code_library"
|
README.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Table of Contents
|
| 2 |
+
|
| 3 |
+
* [MemoryReadingAtomicFlow](#MemoryReadingAtomicFlow)
|
| 4 |
+
* [MemoryReadingAtomicFlow](#MemoryReadingAtomicFlow.MemoryReadingAtomicFlow)
|
| 5 |
+
* [\_\_init\_\_](#__init__)
|
| 6 |
+
|
| 7 |
+
<a id="MemoryReadingAtomicFlow"></a>
|
| 8 |
+
|
| 9 |
+
# MemoryReadingAtomicFlow
|
| 10 |
+
|
| 11 |
+
<a id="MemoryReadingAtomicFlow.MemoryReadingAtomicFlow"></a>
|
| 12 |
+
|
| 13 |
+
## MemoryReadingAtomicFlow Objects
|
| 14 |
+
|
| 15 |
+
```python
|
| 16 |
+
class MemoryReadingAtomicFlow(AtomicFlow)
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
A flow to read memory from given files.
|
| 20 |
+
|
| 21 |
+
Any composite flow that uses this flow should have
|
| 22 |
+
memory_files: Dict[str, str] which maps memory name to its memory file location in the flow_state
|
| 23 |
+
|
| 24 |
+
*Input Interface*:
|
| 25 |
+
- `memory_files` : name of the Dict which maps the memory name to its file location e.g.
|
| 26 |
+
{"plan": "examples/JARVIS/plan.txt"}
|
| 27 |
+
|
| 28 |
+
*Output_Interface*:
|
| 29 |
+
- corresponding memory content, for example, `code_library`. There could be multiple memory content returned.
|
| 30 |
+
|
| 31 |
+
<a id="__init__"></a>
|
| 32 |
+
|
| 33 |
+
# \_\_init\_\_
|
| 34 |
+
|
__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .MemoryReadingAtomicFlow import MemoryReadingAtomicFlow
|
example_library.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def add_two_numbers(a, b):
|
| 2 |
+
"""Add two numbers together and return the result
|
| 3 |
+
:param a: first number
|
| 4 |
+
:param b: second number
|
| 5 |
+
:return: sum of a and b
|
| 6 |
+
"""
|
| 7 |
+
return a + b
|
example_logs.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
some logs
|
example_plan.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
1. step1
|
| 2 |
+
2. step2
|
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.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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 |
+
|
| 14 |
+
logging.set_verbosity_debug()
|
| 15 |
+
logging.auto_set_dir()
|
| 16 |
+
|
| 17 |
+
dependencies = [
|
| 18 |
+
{"url": "aiflows/MemoryReadingFlowModule", "revision": "main"},
|
| 19 |
+
]
|
| 20 |
+
|
| 21 |
+
from aiflows import flow_verse
|
| 22 |
+
|
| 23 |
+
flow_verse.sync_dependencies(dependencies)
|
| 24 |
+
|
| 25 |
+
if __name__ == "__main__":
|
| 26 |
+
current_dir = os.getcwd()
|
| 27 |
+
cfg_path = os.path.join(current_dir, "MemoryReadingAtomicFlow.yaml")
|
| 28 |
+
cfg = read_yaml_file(cfg_path)
|
| 29 |
+
|
| 30 |
+
MemReadFlow = hydra.utils.instantiate(cfg, _recursive_=False, _convert_="partial")
|
| 31 |
+
|
| 32 |
+
input_data = {
|
| 33 |
+
"memory_files": {
|
| 34 |
+
"plan": os.path.join(current_dir, "example_plan.txt"),
|
| 35 |
+
"logs": os.path.join(current_dir, "example_logs.txt"),
|
| 36 |
+
"code_library": os.path.join(current_dir, "example_library.txt")
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
input_message = InputMessage.build(
|
| 40 |
+
data_dict=input_data,
|
| 41 |
+
src_flow="Launcher",
|
| 42 |
+
dst_flow=MemReadFlow.name
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
# ~~~ calling the flow ~~~
|
| 46 |
+
output_message = MemReadFlow(input_message)
|
| 47 |
+
|
| 48 |
+
print(output_message.data)
|