From 7f11edb70656dafc85d1154d4a827378de1215d9 Mon Sep 17 00:00:00 2001 From: Liu Zhicong Date: Mon, 25 Sep 2023 20:57:10 -0700 Subject: [PATCH] Jarvis can update contract, aios_shell can update contract. --- src/aios_kernel/contact_manager.py | 17 +++- src/aios_kernel/google_text_to_speech_node.py | 2 +- src/aios_kernel/local_stability_node.py | 2 +- src/aios_kernel/storage.py | 16 ++++ src/aios_kernel/tg_tunnel.py | 8 +- src/aios_kernel/workflow_env.py | 51 ++++++++++ src/service/aios_shell/aios_shell.py | 94 ++++++++++++++++++- 7 files changed, 182 insertions(+), 8 deletions(-) diff --git a/src/aios_kernel/contact_manager.py b/src/aios_kernel/contact_manager.py index 225e9a1..39c5711 100644 --- a/src/aios_kernel/contact_manager.py +++ b/src/aios_kernel/contact_manager.py @@ -49,7 +49,7 @@ class ContactManager: @classmethod def get_instance(cls,filename=None) -> "ContactManager": if cls._instance is None: - cls._instance = ContactManager(filename) + cls._instance = ContactManager(str(filename)) return cls._instance def __init__(self, filename="contacts.toml"): @@ -79,6 +79,21 @@ class ContactManager: with open(self.filename, "w") as f: toml.dump(data, f) + def set_contact(self, name:str, new_contact:Contact): + assert name == new_contact.name + for i, contact in enumerate(self.contacts): + if contact.name == name: + self.contacts[i] = new_contact + self.save_data() + return True + for i, member in enumerate(self.family_members): + if member.name == name: + self.family_members[i] = new_contact + self.save_data() + return True + + return False + def add_contact(self, name:str, new_contact:Contact): assert name == new_contact.name self.contacts.append(new_contact) diff --git a/src/aios_kernel/google_text_to_speech_node.py b/src/aios_kernel/google_text_to_speech_node.py index 9bee115..b76d0fc 100644 --- a/src/aios_kernel/google_text_to_speech_node.py +++ b/src/aios_kernel/google_text_to_speech_node.py @@ -176,5 +176,5 @@ class GoogleTextToSpeechNode(ComputeNode): user_config = AIStorage.get_instance().get_user_config() user_config.add_user_config("google_application_credentials", "google application credentials, please visit:https://cloud.google.com/text-to-speech/docs/before-you-begin", - False, + True, None) diff --git a/src/aios_kernel/local_stability_node.py b/src/aios_kernel/local_stability_node.py index 77971b8..d773105 100644 --- a/src/aios_kernel/local_stability_node.py +++ b/src/aios_kernel/local_stability_node.py @@ -30,7 +30,7 @@ class Local_Stability_ComputeNode(ComputeNode): user_config = AIStorage.get_instance().get_user_config() if os.getenv("LOCAL_STABILITY_URL") is None: user_config.add_user_config( - "local_stability_url", "local stability url", False, None) + "local_stability_url", "local stability url", True, None) if os.getenv("TEXT2IMG_OUTPUT_DIR") is None: home_dir = Path.home() output_dir = Path.joinpath(home_dir, "text2img_output") diff --git a/src/aios_kernel/storage.py b/src/aios_kernel/storage.py index d57546e..211129d 100644 --- a/src/aios_kernel/storage.py +++ b/src/aios_kernel/storage.py @@ -13,6 +13,10 @@ class ResourceLocation: def __init__(self) -> None: pass +class FeatureItem: + def __init__(self) -> None: + pass + class UserConfigItem: def __init__(self,desc:str=None) -> None: self.default_value = None @@ -142,6 +146,18 @@ class AIStorage: await self.user_config.load_value_from_file(self.get_system_dir() + "/system.cfg.toml") await self.user_config.load_value_from_file(self.user_config.user_config_path,True) + async def enable_feature(self,feature_name:str) -> None: + pass + + async def disable_feature(self,feature_name:str) -> None: + pass + + async def set_feature_init_result(self,feature_name:str,result:bool) -> None: + pass + + async def is_feature_enable(self,feature_name:str) -> bool: + pass + def get_user_config(self) -> UserConfig: return self.user_config diff --git a/src/aios_kernel/tg_tunnel.py b/src/aios_kernel/tg_tunnel.py index 735da43..7062a99 100644 --- a/src/aios_kernel/tg_tunnel.py +++ b/src/aios_kernel/tg_tunnel.py @@ -10,6 +10,7 @@ from telegram.ext import Updater from telegram.error import Forbidden, NetworkError from .tunnel import AgentTunnel +from .storage import AIStorage from .contact_manager import ContactManager,Contact,FamilyMember from .agent_message import AgentMsg @@ -119,6 +120,10 @@ class TelegramTunnel(AgentTunnel): cm : ContactManager = ContactManager.get_instance() reomte_user_name = f"{update.effective_user.id}@telegram" + #owner_tg_username = AIStorage.get_instance().get_user_config().get_value("telegram") + #owner_name = AIStorage.get_instance().get_user_config().get_value("username") + + contact : Contact = cm.find_contact_by_telegram(update.effective_user.username) if contact is None: contact = cm.find_contact_by_telegram(str(update.effective_user.id)) @@ -136,8 +141,7 @@ class TelegramTunnel(AgentTunnel): contact.added_by = self.target_id cm.add_contact(contact.name, contact) reomte_user_name = contact.name - - # create gust account? + agent_msg = await self.conver_tg_msg_to_agent_msg(update) agent_msg.sender = reomte_user_name self.ai_bus.register_message_handler(reomte_user_name, self._process_message) diff --git a/src/aios_kernel/workflow_env.py b/src/aios_kernel/workflow_env.py index f872e5b..39dda7b 100644 --- a/src/aios_kernel/workflow_env.py +++ b/src/aios_kernel/workflow_env.py @@ -13,6 +13,7 @@ from .compute_kernel import ComputeKernel from .environment import Environment,EnvironmentEvent from .ai_function import SimpleAIFunction from .storage import AIStorage +from .contact_manager import ContactManager,Contact,FamilyMember import aiosqlite @@ -93,6 +94,17 @@ class CalenderEnvironment(Environment): "Draw a picture according to the description", self._paint,paint_param)) + self.add_ai_function(SimpleAIFunction("get_contact", + "get contact info", + self._get_contact,{"name":"name of contact"})) + + self.add_ai_function(SimpleAIFunction("set_contact", + "set contact info", + self._set_contact,{"name":"name of contact","contact_info":"A json to descrpit contact"})) + + + + #self.add_ai_function(SimpleAIFunction("user_confirm", # "user confirm", # self._user_confirm)) @@ -222,6 +234,45 @@ class CalenderEnvironment(Environment): def _do_get_value(self,key:str) -> Optional[str]: return None + + async def _get_contact(self,name:str) -> str: + cm = ContactManager.get_instance() + contact : Contact = cm.find_contact_by_name(name) + if contact: + s = json.dumps(contact.to_dict()) + return f"Execute get_contact OK , contact {name} is {s}" + else: + return f"Execute get_contact OK , contact {name} not found!" + + async def _set_contact(self,name:str,contact_info:str) -> str: + cm = ContactManager.get_instance() + contact = cm.find_contact_by_name(name) + contact_info = json.loads(contact_info) + if contact is None: + contact = Contact(name) + contact.email = contact_info.get("email") + contact.telegram = contact_info.get("telegram") + contact.notes = contact_info.get("notes") + contact.added_by = self.env_id + + cm.add_contact(name,contact) + + return f"Execute set_contact OK , new contact {name} added!" + else: + if contact_info.get("email") is not None: + contact.email = contact_info.get("email") + if contact_info.get("telegram") is not None: + contact.telegram = contact_info.get("telegram") + if contact_info.get("notes") is not None: + contact.notes = contact_info.get("notes") + + contact.added_by = self.env_id + cm.set_contact(name,contact) + + return f"Execute set_contact OK , contact {name} updated!" + + + async def start(self) -> None: if self.is_run: diff --git a/src/service/aios_shell/aios_shell.py b/src/service/aios_shell/aios_shell.py index 69d9b54..ca43eca 100644 --- a/src/service/aios_shell/aios_shell.py +++ b/src/service/aios_shell/aios_shell.py @@ -24,6 +24,7 @@ sys.path.append(directory + '/../../') from aios_kernel import AIOS_Version,UserConfigItem,AIStorage,Workflow,AIAgent,AgentMsg,AgentMsgStatus,ComputeKernel,OpenAI_ComputeNode,AIBus,AIChatSession,AgentTunnel,TelegramTunnel,CalenderEnvironment,Environment,EmailTunnel,LocalLlama_ComputeNode,Local_Stability_ComputeNode +from aios_kernel import ContactManager,Contact import proxy from aios_kernel import * @@ -51,8 +52,14 @@ class AIOS_Shell: self.is_working = True def declare_all_user_config(self): + cm_path = AIStorage.get_instance().get_myai_dir() / "contacts.toml" + cm = ContactManager.get_instance(cm_path) + cm.load_data() + user_config = AIStorage.get_instance().get_user_config() - user_config.add_user_config("username","username is your full name when using AIOS",False,None,) + user_config.add_user_config("username","username is your full name when using AIOS",False,None) + user_config.add_user_config("telegram","Your telgram username",False,None) + user_config.add_user_config("email","Your email",False,None) openai_node = OpenAI_ComputeNode.get_instance() openai_node.declare_user_config() @@ -88,6 +95,17 @@ class AIOS_Shell: return False async def initial(self) -> bool: + cm = ContactManager.get_instance() + owenr = cm.find_contact_by_name(self.username) + if owenr is None: + owenr = Contact(self.username) + owenr.added_by = self.username + owenr.is_family_member = True + owenr.email = AIStorage.get_instance().get_user_config().get_value("email") + owenr.telegram = AIStorage.get_instance().get_user_config().get_value("telegram") + + cm.add_contact(self.username,owenr) + cal_env = CalenderEnvironment("calender") await cal_env.start() Environment.set_env_by_id("calender",cal_env) @@ -99,6 +117,7 @@ class AIOS_Shell: await AgentManager.get_instance().initial() await WorkflowManager.get_instance().initial() + open_ai_node = OpenAI_ComputeNode.get_instance() if await open_ai_node.initial() is not True: logger.error("openai node initial failed!") @@ -120,7 +139,7 @@ class AIOS_Shell: local_sd_node = Local_Stability_ComputeNode.get_instance() if await local_sd_node.initial() is not True: logger.error("local stability node initial failed!") - return False + ComputeKernel.get_instance().add_compute_node(local_sd_node) await ComputeKernel.get_instance().start() @@ -164,6 +183,7 @@ class AIOS_Shell: async def _user_process_msg(self,msg:AgentMsg) -> AgentMsg: pass + async def get_tunnel_config_from_input(self,tunnel_target,tunnel_type): tunnel_config = {} @@ -206,6 +226,59 @@ class AIOS_Shell: except Exception as e: logger.warning(f"load tunnels config from {tunnels_config_path} failed!") + async def handle_contact_commands(self,args): + cm = ContactManager.get_instance() + if len(args) < 1: + return FormattedText([("class:error", f'/contact $contact_name, Like /contact "Jim Green"')]) + contact_name = args[0] + contact = cm.find_contact_by_name(contact_name) + is_update = False + if contact is not None: + #show old info and ask user to update or remove + is_update = True + op_str = await try_get_input(f"Contact {contact_name} already exist, update or remove? (u/r)") + if op_str is None: + return None + if op_str == "r": + cm.remove_contact(contact_name) + return FormattedText([("class:title", f"remove {contact_name} success!")]) + else: + print(f"old info: {contact}") + else: + contact = Contact(contact_name) + + contact.is_family_member = False + is_family_member = await try_get_input(f"Is {contact_name} your family member? (y/n)") + if is_family_member is not None: + if is_family_member == "y" or is_family_member == "Y": + contact.is_family_member = True + else: + return None + + contact_telegram = await try_get_input(f"Input {contact_name}'s telegram username:") + if contact_telegram is None: + return None + contact.telegram = contact_telegram + + contact_email = await try_get_input(f"Input {contact_name}'s email:") + if contact_email is None: + return None + contact.email = contact_email + + contact_phone = await try_get_input(f"Input {contact_name}'s phone (optional):") + if contact_phone is not None: + contact.phone = contact_phone + + contact_note = await try_get_input(f"Input {contact_name}'s note (optional):") + if contact_note is not None: + contact.note = contact_note + + contact.added_by = self.username + if is_update: + cm.set_contact(contact_name,contact) + else: + cm.add_contact(contact_name,contact) + async def handle_knowledge_commands(self, args): show_text = FormattedText([("class:title", "sub command not support!\n" "/knowledge add email | dir\n" @@ -228,7 +301,7 @@ class AIOS_Shell: if error is not None: return FormattedText([("class:title", f"/knowledge add email failed {error}\n")]) else: - KnowledgePipline.get_instance().save_config() + KnowledgePipline.get_instance().save_cosnfig() if args[1] == "dir": config = dict() for key, item in KnowledgeDirSource.user_config_items(): @@ -299,11 +372,19 @@ class AIOS_Shell: return show_text case 'knowledge': return await self.handle_knowledge_commands(args) + case 'contact': + return await self.handle_contact_commands(args) case 'open': if len(args) >= 1: target_id = args[0] + else: + show_text = FormattedText([("class:error", "/open Need Target Agent/Workflow ID! like /open Jarvis default")]) + return show_text + if len(args) >= 2: topic = args[1] + else: + topic = "default" self.current_target = target_id self.current_topic = topic @@ -382,6 +463,12 @@ async def try_get_input(desc:str,check_func:callable = None) -> str: async def get_user_config_from_input(check_result:dict) -> bool: for key,item in check_result.items(): user_input = await try_get_input(f"System config {key} ({item.desc}) not define!") + if user_input is None: + if item.is_optional: + continue + else: + True + if len(user_input) > 0: AIStorage.get_instance().get_user_config().set_value(key,user_input) @@ -478,6 +565,7 @@ async def main(): '/history $num $offset', '/login $username', '/connect $target', + '/contact $name', '/knowledge add email | dir', '/knowledge journal [$topn]', '/knowledge query $query'