From 601bd54139c4410ee05b3d8db7c6f7862771e47a Mon Sep 17 00:00:00 2001 From: Daniel McKnight <34697904+NeonDaniel@users.noreply.github.com> Date: Tue, 16 May 2023 09:51:34 -0700 Subject: [PATCH 1/8] Update for packaging and deployment (#3) * Refactor code into Python package with setup.py Update config handling to use `ovos-config` and match other Diana services Update README.md Add GitHub automation * Add license including setup.py and file headers Update relative imports to use absolute paths --- .github/workflows/license_tests.yml | 12 +++ .github/workflows/propose_release.yml | 27 ++++++ .github/workflows/publish_release.yml | 25 +++++ .github/workflows/publish_test_build.yml | 22 +++++ .github/workflows/unit_tests.yml | 12 +++ Dockerfile | 11 ++- LICENSE.md | 21 +++++ README.md | 44 ++++++++- app/.gitignore | 1 - app/chatgpt.py | 38 -------- app/main.py | 15 --- app/rmq.py | 77 ---------------- docker_overlay/config/neon/.keep | 0 docker_overlay/etc/OpenVoiceOS/ovos.conf | 11 +++ docker_overlay/etc/neon/diana.yaml | 21 +++++ neon_llm_chatgpt/__init__.py | 25 +++++ neon_llm_chatgpt/__main__.py | 39 ++++++++ neon_llm_chatgpt/chatgpt.py | 63 +++++++++++++ neon_llm_chatgpt/config.py | 50 ++++++++++ {app => neon_llm_chatgpt}/default_config.json | 6 +- neon_llm_chatgpt/rmq.py | 90 ++++++++++++++++++ requirements.txt | 2 - requirements/requirements.txt | 4 + setup.py | 91 +++++++++++++++++++ version.py | 29 ++++++ 25 files changed, 592 insertions(+), 144 deletions(-) create mode 100644 .github/workflows/license_tests.yml create mode 100644 .github/workflows/propose_release.yml create mode 100644 .github/workflows/publish_release.yml create mode 100644 .github/workflows/publish_test_build.yml create mode 100644 .github/workflows/unit_tests.yml create mode 100644 LICENSE.md delete mode 100644 app/.gitignore delete mode 100644 app/chatgpt.py delete mode 100644 app/main.py delete mode 100644 app/rmq.py create mode 100644 docker_overlay/config/neon/.keep create mode 100644 docker_overlay/etc/OpenVoiceOS/ovos.conf create mode 100644 docker_overlay/etc/neon/diana.yaml create mode 100644 neon_llm_chatgpt/__init__.py create mode 100644 neon_llm_chatgpt/__main__.py create mode 100644 neon_llm_chatgpt/chatgpt.py create mode 100644 neon_llm_chatgpt/config.py rename {app => neon_llm_chatgpt}/default_config.json (77%) create mode 100644 neon_llm_chatgpt/rmq.py delete mode 100644 requirements.txt create mode 100644 requirements/requirements.txt create mode 100644 setup.py create mode 100644 version.py diff --git a/.github/workflows/license_tests.yml b/.github/workflows/license_tests.yml new file mode 100644 index 0000000..1404a33 --- /dev/null +++ b/.github/workflows/license_tests.yml @@ -0,0 +1,12 @@ +name: Run License Tests +on: + push: + workflow_dispatch: + pull_request: + branches: + - master +jobs: + license_tests: + uses: neongeckocom/.github/.github/workflows/license_tests.yml@master + with: + packages-exclude: '^(neon-llm-chatgpt|tqdm).*' \ No newline at end of file diff --git a/.github/workflows/propose_release.yml b/.github/workflows/propose_release.yml new file mode 100644 index 0000000..81dfe43 --- /dev/null +++ b/.github/workflows/propose_release.yml @@ -0,0 +1,27 @@ +name: Propose Stable Release +on: + workflow_dispatch: + inputs: + release_type: + type: choice + description: Release Type + options: + - patch + - minor + - major +jobs: + update_version: + uses: neongeckocom/.github/.github/workflows/propose_semver_release.yml@master + with: + branch: dev + release_type: ${{ inputs.release_type }} + update_changelog: True + pull_changes: + uses: neongeckocom/.github/.github/workflows/pull_master.yml@master + needs: update_version + with: + pr_reviewer: neonreviewers + pr_assignee: ${{ github.actor }} + pr_draft: false + pr_title: ${{ needs.update_version.outputs.version }} + pr_body: ${{ needs.update_version.outputs.changelog }} \ No newline at end of file diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml new file mode 100644 index 0000000..441affa --- /dev/null +++ b/.github/workflows/publish_release.yml @@ -0,0 +1,25 @@ +# This workflow will generate a release distribution and upload it to PyPI + +name: Publish Build and GitHub Release +on: + push: + branches: + - master + +jobs: + tag_release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Get Version + run: | + VERSION=$(python setup.py --version) + echo "VERSION=${VERSION}" >> $GITHUB_ENV + - uses: ncipollo/release-action@v1 + with: + token: ${{secrets.GITHUB_TOKEN}} + tag: ${{env.VERSION}} + + build_and_publish_docker: + uses: neongeckocom/.github/.github/workflows/publish_docker.yml@master + secrets: inherit diff --git a/.github/workflows/publish_test_build.yml b/.github/workflows/publish_test_build.yml new file mode 100644 index 0000000..833478a --- /dev/null +++ b/.github/workflows/publish_test_build.yml @@ -0,0 +1,22 @@ +# This workflow will generate a distribution and upload it to PyPI + +name: Publish Alpha Build +on: + push: + branches: + - dev + paths-ignore: + - 'version.py' + +jobs: + publish_alpha_release: + uses: neongeckocom/.github/.github/workflows/publish_alpha_release.yml@master + secrets: inherit + with: + version_file: "version.py" + publish_prerelease: true + publish_pypi: false + build_and_publish_docker: + needs: publish_alpha_release + uses: neongeckocom/.github/.github/workflows/publish_docker.yml@master + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 0000000..189e33d --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,12 @@ +name: Run Unit Tests +on: + pull_request: + workflow_dispatch: + +jobs: + py_build_tests: + uses: neongeckocom/.github/.github/workflows/python_build_tests.yml@master + with: + python_version: "3.8" + docker_build_tests: + uses: neongeckocom/.github/.github/workflows/docker_build_tests.yml@master diff --git a/Dockerfile b/Dockerfile index acd1f0a..51dc8e0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,10 @@ FROM python:3.9-slim -WORKDIR /app - -COPY . . +ENV XDG_CONFIG_HOME /config +COPY docker_overlay/ / -RUN pip install -r requirements.txt +WORKDIR /app +COPY . /app +RUN pip install /app -CMD [ "python3", "app/main.py" ] \ No newline at end of file +CMD [ "neon-llm-chatgpt" ] \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..525bb37 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 Neongecko.com Inc. +# BSD-3 License + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index b055b77..353a84c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,44 @@ -## Run with Neon +# NeonAI LLM ChatGPT +Proxies API calls to ChatGPT. +## Request Format +API requests should include `history`, a list of tuples of strings, and the current +`query` + +>Example Request: +>```json +>{ +> "history": [["user", "hello"], ["llm", "hi"]], +> "query": "how are you?" +>} +>``` + +## Response Format +Responses will be returned as dictionaries. Responses should contain the following: +- `response` - String LLM response to the query + +## Docker Configuration +When running this as a docker container, the `XDG_CONFIG_HOME` envvar is set to `/config`. +A configuration file at `/config/neon/diana.yaml` is required and should look like: +```yaml +MQ: + port: + server: + users: + mq-chatgpt-api: + password: + user: neon_chatgpt +ChatGPT: + key: "" + model: "gpt-3.5-turbo" + role: "You are trying to give a short answer in less than 40 words." + context_depth: 3 + max_tokens: 100 ``` -docker run -it -v :/app/app/config.json:ro chatgpt-api-server + +For example, if your configuration resides in `~/.config`: +```shell +export CONFIG_PATH="/home/${USER}/.config" +docker run -v ${CONFIG_PATH}:/config neon_llm_chatgpt ``` -where `` - absolute path to MQ config \ No newline at end of file +> Note: If connecting to a local MQ server, you may need to specify `--network host` \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore deleted file mode 100644 index 0cffcb3..0000000 --- a/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -config.json \ No newline at end of file diff --git a/app/chatgpt.py b/app/chatgpt.py deleted file mode 100644 index 6bd8a55..0000000 --- a/app/chatgpt.py +++ /dev/null @@ -1,38 +0,0 @@ -import openai - - - -class ChatGPT: - def __init__(self, config): - self.model = config["model"] - self.role = config["role"] - self.context_depth = config["context_depth"] - self.max_tokens = config["max_tokens"] - openai.api_key = config["key"] - - @staticmethod - def convert_role(role): - if (role == "user"): - role_chatgpt = "user" - elif (role == "llm"): - role_chatgpt = "assistant" - return role_chatgpt - - def ask(self, message, chat_history): - messages=[ - {"role": "system", "content": self.role}, - ] - # Context N messages - for role, content in chat_history[-self.context_depth:]: - role_chatgpt = self.convert_role(role) - messages.append({"role": role_chatgpt, "content": content}) - messages.append({"role": "user", "content": message}) - - response = openai.ChatCompletion.create( - model=self.model, - messages=messages, - temperature=0, - max_tokens=self.max_tokens, - ) - bot_message = response.choices[0].message['content'] - return bot_message \ No newline at end of file diff --git a/app/main.py b/app/main.py deleted file mode 100644 index 450ee3b..0000000 --- a/app/main.py +++ /dev/null @@ -1,15 +0,0 @@ -from rmq import ChatgptMQ - - - -def main(): - # Run RabbitMQ - chatgptMQ = ChatgptMQ() - chatgptMQ.run(run_sync=False, run_consumers=True, - daemonize_consumers=True) - chatgptMQ.observer_thread.join() - - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/app/rmq.py b/app/rmq.py deleted file mode 100644 index 169aaf9..0000000 --- a/app/rmq.py +++ /dev/null @@ -1,77 +0,0 @@ -import pika -import json -import os.path - -from ovos_utils import LOG -from neon_mq_connector.connector import MQConnector -from neon_mq_connector.utils.network_utils import dict_to_b64 -from neon_mq_connector.utils.rabbit_utils import create_mq_callback - -from chatgpt import ChatGPT - - -class ChatgptMQ(MQConnector): - """ - Module for processing MQ requests from PyKlatchat to LibreTranslate""" - - def __init__(self): - config = self.load_mq_config() - chatgpt_config = config.pop("ChatGPT", None) - self.chatGPT = ChatGPT(chatgpt_config) - - self.service_name = 'mq-chatgpt-api' - - mq_config = config.pop("MQ", None) - super().__init__(config = mq_config, service_name = self.service_name) - - self.vhost = "/llm" - self.queue = "chat_gpt_input" - self.register_consumer(name=self.service_name, - vhost=self.vhost, - queue=self.queue, - callback=self.handle_request, - on_error=self.default_error_handler, - auto_ack=False) - - def load_mq_config(self, config_path: str = "app/config.json"): - default_config_path = "app/default_config.json" - - config_path = config_path if os.path.isfile(config_path) else default_config_path - with open(config_path) as config_file: - config = json.load(config_file) - LOG.info(f"Loaded MQ config from path {config_path}") - return config - - @create_mq_callback(include_callback_props=('channel', 'method', 'body')) - def handle_request(self, - channel: pika.channel.Channel, - method: pika.spec.Basic.Return, - body: dict): - """ - Handles requests from MQ to ChatGPT received on queue - "request_chatgpt" - - :param channel: MQ channel object (pika.channel.Channel) - :param method: MQ return method (pika.spec.Basic.Return) - :param body: request body (dict) - """ - message_id = body["message_id"] - routing_key = body["routing_key"] - - query = body["query"] - history = body["history"] - - response = self.chatGPT.ask(message = query, chat_history = history) - - api_response = { - "message_id": message_id, - "response": response - } - - - channel.basic_publish(exchange='', - routing_key=routing_key, - body=dict_to_b64(api_response), - properties=pika.BasicProperties( - expiration=str(1000))) - channel.basic_ack(method.delivery_tag) \ No newline at end of file diff --git a/docker_overlay/config/neon/.keep b/docker_overlay/config/neon/.keep new file mode 100644 index 0000000..e69de29 diff --git a/docker_overlay/etc/OpenVoiceOS/ovos.conf b/docker_overlay/etc/OpenVoiceOS/ovos.conf new file mode 100644 index 0000000..46eba3c --- /dev/null +++ b/docker_overlay/etc/OpenVoiceOS/ovos.conf @@ -0,0 +1,11 @@ +{ + "module_overrides": { + "diana": { + "base_folder": "neon", + "config_filename": "diana.yaml" + } + }, + "submodule_mappings": { + "neon_llm_chatgpt": "diana" + } +} \ No newline at end of file diff --git a/docker_overlay/etc/neon/diana.yaml b/docker_overlay/etc/neon/diana.yaml new file mode 100644 index 0000000..78e5933 --- /dev/null +++ b/docker_overlay/etc/neon/diana.yaml @@ -0,0 +1,21 @@ +log_level: INFO +logs: + level_overrides: + error: + - pika + warning: + - filelock + info: [] + debug: [] +MQ: + server: api.neon.ai + port: 5672 + users: + mq_handler: + user: neon_api_utils + password: Klatchat2021 +ChatGPT: + model: "gpt-3.5-turbo" + role: "You are trying to give a short answer in less than 40 words." + context_depth: 3 + max_tokens: 100 \ No newline at end of file diff --git a/neon_llm_chatgpt/__init__.py b/neon_llm_chatgpt/__init__.py new file mode 100644 index 0000000..d782cbb --- /dev/null +++ b/neon_llm_chatgpt/__init__.py @@ -0,0 +1,25 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 Neongecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/neon_llm_chatgpt/__main__.py b/neon_llm_chatgpt/__main__.py new file mode 100644 index 0000000..e98ff45 --- /dev/null +++ b/neon_llm_chatgpt/__main__.py @@ -0,0 +1,39 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 Neongecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from neon_llm_chatgpt.rmq import ChatgptMQ + + +def main(): + # Run RabbitMQ + chatgptMQ = ChatgptMQ() + chatgptMQ.run(run_sync=False, run_consumers=True, + daemonize_consumers=True) + chatgptMQ.observer_thread.join() + + +if __name__ == "__main__": + main() diff --git a/neon_llm_chatgpt/chatgpt.py b/neon_llm_chatgpt/chatgpt.py new file mode 100644 index 0000000..5751cfa --- /dev/null +++ b/neon_llm_chatgpt/chatgpt.py @@ -0,0 +1,63 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 Neongecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import openai + + +class ChatGPT: + def __init__(self, config): + self.model = config["model"] + self.role = config["role"] + self.context_depth = config["context_depth"] + self.max_tokens = config["max_tokens"] + openai.api_key = config["key"] + + @staticmethod + def convert_role(role): + if role == "user": + role_chatgpt = "user" + elif role == "llm": + role_chatgpt = "assistant" + return role_chatgpt + + def ask(self, message, chat_history): + messages = [ + {"role": "system", "content": self.role}, + ] + # Context N messages + for role, content in chat_history[-self.context_depth:]: + role_chatgpt = self.convert_role(role) + messages.append({"role": role_chatgpt, "content": content}) + messages.append({"role": "user", "content": message}) + + response = openai.ChatCompletion.create( + model=self.model, + messages=messages, + temperature=0, + max_tokens=self.max_tokens, + ) + bot_message = response.choices[0].message['content'] + return bot_message diff --git a/neon_llm_chatgpt/config.py b/neon_llm_chatgpt/config.py new file mode 100644 index 0000000..2787463 --- /dev/null +++ b/neon_llm_chatgpt/config.py @@ -0,0 +1,50 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 Neongecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import json + +from os.path import join, dirname, isfile +from ovos_utils.log import LOG +from ovos_config.config import Configuration + + +def load_config() -> dict: + """ + Load and return a configuration object, + """ + legacy_config_path = "/app/app/config.json" + if isfile(legacy_config_path): + LOG.warning(f"Deprecated configuration found at {legacy_config_path}") + with open(legacy_config_path) as f: + config = json.load(f) + return config + config = Configuration() + if not config: + LOG.warning(f"No configuration found! falling back to defaults") + default_config_path = join(dirname(__file__), "default_config.json") + with open(default_config_path) as f: + config = json.load(f) + return config diff --git a/app/default_config.json b/neon_llm_chatgpt/default_config.json similarity index 77% rename from app/default_config.json rename to neon_llm_chatgpt/default_config.json index 271383f..f8887c7 100644 --- a/app/default_config.json +++ b/neon_llm_chatgpt/default_config.json @@ -4,13 +4,13 @@ "port": "5672", "users": { "mq-chatgpt-api": { - "user": "", - "password": "" + "user": "", + "password": "" } } }, "ChatGPT": { - "key": "sk-XXX", + "key": "", "model": "gpt-3.5-turbo", "role": "You are trying to give a short answer in less than 40 words.", "context_depth": 3, diff --git a/neon_llm_chatgpt/rmq.py b/neon_llm_chatgpt/rmq.py new file mode 100644 index 0000000..a252fa4 --- /dev/null +++ b/neon_llm_chatgpt/rmq.py @@ -0,0 +1,90 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 Neongecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import pika +from neon_mq_connector.connector import MQConnector +from neon_mq_connector.utils.network_utils import dict_to_b64 +from neon_mq_connector.utils.rabbit_utils import create_mq_callback + +from neon_llm_chatgpt.chatgpt import ChatGPT +from neon_llm_chatgpt.config import load_config + + +class ChatgptMQ(MQConnector): + """ + Module for processing MQ requests from PyKlatchat to LibreTranslate""" + + def __init__(self): + config = load_config() + chatgpt_config = config.get("ChatGPT", None) + self.chatGPT = ChatGPT(chatgpt_config) + + self.service_name = 'neon_llm_chatgpt' + + mq_config = config.get("MQ", None) + super().__init__(config=mq_config, service_name=self.service_name) + + self.vhost = "/llm" + self.queue = "chat_gpt_input" + self.register_consumer(name=self.service_name, + vhost=self.vhost, + queue=self.queue, + callback=self.handle_request, + on_error=self.default_error_handler, + auto_ack=False) + + @create_mq_callback(include_callback_props=('channel', 'method', 'body')) + def handle_request(self, + channel: pika.channel.Channel, + method: pika.spec.Basic.Return, + body: dict): + """ + Handles requests from MQ to ChatGPT received on queue + "request_chatgpt" + + :param channel: MQ channel object (pika.channel.Channel) + :param method: MQ return method (pika.spec.Basic.Return) + :param body: request body (dict) + """ + message_id = body["message_id"] + routing_key = body["routing_key"] + + query = body["query"] + history = body["history"] + + response = self.chatGPT.ask(message=query, chat_history=history) + + api_response = { + "message_id": message_id, + "response": response + } + + channel.basic_publish(exchange='', + routing_key=routing_key, + body=dict_to_b64(api_response), + properties=pika.BasicProperties( + expiration=str(1000))) + channel.basic_ack(method.delivery_tag) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 6c59820..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -openai -neon-mq-connector>=0.6.1a9,~=0.6 \ No newline at end of file diff --git a/requirements/requirements.txt b/requirements/requirements.txt new file mode 100644 index 0000000..1f12555 --- /dev/null +++ b/requirements/requirements.txt @@ -0,0 +1,4 @@ +openai~=0.27 +neon-mq-connector>=0.6.1a9,~=0.6 +ovos-utils~=0.0.32 +ovos-config~=0.0.8 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..64747b5 --- /dev/null +++ b/setup.py @@ -0,0 +1,91 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2022 Neongecko.com Inc. +# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds, +# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo +# BSD-3 License +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import setuptools +from setuptools import setup +from os import getenv, path + +BASE_PATH = path.abspath(path.dirname(__file__)) + + +def get_requirements(requirements_filename: str): + requirements_file = path.join(BASE_PATH, "requirements", + requirements_filename) + with open(requirements_file, 'r', encoding='utf-8') as r: + requirements = r.readlines() + requirements = [r.strip() for r in requirements if r.strip() + and not r.strip().startswith("#")] + + for i in range(0, len(requirements)): + r = requirements[i] + if "@" in r: + parts = [p.lower() if p.strip().startswith("git+http") else + p for p in r.split('@')] + r = "@".join(parts) + if getenv("GITHUB_TOKEN"): + if "github.com" in r: + requirements[i] = \ + r.replace("github.com", + f"{getenv('GITHUB_TOKEN')}@github.com") + return requirements + + +with open(path.join(BASE_PATH, "README.md"), "r") as f: + long_description = f.read() + +with open(path.join(BASE_PATH, "version.py"), "r", encoding="utf-8") as v: + for line in v.readlines(): + if line.startswith("__version__"): + if '"' in line: + version = line.split('"')[1] + else: + version = line.split("'")[1] + +setup( + name='neon-llm-chatgpt', + version=version, + description='LLM service for Chat GPT', + long_description=long_description, + long_description_content_type="text/markdown", + url='https://github.com/NeonGeckoCom/neon-llm-chatgpt', + author='Neongecko', + author_email='developers@neon.ai', + license='BSD-3.0', + packages=setuptools.find_packages(), + install_requires=get_requirements("requirements.txt"), + zip_safe=True, + classifiers=[ + 'Intended Audience :: Developers', + 'Programming Language :: Python :: 3.6', + ], + entry_points={ + 'console_scripts': [ + 'neon-llm-chatgpt=neon_llm_chatgpt.__main__:main' + ] + } +) diff --git a/version.py b/version.py new file mode 100644 index 0000000..e47fc1c --- /dev/null +++ b/version.py @@ -0,0 +1,29 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2022 Neongecko.com Inc. +# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds, +# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo +# BSD-3 License +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +__version__ = "0.0.1a1" From e94f28e5d5fe4a795eff964df4f24db5f4674d49 Mon Sep 17 00:00:00 2001 From: NeonDaniel Date: Tue, 16 May 2023 16:51:51 +0000 Subject: [PATCH 2/8] Increment Version to 0.0.1a2 --- version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.py b/version.py index e47fc1c..cc11eeb 100644 --- a/version.py +++ b/version.py @@ -26,4 +26,4 @@ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -__version__ = "0.0.1a1" +__version__ = "0.0.1a2" From 3f753a7f8e662e0926c67d8f7c6fb30ca76f2c6e Mon Sep 17 00:00:00 2001 From: Daniel McKnight <34697904+NeonDaniel@users.noreply.github.com> Date: Tue, 6 Jun 2023 12:01:39 -0700 Subject: [PATCH 3/8] Add log for handled requests (#4) --- neon_llm_chatgpt/rmq.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/neon_llm_chatgpt/rmq.py b/neon_llm_chatgpt/rmq.py index a252fa4..52fcceb 100644 --- a/neon_llm_chatgpt/rmq.py +++ b/neon_llm_chatgpt/rmq.py @@ -25,9 +25,11 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import pika + from neon_mq_connector.connector import MQConnector from neon_mq_connector.utils.network_utils import dict_to_b64 from neon_mq_connector.utils.rabbit_utils import create_mq_callback +from ovos_utils.log import LOG from neon_llm_chatgpt.chatgpt import ChatGPT from neon_llm_chatgpt.config import load_config @@ -88,3 +90,4 @@ def handle_request(self, properties=pika.BasicProperties( expiration=str(1000))) channel.basic_ack(method.delivery_tag) + LOG.info(f"Handled request: {message_id}") From 47b46b4452231e88284ffeb68d584ea967866e74 Mon Sep 17 00:00:00 2001 From: NeonDaniel Date: Tue, 6 Jun 2023 19:01:56 +0000 Subject: [PATCH 4/8] Increment Version to 0.0.1a3 --- version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.py b/version.py index cc11eeb..caf633f 100644 --- a/version.py +++ b/version.py @@ -26,4 +26,4 @@ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -__version__ = "0.0.1a2" +__version__ = "0.0.1a3" From 8a75118a8cf4e1dd8e80f6fa07626e22bba1a0c7 Mon Sep 17 00:00:00 2001 From: Daniel McKnight <34697904+NeonDaniel@users.noreply.github.com> Date: Fri, 30 Jun 2023 15:42:04 -0700 Subject: [PATCH 5/8] Update config handling to use envvars instead of FS overlay (#6) * Update config handling to use envvars instead of FS overlay * Update dependencies to stable versions --- Dockerfile | 5 +++++ docker_overlay/etc/OpenVoiceOS/ovos.conf | 11 ----------- requirements/requirements.txt | 4 ++-- 3 files changed, 7 insertions(+), 13 deletions(-) delete mode 100644 docker_overlay/etc/OpenVoiceOS/ovos.conf diff --git a/Dockerfile b/Dockerfile index 51dc8e0..822d628 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,10 @@ FROM python:3.9-slim +LABEL vendor=neon.ai \ + ai.neon.name="neon-llm-chatgpt" + +ENV OVOS_CONFIG_BASE_FOLDER neon +ENV OVOS_CONFIG_FILENAME diana.yaml ENV XDG_CONFIG_HOME /config COPY docker_overlay/ / diff --git a/docker_overlay/etc/OpenVoiceOS/ovos.conf b/docker_overlay/etc/OpenVoiceOS/ovos.conf deleted file mode 100644 index 46eba3c..0000000 --- a/docker_overlay/etc/OpenVoiceOS/ovos.conf +++ /dev/null @@ -1,11 +0,0 @@ -{ - "module_overrides": { - "diana": { - "base_folder": "neon", - "config_filename": "diana.yaml" - } - }, - "submodule_mappings": { - "neon_llm_chatgpt": "diana" - } -} \ No newline at end of file diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 1f12555..a769c8b 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,4 +1,4 @@ openai~=0.27 -neon-mq-connector>=0.6.1a9,~=0.6 +neon-mq-connector~=0.7 ovos-utils~=0.0.32 -ovos-config~=0.0.8 \ No newline at end of file +ovos-config~=0.0.10 \ No newline at end of file From 22bf82a31f49b2c2c41a288c49ef932740686d72 Mon Sep 17 00:00:00 2001 From: NeonDaniel Date: Fri, 30 Jun 2023 22:42:19 +0000 Subject: [PATCH 6/8] Increment Version to 0.0.1a4 --- version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.py b/version.py index caf633f..31b1136 100644 --- a/version.py +++ b/version.py @@ -26,4 +26,4 @@ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -__version__ = "0.0.1a3" +__version__ = "0.0.1a4" From 9aa54b3f8d1a52c389137599bf56776cdb079660 Mon Sep 17 00:00:00 2001 From: NeonDaniel Date: Fri, 30 Jun 2023 22:49:07 +0000 Subject: [PATCH 7/8] Increment Version to 0.0.1 --- version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.py b/version.py index 31b1136..ef7228e 100644 --- a/version.py +++ b/version.py @@ -26,4 +26,4 @@ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -__version__ = "0.0.1a4" +__version__ = "0.0.1" From dba3c610dae0836476999e4f39338d93f33fb7e8 Mon Sep 17 00:00:00 2001 From: NeonDaniel Date: Fri, 30 Jun 2023 22:49:28 +0000 Subject: [PATCH 8/8] Update Changelog --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c8e8477 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +## [0.0.1a4](https://github.com/NeonGeckoCom/neon-llm-chatgpt/tree/0.0.1a4) (2023-06-30) + +[Full Changelog](https://github.com/NeonGeckoCom/neon-llm-chatgpt/compare/0.0.1a3...0.0.1a4) + +**Merged pull requests:** + +- Update config handling to use envvars instead of FS overlay [\#6](https://github.com/NeonGeckoCom/neon-llm-chatgpt/pull/6) ([NeonDaniel](https://github.com/NeonDaniel)) + +## [0.0.1a3](https://github.com/NeonGeckoCom/neon-llm-chatgpt/tree/0.0.1a3) (2023-06-06) + +[Full Changelog](https://github.com/NeonGeckoCom/neon-llm-chatgpt/compare/0.0.1a2...0.0.1a3) + +**Merged pull requests:** + +- Add log for handled requests [\#4](https://github.com/NeonGeckoCom/neon-llm-chatgpt/pull/4) ([NeonDaniel](https://github.com/NeonDaniel)) + +## [0.0.1a2](https://github.com/NeonGeckoCom/neon-llm-chatgpt/tree/0.0.1a2) (2023-05-16) + +[Full Changelog](https://github.com/NeonGeckoCom/neon-llm-chatgpt/compare/babd77e0f173fbe3681927677602c72c58774ff0...0.0.1a2) + +**Implemented enhancements:** + +- \[FEAT\] Python Packaging [\#1](https://github.com/NeonGeckoCom/neon-llm-chatgpt/issues/1) + +**Merged pull requests:** + +- Update for packaging and deployment [\#3](https://github.com/NeonGeckoCom/neon-llm-chatgpt/pull/3) ([NeonDaniel](https://github.com/NeonDaniel)) + + + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*