From c8b5639944e123f2cb427d2699601e7bcd3336f7 Mon Sep 17 00:00:00 2001 From: Dmitry Afanasyev <71835315+Balshgit@users.noreply.github.com> Date: Thu, 28 Sep 2023 18:35:20 +0300 Subject: [PATCH] update gpt provider to latest changes (#17) --- README.md | 9 +- bot_microservice/api/bot/controllers.py | 3 +- bot_microservice/core/bot.py | 3 +- bot_microservice/core/commands.py | 11 +- bot_microservice/core/handlers.py | 15 +- bot_microservice/core/keyboards.py | 3 +- bot_microservice/core/logging.py | 3 +- bot_microservice/core/utils.py | 3 +- bot_microservice/main.py | 5 +- bot_microservice/routers.py | 5 +- bot_microservice/settings/config.py | 3 +- .../tests/integration/bot/conftest.py | 9 +- .../tests/integration/bot/test_bot_updates.py | 3 +- .../tests/integration/factories/bot.py | 1 + chat_gpt_microservice/.gitignore | 157 ++++++++++ chat_gpt_microservice/README.md | 2 +- chat_gpt_microservice/git-clang-format.py | 280 ++++++++---------- docker-compose.yml | 2 - pyproject.toml | 2 +- ...pt_chat_bot.service => gptchatbot.service} | 4 +- 20 files changed, 330 insertions(+), 193 deletions(-) create mode 100644 chat_gpt_microservice/.gitignore rename scripts/{gpt_chat_bot.service => gptchatbot.service} (63%) diff --git a/README.md b/README.md index d2f0b79..05335c0 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,14 @@ install service - sudo cp scripts/gpt_chat_bot.service /etc/systemd/system +```bash +sudo cp scripts/gptchatbot.service /etc/systemd/system +sudo systemctl enable gptchatbot.service +sudo systemctl start gptchatbot.service +sudo +``` + + ```bash cd ~/PycharmProjects/chat_gpt_bot diff --git a/bot_microservice/api/bot/controllers.py b/bot_microservice/api/bot/controllers.py index 6019446..afc501e 100644 --- a/bot_microservice/api/bot/controllers.py +++ b/bot_microservice/api/bot/controllers.py @@ -1,8 +1,9 @@ from fastapi import APIRouter, Request -from settings.config import settings from starlette import status from starlette.responses import Response +from settings.config import settings + router = APIRouter() diff --git a/bot_microservice/core/bot.py b/bot_microservice/core/bot.py index cd2051e..6d231dd 100644 --- a/bot_microservice/core/bot.py +++ b/bot_microservice/core/bot.py @@ -8,10 +8,11 @@ from typing import Any from fastapi import Request, Response from loguru import logger -from settings.config import AppSettings from telegram import Update from telegram.ext import Application +from settings.config import AppSettings + class BotApplication: def __init__( diff --git a/bot_microservice/core/commands.py b/bot_microservice/core/commands.py index f6d7aee..3305797 100644 --- a/bot_microservice/core/commands.py +++ b/bot_microservice/core/commands.py @@ -5,14 +5,15 @@ from urllib.parse import urljoin from uuid import uuid4 import httpx +from httpx import AsyncClient, AsyncHTTPTransport +from loguru import logger +from telegram import InlineKeyboardMarkup, Update +from telegram.ext import ContextTypes + from constants import CHAT_GPT_BASE_URL, BotEntryPoints from core.keyboards import main_keyboard from core.utils import SpeechToTextService -from httpx import AsyncClient, AsyncHTTPTransport -from loguru import logger from settings.config import settings -from telegram import InlineKeyboardMarkup, Update -from telegram.ext import ContextTypes async def main_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: @@ -37,7 +38,7 @@ async def about_bot(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: return None await update.effective_message.reply_text( "Бот использует бесплатную модель Chat-GPT3.5 для ответов на вопросы. " - "Принимает запросы на разных языках. \n\nБот так же умеет переводить голосовые сообщения в текст." + "Принимает запросы на разных языках. \n\nБот так же умеет переводить голосовые сообщения в текст. " "Просто пришлите голосовуху и получите поток сознания без запятых в виде текста", parse_mode='Markdown', ) diff --git a/bot_microservice/core/handlers.py b/bot_microservice/core/handlers.py index 22c563f..97552a4 100644 --- a/bot_microservice/core/handlers.py +++ b/bot_microservice/core/handlers.py @@ -1,6 +1,14 @@ from dataclasses import dataclass, field from typing import Any +from telegram.ext import ( + CallbackQueryHandler, + CommandHandler, + ConversationHandler, + MessageHandler, + filters, +) + from constants import BotEntryPoints, BotStagesEnum from core.commands import ( about_bot, @@ -11,13 +19,6 @@ from core.commands import ( voice_recognize, website, ) -from telegram.ext import ( - CallbackQueryHandler, - CommandHandler, - ConversationHandler, - MessageHandler, - filters, -) @dataclass diff --git a/bot_microservice/core/keyboards.py b/bot_microservice/core/keyboards.py index e9d5d01..5eeb849 100644 --- a/bot_microservice/core/keyboards.py +++ b/bot_microservice/core/keyboards.py @@ -1,6 +1,7 @@ -from constants import BotStagesEnum from telegram import InlineKeyboardButton +from constants import BotStagesEnum + main_keyboard = [ [ InlineKeyboardButton("Обо мне", callback_data=str(BotStagesEnum.about_me)), diff --git a/bot_microservice/core/logging.py b/bot_microservice/core/logging.py index b61245a..5e96c33 100644 --- a/bot_microservice/core/logging.py +++ b/bot_microservice/core/logging.py @@ -3,9 +3,10 @@ import sys from types import FrameType from typing import TYPE_CHECKING, Any, cast -from constants import LogLevelEnum from loguru import logger from sentry_sdk.integrations.logging import EventHandler + +from constants import LogLevelEnum from settings.config import settings if TYPE_CHECKING: diff --git a/bot_microservice/core/utils.py b/bot_microservice/core/utils.py index 63090d8..f60fec0 100644 --- a/bot_microservice/core/utils.py +++ b/bot_microservice/core/utils.py @@ -5,7 +5,6 @@ from datetime import datetime, timedelta from functools import lru_cache, wraps from typing import Any -from constants import AUDIO_SEGMENT_DURATION from loguru import logger from pydub import AudioSegment from speech_recognition import ( @@ -14,6 +13,8 @@ from speech_recognition import ( UnknownValueError as SpeechRecognizerError, ) +from constants import AUDIO_SEGMENT_DURATION + def timed_cache(**timedelta_kwargs: Any) -> Any: def _wrapper(func: Any) -> Any: diff --git a/bot_microservice/main.py b/bot_microservice/main.py index ee45b1f..3ec940a 100644 --- a/bot_microservice/main.py +++ b/bot_microservice/main.py @@ -2,12 +2,13 @@ import asyncio from functools import cached_property import sentry_sdk +from fastapi import FastAPI +from fastapi.responses import UJSONResponse + from constants import LogLevelEnum from core.bot import BotApplication, BotQueue from core.handlers import bot_event_handlers from core.logging import configure_logging -from fastapi import FastAPI -from fastapi.responses import UJSONResponse from routers import api_router from settings.config import AppSettings, get_settings diff --git a/bot_microservice/routers.py b/bot_microservice/routers.py index 843df5b..fa70dd0 100644 --- a/bot_microservice/routers.py +++ b/bot_microservice/routers.py @@ -1,7 +1,8 @@ -from api.bot.controllers import router as bot_router -from api.system.controllers import router as system_router from fastapi import APIRouter from fastapi.responses import ORJSONResponse + +from api.bot.controllers import router as bot_router +from api.system.controllers import router as system_router from settings.config import settings api_router = APIRouter( diff --git a/bot_microservice/settings/config.py b/bot_microservice/settings/config.py index d9d2f03..5c3ddc6 100644 --- a/bot_microservice/settings/config.py +++ b/bot_microservice/settings/config.py @@ -3,11 +3,12 @@ from os import environ from pathlib import Path from typing import Any -from constants import API_PREFIX from dotenv import load_dotenv from pydantic import HttpUrl, ValidationInfo, field_validator from pydantic_settings import BaseSettings +from constants import API_PREFIX + BASE_DIR = Path(__file__).parent.parent SHARED_DIR = BASE_DIR.resolve().joinpath("shared") SHARED_DIR.mkdir(exist_ok=True) diff --git a/bot_microservice/tests/integration/bot/conftest.py b/bot_microservice/tests/integration/bot/conftest.py index e7c68dc..3020698 100644 --- a/bot_microservice/tests/integration/bot/conftest.py +++ b/bot_microservice/tests/integration/bot/conftest.py @@ -9,15 +9,16 @@ from typing import Any, AsyncGenerator import pytest import pytest_asyncio -from core.bot import BotApplication -from core.handlers import bot_event_handlers from fastapi import FastAPI from httpx import AsyncClient -from main import Application as AppApplication from pytest_asyncio.plugin import SubRequest -from settings.config import AppSettings, get_settings from telegram import Bot, User from telegram.ext import Application, ApplicationBuilder, Defaults, ExtBot + +from core.bot import BotApplication +from core.handlers import bot_event_handlers +from main import Application as AppApplication +from settings.config import AppSettings, get_settings from tests.integration.bot.networking import NonchalantHttpxRequest from tests.integration.factories.bot import BotInfoFactory diff --git a/bot_microservice/tests/integration/bot/test_bot_updates.py b/bot_microservice/tests/integration/bot/test_bot_updates.py index 68bd088..6ccf52d 100644 --- a/bot_microservice/tests/integration/bot/test_bot_updates.py +++ b/bot_microservice/tests/integration/bot/test_bot_updates.py @@ -5,9 +5,10 @@ from typing import Any import pytest from assertpy import assert_that -from core.bot import BotApplication, BotQueue from faker import Faker from httpx import AsyncClient + +from core.bot import BotApplication, BotQueue from main import Application from tests.integration.bot.networking import MockedRequest from tests.integration.factories.bot import ( diff --git a/bot_microservice/tests/integration/factories/bot.py b/bot_microservice/tests/integration/factories/bot.py index 3704730..be0d25f 100644 --- a/bot_microservice/tests/integration/factories/bot.py +++ b/bot_microservice/tests/integration/factories/bot.py @@ -2,6 +2,7 @@ import string import factory from faker import Faker + from tests.integration.factories.models import Chat, User faker = Faker("ru_RU") diff --git a/chat_gpt_microservice/.gitignore b/chat_gpt_microservice/.gitignore new file mode 100644 index 0000000..e5279d7 --- /dev/null +++ b/chat_gpt_microservice/.gitignore @@ -0,0 +1,157 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# others +.config/ +.upm/ +.vscode/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/chat_gpt_microservice/README.md b/chat_gpt_microservice/README.md index 6a42d33..7b2dcb9 100644 --- a/chat_gpt_microservice/README.md +++ b/chat_gpt_microservice/README.md @@ -119,4 +119,4 @@ Please note the following: By using this repository or any code related to it, you agree to these terms. The author is not responsible for any copies, forks, or reuploads made by other users. This is the author's only account and repository. To prevent -impersonation or irresponsible actions, you may comply with the GNU GPL license this Repository uses +impersonation or irresponsible actions, you may comply with the GNU GPL license this Repository uses. diff --git a/chat_gpt_microservice/git-clang-format.py b/chat_gpt_microservice/git-clang-format.py index cdb3042..d7821ac 100644 --- a/chat_gpt_microservice/git-clang-format.py +++ b/chat_gpt_microservice/git-clang-format.py @@ -33,9 +33,9 @@ import re import subprocess import sys -usage = "git clang-format [OPTIONS] [] [] [--] [...]" +usage = 'git clang-format [OPTIONS] [] [] [--] [...]' -desc = """ +desc = ''' If zero or one commits are given, run clang-format on all lines that differ between the working directory and , which defaults to HEAD. Changes are only applied to the working directory. @@ -48,14 +48,14 @@ The following git-config settings set the default of the corresponding option: clangFormat.commit clangFormat.extension clangFormat.style -""" +''' # Name of the temporary index file in which save the output of clang-format. # This file is created within the .git directory. -temp_index_basename = "clang-format-index" +temp_index_basename = 'clang-format-index' -Range = collections.namedtuple("Range", "start, count") +Range = collections.namedtuple('Range', 'start, count') def main(): @@ -66,87 +66,61 @@ def main(): # nargs=argparse.REMAINDER disallows options after positionals.) argv = sys.argv[1:] try: - idx = argv.index("--") + idx = argv.index('--') except ValueError: dash_dash = [] else: dash_dash = argv[idx:] argv = argv[:idx] - default_extensions = ",".join( + default_extensions = ','.join( [ # From clang/lib/Frontend/FrontendOptions.cpp, all lower case - "c", - "h", # C - "m", # ObjC - "mm", # ObjC++ - "cc", - "cp", - "cpp", - "c++", - "cxx", - "hh", - "hpp", - "hxx", # C++ - "cu", # CUDA + 'c', + 'h', # C + 'm', # ObjC + 'mm', # ObjC++ + 'cc', + 'cp', + 'cpp', + 'c++', + 'cxx', + 'hh', + 'hpp', + 'hxx', # C++ + 'cu', # CUDA # Other languages that clang-format supports - "proto", - "protodevel", # Protocol Buffers - "java", # Java - "js", # JavaScript - "ts", # TypeScript - "cs", # C Sharp + 'proto', + 'protodevel', # Protocol Buffers + 'java', # Java + 'js', # JavaScript + 'ts', # TypeScript + 'cs', # C Sharp ] ) - p = argparse.ArgumentParser( - usage=usage, - formatter_class=argparse.RawDescriptionHelpFormatter, - description=desc, - ) + p = argparse.ArgumentParser(usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter, description=desc) + p.add_argument('--binary', default=config.get('clangformat.binary', 'clang-format'), help='path to clang-format'), p.add_argument( - "--binary", - default=config.get("clangformat.binary", "clang-format"), - help="path to clang-format", + '--commit', default=config.get('clangformat.commit', 'HEAD'), help='default commit to use if none is specified' ), + p.add_argument('--diff', action='store_true', help='print a diff instead of applying the changes') p.add_argument( - "--commit", - default=config.get("clangformat.commit", "HEAD"), - help="default commit to use if none is specified", + '--extensions', + default=config.get('clangformat.extensions', default_extensions), + help=('comma-separated list of file extensions to format, ' 'excluding the period and case-insensitive'), ), - p.add_argument( - "--diff", - action="store_true", - help="print a diff instead of applying the changes", - ) - p.add_argument( - "--extensions", - default=config.get("clangformat.extensions", default_extensions), - help=("comma-separated list of file extensions to format, " "excluding the period and case-insensitive"), - ), - p.add_argument("-f", "--force", action="store_true", help="allow changes to unstaged files") - p.add_argument("-p", "--patch", action="store_true", help="select hunks interactively") - p.add_argument("-q", "--quiet", action="count", default=0, help="print less information") - p.add_argument( - "--style", - default=config.get("clangformat.style", None), - help="passed to clang-format", - ), - p.add_argument("-v", "--verbose", action="count", default=0, help="print extra information") + p.add_argument('-f', '--force', action='store_true', help='allow changes to unstaged files') + p.add_argument('-p', '--patch', action='store_true', help='select hunks interactively') + p.add_argument('-q', '--quiet', action='count', default=0, help='print less information') + p.add_argument('--style', default=config.get('clangformat.style', None), help='passed to clang-format'), + p.add_argument('-v', '--verbose', action='count', default=0, help='print extra information') # We gather all the remaining positional arguments into 'args' since we need # to use some heuristics to determine whether or not was present. # However, to print pretty messages, we make use of metavar and help. + p.add_argument('args', nargs='*', metavar='', help='revision from which to compute the diff') p.add_argument( - "args", - nargs="*", - metavar="", - help="revision from which to compute the diff", - ) - p.add_argument( - "ignored", - nargs="*", - metavar="...", - help="if specified, only consider differences in these files", + 'ignored', nargs='*', metavar='...', help='if specified, only consider differences in these files' ) opts = p.parse_args(argv) @@ -156,26 +130,26 @@ def main(): commits, files = interpret_args(opts.args, dash_dash, opts.commit) if len(commits) > 1: if not opts.diff: - die("--diff is required when two commits are given") + die('--diff is required when two commits are given') else: if len(commits) > 2: - die("at most two commits allowed; %d given" % len(commits)) + die('at most two commits allowed; %d given' % len(commits)) changed_lines = compute_diff_and_extract_lines(commits, files) if opts.verbose >= 1: ignored_files = set(changed_lines) - filter_by_extension(changed_lines, opts.extensions.lower().split(",")) + filter_by_extension(changed_lines, opts.extensions.lower().split(',')) if opts.verbose >= 1: ignored_files.difference_update(changed_lines) if ignored_files: - print("Ignoring changes in the following files (wrong extension):") + print('Ignoring changes in the following files (wrong extension):') for filename in ignored_files: - print(" %s" % filename) + print(' %s' % filename) if changed_lines: - print("Running clang-format on the following files:") + print('Running clang-format on the following files:') for filename in changed_lines: - print(" %s" % filename) + print(' %s' % filename) if not changed_lines: - print("no modified files to format") + print('no modified files to format') return # The computed diff outputs absolute paths, so we must cd before accessing # those files. @@ -189,19 +163,19 @@ def main(): old_tree = create_tree_from_workdir(changed_lines) new_tree = run_clang_format_and_save_to_tree(changed_lines, binary=opts.binary, style=opts.style) if opts.verbose >= 1: - print("old tree: %s" % old_tree) - print("new tree: %s" % new_tree) + print('old tree: %s' % old_tree) + print('new tree: %s' % new_tree) if old_tree == new_tree: if opts.verbose >= 0: - print("clang-format did not modify any files") + print('clang-format did not modify any files') elif opts.diff: print_diff(old_tree, new_tree) else: changed_files = apply_changes(old_tree, new_tree, force=opts.force, patch_mode=opts.patch) if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: - print("changed files:") + print('changed files:') for filename in changed_files: - print(" %s" % filename) + print(' %s' % filename) def load_git_config(non_string_options=None): @@ -213,11 +187,11 @@ def load_git_config(non_string_options=None): if non_string_options is None: non_string_options = {} out = {} - for entry in run("git", "config", "--list", "--null").split("\0"): + for entry in run('git', 'config', '--list', '--null').split('\0'): if entry: - name, value = entry.split("\n", 1) + name, value = entry.split('\n', 1) if name in non_string_options: - value = run("git", "config", non_string_options[name], name) + value = run('git', 'config', non_string_options[name], name) out[name] = value return out @@ -239,7 +213,7 @@ def interpret_args(args, dash_dash, default_commit): commits = args for commit in commits: object_type = get_object_type(commit) - if object_type not in ("commit", "tag"): + if object_type not in ('commit', 'tag'): if object_type is None: die("'%s' is not a commit" % commit) else: @@ -264,19 +238,19 @@ def disambiguate_revision(value): """Returns True if `value` is a revision, False if it is a file, or dies.""" # If `value` is ambiguous (neither a commit nor a file), the following # command will die with an appropriate error message. - run("git", "rev-parse", value, verbose=False) + run('git', 'rev-parse', value, verbose=False) object_type = get_object_type(value) if object_type is None: return False - if object_type in ("commit", "tag"): + if object_type in ('commit', 'tag'): return True - die("`%s` is a %s, but a commit or filename was expected" % (value, object_type)) + die('`%s` is a %s, but a commit or filename was expected' % (value, object_type)) def get_object_type(value): """Returns a string description of an object's type, or None if it is not a valid git object.""" - cmd = ["git", "cat-file", "-t", value] + cmd = ['git', 'cat-file', '-t', value] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode != 0: @@ -303,10 +277,10 @@ def compute_diff(commits, files): differences between the working directory and the first commit if a single one was specified, or the difference between both specified commits, filtered on `files` (if non-empty). Zero context lines are used in the patch.""" - git_tool = "diff-index" + git_tool = 'diff-index' if len(commits) > 1: - git_tool = "diff-tree" - cmd = ["git", git_tool, "-p", "-U0"] + commits + ["--"] + git_tool = 'diff-tree' + cmd = ['git', git_tool, '-p', '-U0'] + commits + ['--'] cmd.extend(files) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.stdin.close() @@ -325,10 +299,10 @@ def extract_lines(patch_file): matches = {} for line in patch_file: line = convert_string(line) - match = re.search(r"^\+\+\+\ [^/]+/(.*)", line) + match = re.search(r'^\+\+\+\ [^/]+/(.*)', line) if match: - filename = match.group(1).rstrip("\r\n") - match = re.search(r"^@@ -[0-9,]+ \+(\d+)(,(\d+))?", line) + filename = match.group(1).rstrip('\r\n') + match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line) if match: start_line = int(match.group(1)) line_count = 1 @@ -346,8 +320,8 @@ def filter_by_extension(dictionary, allowed_extensions): excluding the period.""" allowed_extensions = frozenset(allowed_extensions) for filename in list(dictionary.keys()): - base_ext = filename.rsplit(".", 1) - if len(base_ext) == 1 and "" in allowed_extensions: + base_ext = filename.rsplit('.', 1) + if len(base_ext) == 1 and '' in allowed_extensions: continue if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions: del dictionary[filename] @@ -355,7 +329,7 @@ def filter_by_extension(dictionary, allowed_extensions): def cd_to_toplevel(): """Change to the top level of the git repository.""" - toplevel = run("git", "rev-parse", "--show-toplevel") + toplevel = run('git', 'rev-parse', '--show-toplevel') os.chdir(toplevel) @@ -363,10 +337,10 @@ def create_tree_from_workdir(filenames): """Create a new git tree with the given files from the working directory. Returns the object ID (SHA-1) of the created tree.""" - return create_tree(filenames, "--stdin") + return create_tree(filenames, '--stdin') -def run_clang_format_and_save_to_tree(changed_lines, revision=None, binary="clang-format", style=None): +def run_clang_format_and_save_to_tree(changed_lines, revision=None, binary='clang-format', style=None): """Run clang-format on each file and save the result to a git tree. Returns the object ID (SHA-1) of the created tree.""" @@ -381,9 +355,9 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None, binary="clan for filename, line_ranges in iteritems(changed_lines): if revision: git_metadata_cmd = [ - "git", - "ls-tree", - "%s:%s" % (revision, os.path.dirname(filename)), + 'git', + 'ls-tree', + '%s:%s' % (revision, os.path.dirname(filename)), os.path.basename(filename), ] git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) @@ -392,12 +366,12 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None, binary="clan else: mode = oct(os.stat(filename).st_mode) # Adjust python3 octal format so that it matches what git expects - if mode.startswith("0o"): - mode = "0" + mode[2:] + if mode.startswith('0o'): + mode = '0' + mode[2:] blob_id = clang_format_to_blob(filename, line_ranges, revision=revision, binary=binary, style=style) - yield "%s %s\t%s" % (mode, blob_id, filename) + yield '%s %s\t%s' % (mode, blob_id, filename) - return create_tree(index_info_generator(), "--index-info") + return create_tree(index_info_generator(), '--index-info') def create_tree(input_lines, mode): @@ -407,20 +381,20 @@ def create_tree(input_lines, mode): '--index-info' is must be a list of values suitable for "git update-index --index-info", such as " ". Any other mode is invalid.""" - assert mode in ("--stdin", "--index-info") - cmd = ["git", "update-index", "--add", "-z", mode] + assert mode in ('--stdin', '--index-info') + cmd = ['git', 'update-index', '--add', '-z', mode] with temporary_index_file(): p = subprocess.Popen(cmd, stdin=subprocess.PIPE) for line in input_lines: - p.stdin.write(to_bytes("%s\0" % line)) + p.stdin.write(to_bytes('%s\0' % line)) p.stdin.close() if p.wait() != 0: - die("`%s` failed" % " ".join(cmd)) - tree_id = run("git", "write-tree") + die('`%s` failed' % ' '.join(cmd)) + tree_id = run('git', 'write-tree') return tree_id -def clang_format_to_blob(filename, line_ranges, revision=None, binary="clang-format", style=None): +def clang_format_to_blob(filename, line_ranges, revision=None, binary='clang-format', style=None): """Run clang-format on the given file and save the result to a git blob. Runs on the file in `revision` if not None, or on the file in the working @@ -429,13 +403,13 @@ def clang_format_to_blob(filename, line_ranges, revision=None, binary="clang-for Returns the object ID (SHA-1) of the created blob.""" clang_format_cmd = [binary] if style: - clang_format_cmd.extend(["-style=" + style]) + clang_format_cmd.extend(['-style=' + style]) clang_format_cmd.extend( - ["-lines=%s:%s" % (start_line, start_line + line_count - 1) for start_line, line_count in line_ranges] + ['-lines=%s:%s' % (start_line, start_line + line_count - 1) for start_line, line_count in line_ranges] ) if revision: - clang_format_cmd.extend(["-assume-filename=" + filename]) - git_show_cmd = ["git", "cat-file", "blob", "%s:%s" % (revision, filename)] + clang_format_cmd.extend(['-assume-filename=' + filename]) + git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)] git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) git_show.stdin.close() clang_format_stdin = git_show.stdout @@ -453,17 +427,17 @@ def clang_format_to_blob(filename, line_ranges, revision=None, binary="clang-for else: raise clang_format_stdin.close() - hash_object_cmd = ["git", "hash-object", "-w", "--path=" + filename, "--stdin"] + hash_object_cmd = ['git', 'hash-object', '-w', '--path=' + filename, '--stdin'] hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout, stdout=subprocess.PIPE) clang_format.stdout.close() stdout = hash_object.communicate()[0] if hash_object.returncode != 0: - die("`%s` failed" % " ".join(hash_object_cmd)) + die('`%s` failed' % ' '.join(hash_object_cmd)) if clang_format.wait() != 0: - die("`%s` failed" % " ".join(clang_format_cmd)) + die('`%s` failed' % ' '.join(clang_format_cmd)) if git_show and git_show.wait() != 0: - die("`%s` failed" % " ".join(git_show_cmd)) - return convert_string(stdout).rstrip("\r\n") + die('`%s` failed' % ' '.join(git_show_cmd)) + return convert_string(stdout).rstrip('\r\n') @contextlib.contextmanager @@ -471,15 +445,15 @@ def temporary_index_file(tree=None): """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting the file afterward.""" index_path = create_temporary_index(tree) - old_index_path = os.environ.get("GIT_INDEX_FILE") - os.environ["GIT_INDEX_FILE"] = index_path + old_index_path = os.environ.get('GIT_INDEX_FILE') + os.environ['GIT_INDEX_FILE'] = index_path try: yield finally: if old_index_path is None: - del os.environ["GIT_INDEX_FILE"] + del os.environ['GIT_INDEX_FILE'] else: - os.environ["GIT_INDEX_FILE"] = old_index_path + os.environ['GIT_INDEX_FILE'] = old_index_path os.remove(index_path) @@ -488,11 +462,11 @@ def create_temporary_index(tree=None): If `tree` is not None, use that as the tree to read in. Otherwise, an empty index is created.""" - gitdir = run("git", "rev-parse", "--git-dir") + gitdir = run('git', 'rev-parse', '--git-dir') path = os.path.join(gitdir, temp_index_basename) if tree is None: - tree = "--empty" - run("git", "read-tree", "--index-output=" + path, tree) + tree = '--empty' + run('git', 'read-tree', '--index-output=' + path, tree) return path @@ -505,7 +479,7 @@ def print_diff(old_tree, new_tree): # We also only print modified files since `new_tree` only contains the files # that were modified, so unmodified files would show as deleted without the # filter. - subprocess.check_call(["git", "diff", "--diff-filter=M", old_tree, new_tree, "--"]) + subprocess.check_call(['git', 'diff', '--diff-filter=M', old_tree, new_tree, '--']) def apply_changes(old_tree, new_tree, force=False, patch_mode=False): @@ -514,28 +488,16 @@ def apply_changes(old_tree, new_tree, force=False, patch_mode=False): Bails if there are local changes in those files and not `force`. If `patch_mode`, runs `git checkout --patch` to select hunks interactively.""" changed_files = ( - run( - "git", - "diff-tree", - "--diff-filter=M", - "-r", - "-z", - "--name-only", - old_tree, - new_tree, - ) - .rstrip("\0") - .split("\0") + run('git', 'diff-tree', '--diff-filter=M', '-r', '-z', '--name-only', old_tree, new_tree) + .rstrip('\0') + .split('\0') ) if not force: - unstaged_files = run("git", "diff-files", "--name-status", *changed_files) + unstaged_files = run('git', 'diff-files', '--name-status', *changed_files) if unstaged_files: - print( - "The following files would be modified but " "have unstaged changes:", - file=sys.stderr, - ) + print('The following files would be modified but ' 'have unstaged changes:', file=sys.stderr) print(unstaged_files, file=sys.stderr) - print("Please commit, stage, or stash them first.", file=sys.stderr) + print('Please commit, stage, or stash them first.', file=sys.stderr) sys.exit(2) if patch_mode: # In patch mode, we could just as well create an index from the new tree @@ -545,17 +507,17 @@ def apply_changes(old_tree, new_tree, force=False, patch_mode=False): # better message, "Apply ... to index and worktree". This is not quite # right, since it won't be applied to the user's index, but oh well. with temporary_index_file(old_tree): - subprocess.check_call(["git", "checkout", "--patch", new_tree]) + subprocess.check_call(['git', 'checkout', '--patch', new_tree]) else: with temporary_index_file(new_tree): - run("git", "checkout-index", "-a", "-f") + run('git', 'checkout-index', '-a', '-f') return changed_files def run(*args, **kwargs): - stdin = kwargs.pop("stdin", "") - verbose = kwargs.pop("verbose", True) - strip = kwargs.pop("strip", True) + stdin = kwargs.pop('stdin', '') + verbose = kwargs.pop('verbose', True) + strip = kwargs.pop('strip', True) for name in kwargs: raise TypeError("run() got an unexpected keyword argument '%s'" % name) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) @@ -567,20 +529,20 @@ def run(*args, **kwargs): if p.returncode == 0: if stderr: if verbose: - print("`%s` printed to stderr:" % " ".join(args), file=sys.stderr) + print('`%s` printed to stderr:' % ' '.join(args), file=sys.stderr) print(stderr.rstrip(), file=sys.stderr) if strip: - stdout = stdout.rstrip("\r\n") + stdout = stdout.rstrip('\r\n') return stdout if verbose: - print("`%s` returned %s" % (" ".join(args), p.returncode), file=sys.stderr) + print('`%s` returned %s' % (' '.join(args), p.returncode), file=sys.stderr) if stderr: print(stderr.rstrip(), file=sys.stderr) sys.exit(2) def die(message): - print("error:", message, file=sys.stderr) + print('error:', message, file=sys.stderr) sys.exit(2) @@ -588,23 +550,23 @@ def to_bytes(str_input): # Encode to UTF-8 to get binary data. if isinstance(str_input, bytes): return str_input - return str_input.encode("utf-8") + return str_input.encode('utf-8') def to_string(bytes_input): if isinstance(bytes_input, str): return bytes_input - return bytes_input.encode("utf-8") + return bytes_input.encode('utf-8') def convert_string(bytes_input): try: - return to_string(bytes_input.decode("utf-8")) + return to_string(bytes_input.decode('utf-8')) except AttributeError: # 'str' object has no attribute 'decode'. return str(bytes_input) except UnicodeError: return str(bytes_input) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/docker-compose.yml b/docker-compose.yml index b6f8bba..5a65fd5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,8 +39,6 @@ services: restart: unless-stopped environment: CHAT_PATH: "/gpt/chat" - volumes: - - ./chat_gpt_microservice/client:/app/client networks: chat-gpt-network: ipv4_address: 200.20.0.11 diff --git a/pyproject.toml b/pyproject.toml index 379da4f..3f76277 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -127,7 +127,7 @@ remove-duplicate-keys = true [tool.isort] profile = "black" multi_line_output = 3 -src_paths = ["app", "settings", "tests"] +src_paths = ["bot_microservice",] combine_as_imports = true diff --git a/scripts/gpt_chat_bot.service b/scripts/gptchatbot.service similarity index 63% rename from scripts/gpt_chat_bot.service rename to scripts/gptchatbot.service index ee23e31..fc9c276 100644 --- a/scripts/gpt_chat_bot.service +++ b/scripts/gptchatbot.service @@ -4,8 +4,8 @@ Wants=network-online.target After=network-online.target [Service] Restart=always -WorkingDirectory=/opt/chat_gpt_bot -ExecStart=bash -c "STAGE=production docker compose -f /opt/gpt_chat_bot/docker-compose.yml up" +WorkingDirectory=/opt/gpt_chat_bot +ExecStart=bash -c "docker compose -f /opt/gpt_chat_bot/docker-compose.yml up" ExecStop=bash -c "docker compose -f /opt/gpt_chat_bot/docker-compose.yml down" [Install] WantedBy=multi-user.target \ No newline at end of file