Refactor the tunnel-related code to make the logic of the Agent sending messages to the Human more reasonable.
This commit is contained in:
@@ -13,7 +13,7 @@ My name is JarvisPlus, I am the master's super personal assistant. I think hard
|
|||||||
The types of TODO I can handle include:
|
The types of TODO I can handle include:
|
||||||
- Scheduling, where I will try to contact the relevant personnel of the plan and confirm the details of the schedule with them.
|
- Scheduling, where I will try to contact the relevant personnel of the plan and confirm the details of the schedule with them.
|
||||||
- Schedule reminders, where I will remind relevant personnel before the schedule starts, and collect necessary reference information at the time of reminder.
|
- Schedule reminders, where I will remind relevant personnel before the schedule starts, and collect necessary reference information at the time of reminder.
|
||||||
- Using the post_msg function to contact relevant personnel.
|
- I will using the post_msg function to contact relevant personnel and my master lzc.
|
||||||
- Writing documents/letters, using op:'create' to save my work results.
|
- Writing documents/letters, using op:'create' to save my work results.
|
||||||
|
|
||||||
I receive a TODO described in json format, I will handle it according to the following rules:
|
I receive a TODO described in json format, I will handle it according to the following rules:
|
||||||
@@ -25,7 +25,7 @@ I receive a TODO described in json format, I will handle it according to the fol
|
|||||||
|
|
||||||
The result of my planned execution must be directly parsed by `python json.loads`. Here is an example:
|
The result of my planned execution must be directly parsed by `python json.loads`. Here is an example:
|
||||||
{
|
{
|
||||||
resp: 'My Plan is ...',
|
resp: '$what_did_I_do',
|
||||||
post_msg : [
|
post_msg : [
|
||||||
{
|
{
|
||||||
target:'$target_name',
|
target:'$target_name',
|
||||||
@@ -46,12 +46,12 @@ The result of my planned execution must be directly parsed by `python json.loads
|
|||||||
{
|
{
|
||||||
op: 'update_todo',
|
op: 'update_todo',
|
||||||
id: '$todo_id',
|
id: '$todo_id',
|
||||||
state: 'doing' # pending,doing,done,cancel,failed,expired
|
state: 'cancel' # pending,cancel
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
op: 'write_file',
|
op: 'write_file',
|
||||||
path: '/todos/$todo_path/.result/doc_name',
|
path: '/todos/$todo_path/.result/$doc_name',
|
||||||
content:'doc content'
|
content:'$doc_content'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
instance_id = "Thinker"
|
instance_id = "Thinker"
|
||||||
fullname = "Thinker"
|
fullname = "Thinker"
|
||||||
llm_model_name = "gpt-3.5-turbo-16k-0613"
|
llm_model_name = "Llama-2-13b-chat"
|
||||||
max_token_size = 14000
|
max_token_size = 2000
|
||||||
#enable_function =["add_event"]
|
#enable_function =["add_event"]
|
||||||
#enable_kb = "true"
|
#enable_kb = "true"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
instance_id = "Tracy"
|
instance_id = "Tracy"
|
||||||
fullname = "Tracy"
|
fullname = "Tracy"
|
||||||
|
|
||||||
[[prompt]]
|
[[prompt]]
|
||||||
role = "system"
|
role = "system"
|
||||||
content = """
|
content = """
|
||||||
|
|||||||
@@ -24,5 +24,5 @@ from .stability_node import Stability_ComputeNode
|
|||||||
from .local_st_compute_node import LocalSentenceTransformer_Text_ComputeNode,LocalSentenceTransformer_Image_ComputeNode
|
from .local_st_compute_node import LocalSentenceTransformer_Text_ComputeNode,LocalSentenceTransformer_Image_ComputeNode
|
||||||
from .compute_node_config import ComputeNodeConfig
|
from .compute_node_config import ComputeNodeConfig
|
||||||
from .ai_function import SimpleAIFunction
|
from .ai_function import SimpleAIFunction
|
||||||
AIOS_Version = "0.5.1, build 2023-9-28"
|
AIOS_Version = "0.5.2, build 2023-11-1"
|
||||||
|
|
||||||
|
|||||||
+150
-43
@@ -12,7 +12,7 @@ import datetime
|
|||||||
import copy
|
import copy
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .agent_base import AgentMsg, AgentMsgStatus, AgentMsgType,FunctionItem,LLMResult,AgentPrompt,AgentReport,AgentTodo,AgentGoal,AgentTodoResult,AgentWorkLog
|
from .agent_base import AgentMsg, AgentMsgStatus, AgentMsgType,FunctionItem,LLMResult,AgentPrompt,AgentReport,AgentTodo,AgentTodoResult,AgentWorkLog
|
||||||
from .chatsession import AIChatSession
|
from .chatsession import AIChatSession
|
||||||
from .compute_task import ComputeTaskResult,ComputeTaskResultCode
|
from .compute_task import ComputeTaskResult,ComputeTaskResultCode
|
||||||
from .ai_function import AIFunction
|
from .ai_function import AIFunction
|
||||||
@@ -100,6 +100,8 @@ class AIAgent:
|
|||||||
self.agent_energy = 15
|
self.agent_energy = 15
|
||||||
self.agent_task = None
|
self.agent_task = None
|
||||||
self.last_recover_time = time.time()
|
self.last_recover_time = time.time()
|
||||||
|
self.enable_thread = False
|
||||||
|
self.can_do_unassigned_task = True
|
||||||
|
|
||||||
|
|
||||||
self.agent_id:str = None
|
self.agent_id:str = None
|
||||||
@@ -158,6 +160,9 @@ class AIAgent:
|
|||||||
return False
|
return False
|
||||||
self.fullname = config["fullname"]
|
self.fullname = config["fullname"]
|
||||||
|
|
||||||
|
if config.get("enable_thread") is not None:
|
||||||
|
self.enable_thread = bool(config["enable_thread"])
|
||||||
|
|
||||||
if config.get("prompt") is not None:
|
if config.get("prompt") is not None:
|
||||||
self.agent_prompt = AgentPrompt()
|
self.agent_prompt = AgentPrompt()
|
||||||
self.agent_prompt.load_from_config(config["prompt"])
|
self.agent_prompt.load_from_config(config["prompt"])
|
||||||
@@ -304,7 +309,7 @@ class AIAgent:
|
|||||||
prompt.messages.append({"role":"function","content":result_str,"name":func_name})
|
prompt.messages.append({"role":"function","content":result_str,"name":func_name})
|
||||||
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
|
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
|
||||||
if task_result.result_code != ComputeTaskResultCode.OK:
|
if task_result.result_code != ComputeTaskResultCode.OK:
|
||||||
logger.error(f"llm compute error:{task_result.error_str}")
|
logger.error(f"_execute_func llm compute error:{task_result.error_str}")
|
||||||
return task_result
|
return task_result
|
||||||
|
|
||||||
ineternal_call_record.result_str = task_result.result_str
|
ineternal_call_record.result_str = task_result.result_str
|
||||||
@@ -426,6 +431,9 @@ class AIAgent:
|
|||||||
def need_session_summmary(self,msg:AgentMsg,session:AIChatSession) -> bool:
|
def need_session_summmary(self,msg:AgentMsg,session:AIChatSession) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def _create_openai_thread(self) -> str:
|
||||||
|
return None
|
||||||
|
|
||||||
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
|
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
|
||||||
msg_prompt = AgentPrompt()
|
msg_prompt = AgentPrompt()
|
||||||
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
||||||
@@ -447,6 +455,19 @@ class AIAgent:
|
|||||||
msg_prompt.messages = [{"role":"user","content":msg.body}]
|
msg_prompt.messages = [{"role":"user","content":msg.body}]
|
||||||
session_topic = msg.get_sender() + "#" + msg.topic
|
session_topic = msg.get_sender() + "#" + msg.topic
|
||||||
chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db)
|
chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db)
|
||||||
|
if self.enable_thread:
|
||||||
|
need_create_thread = False
|
||||||
|
if chatsession.openai_thread_id is not None:
|
||||||
|
if len(chatsession.openai_thread_id) < 1:
|
||||||
|
need_create_thread = True
|
||||||
|
else:
|
||||||
|
need_create_thread = True
|
||||||
|
|
||||||
|
if need_create_thread:
|
||||||
|
openai_thread_id = await self._create_openai_thread()
|
||||||
|
if openai_thread_id is not None:
|
||||||
|
chatsession.update_openai_thread_id(openai_thread_id)
|
||||||
|
|
||||||
|
|
||||||
workspace = self.get_workspace_by_msg(msg)
|
workspace = self.get_workspace_by_msg(msg)
|
||||||
|
|
||||||
@@ -544,6 +565,7 @@ class AIAgent:
|
|||||||
|
|
||||||
|
|
||||||
async def _get_history_prompt_for_think(self,chatsession:AIChatSession,summary:str,system_token_len:int,pos:int)->(AgentPrompt,int):
|
async def _get_history_prompt_for_think(self,chatsession:AIChatSession,summary:str,system_token_len:int,pos:int)->(AgentPrompt,int):
|
||||||
|
|
||||||
history_len = (self.max_token_size * 0.7) - system_token_len
|
history_len = (self.max_token_size * 0.7) - system_token_len
|
||||||
|
|
||||||
messages = chatsession.read_history(self.history_len,pos,"natural") # read
|
messages = chatsession.read_history(self.history_len,pos,"natural") # read
|
||||||
@@ -663,43 +685,65 @@ class AIAgent:
|
|||||||
workspace : WorkspaceEnvironment = self.get_workspace_by_msg(None)
|
workspace : WorkspaceEnvironment = self.get_workspace_by_msg(None)
|
||||||
logger.info(f"agent {self.agent_id} do my work start!")
|
logger.info(f"agent {self.agent_id} do my work start!")
|
||||||
|
|
||||||
# review todo能更整体的思考一次todo的优先级
|
# review todolist
|
||||||
if await self.need_review_todos():
|
#if await self.need_review_todolist():
|
||||||
await self._llm_review_todos(workspace)
|
# await self._llm_review_todolist(workspace)
|
||||||
|
|
||||||
todo_list = await workspace.get_todo_list(self.agent_id)
|
todo_list = await workspace.get_todo_list(self.agent_id)
|
||||||
|
check_count = 0
|
||||||
|
do_count = 0
|
||||||
|
|
||||||
for todo in todo_list:
|
for todo in todo_list:
|
||||||
if self.agent_energy <= 0:
|
if self.agent_energy <= 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
if await self.can_do(todo,workspace) is False:
|
if await self.need_review_todo(todo,workspace):
|
||||||
continue
|
review_result = await self._llm_review_todo(todo,workspace)
|
||||||
|
todo.last_review_time = datetime.datetime.now().timestamp()
|
||||||
|
|
||||||
if todo.retry_count < 2:
|
elif await self.can_check(todo,workspace):
|
||||||
need_think_todo_from_goal = False
|
check_result : AgentTodoResult = await self._llm_check_todo(todo,workspace)
|
||||||
await self._llm_do(todo,workspace)
|
todo.last_check_time = datetime.datetime.now().timestamp()
|
||||||
|
|
||||||
|
match check_result.result_code:
|
||||||
|
case AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR:
|
||||||
|
continue
|
||||||
|
case AgentTodoResult.TODO_RESULT_CODE_OK:
|
||||||
|
await workspace.update_todo(todo.todo_id,AgentTodo.TODO_STATE_DONE)
|
||||||
|
case AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR:
|
||||||
|
await workspace.update_todo(todo.todo_id,AgentTodo.TDDO_STATE_CHECKFAILED)
|
||||||
|
|
||||||
|
await workspace.append_worklog(todo,check_result)
|
||||||
self.agent_energy -= 1
|
self.agent_energy -= 1
|
||||||
if todo.state == "done":
|
check_count += 1
|
||||||
await self._llm_check_todo(todo,workspace)
|
elif await self.can_do(todo,workspace):
|
||||||
self.agent_energy -= 1
|
do_result : AgentTodoResult = await self._llm_do(todo,workspace)
|
||||||
|
todo.last_do_time = datetime.datetime.now().timestamp()
|
||||||
|
todo.retry_count += 1
|
||||||
|
|
||||||
logger.info(f"agent {self.agent_id} do my work done!")
|
match do_result.result_code:
|
||||||
|
case AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR:
|
||||||
|
continue
|
||||||
|
case AgentTodoResult.TODO_RESULT_CODE_OK:
|
||||||
|
await workspace.update_todo(todo.todo_id,AgentTodo.TODO_STATE_WAITING_CHECK)
|
||||||
|
case AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR:
|
||||||
|
await workspace.update_todo(todo.todo_id,AgentTodo.TODO_STATE_EXEC_FAILED)
|
||||||
|
|
||||||
def get_review_todo_prompt(self) -> AgentPrompt:
|
await workspace.append_worklog(todo,do_result)
|
||||||
|
self.agent_energy -= 2
|
||||||
|
do_count += 1
|
||||||
|
|
||||||
|
logger.info(f"agent {self.agent_id} ,check:{check_count} todo,do:{do_count} todo.")
|
||||||
|
|
||||||
|
def get_review_todo_prompt(self,todo:AgentTodo) -> AgentPrompt:
|
||||||
return self.review_todo_prompt
|
return self.review_todo_prompt
|
||||||
|
|
||||||
async def need_review_todos(self) -> bool:
|
async def _llm_review_todo(self,todo:AgentTodo,workspace:WorkspaceEnvironment):
|
||||||
if self.get_review_todo_prompt() is None:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
async def _llm_review_todos(self,workspace:WorkspaceEnvironment):
|
|
||||||
prompt = AgentPrompt()
|
prompt = AgentPrompt()
|
||||||
|
|
||||||
prompt.append(workspace.get_prompt())
|
prompt.append(workspace.get_prompt())
|
||||||
prompt.append(workspace.get_role_prompt(self.agent_id))
|
prompt.append(workspace.get_role_prompt(self.agent_id))
|
||||||
prompt.append(self.get_review_todo_prompt())
|
prompt.append(self.get_review_todo_prompt(todo))
|
||||||
|
|
||||||
todo_tree = workspace.get_todo_tree("/")
|
todo_tree = workspace.get_todo_tree("/")
|
||||||
prompt.append(AgentPrompt(todo_tree))
|
prompt.append(AgentPrompt(todo_tree))
|
||||||
@@ -712,49 +756,105 @@ class AIAgent:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def get_do_prompt(self,todo_type:str=None) -> AgentPrompt:
|
def get_do_prompt(self,todo:AgentTodo) -> AgentPrompt:
|
||||||
return self.do_prompt
|
return self.do_prompt
|
||||||
|
|
||||||
def get_prompt_from_todo(self,todo:AgentTodo) -> AgentPrompt:
|
def get_prompt_from_todo(self,todo:AgentTodo) -> AgentPrompt:
|
||||||
json_str = json.dumps(todo.raw_obj)
|
json_str = json.dumps(todo.raw_obj)
|
||||||
return AgentPrompt(json_str)
|
return AgentPrompt(json_str)
|
||||||
|
|
||||||
|
async def need_review_todo(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def can_check(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool:
|
||||||
|
if self.get_check_prompt(todo) is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if todo.can_check() is False:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if todo.checker is not None:
|
||||||
|
if todo.checker != self.agent_id:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if self.can_do_unassigned_task is False:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
todo.checker = self.agent_id
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
async def can_do(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool:
|
async def can_do(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool:
|
||||||
return todo.can_do()
|
if todo.can_do() is False:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if todo.worker is not None:
|
||||||
|
if todo.worker != self.agent_id:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if self.can_do_unassigned_task is False:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
todo.worker = self.agent_id
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
async def _llm_do(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> AgentTodoResult:
|
async def _llm_do(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> AgentTodoResult:
|
||||||
|
result = AgentTodoResult()
|
||||||
prompt : AgentPrompt = AgentPrompt()
|
prompt : AgentPrompt = AgentPrompt()
|
||||||
#prompt.append(self.agent_prompt)
|
#prompt.append(self.agent_prompt)
|
||||||
prompt.append(workspace.get_role_prompt(self.agent_id))
|
prompt.append(workspace.get_role_prompt(self.agent_id))
|
||||||
|
|
||||||
do_prompt = workspace.get_do_prompt()
|
do_prompt = workspace.get_do_prompt(todo)
|
||||||
if do_prompt is None:
|
if do_prompt is None:
|
||||||
do_prompt = self.get_do_prompt()
|
do_prompt = self.get_do_prompt(todo)
|
||||||
|
|
||||||
prompt.append(do_prompt)
|
prompt.append(do_prompt)
|
||||||
|
|
||||||
# 有通用的todo执行方法,也有定制的,针对特定类型TODO更高效的执行方法
|
# There are general methods for executing todos, as well as customized ones that are more efficient for specific types of TODOS.
|
||||||
# 根据经验,Agent可以自主掌握/整理更多类型的TODO的执行方法
|
# Based on experience, an Agent can autonomously master/organize execution methods for a greater variety of TODO types.
|
||||||
|
|
||||||
#prompt.append(do_log_prompt)
|
#prompt.append(work_log_prompt)
|
||||||
prompt.append(self.get_prompt_from_todo(todo))
|
prompt.append(self.get_prompt_from_todo(todo))
|
||||||
|
|
||||||
task_result:ComputeTaskResult = await self._do_llm_complection(prompt)
|
task_result:ComputeTaskResult = await self._do_llm_complection(prompt)
|
||||||
if task_result.error_str is not None:
|
if task_result.error_str is not None:
|
||||||
logger.error(f"_llm_do compute error:{task_result.error_str}")
|
logger.error(f"_llm_do compute error:{task_result.error_str}")
|
||||||
|
result.result_code = AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR
|
||||||
|
result.error_str = task_result.error_str
|
||||||
|
return result
|
||||||
|
|
||||||
llm_result = LLMResult.from_str(task_result.result_str)
|
llm_result = LLMResult.from_str(task_result.result_str)
|
||||||
|
# result_str is the explain of how to do this todo
|
||||||
|
result.result_str = llm_result.resp
|
||||||
|
result.op_list = llm_result.op_list
|
||||||
|
if llm_result.post_msgs is not None:
|
||||||
|
for msg in llm_result.post_msgs:
|
||||||
|
msg.sender = self.agent_id
|
||||||
|
msg.topic = f"{todo.title}##{todo.todo_id}"
|
||||||
|
#msg.prev_msg_id = todo.todo_id
|
||||||
|
chatsession = AIChatSession.get_session(self.agent_id,f"{msg.target}#{msg.topic}",self.chat_db)
|
||||||
|
chatsession.append(msg)
|
||||||
|
resp = await AIBus.get_default_bus().post_message(msg)
|
||||||
|
logging.info(f"agent {self.agent_id} send msg to {msg.target} result:{resp}")
|
||||||
|
|
||||||
await workspace.exec_op_list(llm_result.op_list,self.agent_id)
|
op_errors,have_error = await workspace.exec_op_list(llm_result.op_list,self.agent_id)
|
||||||
await workspace.append_do_result(self.agent_id,llm_result)
|
if have_error:
|
||||||
|
result.result_code = AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR
|
||||||
|
#result.error_str = error_str
|
||||||
|
return result
|
||||||
|
|
||||||
return task_result
|
return result
|
||||||
|
|
||||||
def get_check_prompt(self) -> AgentPrompt:
|
async def append_toddo_result(self,todo,worksapce,llm_result,result_str):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_check_prompt(self,todo:AgentTodo) -> AgentPrompt:
|
||||||
return self.check_prompt
|
return self.check_prompt
|
||||||
|
|
||||||
async def _llm_check_todo(self, todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool:
|
async def _llm_check_todo(self, todo:AgentTodo,workspace:WorkspaceEnvironment) :
|
||||||
if self.get_check_prompt(todo) is None:
|
if self.get_check_prompt(todo) is None:
|
||||||
return True
|
return None
|
||||||
|
|
||||||
prompt : AgentPrompt = AgentPrompt()
|
prompt : AgentPrompt = AgentPrompt()
|
||||||
prompt.append(self.agent_prompt)
|
prompt.append(self.agent_prompt)
|
||||||
@@ -920,7 +1020,7 @@ class AIAgent:
|
|||||||
#3) llm summarize chat history
|
#3) llm summarize chat history
|
||||||
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,None)
|
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,None)
|
||||||
if task_result.result_code != ComputeTaskResultCode.OK:
|
if task_result.result_code != ComputeTaskResultCode.OK:
|
||||||
logger.error(f"llm compute error:{task_result.error_str}")
|
logger.error(f"think_chatsession llm compute error:{task_result.error_str}")
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
new_summary= task_result.result_str
|
new_summary= task_result.result_str
|
||||||
@@ -930,6 +1030,8 @@ class AIAgent:
|
|||||||
|
|
||||||
async def get_prompt_from_session(self,chatsession:AIChatSession,system_token_len,input_token_len) -> AgentPrompt:
|
async def get_prompt_from_session(self,chatsession:AIChatSession,system_token_len,input_token_len) -> AgentPrompt:
|
||||||
# TODO: get prompt from group chat is different from single chat
|
# TODO: get prompt from group chat is different from single chat
|
||||||
|
if self.enable_thread:
|
||||||
|
return None
|
||||||
|
|
||||||
history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len
|
history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len
|
||||||
messages = chatsession.read_history(self.history_len) # read
|
messages = chatsession.read_history(self.history_len) # read
|
||||||
@@ -971,7 +1073,7 @@ class AIAgent:
|
|||||||
#logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ")
|
#logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ")
|
||||||
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
|
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
|
||||||
if task_result.result_code != ComputeTaskResultCode.OK:
|
if task_result.result_code != ComputeTaskResultCode.OK:
|
||||||
logger.error(f"llm compute error:{task_result.error_str}")
|
logger.error(f"_do_llm_complection llm compute error:{task_result.error_str}")
|
||||||
#error_resp = msg.create_error_resp(task_result.error_str)
|
#error_resp = msg.create_error_resp(task_result.error_str)
|
||||||
return task_result
|
return task_result
|
||||||
|
|
||||||
@@ -987,7 +1089,15 @@ class AIAgent:
|
|||||||
return task_result
|
return task_result
|
||||||
|
|
||||||
def need_work(self) -> bool:
|
def need_work(self) -> bool:
|
||||||
return True
|
if self.do_prompt is not None:
|
||||||
|
return True
|
||||||
|
if self.check_prompt is not None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.agent_energy > 2:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def need_self_think(self) -> bool:
|
def need_self_think(self) -> bool:
|
||||||
return False
|
return False
|
||||||
@@ -1014,28 +1124,25 @@ class AIAgent:
|
|||||||
self.agent_energy += (now - self.last_recover_time) / 60
|
self.agent_energy += (now - self.last_recover_time) / 60
|
||||||
self.last_recover_time = now
|
self.last_recover_time = now
|
||||||
|
|
||||||
|
|
||||||
if self.agent_energy <= 1:
|
if self.agent_energy <= 1:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# complete todo
|
# complete & check todo
|
||||||
if self.need_work():
|
if self.need_work():
|
||||||
await self.do_my_work()
|
await self.do_my_work()
|
||||||
|
|
||||||
# review other's todo
|
# review other's todo
|
||||||
# self.review_other_works()
|
# self.review_other_works()
|
||||||
|
|
||||||
# do work summary
|
|
||||||
if self.need_self_think():
|
if self.need_self_think():
|
||||||
await self.do_self_think()
|
await self.do_self_think()
|
||||||
|
|
||||||
#
|
|
||||||
if self.need_self_learn():
|
if self.need_self_learn():
|
||||||
await self.do_self_learn()
|
await self.do_self_learn()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
tb_str = traceback.format_exc()
|
tb_str = traceback.format_exc()
|
||||||
logger.error(f"agent {self.agent_id} on timer error:{tb_str},{e}")
|
logger.error(f"agent {self.agent_id} on timer error:{e},{tb_str}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+113
-24
@@ -258,8 +258,16 @@ class LLMResult:
|
|||||||
r.state = llm_json.get("state")
|
r.state = llm_json.get("state")
|
||||||
r.resp = llm_json.get("resp")
|
r.resp = llm_json.get("resp")
|
||||||
|
|
||||||
r.post_msgs = llm_json.get("post_msgs")
|
post_msgs = llm_json.get("post_msg")
|
||||||
r.send_msgs = llm_json.get("send_msgs")
|
r.post_msgs = []
|
||||||
|
if post_msgs:
|
||||||
|
for msg in post_msgs:
|
||||||
|
new_msg = AgentMsg()
|
||||||
|
target_id = msg.get("target")
|
||||||
|
msg_content = msg.get("content")
|
||||||
|
new_msg.set("",target_id,msg_content)
|
||||||
|
r.post_msgs.append(new_msg)
|
||||||
|
#new_msg.msg_type = AgentMsgType.TYPE_MSG
|
||||||
|
|
||||||
r.calls = llm_json.get("calls")
|
r.calls = llm_json.get("calls")
|
||||||
r.post_calls = llm_json.get("post_calls")
|
r.post_calls = llm_json.get("post_calls")
|
||||||
@@ -352,22 +360,43 @@ class LLMResult:
|
|||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
class AgentGoal:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.description = None
|
|
||||||
|
|
||||||
|
|
||||||
class AgentReport:
|
class AgentReport:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class AgentTodoResult:
|
class AgentTodoResult:
|
||||||
|
TODO_RESULT_CODE_OK = 0,
|
||||||
|
TODO_RESULT_CODE_LLM_ERROR = 1,
|
||||||
|
TODO_RESULT_CODE_EXEC_OP_ERROR = 2
|
||||||
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.result_state = "error"
|
self.result_code = AgentTodoResult.TODO_RESULT_CODE_OK
|
||||||
|
self.result_str = None
|
||||||
|
self.error_str = None
|
||||||
|
self.op_list = None
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
result = {}
|
||||||
|
result["result_code"] = self.result_code
|
||||||
|
result["result_str"] = self.result_str
|
||||||
|
result["error_str"] = self.error_str
|
||||||
|
result["op_list"] = self.op_list
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class AgentTodo:
|
class AgentTodo:
|
||||||
|
TODO_STATE_WAIT_ASSIGN = "wait_assign"
|
||||||
|
TODO_STATE_INIT = "init"
|
||||||
|
|
||||||
|
TODO_STATE_PENDING = "pending"
|
||||||
|
TODO_STATE_WAITING_CHECK = "wait_check"
|
||||||
|
TODO_STATE_EXEC_FAILED = "exec_failed"
|
||||||
|
TDDO_STATE_CHECKFAILED = "check_failed"
|
||||||
|
|
||||||
|
TODO_STATE_CASNCEL = "cancel"
|
||||||
|
TODO_STATE_DONE = "done"
|
||||||
|
TODO_STATE_EXPIRED = "expired"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.todo_id = "todo#" + uuid.uuid4().hex
|
self.todo_id = "todo#" + uuid.uuid4().hex
|
||||||
@@ -376,20 +405,23 @@ class AgentTodo:
|
|||||||
self.todo_path = None # get parent todo,sub todo by path
|
self.todo_path = None # get parent todo,sub todo by path
|
||||||
#self.parent = None
|
#self.parent = None
|
||||||
self.create_time = time.time()
|
self.create_time = time.time()
|
||||||
self.due_date = time.time() + 3600 * 24 * 2
|
|
||||||
self.state = "pending"
|
|
||||||
|
|
||||||
self.depend_todo_ids = []
|
|
||||||
self.sub_todos = {}
|
|
||||||
|
|
||||||
self.need_check = True
|
|
||||||
self.result : ComputeTaskResult = None
|
|
||||||
self.last_check_result = None
|
|
||||||
|
|
||||||
|
self.state = "wait_assign"
|
||||||
self.worker = None
|
self.worker = None
|
||||||
self.checker = None
|
self.checker = None
|
||||||
self.createor = None
|
self.createor = None
|
||||||
|
|
||||||
|
self.need_check = True
|
||||||
|
self.due_date = time.time() + 3600 * 24 * 2
|
||||||
|
self.last_do_time = None
|
||||||
|
self.last_check_time = None
|
||||||
|
self.last_review_time = None
|
||||||
|
|
||||||
|
self.depend_todo_ids = []
|
||||||
|
self.sub_todos = {}
|
||||||
|
|
||||||
|
self.result : AgentTodoResult = None
|
||||||
|
self.last_check_result = None
|
||||||
self.retry_count = 0
|
self.retry_count = 0
|
||||||
self.raw_obj = None
|
self.raw_obj = None
|
||||||
|
|
||||||
@@ -400,14 +432,26 @@ class AgentTodo:
|
|||||||
todo.todo_id = json_obj.get("id")
|
todo.todo_id = json_obj.get("id")
|
||||||
|
|
||||||
todo.title = json_obj.get("title")
|
todo.title = json_obj.get("title")
|
||||||
|
todo.state = json_obj.get("state")
|
||||||
create_time = json_obj.get("create_time")
|
create_time = json_obj.get("create_time")
|
||||||
if create_time:
|
if create_time:
|
||||||
todo.create_time = datetime.fromisoformat(create_time).timestamp()
|
todo.create_time = datetime.fromisoformat(create_time).timestamp()
|
||||||
|
|
||||||
todo.detail = json_obj.get("detail")
|
todo.detail = json_obj.get("detail")
|
||||||
due_date = json_obj.get("due_date")
|
due_date = json_obj.get("due_date")
|
||||||
if due_date:
|
if due_date:
|
||||||
todo.due_date = datetime.fromisoformat(due_date).timestamp()
|
todo.due_date = datetime.fromisoformat(due_date).timestamp()
|
||||||
todo.todo_path = json_obj.get("todo_path")
|
|
||||||
|
last_do_time = json_obj.get("last_do_time")
|
||||||
|
if last_do_time:
|
||||||
|
todo.last_do_time = datetime.fromisoformat(last_do_time).timestamp()
|
||||||
|
last_check_time = json_obj.get("last_check_time")
|
||||||
|
if last_check_time:
|
||||||
|
todo.last_check_time = datetime.fromisoformat(last_check_time).timestamp()
|
||||||
|
last_review_time = json_obj.get("last_review_time")
|
||||||
|
if last_review_time:
|
||||||
|
todo.last_review_time = datetime.fromisoformat(last_review_time).timestamp()
|
||||||
|
|
||||||
todo.depend_todo_ids = json_obj.get("depend_todo_ids")
|
todo.depend_todo_ids = json_obj.get("depend_todo_ids")
|
||||||
todo.need_check = json_obj.get("need_check")
|
todo.need_check = json_obj.get("need_check")
|
||||||
#todo.result = json_obj.get("result")
|
#todo.result = json_obj.get("result")
|
||||||
@@ -423,14 +467,21 @@ class AgentTodo:
|
|||||||
return todo
|
return todo
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
result = {}
|
if self.raw_obj:
|
||||||
|
result = self.raw_obj
|
||||||
|
else:
|
||||||
|
result = {}
|
||||||
|
|
||||||
result["id"] = self.todo_id
|
result["id"] = self.todo_id
|
||||||
result["todo_path"] = self.todo_path
|
|
||||||
#result["parent_id"] = self.parent_id
|
#result["parent_id"] = self.parent_id
|
||||||
result["title"] = self.title
|
result["title"] = self.title
|
||||||
|
result["state"] = self.state
|
||||||
result["create_time"] = datetime.fromtimestamp(self.create_time).isoformat()
|
result["create_time"] = datetime.fromtimestamp(self.create_time).isoformat()
|
||||||
result["detail"] = self.detail
|
result["detail"] = self.detail
|
||||||
result["due_date"] = datetime.fromtimestamp(self.due_date).isoformat()
|
result["due_date"] = datetime.fromtimestamp(self.due_date).isoformat()
|
||||||
|
result["last_do_time"] = datetime.fromtimestamp(self.last_do_time).isoformat() if self.last_do_time else None
|
||||||
|
result["last_check_time"] = datetime.fromtimestamp(self.last_check_time).isoformat() if self.last_check_time else None
|
||||||
|
result["last_review_time"] = datetime.fromtimestamp(self.last_review_time).isoformat() if self.last_review_time else None
|
||||||
result["depend_todo_ids"] = self.depend_todo_ids
|
result["depend_todo_ids"] = self.depend_todo_ids
|
||||||
result["need_check"] = self.need_check
|
result["need_check"] = self.need_check
|
||||||
result["worker"] = self.worker
|
result["worker"] = self.worker
|
||||||
@@ -439,19 +490,57 @@ class AgentTodo:
|
|||||||
result["retry_count"] = self.retry_count
|
result["retry_count"] = self.retry_count
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def can_check(self)->bool:
|
||||||
|
if self.state != AgentTodo.TODO_STATE_WAITING_CHECK:
|
||||||
|
return False
|
||||||
|
|
||||||
|
now = datetime.now().timestamp()
|
||||||
|
if self.last_check_time:
|
||||||
|
time_diff = now - self.last_check_time
|
||||||
|
if time_diff < 60*15:
|
||||||
|
logger.info(f"todo {self.title} is already checked, ignore")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def can_do(self) -> bool:
|
def can_do(self) -> bool:
|
||||||
for sub_todo in self.sub_todos.values():
|
match self.state:
|
||||||
if sub_todo.state:
|
case AgentTodo.TODO_STATE_DONE:
|
||||||
if sub_todo.state != "done":
|
logger.info(f"todo {self.title} is done, ignore")
|
||||||
|
return False
|
||||||
|
case AgentTodo.TODO_STATE_CASNCEL:
|
||||||
|
logger.info(f"todo {self.title} is cancel, ignore")
|
||||||
|
return False
|
||||||
|
case AgentTodo.TODO_STATE_EXPIRED:
|
||||||
|
logger.info(f"todo {self.title} is expired, ignore")
|
||||||
|
return False
|
||||||
|
case AgentTodo.TODO_STATE_EXEC_FAILED:
|
||||||
|
if self.retry_count > 3:
|
||||||
|
logger.info(f"todo {self.title} retry count ({self.retry_count}) is too many, ignore")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
now = datetime.now().timestamp()
|
now = datetime.now().timestamp()
|
||||||
time_diff = now - self.due_date
|
time_diff = self.due_date - now
|
||||||
|
if time_diff < 0:
|
||||||
|
logger.info(f"todo {self.title} is expired, ignore")
|
||||||
|
self.state = AgentTodo.TODO_STATE_EXPIRED
|
||||||
|
return False
|
||||||
|
|
||||||
if time_diff > 7*24*3600:
|
if time_diff > 7*24*3600:
|
||||||
|
logger.info(f"todo {self.title} is far before due date, ignore")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if self.last_do_time:
|
||||||
|
time_diff = now - self.last_do_time
|
||||||
|
if time_diff < 60*15:
|
||||||
|
logger.info(f"todo {self.title} is already do ignore")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"todo {self.title} can do.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class AgentWorkLog:
|
class AgentWorkLog:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ class AIBus:
|
|||||||
# means sub
|
# means sub
|
||||||
def register_message_handler(self,handler_name:str,handler:Any) -> Queue:
|
def register_message_handler(self,handler_name:str,handler:Any) -> Queue:
|
||||||
handler_node = AIBusHandler(handler,self)
|
handler_node = AIBusHandler(handler,self)
|
||||||
|
if self.handlers.get(handler_name) is not None:
|
||||||
|
logger.warn(f"handler {handler_name} already register on AI_BUS!")
|
||||||
|
|
||||||
self.handlers[handler_name] = handler_node
|
self.handlers[handler_name] = handler_node
|
||||||
return handler_node.queue
|
return handler_node.queue
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ class ChatSessionDB:
|
|||||||
SessionTopic TEXT,
|
SessionTopic TEXT,
|
||||||
StartTime TEXT,
|
StartTime TEXT,
|
||||||
SummarizePos INTEGER,
|
SummarizePos INTEGER,
|
||||||
Summary TEXT
|
Summary TEXT,
|
||||||
|
ThreadID TEXT
|
||||||
);
|
);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
@@ -89,14 +90,14 @@ class ChatSessionDB:
|
|||||||
except Error as e:
|
except Error as e:
|
||||||
logging.error("Error occurred while creating tables: %s", e)
|
logging.error("Error occurred while creating tables: %s", e)
|
||||||
|
|
||||||
def insert_chatsession(self, session_id, session_owner,session_topic, start_time):
|
def insert_chatsession(self, session_id, session_owner,session_topic, start_time,thread_id = ""):
|
||||||
""" insert a new session into the ChatSessions table """
|
""" insert a new session into the ChatSessions table """
|
||||||
try:
|
try:
|
||||||
conn = self._get_conn()
|
conn = self._get_conn()
|
||||||
conn.execute("""
|
conn.execute("""
|
||||||
INSERT INTO ChatSessions (SessionID, SessionOwner,SessionTopic, StartTime,SummarizePos,Summary)
|
INSERT INTO ChatSessions (SessionID, SessionOwner,SessionTopic, StartTime,SummarizePos,Summary,ThreadID)
|
||||||
VALUES (?,?, ?, ?,0,"")
|
VALUES (?,?, ?, ?,0,"",?)
|
||||||
""", (session_id, session_owner,session_topic, start_time))
|
""", (session_id, session_owner,session_topic, start_time,thread_id))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return 0 # return 0 if successful
|
return 0 # return 0 if successful
|
||||||
except Error as e:
|
except Error as e:
|
||||||
@@ -254,6 +255,21 @@ class ChatSessionDB:
|
|||||||
logging.error("Error occurred while updating session summary: %s", e)
|
logging.error("Error occurred while updating session summary: %s", e)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
def update_session_thread_id(self, session_id, thread_id):
|
||||||
|
""" update the threadid of a session """
|
||||||
|
try:
|
||||||
|
conn = self._get_conn()
|
||||||
|
conn.execute("""
|
||||||
|
UPDATE ChatSessions
|
||||||
|
SET ThreadID = ?
|
||||||
|
WHERE SessionID = ?
|
||||||
|
""", (thread_id, session_id))
|
||||||
|
conn.commit()
|
||||||
|
return 0 # return 0 if successful
|
||||||
|
except Error as e:
|
||||||
|
logging.error("Error occurred while updating session threadid: %s", e)
|
||||||
|
return -1
|
||||||
|
|
||||||
# chat session store the chat history between owner and agent
|
# chat session store the chat history between owner and agent
|
||||||
# chat session might be large, so can read / write at stream mode.
|
# chat session might be large, so can read / write at stream mode.
|
||||||
class AIChatSession:
|
class AIChatSession:
|
||||||
@@ -286,6 +302,7 @@ class AIChatSession:
|
|||||||
result.topic = session_topic
|
result.topic = session_topic
|
||||||
result.summarize_pos = session[4]
|
result.summarize_pos = session[4]
|
||||||
result.summary = session[5]
|
result.summary = session[5]
|
||||||
|
result.openai_thread_id = session[6]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -305,6 +322,7 @@ class AIChatSession:
|
|||||||
result.topic = session[2]
|
result.topic = session[2]
|
||||||
result.summarize_pos = session[4]
|
result.summarize_pos = session[4]
|
||||||
result.summary = session[5]
|
result.summary = session[5]
|
||||||
|
result.openai_thread_id = session[6]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -331,6 +349,7 @@ class AIChatSession:
|
|||||||
self.start_time : str = None
|
self.start_time : str = None
|
||||||
self.summarize_pos : int = 0
|
self.summarize_pos : int = 0
|
||||||
self.summary = None
|
self.summary = None
|
||||||
|
self.openai_thread_id = None
|
||||||
|
|
||||||
def get_owner_id(self) -> str:
|
def get_owner_id(self) -> str:
|
||||||
return self.owner_id
|
return self.owner_id
|
||||||
@@ -376,6 +395,10 @@ class AIChatSession:
|
|||||||
self.summarize_pos = progress
|
self.summarize_pos = progress
|
||||||
self.summary = new_summary
|
self.summary = new_summary
|
||||||
|
|
||||||
|
def update_openai_thread_id(self,thread_id:str) -> None:
|
||||||
|
self.db.update_session_thread_id(self.session_id,thread_id)
|
||||||
|
self.openai_thread_id = thread_id
|
||||||
|
|
||||||
#def attach_event_handler(self,handler) -> None:
|
#def attach_event_handler(self,handler) -> None:
|
||||||
# """chat session changed event handler"""
|
# """chat session changed event handler"""
|
||||||
# pass
|
# pass
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
from typing import List
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from .agent_base import AgentMsg
|
||||||
|
from .tunnel import AgentTunnel
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
class Contact:
|
||||||
|
def __init__(self, name, phone=None, email=None, telegram=None,added_by=None, tags=[], notes=""):
|
||||||
|
self.name = name
|
||||||
|
self.phone = phone
|
||||||
|
self.email = email
|
||||||
|
self.telegram = telegram
|
||||||
|
self.added_by = added_by
|
||||||
|
self.tags = tags
|
||||||
|
self.notes = notes
|
||||||
|
self.is_family_member = False
|
||||||
|
self.active_tunnels = {}
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"name": self.name,
|
||||||
|
"phone": self.phone,
|
||||||
|
"email": self.email,
|
||||||
|
"telegram" : self.telegram,
|
||||||
|
|
||||||
|
"added_by": self.added_by,
|
||||||
|
"tags": self.tags,
|
||||||
|
"notes": self.notes,
|
||||||
|
"now" : datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
}
|
||||||
|
|
||||||
|
async def _process_msg(self,msg:AgentMsg):
|
||||||
|
tunnel : AgentTunnel = self.get_active_tunnel(msg.sender)
|
||||||
|
if tunnel is not None:
|
||||||
|
await tunnel.post_message(msg)
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
tunnel = await self.create_default_tunnel(msg.sender)
|
||||||
|
if tunnel is not None:
|
||||||
|
self.active_tunnels[msg.sender] = tunnel
|
||||||
|
await tunnel.post_message(msg)
|
||||||
|
return None
|
||||||
|
|
||||||
|
logger.warn(f"contact {self.name} cann't get tunnel,post message failed!")
|
||||||
|
|
||||||
|
def get_active_tunnel(self,agent_id) -> AgentTunnel:
|
||||||
|
tunnel = self.active_tunnels.get(agent_id)
|
||||||
|
return tunnel
|
||||||
|
|
||||||
|
def set_active_tunnel(self,agent_id,tunnel:AgentTunnel):
|
||||||
|
self.active_tunnels[agent_id] = tunnel
|
||||||
|
|
||||||
|
async def create_default_tunnel(self,agent_id:str) -> AgentTunnel:
|
||||||
|
from .email_tunnel import EmailTunnel
|
||||||
|
|
||||||
|
result_tunnels = AgentTunnel.get_tunnel_by_agentid(agent_id)
|
||||||
|
for tunnel in result_tunnels:
|
||||||
|
if isinstance(tunnel,EmailTunnel):
|
||||||
|
return tunnel
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data):
|
||||||
|
return Contact(data.get("name"), data.get("phone"), data.get("email"), data.get("telegram"),data.get("added_by"), data.get("tags"), data.get("notes"))
|
||||||
|
|
||||||
|
class FamilyMember(Contact):
|
||||||
|
def __init__(self, name, relationship,phone=None, email=None,telegram=None):
|
||||||
|
super().__init__(name, phone, email, telegram)
|
||||||
|
self.name = name
|
||||||
|
self.relationship = relationship
|
||||||
|
self.is_family_member = True
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
result = super().to_dict()
|
||||||
|
result["relationship"] = self.relationship
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data):
|
||||||
|
return FamilyMember(data.get("name"),data.get("relationship"),data.get("phone"), data.get("email"),data.get("telegram"))
|
||||||
@@ -2,49 +2,13 @@ from typing import List
|
|||||||
import toml
|
import toml
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
class Contact:
|
from .agent_base import AgentMsg
|
||||||
def __init__(self, name, phone=None, email=None, telegram=None,added_by=None, tags=[], notes=""):
|
from .tunnel import AgentTunnel
|
||||||
self.name = name
|
from .contact import Contact,FamilyMember
|
||||||
self.phone = phone
|
import logging
|
||||||
self.email = email
|
|
||||||
self.telegram = telegram
|
|
||||||
self.added_by = added_by
|
|
||||||
self.tags = tags
|
|
||||||
self.notes = notes
|
|
||||||
self.is_family_member = False
|
|
||||||
|
|
||||||
def to_dict(self):
|
logger = logging.getLogger(__name__)
|
||||||
return {
|
|
||||||
"name": self.name,
|
|
||||||
"phone": self.phone,
|
|
||||||
"email": self.email,
|
|
||||||
"telegram" : self.telegram,
|
|
||||||
|
|
||||||
"added_by": self.added_by,
|
|
||||||
"tags": self.tags,
|
|
||||||
"notes": self.notes,
|
|
||||||
"now" : datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, data):
|
|
||||||
return Contact(data.get("name"), data.get("phone"), data.get("email"), data.get("telegram"),data.get("added_by"), data.get("tags"), data.get("notes"))
|
|
||||||
|
|
||||||
class FamilyMember(Contact):
|
|
||||||
def __init__(self, name, relationship,phone=None, email=None,telegram=None):
|
|
||||||
super().__init__(name, phone, email, telegram)
|
|
||||||
self.name = name
|
|
||||||
self.relationship = relationship
|
|
||||||
self.is_family_member = True
|
|
||||||
|
|
||||||
def to_dict(self):
|
|
||||||
result = super().to_dict()
|
|
||||||
result["relationship"] = self.relationship
|
|
||||||
return result
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, data):
|
|
||||||
return FamilyMember(data.get("name"),data.get("relationship"),data.get("phone"), data.get("email"),data.get("telegram"))
|
|
||||||
|
|
||||||
class ContactManager:
|
class ContactManager:
|
||||||
_instance = None
|
_instance = None
|
||||||
@@ -154,6 +118,9 @@ class ContactManager:
|
|||||||
def list_family_members(self):
|
def list_family_members(self):
|
||||||
return self.family_members
|
return self.family_members
|
||||||
|
|
||||||
|
#def register_to_ai_bus(self, ai_bus:AIBus):
|
||||||
|
# ai_bus.register_message_handler("contact_manager", self.process_msg)
|
||||||
|
|
||||||
|
|
||||||
#async def process_msg(self,msg:AgentMsg):
|
#async def process_msg(self,msg:AgentMsg):
|
||||||
# # forword message to contact
|
# # forword message to contact
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import time
|
|||||||
import datetime
|
import datetime
|
||||||
from .tunnel import AgentTunnel
|
from .tunnel import AgentTunnel
|
||||||
from .agent_base import AgentMsg
|
from .agent_base import AgentMsg
|
||||||
|
from .contact_manager import ContactManager
|
||||||
|
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ class EmailTunnel(AgentTunnel):
|
|||||||
self.target_id = config["target"]
|
self.target_id = config["target"]
|
||||||
self.tunnel_id = config["tunnel_id"]
|
self.tunnel_id = config["tunnel_id"]
|
||||||
|
|
||||||
self.type = "TelegramTunnel"
|
self.type = "EmailTunnel"
|
||||||
self.email = config["email"]
|
self.email = config["email"]
|
||||||
self.imap_server = config["imap"]
|
self.imap_server = config["imap"]
|
||||||
s = self.imap_server.split(":")
|
s = self.imap_server.split(":")
|
||||||
@@ -46,8 +47,10 @@ class EmailTunnel(AgentTunnel):
|
|||||||
|
|
||||||
self.login_user = config["user"]
|
self.login_user = config["user"]
|
||||||
self.login_password = config["password"]
|
self.login_password = config["password"]
|
||||||
self.folder = config["folder"]
|
if config.get("folder") is not None:
|
||||||
self.check_interval = config["interval"]
|
self.folder = config["folder"]
|
||||||
|
if config.get("interval") is not None:
|
||||||
|
self.check_interval = config["interval"]
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -55,6 +58,8 @@ class EmailTunnel(AgentTunnel):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.is_start = False
|
self.is_start = False
|
||||||
self.read_email = {}
|
self.read_email = {}
|
||||||
|
self.folder = "INBOX"
|
||||||
|
self.check_interval = 60
|
||||||
|
|
||||||
async def on_new_email(self,mail:mailparser.MailParser) -> None:
|
async def on_new_email(self,mail:mailparser.MailParser) -> None:
|
||||||
remote_email_addr = mail.from_[0][1]
|
remote_email_addr = mail.from_[0][1]
|
||||||
@@ -86,6 +91,31 @@ class EmailTunnel(AgentTunnel):
|
|||||||
password=self.login_password,
|
password=self.login_password,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def post_message(self, msg: AgentMsg) -> None:
|
||||||
|
cm = ContactManager.get_instance()
|
||||||
|
contact = cm.find_contact_by_name(msg.target)
|
||||||
|
if contact is None:
|
||||||
|
logger.error(f"can't find contact {msg.target} , post message through email_tunnel failed!")
|
||||||
|
return
|
||||||
|
|
||||||
|
target_email = contact.email
|
||||||
|
if target_email is None:
|
||||||
|
logger.error(f"contact {msg.target} has no email, post message through email_tunnel failed!")
|
||||||
|
return
|
||||||
|
|
||||||
|
email_msg = EmailMessage()
|
||||||
|
email_msg['Subject'] = f"{msg.topic},From AIAgent {msg.sender}"
|
||||||
|
email_msg['From'] = self.email
|
||||||
|
email_msg['To'] = target_email
|
||||||
|
email_msg.set_content(msg)
|
||||||
|
|
||||||
|
await aiosmtplib.send(
|
||||||
|
email_msg,
|
||||||
|
hostname = self.smtp_server,
|
||||||
|
port=self.smtp_port,
|
||||||
|
username=self.login_user,
|
||||||
|
password=self.login_password,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def conver_mail_to_agent_msg(self,mail:mailparser.MailParser) -> AgentMsg:
|
def conver_mail_to_agent_msg(self,mail:mailparser.MailParser) -> AgentMsg:
|
||||||
@@ -142,3 +172,4 @@ class EmailTunnel(AgentTunnel):
|
|||||||
async def _process_message(self, msg: AgentMsg) -> None:
|
async def _process_message(self, msg: AgentMsg) -> None:
|
||||||
logger.warn(f"process message {msg.msg_id} from {msg.sender} to {msg.target}")
|
logger.warn(f"process message {msg.msg_id} from {msg.sender} to {msg.target}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ from .agent_base import AgentMsg,AgentMsgType
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class TelegramTunnel(AgentTunnel):
|
class TelegramTunnel(AgentTunnel):
|
||||||
|
all_bots = {}
|
||||||
|
default_chatid = {}
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_to_loader(cls):
|
def register_to_loader(cls):
|
||||||
async def load_tg_tunnel(config:dict) -> AgentTunnel:
|
async def load_tg_tunnel(config:dict) -> AgentTunnel:
|
||||||
@@ -55,6 +56,7 @@ class TelegramTunnel(AgentTunnel):
|
|||||||
self.update_queue = None
|
self.update_queue = None
|
||||||
self.allow_group = "contact"
|
self.allow_group = "contact"
|
||||||
self.in_process_tg_msg = {}
|
self.in_process_tg_msg = {}
|
||||||
|
self.chatid_record = {}
|
||||||
|
|
||||||
async def _do_process_raw_message(self,bot: Bot, update_id: int) -> int:
|
async def _do_process_raw_message(self,bot: Bot, update_id: int) -> int:
|
||||||
# Request updates after the last update_id
|
# Request updates after the last update_id
|
||||||
@@ -81,6 +83,7 @@ class TelegramTunnel(AgentTunnel):
|
|||||||
self.update_queue = asyncio.Queue()
|
self.update_queue = asyncio.Queue()
|
||||||
self.bot_updater = Updater(self.bot,update_queue=self.update_queue)
|
self.bot_updater = Updater(self.bot,update_queue=self.update_queue)
|
||||||
|
|
||||||
|
TelegramTunnel.all_bots[self.target_id] = self.bot
|
||||||
|
|
||||||
async def _run_app():
|
async def _run_app():
|
||||||
try:
|
try:
|
||||||
@@ -110,8 +113,41 @@ class TelegramTunnel(AgentTunnel):
|
|||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def _process_message(self, msg: AgentMsg) -> None:
|
async def _process_message(self, msg: AgentMsg) -> bool:
|
||||||
logger.warn(f"process message {msg.msg_id} from {msg.sender} to {msg.target}")
|
logger.warn(f"tg_tunnel process message {msg.msg_id} from agent {msg.sender} to human {msg.target}")
|
||||||
|
|
||||||
|
# async def _process_message(self, msg: AgentMsg) -> bool:
|
||||||
|
# logger.info(f"tg_tunnel process message {msg.msg_id} from agent {msg.sender} to human {msg.target}")
|
||||||
|
# cm = ContactManager.get_instance()
|
||||||
|
# contact = cm.find_contact_by_name(msg.target)
|
||||||
|
# bot = TelegramTunnel.all_bots.get(msg.sender)
|
||||||
|
# chatid_index = f"{self.target_id}#{msg.target}"
|
||||||
|
# chatid = TelegramTunnel.default_chatid.get(chatid_index)
|
||||||
|
# if chatid is None:
|
||||||
|
# logger.warning(f"tg_tunnel process message {msg.msg_id} from agent {msg.sender} to human {msg.target} failed! chatid not found!")
|
||||||
|
# return None
|
||||||
|
|
||||||
|
# if bot is None:
|
||||||
|
# logger.warning(f"tg_tunnel process message {msg.msg_id} from agent {msg.sender} to human {msg.target} failed! bot not found!")
|
||||||
|
# return None
|
||||||
|
|
||||||
|
# if contact:
|
||||||
|
# if contact.telegram:
|
||||||
|
# await bot.send_message(chat_id=chatid,text=msg.body)
|
||||||
|
# logging.info(f"tg_tunnel send message {msg.msg_id} from agent {msg.sender} to human {msg.target} @ chatid:{chatid}success!")
|
||||||
|
# return None
|
||||||
|
|
||||||
|
# logger.warning(f"tg_tunnel process message {msg.msg_id} from agent {msg.sender} to human {msg.target} failed! contact not found!")
|
||||||
|
# return None
|
||||||
|
|
||||||
|
async def post_message(self, msg: AgentMsg) -> None:
|
||||||
|
chatid = self.chatid_record.get(msg.target)
|
||||||
|
if chatid:
|
||||||
|
# TODO: support image and audio
|
||||||
|
await self.bot.send_message(chat_id=chatid,text=msg.body)
|
||||||
|
logging.info(f"tg_tunnel send message {msg.msg_id} from agent {msg.sender} to human {msg.target} @ chatid:{chatid}success!")
|
||||||
|
else:
|
||||||
|
logger.warning(f"tg_tunnel process message {msg.msg_id} from agent {msg.sender} to human {msg.target} failed! chatid not found!")
|
||||||
|
|
||||||
|
|
||||||
async def conver_tg_msg_to_agent_msg(self,message:Message) -> AgentMsg:
|
async def conver_tg_msg_to_agent_msg(self,message:Message) -> AgentMsg:
|
||||||
@@ -189,6 +225,8 @@ class TelegramTunnel(AgentTunnel):
|
|||||||
|
|
||||||
if contact is not None:
|
if contact is not None:
|
||||||
reomte_user_name = contact.name
|
reomte_user_name = contact.name
|
||||||
|
|
||||||
|
#TelegramTunnel.default_chatid[f"{self.target_id}#{reomte_user_name}"] = update.effective_chat.id
|
||||||
if not contact.is_family_member:
|
if not contact.is_family_member:
|
||||||
if self.allow_group != "contact" and self.allow_group !="guest":
|
if self.allow_group != "contact" and self.allow_group !="guest":
|
||||||
await update.message.reply_text(f"You're not supposed to talk to me! Please contact my father~")
|
await update.message.reply_text(f"You're not supposed to talk to me! Please contact my father~")
|
||||||
@@ -210,6 +248,10 @@ class TelegramTunnel(AgentTunnel):
|
|||||||
cm.add_contact(contact.name, contact)
|
cm.add_contact(contact.name, contact)
|
||||||
reomte_user_name = contact.name
|
reomte_user_name = contact.name
|
||||||
|
|
||||||
|
if contact is not None:
|
||||||
|
contact.set_active_tunnel(self.target_id,self)
|
||||||
|
self.chatid_record[reomte_user_name] = update.effective_chat.id
|
||||||
|
self.ai_bus.register_message_handler(reomte_user_name,contact._process_msg)
|
||||||
|
|
||||||
agent_msg.sender = reomte_user_name
|
agent_msg.sender = reomte_user_name
|
||||||
logger.info(f"process message {agent_msg.msg_id} from {agent_msg.sender} to {agent_msg.target}")
|
logger.info(f"process message {agent_msg.msg_id} from {agent_msg.sender} to {agent_msg.target}")
|
||||||
@@ -217,7 +259,7 @@ class TelegramTunnel(AgentTunnel):
|
|||||||
self.ai_bus.register_message_handler(agent_msg.target, self._process_message)
|
self.ai_bus.register_message_handler(agent_msg.target, self._process_message)
|
||||||
resp_msg = await self.ai_bus.send_message(agent_msg,self.target_id,agent_msg.target)
|
resp_msg = await self.ai_bus.send_message(agent_msg,self.target_id,agent_msg.target)
|
||||||
else:
|
else:
|
||||||
self.ai_bus.register_message_handler(reomte_user_name, self._process_message)
|
#self.ai_bus.register_message_handler(reomte_user_name, self._process_message)
|
||||||
resp_msg = await self.ai_bus.send_message(agent_msg)
|
resp_msg = await self.ai_bus.send_message(agent_msg)
|
||||||
#await bot.send_chat_action(chat_id=update.effective_chat.id, action="typing")
|
#await bot.send_chat_action(chat_id=update.effective_chat.id, action="typing")
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,14 @@ class AgentTunnel(ABC):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def get_tunnel_by_agentid(cls,agent_id:str):
|
||||||
|
result = []
|
||||||
|
for tunnel in cls._all_tunnels.values():
|
||||||
|
if tunnel.target_id == agent_id:
|
||||||
|
result.append(tunnel)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -75,6 +83,9 @@ class AgentTunnel(ABC):
|
|||||||
self.ai_bus = ai_bus
|
self.ai_bus = ai_bus
|
||||||
self.is_connected = True
|
self.is_connected = True
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def post_message(self, msg: AgentMsg) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from typing import Any,List
|
|||||||
import os
|
import os
|
||||||
import chardet
|
import chardet
|
||||||
|
|
||||||
from .agent_base import AgentMsg,AgentTodo,AgentPrompt
|
from .agent_base import AgentMsg,AgentTodo,AgentPrompt,AgentTodoResult
|
||||||
from .environment import Environment,EnvironmentEvent
|
from .environment import Environment,EnvironmentEvent
|
||||||
from .ai_function import AIFunction,SimpleAIFunction
|
from .ai_function import AIFunction,SimpleAIFunction
|
||||||
from .storage import AIStorage,ResourceLocation
|
from .storage import AIStorage,ResourceLocation
|
||||||
@@ -47,40 +47,53 @@ class WorkspaceEnvironment(Environment):
|
|||||||
def get_knowledge_base(self) -> str:
|
def get_knowledge_base(self) -> str:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_do_prompt(self,todo_type:str=None)->AgentPrompt:
|
def get_do_prompt(self,todo:AgentTodo=None)->AgentPrompt:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def exec_op_list(self,oplist:List,agent_id:str)->None:
|
# 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"
|
||||||
if oplist is None:
|
if oplist is None:
|
||||||
return
|
return None,False
|
||||||
|
|
||||||
|
result_str = []
|
||||||
|
have_error = False
|
||||||
for op in oplist:
|
for op in oplist:
|
||||||
if op["op"] == "create":
|
if op["op"] == "create":
|
||||||
return await self.create(op["path"],op["content"])
|
await self.create(op["path"],op["content"])
|
||||||
elif op["op"] == "write_file":
|
elif op["op"] == "write_file":
|
||||||
is_append = op.get("is_append")
|
is_append = op.get("is_append")
|
||||||
if is_append is None:
|
if is_append is None:
|
||||||
is_append = False
|
is_append = False
|
||||||
return await self.write(op["path"],op["content"],is_append)
|
error_str = await self.write(op["path"],op["content"],is_append)
|
||||||
elif op["op"] == "delete":
|
elif op["op"] == "delete":
|
||||||
return await self.delete(op["path"])
|
error_str = await self.delete(op["path"])
|
||||||
elif op["op"] == "rename":
|
elif op["op"] == "rename":
|
||||||
return await self.rename(op["path"],op["new_name"])
|
error_str = await self.rename(op["path"],op["new_name"])
|
||||||
elif op["op"] == "mkdir":
|
elif op["op"] == "mkdir":
|
||||||
return await self.mkdir(op["path"])
|
error_str = await self.mkdir(op["path"])
|
||||||
elif op["op"] == "create_todo":
|
elif op["op"] == "create_todo":
|
||||||
todoObj = AgentTodo.from_dict(op["todo"])
|
todoObj = AgentTodo.from_dict(op["todo"])
|
||||||
todoObj.worker = agent_id
|
todoObj.worker = agent_id
|
||||||
todoObj.createor = agent_id
|
todoObj.createor = agent_id
|
||||||
parent_id = op.get("parent")
|
parent_id = op.get("parent")
|
||||||
await self.create_todo(parent_id,todoObj)
|
error_str = await self.create_todo(parent_id,todoObj)
|
||||||
elif op["op"] == "update_todo":
|
elif op["op"] == "update_todo":
|
||||||
todo_id = op["id"]
|
todo_id = op["id"]
|
||||||
new_stat = op["state"]
|
new_stat = op["state"]
|
||||||
await self.update_todo(todo_id,new_stat)
|
error_str = await self.update_todo(todo_id,new_stat)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.error(f"execute op list failed: unknown op:{op['op']}")
|
logger.error(f"execute op list failed: unknown op:{op['op']}")
|
||||||
|
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
|
||||||
|
|
||||||
async def list(self,path:str,only_dir:bool=False) -> str:
|
async def list(self,path:str,only_dir:bool=False) -> str:
|
||||||
directory_path = self.root_path + path
|
directory_path = self.root_path + path
|
||||||
@@ -112,13 +125,16 @@ class WorkspaceEnvironment(Environment):
|
|||||||
|
|
||||||
async def write(self,path:str,content:str,is_append:bool=False) -> str:
|
async def write(self,path:str,content:str,is_append:bool=False) -> str:
|
||||||
file_path = self.root_path + path
|
file_path = self.root_path + path
|
||||||
if is_append:
|
try:
|
||||||
async with aiofiles.open(file_path, mode='a', encoding="utf-8") as f:
|
if is_append:
|
||||||
await f.write(content)
|
async with aiofiles.open(file_path, mode='a', encoding="utf-8") as f:
|
||||||
else:
|
await f.write(content)
|
||||||
async with aiofiles.open(file_path, mode='w', encoding="utf-8") as f:
|
else:
|
||||||
await f.write(content)
|
async with aiofiles.open(file_path, mode='w', encoding="utf-8") as f:
|
||||||
return "success"
|
await f.write(content)
|
||||||
|
except Exception as e:
|
||||||
|
return str(e)
|
||||||
|
return None
|
||||||
|
|
||||||
async def create(self,path:str,content:str=None) -> bool:
|
async def create(self,path:str,content:str=None) -> bool:
|
||||||
if content is None:
|
if content is None:
|
||||||
@@ -132,21 +148,29 @@ class WorkspaceEnvironment(Environment):
|
|||||||
await f.write(content)
|
await f.write(content)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def delete(self,path:str) -> bool:
|
async def delete(self,path:str) -> str:
|
||||||
file_path = self.root_path + path
|
try:
|
||||||
os.remove(file_path)
|
file_path = self.root_path + path
|
||||||
return True
|
os.remove(file_path)
|
||||||
|
except Exception as e:
|
||||||
|
return str(e)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
async def mkdir(self,path:str) -> bool:
|
async def mkdir(self,path:str) -> bool:
|
||||||
dir_path = self.root_path + path
|
dir_path = self.root_path + path
|
||||||
os.makedirs(dir_path)
|
os.makedirs(dir_path)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def rename(self,path:str,new_name:str) -> bool:
|
async def rename(self,path:str,new_name:str) -> str:
|
||||||
file_path = self.root_path + path
|
try:
|
||||||
new_path = self.root_path + new_name
|
file_path = self.root_path + path
|
||||||
os.rename(file_path,new_path)
|
new_path = self.root_path + new_name
|
||||||
return True
|
os.rename(file_path,new_path)
|
||||||
|
except Exception as e:
|
||||||
|
return str(e)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
async def get_todo_tree(self,path:str = None,deep:int = 4):
|
async def get_todo_tree(self,path:str = None,deep:int = 4):
|
||||||
if path:
|
if path:
|
||||||
@@ -205,17 +229,17 @@ class WorkspaceEnvironment(Environment):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
todo = await self.get_todo_by_fullpath(entry.path)
|
todo = await self.get_todo_by_fullpath(entry.path)
|
||||||
if todo.worker != agent_id:
|
|
||||||
continue
|
|
||||||
|
|
||||||
todo.rank = int(todo.create_time)>>deep
|
|
||||||
|
|
||||||
if todo:
|
if todo:
|
||||||
|
if todo.worker:
|
||||||
|
if todo.worker != agent_id:
|
||||||
|
continue
|
||||||
|
|
||||||
if parent:
|
if parent:
|
||||||
parent.sub_todos[todo.todo_id] = todo
|
parent.sub_todos[todo.todo_id] = todo
|
||||||
result_list.append(todo)
|
|
||||||
|
|
||||||
await scan_dir(entry.path,deep + 1,todo)
|
result_list.append(todo)
|
||||||
|
todo.rank = int(todo.create_time)>>deep
|
||||||
|
await scan_dir(entry.path,deep + 1,todo)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -226,68 +250,89 @@ class WorkspaceEnvironment(Environment):
|
|||||||
return result_list
|
return result_list
|
||||||
|
|
||||||
async def get_todo_by_fullpath(self,path:str) -> AgentTodo:
|
async def get_todo_by_fullpath(self,path:str) -> AgentTodo:
|
||||||
#logger.info("get_todo_by_fullpath:%s",path)
|
logger.info("get_todo_by_fullpath:%s",path)
|
||||||
|
|
||||||
detail_path = path + "/detail"
|
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)
|
||||||
|
|
||||||
async with aiofiles.open(detail_path, mode='r', encoding="utf-8") as f:
|
return result_todo
|
||||||
content = await f.read(4096)
|
except Exception as e:
|
||||||
logger.debug("get_todo_by_fullpath:%s,content:%s",path,content)
|
logger.error("get_todo_by_path:%s,failed:%s",path,e)
|
||||||
todo_dict = json.loads(content)
|
return None
|
||||||
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
|
|
||||||
|
|
||||||
async def get_todo(self,id:str) -> AgentTodo:
|
async def get_todo(self,id:str) -> AgentTodo:
|
||||||
return self.known_todo.get(id)
|
return self.known_todo.get(id)
|
||||||
|
|
||||||
async def create_todo(self,parent_id:str,todo:AgentTodo) -> None:
|
async def create_todo(self,parent_id:str,todo:AgentTodo) -> str:
|
||||||
if parent_id:
|
try:
|
||||||
if parent_id not in self.known_todo:
|
if parent_id:
|
||||||
logger.error("create_todo failed: parent_id not found!")
|
if parent_id not in self.known_todo:
|
||||||
return False
|
logger.error("create_todo failed: parent_id not found!")
|
||||||
|
return False
|
||||||
|
|
||||||
parent_path = self.known_todo.get(parent_id).todo_path
|
parent_path = self.known_todo.get(parent_id).todo_path
|
||||||
todo_path = f"{parent_path}/{todo.title}"
|
todo_path = f"{parent_path}/{todo.title}"
|
||||||
else:
|
else:
|
||||||
todo_path = todo.title
|
todo_path = todo.title
|
||||||
|
|
||||||
dir_path = f"{self.root_path}/todos/{todo_path}"
|
dir_path = f"{self.root_path}/todos/{todo_path}"
|
||||||
|
|
||||||
os.makedirs(dir_path)
|
os.makedirs(dir_path)
|
||||||
detail_path = f"{dir_path}/detail"
|
detail_path = f"{dir_path}/detail"
|
||||||
if todo.todo_path is None:
|
if todo.todo_path is None:
|
||||||
todo.todo_path = todo_path
|
todo.todo_path = todo_path
|
||||||
logger.info("create_todo %s",detail_path)
|
logger.info("create_todo %s",detail_path)
|
||||||
async with aiofiles.open(detail_path, mode='w', encoding="utf-8") as f:
|
|
||||||
await f.write(json.dumps(todo.to_dict()))
|
|
||||||
self.known_todo[todo.todo_id] = todo
|
|
||||||
return True
|
|
||||||
|
|
||||||
async def update_todo(self,todo_id:str,new_stat:str)->bool:
|
|
||||||
todo : AgentTodo = self.known_todo.get(todo_id)
|
|
||||||
if todo:
|
|
||||||
todo.status = new_stat
|
|
||||||
if todo.raw_obj is None:
|
|
||||||
todo.raw_obj = todo.to_dict()
|
|
||||||
todo.raw_obj["status"] = 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:
|
async with aiofiles.open(detail_path, mode='w', encoding="utf-8") as f:
|
||||||
await f.write(json.dumps(todo.raw_obj))
|
await f.write(json.dumps(todo.to_dict()))
|
||||||
return True
|
self.known_todo[todo.todo_id] = todo
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("create_todo failed:%s",e)
|
||||||
|
return str(e)
|
||||||
|
|
||||||
return False
|
return None
|
||||||
|
|
||||||
async def append_do_result(self,todo:AgentTodo,result):
|
async def update_todo(self,todo_id:str,new_stat:str)->str:
|
||||||
return True
|
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)
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
class CodeInterpreter:
|
class CodeInterpreter:
|
||||||
def __init__(self, language, debug_mode):
|
def __init__(self, language, debug_mode):
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ class AIOS_Shell:
|
|||||||
bus.register_message_handler(target_id,a_workflow._process_msg)
|
bus.register_message_handler(target_id,a_workflow._process_msg)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
a_contact = await ContactManager.get_instance().get_contact(target_id)
|
||||||
|
if a_contact is not None:
|
||||||
|
bus.register_message_handler(target_id,a_contact._process_msg)
|
||||||
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def is_agent(self,target_id:str) -> bool:
|
async def is_agent(self,target_id:str) -> bool:
|
||||||
@@ -184,7 +189,7 @@ class AIOS_Shell:
|
|||||||
await ComputeKernel.get_instance().start()
|
await ComputeKernel.get_instance().start()
|
||||||
|
|
||||||
AIBus().get_default_bus().register_unhandle_message_handler(self._handle_no_target_msg)
|
AIBus().get_default_bus().register_unhandle_message_handler(self._handle_no_target_msg)
|
||||||
AIBus().get_default_bus().register_message_handler(self.username,self._user_process_msg)
|
#AIBus().get_default_bus().register_message_handler(self.username,self._user_process_msg)
|
||||||
|
|
||||||
|
|
||||||
pipelines = KnowledgePipelineManager.initial(os.path.join(AIStorage().get_instance().get_myai_dir(), "knowledge/pipelines"))
|
pipelines = KnowledgePipelineManager.initial(os.path.join(AIStorage().get_instance().get_myai_dir(), "knowledge/pipelines"))
|
||||||
@@ -212,6 +217,7 @@ class AIOS_Shell:
|
|||||||
return "0.5.1"
|
return "0.5.1"
|
||||||
|
|
||||||
async def send_msg(self,msg:str,target_id:str,topic:str,sender:str = None) -> str:
|
async def send_msg(self,msg:str,target_id:str,topic:str,sender:str = None) -> str:
|
||||||
|
#AIBus().get_default_bus().register_message_handler(self.username,self._user_process_msg)
|
||||||
agent_msg = AgentMsg()
|
agent_msg = AgentMsg()
|
||||||
agent_msg.set(sender,target_id,msg)
|
agent_msg.set(sender,target_id,msg)
|
||||||
agent_msg.topic = topic
|
agent_msg.topic = topic
|
||||||
@@ -241,6 +247,11 @@ class AIOS_Shell:
|
|||||||
input_table["allow"] = UserConfigItem("allow group (default is member,you can choose contact or guest)")
|
input_table["allow"] = UserConfigItem("allow group (default is member,you can choose contact or guest)")
|
||||||
case "email":
|
case "email":
|
||||||
tunnel_config["type"] = "EmailTunnel"
|
tunnel_config["type"] = "EmailTunnel"
|
||||||
|
input_table["email"] = UserConfigItem("email address agent will use \n")
|
||||||
|
input_table["imap"] = UserConfigItem("imap server address,like hostname:port")
|
||||||
|
input_table["smtp"] = UserConfigItem("smtp server address,like hostname:port")
|
||||||
|
input_table["user"] = UserConfigItem("mail server login user name")
|
||||||
|
input_table["password"] = UserConfigItem("main server login password")
|
||||||
case _:
|
case _:
|
||||||
error_text = FormattedText([("class:error", f"tunnel type {tunnel_type}not support!")])
|
error_text = FormattedText([("class:error", f"tunnel type {tunnel_type}not support!")])
|
||||||
print_formatted_text(error_text,style=shell_style)
|
print_formatted_text(error_text,style=shell_style)
|
||||||
@@ -406,13 +417,21 @@ class AIOS_Shell:
|
|||||||
match func_name:
|
match func_name:
|
||||||
case 'send':
|
case 'send':
|
||||||
show_text = FormattedText([("class:error", f'send args error,/send Tracy "Hello! It is a good day!" default')])
|
show_text = FormattedText([("class:error", f'send args error,/send Tracy "Hello! It is a good day!" default')])
|
||||||
|
sender = None
|
||||||
if len(args) == 3:
|
if len(args) == 3:
|
||||||
target_id = args[0]
|
target_id = args[0]
|
||||||
msg_content = args[1]
|
msg_content = args[1]
|
||||||
topic = args[2]
|
topic = args[2]
|
||||||
resp = await self.send_msg(msg_content,target_id,topic,self.username)
|
sender = self.username
|
||||||
show_text = FormattedText([("class:title", f"{self.current_topic}@{self.current_target} >>> "),
|
elif len(args) == 4:
|
||||||
("class:content", resp)])
|
target_id = args[0]
|
||||||
|
msg_content = args[1]
|
||||||
|
topic = args[2]
|
||||||
|
sender = args[3]
|
||||||
|
|
||||||
|
resp = await self.send_msg(msg_content,target_id,topic,sender)
|
||||||
|
show_text = FormattedText([("class:title", f"{self.current_topic}@{self.current_target} >>> "),
|
||||||
|
("class:content", resp)])
|
||||||
return show_text
|
return show_text
|
||||||
case 'set_config':
|
case 'set_config':
|
||||||
show_text = FormattedText([("class:error", f"set config args error,/set_config $config_item! ")])
|
show_text = FormattedText([("class:error", f"set config args error,/set_config $config_item! ")])
|
||||||
|
|||||||
Reference in New Issue
Block a user