story maker

This commit is contained in:
wugren
2023-09-21 15:52:56 +08:00
parent 270debef67
commit 4e45130140
10 changed files with 341 additions and 88 deletions
@@ -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 = "你是一个童话做作家,能够写出各种有趣的童话。"
+7
View File
@@ -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生成音频数据。"
@@ -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=""
+1
View File
@@ -19,6 +19,7 @@ from .tg_tunnel import TelegramTunnel
from .email_tunnel import EmailTunnel from .email_tunnel import EmailTunnel
from .storage import ResourceLocation,AIStorage,UserConfig,UserConfigItem from .storage import ResourceLocation,AIStorage,UserConfig,UserConfigItem
from .contact_manager import ContactManager,Contact,FamilyMember from .contact_manager import ContactManager,Contact,FamilyMember
from .text_to_speech_function import TextToSpeechFunction
AIOS_Version = "0.5.1, build 2023-9-17" AIOS_Version = "0.5.1, build 2023-9-17"
+14 -13
View File
@@ -35,10 +35,10 @@ class ChatSessionDB:
self._create_table(conn) self._create_table(conn)
return conn return conn
def close(self): def close(self):
if not hasattr(self.local, 'conn'): if not hasattr(self.local, 'conn'):
return return
self.local.conn.close() self.local.conn.close()
def _create_table(self, conn): def _create_table(self, conn):
@@ -56,7 +56,7 @@ class ChatSessionDB:
# create messages table # create messages table
# reciver_id could be None # reciver_id could be None
conn.execute(""" conn.execute("""
CREATE TABLE IF NOT EXISTS Messages ( CREATE TABLE IF NOT EXISTS Messages (
MessageID TEXT PRIMARY KEY, MessageID TEXT PRIMARY KEY,
@@ -142,7 +142,7 @@ class ChatSessionDB:
except Error as e: except Error as e:
logging.error("Error occurred while inserting message: %s", e) logging.error("Error occurred while inserting message: %s", e)
return -1 # return -1 if an error occurs return -1 # return -1 if an error occurs
def get_chatsession_by_id(self, session_id): def get_chatsession_by_id(self, session_id):
"""Get a message by its ID""" """Get a message by its ID"""
conn = self._get_conn() conn = self._get_conn()
@@ -150,7 +150,7 @@ class ChatSessionDB:
c.execute("SELECT * FROM ChatSessions WHERE SessionID = ?", (session_id,)) c.execute("SELECT * FROM ChatSessions WHERE SessionID = ?", (session_id,))
chatsession = c.fetchone() chatsession = c.fetchone()
return chatsession return chatsession
def get_chatsession_by_owner_topic(self, owner_id, topic): def get_chatsession_by_owner_topic(self, owner_id, topic):
"""Get a chatsession by its owner and topic""" """Get a chatsession by its owner and topic"""
conn = self._get_conn() conn = self._get_conn()
@@ -175,7 +175,7 @@ class ChatSessionDB:
except Error as e: except Error as e:
logging.error("Error occurred while getting sessions: %s", e) logging.error("Error occurred while getting sessions: %s", e)
return -1, None # return -1 and None if an error occurs return -1, None # return -1 and None if an error occurs
def get_message_by_id(self, message_id): def get_message_by_id(self, message_id):
"""Get a message by its ID""" """Get a message by its ID"""
conn =self._get_conn() conn =self._get_conn()
@@ -216,7 +216,7 @@ class ChatSessionDB:
except Error as e: except Error as e:
logging.error("Error occurred while updating message status: %s", e) logging.error("Error occurred while updating message status: %s", e)
return -1 # return -1 if an error occurs return -1 # return -1 if an error occurs
# 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.
@@ -230,7 +230,7 @@ class AIChatSession:
# cls._dbs[db_path] = db # cls._dbs[db_path] = db
# db.get_chatsession_by_id(session_id) # db.get_chatsession_by_id(session_id)
# #result = AIChatSession() # #result = AIChatSession()
@classmethod @classmethod
def get_session(cls,owner_id:str,session_topic:str,db_path:str,auto_create = True) -> str: def get_session(cls,owner_id:str,session_topic:str,db_path:str,auto_create = True) -> str:
db = cls._dbs.get(db_path) db = cls._dbs.get(db_path)
@@ -249,21 +249,22 @@ class AIChatSession:
result = AIChatSession(owner_id,session[0],db) result = AIChatSession(owner_id,session[0],db)
result.topic = session_topic result.topic = session_topic
return result return result
def __init__(self,owner_id:str, session_id:str, db:ChatSessionDB) -> None: def __init__(self,owner_id:str, session_id:str, db:ChatSessionDB) -> None:
self.owner_id :str = owner_id self.owner_id :str = owner_id
self.session_id : str = session_id self.session_id : str = session_id
self.db : ChatSessionDB = db self.db : ChatSessionDB = db
self.topic : str = None self.topic : str = None
self.start_time : str = None self.start_time : str = None
def get_owner_id(self) -> str: def get_owner_id(self) -> str:
return self.owner_id return self.owner_id
def read_history(self, number:int=10,offset=0) -> [AgentMsg]: def read_history(self, number:int=10,offset=0) -> [AgentMsg]:
return []
msgs = self.db.get_messages(self.session_id, number, offset) msgs = self.db.get_messages(self.session_id, number, offset)
result = [] result = []
for msg in msgs: for msg in msgs:
@@ -298,4 +299,4 @@ class AIChatSession:
# """chat session changed event handler""" # """chat session changed event handler"""
# pass # pass
#TODO : add iterator interface for read chat history #TODO : add iterator interface for read chat history
+42 -8
View File
@@ -7,7 +7,7 @@ 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 from .compute_task import ComputeTask, ComputeTaskState, ComputeTaskResult, ComputeTaskType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -23,7 +23,7 @@ class ComputeKernel:
if cls._instance is None: if cls._instance is None:
cls._instance = ComputeKernel() cls._instance = ComputeKernel()
return cls._instance return cls._instance
def __init__(self) -> None: def __init__(self) -> None:
self.is_start = False self.is_start = False
self.task_queue = Queue() self.task_queue = Queue()
@@ -73,7 +73,7 @@ class ComputeKernel:
hit_pos = random.randint(0, total_weights - 1) hit_pos = random.randint(0, total_weights - 1)
for i in range(min(len(support_nodes) - 1, hit_pos), -1, -1): for i in range(min(len(support_nodes) - 1, hit_pos), -1, -1):
if support_nodes[i]["pos"] <= hit_pos: if support_nodes[i]["pos"] <= hit_pos:
return node return support_nodes[i]["node"]
logger.warning( logger.warning(
f"task {task.display()} is not support by any compute node") 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) task_req.set_text_embedding_params(input,model_name)
self.run(task_req) self.run(task_req)
return task_req return task_req
async def do_text_embedding(self,input:str,model_name:Optional[str] = None) -> [float]: async def do_text_embedding(self,input:str,model_name:Optional[str] = None) -> [float]:
task_req = self.text_embedding(input,model_name) task_req = self.text_embedding(input,model_name)
async def check_timer(): async def check_timer():
@@ -155,11 +155,45 @@ class ComputeKernel:
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
check_times += 1 check_times += 1
await asyncio.create_task(check_timer()) await asyncio.create_task(check_timer())
if task_req.state == ComputeTaskState.DONE: if task_req.state == ComputeTaskState.DONE:
return task_req.result.result 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!")
+70 -11
View File
@@ -21,24 +21,69 @@ see:https://cloud.google.com/text-to-speech/docs/before-you-begin
class GoogleTextToSpeechNode(ComputeNode): class GoogleTextToSpeechNode(ComputeNode):
_instance = None _instance = None
def __new__(cls, *args, **kwargs): @classmethod
def get_instance(cls):
if cls._instance is None: if cls._instance is None:
cls._instance = super(GoogleTextToSpeechNode, cls).__new__(cls) cls._instance = cls()
cls._instance.is_start = False
return cls._instance return cls._instance
def __init__(self): def __init__(self):
super().__init__() 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.node_id = "google_text_to_speech_node"
self.task_queue = Queue() self.task_queue = Queue()
self.client = texttospeech.TextToSpeechClient() 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() self.start()
def start(self): def start(self):
@@ -64,10 +109,24 @@ class GoogleTextToSpeechNode(ComputeNode):
task.state = ComputeTaskState.RUNNING task.state = ComputeTaskState.RUNNING
language_code = task.params["language_code"] language_code = task.params["language_code"]
text = task.params["text"] 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) synthesis_input = texttospeech.SynthesisInput(text=text)
voice = texttospeech.VoiceSelectionParams(language_code=language_code, 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) audio_config = texttospeech.AudioConfig(audio_encoding=texttospeech.AudioEncoding.MP3)
@@ -95,8 +154,8 @@ class GoogleTextToSpeechNode(ComputeNode):
def get_capacity(self): def get_capacity(self):
return 0 return 0
def is_support(self, task_type: ComputeTaskType) -> bool: def is_support(self, task: ComputeTask) -> bool:
if task_type == ComputeTaskType.TEXT_2_VOICE: if task.task_type == ComputeTaskType.TEXT_2_VOICE:
return True return True
return False return False
+101
View File
@@ -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
+24 -21
View File
@@ -7,6 +7,8 @@ from sqlite3 import Error
import threading import threading
import logging import logging
from typing import Optional from typing import Optional
from .text_to_speech_function import TextToSpeechFunction
from .environment import Environment,EnvironmentEvent from .environment import Environment,EnvironmentEvent
from .ai_function import SimpleAIFunction from .ai_function import SimpleAIFunction
from .storage import AIStorage from .storage import AIStorage
@@ -23,8 +25,8 @@ class CalenderEvent(EnvironmentEvent):
self.data = data self.data = data
def display(self) -> str: 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 # AI Calender GOAL: Let user use "create notify after 2 days" to create a timer event
class CalenderEnvironment(Environment): class CalenderEnvironment(Environment):
def __init__(self, env_id: str) -> None: def __init__(self, env_id: str) -> None:
@@ -39,7 +41,7 @@ class CalenderEnvironment(Environment):
#self.add_ai_function(SimpleAIFunction("serach_events", #self.add_ai_function(SimpleAIFunction("serach_events",
# "search events in calender", # "search events in calender",
# self._search_events)) # self._search_events))
get_param = { get_param = {
"start_time": "start time (UTC) of event", "start_time": "start time (UTC) of event",
"end_time": "end 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", self.add_ai_function(SimpleAIFunction("add_event",
"add event to calender", "add event to calender",
self._add_event,add_param)) self._add_event,add_param))
delete_param = { delete_param = {
"event_id": "id of event" "event_id": "id of event"
} }
self.add_ai_function(SimpleAIFunction("delete_event", self.add_ai_function(SimpleAIFunction("delete_event",
"delete event from calender", "delete event from calender",
self._delete_event,delete_param)) self._delete_event,delete_param))
update_param = { update_param = {
"event_id": "id of event", "event_id": "id of event",
"new_title": "new title of event", "new_title": "new title of event",
@@ -79,7 +81,7 @@ class CalenderEnvironment(Environment):
self.add_ai_function(SimpleAIFunction("update_event", self.add_ai_function(SimpleAIFunction("update_event",
"update event in calender", "update event in calender",
self._update_event,update_param)) self._update_event,update_param))
#self.add_ai_function(SimpleAIFunction("user_confirm", #self.add_ai_function(SimpleAIFunction("user_confirm",
# "user confirm", # "user confirm",
# self._user_confirm)) # self._user_confirm))
@@ -98,7 +100,7 @@ class CalenderEnvironment(Environment):
); );
""") """)
await db.commit() await db.commit()
async def _add_event(self,title, start_time, end_time, participants=None, location=None, details=None): 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: async with aiosqlite.connect(self.db_file) as db:
await db.execute(""" await db.execute("""
@@ -127,7 +129,7 @@ class CalenderEnvironment(Environment):
_event["details"] = row[6] _event["details"] = row[6]
result[row[0]] = _event result[row[0]] = _event
return json.dumps(result, indent=4, sort_keys=True) return json.dumps(result, indent=4, sort_keys=True)
async def _get_events_by_time_range(self,start_time, end_time): async def _get_events_by_time_range(self,start_time, end_time):
async with aiosqlite.connect(self.db_file) as db: async with aiosqlite.connect(self.db_file) as db:
cursor = await db.execute(""" cursor = await db.execute("""
@@ -153,7 +155,7 @@ class CalenderEnvironment(Environment):
return "No event." return "No event."
return json.dumps(result, indent=4, sort_keys=True) 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): 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 = [] fields_to_update = []
values = [] values = []
@@ -191,8 +193,8 @@ class CalenderEnvironment(Environment):
WHERE id = ?; WHERE id = ?;
""" """
values.append(event_id) values.append(event_id)
async with aiosqlite.connect(self.db_file) as db: async with aiosqlite.connect(self.db_file) as db:
await db.execute(sql_update_query, values) await db.execute(sql_update_query, values)
await db.commit() await db.commit()
@@ -212,16 +214,16 @@ class CalenderEnvironment(Environment):
async def start(self) -> None: async def start(self) -> None:
if self.is_run: if self.is_run:
return return
self.is_run = True self.is_run = True
await self.init_db() await self.init_db()
self.register_get_handler("now",self.get_now) self.register_get_handler("now",self.get_now)
async def timer_loop(): async def timer_loop():
while True: while True:
if self.is_run == False: if self.is_run == False:
break break
await asyncio.sleep(1.0) await asyncio.sleep(1.0)
now = datetime.now() now = datetime.now()
formatted_time = now.strftime('%Y-%m-%d %H:%M:%S') formatted_time = now.strftime('%Y-%m-%d %H:%M:%S')
@@ -238,13 +240,13 @@ class CalenderEnvironment(Environment):
def get_now(self)->str: def get_now(self)->str:
now = datetime.now() now = datetime.now()
formatted_time = now.strftime('%Y-%m-%d %H:%M:%S') formatted_time = now.strftime('%Y-%m-%d %H:%M:%S')
return formatted_time return formatted_time
async def _get_now(self) -> str: async def _get_now(self) -> str:
now = datetime.now() now = datetime.now()
formatted_time = now.strftime('%Y-%m-%d %H:%M:%S') formatted_time = now.strftime('%Y-%m-%d %H:%M:%S')
return formatted_time return formatted_time
# Default Workflow Environment(Context) # Default Workflow Environment(Context)
class WorkflowEnvironment(Environment): class WorkflowEnvironment(Environment):
def __init__(self, env_id: str,db_file:str) -> None: def __init__(self, env_id: str,db_file:str) -> None:
@@ -252,6 +254,7 @@ class WorkflowEnvironment(Environment):
self.db_file = db_file self.db_file = db_file
self.local = threading.local() self.local = threading.local()
self.table_name = "WorkflowEnv_" + env_id self.table_name = "WorkflowEnv_" + env_id
self.add_ai_function(TextToSpeechFunction())
def _get_conn(self): def _get_conn(self):
@@ -259,7 +262,7 @@ class WorkflowEnvironment(Environment):
if not hasattr(self.local, 'conn'): if not hasattr(self.local, 'conn'):
self.local.conn = self._create_connection() self.local.conn = self._create_connection()
return self.local.conn return self.local.conn
def _create_connection(self): def _create_connection(self):
""" create a database connection to a SQLite database """ """ create a database connection to a SQLite database """
conn = None conn = None
@@ -273,10 +276,10 @@ class WorkflowEnvironment(Environment):
self._create_table(conn) self._create_table(conn)
return conn return conn
def close(self): def close(self):
if not hasattr(self.local, 'conn'): if not hasattr(self.local, 'conn'):
return return
self.local.conn.close() self.local.conn.close()
def _create_table(self, conn): def _create_table(self, conn):
@@ -293,7 +296,7 @@ class WorkflowEnvironment(Environment):
conn.commit() conn.commit()
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 _do_get_value(self, key: str) -> str | None: def _do_get_value(self, key: str) -> str | None:
try: try:
conn = self._get_conn() conn = self._get_conn()
@@ -311,7 +314,7 @@ class WorkflowEnvironment(Environment):
super().set_value(key,str_value) super().set_value(key,str_value)
if is_storage is False: if is_storage is False:
return return
try: try:
conn = self._get_conn() conn = self._get_conn()
conn.execute(""" conn.execute("""
+38 -35
View File
@@ -64,17 +64,17 @@ class AIOS_Shell:
target_id = msg.target.split(".")[0] target_id = msg.target.split(".")[0]
agent : AIAgent = await AgentManager.get_instance().get(target_id) agent : AIAgent = await AgentManager.get_instance().get(target_id)
if agent is not None: 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) bus.register_message_handler(target_id,agent._process_msg)
return True return True
a_workflow = await WorkflowManager.get_instance().get_workflow(target_id) a_workflow = await WorkflowManager.get_instance().get_workflow(target_id)
if a_workflow is not None: if a_workflow is not None:
bus.register_message_handler(target_id,a_workflow._process_msg) bus.register_message_handler(target_id,a_workflow._process_msg)
return True return True
return False return False
async def is_agent(self,target_id:str) -> bool: async def is_agent(self,target_id:str) -> bool:
agent : AIAgent = await AgentManager.get_instance().get(target_id) agent : AIAgent = await AgentManager.get_instance().get(target_id)
if agent is not None: if agent is not None:
@@ -96,7 +96,10 @@ class AIOS_Shell:
logger.error("openai node initial failed!") logger.error("openai node initial failed!")
return False return False
ComputeKernel.get_instance().add_compute_node(open_ai_node) 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() llama_ai_node = LocalLlama_ComputeNode()
await llama_ai_node.start() await llama_ai_node.start()
# ComputeKernel.get_instance().add_compute_node(llama_ai_node) # 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") tunnels_config_path = os.path.abspath(f"{user_data_dir}/etc/tunnels.cfg.toml")
tunnel_config = None tunnel_config = None
try: try:
tunnel_config = toml.load(tunnels_config_path) tunnel_config = toml.load(tunnels_config_path)
if tunnel_config is not None: if tunnel_config is not None:
await AgentTunnel.load_all_tunnels_from_config(tunnel_config) await AgentTunnel.load_all_tunnels_from_config(tunnel_config)
@@ -156,11 +159,11 @@ class AIOS_Shell:
case "email": case "email":
tunnel_config["type"] = "EmailTunnel" tunnel_config["type"] = "EmailTunnel"
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)
return None return None
intro_text = FormattedText([("class:prompt", tunnel_introduce)]) intro_text = FormattedText([("class:prompt", tunnel_introduce)])
print_formatted_text(intro_text,style=shell_style) print_formatted_text(intro_text,style=shell_style)
for key,item in intpu_table.items(): for key,item in intpu_table.items():
user_input = await try_get_input(f"{key} : {item.desc}") 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): async def append_tunnel_config(self,tunnel_config):
user_data_dir = AIStorage.get_instance().get_myai_dir() user_data_dir = AIStorage.get_instance().get_myai_dir()
tunnels_config_path = os.path.abspath(f"{user_data_dir}/etc/tunnels.cfg.toml") tunnels_config_path = os.path.abspath(f"{user_data_dir}/etc/tunnels.cfg.toml")
try: try:
all_tunnels = toml.load(tunnels_config_path) all_tunnels = toml.load(tunnels_config_path)
if all_tunnels is not None: if all_tunnels is not None:
all_tunnels[tunnel_config["tunnel_id"]] = tunnel_config 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) AIStorage.get_instance().get_user_config().set_value(key,value)
await AIStorage.get_instance().get_user_config().save_to_user_config() await AIStorage.get_instance().get_user_config().save_to_user_config()
show_text = FormattedText([("class:title", f"set {key} to {value} success!")]) show_text = FormattedText([("class:title", f"set {key} to {value} success!")])
return show_text return show_text
case 'connect': case 'connect':
show_text = FormattedText([("class:title", "args error, /connect $target telegram | email")]) show_text = FormattedText([("class:title", "args error, /connect $target telegram | email")])
@@ -293,8 +296,8 @@ class AIOS_Shell:
if len(args) >= 1: if len(args) >= 1:
self.username = args[0] self.username = args[0]
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)
return self.username + " login success!" return self.username + " login success!"
case 'history': case 'history':
num = 10 num = 10
offset = 0 offset = 0
@@ -324,9 +327,9 @@ class AIOS_Shell:
return FormattedText([("class:title", f"help~~~")]) return FormattedText([("class:title", f"help~~~")])
########################################################################################################################## ##########################################################################################################################
history = FileHistory('aios_shell_history.txt') history = FileHistory('aios_shell_history.txt')
session = PromptSession(history=history) session = PromptSession(history=history)
def parse_function_call(func_string): def parse_function_call(func_string):
if len(func_string) > 2: if len(func_string) > 2:
@@ -337,7 +340,7 @@ def parse_function_call(func_string):
return func_name, params return func_name, params
else: else:
return None return None
async def try_get_input(desc:str,check_func:callable = None) -> str: 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) user_input = await session.prompt_async(f"{desc} \nType /exit to abort. \nPlease input:",style=shell_style)
err_str = "" err_str = ""
@@ -347,13 +350,13 @@ async def try_get_input(desc:str,check_func:callable = None) -> str:
return user_input return user_input
else: else:
return None return None
else: else:
is_ok,err_str = check_func(user_input) is_ok,err_str = check_func(user_input)
if is_ok: if is_ok:
return user_input 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) print_formatted_text(error_text,style=shell_style)
return await try_get_input(desc,check_func) return await try_get_input(desc,check_func)
@@ -373,7 +376,7 @@ async def main_daemon_loop(shell:AIOS_Shell):
return 0 return 0
def print_welcome_screen(): def print_welcome_screen():
print("\033[1;31m") print("\033[1;31m")
logo = """ logo = """
\t _______ ____________________ __ \t _______ ____________________ __
\t __ __ \______________________ __ \__ |__ | / / \t __ __ \______________________ __ \__ |__ | / /
@@ -384,9 +387,9 @@ def print_welcome_screen():
""" """
print(logo) 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 = """ 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. \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 \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) \ton the current development progress of MVP(0.5.1,0.5.2)
""" """
print(introduce) print(introduce)
print(f"\033[1;34m \t\tVersion: {AIOS_Version}\n\033") 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 \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 \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("\033[1;33m \tWebsite\t: https://www.opendan.ai\033[0m")
print("\n\n") print("\n\n")
@@ -412,19 +415,19 @@ async def main():
logging.basicConfig(filename="aios_shell.log",filemode="w",encoding='utf-8',force=True, logging.basicConfig(filename="aios_shell.log",filemode="w",encoding='utf-8',force=True,
level=logging.INFO, level=logging.INFO,
format='[%(asctime)s]%(name)s[%(levelname)s]: %(message)s') format='[%(asctime)s]%(name)s[%(levelname)s]: %(message)s')
if os.path.isdir(f"{directory}/../../../rootfs"): if os.path.isdir(f"{directory}/../../../rootfs"):
AIStorage.get_instance().is_dev_mode = True AIStorage.get_instance().is_dev_mode = True
else: else:
AIStorage.get_instance().is_dev_mode = False AIStorage.get_instance().is_dev_mode = False
is_daemon = False is_daemon = False
if os.name != 'nt': if os.name != 'nt':
if os.getppid() == 1: if os.getppid() == 1:
is_daemon = True is_daemon = True
shell = AIOS_Shell("user") shell = AIOS_Shell("user")
shell.declare_all_user_config() shell.declare_all_user_config()
await AIStorage.get_instance().initial() await AIStorage.get_instance().initial()
check_result = AIStorage.get_instance().get_user_config().check_config() check_result = AIStorage.get_instance().get_user_config().check_config()
if check_result is not None: if check_result is not None:
@@ -435,7 +438,7 @@ async def main():
#Remind users to enter necessary configurations. #Remind users to enter necessary configurations.
if await get_user_config_from_input(check_result) is False: if await get_user_config_from_input(check_result) is False:
return 1 return 1
init_result = await shell.initial() init_result = await shell.initial()
if init_result is False: if init_result is False:
if is_daemon: if is_daemon:
@@ -451,8 +454,8 @@ async def main():
proxy.apply_storage() proxy.apply_storage()
#TODO: read last input config #TODO: read last input config
completer = WordCompleter(['/send $target $msg $topic', completer = WordCompleter(['/send $target $msg $topic',
'/open $target $topic', '/open $target $topic',
'/history $num $offset', '/history $num $offset',
'/login $username', '/login $username',
'/connect $target', '/connect $target',
@@ -462,7 +465,7 @@ async def main():
'/set_config $key', '/set_config $key',
'/list_config', '/list_config',
'/show', '/show',
'/exit', '/exit',
'/help'], ignore_case=True) '/help'], ignore_case=True)
current = AIStorage.get_instance().get_user_config().get_value("shell.current") 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_target = current[1]
shell.current_topic = current[0] shell.current_topic = current[0]
await asyncio.sleep(0.2) await asyncio.sleep(0.2)
while True: while True:
user_input = await session.prompt_async(f"{shell.username}<->{shell.current_topic}@{shell.current_target}$ ",completer=completer,style=shell_style) 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: 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) #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()) asyncio.run(main())