shell knowledge commands

This commit is contained in:
tsukasa
2023-09-21 18:32:17 +08:00
parent edbd1c41da
commit a0a45b8998
11 changed files with 409 additions and 74 deletions
+105
View File
@@ -4,3 +4,108 @@
# 2023-09-19 16:05:27.714815 # 2023-09-19 16:05:27.714815
+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 +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
# 2023-09-20 14:48:06.181582
+/connect
# 2023-09-20 14:48:22.678945
+/connect Jarvis email
# 2023-09-20 14:54:35.101026
+/connect Jarvis telegram
# 2023-09-20 14:54:55.229801
+token abcd
# 2023-09-20 16:22:34.229084
+/knowledge add email
# 2023-09-20 16:22:46.797842
+puotosssa@live.com
# 2023-09-20 16:22:53.622015
+p19870626
# 2023-09-20 16:22:59.186145
+imap@live.com
# 2023-09-20 16:23:02.334411
+993
# 2023-09-20 16:40:29.996329
+/knowledge add email
# 2023-09-20 16:40:52.860700
+puotosssa@live.com
# 2023-09-20 16:40:55.324994
+fdafda
# 2023-09-20 16:40:58.167154
+fdafdaf
# 2023-09-20 16:40:59.757363
+993
# 2023-09-20 16:42:55.288680
+/knowledge add email
# 2023-09-20 16:42:56.916408
+fdfa
# 2023-09-20 16:42:57.965232
+fdfds
# 2023-09-20 16:42:59.149516
+fdafd
# 2023-09-20 16:43:00.949104
+993
# 2023-09-21 14:11:33.500467
+/knowledge add c:/users/tsukasa/myai/photos
# 2023-09-21 14:14:31.488794
+/knowledge add dir
# 2023-09-21 14:14:49.298635
+c:/users/tsukasa/myai/photos
# 2023-09-21 15:27:58.550658
+/knowledge add dir
# 2023-09-21 15:28:10.190467
+c:/users/tsukasa/myai/photos
# 2023-09-21 15:32:32.431305
+/knowledge add dir
# 2023-09-21 15:32:46.695566
+c:/users/tsukasa/myai/photos
# 2023-09-21 15:33:36.391465
+/knowledge add dir
# 2023-09-21 15:33:47.574853
+c:/users/tsukasa/myai/photos
# 2023-09-21 17:44:26.308692
+/knowledge journals
# 2023-09-21 17:45:42.925359
+/knowledge journal
# 2023-09-21 18:13:08.720054
+/knowledge query what a see is
# 2023-09-21 18:20:09.275556
+/knowledge journal
# 2023-09-21 18:20:10.746056
+/knowledge query what a see is
# 2023-09-21 18:29:48.391223
+/knowledge journal
# 2023-09-21 18:29:51.406532
+/knowledge query what a see is
+1 -1
View File
@@ -6,7 +6,7 @@ from .compute_kernel import ComputeKernel,ComputeTask
from .compute_node import ComputeNode,LocalComputeNode from .compute_node import ComputeNode,LocalComputeNode
from .open_ai_node import OpenAI_ComputeNode from .open_ai_node import OpenAI_ComputeNode
from .knowledge_base import KnowledgeBase from .knowledge_base import KnowledgeBase
from .knowledge_pipeline import EmailSpider from .knowledge_pipeline import KnowledgeEmailSource, KnowledgeDirSource, KnowledgePipline
from .role import AIRole,AIRoleGroup from .role import AIRole,AIRoleGroup
from .workflow import Workflow from .workflow import Workflow
from .bus import AIBus from .bus import AIBus
+6 -3
View File
@@ -1,7 +1,9 @@
# define a knowledge base class # define a knowledge base class
import json import json
import logging import logging
from . import AgentPrompt, ComputeKernel, AIStorage from .agent import AgentPrompt
from .compute_kernel import ComputeKernel
from .storage import AIStorage
from knowledge import * from knowledge import *
@@ -17,7 +19,7 @@ class KnowledgeBase:
def __singleton_init__(self) -> None: def __singleton_init__(self) -> None:
self.store = KnowledgeStore() self.store = KnowledgeStore()
self.compute_kernel = ComputeKernel() self.compute_kernel = ComputeKernel.get_instance()
async def __embedding_document(self, document: DocumentObject): async def __embedding_document(self, document: DocumentObject):
for chunk_id in document.get_chunk_list(): for chunk_id in document.get_chunk_list():
@@ -155,7 +157,7 @@ class KnowledgeBase:
# pass # pass
async def insert_object(self, object: KnowledgeObject): async def insert_object(self, object: KnowledgeObject):
# self.__save_object(object) self.store.get_object_store().put_object(object.calculate_id(), object.encode())
await self.__do_embedding(object) await self.__do_embedding(object)
async def query_prompt(self, prompt: AgentPrompt): async def query_prompt(self, prompt: AgentPrompt):
@@ -164,6 +166,7 @@ class KnowledgeBase:
knowledge_prompt = self.prompt_from_objects(objects) knowledge_prompt = self.prompt_from_objects(objects)
logging.info(f"prompt_from_objects result: {knowledge_prompt.as_str()}") logging.info(f"prompt_from_objects result: {knowledge_prompt.as_str()}")
prompt.append(knowledge_prompt) prompt.append(knowledge_prompt)
return prompt
async def query_objects(self, prompt: AgentPrompt) -> [ObjectID]: async def query_objects(self, prompt: AgentPrompt) -> [ObjectID]:
results = [] results = []
+223 -47
View File
@@ -15,10 +15,10 @@ An example of a local file is as follows:
│ └── dd-login-service-min.png │ └── dd-login-service-min.png
""" """
import asyncio
import datetime
import sqlite3
import imaplib import imaplib
import os
import toml
import logging import logging
import mailparser import mailparser
import hashlib import hashlib
@@ -26,63 +26,139 @@ import json
import base64 import base64
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import requests import requests
import os
import toml
from .storage import AIStorage, UserConfigItem
from .knowledge_base import KnowledgeBase, ImageObjectBuilder, ObjectID, ObjectType, DocumentObjectBuilder
class EmailSpider: class KnowledgeJournal:
def __init__(self, source_type: str, source_id: str, item_id: str, object_id: str, timestamp=None):
# define a timestamp variable
self.timestamp = datetime.datetime.now() if timestamp is None else timestamp
self.object_id = object_id
self.source_type = source_type
self.source_id = source_id
self.item_id = item_id
def __str__(self) -> str:
if self.source_type == "dir":
object_id = ObjectID.from_base58(self.object_id)
object_type = None
if object_id.get_object_type() == ObjectType.Image:
object_type = "image"
else:
pass
return f"Add {object_type} from {os.path.join(self.source_id, self.item_id)}"
if self.source_type == "email":
pass
# init sqlite3 client
class KnowledgeJournalClient:
def __init__(self): def __init__(self):
# logger config knowledge_dir = os.path.join(AIStorage.get_instance().get_myai_dir(), "knowledge")
self.logger = logging.getLogger('email spider') if not os.path.exists(knowledge_dir):
self.logger.setLevel(logging.DEBUG) os.makedirs(knowledge_dir)
ch = logging.StreamHandler() self.journal_path = os.path.join(knowledge_dir, "journal.db")
formatter = logging.Formatter('%(asctime)s [%(name)s] [%(levelname)s] %(message)s')
ch.setFormatter(formatter)
self.logger.addHandler(ch)
conn = sqlite3.connect(self.journal_path)
conn.execute(
'''CREATE TABLE IF NOT EXISTS journal (
id INTEGER PRIMARY KEY AUTOINCREMENT,
time DATETIME DEFAULT CURRENT_TIMESTAMP,
source_type TEXT,
source_id TEXT,
item_id TEXT,
object_id TEXT)'''
)
conn.commit()
def insert(self, journal: KnowledgeJournal):
conn = sqlite3.connect(self.journal_path)
conn.execute(
"INSERT INTO journal (time, source_type, source_id, item_id, object_id) VALUES (?, ?, ?, ?, ?)",
(journal.timestamp, journal.source_type, journal.source_id, journal.item_id, journal.object_id),
)
conn.commit()
def latest_journal(self, source_id: str) -> KnowledgeJournal:
conn = sqlite3.connect(self.journal_path)
cursor = conn.cursor()
cursor.execute("SELECT * FROM journal WHERE source_id = ? ORDER BY id DESC LIMIT 1", (source_id,))
result = cursor.fetchone()
if result is None:
return None
else:
(_, timestamp, source_type, sorce_id, item_id, object_id) = result
return KnowledgeJournal(source_type, sorce_id, item_id, object_id, timestamp)
def latest_journals(self, topn) -> [KnowledgeJournal]:
conn = sqlite3.connect(self.journal_path)
cursor = conn.cursor()
cursor.execute("SELECT * FROM journal ORDER BY id DESC LIMIT ?", (topn,))
return [KnowledgeJournal(source_type, sorce_id, item_id, object_id, timestamp) for (_, timestamp, source_type, sorce_id, item_id, object_id) in cursor.fetchall()]
class KnowledgeEmailSource:
def __init__(self, config:dict):
self.config = config
self.config["type"] = "email"
def id(self):
"::".join([self.config["imap_server"], self.config["address"]])
@classmethod
def user_config_items(cls):
return [("address", "email address"),
("password", "email password"),
("imap_server", "imap server"),
("imap_port", "imap port")
]
@classmethod
def local_root(cls):
user_data_dir = AIStorage.get_instance().get_myai_dir()
return os.path.abspath(f"{user_data_dir}/email")
async def run_once(self):
# read config from toml file # read config from toml file
# and read from config config.local.toml if exists (config.local.toml is ignored by git) # and read from config config.local.toml if exists (config.local.toml is ignored by git)
self.config = toml.load('./rootfs/email/config.toml')
if os.path.exists('./rootfs/email/config.local.toml'):
self.config = toml.load('./rootfs/email/config.local.toml')
self.client = self.email_client() self.client = self.email_client()
await self.read_emails()
def email_client(self) -> imaplib.IMAP4_SSL: def email_client(self) -> imaplib.IMAP4_SSL:
self.logger.info(f"read email config from {self.config.get('EMAIL_IMAP_SERVER')}") logging.info(f"read email config from {self.config.get('imap_server')}")
client = imaplib.IMAP4_SSL( client = imaplib.IMAP4_SSL(
host=self.config.get('EMAIL_IMAP_SERVER'), host=self.config.get('imap_server'),
port=self.config.get('EMAIL_IMAP_PORT') port=self.config.get('imap_port')
) )
client.login(self.config.get('EMAIL_ADDRESS'), self.config.get('EMAIL_PASSWORD')) client.login(self.config.get('address'), self.config.get('password'))
return client return client
def list_box(self): async def read_emails(self, folder: str = 'INBOX', imap_keyword: str = "UNSEEN"):
_, mailbox_list = self.client.list()
for mailbox in mailbox_list:
print(mailbox.decode())
def read_emails(self, folder: str = 'INBOX', imap_keyword: str = "UNSEEN"):
self.client.select(folder) self.client.select(folder)
_, data = self.client.uid('search', None, imap_keyword) _, data = self.client.uid('search', None, imap_keyword)
# get email uid list # get email uid list
email_list = data[0].split() email_list = data[0].split()
self.logger.info(f"got {len(email_list)} emails") logging.info(f"got {len(email_list)} emails")
email_list.reverse() email_list.reverse()
for uid in email_list: for uid in email_list:
if self.check_email_saved(uid): if self.check_email_saved(uid):
self.logger.info(f"email uid {uid} already saved") logging.info(f"email uid {uid} already saved")
else: else:
self.read_and_save_email(uid) self.read_and_save_email(uid)
self.logger.info(f"email uid {uid} saved") logging.info(f"email uid {uid} saved")
def read_and_save_email(self, uid: str): def read_and_save_email(self, uid: str):
message_parts = "(BODY.PEEK[])" message_parts = "(BODY.PEEK[])"
_, email_data = self.client.uid('fetch', uid, message_parts) _, email_data = self.client.uid('fetch', uid, message_parts)
mail = mailparser.parse_from_bytes(email_data[0][1]) mail = mailparser.parse_from_bytes(email_data[0][1])
self.logger.info(f"got email subject [{mail.subject}]") logging.info(f"got email subject [{mail.subject}]")
self.save_email(mail) self.save_email(mail)
def get_local_dir_name(self, mail: mailparser.MailParser) -> str: def get_local_dir_name(self, mail: mailparser.MailParser) -> str:
dir = f"{self.config.get('LOCAL_DIR')}/{self.config.get('EMAIL_ADDRESS')}" dir = f"{self.local_root()}/{self.config.get('address')}"
name = f"{mail.subject}__{mail.date}" name = f"{mail.subject}__{mail.date}"
name = hashlib.md5(name.encode('utf-8')).hexdigest() name = hashlib.md5(name.encode('utf-8')).hexdigest()
return f"{dir}/{name}" return f"{dir}/{name}"
@@ -91,9 +167,9 @@ class EmailSpider:
message_parts = "(BODY[HEADER])" message_parts = "(BODY[HEADER])"
_, email_data = self.client.uid('fetch', uid, message_parts) _, email_data = self.client.uid('fetch', uid, message_parts)
mail = mailparser.parse_from_bytes(email_data[0][1]) mail = mailparser.parse_from_bytes(email_data[0][1])
self.logger.info(f"[{uid}]check email subject [{mail.subject}]") logging.info(f"[{uid}]check email subject [{mail.subject}]")
dir = self.get_local_dir_name(mail) dir = self.get_local_dir_name(mail)
self.logger.info(f"check email saved {dir}") logging.info(f"check email saved {dir}")
file = f"{dir}/email.txt" file = f"{dir}/email.txt"
if os.path.exists(file): if os.path.exists(file):
return False return False
@@ -116,7 +192,7 @@ class EmailSpider:
image_data = image_data.encode() image_data = image_data.encode()
with open(filefullname, 'wb') as f: with open(filefullname, 'wb') as f:
f.write(image_data) f.write(image_data)
self.logger.info(f"save email image {filename} success") logging.info(f"save email image {filename} success")
# save email body images(html content) # save email body images(html content)
def save_body_images(self, html_content: str, email_dir: str): def save_body_images(self, html_content: str, email_dir: str):
@@ -124,7 +200,7 @@ class EmailSpider:
soup = BeautifulSoup(html_content, 'html.parser') soup = BeautifulSoup(html_content, 'html.parser')
img_tags = soup.find_all('img') img_tags = soup.find_all('img')
img_urls = [img['src'] for img in img_tags if 'src' in img.attrs] img_urls = [img['src'] for img in img_tags if 'src' in img.attrs]
self.logger.info(f'Found {len(img_urls)} images in email body') logging.info(f'Found {len(img_urls)} images in email body')
if not os.path.exists(email_dir): if not os.path.exists(email_dir):
os.makedirs(email_dir) os.makedirs(email_dir)
@@ -138,17 +214,17 @@ class EmailSpider:
with open(img_filename, 'wb') as img_file: with open(img_filename, 'wb') as img_file:
for chunk in response.iter_content(1024): for chunk in response.iter_content(1024):
img_file.write(chunk) img_file.write(chunk)
self.logger.info(f'Downloaded {img_url} to {img_filename}') logging.info(f'Downloaded {img_url} to {img_filename}')
else: else:
self.logger.info(f'Failed to download {img_url}') logging.info(f'Failed to download {img_url}')
# save email content to local dir # save email content to local dir
def save_email(self, mail: mailparser.MailParser): def save_email(self, mail: mailparser.MailParser):
dir = f"{self.config.get('LOCAL_DIR')}/{self.config.get('EMAIL_ADDRESS')}" dir = f"{self.local_root()}/{self.config.get('address')}"
if not os.path.exists(dir): if not os.path.exists(dir):
os.makedirs(dir) os.makedirs(dir)
email_dir = self.get_local_dir_name(mail) email_dir = self.get_local_dir_name(mail)
self.logger.info(f"save email to {email_dir}") logging.info(f"save email to {email_dir}")
if not os.path.exists(email_dir): if not os.path.exists(email_dir):
os.makedirs(email_dir) os.makedirs(email_dir)
with open(f"{email_dir}/email.txt", "w") as f: with open(f"{email_dir}/email.txt", "w") as f:
@@ -158,14 +234,58 @@ class EmailSpider:
if 'body' in mail_dict: if 'body' in mail_dict:
del mail_dict['body'] del mail_dict['body']
json.dump(mail_dict, f, ensure_ascii=False, indent=4) json.dump(mail_dict, f, ensure_ascii=False, indent=4)
self.logger.info(f"save email meta info {f.name}") logging.info(f"save email meta info {f.name}")
self.save_email_attachment(mail, email_dir) self.save_email_attachment(mail, email_dir)
self.save_body_images(mail.body, f"{email_dir}/body_image") self.save_body_images(mail.body, f"{email_dir}/body_image")
class KnowledgeDirSource:
def __init__(self, config):
self.config = config
self.config["type"] = "dir"
@classmethod
def user_config_items(cls):
return [("path", "local dir path")]
def id(self):
return self.config["path"]
def path(self):
return self.config["path"]
async def run_once(self):
logging.debug(f"knowledge dir source {self.id()} run once")
journal_client = KnowledgeJournalClient()
latest_journal = journal_client.latest_journal(self.id())
if latest_journal is not None:
if os.path.getmtime(self.path()) <= latest_journal.timestamp:
logging.debug(f"knowledge dir source {self.id()} ingnored for no update")
return
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)
timestamp = os.path.getctime(file_path)
if latest_journal is not None:
if timestamp <= latest_journal.timestamp:
continue
ext = os.path.splitext(file_path)[1].lower()
if ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp']:
logging.info(f"knowledge dir source {self.id()} found image file {file_path}")
image = ImageObjectBuilder({}, {}, file_path).build()
await KnowledgeBase().insert_object(image)
journal_client.insert(KnowledgeJournal("dir", self.id(), rel_path, str(image.calculate_id()), timestamp))
if ext in ['.txt']:
logging.info(f"knowledge dir source {self.id()} found text file {file_path}")
with open(file_path, "r", encoding="utf-8") as f:
text = f.read()
document = DocumentObjectBuilder({}, {}, text).build()
await KnowledgeBase().insert_object(document)
journal_client.insert(KnowledgeJournal("dir", self.id(), rel_path, str(document.calculate_id()), timestamp))
from . import AIStorage, KnowledgeBase
# define singleton class knowledge pipline # define singleton class knowledge pipline
class KnowledgePipline: class KnowledgePipline:
@@ -179,14 +299,70 @@ class KnowledgePipline:
return cls._instance return cls._instance
def __singleton_init__(self) -> None: def initial(self):
config_path = self.__config_path()
logging.info(f"initial knowledge pipline from {config_path}")
if os.path.exists(config_path):
config = toml.load(self.__config_path())
for source_config in config["sources"]:
if source_config['type'] == 'email':
self.add_email_source(KnowledgeEmailSource(source_config))
if source_config['type'] == 'dir':
self.add_dir_source(KnowledgeDirSource(source_config))
def __singleton_init__(self):
self.knowledge_base = KnowledgeBase() self.knowledge_base = KnowledgeBase()
self.email_sources = dict()
self.dir_sources = dict()
self.source_queue = list()
self.run_lock = asyncio.Lock()
asyncio.create_task(self.run_loop())
def save_config(self):
config = dict()
config["sources"] = [source.config for source in self.source_queue]
with open(self.__config_path(), "w") as f:
toml.dump(config, f)
@classmethod @classmethod
def declare_user_config(cls): def __config_path(cls) -> str:
user_config = AIStorage.get_instance().get_user_config() user_data_dir = AIStorage.get_instance().get_myai_dir()
user_config.add_user_config("email_spiders","email addresses to build knowledge base",True,None,"list") return os.path.abspath(f"{user_data_dir}/etc/knowledge.cfg.toml")
user_config.add_user_config("personal_dirs", "personal directories to build knowledge base", True, None, "list")
def add_email_address(self, ) -> None:
pass def add_email_source(self, source: KnowledgeEmailSource):
if self.email_sources.get(source.id()) is not None:
return "already exists"
self.email_sources[source.id()] = source
self.source_queue.append(source)
return None
def add_dir_source(self, source: KnowledgeDirSource):
if self.dir_sources.get(source.id()) is not None:
logging.info(f"knowledge add source {source.id()} failed for already exists")
return "already exists"
logging.info(f"knowledge added source {source.id()}")
self.dir_sources[source.id()] = source
self.source_queue.append(source)
return None
def get_latest_journals(self, topn) -> [KnowledgeJournal]:
return KnowledgeJournalClient().latest_journals(topn)
async def run_loop(self):
while True:
await self.run_once()
await asyncio.sleep(5)
async def run_once(self):
logging.info(f"knowledge pipeline started")
# sources = list()
# async with self.run_lock:
# for source in self.source_queue:
# sources.append(source)
# for source in sources:
# await source.run_once()
for source in self.source_queue:
await source.run_once()
+2 -2
View File
@@ -49,7 +49,7 @@ class DocumentObjectBuilder:
self.text = text self.text = text
return self return self
def build(self, relation_store: ObjectRelationStore) -> DocumentObject: def build(self) -> DocumentObject:
chunk_list = KnowledgeStore().get_chunk_list_writer().create_chunk_list_from_text( chunk_list = KnowledgeStore().get_chunk_list_writer().create_chunk_list_from_text(
self.text, self.text,
1024 * 4, 1024 * 4,
@@ -60,6 +60,6 @@ class DocumentObjectBuilder:
# Add relation to store # Add relation to store
for chunk_id in chunk_list.chunk_list: for chunk_id in chunk_list.chunk_list:
relation_store.add_relation(chunk_id, doc_id) KnowledgeStore().get_relation_store().add_relation(chunk_id, doc_id)
return doc return doc
+1 -1
View File
@@ -94,7 +94,7 @@ class EmailObjectBuilder:
with open(content_file, "r", encoding="utf-8") as f: with open(content_file, "r", encoding="utf-8") as f:
text = f.read() text = f.read()
document = DocumentObjectBuilder({}, {}, text).build(relation_store=relation) document = DocumentObjectBuilder({}, {}, text).build()
document_id = document.calculate_id() document_id = document.calculate_id()
store.put_object(document_id, document.encode()) store.put_object(document_id, document.encode())
documents = {"email.txt": document_id} documents = {"email.txt": document_id}
+1 -1
View File
@@ -52,7 +52,7 @@ def get_exif_data(image_path: str):
return { return {
TAGS.get(key): exif_data[key] TAGS.get(key): exif_data[key]
for key in exif_data.keys() for key in exif_data.keys()
if key in TAGS and isinstance(exif_data[key], (bytes, str)) if key in TAGS and isinstance(exif_data[key], str)
} }
else: else:
return {} return {}
+1 -1
View File
@@ -46,7 +46,7 @@ class ChunkListWriter:
) )
file_hash = HashValue(hash_obj.digest()) file_hash = HashValue(hash_obj.digest())
print(f"calc file hash: {file_path}, {file_hash}") # print(f"calc file hash: {file_path}, {file_hash}")
return ChunkList(chunk_list, file_hash) return ChunkList(chunk_list, file_hash)
+6 -10
View File
@@ -4,7 +4,7 @@ from .object import ObjectStore, ObjectRelationStore
from .data import ChunkStore, ChunkTracker, ChunkListWriter, ChunkReader from .data import ChunkStore, ChunkTracker, ChunkListWriter, ChunkReader
from .vector import ChromaVectorStore, VectorBase from .vector import ChromaVectorStore, VectorBase
import logging import logging
import aios_kernel
# KnowledgeStore class, which aggregates ChunkStore, ChunkTracker, and ObjectStore, and is a global singleton that makes it easy to use these three built-in store examples # KnowledgeStore class, which aggregates ChunkStore, ChunkTracker, and ObjectStore, and is a global singleton that makes it easy to use these three built-in store examples
class KnowledgeStore: class KnowledgeStore:
@@ -13,16 +13,12 @@ class KnowledgeStore:
def __new__(cls): def __new__(cls):
if cls._instance is None: if cls._instance is None:
cls._instance = super().__new__(cls) cls._instance = super().__new__(cls)
directory = os.path.join( knowledge_dir = aios_kernel.storage.AIStorage().get_myai_dir() / "knowledge"
os.path.dirname(__file__), "../../rootfs/data/"
)
directory = os.path.normpath(directory)
print(directory)
if not os.path.exists(directory): if not os.path.exists(knowledge_dir):
os.makedirs(directory) os.makedirs(knowledge_dir)
cls._instance.__singleton_init__(directory) cls._instance.__singleton_init__(knowledge_dir)
return cls._instance return cls._instance
@@ -64,5 +60,5 @@ class KnowledgeStore:
def get_vector_store(self, model_name: str) -> VectorBase: def get_vector_store(self, model_name: str) -> VectorBase:
if model_name not in self.vector_store: if model_name not in self.vector_store:
self.vector_store[model_name] = ChromaVectorStore(model_name) self.vector_store[model_name] = ChromaVectorStore(self.root, model_name)
return self.vector_store[model_name] return self.vector_store[model_name]
+2 -4
View File
@@ -6,16 +6,14 @@ import os
class ChromaVectorStore(VectorBase): class ChromaVectorStore(VectorBase):
def __init__(self, model_name: str) -> None: def __init__(self, root_dir, model_name: str) -> None:
super().__init__(model_name) super().__init__(model_name)
logging.info( logging.info(
"will init chroma vector store, model={}".format(model_name) "will init chroma vector store, model={}".format(model_name)
) )
directory = os.path.join( directory = os.path.join(root_dir, "vector")
os.path.dirname(__file__), "../../../rootfs/data/vector"
)
logging.info("will use vector store: {}".format(directory)) logging.info("will use vector store: {}".format(directory))
client = chromadb.PersistentClient( client = chromadb.PersistentClient(
+59 -2
View File
@@ -22,8 +22,11 @@ from prompt_toolkit.styles import Style
directory = os.path.dirname(__file__) directory = os.path.dirname(__file__)
sys.path.append(directory + '/../../') 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
import proxy import proxy
from aios_kernel import *
sys.path.append(directory + '/../../component/') sys.path.append(directory + '/../../component/')
from agent_manager import AgentManager from agent_manager import AgentManager
@@ -116,6 +119,7 @@ class AIOS_Shell:
except Exception as e: except Exception as e:
logger.warning(f"load tunnels config from {tunnels_config_path} failed!") logger.warning(f"load tunnels config from {tunnels_config_path} failed!")
KnowledgePipline.get_instance().initial()
return True return True
@@ -165,7 +169,6 @@ class AIOS_Shell:
return tunnel_config return tunnel_config
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")
@@ -179,6 +182,55 @@ class AIOS_Shell:
except Exception as e: except Exception as e:
logger.warning(f"load tunnels config from {tunnels_config_path} failed!") logger.warning(f"load tunnels config from {tunnels_config_path} failed!")
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"
"/knowledge query $query\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_config()
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 == "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))
if sub_cmd == "query":
if len(args) < 2:
return show_text
prompt = AgentPrompt()
prompt.messages.append({"role": "user", "content":" ".join(args[1:])})
result = await KnowledgeBase().query_prompt(prompt)
print_formatted_text(result.as_str())
async def call_func(self,func_name, args): async def call_func(self,func_name, args):
match func_name: match func_name:
case 'send': case 'send':
@@ -221,6 +273,8 @@ class AIOS_Shell:
show_text = FormattedText([("class:title", f"connect to {tunnel_target} success!")]) show_text = FormattedText([("class:title", f"connect to {tunnel_target} success!")])
return show_text return show_text
case 'knowledge':
return await self.handle_knowledge_commands(args)
case 'open': case 'open':
if len(args) >= 1: if len(args) >= 1:
target_id = args[0] target_id = args[0]
@@ -400,6 +454,9 @@ async def main():
'/history $num $offset', '/history $num $offset',
'/login $username', '/login $username',
'/connect $target', '/connect $target',
'/knowledge add email | dir',
'/knowledge journal [$topn]',
'/knowledge query $query'
'/set_config $key', '/set_config $key',
'/list_config', '/list_config',
'/show', '/show',