commit cb5f504d31ca93c564babd78183ac1c5e4e099ec Author: Sviatoslav Tsariov Date: Wed Feb 28 20:45:03 2024 +0300 initial commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..763bdb6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,142 @@ +.git +Dockerfile +.DS_Store +.gitignore +.dockerignore + +/credentials +/cache +/store + +/node_modules + +# https://github.com/github/gitignore/blob/master/Global/macOS.gitignore + +# General +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# 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/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68bc17f --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# 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 + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__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/ + +# PyCharm +# JetBrains specific template is maintained 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/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3b3403f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.10-slim + +WORKDIR /app + +COPY requirements.txt /app +RUN apt-get update && apt-get install git -y +RUN pip3 install -r requirements.txt +RUN pip3 install "git+https://github.com/openai/whisper.git" +RUN apt-get install -y ffmpeg + +RUN whisper --model medium --language ru dummy.wav; exit 0 +RUN whisper --model small --language ru dummy.wav; exit 0 + +COPY . . + +EXPOSE 5000 + +ENV FLASK_APP=src/app.py + +CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"] diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..840ef13 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,10 @@ +version: '2.1' +services: + harpyia: + container_name: harpyia + image: harpyia + build: . + ports: + - "8080:5000" + volumes: + - "./src:/app/src" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a3e0ffb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +flask==3.0.2 +Jinja2==3.1.3 +blinker==1.7.0 +Werkzeug==3.0.1 +click==8.1.7 +itsdangerous==2.1.2 +MarkupSafe==2.1.5 +python-dotenv==1.0.1 diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..25ac26a --- /dev/null +++ b/src/app.py @@ -0,0 +1,67 @@ +from flask import Flask, abort, request +from tempfile import NamedTemporaryFile +from dotenv import load_dotenv +import os +import whisper +import torch +import sys +import re + +load_dotenv() + +HARPYIA_PROMPT = os.getenv('HARPYIA_PROMPT') or 'спасите помогите на помощь пожар' +HARPYIA_MODEL = os.getenv('HARPYIA_MODEL') or 'medium' +HARPYIA_LANGUAGE = os.getenv('HARPYIA_LANGUAGE') or 'ru' + +# Check if NVIDIA GPU is available +DEVICE = "cuda" if torch.cuda.is_available() else "cpu" + +# Load the Whisper model: +model = whisper.load_model(HARPYIA_MODEL, device=DEVICE) + +app = Flask(__name__) + +@app.route("/") +def hello(): + return "To recognize an audio file, upload it using a POST request with '/recognize' or '/recognize_number' route." + +def recognize_files(handler_fn): + if not request.files: + abort(400) + + results = [] + + for filename, handle in request.files.items(): + temp = NamedTemporaryFile() + handle.save(temp) + result = model.transcribe(temp.name, language=HARPYIA_LANGUAGE, initial_prompt=HARPYIA_PROMPT) + results.append({ + 'filename': filename, + 'transcript': handler_fn(result['text']), + }) + + print(results, file=sys.stderr) + return {'results': results} + +@app.route('/recognize', methods=['POST']) +def recognize(): + return recognize_files(lambda text: text) + +@app.route('/recognize_number', methods=['POST']) +def recognize_number(): + return recognize_files(transfer_and_clean) + +def transfer_and_clean(input_string): + number_mapping = { + "один": "1", + "два": "2", + "три": "3" + } + + for word, number in number_mapping.items(): + input_string = input_string.replace(word, number) + + input_string = re.sub(r'[^\d]+', '', input_string) + + return input_string +