From 06b38f8c946a4b435197d0c45f91280c42d49d09 Mon Sep 17 00:00:00 2001 From: Kurt Heiden Date: Mon, 28 Oct 2024 11:21:10 -0600 Subject: [PATCH 1/7] Add docker-compose build Purpose: Ensuring that all dependencies are captured both at the container level and the python level. --- .env.template | 1 + Dockerfile | 12 ++++++++++++ README.md | 5 +++++ build.py | 1 + docker-compose.yaml | 10 ++++++++++ requirements.txt | 4 ++++ 6 files changed, 33 insertions(+) create mode 100644 .env.template create mode 100644 Dockerfile create mode 100644 build.py create mode 100644 docker-compose.yaml create mode 100644 requirements.txt diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..9847a1d --- /dev/null +++ b/.env.template @@ -0,0 +1 @@ +OPENAI_API_KEY= \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5d0954e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.12.0 + +RUN apt-get update -y && apt-get upgrade -y +RUN pip install --upgrade pip + +COPY requirements.txt /src/requirements.txt + +WORKDIR /src + +RUN pip install -r requirements.txt + +ENTRYPOINT ["python", "build.py"] \ No newline at end of file diff --git a/README.md b/README.md index bde1855..60b405d 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,11 @@ To get started: 3. Make your changes. 4. Submit a pull request. +## Building +1. Clone the repository. +2. `cd` to the root directory. +3. Run `docker-compose up --build` + ## License SimpleMind is licensed under the MIT License. diff --git a/build.py b/build.py new file mode 100644 index 0000000..e321660 --- /dev/null +++ b/build.py @@ -0,0 +1 @@ +print('Build successful.') \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..d756f99 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,10 @@ +services: + simplemind: + build: + context: . + dockerfile: Dockerfile + volumes: + - ./simplemind:/src/simplemind + - ./build.py:/src/build.py + env_file: + - .env \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6926b64 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +instructor +anthropic +requests +openai \ No newline at end of file From 7ae1aae7d07bf090b7423f0394ce75acf3f6ec34 Mon Sep 17 00:00:00 2001 From: Kurt Heiden Date: Mon, 28 Oct 2024 11:23:14 -0600 Subject: [PATCH 2/7] Update build.py --- build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/build.py b/build.py index e321660..63f8532 100644 --- a/build.py +++ b/build.py @@ -1 +1,2 @@ +import simplemind print('Build successful.') \ No newline at end of file From 8a458a6adf370d963f063754bdf50815f6afbd1a Mon Sep 17 00:00:00 2001 From: Kurt Heiden Date: Mon, 28 Oct 2024 12:43:57 -0600 Subject: [PATCH 3/7] Add example values for .env.template --- .env.template | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.env.template b/.env.template index 9847a1d..1d685e0 100644 --- a/.env.template +++ b/.env.template @@ -1 +1,3 @@ -OPENAI_API_KEY= \ No newline at end of file +OPENAI_API_KEY= +OLLAMA_HOST_URL=http://host.docker.internal:11434 +OLLAMA_MODEL=llama3.2 \ No newline at end of file From afb375b17061fb73a4cee153e00c833019f50d43 Mon Sep 17 00:00:00 2001 From: Kurt Heiden Date: Mon, 28 Oct 2024 12:44:01 -0600 Subject: [PATCH 4/7] Update requirements.txt --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6926b64..cf939c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ instructor anthropic requests -openai \ No newline at end of file +openai +ollama \ No newline at end of file From f2777a013d2804b89098e7e4bbfa01eb3e868244 Mon Sep 17 00:00:00 2001 From: Kurt Heiden Date: Mon, 28 Oct 2024 12:44:40 -0600 Subject: [PATCH 5/7] Build ollama integration Based on functionality defined in the top level README.md --- simplemind/conversation.py | 12 ++++ simplemind/integrations/__init__.py | 1 + simplemind/integrations/ollama.py | 107 ++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 simplemind/conversation.py create mode 100644 simplemind/integrations/ollama.py diff --git a/simplemind/conversation.py b/simplemind/conversation.py new file mode 100644 index 0000000..cb07fbf --- /dev/null +++ b/simplemind/conversation.py @@ -0,0 +1,12 @@ +class Conversation: + def __init__(self, ai_client): + self.messages = [] + self.ai_client = ai_client + + def say(self, message): + self.messages.append({'role': 'user', 'content': message}) + + def get_reply(self): + reply = self.ai_client.message(messages=self.messages) + self.messages.append({'role': 'system', 'content': reply.text}) + return reply \ No newline at end of file diff --git a/simplemind/integrations/__init__.py b/simplemind/integrations/__init__.py index 6942e5c..b69523f 100644 --- a/simplemind/integrations/__init__.py +++ b/simplemind/integrations/__init__.py @@ -1,2 +1,3 @@ from .anthropic import Anthropic from .openai import OpenAI +from .ollama import Ollama \ No newline at end of file diff --git a/simplemind/integrations/ollama.py b/simplemind/integrations/ollama.py new file mode 100644 index 0000000..6bb1d7e --- /dev/null +++ b/simplemind/integrations/ollama.py @@ -0,0 +1,107 @@ +import os + +from ollama import Client as BaseOllama + +from .base import BaseClientProvider +from ..models import AIResponse +from ..conversation import Conversation + +TIMEOUT = 60 + +class Ollama(BaseClientProvider): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.login() + self.conversation = [] + + def login(self): + """Initialize Ollama client, with Instructor enabled.""" + if not os.environ.get('OLLAMA_HOST_URL'): + raise ValueError("Please set the OLLAMA_HOST_URL environment variable") + + if not os.environ.get('OLLAMA_MODEL'): + raise ValueError("Please set the OLLAMA_MODEL environment variable") + else: + self.model = os.environ.get('OLLAMA_MODEL') + + self.client = BaseOllama( + timeout=TIMEOUT, + host=os.environ.get('OLLAMA_HOST_URL')) + assert self.test_connection() + + @property + def available_models(self): + """Returns the available models from the OpenAI client.""" + + def gen(): + for model in self.client.list().get('models'): + yield model + + return [g for g in gen()] + + def test_connection(self): + """Test the connection to ollama. Returns True if successful.""" + + return bool(len(self.available_models)) + + def generate_text(self, prompt, *, response_model=False, **kwargs): + use_instructor = bool(response_model) + + client = self.instructor_client if use_instructor else self.client + + # Parameters for the Ollama client. + params = { + "prompt": prompt, + "model": self.model, + } + params.update(kwargs) + + if use_instructor: + params["response_model"] = response_model + + # Make the request to Ollama. + completion = client.generate(**params) + if use_instructor: + return completion.model_dump() + + else: + return AIResponse( + response=completion, + text=completion.get('response'), + ) + + def message(self, message=None, message_history=None, response_model=False, **kwargs): + """Generates a response from the OpenAI client.""" + use_instructor = bool(response_model) + + client = self.instructor_client if use_instructor else self.client + + # Parameters for the Ollama client. + all_messages = [] + if message_history: + all_messages.extend(message_history) + if message: + all_messages.append({'role': 'user', 'content': message}) + params = { + "messages": all_messages, + "model": self.model, + } + params.update(kwargs) + + if use_instructor: + params["response_model"] = response_model + + # Make the request to Ollama. + completion = client.chat(**params) + if use_instructor: + return completion.model_dump() + + else: + return AIResponse( + response=completion, + text=completion.get('message').get('content'), + ) + + def start_conversation(self): + return Conversation(self) From 19e32aa504633939178705686a23e709b4e82a47 Mon Sep 17 00:00:00 2001 From: Kurt Heiden Date: Mon, 28 Oct 2024 12:45:02 -0600 Subject: [PATCH 6/7] Add ollama example usage file --- t3.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 t3.py diff --git a/t3.py b/t3.py new file mode 100644 index 0000000..1fb3124 --- /dev/null +++ b/t3.py @@ -0,0 +1,28 @@ +import simplemind + +aiclient = simplemind.Ollama() + +print('Messaging client') +message_response = aiclient.message(message="Once upon a time in a land far away...") +print(message_response) + +print('Generating Text') +generated_text = aiclient.generate_text(prompt="Once upon a time in a land far away...") +print(generated_text) + +print('Initiating Conversation') +conversation = aiclient.start_conversation() + +# Add a message to the conversation +conversation.say("Please remember the number 42") + +# Get the AI's response +reply = conversation.get_reply() +print(reply) + +# Add another message to the conversation +conversation.say("What number did I ask you to remember?") + +# Get the AI's response +reply = conversation.get_reply() +print(reply) \ No newline at end of file From edbd166ec646aa37fa0b883ca2dd33a3316c9d75 Mon Sep 17 00:00:00 2001 From: Kurt Heiden Date: Mon, 28 Oct 2024 12:46:28 -0600 Subject: [PATCH 7/7] Update ollama.py --- simplemind/integrations/ollama.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/simplemind/integrations/ollama.py b/simplemind/integrations/ollama.py index 6bb1d7e..7581c61 100644 --- a/simplemind/integrations/ollama.py +++ b/simplemind/integrations/ollama.py @@ -32,7 +32,7 @@ class Ollama(BaseClientProvider): @property def available_models(self): - """Returns the available models from the OpenAI client.""" + """Returns the available models from the Ollama client.""" def gen(): for model in self.client.list().get('models'): @@ -41,7 +41,7 @@ class Ollama(BaseClientProvider): return [g for g in gen()] def test_connection(self): - """Test the connection to ollama. Returns True if successful.""" + """Test the connection to Ollama. Returns True if successful.""" return bool(len(self.available_models)) @@ -72,7 +72,7 @@ class Ollama(BaseClientProvider): ) def message(self, message=None, message_history=None, response_model=False, **kwargs): - """Generates a response from the OpenAI client.""" + """Generates a response from the Ollama client.""" use_instructor = bool(response_model) client = self.instructor_client if use_instructor else self.client