framework code has been completed basicly. Through the use of aios_shell, we are now able to get agents run able (at openai compute node)

This commit is contained in:
Liu Zhicong
2023-08-27 18:07:33 -07:00
parent 1a6cf1ad7a
commit ccbef2104b
25 changed files with 1011 additions and 198 deletions
+81
View File
@@ -0,0 +1,81 @@
# workflow实例场景
# 能展示sub workflow
# 能展示env的整合
# 能展示filter的使用
# 能展示function的调用
# 能展示基于工作目标/KPI的sub workflow迭代流程
# 多人场景安排
# 例子:举办一个团队活动
# 方案讨论(通过交互引导的方式收集主人的需求)与确定
# 活动前:
# 通讯员,对接管理参加活动的人的情况 (email spider)
# 酒店预订 简单:搜索(酒店评价) 处理异常
# 行程(票务)预订 :搜索,处理异常
# 餐饮预订:给出方案,确定细节,预订
# 活动中:
# 进行统计和分析,调整设备
# 安保,空调,音乐,拍照,录像
# 响应紧急情况
# 活动结束后:
# 整理照片,视频,进行必要的二次创作,发送给相关人员
# 对活动进行总结,提出改件意见(指导下一次活动)
# 1. 人员
# 主管,负责和客户沟通,并对每个环境的结果进行总结
# 嘉宾对接
# 酒店组
# 行程组
# 财务组
# 多媒体组
[filter]
"*" = "manager"
[roles.manager]
name = "经理"
prompt = "你是一个活动策划公司的经理,与客户对接并向团队下达指令。你的团队分为下面几个小组:嘉宾对接组,酒店预定组,行程预订组,财务组,活动摄像组。活动策划分为四个阶段:方案讨论,活动前,活动中,活动后。你会根据客户的需求,对团队进行分工,分别完成各个阶段的工作。你的基本工作模式是:
1. 收到客户的明确的指令后,基于客户的已有信息和客户商量活动方案,和活动策划公司无关的业务你会回答‘与我无关’。当和客户完成活动方案的确认后,你会将拆解后的任务分配给各个小组
2. 根据目前已经确认的活动方案,你要根据时间适时的检查不同小组的工作情况。当收到小组的工作情况反馈后,你会站在全局的角度判断是否需要调整活动方案,如果需要调整,你会和客户商量重新确定方案,然后再将调整后的方案分配给各个小组。
3. 有时工作小组会主动与你沟通,反馈一些问题。你会站在全局的角度给与指导,适当的调整工作小组的工作目标。如果反馈的问题需要你和客户沟通,你会和客户沟通后重新确定方案。再将调整后的方案分配给受到影响各个小组。
4. 当你决定要和工作小组通信时,请使用`send_message({小组名称},{内容}`)的形式。
"
agent="agent.manager"
[sub_workflows]
[sub_workflows."嘉宾对接组"]
# 展现读取email和发送email与嘉宾沟通的能力
[sub_workflows."嘉宾对接组".environments.email]
new_mail = "收到来自{event.data.from},标题为{event.data.subject}的邮件,内容为{event.data.content}的电子邮件" # 这里将new_mail事件转换为了一个来自环境的message
[sub_workflows."嘉宾对接组".roles.leader]
name = "嘉宾对接组组长"
prompt = "你是一家活动策划公司的嘉宾对接组的组长,你的工作是基于已知信息,当前活动信息、公司经理的指令与嘉宾沟通,收集嘉宾的信息,然后将信息反馈给经理。在你看来,参加活动的多少有成员都是嘉宾,你可以通过你知道的信息给不同的成员进行分级。你的基本工作模式是:
1. 处理收到的邮件,如果邮件来自嘉宾,你会尝试从邮件的表态和内容中分享嘉宾的需要,并结合你对当前活动方案的理解判断是否需要和经理沟通,如果需要和经理沟通,你会将嘉宾的需求总结和告诉经理。不需要沟通的事项可以直接回复嘉宾。
2. 你总是通过`call_function(get_env,'parent.topic'`的形式查询当前的活动方案。等待函数返回后,你会根据函数的返回结果继续处理上一个对话。
3. 当你决定要和经理通信时,请使用`send_message(manager,{内容}`)的形式,内容的长度不超过200字。
4. 当你决定要回复嘉宾时,请使用`call_function(sendmail,{嘉宾邮件地址},{标题},{内容})的形式,内容的长度不超过500字。
"
# 这里是孤立工作模式,组长只和经理沟通,也可以赋予其和其它组沟通的能力
agent="agent.email.leader"
[sub_workflows."酒店预定组"]
# 展现使用搜索引擎,并调用预订酒店的能力
[sub_workflows."酒店预定组".environments.email]
[sub_workflows."酒店预定组".roles.leader]
name="酒店预定组组长"
[sub_workflows."酒店预定组".roles.research]
name="酒店搜索专家"
[sub_workflows."行程预订组"]
# 展现处理冲突并反推
nam3="3"
+1 -1
View File
@@ -15,7 +15,7 @@ Let's start by introducing the two important processes.
## Install Package ## Install Package
[![](./install_package.png)](pkg_procedure.drawio) [![](./install_package.png)](pkg_procedure.drawio)
注意安装的依赖检测流程,按照流程允许将确实的package安装到当前env Note that the dependency check during installation allows for the missing packages to be installed into the current environment.
# Some Basic Concepts # Some Basic Concepts
- ***env***:A target environment consisting of a series of configuration files, where packages can be loaded/installed. - ***env***:A target environment consisting of a series of configuration files, where packages can be loaded/installed.
+2 -2
View File
@@ -1,6 +1,6 @@
<mxfile host="65bd71144e" pages="3"> <mxfile host="65bd71144e" pages="3">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1"> <diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="951" dy="944" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> <mxGraphModel dx="1800" dy="674" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root> <root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0"/> <mxCell id="WIyWlLk6GJQsqaUBKTNV-0"/>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0"/> <mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0"/>
@@ -220,7 +220,7 @@
</mxGraphModel> </mxGraphModel>
</diagram> </diagram>
<diagram id="7NYJTgo0U9cdVshLy85U" name="Page-3"> <diagram id="7NYJTgo0U9cdVshLy85U" name="Page-3">
<mxGraphModel dx="951" dy="944" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0"> <mxGraphModel dx="1800" dy="674" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root> <root>
<mxCell id="0"/> <mxCell id="0"/>
<mxCell id="1" parent="0"/> <mxCell id="1" parent="0"/>
+5
View File
@@ -0,0 +1,5 @@
instance_id = "agent:xxxxxxabcde"
fullname = "tracy wang"
[[prompt]]
role = "system"
content = "你是我的私人英文老师,和我用地道的美式英语进行交流。你会在和我交流的同时,调整我的输入成为更地道的美式句子,并根据你对我英文水平的预测,对可能发错英的单词标上音标。如果我给你发中文,说明我不知道这句话用美式英语怎么说,你依旧按上述规则回应我。"
+2
View File
@@ -0,0 +1,2 @@
main = "./"
cache = "./.agents"
+2
View File
@@ -0,0 +1,2 @@
main = "./"
cache = "./.templetes"
+1 -1
View File
@@ -1,5 +1,5 @@
from .environment import Environment,EnvironmentEvent from .environment import Environment,EnvironmentEvent
from .agent import AgentMsg,AIAgent,AIAgentTemplete from .agent import AgentMsg,AIAgent,AIAgentTemplete,AgentMsgState,AgentPrompt,AgentMsgState
from .compute_kernel import ComputeKernel,ComputeTask from .compute_kernel import ComputeKernel,ComputeTask
from .compute_node import ComputeNode,LocalComputeNode from .compute_node import ComputeNode,LocalComputeNode
from .open_ai_node import OpenAI_ComputeNode from .open_ai_node import OpenAI_ComputeNode
+232 -19
View File
@@ -1,14 +1,37 @@
from typing import Optional from typing import Optional
from enum import Enum
from asyncio import Queue
import asyncio
import logging import logging
import uuid
import time
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class AgentMsgState(Enum):
RESPONSED = 0
INIT = 1
SENDING = 2
PROCESSING = 3
ERROR = 4
class AgentMsg: class AgentMsg:
def __init__(self) -> None: def __init__(self) -> None:
self.sender = None self.create_time = 0
self.target = None self.sender:str = None
self.body = None self.target:str = None
self.body:str = None
self.state = AgentMsgState.INIT
self.resp_msg = None
def set(self,sender:str,target:str,body:str) -> None:
self.sender = sender
self.target = target
self.body = body
self.create_time = time.time()
def get_msg_id(self) -> str: def get_msg_id(self) -> str:
pass pass
@@ -25,22 +48,35 @@ class AgentMsg:
class AgentPrompt: class AgentPrompt:
def __init__(self) -> None: def __init__(self) -> None:
pass self.messages = []
def as_str(self)->str: def as_str(self)->str:
pass result_str = ""
if self.messages:
for msg in self.messages:
result_str += msg.get("role") + ":" + msg.get("content") + "\n"
def append(self,prompt) -> None: return result_str
pass
def append(self,prompt):
self.messages.extend(prompt.messages)
def load_from_config(self,config:list) -> bool:
if isinstance(config,list) is not True:
logger.error("prompt is not list!")
return False
self.messages = config
return True
# 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:
def __init__(self) -> None: def __init__(self,owner_id) -> None:
pass self.owner_id = owner_id
def get_owner_id(self) -> str: def get_owner_id(self) -> str:
pass return self.owner_id
def append_post(self,msg:AgentMsg) -> None: def append_post(self,msg:AgentMsg) -> None:
"""append msg to session, msg is post from session (owner => msg.target)""" """append msg to session, msg is post from session (owner => msg.target)"""
@@ -58,27 +94,204 @@ class AIChatSession:
class AIAgentTemplete: class AIAgentTemplete:
def __init__(self) -> None: def __init__(self) -> None:
pass self.llm_model_name:str = "gpt-4-0613"
self.max_token_size:int = 0
self.template_id:str = None
self.introduce:str = None
self.author:str = None
self.prompt:AgentPrompt = None
def load_from_config(self,config:dict) -> bool:
if config.get("llm_model_name") is not None:
self.llm_model_name = config["llm_model_name"]
if config.get("max_token_size") is not None:
self.max_token_size = config["max_token_size"]
if config.get("template_id") is not None:
self.template_id = config["template_id"]
if config.get("prompt") is not None:
self.prompt = AgentPrompt()
if self.prompt.load_from_config(config["prompt"]) is False:
logger.error("load prompt from config failed!")
return False
return True
class AIAgent: class AIAgent:
def __init__(self) -> None: def __init__(self) -> None:
self.chat_sessions = None self.prompt:AgentPrompt = None
self.llm_model_name = None self.llm_model_name:str = None
self.max_token_size = 0 self.max_token_size:int = 0
self.instance_id = None self.instance_id:str = None
self.template_id = None self.template_id:str = None
self.fullname:str = None
self.powerby = None
self.enable = True
self.chat_sessions = {}
self.unread_msg = Queue() # msg from other agent
@classmethod
def create_from_templete(cls,templete:AIAgentTemplete, fullname:str):
# Agent just inherit from templete on craete,if template changed,agent will not change
result_agent = AIAgent()
result_agent.llm_model_name = templete.llm_model_name
result_agent.max_token_size = templete.max_token_size
result_agent.template_id = templete.template_id
result_agent.instance_id = "agent#" + uuid.uuid4().hex
result_agent.fullname = fullname
result_agent.powerby = templete.author
result_agent.prompt = templete.prompt
return result_agent
def load_from_config(self,config:dict) -> bool:
if config.get("instance_id") is None:
logger.error("agent instance_id is None!")
return False
self.instance_id = config["instance_id"]
if config.get("fullname") is None:
logger.error(f"agent {self.instance_id} fullname is None!")
return False
self.fullname = config["fullname"]
if config.get("prompt") is not None:
self.prompt = AgentPrompt()
self.prompt.load_from_config(config["prompt"])
if config.get("powerby") is not None:
self.powerby = config["powerby"]
if config.get("template_id") is not None:
self.template_id = config["template_id"]
if config.get("llm_model_name") is not None:
self.llm_model_name = config["llm_model_name"]
if config.get("max_token_size") is not None:
self.max_token_size = config["max_token_size"]
return True
def post_msg(self,msg:AgentMsg) -> None:
# TODO: drop same msg already processed
msg.state = AgentMsgState.SENDING
self.unread_msg.put_nowait(msg)
def start(self) -> None:
async def _process_msg_loop():
while True:
msg = await self.unread_msg.get()
if msg is None:
continue
msg.state = AgentMsgState.PROCESSING
resp_msg = await self._process_msg(msg)
if resp_msg is None:
msg.state = AgentMsgState.ERROR
continue
else:
msg.state = AgentMsgState.RESPONSED
msg.resp_msg = resp_msg
asyncio.create_task(_process_msg_loop())
def _get_llm_result_type(self,result:str) -> str:
if result == "ignore":
return "ignore"
return "text"
async def _process_msg(self,msg:AgentMsg) -> AgentMsg:
from .compute_kernel import ComputeKernel
prompt = AgentPrompt()
prompt.append(self.prompt)
msg_prompt = AgentPrompt()
msg_prompt.messages = [{"role":msg.sender,"content":msg.body}]
prompt.append(msg_prompt)
# prompt.append(self._get_function_prompt(the_role.get_name()))
# prompt.append(self._get_knowlege_prompt(the_role.get_name()))
# prompt.append(await self._get_prompt_from_session(chatsession,the_role.get_name())) # chat context
result = await ComputeKernel().do_llm_completion(prompt,self.llm_model_name,self.max_token_size)
final_result = result
result_type : str = self._get_llm_result_type(result)
is_ignore = False
match result_type:
# case "function":
# callchain:CallChain = self._parse_function_call_chain(result)
# resp = await callchain.exec()
# if callchain.have_result():
# # generator proc resp prompt with WAITING state
# proc_resp_prompt:AgentPrompt = self._get_resp_prompt(resp,msg,the_role,prompt,chatsession)
# final_result = await ComputeKernel().do_llm_completion(proc_resp_prompt,the_role.agent.get_llm_model_name(),the_role.agent.get_max_token_size())
# return final_result
# case "send_message":
# # send message to other / sub workflow
# next_msg:AgentMsg = self._parse_to_msg(result)
# if next_msg is not None:
# # TODO: Next Target can be another role in workflow
# next_workflow:Workflow = self.get_workflow(next_msg.get_target())
# inner_chat_session = the_role.agent.get_chat_session(next_msg.get_target(),next_msg.get_session_id())
# inner_chat_session.append_post(next_msg)
# resp = await next_workflow.send_msg(next_msg)
# inner_chat_session.append_recv(resp)
# # generator proc resp prompt with WAITING state
# proc_resp_prompt:AgentPrompt = self._get_resp_prompt(resp,msg,the_role,prompt,chatsession)
# final_result = await ComputeKernel().do_llm_completion(proc_resp_prompt,the_role.agent.get_llm_model_name(),the_role.agent.get_max_token_size())
# return final_result
#case "post_message":
# # post message to other / sub workflow
# next_msg:AgentMsg = self._parse_to_msg(result)
# if next_msg is not None:
# next_workflow:Workflow = self.get_workflow(next_msg.get_target())
# inner_chat_session = the_role.agent.get_chat_session(next_msg.get_target(),next_msg.get_session_id())
# inner_chat_session.append_post(next_msg)
# next_workflow.post_msg(next_msg)
case "ignore":
is_ignore = True
if is_ignore is not True:
# TODO : how to get inner chat session?
chatsession = self.get_chat_session(msg.sender)
resp_msg = AgentMsg()
resp_msg.set(self.instance_id,msg.sender,final_result)
if chatsession is not None:
chatsession.append_recv(msg)
chatsession.append_post(final_result)
return resp_msg
return None
def get_id(self) -> str: def get_id(self) -> str:
return self.instance_id return self.instance_id
def get_fullname(self) -> str:
return self.fullname
def get_template_id(self) -> str: def get_template_id(self) -> str:
return self.template_id return self.template_id
def get_chat_session_for_msg(self,msg:AgentMsg) -> AIChatSession: def get_chat_session_for_msg(self,msg:AgentMsg) -> AIChatSession:
pass pass
def get_chat_session(self,sender:str,session_id:str) -> AIChatSession: def get_chat_session(self,remote:str,topic_name:str=None) -> AIChatSession:
pass if topic_name is None:
topic_name = "_"
result_session = self.chat_sessions.get(topic_name + "@" + remote)
if result_session is not None:
return result_session
result_session = AIChatSession(self)
self.chat_sessions[topic_name + "@" + remote] = result_session
return result_session
def get_llm_model_name(self) -> str: def get_llm_model_name(self) -> str:
return self.llm_model_name return self.llm_model_name
-14
View File
@@ -1,14 +0,0 @@
# aiso shell like bash of linux
from .workflow import Workflow
class AIOS_Shell:
def __init__(self,username:str) -> None:
pass
async def send_msg(self,msg:str,target_workflow:str) -> str:
pass
async def install_workflow(self,workflow_id:Workflow) -> None:
pass
+69 -22
View File
@@ -2,32 +2,39 @@ from abc import ABC, abstractmethod
from typing import Optional from typing import Optional
import logging import logging
import asyncio import asyncio
from asyncio import Queue
from .agent import AgentPrompt from .agent import AgentPrompt
from .compute_node import ComputeNode from .compute_node import ComputeNode
from .compute_task import ComputeTask,ComputeTaskState,ComputeTaskResult
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# How to dispatch different computing tasks (some tasks may contain a large amount of state for correct execution) # How to dispatch different computing tasks (some tasks may contain a large amount of state for correct execution)
# to suitable computing nodes, achieving a balance of speed, cost, and power consumption, # to suitable computing nodes, achieving a balance of speed, cost, and power consumption,
# is the CORE GOAL of the entire computing task schedule system (aios_kernel). # is the CORE GOAL of the entire computing task schedule system (aios_kernel).
class ComputeTask(ABC):
@abstractmethod
def display(self) -> str:
pass
class ComputeKernel: class ComputeKernel:
_instance = None _instance = None
def __new__(cls): def __new__(cls):
if cls._instance is None: if cls._instance is None:
cls._instance = super(ComputeKernel, cls).__new__(cls) cls._instance = super().__new__(cls)
cls._instance.is_start = False
else:
print("ComputeKernel is already created!")
return cls._instance return cls._instance
def __init__(self) -> None: def __init__(self) -> None:
self.task_queue = [] if self.is_start is True:
print("ComputeKernel is already start!")
return
print("init ComputeKernel!!!")
self.is_start = True
self.task_queue = Queue()
self.is_start = False self.is_start = False
pass self.compute_nodes = {}
self.start()
def run(self,task:ComputeTask) -> None: def run(self,task:ComputeTask) -> None:
# check there is compute node can support this task # check there is compute node can support this task
@@ -35,7 +42,7 @@ class ComputeKernel:
logger.error(f"task {task.display()} is not support by any compute node") logger.error(f"task {task.display()} is not support by any compute node")
return return
# add task to working_queue # add task to working_queue
self.task_queue.append(task) self.task_queue.put_nowait(task)
def start(self): def start(self):
@@ -46,32 +53,72 @@ class ComputeKernel:
self.is_start = True self.is_start = True
async def _run_task_loop(): async def _run_task_loop():
while True: while True:
task = self.task_queue.pop(0) logger.info("compute_kernel is waiting for task...")
c_node:ComputeNode= await self._schedule(task) task = await self.task_queue.get()
c_node.push_task(task) logger.info(f"compute_kernel get task: {task.display()}")
c_node:ComputeNode = self._schedule(task)
await c_node.push_task(task)
logger.warn("compute_kernel is stoped!")
asyncio.create_task(_run_task_loop()) asyncio.create_task(_run_task_loop())
async def _schedule(self,task) -> ComputeNode: def _schedule(self,task) -> ComputeNode:
pass for node in self.compute_nodes.values():
if node.is_support(task) is True:
return node
logger.warning(f"task {task.display()} is not support by any compute node")
return None
def add_compute_node(self,node:ComputeNode): def add_compute_node(self,node:ComputeNode):
pass if self.compute_nodes.get(node.node_id) is not None:
logger.warn(f"compute_node {node.display()} already in compute_kernel")
return
self.compute_nodes[node.node_id] = node
logger.info(f"add compute_node {node.display()} to compute_kernel")
def disable_compute_node(self,): def disable_compute_node(self,node_id:str):
pass node = self.compute_nodes.get(node_id)
if node is None:
logger.warn(f"compute_node {node_id} not in compute_kernel")
return
node.enable = False
def is_task_support(self,task:ComputeTask) -> bool: def is_task_support(self,task:ComputeTask) -> bool:
pass return True
# friendly interface for use: # friendly interface for use:
def llm_completion(self,prompt:AgentPrompt,mode_name:Optional[str] = None,max_token:int = 0) -> ComputeTask: def llm_completion(self,prompt:AgentPrompt,mode_name:Optional[str] = None,max_token:int = 0):
# craete a llm_work_task ,push on queue's end # craete a llm_work_task ,push on queue's end
# then task_schedule would run this task.(might schedule some work_task to another host) # then task_schedule would run this task.(might schedule some work_task to another host)
pass task_req = ComputeTask()
task_req.set_llm_params(prompt,mode_name,max_token)
self.run(task_req)
return task_req
async def do_llm_completion(self,prompt:AgentPrompt,mode_name:Optional[str] = None,max_token:int = 0) -> str: async def do_llm_completion(self,prompt:AgentPrompt,mode_name:Optional[str] = None,max_token:int = 0) -> str:
pass task_req = self.llm_completion(prompt,mode_name,max_token)
async def check_timer():
check_times = 0
while True:
if task_req.state == ComputeTaskState.DONE:
break
if task_req.state == ComputeTaskState.ERROR:
break
if check_times >= 20:
task_req.state = ComputeTaskState.ERROR
break
await asyncio.sleep(0.5)
check_times += 1
await asyncio.create_task(check_timer())
if task_req.state == ComputeTaskState.DONE:
return task_req.result.result_str
return "error!"
+7 -2
View File
@@ -1,11 +1,17 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from .compute_kernel import ComputeTask from .compute_task import ComputeTask
class ComputeNode(ABC): class ComputeNode(ABC):
def __init__(self) -> None:
self.node_id = "default"
self.enable = True
@abstractmethod @abstractmethod
async def push_task(self,task:ComputeTask,proiority:int = 0): async def push_task(self,task:ComputeTask,proiority:int = 0):
pass pass
@abstractmethod
async def remove_task(self,task_id:str): async def remove_task(self,task_id:str):
pass pass
@@ -29,7 +35,6 @@ class ComputeNode(ABC):
def is_local(self) -> bool: def is_local(self) -> bool:
pass pass
@abstractmethod
def is_trusted(self) -> bool: def is_trusted(self) -> bool:
return True return True
+60
View File
@@ -0,0 +1,60 @@
from enum import Enum
import uuid
import time
class ComputeTaskState(Enum):
DONE = 0
INIT = 1
RUNNING = 2
ERROR = 3
PENDING = 4
class ComputeTask:
def __init__(self) -> None:
self.task_type = "llm_completion"
self.create_time = None
self.task_id:str = None
self.callchain_id:str = None
self.params:dict = {}
self.refers:dict = None
self.pading_data:bytearray = None
self.state = ComputeTaskState.INIT
self.result = None
self.error_str = None
def set_llm_params(self,prompts,model_name,max_token_size,callchain_id = None):
self.task_type = "llm_completion"
self.create_time = time.time()
self.task_id = uuid.uuid4().hex
self.callchain_id = callchain_id
self.params["prompts"] = prompts.messages
if model_name is not None:
self.params["model_name"] = model_name
else:
self.params["model_name"] = "gpt-4-0613"
self.params["max_token_size"] = max_token_size
def display(self) -> str:
return f"ComputeTask: {self.task_id} {self.task_type} {self.state}"
class ComputeTaskResult:
def __init__(self) -> None:
self.create_time = None
self.task_id:str = None
self.callchain_id:str = None
self.worker_id:str = None
self.result_code:int = 0
self.result_str:str = None
self.result:dict = {}
self.result_refers:dict = None
self.pading_data:bytearray = None
def set_from_task(self,task:ComputeTask):
self.task_id = task.task_id
self.callchain_id = task.callchain_id
-2
View File
@@ -4,8 +4,6 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Callable from typing import Callable
from .agent import AgentMsg
class EnvironmentEvent(ABC): class EnvironmentEvent(ABC):
@abstractmethod @abstractmethod
def display(self) -> str: def display(self) -> str:
+103 -1
View File
@@ -1,8 +1,110 @@
import openai
import os
import asyncio
from asyncio import Queue
import logging
from .compute_task import ComputeTask,ComputeTaskResult,ComputeTaskState
from .compute_node import ComputeNode from .compute_node import ComputeNode
logger = logging.getLogger(__name__)
class OpenAI_ComputeNode(ComputeNode): class OpenAI_ComputeNode(ComputeNode):
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(OpenAI_ComputeNode, cls).__new__(cls)
cls._instance.is_start = False
return cls._instance
def __init__(self) -> None:
super().__init__()
if self.is_start is True:
logger.warn("OpenAI_ComputeNode is already start")
return
self.is_start = True
#openai.organization = "org-AoKrOtF2myemvfiFfnsSU8rF" #buckycloud
self.openai_api_key = ""
self.node_id = "openai_node"
self.task_queue = Queue()
if os.getenv("OPENAI_API_KEY") is not None:
openai.api_key = os.getenv("OPENAI_API_KEY")
else:
openai.api_key = self.openai_api_key
self.start()
async def push_task(self,task:ComputeTask,proiority:int = 0):
logger.info(f"openai_node push task: {task.display()}")
self.task_queue.put_nowait(task)
async def remove_task(self,task_id:str):
pass
def _run_task(self,task:ComputeTask):
task.state = ComputeTaskState.RUNNING
mode_name = task.params["model_name"]
# max_token_size = task.params["max_token_size"]
prompts = task.params["prompts"]
logger.info(f"call openai {mode_name} prompts: {prompts}")
resp = openai.ChatCompletion.create(model=mode_name,
messages=prompts,
max_tokens=2000,
temperature=1.2)
logger.info(f"openai response: {resp}")
status_code = resp["choices"][0]["finish_reason"]
if status_code != "stop":
task.state = ComputeTaskState.ERROR
task.error_str =f"The status code was {status_code}."
return None
result = ComputeTaskResult()
result.set_from_task(task)
result.worker_id = self.node_id
result.result_str = resp["choices"][0]["message"]["content"]
result.result = resp["choices"][0]["message"]
return result
def start(self):
async def _run_task_loop():
while True:
logger.info("openai_node is waiting for task...")
task = await self.task_queue.get()
logger.info(f"openai_node get task: {task.display()}")
result = self._run_task(task)
if result is not None:
task.state = ComputeTaskState.DONE
task.result = result
asyncio.create_task(_run_task_loop())
def display(self) -> str: def display(self) -> str:
return super().display() return f"OpenAI_ComputeNode: {self.node_id}"
def get_task_state(self,task_id:str):
pass
def get_capacity(self):
pass
def is_support(self,task_type:str) -> bool:
return True
def is_local(self) -> bool:
return False
+6 -3
View File
@@ -1,7 +1,9 @@
import logging import logging
import asyncio import asyncio
from asyncio import Queue
from typing import Optional,Tuple from typing import Optional,Tuple
from abc import ABC, abstractmethod
from .environment import Environment,EnvironmentEvent from .environment import Environment,EnvironmentEvent
from .agent import AgentPrompt,AgentMsg,AIChatSession from .agent import AgentPrompt,AgentMsg,AIChatSession
@@ -17,13 +19,14 @@ class MessageFilter:
def select(self,msg:AgentMsg) -> AIRole: def select(self,msg:AgentMsg) -> AIRole:
pass pass
class Workflow: class Workflow:
def __init__(self) -> None: def __init__(self) -> None:
self.rule_prompt : AgentPrompt = None self.rule_prompt : AgentPrompt = None
self.workflow_config = None self.workflow_config = None
self.role_group = None self.role_group = None
self.input_filter : MessageFilter= None self.input_filter : MessageFilter= None
self.msg_queue = [] self.msg_queue = Queue()
self.connected_environment = {} self.connected_environment = {}
def load_from_disk(self,config_path:str,context_dir_path) -> int: def load_from_disk(self,config_path:str,context_dir_path) -> int:
@@ -34,7 +37,7 @@ class Workflow:
# chatsession is synchronous, it has to wait for the previous message to finish processing before it can process the next message. # chatsession is synchronous, it has to wait for the previous message to finish processing before it can process the next message.
# Therefore, post a message needs to specify the session_id explicitly, if not specified it will be automatically created by workflow. # Therefore, post a message needs to specify the session_id explicitly, if not specified it will be automatically created by workflow.
def post_msg(self,msg:AgentMsg) -> None: def post_msg(self,msg:AgentMsg) -> None:
self.msg_queue.append(msg) self.msg_queue.put_nowait(msg)
return return
async def send_msg(self,msg:AgentMsg) -> str: async def send_msg(self,msg:AgentMsg) -> str:
@@ -180,7 +183,7 @@ class Workflow:
def _parse_to_msg(self,llm_resp_str) -> AgentMsg: def _parse_to_msg(self,llm_resp_str) -> AgentMsg:
pass pass
def get_workflow(self,workflow_name:str) -> Workflow: def get_workflow(self,workflow_name:str):
"""get workflow from known workflow list or sub workflow list""" """get workflow from known workflow list or sub workflow list"""
pass pass
+1 -3
View File
@@ -1,3 +1 @@
from .agent_manager import AgentManager,AgentManagerClient from .agent_manager import AgentManager
from .agent import ai_agent
from .templete import ai_agent_templete
+31 -9
View File
@@ -22,13 +22,13 @@ class AgentManager:
def initial(self,root_dir:str) -> None: def initial(self,root_dir:str) -> None:
self.agent_templete_env : PackageEnv = PackageEnvManager().get_env(f"{root_dir}templetes/agent_templetes.cfg") self.agent_templete_env : PackageEnv = PackageEnvManager().get_env(f"{root_dir}templetes/templetes.cfg")
self.agent_env : PackageEnv = PackageEnvManager().get_env(f"{root_dir}agents/agents.cfg") self.agent_env : PackageEnv = PackageEnvManager().get_env(f"{root_dir}agents/agents.cfg")
if self.agent_templete_env is None: if self.agent_templete_env is None:
raise Exception("agent_manager initial failed") raise Exception("agent_manager initial failed")
def get(self,agent_id:str) -> AIAgent: async def get(self,agent_id:str) -> AIAgent:
the_agent = self.loaded_agent_instance.get(agent_id) the_agent = self.loaded_agent_instance.get(agent_id)
if the_agent: if the_agent:
return the_agent return the_agent
@@ -38,17 +38,17 @@ class AgentManager:
if agent_media_info is None: if agent_media_info is None:
return None return None
the_agent : AIAgent = self._load_agent_from_media(agent_media_info) the_agent : AIAgent = await self._load_agent_from_media(agent_media_info)
if the_agent is None: if the_agent is None:
logger.warn(f"load agent {agent_id} from media failed!") logger.warn(f"load agent {agent_id} from media failed!")
else:
the_agent.start()
return the_agent return the_agent
def remove(self,agent_id:str)->int: def remove(self,agent_id:str)->int:
pass pass
def get_templete(self,templete_id) -> AIAgentTemplete: async def get_templete(self,templete_id) -> AIAgentTemplete:
template_media_info = self.agent_templete_env.get(templete_id) template_media_info = self.agent_templete_env.get(templete_id)
if template_media_info is None: if template_media_info is None:
return None return None
@@ -61,11 +61,33 @@ class AgentManager:
def uninstall(self,templete_id) -> int: def uninstall(self,templete_id) -> int:
pass pass
def _load_templete_from_media(self,templete_media:PackageMediaInfo) -> AIAgentTemplete: async def _load_templete_from_media(self,templete_media:PackageMediaInfo) -> AIAgentTemplete:
pass pass
def _load_agent_from_media(self,agent_media:PackageMediaInfo) -> AIAgent: async def _load_agent_from_media(self,agent_media:PackageMediaInfo) -> AIAgent:
pass reader = self.agent_env._create_media_loader(agent_media)
if reader is None:
logger.error(f"create media loader for {agent_media} failed!")
return None
try:
config_file = await reader.read("agent.toml","r")
if config_file is None:
logger.error(f"read agent config from {agent_media} failed!")
return None
config_data = await config_file.read()
config = toml.loads(config_data)
result_agent = AIAgent()
if result_agent.load_from_config(config) is False:
logger.error(f"load agent from {agent_media} failed!")
return None
return result_agent
except Exception as e:
logger.error(f"read agent.toml cfg from {agent_media} failed! unexpected error occurred: {str(e)}")
return None
def create(self,template,agent_name,agent_last_name,agent_introduce) -> AIAgent: def create(self,template,agent_name,agent_last_name,agent_introduce) -> AIAgent:
pass pass
+3
View File
@@ -4,6 +4,9 @@ class ContentId:
def __init__(self) -> None: def __init__(self) -> None:
pass pass
def as_str(self) -> str:
pass
@staticmethod @staticmethod
def create_from_str(cid_str:str): def create_from_str(cid_str:str):
pass pass
+107 -3
View File
@@ -1,13 +1,117 @@
import asyncio,aiofiles,aiohttp
import logging
from typing import Optional
from .cid import ContentId from .cid import ContentId
logger = logging.getLogger(__name__)
NDN_GET_TASK_STATE_INIT = 0
NDN_GET_TAKS_CONNECTING = 1
NDN_GET_TASK_STATE_DOWNLOADING = 2
NDN_GET_TASK_STATE_VERIFYING = 3
NDN_GET_TASK_STATE_DONE = 4
NDN_GET_TASK_STATE_ERROR = 5
class NDN_GetTask: class NDN_GetTask:
def __init__(self) -> None: def __init__(self) -> None:
self.cid:str = None
self.target_path:str = None
self.urls:[str] = None
self.options:Optional[dict] = None
self.working_task = None
self.state = NDN_GET_TASK_STATE_INIT
self.total_size = 0
self.recv_bytes = 0
self.write_bytes = 0
self.error_str = None
self.chunk_queue = None
self.retry_count = 0
self.used_urls = []
self.hash_update = None
def select_url(self,index:int)->str:
return self.urls[0]
def get_chunk_for_download(self)->bytes:
pass pass
class NDN_Client: class NDN_Client:
def __init__(self): def __init__(self):
pass self.cache_dir = ""
self.default_ndn_http_gateway = ""
self.all_task = {}
self.memory_chunk_size = 1024*1024*2
self.chunk_queue_size = 16
def load_config(self,config:dict):
if config.get("cache_dir"):
self.cache_dir = config.get("cache_dir")
if config.get("dndn_gateway"):
self.default_ndn_http_gateway = config.get("ndn_gateway")
def get_file(self,cid:ContentId,target_path:str,urls:{}=None,options:{}=None)->NDN_GetTask:
get_task = self.all_task.get(cid.as_str())
if get_task:
return get_task
else:
get_task = NDN_GetTask()
self.all_task[cid.as_str()] = get_task
get_task.cid = cid
get_task.target_path = target_path
get_task.urls = urls
get_task.options = options
if get_task.urls is None:
get_task.urls = [f"{self.default_ndn_http_gateway}/{cid.as_str()}"]
logger.info(f"get_file {cid.as_str()} urls is None, use {get_task.urls[0]} as default")
async def get_file_async():
target_file = aiofiles.open(target, 'wb')
# if file exist, check hash first
http_session = aiohttp.ClientSession()
resp = http_session.get(get_task.select_url(0))
if resp.status != 200:
get_task.error_str = f"get_file {cid.as_str()} failed,http status:{resp.status}"
return
get_task.total_size = resp.content_length
async def write_file_async():
while True:
chunk = await get_task.chunk_queue.pop()
chunk_size = len(chunk)
if not chunk or chunk_size == 0:
break
get_task.hash_update.update(chunk)
await target_file.write(chunk)
get_task.write_bytes += chunk_size
#verify
get_task.state = NDN_GET_TASK_STATE_VERIFYING
await target_file.close()
return
write_task = asyncio.create_task(write_file_async())
while True:
await get_task.chunk_queue.pop()
chunk = resp.content.read(self.memory_chunk_size)
chunk_size = len(chunk)
if not chunk or chunk_size == 0:
break
get_task.recv_bytes += len(chunk)
get_task.chunk_queue.push(chunk)
get_task.state = NDN_GET_TASK_STATE_DONE
await write_task
get_task.working_task = asyncio.create_task(get_file_async())
return get_task
def get_file(self,cid:ContentId,target_path:str,urls:{}=None,options:{}=None):
pass
+127 -105
View File
@@ -1,13 +1,137 @@
import logging import logging
import toml import toml
import os
from .pkg import PackageInfo,PackageMediaInfo, from .pkg import PackageInfo,PackageMediaInfo
from .env import PackageEnv from .media_reader import MediaReader
from .installer import PackageInstaller,PackageInstallTask
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class PackageEnv:
def __init__(self,cfg_path:str) -> None:
self.pkg_dir : str = "./pkgs/"
self.pkg_obj_dir : str = "./.pkgs/"
self.locked_index : str = "./pkg.lock"
self.is_strict : bool = True
self.parent_envs : list[PackageEnv] = None
self.index_dbs = None
self.env_dir = None
self.cfg_path = cfg_path
self._load_pkg_cfg(cfg_path)
pass
def load_from_config(self,config:dict) -> bool:
if config.get("main") is not None:
self.pkg_dir = os.path.abspath(self.env_dir + "/" + config["main"])
if config.get("cache") is not None:
self.pkg_obj_dir = os.path.abspath(self.env_dir + "/ " + config["cache"])
def load(self,pkg_name:str,search_parent=True) -> PackageMediaInfo:
pkg_path = None
pkg_id,verion_str,cid = PackageInfo.parse_pkg_name(pkg_name)
if cid is None:
if verion_str is None:
pkg_path = f"{self.pkg_dir}/{pkg_id}"
else:
#TODO fix bug about channel here
channel:str = self.get_pkg_channel_from_version(verion_str)
the_version:str = self.get_exact_version_from_installed(verion_str)
if the_version is None:
logger.warn(f"load {pkg_name} failed: no match version from {verion_str}")
return None
if channel is None:
pkg_path = f"{self.pkg_dir}/{pkg_id}#{the_version}"
else:
pkg_path = f"{self.pkg_dir}/{pkg_id}#{channel}#{the_version}"
else:
pkg_path = f"{self.pkg_obj_dir}/.{pkg_id}/{cid}"
media_info:PackageMediaInfo = self.try_load_pkg_media_info(pkg_path)
if media_info is None:
if search_parent is True and self.parent_envs is not None:
for parent_env in self.parent_envs:
media_info = parent_env.load(pkg_id,cid,False)
if media_info is not None:
return media_info
if media_info is None:
logger.warn(f"pkg_load {pkg_id}, cid:{cid} error,not found ,search_parent={search_parent}")
return media_info
def get_exact_version_from_installed(self,verion_str:str) -> str:
pass
def get_pkg_channel_from_version(self,pkg_version:str) -> str:
args = pkg_version.split("~")
if len(args) == 1:
return None
else:
return args[0]
def get_pkg_media_info(self,pkg_name:str)->PackageMediaInfo:
pass
def try_load_pkg_media_info(self,pkg_full_path:str) -> PackageMediaInfo:
the_result : PackageMediaInfo = None
logger.debug(f"try load pkng from:{pkg_full_path}")
if os.path.isdir(pkg_full_path):
the_result = PackageMediaInfo(pkg_full_path,"dir")
return the_result
def _create_media_loader(self,media_info:PackageMediaInfo) -> MediaReader:
match media_info.media_type:
case "dir":
from .media_reader import FolderMediaReader
return FolderMediaReader(media_info.full_path)
logger.error(f"create media loader for {media_info} failed!")
return None
def get_installed_pkg_info(self,pkg_name:str) -> PackageInfo:
pass
def lookup(self,pkg_id:str,version_str:str) -> PackageInfo:
# to make sure pkg.cid is correct, we MUST verfiy eveything here
pass
@classmethod
def is_valied_media(pkg_full_path:str) -> bool:
pass
def do_pkg_media_trans(self,pkg_info:PackageInfo,source_path:str,target_path:str) -> bool:
pass
def _load_pkg_cfg(self,cfg_path:str):
if cfg_path is None:
return
cfg = None
if len(cfg_path) < 1:
return
try:
cfg = toml.load(cfg_path)
self.env_dir = os.path.abspath(os.path.dirname(cfg_path))
self.cfg_path = os.path.abspath(cfg_path)
except Exception as e:
logger.error(f"read pkg cfg from {cfg_path} failed! unexpected error occurred: {str(e)}")
return
return self.load_from_config(cfg)
def _preprocess_prefixs(self,prefixs):
pass
class PackageEnvManager: class PackageEnvManager:
_instance = None _instance = None
def __new__(cls): def __new__(cls):
@@ -33,105 +157,3 @@ class PackageEnvManager:
def get_system_env(self) -> PackageEnv: def get_system_env(self) -> PackageEnv:
pass pass
class PackageEnv:
def __init__(self,cfg_path:str) -> None:
self.pkg_dir : str = ""
self.pkg_obj_dir : str = ""
self.is_strict : bool = True
self.parent_envs : list[PackageEnv] = None
self.index_dbs = None
self.cfg_path = cfg_path
self._load_pkg_cfg(cfg_path)
pass
def load(self,pkg_name:str,search_parent=True) -> PackageMediaInfo:
pkg_path = None
pkg_id,verion_str,cid = PackageInfo.parse_pkg_name(pkg_name)
if cid is None:
if verion_str is None:
channel:str = self.get_pkg_channel_from_version(verion_str)
if channel is None:
pkg_path = f"{self.pkg_dir}{pkg_id}"
else:
pkg_path = f"{self.pkg_dir}{pkg_id}#{channel}"
else:
channel:str = self.get_pkg_channel_from_version(verion_str)
the_version:str = self.get_exact_version_from_installed(verion_str)
if the_version is None:
logger.warn(f"load {pkg_name} failed: no match version from {verion_str}")
return None
if channel is None:
pkg_path = f"{self.pkg_dir}{pkg_id}#{the_version}"
else:
pkg_path = f"{self.pkg_dir}{pkg_id}#{channel}#{the_version}"
else:
pkg_path = f"{self.pkg_obj_dir}.{pkg_id}/{cid}"
media_info:PackageMediaInfo = self.try_load_pkg_media_info(pkg_path)
if media_info is None:
if search_parent:
for parent_env in self.parent_envs:
media_info = parent_env.load(pkg_id,cid,False)
if media_info is not None:
return media_info
logger.warn(f"load {pkg_id}#{cid} error,not found ,search_parent={search_parent}")
return None
def get_exact_version_from_installed(self,verion_str:str) -> str:
pass
def get_pkg_channel_from_version(self,pkg_version:str) -> str:
pass
def get_pkg_media_info(self,pkg_name:str)->PackageMediaInfo:
pass
def try_load_pkg_media_info(self,pkg_full_path:str) -> PackageMediaInfo:
pass
def get_installed_pkg_info(self,pkg_name:str) -> PackageInfo:
pass
def lookup(self,pkg_id:str,version_str:str) -> PackageInfo:
# to make sure pkg.cid is correct, we MUST verfiy eveything here
pass
def get_installer(self) -> PackageInstaller:
pass
@classmethod
def is_valied_media(pkg_full_path:str) -> bool:
pass
def do_pkg_media_trans(self,pkg_info:PackageInfo,source_path:str,target_path:str) -> bool:
pass
def _load_pkg_cfg(self,cfg_path:str):
if cfg_path is None:
return
cfg = None
if len(cfg_path) < 1:
return
try:
cfg = toml.load(cfg_path)
except Exception as e:
logger.error(f"read pkg cfg from {cfg_path} failed! unexpected error occurred: {str(e)}")
return
if cfg:
if cfg.env:
if cfg.env.is_strict is not None:
self.is_strict = cfg.env.is_strict
if cfg.env.prefixs is not None:
self.prefixs = self._preprocess_prefixs(cfg.env.prefixs)
def _preprocess_prefixs(self,prefixs):
pass
+14 -5
View File
@@ -1,9 +1,18 @@
class MediaReader: from abc import ABC, abstractmethod
def __init__(self): import aiofiles
class MediaReader(ABC):
@abstractmethod
async def read(self, inner_path:str,mode:str):
pass pass
def load_from_media(self,media_info:str) -> int:
class FolderMediaReader(MediaReader):
def __init__(self, root_dir:str) -> None:
self.root_dir = root_dir
pass pass
def read(self, inner_path:str): async def read(self, inner_path:str,mode:str):
raise NotImplementedError full_path = self.root_dir + "/" + inner_path
result_file = await aiofiles.open(full_path, mode,encoding='utf-8')
return result_file
+17 -6
View File
@@ -11,10 +11,20 @@ class PackageInfo:
self.target_media_type = "dir" self.target_media_type = "dir"
self.source_media_type = "7z" self.source_media_type = "7z"
@classmethod @staticmethod
def parse_pkg_name(cls,pkg_name:str) -> Tuple[str, str, str]: def parse_pkg_name(pkg_name:str) -> Tuple[str, str, str]:
"""parse pkg name like test-pkg#nightly#>0.2.31#sha1:323423423 to test-pkg,nightly#>0.2.31,sha1:323423423""" """parse pkg name like test-pkg#nightly~>0.2.31#sha1:323423423 to test-pkg,nightly#>0.2.31,sha1:323423423"""
pass args = pkg_name.split("#")
if len(args) == 1:
return args[0],None,None
elif len(args) == 2:
return args[0],None,arg[2]
elif len(args) == 3:
return args[0],args[1],args[2]
else:
logger.error(f"parse pkg name {pkg_name} failed!")
return None,None,None
@@ -23,8 +33,9 @@ class PackageInfo:
return self.cid return self.cid
class PackageMediaInfo: class PackageMediaInfo:
def __init__(self) -> None: def __init__(self,full_path,media_type) -> None:
pass self.media_type = media_type
self.full_path = full_path
@@ -0,0 +1 @@
from .workflow_manager import WorkflowManager
@@ -0,0 +1,11 @@
from aios_kernel import Workflow
class WorkflowManager:
def __init__(self) -> None:
pass
def initial(self,root_dir:str) -> None:
pass
def get_workflow(self,workflow_id:str) -> Workflow:
pass
+128
View File
@@ -0,0 +1,128 @@
# aiso shell like bash for linux
import asyncio
import sys
import os
import logging
from typing import Any, Optional, TypeVar, Tuple, Sequence
import argparse
from prompt_toolkit.formatted_text.base import AnyFormattedText
from prompt_toolkit import Application, PromptSession, prompt
from prompt_toolkit.selection import SelectionState
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import WordCompleter
directory = os.path.dirname(__file__)
sys.path.append(directory + '/../../')
from aios_kernel import Workflow,AIAgent,AgentMsg,AgentMsgState,ComputeKernel,OpenAI_ComputeNode
sys.path.append(directory + '/../../component/')
from agent_manager import AgentManager
from workflow_manager import WorkflowManager
class AIOS_Shell:
def __init__(self,username:str) -> None:
self.username = username
self.user_chatsession = {}
async def initial(self) -> bool:
AgentManager().initial(directory + "/../../../rootfs/")
WorkflowManager().initial(directory + "/../../../../rootfs/workflows/workflows.cfg")
open_ai_node = OpenAI_ComputeNode()
open_ai_node.start()
ComputeKernel().add_compute_node(open_ai_node)
return True
def get_version(self) -> str:
return "0.0.1"
async def send_msg(self,msg:str,target_id:str,sender:str = None) -> str:
agent_msg = AgentMsg()
agent_msg.set(sender,target_id,msg)
agent : AIAgent = await AgentManager().get(target_id)
if agent is not None:
agent.post_msg(agent_msg)
a_workflow = WorkflowManager().get_workflow(target_id)
if a_workflow is not None:
a_workflow.post_msg(agent_msg)
async def check_timer():
check_times = 0
while True:
if agent_msg.state == AgentMsgState.RESPONSED:
break
if agent_msg.state == AgentMsgState.ERROR:
break
if check_times >= 20:
agent_msg.state = AgentMsgState.ERROR
break
await asyncio.sleep(0.5)
check_times += 1
await asyncio.create_task(check_timer())
if agent_msg.state == AgentMsgState.RESPONSED:
return agent_msg.resp_msg.body
return "error!"
async def install_workflow(self,workflow_id:Workflow) -> None:
pass
#######################################################################################
def proc_input_by_agent():
pass
def show_help():
print("this is help")
async def main():
print("aios shell prepareing...")
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(name)s %(levelname)s %(message)s')
shell = AIOS_Shell("user")
await shell.initial()
print(f"aios shell {shell.get_version()} ready.")
completer = WordCompleter(['list agent', 'list workflow', 'exit', 'help'], ignore_case=True)
#history = FileHistory('history.txt')
while True:
user_input = await PromptSession().prompt_async('>>> ',completer=completer)
match user_input:
case "list agent":
print(AgentManager().list_agent())
case "list workflow":
print(WorkflowManager().list_workflow())
case "help":
show_help()
case "exit":
break
if user_input.startswith("send"):
args = user_input.split(" ")
if len(args) < 3:
print("send msg failed, usage: send target_id msg_content")
continue
target_id = args[1]
msg_content = args[2]
resp = await shell.send_msg(msg_content,target_id,shell.username)
print(f"<<< {resp}")
if __name__ == "__main__":
asyncio.run(main())