change /knowledge commands in shell

This commit is contained in:
tsukasa
2023-10-20 14:19:41 +08:00
parent 030077e4d3
commit d0b74545eb
13 changed files with 279 additions and 74 deletions
+1
View File
@@ -23,5 +23,6 @@ from .local_stability_node import Local_Stability_ComputeNode
from .stability_node import Stability_ComputeNode
from .local_st_compute_node import LocalSentenceTransformer_Text_ComputeNode,LocalSentenceTransformer_Image_ComputeNode
from .compute_node_config import ComputeNodeConfig
from .ai_function import SimpleAIFunction
AIOS_Version = "0.5.1, build 2023-9-28"
+2 -1
View File
@@ -122,7 +122,8 @@ class AIAgent:
self.contact_prompt_str = config["contact_prompt"]
if config.get("owner_env") is not None:
self.owner_env = Environment.get_env_by_id(config["owner_env"])
self.owner_env = config.get("owner_env")
if config.get("powerby") is not None:
self.powerby = config["powerby"]
+15 -1
View File
@@ -1,9 +1,11 @@
import logging
import toml
import os
import runpy
from typing import Any, Callable, Dict, List, Optional, Union
from aios_kernel import AIAgent,AIAgentTemplete,AIStorage
from aios_kernel import AIAgent,AIAgentTemplete,AIStorage,Environment
from package_manager import PackageEnv,PackageEnvManager,PackageMediaInfo,PackageInstallTask
logger = logging.getLogger(__name__)
@@ -105,6 +107,18 @@ class AgentManager:
config_data = await config_file.read()
config = toml.loads(config_data)
result_agent = AIAgent()
if "owner_env" in config:
owner_env = config["owner_env"]
_, ext = os.path.splitext(owner_env)
if ext == ".py":
env_path = os.path.join(agent_media.full_path, owner_env)
owner_env = runpy.run_path(env_path)["init"]()
config["owner_env"] = owner_env
else:
owner_env = Environment.get_env_by_id(config["owner_env"])
config["owner_env"] = owner_env
if result_agent.load_from_config(config) is False:
logger.error(f"load agent from {agent_media} failed!")
return None
@@ -30,16 +30,19 @@ class KnowledgeDirSource:
return await f.read()
async def next(self):
journals = self.env.journal.latest_journals(1)
from_time = 0
if len(journals) == 1:
latest_journal = journals[0]
if latest_journal.is_finish():
yield None
from_time = os.path.getctime(latest_journal.get_input())
if os.path.getmtime(self.path()) <= from_time:
yield (None, None)
while True:
journals = self.env.journal.latest_journals(1)
from_time = 0
if len(journals) == 1:
latest_journal = journals[0]
if latest_journal.is_finish():
yield None
continue
from_time = os.path.getctime(latest_journal.get_input())
if os.path.getmtime(self.path()) <= from_time:
yield (None, None)
continue
file_pathes = sorted(os.listdir(self.path()), key=lambda x: os.path.getctime(os.path.join(self.path(), x)))
for rel_path in file_pathes:
file_path = os.path.join(self.path(), rel_path)
+18 -2
View File
@@ -4,7 +4,17 @@ import toml
import asyncio
from knowledge import KnowledgePipelineEnvironment, KnowledgePipeline
class KnowledgePipelineManager:
@classmethod
def initial(cls, root_dir: str):
cls._instance = KnowledgePipelineManager(root_dir)
return cls._instance
@classmethod
def get_instance(cls):
return cls._instance
def __init__(self, root_dir: str):
self.root_dir = root_dir
self.input_modules = {}
@@ -32,7 +42,7 @@ class KnowledgePipelineManager:
input_module = config["input"]["module"]
_, ext = os.path.splitext(input_module)
if ext == ".py":
input_module = os.path.abspath(path, input_module)
input_module = os.path.join(path, input_module)
input_init = runpy.run_path(input_module)["init"]
else:
input_init = self.input_modules.get(input_module)
@@ -41,7 +51,7 @@ class KnowledgePipelineManager:
parser_module = config["parser"]["module"]
_, ext = os.path.splitext(parser_module)
if ext == ".py":
parser_module = os.path.abspath(path, parser_module)
parser_module = os.path.join(path, parser_module)
parser_init = runpy.run_path(parser_module)["init"]
else:
parser_init = self.parser_modules.get(parser_module)
@@ -54,6 +64,12 @@ class KnowledgePipelineManager:
self.pipelines["names"][name] = pipeline
self.pipelines["running"].append(pipeline)
def get_pipelines(self) -> [KnowledgePipeline]:
return self.pipelines["running"]
def get_pipeline(self, name: str) -> KnowledgePipeline:
return self.pipelines["names"].get(name)
async def run(self):
while True:
for pipeline in self.pipelines["running"]:
@@ -1,82 +0,0 @@
class KnowledgeEnvironment(Environment):
def __init__(self, env_id: str) -> None:
super().__init__(env_id)
query_param = {
"tokens": "key words to query",
"types": "prefered knowledge types, one or more of [text, image]",
"index": "index of query result"
}
self.add_ai_function(SimpleAIFunction("query_knowledge",
"vector query content from local knowledge base",
self._query,
query_param))
async def query_objects(self, tokens: str, types: list[str], topk: int) -> [ObjectID]:
texts = []
if "text" in types:
vector = await self.compute_kernel.do_text_embedding(tokens, self._default_text_model)
texts = await self.store.get_vector_store(self._default_text_model).query(vector, topk)
images = []
if "image" in types:
vector = await self.compute_kernel.do_text_embedding(tokens, self._default_image_model)
images = await self.store.get_vector_store(self._default_image_model).query(vector, topk)
return texts + images
def tokens_from_objects(self, object_ids: [ObjectID]) -> list[str]:
results = dict()
for object_id in object_ids:
parents = self.store.get_relation_store().get_related_root_objects(object_id)
# last parent is the root object
root_object_id = parents[0] if parents else object_id
logging.info(f"object_id: {str(object_id)} root_object_id: {str(root_object_id)}")
if str(root_object_id) in results:
results[str(root_object_id)].append(object_id)
else:
results[str(root_object_id)] = [root_object_id, object_id]
content = ""
result_desc = []
for result in results.values():
# first element in result is the root object
root_object_id = result[0]
if root_object_id.get_object_type() == ObjectType.Email:
email = self.load_object(root_object_id)
desc = email.get_desc()
desc["type"] = "email"
desc["contents"] = []
result_desc.append(desc)
upper_list = desc["contents"]
result = result[1:]
else:
upper_list = result_desc
for object_id in result:
if object_id.get_object_type() == ObjectType.Chunk:
upper_list.append({"type": "text", "content": self.store.get_chunk_reader().get_chunk(object_id).read().decode("utf-8")})
if object_id.get_object_type() == ObjectType.Image:
# image = self.load_object(object_id)
desc = dict()
desc["id"] = str(object_id)
desc["type"] = "image"
upper_list.append(desc)
if object_id.get_object_type() == ObjectType.Video:
video = self.load_object(object_id)
desc = video.get_desc()
desc["type"] = "video"
upper_list.append(desc)
else:
pass
content += json.dumps(result_desc)
content += ".\n"
return content
async def _query(self, tokens: str, types: list[str] = ["text"], index: str=0):
index = int(index)
object_ids = await KnowledgeBase().query_objects(tokens, types, 4)
if len(object_ids) <= index:
return "*** I have no more information for your reference.\n"
else:
content = "*** I have provided the following known information for your reference with json format:\n"
return content + KnowledgeBase().tokens_from_objects(object_ids[index:index+1])
+14
View File
@@ -19,6 +19,12 @@ class KnowledgePipelineJournal:
def get_parser(self) -> str:
return self.parser
def __str__(self) -> str:
if self.is_finish():
return f"{self.time}: finished)"
else:
return f"{self.time}: object:{self.object_id} input:{self.input}, parser:{self.parser})"
# init sqlite3 client
class KnowledgePipelineJournalClient:
@@ -84,6 +90,12 @@ class KnowledgePipeline:
self.env = env
self.input = None
self.parser = None
def get_name(self):
return self.name
def get_journal(self) -> KnowledgePipelineJournalClient:
return self.env.journal
async def run(self):
if self.state == KnowledgePipelineState.INIT:
@@ -100,6 +112,8 @@ class KnowledgePipeline:
if object_id is not None:
parser_journal = await self.parser.parse(object_id)
self.env.journal.insert(object_id, input_journal, parser_journal)
else:
return
if self.state == KnowledgePipelineState.STOPPED:
return
if self.state == KnowledgePipelineState.FINISHED:
+17 -42
View File
@@ -27,6 +27,7 @@ sys.path.append(directory + '/../../')
import proxy
from aios_kernel import *
from knowledge import *
sys.path.append(directory + '/../../component/')
@@ -186,7 +187,7 @@ class AIOS_Shell:
AIBus().get_default_bus().register_message_handler(self.username,self._user_process_msg)
pipelines = KnowledgePipelineManager(os.path.join(AIStorage().get_instance().get_myai_dir(), "knowledge/pipelines"))
pipelines = KnowledgePipelineManager.initial(os.path.join(AIStorage().get_instance().get_myai_dir(), "knowledge/pipelines"))
pipelines.load_dir(os.path.join(AIStorage().get_instance().get_system_app_dir(), "knowledge_pipelines"))
pipelines.load_dir(os.path.join(AIStorage().get_instance().get_myai_dir(), "knowledge_pipelines"))
asyncio.create_task(pipelines.run())
@@ -333,46 +334,21 @@ class AIOS_Shell:
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"
"/knowledge journal [$topn]\n"
show_text = FormattedText([("class:title", "sub command not support!\n"
"/knowledge pipelines\n"
"/knowledge journal $pipeline [$topn]\n"
"/knowledge query $object_id\n")])
if len(args) < 1:
return show_text
sub_cmd = args[0]
if sub_cmd == "add":
if len(args) < 2:
return show_text
if args[1] == "email":
config = dict()
for key, item in KnowledgeEmailSource.user_config_items():
user_input = await try_get_input(f"{key} : {item}")
if user_input is None:
return show_text
config[key] = user_input
error = KnowledgePipline.get_instance().add_email_source(KnowledgeEmailSource(config))
if error is not None:
return FormattedText([("class:title", f"/knowledge add email failed {error}\n")])
else:
KnowledgePipline.get_instance().save_cosnfig()
if args[1] == "dir":
config = dict()
for key, item in KnowledgeDirSource.user_config_items():
user_input = await try_get_input(f"{key} : {item}")
if user_input is None:
return show_text
config[key] = user_input
error = KnowledgePipline.get_instance().add_dir_source(KnowledgeDirSource(config))
if error is not None:
return FormattedText([("class:title", f"/knowledge add dir failed {error}\n")])
else:
KnowledgePipline.get_instance().save_config()
else:
return show_text
if sub_cmd == "pipelines":
pipelines = KnowledgePipelineManager.get_instance().get_pipelines()
print_formatted_text("\r\n".join(pipeline.get_name() for pipeline in pipelines))
if sub_cmd == "journal":
topn = 10 if len(args) == 1 else int(args[1])
journals = [str(journal) for journal in KnowledgePipline.get_instance().get_latest_journals(topn)]
print_formatted_text("\r\n".join(journals))
name = args[1]
topn = 10 if len(args) == 2 else int(args[2])
journals = [str(journal) for journal in KnowledgePipelineManager.get_instance().get_pipeline(name).get_journal().latest_journals(topn)]
print_formatted_text("\r\n".join(str(journal) for journal in journals))
if sub_cmd == "query":
if len(args) < 2:
return show_text
@@ -381,8 +357,8 @@ class AIOS_Shell:
if object_id.get_object_type() == ObjectType.Image:
from PIL import Image
import io
image = KnowledgeBase().load_object(object_id)
image_data = KnowledgeBase().bytes_from_object(image)
image = KnowledgeStore().load_object(object_id)
image_data = KnowledgeStore().bytes_from_object(image)
image = Image.open(io.BytesIO(image_data))
image.show()
@@ -671,9 +647,8 @@ def print_welcome_screen():
\033[1;94m\tGive your Agent a Telegram account :\033[0m /connect $agent_name
\033[1;94m\tAdd personal files to the AI Knowledge Base. \033[0m
\t\t1) Copy your file to ~/myai/data
\t\t2) /knowlege add dir
\033[1;94m\tSearch your knowledge base :\033[0m /open Mia
\033[1;94m\tCheck the progress of AI reading personal data :\033[0m /knowledge journal
\033[1;94m\tCheck the progress of AI reading personal data :\033[0m /knowledge $pipeline journal
\033[1;94m\tQuery object with ID in knowledge base :\033[0m /knowledge query $object_id
\033[1;94m\tOpen AI Bash (For Developer Only):\033[0m /open ai_bash
\033[1;94m\tEnable AIGC Feature :\033[0m /enable aigc
@@ -752,8 +727,8 @@ async def main():
'/history $num $offset',
'/connect $target',
'/contact $name',
'/knowledge add email | dir',
'/knowledge journal [$topn]',
'/knowledge pipelines',
'/knowledge journal $pipeline [$topn]',
'/knowledge query $object_id',
'/set_config $key',
'/enable $feature',