From ad12e795c5e913dbe2399dd3c26c3d75ae00ca95 Mon Sep 17 00:00:00 2001 From: Liu Zhicong Date: Thu, 21 Sep 2023 23:18:52 -0700 Subject: [PATCH 1/2] Add linux_admin agent, funny~ --- rootfs/agents/Jarvis/agent.toml | 3 +- rootfs/agents/linux_admin/agent.toml | 7 +- src/aios_kernel/__init__.py | 1 + src/aios_kernel/agent.py | 2 + src/aios_kernel/tg_tunnel.py | 6 +- src/aios_kernel/worksapce_env.py | 2 - src/aios_kernel/workspace_env.py | 173 +++++++++++++++++++++++++++ src/service/aios_shell/aios_shell.py | 4 +- test/test_contac_manager.py | 2 +- test/workflow_test.py | 26 +++- 10 files changed, 208 insertions(+), 18 deletions(-) delete mode 100644 src/aios_kernel/worksapce_env.py create mode 100644 src/aios_kernel/workspace_env.py diff --git a/rootfs/agents/Jarvis/agent.toml b/rootfs/agents/Jarvis/agent.toml index ca1c7b0..c8c3ae7 100644 --- a/rootfs/agents/Jarvis/agent.toml +++ b/rootfs/agents/Jarvis/agent.toml @@ -7,12 +7,11 @@ max_token_size = 16000 enable_timestamp = "true" owner_prompt = "我是你的主人{name}" contact_prompt = "我是你的朋友{name}" -guest_prompt = "xxx" +owner_env = "calender" [[prompt]] role = "system" content = """ -现在是{now} 你叫Jarvis,是我的超级私人助理。 你领导一个团队为我服务,团队的成员有: Tracy,私人英语老师 diff --git a/rootfs/agents/linux_admin/agent.toml b/rootfs/agents/linux_admin/agent.toml index 7988669..9eaef70 100644 --- a/rootfs/agents/linux_admin/agent.toml +++ b/rootfs/agents/linux_admin/agent.toml @@ -1,12 +1,13 @@ instance_id = "linux_admin" fullname = "linux_admin" - +owner_env = "bash" [[prompt]] role = "system" content = """你的名字是linux_admin,是非常资深的linux系统管理员,我理解一些linux,但对linux的sh命令记得不太清楚了。 我给你的输入有两种 -1. 标准的linux bash命令,如果你认为这些命令在当前系统上是正确的,那么直接返回这些命令,我会去执行 -2. 如果你为这些命令需要调整后才能执行,先把你修正后的命令告诉我,我确认后再执行 +1. 标准的linux bash命令,如果你认为这些命令在当前系统上是正确且可执行的,那么你可以直接执行 +2. 如果我的命令不对,或则命令可能对系统有害,你要对这些命令进行调整后,先把你调整后的命令告诉我,我确认后你再执行正确的命令 3. 如果我给你的信息不是linux bash操作,而是一些需求。你可以尝试理解后,给出一组实现这些需求的命令,我确认后再执行 4. 其它信息,请站在你专业角度尽力执行。 +5. 每次执行命令后,要把结果告诉我。 """ diff --git a/src/aios_kernel/__init__.py b/src/aios_kernel/__init__.py index 79d4456..b821013 100644 --- a/src/aios_kernel/__init__.py +++ b/src/aios_kernel/__init__.py @@ -19,6 +19,7 @@ from .tg_tunnel import TelegramTunnel from .email_tunnel import EmailTunnel from .storage import ResourceLocation,AIStorage,UserConfig,UserConfigItem from .contact_manager import ContactManager,Contact,FamilyMember +from .workspace_env import WorkspaceEnvironment AIOS_Version = "0.5.1, build 2023-9-17" diff --git a/src/aios_kernel/agent.py b/src/aios_kernel/agent.py index cc79637..b1c3e40 100644 --- a/src/aios_kernel/agent.py +++ b/src/aios_kernel/agent.py @@ -162,6 +162,8 @@ class AIAgent: if config.get("contact_prompt") is not None: self.contact_prompt_str = config["contact_prompt"] + if config.get("owner_env") is not None: + self.owner_env = Environment.get_env_by_id(config["owner_env"]) if config.get("powerby") is not None: self.powerby = config["powerby"] diff --git a/src/aios_kernel/tg_tunnel.py b/src/aios_kernel/tg_tunnel.py index edc7b93..4cad825 100644 --- a/src/aios_kernel/tg_tunnel.py +++ b/src/aios_kernel/tg_tunnel.py @@ -4,11 +4,7 @@ import asyncio import uuid import time -from typing import Callable - -from telegram import ForceReply, Update -from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters - +from telegram import Update from telegram import Bot from telegram.ext import Updater from telegram.error import Forbidden, NetworkError diff --git a/src/aios_kernel/worksapce_env.py b/src/aios_kernel/worksapce_env.py deleted file mode 100644 index d8611f2..0000000 --- a/src/aios_kernel/worksapce_env.py +++ /dev/null @@ -1,2 +0,0 @@ -# this env is designed for workflow owner filesystem, support file/directory operations - diff --git a/src/aios_kernel/workspace_env.py b/src/aios_kernel/workspace_env.py new file mode 100644 index 0000000..8bfe00e --- /dev/null +++ b/src/aios_kernel/workspace_env.py @@ -0,0 +1,173 @@ +# this env is designed for workflow owner filesystem, support file/directory operations + +import subprocess +import tempfile +import threading +import traceback +import time +import ast +import sys +import os +import re + +from .environment import Environment,EnvironmentEvent +from .ai_function import AIFunction,SimpleAIFunction + + +class CodeInterpreter: + def __init__(self, language, debug_mode): + self.language = language + self.proc = None + self.active_line = None + self.debug_mode = debug_mode + + def start_process(self): + start_cmd = sys.executable + " -i -q -u" + self.proc = subprocess.Popen(start_cmd.split(), + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=0) + + # Start watching ^ its `stdout` and `stderr` streams + threading.Thread(target=self.save_and_display_stream, + args=(self.proc.stdout, False), # Passes False to is_error_stream + daemon=True).start() + threading.Thread(target=self.save_and_display_stream, + args=(self.proc.stderr, True), # Passes True to is_error_stream + daemon=True).start() + + def warp_code(self,pycode:str)->str: + # Add import traceback + code = "import traceback\n" + pycode + # Parse the input code into an AST + parsed_code = ast.parse(code) + # Wrap the entire code's AST in a single try-except block + try_except = ast.Try( + body=parsed_code.body, + handlers=[ + ast.ExceptHandler( + type=ast.Name(id="Exception", ctx=ast.Load()), + name=None, + body=[ + ast.Expr( + value=ast.Call( + func=ast.Attribute(value=ast.Name(id="traceback", ctx=ast.Load()), attr="print_exc", ctx=ast.Load()), + args=[], + keywords=[] + ) + ), + ] + ) + ], + orelse=[], + finalbody=[] + ) + + parsed_code.body = [try_except] + return ast.unparse(parsed_code) + + def run(self,py_code:str): + """ + Executes code. + """ + # Get code to execute + self.code = py_code + + # Start the subprocess if it hasn't been started + if not self.proc: + try: + self.start_process() + except Exception as e: + # Sometimes start_process will fail! + # Like if they don't have `node` installed or something. + + traceback_string = traceback.format_exc() + self.output = traceback_string + # Before you return, wait for the display to catch up? + # (I'm not sure why this works) + time.sleep(0.1) + + return self.output + + self.output = "" + + self.print_cmd = 'print("{}")' + code = self.warp_code(py_code) + + if self.debug_mode: + print("Running code:") + print(code) + print("---") + + self.done = threading.Event() + self.done.clear() + + # Write code to stdin of the process + try: + self.proc.stdin.write(code + "\n") + self.proc.stdin.flush() + except BrokenPipeError: + return + self.done.wait() + time.sleep(0.1) + return self.output + + def save_and_display_stream(self, stream, is_error_stream): + + for line in iter(stream.readline, ''): + if self.debug_mode: + print("Recieved output line:") + print(line) + print("---") + + line = line.strip() + if is_error_stream and "KeyboardInterrupt" in line: + raise KeyboardInterrupt + elif "END_OF_EXECUTION" in line: + self.done.set() + self.active_line = None + else: + self.output += "\n" + line + self.output = self.output.strip() + + + +class WorkspaceEnvironment(Environment): + def __init__(self, env_id: str) -> None: + super().__init__(env_id) + + operator_param = { + "command": "command will execute", + } + self.add_ai_function(SimpleAIFunction("shell_exec", + "execute shell command in linux bash", + self.shell_exec,operator_param)) + + #run_code_param = { + # "pycode": "python code will execute", + #} + #self.add_ai_function(SimpleAIFunction("run_code", + # "execute python code", + # self.run_code,run_code_param)) + + + async def shell_exec(self,command:str) -> str: + import asyncio.subprocess + process = await asyncio.create_subprocess_shell( + command, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() + returncode = process.returncode + if returncode == 0: + return f"Execute success! stdout is:\n{stdout}\n" + else: + return f"Execute failed! stderr is:\n{stderr}\n" + + async def run_code(self,pycode:str) -> str: + interpreter = CodeInterpreter("python",True) + return interpreter.run(pycode) + diff --git a/src/service/aios_shell/aios_shell.py b/src/service/aios_shell/aios_shell.py index 984bb08..49224b8 100644 --- a/src/service/aios_shell/aios_shell.py +++ b/src/service/aios_shell/aios_shell.py @@ -64,7 +64,6 @@ class AIOS_Shell: target_id = msg.target.split(".")[0] agent : AIAgent = await AgentManager.get_instance().get(target_id) if agent is not None: - agent.owner_env = Environment.get_env_by_id("calender") bus.register_message_handler(target_id,agent._process_msg) return True @@ -87,6 +86,9 @@ class AIOS_Shell: await cal_env.start() Environment.set_env_by_id("calender",cal_env) + workspace_env = WorkspaceEnvironment("bash") + Environment.set_env_by_id("bash",workspace_env) + await AgentManager.get_instance().initial() await WorkflowManager.get_instance().initial() diff --git a/test/test_contac_manager.py b/test/test_contac_manager.py index 4272b16..9dbd39d 100644 --- a/test/test_contac_manager.py +++ b/test/test_contac_manager.py @@ -1,6 +1,6 @@ import unittest -import os import toml +import os import sys directory = os.path.dirname(__file__) diff --git a/test/workflow_test.py b/test/workflow_test.py index c985cb5..3e5e586 100644 --- a/test/workflow_test.py +++ b/test/workflow_test.py @@ -1,9 +1,22 @@ +import os +import sys +import asyncio +directory = os.path.dirname(__file__) +sys.path.append(directory + '/../src') +from aios_kernel import WorkspaceEnvironment -def test_workflow(): +async def test_workflow(): + env = WorkspaceEnvironment("test") + + test_code =""" +import toml +print("hello world") +print(100+23) +toml.dump({"abc":"123"},open("test.toml","w")) + """ + await env.run_code(test_code) - - pass async def _test_llm_parser(): @@ -39,4 +52,9 @@ sfsadfasdf llm_result = Workflow.prase_llm_result(test_llm_result) assert len(llm_result.calls) == 1 assert len(llm_result.send_msgs) == 1 - print(llm_result) \ No newline at end of file + print(llm_result) + +if __name__ == "__main__": + asyncio.run(test_workflow()) + print("OK!") + From 0d556003c5794dca1c91ca641364cef6ea82f9d7 Mon Sep 17 00:00:00 2001 From: Liu Zhicong Date: Fri, 22 Sep 2023 00:05:32 -0700 Subject: [PATCH 2/2] fix bug --- src/aios_kernel/contact_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aios_kernel/contact_manager.py b/src/aios_kernel/contact_manager.py index ca5b7c5..225e9a1 100644 --- a/src/aios_kernel/contact_manager.py +++ b/src/aios_kernel/contact_manager.py @@ -10,6 +10,7 @@ class Contact: self.added_by = added_by self.tags = tags self.notes = notes + self.is_family_member = False def to_dict(self): return {