Skip to content

Commit

Permalink
Merge pull request #143 from mindsdb/staging
Browse files Browse the repository at this point in the history
Release 3.1.0
  • Loading branch information
ea-rus authored Aug 6, 2024
2 parents 31d1441 + 841290a commit 8e17af6
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 92 deletions.
5 changes: 2 additions & 3 deletions examples/using_agents_with_retrieval.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@
con = mindsdb_sdk.connect()

open_ai_key = os.getenv('OPENAI_API_KEY')
model_name = 'gpt-4'
model_name = 'gpt-4o'

# Now create an agent that will use the model we just created.
agent = con.agents.create(name=f'mindsdb_retrieval_agent_{model_name}_{uuid4().hex}',
model='gpt-4',
model=model_name,
params={'return_context': True})

agent.add_file('./data/tokaido-rulebook.pdf', 'rule book for the board game Tokaido')


question = "what are the rules for the game takaido?"
answer = agent.completion([{'question': question, 'answer': None}])
print(answer.context)
Expand Down
33 changes: 33 additions & 0 deletions examples/using_agents_with_streaming_with_retrieval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import mindsdb_sdk
from uuid import uuid4
import os

con = mindsdb_sdk.connect()

open_ai_key = os.getenv('OPENAI_API_KEY')
model_name = 'gpt-4o'

# Now create an agent that will use the model we just created.
agent = con.agents.create(name=f'mindsdb_retrieval_agent_{model_name}_{uuid4().hex}',
model=model_name,
params={'return_context': True})

agent.add_file('./data/tokaido-rulebook.pdf', 'rule book for the board game Tokaido')

question = "what are the rules for the game takaido?"

# Stream the completion
completion_stream = agent.completion_stream([{'question': question, 'answer': None}])

# Process the streaming response
full_response = ""
for chunk in completion_stream:
print(chunk) # Print the entire chunk for debugging
if isinstance(chunk, dict):
if 'output' in chunk:
full_response += chunk['output']
elif isinstance(chunk, str):
full_response += chunk

print("\n\nFull response:")
print(full_response)
2 changes: 1 addition & 1 deletion mindsdb_sdk/__about__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__title__ = 'mindsdb_sdk'
__package_name__ = 'mindsdb_sdk'
__version__ = '3.0.2'
__version__ = '3.1.0'
__description__ = "MindsDB Python SDK, provides an SDK to use a remote mindsdb instance"
__email__ = "[email protected]"
__author__ = 'MindsDB Inc'
Expand Down
105 changes: 42 additions & 63 deletions mindsdb_sdk/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
from uuid import uuid4
import datetime
import json
import pandas as pd

from mindsdb_sdk.databases import Databases
from mindsdb_sdk.knowledge_bases import KnowledgeBase, KnowledgeBases
from mindsdb_sdk.ml_engines import MLEngines
from mindsdb_sdk.models import Model, Models
from mindsdb_sdk.skills import Skill, Skills
from mindsdb_sdk.knowledge_bases import KnowledgeBase
from mindsdb_sdk.models import Model
from mindsdb_sdk.skills import Skill
from mindsdb_sdk.utils.objects_collection import CollectionBase

_DEFAULT_LLM_MODEL = 'gpt-4o'
_DEFAULT_LLM_PROMPT = 'Answer the user"s question in a helpful way: {{question}}'


class AgentCompletion:
"""
Expand All @@ -25,6 +24,7 @@ class AgentCompletion:
"""

def __init__(self, content: str, context: List[dict] = None):
self.content = content
self.context = context
Expand Down Expand Up @@ -76,6 +76,7 @@ class Agent:
>>> agents.drop('my_agent')
"""

def __init__(
self,
name: str,
Expand All @@ -84,9 +85,9 @@ def __init__(
params: dict,
created_at: datetime.datetime,
updated_at: datetime.datetime,
provider: str= None,
provider: str = None,
collection: CollectionBase = None
):
):
self.name = name
self.model_name = model_name
self.provider = provider
Expand Down Expand Up @@ -178,22 +179,25 @@ def from_json(cls, json: dict, collection: CollectionBase):

class Agents(CollectionBase):
"""Collection for agents"""
def __init__(self, api, project: str, knowledge_bases: KnowledgeBases, databases: Databases, models: Models, ml_engines: MLEngines, skills: Skills = None):

def __init__(self, project, api):
self.api = api
self.project = project
self.skills = skills or Skills(self.api, project)
self.databases = databases
self.knowledge_bases = knowledge_bases
self.ml_engines = ml_engines
self.models = models

self.knowledge_bases = project.knowledge_bases
self.models = project.models
self.skills = project.skills

self.databases = project.server.databases
self.ml_engines = project.server.ml_engines

def list(self) -> List[Agent]:
"""
List available agents.
:return: list of agents
"""
data = self.api.agents(self.project)
data = self.api.agents(self.project.name)
return [Agent.from_json(agent, self) for agent in data]

def get(self, name: str) -> Agent:
Expand All @@ -204,7 +208,7 @@ def get(self, name: str) -> Agent:
:return: agent with given name
"""
data = self.api.agent(self.project, name)
data = self.api.agent(self.project.name, name)
return Agent.from_json(data, self)

def completion(self, name: str, messages: List[dict]) -> AgentCompletion:
Expand All @@ -216,7 +220,7 @@ def completion(self, name: str, messages: List[dict]) -> AgentCompletion:
:return: completion from querying the agent
"""
data = self.api.agent_completion(self.project, name, messages)
data = self.api.agent_completion(self.project.name, name, messages)
if 'context' in data['message']:
return AgentCompletion(data['message']['content'], data['message'].get('context'))

Expand All @@ -231,7 +235,7 @@ def completion_stream(self, name, messages: List[dict]) -> Iterable[object]:
:return: iterable of completion chunks from querying the agent.
"""
return self.api.agent_completion_stream(self.project, name, messages)
return self.api.agent_completion_stream(self.project.name, name, messages)

def _create_default_knowledge_base(self, agent: Agent, name: str) -> KnowledgeBase:
# Make sure default ML engine for embeddings exists.
Expand All @@ -244,7 +248,7 @@ def _create_default_knowledge_base(self, agent: Agent, name: str) -> KnowledgeBa
agent_model = self.models.get(agent.model_name)
training_options = json.loads(agent_model.data.get('training_options', '{}'))
training_options_using = training_options.get('using', {})
api_key_params = {k:v for k, v in training_options_using.items() if 'api_key' in k}
api_key_params = {k: v for k, v in training_options_using.items() if 'api_key' in k}
kb = self.knowledge_bases.create(name, params=api_key_params)
else:
kb = self.knowledge_bases.create(name)
Expand Down Expand Up @@ -298,7 +302,6 @@ def add_files(self, name: str, file_paths: List[str], description: str, knowledg
agent.skills.append(file_retrieval_skill)
self.update(agent.name, agent)


def add_file(self, name: str, file_path: str, description: str, knowledge_base: str = None):
"""
Add a file to the agent for retrieval.
Expand Down Expand Up @@ -395,50 +398,14 @@ def add_database(self, name: str, database: str, tables: List[str], description:
agent.skills.append(database_sql_skill)
self.update(agent.name, agent)

def _create_ml_engine_if_not_exists(self, name: str = 'langchain'):
try:
_ = self.ml_engines.get('langchain')
except Exception:
# Create the engine if it doesn't exist.
_ = self.ml_engines.create('langchain', handler='langchain')

def _create_model_if_not_exists(self, name: str, model: Union[Model, dict, str]) -> str:
# Create langchain engine if it doesn't exist.
self._create_ml_engine_if_not_exists()
# Create a default model if it doesn't exist.
default_model_params = {
'predict': 'answer',
'engine': 'langchain',
'prompt_template': 'Answer the user"s question in a helpful way: {{question}}',
# Use GPT-4 by default.
'provider': 'openai',
'model_name': 'gpt-4'
}

if isinstance(model, dict):
default_model_params.update(model)
# Create model with passed in params.
return self.models.create(
f'{name}_default_model',
**default_model_params
).name

if model is None:
# Create model with default params.
return _DEFAULT_LLM_MODEL

if isinstance(model, Model):
return model.name

return model

def create(
self,
name: str,
model: Union[Model, dict, str] = None,
provider: str = None,
skills: List[Union[Skill, str]] = None,
params: dict = None) -> Agent:
params: dict = None,
**kwargs) -> Agent:
"""
Create new agent and return it
Expand All @@ -454,16 +421,28 @@ def create(
for skill in skills:
if isinstance(skill, str):
# Check if skill exists.
# TODO what this line does?
_ = self.skills.get(skill)
skill_names.append(skill)
continue
# Create the skill if it doesn't exist.
_ = self.skills.create(skill.name, skill.type, skill.params)
skill_names.append(skill.name)

model = self._create_model_if_not_exists(name, model)
if params is None:
params = {}
params.update(kwargs)

if 'prompt_template' not in params:
params['prompt_template'] = _DEFAULT_LLM_PROMPT

if model is None:
model = _DEFAULT_LLM_MODEL
elif isinstance(model, Model):
model = model.name
provider = 'mindsdb'

data = self.api.create_agent(self.project, name, model, provider, skill_names, params)
data = self.api.create_agent(self.project.name, name, model, provider, skill_names, params)
return Agent.from_json(data, self)

def update(self, name: str, updated_agent: Agent):
Expand Down Expand Up @@ -492,12 +471,12 @@ def update(self, name: str, updated_agent: Agent):
_ = self.skills.create(skill.name, skill.type, skill.params)
updated_skills.add(skill.name)

existing_agent = self.api.agent(self.project, name)
existing_agent = self.api.agent(self.project.name, name)
existing_skills = set([s['name'] for s in existing_agent['skills']])
skills_to_add = updated_skills.difference(existing_skills)
skills_to_remove = existing_skills.difference(updated_skills)
data = self.api.update_agent(
self.project,
self.project.name,
name,
updated_agent.name,
updated_agent.model_name,
Expand All @@ -513,4 +492,4 @@ def drop(self, name: str):
:param name: Name of the agent to be dropped
"""
_ = self.api.delete_agent(self.project, name)
_ = self.api.delete_agent(self.project.name, name)
20 changes: 10 additions & 10 deletions mindsdb_sdk/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ class Project:
"""

def __init__(self, api, name, agents: Agents = None, skills: Skills = None, knowledge_bases: KnowledgeBases = None, databases: Databases = None, ml_engines: MLEngines = None):
def __init__(self, server, api, name):
self.name = name
self.api = api
self.server = server

self.models = Models(self, api)

Expand All @@ -76,11 +77,10 @@ def __init__(self, api, name, agents: Agents = None, skills: Skills = None, know
self.create_job = self.jobs.create
self.drop_job = self.jobs.drop

self.databases = databases or Databases(api)
self.knowledge_bases = knowledge_bases or KnowledgeBases(self, api)
self.knowledge_bases = KnowledgeBases(self, api)

self.skills = skills or Skills(api, name)
self.agents = agents or Agents(api, name, self.knowledge_bases, self.databases, self.models, ml_engines, self.skills)
self.skills = Skills(self, api)
self.agents = Agents(self, api)

def __repr__(self):
return f'{self.__class__.__name__}({self.name})'
Expand All @@ -106,7 +106,6 @@ def drop_model_version(self, name: str, version: int):
self.query(ast_query.to_string()).fetch()



class Projects(CollectionBase):
"""
Projects
Expand All @@ -133,8 +132,9 @@ class Projects(CollectionBase):
"""

def __init__(self, api):
def __init__(self, server, api):
self.api = api
self.server = server

def _list_projects(self):
data = self.api.sql_query("select NAME from information_schema.databases where TYPE='project'")
Expand All @@ -147,7 +147,7 @@ def list(self) -> List[Project]:
:return: list of Project objects
"""
# select * from information_schema.databases where TYPE='project'
return [Project(self.api, name) for name in self._list_projects()]
return [Project(self.server, self.api, name) for name in self._list_projects()]

def get(self, name: str = 'mindsdb') -> Project:
"""
Expand All @@ -158,7 +158,7 @@ def get(self, name: str = 'mindsdb') -> Project:
"""
if name not in self._list_projects():
raise AttributeError("Project doesn't exist")
return Project(self.api, name)
return Project(self.server, self.api, name)

def create(self, name: str) -> Project:
"""
Expand All @@ -175,7 +175,7 @@ def create(self, name: str) -> Project:
)

self.api.sql_query(ast_query.to_string())
return Project(self.api, name)
return Project(self.server, self.api, name)

def drop(self, name: str):
"""
Expand Down
7 changes: 2 additions & 5 deletions mindsdb_sdk/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,24 @@ class Server(Project):

def __init__(self, api, skills: Skills = None, agents: Agents = None):
# server is also mindsdb project
project_name = 'mindsdb'
self.databases = Databases(api)
self.ml_engines = MLEngines(api)
super().__init__(api, project_name, skills=skills, agents=agents, databases=self.databases, ml_engines=self.ml_engines)
super().__init__(self, api, 'mindsdb')

self.projects = Projects(api)
self.projects = Projects(self, api)

# old api
self.get_project = self.projects.get
self.list_projects = self.projects.list
self.create_project = self.projects.create
self.drop_project = self.projects.drop


# old api
self.get_database = self.databases.get
self.list_databases = self.databases.list
self.create_database = self.databases.create
self.drop_database = self.databases.drop


self.ml_handlers = Handlers(self.api, 'ml')
self.data_handlers = Handlers(self.api, 'data')

Expand Down
Loading

0 comments on commit 8e17af6

Please sign in to comment.