From 4e45130140a3908406dff3575887bf67ba874d2e Mon Sep 17 00:00:00 2001 From: wugren Date: Thu, 21 Sep 2023 15:52:56 +0800 Subject: [PATCH] story maker --- rootfs/agents/fairy_tale_writer/agent.toml | 7 ++ rootfs/agents/studio_director/agent.toml | 7 ++ rootfs/workflows/story_maker/workflow.toml | 37 +++++++ src/aios_kernel/__init__.py | 1 + src/aios_kernel/chatsession.py | 27 ++--- src/aios_kernel/compute_kernel.py | 50 +++++++-- src/aios_kernel/google_text_to_speech_node.py | 81 ++++++++++++-- src/aios_kernel/text_to_speech_function.py | 101 ++++++++++++++++++ src/aios_kernel/workflow_env.py | 45 ++++---- src/service/aios_shell/aios_shell.py | 73 +++++++------ 10 files changed, 341 insertions(+), 88 deletions(-) create mode 100644 rootfs/agents/fairy_tale_writer/agent.toml create mode 100644 rootfs/agents/studio_director/agent.toml create mode 100644 rootfs/workflows/story_maker/workflow.toml create mode 100644 src/aios_kernel/text_to_speech_function.py diff --git a/rootfs/agents/fairy_tale_writer/agent.toml b/rootfs/agents/fairy_tale_writer/agent.toml new file mode 100644 index 0000000..1f2256a --- /dev/null +++ b/rootfs/agents/fairy_tale_writer/agent.toml @@ -0,0 +1,7 @@ +instance_id = "fairy_tale_writer" +fullname = "tracy wang" +llm_model_name = "gpt-3.5-turbo-16k-0613" + +[[prompt]] +role = "system" +content = "你是一个童话做作家,能够写出各种有趣的童话。" diff --git a/rootfs/agents/studio_director/agent.toml b/rootfs/agents/studio_director/agent.toml new file mode 100644 index 0000000..eb61bca --- /dev/null +++ b/rootfs/agents/studio_director/agent.toml @@ -0,0 +1,7 @@ +instance_id = "studio_director" +fullname = "tracy wang" +llm_model_name = "gpt-3.5-turbo-16k-0613" + +[[prompt]] +role = "system" +content = "你是一个演播导演,请将下面故事改编成朗读剧本,提取旁白和角色台词,每个角色需要有性别、年龄、以及每句台词的语气。并调用text_to_speech function生成音频数据。" diff --git a/rootfs/workflows/story_maker/workflow.toml b/rootfs/workflows/story_maker/workflow.toml new file mode 100644 index 0000000..30a26d6 --- /dev/null +++ b/rootfs/workflows/story_maker/workflow.toml @@ -0,0 +1,37 @@ +name = "story_maker" + + +[filter] +"*" = "manager" + +[roles.manager] +name = "manager" +fullname = "总导演" +agent="manager" +[[roles.manager.prompt]] +role="system" +content=""" +你是一个语音故事制作总导演,与客户对接并向团队下达指令。你的团队分为下面几个成员:writer,studio_director。一个故事制作分成两个阶段:让writer写出故事,再交由studio_director演播故事。你的基本工作模式是: +1. 收到客户的明确的指令后,让writer写出故事 +2. 将writer写出的故事交给studio_director演播 +3. 当你决定要和成员通信时,请使用下面形式输出需要通信的消息 +``` +##/send_msg 成员名称 +内容 +``` +""" + +[roles.writer] +name = "writer" +agent = "fairy_tale_writer" +fullname = "作家" +[[roles.writer.prompt]] +role="system" +content="" + +[roles.studio_director] +name = "studio_director" +agent = "studio_director" +[[roles.studio_director.prompt]] +role="system" +content="" diff --git a/src/aios_kernel/__init__.py b/src/aios_kernel/__init__.py index 79d4456..9eba338 100644 --- a/src/aios_kernel/__init__.py +++ b/src/aios_kernel/__init__.py @@ -19,6 +19,7 @@ from .tg_tunnel import TelegramTunnel from .email_tunnel import EmailTunnel from .storage import ResourceLocation,AIStorage,UserConfig,UserConfigItem from .contact_manager import ContactManager,Contact,FamilyMember +from .text_to_speech_function import TextToSpeechFunction AIOS_Version = "0.5.1, build 2023-9-17" diff --git a/src/aios_kernel/chatsession.py b/src/aios_kernel/chatsession.py index 5893683..ceb9821 100644 --- a/src/aios_kernel/chatsession.py +++ b/src/aios_kernel/chatsession.py @@ -35,10 +35,10 @@ class ChatSessionDB: self._create_table(conn) return conn - + def close(self): if not hasattr(self.local, 'conn'): - return + return self.local.conn.close() def _create_table(self, conn): @@ -56,7 +56,7 @@ class ChatSessionDB: # create messages table # reciver_id could be None - + conn.execute(""" CREATE TABLE IF NOT EXISTS Messages ( MessageID TEXT PRIMARY KEY, @@ -142,7 +142,7 @@ class ChatSessionDB: except Error as e: logging.error("Error occurred while inserting message: %s", e) return -1 # return -1 if an error occurs - + def get_chatsession_by_id(self, session_id): """Get a message by its ID""" conn = self._get_conn() @@ -150,7 +150,7 @@ class ChatSessionDB: c.execute("SELECT * FROM ChatSessions WHERE SessionID = ?", (session_id,)) chatsession = c.fetchone() return chatsession - + def get_chatsession_by_owner_topic(self, owner_id, topic): """Get a chatsession by its owner and topic""" conn = self._get_conn() @@ -175,7 +175,7 @@ class ChatSessionDB: except Error as e: logging.error("Error occurred while getting sessions: %s", e) return -1, None # return -1 and None if an error occurs - + def get_message_by_id(self, message_id): """Get a message by its ID""" conn =self._get_conn() @@ -216,7 +216,7 @@ class ChatSessionDB: except Error as e: logging.error("Error occurred while updating message status: %s", e) return -1 # return -1 if an error occurs - + # chat session store the chat history between owner and agent # chat session might be large, so can read / write at stream mode. @@ -230,7 +230,7 @@ class AIChatSession: # cls._dbs[db_path] = db # db.get_chatsession_by_id(session_id) # #result = AIChatSession() - + @classmethod def get_session(cls,owner_id:str,session_topic:str,db_path:str,auto_create = True) -> str: db = cls._dbs.get(db_path) @@ -249,21 +249,22 @@ class AIChatSession: result = AIChatSession(owner_id,session[0],db) result.topic = session_topic - return result - + return result + def __init__(self,owner_id:str, session_id:str, db:ChatSessionDB) -> None: self.owner_id :str = owner_id self.session_id : str = session_id self.db : ChatSessionDB = db - + self.topic : str = None self.start_time : str = None def get_owner_id(self) -> str: return self.owner_id - + def read_history(self, number:int=10,offset=0) -> [AgentMsg]: + return [] msgs = self.db.get_messages(self.session_id, number, offset) result = [] for msg in msgs: @@ -298,4 +299,4 @@ class AIChatSession: # """chat session changed event handler""" # pass - #TODO : add iterator interface for read chat history + #TODO : add iterator interface for read chat history diff --git a/src/aios_kernel/compute_kernel.py b/src/aios_kernel/compute_kernel.py index 97f9b9b..5859efe 100644 --- a/src/aios_kernel/compute_kernel.py +++ b/src/aios_kernel/compute_kernel.py @@ -7,7 +7,7 @@ from asyncio import Queue from .agent import AgentPrompt from .compute_node import ComputeNode -from .compute_task import ComputeTask, ComputeTaskState, ComputeTaskResult +from .compute_task import ComputeTask, ComputeTaskState, ComputeTaskResult, ComputeTaskType logger = logging.getLogger(__name__) @@ -23,7 +23,7 @@ class ComputeKernel: if cls._instance is None: cls._instance = ComputeKernel() return cls._instance - + def __init__(self) -> None: self.is_start = False self.task_queue = Queue() @@ -73,7 +73,7 @@ class ComputeKernel: hit_pos = random.randint(0, total_weights - 1) for i in range(min(len(support_nodes) - 1, hit_pos), -1, -1): if support_nodes[i]["pos"] <= hit_pos: - return node + return support_nodes[i]["node"] logger.warning( f"task {task.display()} is not support by any compute node") @@ -137,7 +137,7 @@ class ComputeKernel: task_req.set_text_embedding_params(input,model_name) self.run(task_req) return task_req - + async def do_text_embedding(self,input:str,model_name:Optional[str] = None) -> [float]: task_req = self.text_embedding(input,model_name) async def check_timer(): @@ -155,11 +155,45 @@ class ComputeKernel: 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 - - return "error!" - + + return "error!" + + async def do_text_to_speech(self, + input:str, + language_code:Optional[str] = None, + gender: Optional[str] = None, + age: Optional[str] = None, + voice_name: Optional[str] = None, + tone: Optional[str] = None): + task_req = ComputeTask() + task_req.params["text"] = input + task_req.params["language_code"] = language_code + task_req.params["gender"] = gender + task_req.params["age"] = age + task_req.params["voice_name"] = voice_name + task_req.params["tone"] = tone + task_req.task_type = ComputeTaskType.TEXT_2_VOICE + self.run(task_req) + + check_times = 0 + while True: + if task_req.state == ComputeTaskState.DONE: + break + if task_req.state == ComputeTaskState.ERROR: + break + if check_times >= 60: + task_req.state = ComputeTaskState.ERROR + break + await asyncio.sleep(0.5) + check_times += 1 + + if task_req.state == ComputeTaskState.DONE: + return task_req.result.result + else: + raise Exception("do_text_to_speech failed!") + diff --git a/src/aios_kernel/google_text_to_speech_node.py b/src/aios_kernel/google_text_to_speech_node.py index 7709eef..b05f276 100644 --- a/src/aios_kernel/google_text_to_speech_node.py +++ b/src/aios_kernel/google_text_to_speech_node.py @@ -21,24 +21,69 @@ see:https://cloud.google.com/text-to-speech/docs/before-you-begin class GoogleTextToSpeechNode(ComputeNode): _instance = None - def __new__(cls, *args, **kwargs): + @classmethod + def get_instance(cls): if cls._instance is None: - cls._instance = super(GoogleTextToSpeechNode, cls).__new__(cls) - cls._instance.is_start = False + cls._instance = cls() return cls._instance def __init__(self): super().__init__() - if self.is_start is True: - logger.warn("GoogleTextToSpeechNode is already start") - return - - self.is_start = True self.node_id = "google_text_to_speech_node" self.task_queue = Queue() self.client = texttospeech.TextToSpeechClient() + self.language_list = { + "cnm-CN": { + "female": ["cmn-CN-Standard-A", + "cmn-CN-Standard-D", + "cmn-CN-Wavenet-A", + "cmn-CN-Wavenet-D", + "cmn-TW-Standard-A", + "cmn-TW-Wavenet-A"], + "man": ["cmn-CN-Standard-B", + "cmn-CN-Standard-C", + "cmn-CN-Wavenet-B", + "cmn-CN-Wavenet-C", + "cmn-TW-Standard-B", + "cmn-TW-Standard-C", + "cmn-TW-Wavenet-B", + "cmn-TW-Wavenet-C"] + }, + "en-US": { + "female": ["en-US-Neural2-C", + "en-US-Neural2-E", + "en-US-Neural2-F", + "en-US-Neural2-G", + "en-US-Neural2-H", + "en-US-News-K", + "en-US-News-L", + "en-US-Standard-C", + "en-US-Standard-E", + "en-US-Standard-F", + "en-US-Standard-G", + "en-US-Standard-H", + "en-US-Studio-O", + "en-US-Wavenet-C", + "en-US-Wavenet-E", + "en-US-Wavenet-F", + "en-US-Wavenet-G", + "en-US-Wavenet-H"], + "man": ["en-US-Polyglot-1", + "en-US-Standard-A", + "en-US-Standard-B", + "en-US-Standard-D", + "en-US-Standard-I", + "en-US-Standard-J", + "en-US-Studio-M", + "en-US-Wavenet-A", + "en-US-Wavenet-B", + "en-US-Wavenet-D", + "en-US-Wavenet-I", + "en-US-Wavenet-J"] + } + } self.start() def start(self): @@ -64,10 +109,24 @@ class GoogleTextToSpeechNode(ComputeNode): task.state = ComputeTaskState.RUNNING language_code = task.params["language_code"] text = task.params["text"] + voice_name = task.params["voice_name"] + gender = task.params["gender"] + age = task.params["age"] + + if language_code == "zh": + language_code = "cnm-CN" + elif language_code == "en": + language_code = "en-US" + else: + raise Exception(f"language_code {language_code} not support") + + lang_list = self.language_list[language_code][gender] + voice = lang_list[hash(voice_name) % len(lang_list)] synthesis_input = texttospeech.SynthesisInput(text=text) voice = texttospeech.VoiceSelectionParams(language_code=language_code, - ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL) + ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL, + name=voice) audio_config = texttospeech.AudioConfig(audio_encoding=texttospeech.AudioEncoding.MP3) @@ -95,8 +154,8 @@ class GoogleTextToSpeechNode(ComputeNode): def get_capacity(self): return 0 - def is_support(self, task_type: ComputeTaskType) -> bool: - if task_type == ComputeTaskType.TEXT_2_VOICE: + def is_support(self, task: ComputeTask) -> bool: + if task.task_type == ComputeTaskType.TEXT_2_VOICE: return True return False diff --git a/src/aios_kernel/text_to_speech_function.py b/src/aios_kernel/text_to_speech_function.py new file mode 100644 index 0000000..3237974 --- /dev/null +++ b/src/aios_kernel/text_to_speech_function.py @@ -0,0 +1,101 @@ +import io +import logging +import os +import random +from typing import Dict + +from aios_kernel import ComputeKernel +from aios_kernel.ai_function import AIFunction + +from pydub import AudioSegment + +logger = logging.getLogger(__name__) + +class TextToSpeechFunction(AIFunction): + def __init__(self): + self.func_id = "text_to_speech" + self.description = "根据输入的剧本生成音频数据" + + def get_name(self) -> str: + return self.func_id + + def get_description(self) -> str: + return self.description + + def get_parameters(self) -> Dict: + return { + "type": "object", + "properties": { + "language": {"type": "string", "description": "演播语言", "enum": ["zh", "en"]}, + "roles": {"type": "array", "items": { + "type": "object", + "properties": { + "name": {"type": "string", "description": "角色名字"}, + "gender": {"type": "string", "description": "角色性别", "enum": ["man", "female"]}, + "age": {"type": "string", "description": "年龄", "enum": ["child", "adult"]}, + }}}, + "lines": {"type": "array", "items": { + "type": "object", + "properties": { + "name": {"type": "string", "description": "角色名字"}, + "tone": {"type": "string", "description": "演播情感", + "enum": ["happy", "sad", "angry", "fear", "disgust", "surprise", "neutral"]}, + "text": {"type": "string", "description": "台词"}, + } + }} + } + } + + async def execute(self, **kwargs) -> str: + logger.info(f"execute text_to_speech function: {kwargs}") + + language = kwargs.get("language") + if language is None: + language = "zh" + roles = kwargs.get("roles") + lines = kwargs.get("lines") + + audio = None + for line in lines: + name = line.get("name") + tone = line.get("tone") + text = line.get("text") + gender = None + age = None + for role in roles: + role_name = role.get("name") + if role_name == name: + gender = role.get("gender") + age = role.get("age") + break + i = 0 + while i < 3: + try: + data = await ComputeKernel.get_instance().do_text_to_speech(text, language, gender, age, name, tone) + if audio is None: + audio = AudioSegment.from_mp3(io.BytesIO(data)) + else: + audio = audio + AudioSegment.from_mp3(io.BytesIO(data)) + break + except Exception as e: + logger.error(f"do_text_to_speech failed: {e}") + i += 1 + continue + + if audio is not None: + path = os.path.join(os.curdir, "{}.mp3".format(random.sample('zyxwvutsrqponmlkjihgfedcba', 10))) + audio.export(path, format="mp3") + return "complete.file path:{}".format(path) + else: + return "failed" + + def is_local(self) -> bool: + return True + + def is_in_zone(self) -> bool: + return True + + def is_ready_only(self) -> bool: + return False + + diff --git a/src/aios_kernel/workflow_env.py b/src/aios_kernel/workflow_env.py index 120b3fb..b7211ef 100644 --- a/src/aios_kernel/workflow_env.py +++ b/src/aios_kernel/workflow_env.py @@ -7,6 +7,8 @@ from sqlite3 import Error import threading import logging from typing import Optional + +from .text_to_speech_function import TextToSpeechFunction from .environment import Environment,EnvironmentEvent from .ai_function import SimpleAIFunction from .storage import AIStorage @@ -23,8 +25,8 @@ class CalenderEvent(EnvironmentEvent): self.data = data def display(self) -> str: - return f"#event timer:{self.data}" - + return f"#event timer:{self.data}" + # AI Calender GOAL: Let user use "create notify after 2 days" to create a timer event class CalenderEnvironment(Environment): def __init__(self, env_id: str) -> None: @@ -39,7 +41,7 @@ class CalenderEnvironment(Environment): #self.add_ai_function(SimpleAIFunction("serach_events", # "search events in calender", # self._search_events)) - + get_param = { "start_time": "start time (UTC) of event", "end_time": "end time (UTC) of event" @@ -59,14 +61,14 @@ class CalenderEnvironment(Environment): self.add_ai_function(SimpleAIFunction("add_event", "add event to calender", self._add_event,add_param)) - + delete_param = { "event_id": "id of event" } self.add_ai_function(SimpleAIFunction("delete_event", "delete event from calender", self._delete_event,delete_param)) - + update_param = { "event_id": "id of event", "new_title": "new title of event", @@ -79,7 +81,7 @@ class CalenderEnvironment(Environment): self.add_ai_function(SimpleAIFunction("update_event", "update event in calender", self._update_event,update_param)) - + #self.add_ai_function(SimpleAIFunction("user_confirm", # "user confirm", # self._user_confirm)) @@ -98,7 +100,7 @@ class CalenderEnvironment(Environment): ); """) await db.commit() - + async def _add_event(self,title, start_time, end_time, participants=None, location=None, details=None): async with aiosqlite.connect(self.db_file) as db: await db.execute(""" @@ -127,7 +129,7 @@ class CalenderEnvironment(Environment): _event["details"] = row[6] result[row[0]] = _event return json.dumps(result, indent=4, sort_keys=True) - + async def _get_events_by_time_range(self,start_time, end_time): async with aiosqlite.connect(self.db_file) as db: cursor = await db.execute(""" @@ -153,7 +155,7 @@ class CalenderEnvironment(Environment): return "No event." return json.dumps(result, indent=4, sort_keys=True) - + async def _update_event(self,event_id, new_title=None, new_participants=None, new_location=None, new_details=None ,start_time=None, end_time=None): fields_to_update = [] values = [] @@ -191,8 +193,8 @@ class CalenderEnvironment(Environment): WHERE id = ?; """ - values.append(event_id) - + values.append(event_id) + async with aiosqlite.connect(self.db_file) as db: await db.execute(sql_update_query, values) await db.commit() @@ -212,16 +214,16 @@ class CalenderEnvironment(Environment): async def start(self) -> None: if self.is_run: - return + return self.is_run = True await self.init_db() - + self.register_get_handler("now",self.get_now) async def timer_loop(): while True: if self.is_run == False: break - + await asyncio.sleep(1.0) now = datetime.now() formatted_time = now.strftime('%Y-%m-%d %H:%M:%S') @@ -238,13 +240,13 @@ class CalenderEnvironment(Environment): def get_now(self)->str: now = datetime.now() formatted_time = now.strftime('%Y-%m-%d %H:%M:%S') - return formatted_time + return formatted_time async def _get_now(self) -> str: now = datetime.now() formatted_time = now.strftime('%Y-%m-%d %H:%M:%S') return formatted_time - + # Default Workflow Environment(Context) class WorkflowEnvironment(Environment): def __init__(self, env_id: str,db_file:str) -> None: @@ -252,6 +254,7 @@ class WorkflowEnvironment(Environment): self.db_file = db_file self.local = threading.local() self.table_name = "WorkflowEnv_" + env_id + self.add_ai_function(TextToSpeechFunction()) def _get_conn(self): @@ -259,7 +262,7 @@ class WorkflowEnvironment(Environment): if not hasattr(self.local, 'conn'): self.local.conn = self._create_connection() return self.local.conn - + def _create_connection(self): """ create a database connection to a SQLite database """ conn = None @@ -273,10 +276,10 @@ class WorkflowEnvironment(Environment): self._create_table(conn) return conn - + def close(self): if not hasattr(self.local, 'conn'): - return + return self.local.conn.close() def _create_table(self, conn): @@ -293,7 +296,7 @@ class WorkflowEnvironment(Environment): conn.commit() except Error as e: logging.error("Error occurred while creating tables: %s", e) - + def _do_get_value(self, key: str) -> str | None: try: conn = self._get_conn() @@ -311,7 +314,7 @@ class WorkflowEnvironment(Environment): super().set_value(key,str_value) if is_storage is False: return - + try: conn = self._get_conn() conn.execute(""" diff --git a/src/service/aios_shell/aios_shell.py b/src/service/aios_shell/aios_shell.py index 984bb08..f3df06a 100644 --- a/src/service/aios_shell/aios_shell.py +++ b/src/service/aios_shell/aios_shell.py @@ -64,17 +64,17 @@ class AIOS_Shell: target_id = msg.target.split(".")[0] agent : AIAgent = await AgentManager.get_instance().get(target_id) if agent is not None: - agent.owner_env = Environment.get_env_by_id("calender") + agent.owner_env = Environment.get_env_by_id("calender") bus.register_message_handler(target_id,agent._process_msg) return True - + a_workflow = await WorkflowManager.get_instance().get_workflow(target_id) if a_workflow is not None: bus.register_message_handler(target_id,a_workflow._process_msg) return True - + return False - + async def is_agent(self,target_id:str) -> bool: agent : AIAgent = await AgentManager.get_instance().get(target_id) if agent is not None: @@ -96,7 +96,10 @@ class AIOS_Shell: logger.error("openai node initial failed!") return False ComputeKernel.get_instance().add_compute_node(open_ai_node) - + + google_text_to_speech_node = GoogleTextToSpeechNode.get_instance() + ComputeKernel.get_instance().add_compute_node(google_text_to_speech_node) + llama_ai_node = LocalLlama_ComputeNode() await llama_ai_node.start() # ComputeKernel.get_instance().add_compute_node(llama_ai_node) @@ -116,7 +119,7 @@ class AIOS_Shell: tunnels_config_path = os.path.abspath(f"{user_data_dir}/etc/tunnels.cfg.toml") tunnel_config = None - try: + try: tunnel_config = toml.load(tunnels_config_path) if tunnel_config is not None: await AgentTunnel.load_all_tunnels_from_config(tunnel_config) @@ -156,11 +159,11 @@ class AIOS_Shell: case "email": tunnel_config["type"] = "EmailTunnel" 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) return None - intro_text = FormattedText([("class:prompt", tunnel_introduce)]) + intro_text = FormattedText([("class:prompt", tunnel_introduce)]) print_formatted_text(intro_text,style=shell_style) for key,item in intpu_table.items(): user_input = await try_get_input(f"{key} : {item.desc}") @@ -174,7 +177,7 @@ class AIOS_Shell: async def append_tunnel_config(self,tunnel_config): user_data_dir = AIStorage.get_instance().get_myai_dir() tunnels_config_path = os.path.abspath(f"{user_data_dir}/etc/tunnels.cfg.toml") - try: + try: all_tunnels = toml.load(tunnels_config_path) if all_tunnels is not None: all_tunnels[tunnel_config["tunnel_id"]] = tunnel_config @@ -255,7 +258,7 @@ class AIOS_Shell: AIStorage.get_instance().get_user_config().set_value(key,value) await AIStorage.get_instance().get_user_config().save_to_user_config() show_text = FormattedText([("class:title", f"set {key} to {value} success!")]) - + return show_text case 'connect': show_text = FormattedText([("class:title", "args error, /connect $target telegram | email")]) @@ -293,8 +296,8 @@ class AIOS_Shell: if len(args) >= 1: self.username = args[0] AIBus().get_default_bus().register_message_handler(self.username,self._user_process_msg) - - return self.username + " login success!" + + return self.username + " login success!" case 'history': num = 10 offset = 0 @@ -324,9 +327,9 @@ class AIOS_Shell: return FormattedText([("class:title", f"help~~~")]) -########################################################################################################################## +########################################################################################################################## history = FileHistory('aios_shell_history.txt') -session = PromptSession(history=history) +session = PromptSession(history=history) def parse_function_call(func_string): if len(func_string) > 2: @@ -337,7 +340,7 @@ def parse_function_call(func_string): return func_name, params else: return None - + async def try_get_input(desc:str,check_func:callable = None) -> str: user_input = await session.prompt_async(f"{desc} \nType /exit to abort. \nPlease input:",style=shell_style) err_str = "" @@ -347,13 +350,13 @@ async def try_get_input(desc:str,check_func:callable = None) -> str: return user_input else: return None - + else: is_ok,err_str = check_func(user_input) if is_ok: return user_input - - error_text = FormattedText([("class:error", err_str)]) + + error_text = FormattedText([("class:error", err_str)]) print_formatted_text(error_text,style=shell_style) return await try_get_input(desc,check_func) @@ -373,7 +376,7 @@ async def main_daemon_loop(shell:AIOS_Shell): return 0 def print_welcome_screen(): - print("\033[1;31m") + print("\033[1;31m") logo = """ \t _______ ____________________ __ \t __ __ \______________________ __ \__ |__ | / / @@ -384,9 +387,9 @@ def print_welcome_screen(): """ print(logo) - print("\033[0m") + print("\033[0m") - print("\033[1;32m \t\tWelcome to OpenDAN - Your Personal AI OS\033[0m\n") + print("\033[1;32m \t\tWelcome to OpenDAN - Your Personal AI OS\033[0m\n") introduce = """ \tThe core goal of version 0.5.1 is to turn the concept of AIOS into code and get it up and running as quickly as possible. @@ -396,12 +399,12 @@ def print_welcome_screen(): \twe intend to strengthen some components. This document will explain these changes and provide an update \ton the current development progress of MVP(0.5.1,0.5.2) -""" +""" print(introduce) - print(f"\033[1;34m \t\tVersion: {AIOS_Version}\n\033") - print("\033[1;33m \tOpenDAN is an open-source project, let's define the future of Humans and AI together.\033[0m") - print("\033[1;33m \tGithub\t: https://github.com/fiatrete/OpenDAN-Personal-AI-OS\033[0m") + print(f"\033[1;34m \t\tVersion: {AIOS_Version}\n\033") + print("\033[1;33m \tOpenDAN is an open-source project, let's define the future of Humans and AI together.\033[0m") + print("\033[1;33m \tGithub\t: https://github.com/fiatrete/OpenDAN-Personal-AI-OS\033[0m") print("\033[1;33m \tWebsite\t: https://www.opendan.ai\033[0m") print("\n\n") @@ -412,19 +415,19 @@ async def main(): logging.basicConfig(filename="aios_shell.log",filemode="w",encoding='utf-8',force=True, level=logging.INFO, format='[%(asctime)s]%(name)s[%(levelname)s]: %(message)s') - + if os.path.isdir(f"{directory}/../../../rootfs"): AIStorage.get_instance().is_dev_mode = True else: - AIStorage.get_instance().is_dev_mode = False + AIStorage.get_instance().is_dev_mode = False is_daemon = False if os.name != 'nt': if os.getppid() == 1: is_daemon = True - shell = AIOS_Shell("user") - shell.declare_all_user_config() + shell = AIOS_Shell("user") + shell.declare_all_user_config() await AIStorage.get_instance().initial() check_result = AIStorage.get_instance().get_user_config().check_config() if check_result is not None: @@ -435,7 +438,7 @@ async def main(): #Remind users to enter necessary configurations. if await get_user_config_from_input(check_result) is False: return 1 - + init_result = await shell.initial() if init_result is False: if is_daemon: @@ -451,8 +454,8 @@ async def main(): proxy.apply_storage() #TODO: read last input config - completer = WordCompleter(['/send $target $msg $topic', - '/open $target $topic', + completer = WordCompleter(['/send $target $msg $topic', + '/open $target $topic', '/history $num $offset', '/login $username', '/connect $target', @@ -462,7 +465,7 @@ async def main(): '/set_config $key', '/list_config', '/show', - '/exit', + '/exit', '/help'], ignore_case=True) current = AIStorage.get_instance().get_user_config().get_value("shell.current") @@ -470,7 +473,7 @@ async def main(): shell.current_target = current[1] shell.current_topic = current[0] - await asyncio.sleep(0.2) + await asyncio.sleep(0.2) while True: user_input = await session.prompt_async(f"{shell.username}<->{shell.current_topic}@{shell.current_target}$ ",completer=completer,style=shell_style) if len(user_input) <= 1: @@ -491,6 +494,6 @@ async def main(): #print_formatted_text(f"{shell.username}<->{shell.current_topic}@{shell.current_target} >>> {resp}",style=shell_style) -if __name__ == "__main__": +if __name__ == "__main__": asyncio.run(main())