2023-09-21 23:18:52 -07:00
|
|
|
|
# this env is designed for workflow owner filesystem, support file/directory operations
|
|
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
import hashlib
|
2023-10-18 11:19:11 -07:00
|
|
|
|
import json
|
2023-09-21 23:18:52 -07:00
|
|
|
|
import subprocess
|
2023-11-01 19:29:55 -07:00
|
|
|
|
import logging
|
2023-09-21 23:18:52 -07:00
|
|
|
|
import tempfile
|
|
|
|
|
|
import threading
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
import time
|
|
|
|
|
|
import ast
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import os
|
|
|
|
|
|
import re
|
2023-10-18 11:19:11 -07:00
|
|
|
|
import asyncio
|
2023-11-03 01:16:32 -07:00
|
|
|
|
import aiofiles
|
2023-11-01 19:29:55 -07:00
|
|
|
|
from typing import Any,List
|
2023-11-03 01:16:32 -07:00
|
|
|
|
import os
|
2023-10-18 11:19:11 -07:00
|
|
|
|
import chardet
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
from markdown import Markdown
|
|
|
|
|
|
import PyPDF2
|
2023-11-08 22:13:18 -08:00
|
|
|
|
from .agent_base import AgentMsg,AgentTodo,AgentPrompt,AgentTodoResult
|
2023-09-21 23:18:52 -07:00
|
|
|
|
from .environment import Environment,EnvironmentEvent
|
|
|
|
|
|
from .ai_function import AIFunction,SimpleAIFunction
|
2023-11-03 01:16:32 -07:00
|
|
|
|
from .storage import AIStorage,ResourceLocation
|
2023-11-12 21:59:04 -08:00
|
|
|
|
from .simple_kb_db import SimpleKnowledgeDB
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-01 19:29:55 -07:00
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
class WorkspaceEnvironment(Environment):
|
|
|
|
|
|
def __init__(self, env_id: str) -> None:
|
|
|
|
|
|
super().__init__(env_id)
|
2023-11-03 01:16:32 -07:00
|
|
|
|
myai_path = AIStorage.get_instance().get_myai_dir()
|
|
|
|
|
|
self.root_path = f"{myai_path}/workspace/{env_id}"
|
|
|
|
|
|
if not os.path.exists(self.root_path):
|
|
|
|
|
|
os.makedirs(self.root_path+"/todos")
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
|
|
|
|
|
self.known_todo = {}
|
2023-11-12 21:59:04 -08:00
|
|
|
|
self.kb_db = SimpleKnowledgeDB(f"{self.root_path}/kb.db")
|
|
|
|
|
|
self.doc_dirs = {}
|
|
|
|
|
|
self._scan_thread = None
|
|
|
|
|
|
self._scan_dirthread = None
|
2023-11-03 01:16:32 -07:00
|
|
|
|
|
2023-11-01 19:29:55 -07:00
|
|
|
|
|
|
|
|
|
|
def set_root_path(self,path:str):
|
|
|
|
|
|
self.root_path = path
|
|
|
|
|
|
|
2023-11-03 01:16:32 -07:00
|
|
|
|
def get_prompt(self) -> AgentMsg:
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
2023-11-03 22:31:23 -07:00
|
|
|
|
def get_role_prompt(self,role_id:str) -> AgentPrompt:
|
2023-11-03 01:16:32 -07:00
|
|
|
|
return None
|
|
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
def get_knowledge_base(self,root_dir=None,indent=0) -> str:
|
2023-11-01 19:29:55 -07:00
|
|
|
|
pass
|
|
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
2023-11-08 22:13:18 -08:00
|
|
|
|
def get_do_prompt(self,todo:AgentTodo=None)->AgentPrompt:
|
2023-11-03 22:31:23 -07:00
|
|
|
|
return None
|
|
|
|
|
|
|
2023-11-08 22:13:18 -08:00
|
|
|
|
# result mean: list[op_error_str],have_error
|
|
|
|
|
|
async def exec_op_list(self,oplist:List,agent_id:str)->tuple[List[str],bool]:
|
|
|
|
|
|
result_str = "op list is none"
|
2023-11-03 01:16:32 -07:00
|
|
|
|
if oplist is None:
|
2023-11-08 22:13:18 -08:00
|
|
|
|
return None,False
|
2023-11-03 01:16:32 -07:00
|
|
|
|
|
2023-11-08 22:13:18 -08:00
|
|
|
|
result_str = []
|
|
|
|
|
|
have_error = False
|
2023-11-01 19:29:55 -07:00
|
|
|
|
for op in oplist:
|
|
|
|
|
|
if op["op"] == "create":
|
2023-11-08 22:13:18 -08:00
|
|
|
|
await self.create(op["path"],op["content"])
|
2023-11-03 22:31:23 -07:00
|
|
|
|
elif op["op"] == "write_file":
|
2023-11-05 15:21:27 -08:00
|
|
|
|
is_append = op.get("is_append")
|
|
|
|
|
|
if is_append is None:
|
|
|
|
|
|
is_append = False
|
2023-11-08 22:13:18 -08:00
|
|
|
|
error_str = await self.write(op["path"],op["content"],is_append)
|
2023-11-01 19:29:55 -07:00
|
|
|
|
elif op["op"] == "delete":
|
2023-11-08 22:13:18 -08:00
|
|
|
|
error_str = await self.delete(op["path"])
|
2023-11-01 19:29:55 -07:00
|
|
|
|
elif op["op"] == "rename":
|
2023-11-08 22:13:18 -08:00
|
|
|
|
error_str = await self.rename(op["path"],op["new_name"])
|
2023-11-03 01:16:32 -07:00
|
|
|
|
elif op["op"] == "mkdir":
|
2023-11-08 22:13:18 -08:00
|
|
|
|
error_str = await self.mkdir(op["path"])
|
2023-11-03 01:16:32 -07:00
|
|
|
|
elif op["op"] == "create_todo":
|
|
|
|
|
|
todoObj = AgentTodo.from_dict(op["todo"])
|
2023-11-03 22:31:23 -07:00
|
|
|
|
todoObj.worker = agent_id
|
|
|
|
|
|
todoObj.createor = agent_id
|
|
|
|
|
|
parent_id = op.get("parent")
|
2023-11-08 22:13:18 -08:00
|
|
|
|
error_str = await self.create_todo(parent_id,todoObj)
|
2023-11-03 22:31:23 -07:00
|
|
|
|
elif op["op"] == "update_todo":
|
|
|
|
|
|
todo_id = op["id"]
|
|
|
|
|
|
new_stat = op["state"]
|
2023-11-08 22:13:18 -08:00
|
|
|
|
error_str = await self.update_todo(todo_id,new_stat)
|
2023-11-01 19:29:55 -07:00
|
|
|
|
else:
|
|
|
|
|
|
logger.error(f"execute op list failed: unknown op:{op['op']}")
|
2023-11-08 22:13:18 -08:00
|
|
|
|
error_str = f"execute op list failed: unknown op:{op['op']}"
|
|
|
|
|
|
|
|
|
|
|
|
if error_str:
|
|
|
|
|
|
have_error = True
|
|
|
|
|
|
result_str.append(error_str)
|
|
|
|
|
|
else:
|
|
|
|
|
|
result_str.append(f"execute success!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result_str,have_error
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
|
|
|
|
|
# file system operation: list,read,write,delete,move,stat
|
|
|
|
|
|
# inner_function
|
2023-11-01 19:29:55 -07:00
|
|
|
|
async def list(self,path:str,only_dir:bool=False) -> str:
|
|
|
|
|
|
directory_path = self.root_path + path
|
|
|
|
|
|
items = []
|
|
|
|
|
|
|
|
|
|
|
|
with await aiofiles.os.scandir(directory_path) as entries:
|
|
|
|
|
|
async for entry in entries:
|
|
|
|
|
|
is_dir = entry.is_dir()
|
|
|
|
|
|
if only_dir and not is_dir:
|
|
|
|
|
|
continue
|
|
|
|
|
|
item_type = "directory" if is_dir else "file"
|
|
|
|
|
|
items.append({"name": entry.name, "type": item_type})
|
|
|
|
|
|
|
|
|
|
|
|
return json.dumps(items)
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
|
|
|
|
|
# inner_function
|
2023-11-01 19:29:55 -07:00
|
|
|
|
async def read(self,path:str) -> str:
|
|
|
|
|
|
file_path = self.root_path + path
|
|
|
|
|
|
cur_encode = "utf-8"
|
|
|
|
|
|
async with aiofiles.open(file_path,'rb') as f:
|
|
|
|
|
|
cur_encode = chardet.detect(await f.read())['encoding']
|
|
|
|
|
|
|
|
|
|
|
|
async with aiofiles.open(file_path, mode='r', encoding=cur_encode) as f:
|
|
|
|
|
|
content = await f.read(2048)
|
|
|
|
|
|
return content
|
|
|
|
|
|
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
# operation or inner_function (MOST IMPORTANT FUNCTION)
|
2023-11-01 19:29:55 -07:00
|
|
|
|
async def write(self,path:str,content:str,is_append:bool=False) -> str:
|
|
|
|
|
|
file_path = self.root_path + path
|
2023-11-08 22:13:18 -08:00
|
|
|
|
try:
|
|
|
|
|
|
if is_append:
|
|
|
|
|
|
async with aiofiles.open(file_path, mode='a', encoding="utf-8") as f:
|
|
|
|
|
|
await f.write(content)
|
|
|
|
|
|
else:
|
2023-11-12 21:59:04 -08:00
|
|
|
|
if content is None:
|
|
|
|
|
|
# create dir
|
|
|
|
|
|
dir_path = self.root_path + path
|
|
|
|
|
|
os.makedirs(dir_path)
|
|
|
|
|
|
return True
|
|
|
|
|
|
else:
|
|
|
|
|
|
file_path = self.root_path + path
|
|
|
|
|
|
os.makedirs(os.path.dirname(file_path),exist_ok=True)
|
|
|
|
|
|
async with aiofiles.open(file_path, mode='w', encoding="utf-8") as f:
|
|
|
|
|
|
await f.write(content)
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
2023-11-08 22:13:18 -08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return str(e)
|
|
|
|
|
|
return None
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
2023-11-01 19:29:55 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
# operation or inner_function
|
2023-11-08 22:13:18 -08:00
|
|
|
|
async def delete(self,path:str) -> str:
|
|
|
|
|
|
try:
|
|
|
|
|
|
file_path = self.root_path + path
|
|
|
|
|
|
os.remove(file_path)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return str(e)
|
|
|
|
|
|
|
|
|
|
|
|
return None
|
2023-11-01 19:29:55 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
# operation or inner_function
|
|
|
|
|
|
async def move(self,path:str,new_path:str) -> str:
|
2023-11-08 22:13:18 -08:00
|
|
|
|
try:
|
|
|
|
|
|
file_path = self.root_path + path
|
2023-11-12 21:59:04 -08:00
|
|
|
|
new_path = self.root_path + new_path
|
2023-11-08 22:13:18 -08:00
|
|
|
|
os.rename(file_path,new_path)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return str(e)
|
|
|
|
|
|
|
|
|
|
|
|
return None
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
|
|
|
|
|
# inner_function
|
|
|
|
|
|
async def stat(self,path:str) -> str:
|
|
|
|
|
|
try:
|
|
|
|
|
|
file_path = self.root_path + path
|
|
|
|
|
|
stat = os.stat(file_path)
|
|
|
|
|
|
return json.dumps(stat)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return str(e)
|
|
|
|
|
|
|
|
|
|
|
|
# operation or inner_function
|
|
|
|
|
|
async def symlink(self,path:str,target:str) -> str:
|
|
|
|
|
|
try:
|
2023-11-14 16:49:36 -08:00
|
|
|
|
#file_path = self.root_path + path
|
2023-11-12 21:59:04 -08:00
|
|
|
|
target_path = self.root_path + target
|
2023-11-14 16:49:36 -08:00
|
|
|
|
dir_path = os.path.dirname(target_path)
|
|
|
|
|
|
os.makedirs(dir_path,exist_ok=True)
|
|
|
|
|
|
os.symlink(path,target_path)
|
2023-11-12 21:59:04 -08:00
|
|
|
|
except Exception as e:
|
2023-11-14 16:49:36 -08:00
|
|
|
|
logger.error("symlink failed:%s",e)
|
2023-11-12 21:59:04 -08:00
|
|
|
|
return str(e)
|
|
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
# TODO use diff to update large file content
|
|
|
|
|
|
async def update_by_diff(self,path:str,diff):
|
|
|
|
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# doc system (read_only,agent cann't modify doc)
|
|
|
|
|
|
|
|
|
|
|
|
# inner_function
|
|
|
|
|
|
async def list_db(self) -> str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
# inner_function
|
|
|
|
|
|
async def get_db_desc(self,db_name:str) -> str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
# inner_function
|
|
|
|
|
|
async def query(self,db_name:str,sql:str) -> str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# search (web)
|
|
|
|
|
|
# inner_function
|
|
|
|
|
|
async def google_search(self,keyword:str,opt=None) -> str:
|
|
|
|
|
|
pass
|
2023-11-01 19:29:55 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
# inner_function
|
|
|
|
|
|
async def local_search(self,keyword:str,root_path=None ,opt=None) -> str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# inner_function, might be return a image is better
|
|
|
|
|
|
async def web_get(self,url:str) -> str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# inner_function
|
|
|
|
|
|
async def blockchain_get(self,chainid:str,query:dict) -> str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# code interpreter
|
|
|
|
|
|
# inner_function or operation
|
|
|
|
|
|
async def eval_code(self,pycode:str) -> str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# operation or inner_function
|
|
|
|
|
|
async def improve_code(self,path:str):
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# operation or inner_function
|
|
|
|
|
|
async def run(self,file_path:str)->str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# operation or inner_function
|
|
|
|
|
|
async def pub_service(self,project_path:str):
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# operation or inner_function
|
|
|
|
|
|
async def exec_tx(self,chain_id:str,tx:dict) -> str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# social ability
|
|
|
|
|
|
# operation or inner_function
|
|
|
|
|
|
async def post_message(self,target:str,msg:AgentMsg,wait_time) -> AgentMsg:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# operation or inner_function
|
|
|
|
|
|
async def add_contact(self,name:str,contact_info) -> str:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
# inner_function , include contact realtime info
|
|
|
|
|
|
async def get_contact(self,name_list:List[str],opt:dict) -> List:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Task/todo system , create,update,delete,query
|
2023-11-03 01:16:32 -07:00
|
|
|
|
async def get_todo_tree(self,path:str = None,deep:int = 4):
|
|
|
|
|
|
if path:
|
|
|
|
|
|
directory_path = self.root_path + "/todos/" + path
|
|
|
|
|
|
else:
|
|
|
|
|
|
directory_path = self.root_path + "/todos"
|
|
|
|
|
|
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
2023-11-03 01:16:32 -07:00
|
|
|
|
str_result:str = "/todos\n"
|
|
|
|
|
|
todo_count:int = 0
|
|
|
|
|
|
|
|
|
|
|
|
async def scan_dir(directory_path:str,deep:int):
|
|
|
|
|
|
nonlocal str_result
|
|
|
|
|
|
nonlocal todo_count
|
|
|
|
|
|
if deep <= 0:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2023-11-03 22:31:23 -07:00
|
|
|
|
if os.path.exists(directory_path) is False:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2023-11-03 01:16:32 -07:00
|
|
|
|
for entry in os.scandir(directory_path):
|
|
|
|
|
|
is_dir = entry.is_dir()
|
|
|
|
|
|
if not is_dir:
|
|
|
|
|
|
continue
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
|
|
|
|
|
if entry.name.startswith("."):
|
|
|
|
|
|
continue
|
2023-11-03 01:16:32 -07:00
|
|
|
|
|
|
|
|
|
|
todo_count = todo_count + 1
|
2023-11-05 15:21:27 -08:00
|
|
|
|
str_result = str_result + f"{' '*(4-deep)}{entry.name}\n"
|
2023-11-03 01:16:32 -07:00
|
|
|
|
await scan_dir(entry.path,deep-1)
|
2023-11-01 19:29:55 -07:00
|
|
|
|
|
2023-11-03 01:16:32 -07:00
|
|
|
|
await scan_dir(directory_path,deep)
|
|
|
|
|
|
return str_result,todo_count
|
2023-11-01 19:29:55 -07:00
|
|
|
|
|
2023-11-03 22:31:23 -07:00
|
|
|
|
async def get_todo_list(self,agent_id:str,path:str = None)->List[AgentTodo]:
|
2023-11-05 15:21:27 -08:00
|
|
|
|
logger.info("get_todo_list:%s,%s",agent_id,path)
|
2023-11-03 22:31:23 -07:00
|
|
|
|
if path:
|
|
|
|
|
|
directory_path = self.root_path + "/todos/" + path
|
|
|
|
|
|
else:
|
|
|
|
|
|
directory_path = self.root_path + "/todos"
|
|
|
|
|
|
|
|
|
|
|
|
result_list:List[AgentTodo] = []
|
2023-11-01 19:29:55 -07:00
|
|
|
|
|
2023-11-03 22:31:23 -07:00
|
|
|
|
async def scan_dir(directory_path:str,deep:int,parent:AgentTodo=None):
|
|
|
|
|
|
nonlocal result_list
|
|
|
|
|
|
if os.path.exists(directory_path) is False:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
for entry in os.scandir(directory_path):
|
|
|
|
|
|
is_dir = entry.is_dir()
|
|
|
|
|
|
if not is_dir:
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
if entry.name.startswith("."):
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
todo = await self.get_todo_by_fullpath(entry.path)
|
|
|
|
|
|
if todo:
|
2023-11-08 22:13:18 -08:00
|
|
|
|
if todo.worker:
|
|
|
|
|
|
if todo.worker != agent_id:
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
2023-11-03 22:31:23 -07:00
|
|
|
|
if parent:
|
2023-11-05 15:21:27 -08:00
|
|
|
|
parent.sub_todos[todo.todo_id] = todo
|
2023-11-08 22:13:18 -08:00
|
|
|
|
|
2023-11-03 22:31:23 -07:00
|
|
|
|
result_list.append(todo)
|
2023-11-08 22:13:18 -08:00
|
|
|
|
todo.rank = int(todo.create_time)>>deep
|
|
|
|
|
|
await scan_dir(entry.path,deep + 1,todo)
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
await scan_dir(directory_path,0)
|
|
|
|
|
|
#sort by rank
|
|
|
|
|
|
result_list.sort(key=lambda x:(x.rank,x.title))
|
2023-11-05 15:21:27 -08:00
|
|
|
|
logger.info("get_todo_list return,todolist.length() is %d",len(result_list))
|
2023-11-03 22:31:23 -07:00
|
|
|
|
return result_list
|
|
|
|
|
|
|
|
|
|
|
|
async def get_todo_by_fullpath(self,path:str) -> AgentTodo:
|
2023-11-08 22:13:18 -08:00
|
|
|
|
logger.info("get_todo_by_fullpath:%s",path)
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
2023-11-08 22:13:18 -08:00
|
|
|
|
detail_path = path + "/detail"
|
|
|
|
|
|
try:
|
|
|
|
|
|
async with aiofiles.open(detail_path, mode='r', encoding="utf-8") as f:
|
|
|
|
|
|
content = await f.read(4096)
|
|
|
|
|
|
logger.debug("get_todo_by_fullpath:%s,content:%s",path,content)
|
|
|
|
|
|
todo_dict = json.loads(content)
|
|
|
|
|
|
result_todo = AgentTodo.from_dict(todo_dict)
|
|
|
|
|
|
if result_todo:
|
|
|
|
|
|
relative_path = os.path.relpath(path, self.root_path + "/todos/")
|
|
|
|
|
|
if not relative_path.startswith('/'):
|
|
|
|
|
|
relative_path = '/' + relative_path
|
|
|
|
|
|
result_todo.todo_path = relative_path
|
|
|
|
|
|
self.known_todo[result_todo.todo_id] = result_todo
|
|
|
|
|
|
else:
|
|
|
|
|
|
logger.error("get_todo_by_path:%s,parse failed!",path)
|
|
|
|
|
|
|
|
|
|
|
|
return result_todo
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error("get_todo_by_path:%s,failed:%s",path,e)
|
|
|
|
|
|
return None
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
2023-11-01 19:29:55 -07:00
|
|
|
|
async def get_todo(self,id:str) -> AgentTodo:
|
2023-11-03 22:31:23 -07:00
|
|
|
|
return self.known_todo.get(id)
|
2023-11-01 19:29:55 -07:00
|
|
|
|
|
2023-11-08 22:13:18 -08:00
|
|
|
|
async def create_todo(self,parent_id:str,todo:AgentTodo) -> str:
|
|
|
|
|
|
try:
|
|
|
|
|
|
if parent_id:
|
|
|
|
|
|
if parent_id not in self.known_todo:
|
|
|
|
|
|
logger.error("create_todo failed: parent_id not found!")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
parent_path = self.known_todo.get(parent_id).todo_path
|
|
|
|
|
|
todo_path = f"{parent_path}/{todo.title}"
|
|
|
|
|
|
else:
|
|
|
|
|
|
todo_path = todo.title
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
2023-11-08 22:13:18 -08:00
|
|
|
|
dir_path = f"{self.root_path}/todos/{todo_path}"
|
|
|
|
|
|
|
|
|
|
|
|
os.makedirs(dir_path)
|
|
|
|
|
|
detail_path = f"{dir_path}/detail"
|
|
|
|
|
|
if todo.todo_path is None:
|
|
|
|
|
|
todo.todo_path = todo_path
|
|
|
|
|
|
logger.info("create_todo %s",detail_path)
|
2023-11-03 22:31:23 -07:00
|
|
|
|
async with aiofiles.open(detail_path, mode='w', encoding="utf-8") as f:
|
2023-11-08 22:13:18 -08:00
|
|
|
|
await f.write(json.dumps(todo.to_dict()))
|
|
|
|
|
|
self.known_todo[todo.todo_id] = todo
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error("create_todo failed:%s",e)
|
|
|
|
|
|
return str(e)
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
2023-11-08 22:13:18 -08:00
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
async def update_todo(self,todo_id:str,new_stat:str)->str:
|
|
|
|
|
|
try:
|
|
|
|
|
|
todo : AgentTodo = self.known_todo.get(todo_id)
|
|
|
|
|
|
if todo:
|
|
|
|
|
|
todo.state = new_stat
|
|
|
|
|
|
detail_path = f"{self.root_path}/todos/{todo.todo_path}/detail"
|
|
|
|
|
|
async with aiofiles.open(detail_path, mode='w', encoding="utf-8") as f:
|
|
|
|
|
|
await f.write(json.dumps(todo.to_dict()))
|
|
|
|
|
|
return None
|
|
|
|
|
|
else:
|
|
|
|
|
|
return "todo not found."
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return str(e)
|
2023-11-03 22:31:23 -07:00
|
|
|
|
|
2023-11-08 22:13:18 -08:00
|
|
|
|
async def append_worklog(self,todo:AgentTodo,result:AgentTodoResult):
|
|
|
|
|
|
worklog = f"{self.root_path}/todos/{todo.todo_path}/.worklog"
|
|
|
|
|
|
|
|
|
|
|
|
async with aiofiles.open(worklog, mode='w+', encoding="utf-8") as f:
|
|
|
|
|
|
content = await f.read()
|
|
|
|
|
|
if len(content) > 0:
|
|
|
|
|
|
json_obj = json.loads(content)
|
|
|
|
|
|
else:
|
|
|
|
|
|
json_obj = {}
|
|
|
|
|
|
logs = json_obj.get("logs")
|
|
|
|
|
|
if logs is None:
|
|
|
|
|
|
logs = []
|
|
|
|
|
|
logs.append(result.to_dict())
|
|
|
|
|
|
json_obj["logs"] = logs
|
|
|
|
|
|
await f.write(json.dumps(json_obj))
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
async def set_wakeup_timer(self,todo_id:str,timestamp:int) -> str:
|
|
|
|
|
|
pass
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
# knowledge base system
|
|
|
|
|
|
def get_knowledge_base_ai_functions(self):
|
2023-11-14 16:49:36 -08:00
|
|
|
|
all_inner_function = []
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
2023-11-14 16:49:36 -08:00
|
|
|
|
all_inner_function.append(SimpleAIFunction("get_knowledge_catalog","get knowledge catalog in tree format",
|
2023-11-12 21:59:04 -08:00
|
|
|
|
self.get_knowledege_catalog,
|
2023-11-14 16:49:36 -08:00
|
|
|
|
{"path":f"catalog path,none is /","depth":"max depth of catalog tree,default is 4"}))
|
|
|
|
|
|
all_inner_function.append(SimpleAIFunction("get_knowledge","get knowledge metadata",
|
2023-11-12 21:59:04 -08:00
|
|
|
|
self.get_knowledge,
|
2023-11-14 16:49:36 -08:00
|
|
|
|
{"path":f"knowledge path"}))
|
|
|
|
|
|
all_inner_function.append(SimpleAIFunction("load_knowledge_content","load knowledge content",
|
2023-11-12 21:59:04 -08:00
|
|
|
|
self.load_knowledge_content,
|
2023-11-14 16:49:36 -08:00
|
|
|
|
{"path":f"knowledge path","pos":"start position of content","length":"length of content"}))
|
|
|
|
|
|
result_func = []
|
|
|
|
|
|
result_len = 0
|
|
|
|
|
|
for inner_func in all_inner_function:
|
|
|
|
|
|
func_name = inner_func.get_name()
|
|
|
|
|
|
|
|
|
|
|
|
this_func = {}
|
|
|
|
|
|
this_func["name"] = func_name
|
|
|
|
|
|
this_func["description"] = inner_func.get_description()
|
|
|
|
|
|
this_func["parameters"] = inner_func.get_parameters()
|
|
|
|
|
|
result_len += len(json.dumps(this_func)) / 4
|
|
|
|
|
|
result_func.append(this_func)
|
|
|
|
|
|
|
|
|
|
|
|
return result_func,result_len
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
|
|
|
|
|
async def get_knowledege_catalog(self,path:str=None,only_dir =True,max_depth:int=5)->str:
|
|
|
|
|
|
if path:
|
|
|
|
|
|
full_path = f"{self.root_path}/knowledge/{path}"
|
|
|
|
|
|
else:
|
|
|
|
|
|
full_path = f"{self.root_path}/knowledge"
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
catlogs,file_count = await self.get_directory_structure(full_path,max_depth,only_dir)
|
|
|
|
|
|
return catlogs
|
|
|
|
|
|
|
|
|
|
|
|
async def get_directory_structure(self,root_dir, max_depth:int=4, only_dir=True, indent=1):
|
|
|
|
|
|
file_count = 0
|
|
|
|
|
|
structure_str = ''
|
|
|
|
|
|
if os.path.isdir(root_dir):
|
|
|
|
|
|
sub_files = []
|
|
|
|
|
|
with os.scandir(root_dir) as it:
|
|
|
|
|
|
for entry in it:
|
|
|
|
|
|
if entry.is_dir():
|
|
|
|
|
|
sub_structure, sub_count = await self.get_directory_structure(entry.path, max_depth, only_dir, indent + 1)
|
|
|
|
|
|
if sub_structure:
|
|
|
|
|
|
structure_str += sub_structure
|
|
|
|
|
|
file_count += sub_count
|
|
|
|
|
|
else:
|
|
|
|
|
|
file_count += 1
|
|
|
|
|
|
sub_files.append(entry.name)
|
|
|
|
|
|
|
|
|
|
|
|
if only_dir is False:
|
|
|
|
|
|
for file_name in sub_files:
|
|
|
|
|
|
structure_str = structure_str + ' ' * (indent+1) + file_name + '\n'
|
|
|
|
|
|
|
|
|
|
|
|
dir_name = os.path.basename(root_dir)
|
|
|
|
|
|
dir_info = f"{dir_name} <count: {file_count}>"
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
|
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
structure_str = ' ' * indent + dir_info + '\n' + structure_str
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
if indent - 1 >= max_depth:
|
|
|
|
|
|
return None, file_count
|
|
|
|
|
|
else:
|
|
|
|
|
|
return structure_str, file_count
|
|
|
|
|
|
|
|
|
|
|
|
# inner_function
|
|
|
|
|
|
async def get_knowledge(self,path:str) -> str:
|
|
|
|
|
|
full_path = f"{self.root_path}/knowledge/{path}"
|
|
|
|
|
|
if os.islink(full_path):
|
|
|
|
|
|
org_path = os.readlink(full_path)
|
|
|
|
|
|
hash = self.kb_db.get_hash_by_doc_path(org_path)
|
|
|
|
|
|
if hash:
|
|
|
|
|
|
return self.kb_db.get_knowledge(org_path)
|
|
|
|
|
|
|
|
|
|
|
|
return "not found"
|
|
|
|
|
|
|
2023-11-14 16:49:36 -08:00
|
|
|
|
async def load_knowledge_content(self,path:str,pos:int=0,length:int=None) -> str:
|
|
|
|
|
|
if path.endswith("pdf"):
|
|
|
|
|
|
logger.info("load_knowledge_content:pdf")
|
|
|
|
|
|
dir_path = os.path.dirname(path)
|
|
|
|
|
|
base_name = os.path.basename(path)
|
|
|
|
|
|
text_content_path = f"{dir_path}/.{base_name}.txt"
|
|
|
|
|
|
if os.path.exists(text_content_path) is False:
|
|
|
|
|
|
return None
|
|
|
|
|
|
async with aiofiles.open(path, mode='r', encoding=cur_encode) as f:
|
|
|
|
|
|
await f.seek(pos)
|
|
|
|
|
|
content = await f.read(length)
|
|
|
|
|
|
return content
|
|
|
|
|
|
else:
|
|
|
|
|
|
async with aiofiles.open(path,'rb') as f:
|
|
|
|
|
|
cur_encode = chardet.detect(await f.read(1024))['encoding']
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-14 16:49:36 -08:00
|
|
|
|
async with aiofiles.open(path, mode='r', encoding=cur_encode) as f:
|
|
|
|
|
|
await f.seek(pos)
|
|
|
|
|
|
content = await f.read(length)
|
|
|
|
|
|
return content
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
return "load content failed."
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
def _add_document_dir(self,path:str):
|
|
|
|
|
|
self.doc_dirs[path] = 0
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
def _start_scan_document(self):
|
|
|
|
|
|
if self._scan_thread is None:
|
|
|
|
|
|
self._scan_thread = threading.Thread(target=self._scan_document)
|
|
|
|
|
|
self._scan_thread.start()
|
|
|
|
|
|
if self._scan_dirthread is None:
|
|
|
|
|
|
self._scan_dirthread = threading.Thread(target=self._scan_dir)
|
|
|
|
|
|
self._scan_dirthread.start()
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
def _parse_pdf_bookmarks(self,bookmarks, parent:list):
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
for item in bookmarks:
|
|
|
|
|
|
if isinstance(item,list):
|
|
|
|
|
|
self._parse_pdf_bookmarks(item,parent)
|
|
|
|
|
|
else:
|
|
|
|
|
|
if item.title:
|
|
|
|
|
|
new_item = {}
|
|
|
|
|
|
new_item["page"] = item.page.idnum
|
|
|
|
|
|
new_item["title"] = item.title
|
|
|
|
|
|
my_childs = []
|
|
|
|
|
|
if item.childs:
|
|
|
|
|
|
if len(item.childs) > 0:
|
|
|
|
|
|
self._parse_pdf_bookmarks(item.childs, my_childs)
|
|
|
|
|
|
new_item["childs"] = my_childs
|
|
|
|
|
|
parent.append(new_item)
|
|
|
|
|
|
else:
|
|
|
|
|
|
logger.warning("parse pdf bookmarks failed: item.title is None!")
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def _parse_pdf(self,doc_path:str):
|
|
|
|
|
|
metadata = {}
|
|
|
|
|
|
with open(doc_path, 'rb') as file:
|
|
|
|
|
|
reader = PyPDF2.PdfReader(file)
|
2023-11-14 16:49:36 -08:00
|
|
|
|
try:
|
|
|
|
|
|
doc_info = reader.metadata
|
|
|
|
|
|
if doc_info:
|
|
|
|
|
|
if doc_info.title:
|
|
|
|
|
|
metadata["title"] = doc_info.title
|
|
|
|
|
|
if doc_info.author:
|
|
|
|
|
|
metadata["authors"] = doc_info.author
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.warn("parse pdf metadata failed:%s",e)
|
|
|
|
|
|
|
|
|
|
|
|
dir_path = os.path.dirname(doc_path)
|
|
|
|
|
|
base_name = os.path.basename(doc_path)
|
|
|
|
|
|
text_content_path = f"{dir_path}/.{base_name}.txt"
|
|
|
|
|
|
full_text = ""
|
|
|
|
|
|
|
|
|
|
|
|
for page in reader.pages:
|
|
|
|
|
|
text = page.extract_text()
|
|
|
|
|
|
full_text += text
|
|
|
|
|
|
with open(text_content_path, 'w', encoding='utf-8') as f:
|
|
|
|
|
|
f.write(full_text)
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
bookmarks = reader.outline
|
|
|
|
|
|
if bookmarks:
|
|
|
|
|
|
catalogs = []
|
|
|
|
|
|
self._parse_pdf_bookmarks(bookmarks,catalogs)
|
|
|
|
|
|
metadata["catalogs"] = json.dumps(catalogs)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.warn("parse pdf bookmarks failed:%s",e)
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
|
|
|
|
|
return metadata
|
|
|
|
|
|
|
|
|
|
|
|
def _parse_txt(self,doc_path:str):
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
def _parse_md(self,doc_path:str):
|
|
|
|
|
|
metadata = {}
|
|
|
|
|
|
cur_encode = "utf-8"
|
|
|
|
|
|
with open(doc_path,'rb') as f:
|
|
|
|
|
|
cur_encode = chardet.detect(f.read(1024))['encoding']
|
|
|
|
|
|
|
|
|
|
|
|
with open(doc_path, mode='r', encoding=cur_encode) as f:
|
|
|
|
|
|
content = f.read()
|
|
|
|
|
|
match = re.search(r'^# (.*)', content, re.MULTILINE)
|
|
|
|
|
|
if match:
|
|
|
|
|
|
metadata['title'] = match.group(1).strip()
|
|
|
|
|
|
md = Markdown(extensions=['toc'])
|
|
|
|
|
|
html_str = md.convert(content)
|
|
|
|
|
|
toc = md.toc
|
|
|
|
|
|
if toc:
|
|
|
|
|
|
metadata['catalogs'] = toc
|
|
|
|
|
|
|
|
|
|
|
|
return metadata
|
|
|
|
|
|
|
|
|
|
|
|
def _parse_document(self,doc_path:str):
|
|
|
|
|
|
hash_result = None
|
|
|
|
|
|
title = os.path.basename(doc_path)
|
|
|
|
|
|
meta_data = {}
|
|
|
|
|
|
|
|
|
|
|
|
with open(doc_path, "rb") as f:
|
|
|
|
|
|
hash_md5 = hashlib.md5()
|
|
|
|
|
|
for chunk in iter(lambda: f.read(1024*1024), b""):
|
|
|
|
|
|
hash_md5.update(chunk)
|
|
|
|
|
|
hash_result = hash_md5.hexdigest()
|
|
|
|
|
|
try:
|
|
|
|
|
|
if doc_path.endswith(".md"):
|
|
|
|
|
|
meta_data = self._parse_md(doc_path)
|
|
|
|
|
|
elif doc_path.endswith(".pdf"):
|
|
|
|
|
|
meta_data = self._parse_pdf(doc_path)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error("parse document %s failed:%s",doc_path,e)
|
|
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
|
|
|
|
|
|
if meta_data.get("title"):
|
|
|
|
|
|
title = meta_data["title"]
|
2023-11-14 16:49:36 -08:00
|
|
|
|
logger.info("parse document %s!",doc_path)
|
2023-11-12 21:59:04 -08:00
|
|
|
|
return hash_result,title,meta_data
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
|
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
def _support_file(self,file_name:str) -> bool:
|
2023-11-14 16:49:36 -08:00
|
|
|
|
if file_name.startswith("."):
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
if file_name.endswith(".pdf"):
|
|
|
|
|
|
return True
|
|
|
|
|
|
if file_name.endswith(".md"):
|
|
|
|
|
|
return True
|
|
|
|
|
|
if file_name.endswith(".txt"):
|
|
|
|
|
|
return True
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def _scan_dir(self):
|
|
|
|
|
|
while True:
|
|
|
|
|
|
time.sleep(10)
|
|
|
|
|
|
for directory in self.doc_dirs.keys():
|
|
|
|
|
|
now = time.time()
|
|
|
|
|
|
if now - self.doc_dirs[directory] > 60*15:
|
|
|
|
|
|
self.doc_dirs[directory] = time.time()
|
|
|
|
|
|
else:
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
for root, dirs, files in os.walk(directory):
|
|
|
|
|
|
for file in files:
|
|
|
|
|
|
if self._support_file(file):
|
|
|
|
|
|
full_path = os.path.join(root, file)
|
|
|
|
|
|
full_path = os.path.normpath(full_path)
|
|
|
|
|
|
if self.kb_db.is_doc_exist(full_path):
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
file_stat = os.stat(full_path)
|
|
|
|
|
|
if file_stat.st_size < 1:
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
if file_stat.st_size < 1024*1024*8:
|
|
|
|
|
|
#parse and insert
|
|
|
|
|
|
hash,title,meta_data = self._parse_document(full_path)
|
|
|
|
|
|
self.kb_db.add_doc(full_path,file_stat.st_size,file_stat.st_mtime,hash)
|
|
|
|
|
|
self.kb_db.add_knowledge(hash,title,meta_data)
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.kb_db.add_doc(full_path,file_stat.st_size,file_stat.st_mtime)
|
|
|
|
|
|
|
|
|
|
|
|
def _scan_document(self):
|
|
|
|
|
|
while True:
|
|
|
|
|
|
time.sleep(10)
|
|
|
|
|
|
parse_queue = self.kb_db.get_docs_without_hash()
|
|
|
|
|
|
for doc_path in parse_queue:
|
|
|
|
|
|
hash,title,meta_data = self._parse_document(doc_path)
|
|
|
|
|
|
self.kb_db.set_doc_hash(doc_path,hash)
|
|
|
|
|
|
self.kb_db.add_knowledge(hash,title,meta_data)
|
|
|
|
|
|
|
2023-10-18 11:19:11 -07:00
|
|
|
|
|
|
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
2023-11-01 19:29:55 -07:00
|
|
|
|
# merge to standard workspace env, **ABANDON this!**
|
2023-10-18 11:19:11 -07:00
|
|
|
|
class KnowledgeBaseFileSystemEnvironment(Environment):
|
|
|
|
|
|
def __init__(self, env_id: str) -> None:
|
|
|
|
|
|
super().__init__(env_id)
|
|
|
|
|
|
self.root_path = "."
|
|
|
|
|
|
|
|
|
|
|
|
operator_param = {
|
|
|
|
|
|
"path": "full path of target directory",
|
|
|
|
|
|
}
|
|
|
|
|
|
self.add_ai_function(SimpleAIFunction("list",
|
|
|
|
|
|
"list the files and sub directory in target directory,result is a json array",
|
|
|
|
|
|
self.list,operator_param))
|
|
|
|
|
|
|
|
|
|
|
|
operator_param = {
|
|
|
|
|
|
"path": "full path of target file",
|
|
|
|
|
|
}
|
|
|
|
|
|
self.add_ai_function(SimpleAIFunction("cat",
|
|
|
|
|
|
"cat the file content in target path,result is a string",
|
|
|
|
|
|
self.cat,operator_param))
|
|
|
|
|
|
|
|
|
|
|
|
def set_root_path(self,path:str):
|
|
|
|
|
|
self.root_path = path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def list(self,path:str) -> str:
|
|
|
|
|
|
directory_path = self.root_path + path
|
|
|
|
|
|
items = []
|
|
|
|
|
|
|
|
|
|
|
|
with await aiofiles.os.scandir(directory_path) as entries:
|
|
|
|
|
|
async for entry in entries:
|
|
|
|
|
|
item_type = "directory" if entry.is_dir() else "file"
|
|
|
|
|
|
items.append({"name": entry.name, "type": item_type})
|
|
|
|
|
|
|
|
|
|
|
|
return json.dumps(items)
|
|
|
|
|
|
|
|
|
|
|
|
async def cat(self,path:str) -> str:
|
|
|
|
|
|
file_path = self.root_path + path
|
|
|
|
|
|
cur_encode = "utf-8"
|
|
|
|
|
|
async with aiofiles.open(file_path,'rb') as f:
|
|
|
|
|
|
cur_encode = chardet.detect(await f.read())['encoding']
|
|
|
|
|
|
|
|
|
|
|
|
async with aiofiles.open(file_path, mode='r', encoding=cur_encode) as f:
|
|
|
|
|
|
content = await f.read(2048)
|
|
|
|
|
|
return content
|
2023-09-21 23:18:52 -07:00
|
|
|
|
|
2023-11-12 21:59:04 -08:00
|
|
|
|
|
|
|
|
|
|
class ShellEnvironment(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"
|
|
|
|
|
|
|