diff --git a/rootfs/agents/Jarvis/agent.toml b/rootfs/agents/Jarvis/agent.toml index 7e3fd71..e6da761 100644 --- a/rootfs/agents/Jarvis/agent.toml +++ b/rootfs/agents/Jarvis/agent.toml @@ -1,9 +1,11 @@ instance_id = "Jarvis" fullname = "Jarvis" +llm_model_name = "gpt-3.5-turbo-16k-0613" [[prompt]] role = "system" content = """ +现在是{now} 你叫Jarvis,是我的超级私人助理。 你领导一个团队为我服务,团队的成员有: Tracy,私人英语老师 diff --git a/src/aios_kernel/agent.py b/src/aios_kernel/agent.py index 5bb3027..d673371 100644 --- a/src/aios_kernel/agent.py +++ b/src/aios_kernel/agent.py @@ -198,8 +198,6 @@ class AIAgent: if self.owner_env is None: return None - return None - all_inner_function = self.owner_env.get_all_ai_functions() if all_inner_function is None: return None @@ -219,15 +217,20 @@ class AIAgent: func_name = inenr_func_call_node.get("name") arguments = json.loads(inenr_func_call_node.get("arguments")) + logger.info(f"llm execute inner func:{func_name} ({json.dumps(arguments)})") func_node : AIFunction = self.owner_env.get_ai_function(func_name) if func_node is None: return "execute failed,function not found" ineternal_call_record = AgentMsg.create_internal_call_msg(func_name,arguments,org_msg.get_msg_id(),org_msg.target) + try: + result_str:str = await func_node.execute(**arguments) + except Exception as e: + result_str = "call error:" + str(e) + logger.error(f"llm execute inner func:{func_name} error:{e}") + - result_str:str = await func_node.execute(**arguments) - inner_functions = self._get_inner_functions() prompt.messages.append({"role":"function","content":result_str,"name":func_name}) task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions) @@ -242,6 +245,17 @@ class AIAgent: else: return task_result.result_str + async def _get_agent_prompt(self) -> AgentPrompt: + return self.prompt + + def _format_msg_by_env_value(self,prompt:AgentPrompt): + if self.owner_env is None: + return + + for msg in prompt.messages: + old_content = msg.get("content") + msg["content"] = old_content.format_map(self.owner_env) + async def _process_msg(self,msg:AgentMsg) -> AgentMsg: from .compute_kernel import ComputeKernel from .bus import AIBus @@ -255,7 +269,7 @@ class AIAgent: return None prompt = AgentPrompt() - prompt.append(self.prompt) + prompt.append(await self._get_agent_prompt()) # prompt.append(self._get_knowlege_prompt(the_role.get_name())) prompt.append(await self._get_prompt_from_session(chatsession)) # chat context @@ -263,6 +277,7 @@ class AIAgent: msg_prompt.messages = [{"role":"user","content":msg.body}] prompt.append(msg_prompt) + self._format_msg_by_env_value(prompt) inner_functions = self._get_inner_functions() task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions) diff --git a/src/aios_kernel/agent_message.py b/src/aios_kernel/agent_message.py index 4c19a7a..49daf3a 100644 --- a/src/aios_kernel/agent_message.py +++ b/src/aios_kernel/agent_message.py @@ -35,7 +35,7 @@ class AgentMsgStatus(Enum): class AgentMsg: def __init__(self,msg_type=AgentMsgType.TYPE_MSG) -> None: - self.msg_id = "" + self.msg_id = "msg#" + uuid.uuid4().hex self.msg_type:AgentMsgType = msg_type self.prev_msg_id:str = None @@ -89,7 +89,6 @@ class AgentMsg: def create_resp_msg(self,resp_body): resp_msg = AgentMsg() - resp_msg.msg_id = "msg#" + uuid.uuid4().hex resp_msg.create_time = time.time() resp_msg.rely_msg_id = self.msg_id @@ -101,7 +100,6 @@ class AgentMsg: return resp_msg def set(self,sender:str,target:str,body:str,topic:str=None) -> None: - self.msg_id = "msg#" + uuid.uuid4().hex self.sender = sender self.target = target self.body = body diff --git a/src/aios_kernel/ai_function.py b/src/aios_kernel/ai_function.py index ae5df52..bca41d2 100644 --- a/src/aios_kernel/ai_function.py +++ b/src/aios_kernel/ai_function.py @@ -1,6 +1,13 @@ from abc import ABC, abstractmethod from typing import Dict,Coroutine,Callable +class ParameterDefine: + def __init__(self) -> None: + self.name = None + self.type = None + self.description = None + + class AIFunction: def __init__(self) -> None: self.description : str = None @@ -107,9 +114,19 @@ class SimpleAIFunction(AIFunction): def get_parameters(self) -> Dict: if self.parameters is not None: - return self.parameters + result = {} + result["type"] = "object" + parm_defines = {} + for parm,desc in self.parameters.items(): + parm_item = {} + parm_item["type"] = "string" + parm_item["description"] = desc + parm_defines[parm] = parm_item + result["properties"] = parm_defines + return result return {"type": "object", "properties": {}} + async def execute(self,**kwargs) -> str: if self.func_handler is None: return "error: function not implemented" diff --git a/src/aios_kernel/open_ai_node.py b/src/aios_kernel/open_ai_node.py index c30fc87..857f2c7 100644 --- a/src/aios_kernel/open_ai_node.py +++ b/src/aios_kernel/open_ai_node.py @@ -167,8 +167,12 @@ class OpenAI_ComputeNode(ComputeNode): def is_support(self, task: ComputeTask) -> bool: if task.task_type == ComputeTaskType.LLM_COMPLETION: - if not task.params["model_name"] or task.params["model_name"] == "gpt-4-0613": + if not task.params["model_name"]: return True + model_name : str = task.params["model_name"] + if model_name.startswith("gpt-"): + return True + if task.task_type == ComputeTaskType.TEXT_EMBEDDING: if task.params["model_name"] == "text-embedding-ada-002": return True diff --git a/src/aios_kernel/workflow_env.py b/src/aios_kernel/workflow_env.py index 4ef033b..6223ae3 100644 --- a/src/aios_kernel/workflow_env.py +++ b/src/aios_kernel/workflow_env.py @@ -1,6 +1,7 @@ from datetime import datetime import asyncio +import json import sqlite3 # Because sqlite3 IO operation is small, so we can use sqlite3 directly.(so we don't need to use async sqlite3 now) from sqlite3 import Error import threading @@ -8,6 +9,9 @@ import logging from typing import Optional from .environment import Environment,EnvironmentEvent from .ai_function import SimpleAIFunction +from .storage import AIStorage + +import aiosqlite logger = logging.getLogger(__name__) @@ -25,20 +29,187 @@ class CalenderEvent(EnvironmentEvent): class CalenderEnvironment(Environment): def __init__(self, env_id: str) -> None: super().__init__(env_id) + self.db_file = AIStorage.get_instance().get_myai_dir() / "calender.db" self.is_run = False self.add_ai_function(SimpleAIFunction("get_time", "get current time", self._get_now)) + #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" + } + self.add_ai_function(SimpleAIFunction("get_events", + "get events in calender by time range", + self._get_events_by_time_range,get_param)) + + add_param = { + "title": "title of event", + "start_time": "start time (UTC) of event", + "end_time": "end time (UTC) of event", + "participants": "participants of event", + "location": "location of event", + "details": "details of event" + } + 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", + "new_participants": "new participants of event", + "new_location": "new location of event", + "new_details": "new details of event", + "start_time": "new start time (UTC) of event", + "end_time": "new end time (UTC) of event" + } + 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)) + + async def init_db(self): + async with aiosqlite.connect(self.db_file) as db: + await db.execute(""" + CREATE TABLE IF NOT EXISTS events ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT, + start_time DATETIME, + end_time DATETIME, + participants TEXT, + location TEXT, + details TEXT + ); + """) + 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(""" + INSERT INTO events (title, start_time, end_time, participants, location, details) + VALUES (?, ?, ?, ?, ?, ?); + """, (title, start_time, end_time, participants, location, details)) + await db.commit() + return "Add event ok" + + async def _search_events(self,query): + async with aiosqlite.connect(self.db_file) as db: + cursor = await db.execute(""" + SELECT id,title, start_time, end_time, participants, location, details FROM events + WHERE title LIKE ? OR participants LIKE ? OR location LIKE ? OR details LIKE ?; + """, (f"%{query}%", f"%{query}%", f"%{query}%", f"%{query}%")) + rows = await cursor.fetchall() + + result = {} + for row in rows: + _event = {} + _event["title"] = row[1] + _event["start_time"] = row[2] + _event["end_time"] = row[3] + _event["participants"] = row[4] + _event["location"] = row[5] + _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(""" + SELECT id,title, start_time, end_time, participants, location, details FROM events + WHERE start_time >= ? AND end_time <= ?; + """, (start_time, end_time)) + rows = await cursor.fetchall() + + result = {} + for row in rows: + _event = {} + _event["title"] = row[1] + _event["start_time"] = row[2] + _event["end_time"] = row[3] + _event["participants"] = row[4] + _event["location"] = row[5] + _event["details"] = row[6] + result[row[0]] = _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 = [] + + if new_title is not None: + fields_to_update.append("title = ?") + values.append(new_title) + + if new_participants is not None: + fields_to_update.append("participants = ?") + values.append(new_participants) + + if new_location is not None: + fields_to_update.append("location = ?") + values.append(new_location) + + if new_details is not None: + fields_to_update.append("details = ?") + values.append(new_details) + + if start_time is not None: + fields_to_update.append("start_time = ?") + values.append(start_time) + + if end_time is not None: + fields_to_update.append("end_time = ?") + values.append(end_time) + + if not fields_to_update: + return "No fields to update." + + sql_update_query = f""" + UPDATE events + SET {', '.join(fields_to_update)} + WHERE id = ?; + """ + + values.append(event_id) + + async with aiosqlite.connect(self.db_file) as db: + await db.execute(sql_update_query, values) + await db.commit() + return "update ok" + + async def _delete_event(self,event_id): + async with aiosqlite.connect(self.db_file) as db: + await db.execute(""" + DELETE FROM events + WHERE id = ?; + """, (event_id,)) + await db.commit() + return "Delete event ok" + def _do_get_value(self,key:str) -> Optional[str]: return None - def start(self) -> None: + async def start(self) -> None: if self.is_run: return self.is_run = True - + await self.init_db() + self.register_get_handler("now",self.get_now) async def timer_loop(): while True: diff --git a/src/aios_kernel/worksapce_env.py b/src/aios_kernel/worksapce_env.py index e69de29..d8611f2 100644 --- a/src/aios_kernel/worksapce_env.py +++ b/src/aios_kernel/worksapce_env.py @@ -0,0 +1,2 @@ +# this env is designed for workflow owner filesystem, support file/directory operations + diff --git a/src/component/agent_manager/agent_manager.py b/src/component/agent_manager/agent_manager.py index 5d20cf6..92bad31 100644 --- a/src/component/agent_manager/agent_manager.py +++ b/src/component/agent_manager/agent_manager.py @@ -1,6 +1,7 @@ import logging import toml +from typing import Any, Callable, Dict, List, Optional, Union from aios_kernel import AIAgent,AIAgentTemplete,AIStorage from package_manager import PackageEnv,PackageEnvManager,PackageMediaInfo,PackageInstallTask @@ -17,6 +18,12 @@ class AgentManager: cls._instance = AgentManager() return cls._instance + def __init__(self) -> None: + self.agent_templete_env : PackageEnv = None + self.agent_env : PackageEnv = None + self.db_path : str = None + self.loaded_agent_instance : Dict[str,AIAgent] = None + def initial(self) -> None: system_app_dir = AIStorage.get_instance().get_system_app_dir() user_data_dir = AIStorage.get_instance().get_myai_dir() @@ -27,6 +34,9 @@ class AgentManager: self.loaded_agent_instance = {} if self.agent_templete_env is None: raise Exception("agent_manager initial failed") + + async def scan_all_agent(self)->None: + pass async def get(self,agent_id:str) -> AIAgent: diff --git a/src/requirements.txt b/src/requirements.txt index b9e630e..574b9ea 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -19,3 +19,4 @@ base58 google-cloud-texttospeech openai Pillow +aiosqlite diff --git a/src/service/aios_shell/aios_shell.py b/src/service/aios_shell/aios_shell.py index 8ebc71d..13a5ee8 100644 --- a/src/service/aios_shell/aios_shell.py +++ b/src/service/aios_shell/aios_shell.py @@ -81,7 +81,7 @@ class AIOS_Shell: async def initial(self) -> bool: cal_env = CalenderEnvironment("calender") - cal_env.start() + await cal_env.start() Environment.set_env_by_id("calender",cal_env) AgentManager.get_instance().initial() @@ -409,7 +409,7 @@ async def main(): 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) + 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: continue