Skip to content

Commit d439692

Browse files
committed
list/get/drop/create via http api
1 parent a4fed59 commit d439692

File tree

3 files changed

+128
-100
lines changed

3 files changed

+128
-100
lines changed

mindsdb_sdk/connectors/rest_api.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,34 @@ def insert_into_knowledge_base(self, project: str, knowledge_base_name: str, dat
409409
_raise_for_status(r)
410410

411411
return r.json()
412+
413+
@_try_relogin
414+
def list_knowledge_bases(self, project: str):
415+
r = self.session.get(self.url + f'/api/projects/{project}/knowledge_bases')
416+
_raise_for_status(r)
417+
return r.json()
418+
419+
@_try_relogin
420+
def get_knowledge_base(self, project: str, knowledge_base_name):
421+
r = self.session.get(self.url + f'/api/projects/{project}/knowledge_bases/{knowledge_base_name}')
422+
_raise_for_status(r)
423+
return r.json()
424+
425+
@_try_relogin
426+
def delete_knowledge_base(self, project: str, knowledge_base_name):
427+
r = self.session.delete(self.url + f'/api/projects/{project}/knowledge_bases/{knowledge_base_name}')
428+
_raise_for_status(r)
429+
430+
@_try_relogin
431+
def create_knowledge_base(self, project: str, data):
432+
r = self.session.post(
433+
self.url + f'/api/projects/{project}/knowledge_bases',
434+
json={
435+
'knowledge_base': data
436+
}
437+
)
438+
_raise_for_status(r)
439+
440+
return r.json()
441+
442+

mindsdb_sdk/knowledge_bases.py

Lines changed: 27 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,14 @@ def __init__(self, api, project, data: dict):
3939
self.table_name = Identifier(parts=[self.project.name, self.name])
4040

4141
self.storage = None
42-
if data['storage'] is not None:
43-
# if name contents '.' there could be errors
44-
45-
parts = data['storage'].split('.')
46-
if len(parts) == 2:
47-
database_name, table_name = parts
48-
database = Database(project, database_name)
49-
table = Table(database, table_name)
50-
self.storage = table
42+
if data.get('vector_database_table') is not None:
43+
database = Database(project, data['vector_database'])
44+
table = Table(database, data['vector_database_table'])
45+
self.storage = table
5146

5247
self.model = None
53-
if data['model'] is not None:
54-
self.model = Model(self.project, {'name': data['model']})
48+
if data['embedding_model'] is not None:
49+
self.model = Model(self.project, {'name': data['embedding_model']})
5550

5651
params = data.get('params', {})
5752
if isinstance(params, str):
@@ -241,24 +236,6 @@ def __init__(self, project, api):
241236
self.project = project
242237
self.api = api
243238

244-
def _list(self, name: str = None) -> List[KnowledgeBase]:
245-
246-
# TODO add filter by project. for now 'project' is empty
247-
ast_query = Select(targets=[Star()], from_table=Identifier(parts=['information_schema', 'knowledge_bases']))
248-
if name is not None:
249-
ast_query.where = dict_to_binary_op({'name': name})
250-
251-
df = self.api.sql_query(ast_query.to_string(), database=self.project.name)
252-
253-
# columns to lower case
254-
cols_map = {i: i.lower() for i in df.columns}
255-
df = df.rename(columns=cols_map)
256-
257-
return [
258-
KnowledgeBase(self.api, self.project, item)
259-
for item in df.to_dict('records')
260-
]
261-
262239
def list(self) -> List[KnowledgeBase]:
263240
"""
264241
@@ -268,7 +245,11 @@ def list(self) -> List[KnowledgeBase]:
268245
269246
:return: list of knowledge bases
270247
"""
271-
return self._list()
248+
249+
return [
250+
KnowledgeBase(self.api, self.project, item)
251+
for item in self.api.list_knowledge_bases(self.project.name)
252+
]
272253

273254
def get(self, name: str) -> KnowledgeBase:
274255
"""
@@ -277,13 +258,9 @@ def get(self, name: str) -> KnowledgeBase:
277258
:param name: name of the knowledge base
278259
:return: KnowledgeBase object
279260
"""
280-
item = self._list(name)
281-
if len(item) == 1:
282-
return item[0]
283-
elif len(item) == 0:
284-
raise AttributeError("KnowledgeBase doesn't exist")
285-
else:
286-
raise RuntimeError("Several knowledgeBases with the same name")
261+
262+
data = self.api.get_knowledge_base(self.project.name, name)
263+
return KnowledgeBase(self.api, self.project, data)
287264

288265
def create(
289266
self,
@@ -334,27 +311,21 @@ def create(
334311
params_out.update(params)
335312

336313
if model is not None:
337-
model_name = Identifier(parts=[model.project.name, model.name])
338-
else:
339-
model_name = None
314+
model = model.name
340315

341-
if storage is not None:
342-
storage_name = Identifier(parts=[storage.db.name, storage.name])
343-
else:
344-
storage_name = None
345-
346-
ast_query = CreateKnowledgeBase(
347-
Identifier(parts=[self.project.name, name]),
348-
model=model_name,
349-
storage=storage_name,
350-
params=params_out
351-
)
352-
sql = ast_query.to_string()
316+
payload = {
317+
'name': name,
318+
'model': model,
319+
'params': params_out
320+
}
353321

354-
if is_saving():
355-
return Query(self, sql)
322+
if storage is not None:
323+
payload['storage'] = {
324+
'database': storage.db.name,
325+
'table': storage.name
326+
}
356327

357-
self.api.sql_query(sql)
328+
self.api.create_knowledge_base(self.project.name, data=payload)
358329

359330
return self.get(name)
360331

@@ -365,10 +336,4 @@ def drop(self, name: str):
365336
:return:
366337
"""
367338

368-
ast_query = DropKnowledgeBase(Identifier(parts=[self.project.name, name]))
369-
sql = ast_query.to_string()
370-
371-
if is_saving():
372-
return Query(self, sql)
373-
374-
self.api.sql_query(sql)
339+
return self.api.delete_knowledge_base(self.project.name, name)

tests/test_sdk.py

Lines changed: 70 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,7 +1130,9 @@ def check_project_jobs(self, project, model, database, kb, mock_post):
11301130

11311131
@patch('requests.Session.put')
11321132
@patch('requests.Session.post')
1133-
def check_project_kb(self, project, model, database, mock_post, mock_put):
1133+
@patch('requests.Session.delete')
1134+
@patch('requests.Session.get')
1135+
def check_project_kb(self, project, model, database, mock_get, mock_del, mock_post, mock_put):
11341136

11351137
response_mock(mock_post, pd.DataFrame([{
11361138
'NAME': 'my_kb',
@@ -1140,10 +1142,24 @@ def check_project_kb(self, project, model, database, mock_post, mock_put):
11401142
'PARAMS': {"id_column": "num"},
11411143
}]))
11421144

1145+
example_kb = {
1146+
'id': 1,
1147+
'name': 'my_kb',
1148+
'project_id': 1,
1149+
'embedding_model': 'openai_emb',
1150+
'vector_database': 'pvec',
1151+
'vector_database_table': 'tbl1',
1152+
'updated_at': '2024-10-04 10:55:25.350799',
1153+
'created_at': '2024-10-04 10:55:25.350790',
1154+
'params': {}
1155+
}
1156+
1157+
mock_get().json.return_value = [example_kb]
1158+
11431159
kbs = project.knowledge_bases.list()
11441160

1145-
# TODO add filter by project
1146-
check_sql_call(mock_post, "select * from information_schema.knowledge_bases")
1161+
args, kwargs = mock_get.call_args
1162+
assert args[0] == f'{DEFAULT_CLOUD_API_URL}/api/projects/{project.name}/knowledge_bases'
11471163

11481164
kb = kbs[0]
11491165

@@ -1156,6 +1172,7 @@ def check_project_kb(self, project, model, database, mock_post, mock_put):
11561172
assert kb.storage.name == 'tbl1'
11571173
assert kb.storage.db.name == 'pvec'
11581174

1175+
mock_get().json.return_value = example_kb
11591176
kb = project.knowledge_bases.my_kb
11601177

11611178
str(kb)
@@ -1208,18 +1225,16 @@ def check_project_kb(self, project, model, database, mock_post, mock_put):
12081225
metadata_columns=['date', 'author'],
12091226
params={'k': 'v'}
12101227
)
1211-
1212-
model_name = f'{model.project.name}.{model.name}'
1213-
check_sql_call(
1214-
mock_post,
1215-
f'''
1216-
CREATE KNOWLEDGE_BASE {project.name}.kb2
1217-
USING model={model_name},
1218-
metadata_columns=['date', 'author'],
1219-
k='v'
1220-
''',
1221-
call_stack_num=-2
1222-
)
1228+
args, kwargs = mock_post.call_args
1229+
assert args[0] == f'{DEFAULT_CLOUD_API_URL}/api/projects/{project.name}/knowledge_bases'
1230+
assert kwargs == {'json': {'knowledge_base': {
1231+
'name': 'kb2',
1232+
'model': model.name,
1233+
'params': {
1234+
'k': 'v',
1235+
'metadata_columns': ['date', 'author']
1236+
}
1237+
}}}
12231238

12241239
# create 2
12251240
kb = project.knowledge_bases.create(
@@ -1229,25 +1244,26 @@ def check_project_kb(self, project, model, database, mock_post, mock_put):
12291244
id_column='num'
12301245
)
12311246

1232-
table_name = f'{database.name}.tbl1'
1233-
check_sql_call(
1234-
mock_post,
1235-
f'''
1236-
CREATE KNOWLEDGE_BASE {project.name}.kb2
1237-
USING storage={table_name},
1238-
content_columns=['review'],
1239-
id_column='num'
1240-
''',
1241-
call_stack_num=-2
1242-
)
1247+
args, kwargs = mock_post.call_args
1248+
assert args[0] == f'{DEFAULT_CLOUD_API_URL}/api/projects/{project.name}/knowledge_bases'
1249+
assert kwargs == {'json': {'knowledge_base': {
1250+
'name': 'kb2',
1251+
'model': None,
1252+
'params': {
1253+
'content_columns': ['review'],
1254+
'id_column': 'num'
1255+
},
1256+
'storage': {
1257+
'database': database.name,
1258+
'table': 'tbl1'
1259+
},
1260+
}}}
12431261

12441262
# drop
12451263
project.knowledge_bases.drop('kb2')
12461264

1247-
check_sql_call(
1248-
mock_post,
1249-
f"DROP KNOWLEDGE_BASE {project.name}.kb2"
1250-
)
1265+
args, kwargs = mock_del.call_args
1266+
assert args[0] == f'{DEFAULT_CLOUD_API_URL}/api/projects/{project.name}/knowledge_bases/kb2'
12511267

12521268
return kb
12531269

@@ -1501,6 +1517,18 @@ def test_add_file(self, mock_post, mock_put, mock_get):
15011517
'updated_at': None,
15021518
'provider': 'mindsdb'
15031519
},
1520+
# get KB
1521+
{
1522+
'id': 1,
1523+
'name': 'my_kb',
1524+
'project_id': 1,
1525+
'embedding_model': 'openai_emb',
1526+
'vector_database': 'pvec',
1527+
'vector_database_table': 'tbl1',
1528+
'updated_at': '2024-10-04 10:55:25.350799',
1529+
'created_at': '2024-10-04 10:55:25.350790',
1530+
'params': {}
1531+
},
15041532
# Skills get in Agent update to check if it exists.
15051533
{'name': 'new_skill', 'type': 'retrieval', 'params': {'source': 'test_agent_tokaido_rules_kb'}},
15061534
# Existing agent get in Agent update.
@@ -1515,10 +1543,6 @@ def test_add_file(self, mock_post, mock_put, mock_get):
15151543
},
15161544
])
15171545
responses_mock(mock_post, [
1518-
# KB get (POST /sql).
1519-
pd.DataFrame([
1520-
{'name': 'test_agent_tokaido_rules_kb', 'storage': None, 'model': None},
1521-
]),
15221546
# Skill creation.
15231547
{'name': 'new_skill', 'type': 'retrieval', 'params': {'source': 'test_agent_tokaido_rules_kb'}}
15241548
])
@@ -1568,6 +1592,18 @@ def test_add_webpage(self, mock_post, mock_put, mock_get):
15681592
'updated_at':None,
15691593
'provider':'mindsdb'
15701594
},
1595+
# get KB
1596+
{
1597+
'id': 1,
1598+
'name': 'my_kb',
1599+
'project_id': 1,
1600+
'embedding_model': 'openai_emb',
1601+
'vector_database': 'pvec',
1602+
'vector_database_table': 'tbl1',
1603+
'updated_at': '2024-10-04 10:55:25.350799',
1604+
'created_at': '2024-10-04 10:55:25.350790',
1605+
'params': {}
1606+
},
15711607
# Skills get in Agent update to check if it exists.
15721608
{'name':'new_skill', 'type':'retrieval', 'params':{'source':'test_agent_docs_mdb_ai_kb'}},
15731609
# Existing agent get in Agent update.
@@ -1582,10 +1618,6 @@ def test_add_webpage(self, mock_post, mock_put, mock_get):
15821618
},
15831619
])
15841620
responses_mock(mock_post, [
1585-
# KB get (POST /sql).
1586-
pd.DataFrame([
1587-
{'name':'test_agent_docs_mdb_ai_kb', 'storage':None, 'model':None},
1588-
]),
15891621
# Skill creation.
15901622
{'name':'new_skill', 'type':'retrieval', 'params':{'source':'test_agent_docs_mdb_ai_kb'}}
15911623
])

0 commit comments

Comments
 (0)