Skip to content

Commit 828d3c3

Browse files
committed
Add Knowledge Bases to Core SDK
1 parent 417a16d commit 828d3c3

File tree

5 files changed

+444
-11
lines changed

5 files changed

+444
-11
lines changed

minds/client.py

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from minds.rest_api import RestAPI
33

44
from minds.datasources import Datasources
5+
from minds.knowledge_bases import KnowledgeBases
56
from minds.minds import Minds
67

78

@@ -12,5 +13,6 @@ def __init__(self, api_key, base_url=None):
1213
self.api = RestAPI(api_key, base_url)
1314

1415
self.datasources = Datasources(self)
16+
self.knowledge_bases = KnowledgeBases(self)
1517

1618
self.minds = Minds(self)

minds/knowledge_bases/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .knowledge_bases import *
+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
from typing import Any, Dict, List, Optional, Union
2+
3+
from pydantic import BaseModel
4+
5+
from minds.rest_api import RestAPI
6+
7+
8+
class VectorStoreConfig(BaseModel):
9+
'''Configuration for the underlying vector store for knowledge base embeddings'''
10+
engine: str
11+
connection_data: Dict[str, Any]
12+
table: str = 'embeddings'
13+
14+
15+
class EmbeddingConfig(BaseModel):
16+
'''Configuration for embeddings to use with underlying vector store for knowledge base'''
17+
provider: str
18+
model: str
19+
params: Optional[Dict[str, Any]] = None
20+
21+
22+
class KnowledgeBaseConfig(BaseModel):
23+
'''Configuration for a knowledge base'''
24+
name: str
25+
description: str
26+
vector_store_config: Optional[VectorStoreConfig] = None
27+
embedding_config: Optional[EmbeddingConfig] = None
28+
29+
30+
class KnowledgeBaseDocument(BaseModel):
31+
'''Represents a document that can be inserted into a knowledge base'''
32+
id: Union[int, str]
33+
content: str
34+
metadata: Optional[Dict[str, Any]] = {}
35+
36+
37+
class KnowledgeBase:
38+
def __init__(self, name, api: RestAPI):
39+
self.name = name
40+
self.api = api
41+
42+
def insert_from_select(self, query: str):
43+
'''
44+
Inserts select content of a connected datasource into this knowledge base
45+
46+
:param query: The SQL SELECT query to use to retrieve content to be inserted
47+
'''
48+
update_request = {
49+
'query': query
50+
}
51+
_ = self.api.put(f'/knowledge_bases/{self.name}', data=update_request)
52+
53+
def insert_documents(self, documents: List[KnowledgeBaseDocument]):
54+
'''
55+
Inserts documents directly into this knowledge base
56+
57+
:param documents: The documents to insert
58+
'''
59+
update_request = {
60+
'rows': [d.model_dump() for d in documents]
61+
}
62+
_ = self.api.put(f'/knowledge_bases/{self.name}', data=update_request)
63+
64+
def insert_urls(self, urls: List[str]):
65+
'''
66+
Crawls URLs & inserts the retrieved webpages into this knowledge base
67+
68+
:param urls: Valid URLs to crawl & insert
69+
'''
70+
update_request = {
71+
'urls': urls
72+
}
73+
_ = self.api.put(f'/knowledge_bases/{self.name}', data=update_request)
74+
75+
def insert_files(self, files: List[str]):
76+
'''
77+
Inserts files that have already been uploaded to MindsDB into this knowledge base
78+
79+
:param files: Names of preuploaded files to insert
80+
'''
81+
update_request = {
82+
'files': files
83+
}
84+
_ = self.api.put(f'/knowledge_bases/{self.name}', data=update_request)
85+
86+
87+
class KnowledgeBases:
88+
def __init__(self, client):
89+
self.api = client.api
90+
91+
def create(self, config: KnowledgeBaseConfig) -> KnowledgeBase:
92+
'''
93+
Create new knowledge base and return it
94+
95+
:param config: knowledge base configuration, properties:
96+
- name: str, name of knowledge base
97+
- description: str, description of the knowledge base. Used by minds to know what data can be retrieved.
98+
- vector_store_config: VectorStoreConfig, configuration for embeddings vector store.
99+
- embedding_config: EmbeddingConfig, configuration for embeddings.
100+
:return: knowledge base object
101+
'''
102+
create_request = {
103+
'name': config.name,
104+
'description': config.description
105+
}
106+
if config.vector_store_config is not None:
107+
vector_store_data = {
108+
'engine': config.vector_store_config.engine,
109+
'connection_data': config.vector_store_config.connection_data
110+
}
111+
create_request['vector_store'] = vector_store_data
112+
if config.embedding_config is not None:
113+
embedding_data = {
114+
'provider': config.embedding_config.provider,
115+
'name': config.embedding_config.model
116+
}
117+
if config.embedding_config.params is not None:
118+
embedding_data.update(config.embedding_config.params)
119+
create_request['embedding_model'] = embedding_data
120+
121+
_ = self.api.post('/knowledge_bases', data=create_request)
122+
return self.get(config.name)
123+
124+
def list(self) -> List[KnowledgeBase]:
125+
'''
126+
Returns list of knowledge bases
127+
128+
:return: iterable knowledge bases
129+
'''
130+
131+
list_knowledge_bases_response = self.api.get('/knowledge_bases')
132+
knowledge_bases = list_knowledge_bases_response.json()
133+
134+
all_knowledge_bases = []
135+
for knowledge_base in knowledge_bases:
136+
all_knowledge_bases.append(KnowledgeBase(knowledge_base['name'], self.api))
137+
return all_knowledge_bases
138+
139+
def get(self, name: str) -> KnowledgeBase:
140+
'''
141+
Get knowledge base by name
142+
143+
:param name: name of knowledge base
144+
:return: knowledge base object
145+
'''
146+
147+
knowledge_base_response = self.api.get(f'/knowledge_bases/{name}')
148+
knowledge_base = knowledge_base_response.json()
149+
return KnowledgeBase(knowledge_base['name'], self.api)
150+
151+
def drop(self, name: str, force=False):
152+
'''
153+
Drop knowledge base by name
154+
155+
:param name: name of knowledge base
156+
:param force: if True - remove from all minds, default: False
157+
'''
158+
data = None
159+
if force:
160+
data = {'cascade': True}
161+
162+
self.api.delete(f'/knowledge_bases/{name}', data=data)

minds/minds.py

+91
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import minds.utils as utils
44
import minds.exceptions as exc
55
from minds.datasources import Datasource, DatabaseConfig
6+
from minds.knowledge_bases import KnowledgeBase, KnowledgeBaseConfig
67

78
DEFAULT_PROMPT_TEMPLATE = 'Use your database tools to answer the user\'s question: {{question}}'
89

@@ -13,6 +14,7 @@ def __init__(
1314
provider=None,
1415
parameters=None,
1516
datasources=None,
17+
knowledge_bases=None,
1618
created_at=None,
1719
updated_at=None,
1820
**kwargs
@@ -36,6 +38,7 @@ def __init__(
3638
base_url=base_url
3739
)
3840
self.datasources = datasources
41+
self.knowledge_bases = knowledge_bases
3942

4043
def __repr__(self):
4144
return (f'Mind(name={self.name}, '
@@ -44,6 +47,7 @@ def __repr__(self):
4447
f'created_at="{self.created_at}", '
4548
f'updated_at="{self.updated_at}", '
4649
f'parameters={self.parameters}, '
50+
f'knowledge_bases={self.knowledge_bases}, '
4751
f'datasources={self.datasources})')
4852

4953
def update(
@@ -53,6 +57,7 @@ def update(
5357
provider=None,
5458
prompt_template=None,
5559
datasources=None,
60+
knowledge_bases=None,
5661
parameters=None,
5762
):
5863
"""
@@ -65,11 +70,17 @@ def update(
6570
- Datasource object (minds.datasources.Database)
6671
- datasource config (minds.datasources.DatabaseConfig), in this case datasource will be created
6772
73+
Knowledge base can be passed as
74+
- name, str
75+
- KnowledgeBase object (minds.knowledge_bases.KnowledgeBase)
76+
- Knowledge base config (minds.knowledge_bases.KnowledgeBaseConfig), in this case knowledge base will be created
77+
6878
:param name: new name of the mind, optional
6979
:param model_name: new llm model name, optional
7080
:param provider: new llm provider, optional
7181
:param prompt_template: new prompt template, optional
7282
:param datasources: alter list of datasources used by mind, optional
83+
:param knowledge_bases: alter list of knowledge bases used by mind, optional
7384
:param parameters, dict: alter other parameters of the mind, optional
7485
"""
7586
data = {}
@@ -84,6 +95,13 @@ def update(
8495
ds_names.append(ds)
8596
data['datasources'] = ds_names
8697

98+
if knowledge_bases is not None:
99+
kb_names = []
100+
for kb in knowledge_bases:
101+
kb = self.client.minds._check_knowledge_base(kb)
102+
kb_names.append(kb)
103+
data['knowledge_bases'] = kb_names
104+
87105
if name is not None:
88106
data['name'] = name
89107
if model_name is not None:
@@ -149,6 +167,50 @@ def del_datasource(self, datasource: Union[Datasource, str]):
149167

150168
self.datasources = updated.datasources
151169

170+
def add_knowledge_base(self, knowledge_base: Union[str, KnowledgeBase, KnowledgeBaseConfig]):
171+
"""
172+
Add knowledge base to mind
173+
Knowledge base can be passed as
174+
- name, str
175+
- Knowledge base object (minds.knowledge_bases.KnowledgeBase)
176+
- Knowledge base config (minds.knowledge_bases.KnowledgeBaseConfig), in this case knowledge base will be created
177+
178+
:param knowledge_base: input knowledge base
179+
"""
180+
181+
kb_name = self.client.minds._check_knowledge_base(knowledge_base)
182+
183+
self.api.post(
184+
f'/projects/{self.project}/minds/{self.name}/knowledge_bases',
185+
data={
186+
'name': kb_name,
187+
}
188+
)
189+
updated = self.client.minds.get(self.name)
190+
191+
self.knowledge_bases = updated.knowledge_bases
192+
193+
def del_knowledge_base(self, knowledge_base: Union[KnowledgeBase, str]):
194+
"""
195+
Delete knowledge base from mind
196+
197+
Knowledge base can be passed as
198+
- name, str
199+
- KnowledgeBase object (minds.knowledge_bases.KnowledgeBase)
200+
201+
:param knowledge_base: Knowledge base to delete
202+
"""
203+
if isinstance(knowledge_base, KnowledgeBase):
204+
knowledge_base = knowledge_base.name
205+
elif not isinstance(knowledge_base, str):
206+
raise ValueError(f'Unknown type of knowledge base: {knowledge_base}')
207+
self.api.delete(
208+
f'/projects/{self.project}/minds/{self.name}/knowledge_bases/{knowledge_base}',
209+
)
210+
updated = self.client.minds.get(self.name)
211+
212+
self.knowledge_bases = updated.knowledge_bases
213+
152214
def completion(self, message: str, stream: bool = False) -> Union[str, Iterable[object]]:
153215
"""
154216
Call mind completion
@@ -221,12 +283,28 @@ def _check_datasource(self, ds) -> str:
221283
raise ValueError(f'Unknown type of datasource: {ds}')
222284
return ds
223285

286+
def _check_knowledge_base(self, knowledge_base) -> str:
287+
if isinstance(knowledge_base, KnowledgeBase):
288+
knowledge_base = knowledge_base.name
289+
elif isinstance(knowledge_base, KnowledgeBaseConfig):
290+
# if not exists - create
291+
try:
292+
self.client.knowledge_bases.get(knowledge_base.name)
293+
except exc.ObjectNotFound:
294+
self.client.knowledge_bases.create(knowledge_base)
295+
296+
knowledge_base = knowledge_base.name
297+
elif not isinstance(knowledge_base, str):
298+
raise ValueError(f'Unknown type of knowledge base: {knowledge_base}')
299+
return knowledge_base
300+
224301
def create(
225302
self, name,
226303
model_name=None,
227304
provider=None,
228305
prompt_template=None,
229306
datasources=None,
307+
knowledge_bases=None,
230308
parameters=None,
231309
replace=False,
232310
update=False,
@@ -239,11 +317,17 @@ def create(
239317
- Datasource object (minds.datasources.Database)
240318
- datasource config (minds.datasources.DatabaseConfig), in this case datasource will be created
241319
320+
Knowledge base can be passed as
321+
- name, str
322+
- KnowledgeBase object (minds.knowledge_bases.KnowledgeBase)
323+
- Knowledge base config (minds.knowledge_bases.KnowledgeBaseConfig), in this case knowledge base will be created
324+
242325
:param name: name of the mind
243326
:param model_name: llm model name, optional
244327
:param provider: llm provider, optional
245328
:param prompt_template: instructions to llm, optional
246329
:param datasources: list of datasources used by mind, optional
330+
:param knowledge_bases: alter list of knowledge bases used by mind, optional
247331
:param parameters, dict: other parameters of the mind, optional
248332
:param replace: if true - to remove existing mind, default is false
249333
:param update: if true - to update mind if exists, default is false
@@ -267,6 +351,12 @@ def create(
267351

268352
ds_names.append(ds)
269353

354+
kb_names = []
355+
if knowledge_bases:
356+
for kb in knowledge_bases:
357+
kb = self._check_knowledge_base(kb)
358+
kb_names.append(kb)
359+
270360
if parameters is None:
271361
parameters = {}
272362

@@ -290,6 +380,7 @@ def create(
290380
'provider': provider,
291381
'parameters': parameters,
292382
'datasources': ds_names,
383+
'knowledge_bases': kb_names
293384
}
294385
)
295386
mind = self.get(name)

0 commit comments

Comments
 (0)