Spaces:
Configuration error
Configuration error
Fedir Zadniprovskyi
commited on
Commit
·
d0feed8
1
Parent(s):
aa5390b
feat: add more pre-commit hooks
Browse files- .pre-commit-config.yaml +38 -18
- Dockerfile.cpu +5 -2
- Dockerfile.cuda +5 -2
- pre-commit-scripts/pyright.sh +4 -0
- pre-commit-scripts/ruff-format.sh +2 -0
- pre-commit-scripts/ruff-lint.sh +2 -0
- pyproject.toml +5 -0
- speaches/main.py +14 -6
- speaches/server_models.py +1 -1
- tests/app_test.py +7 -5
.pre-commit-config.yaml
CHANGED
|
@@ -8,21 +8,41 @@ repos:
|
|
| 8 |
- id: end-of-file-fixer
|
| 9 |
- id: check-yaml
|
| 10 |
- id: check-added-large-files
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
- id: end-of-file-fixer
|
| 9 |
- id: check-yaml
|
| 10 |
- id: check-added-large-files
|
| 11 |
+
- repo: https://github.com/python-jsonschema/check-jsonschema
|
| 12 |
+
rev: 0.28.4
|
| 13 |
+
hooks:
|
| 14 |
+
- id: check-taskfile
|
| 15 |
+
- repo: https://github.com/rhysd/actionlint
|
| 16 |
+
rev: v1.7.0
|
| 17 |
+
hooks:
|
| 18 |
+
- id: actionlint
|
| 19 |
+
- repo: https://github.com/IamTheFij/docker-pre-commit
|
| 20 |
+
rev: v3.0.1
|
| 21 |
+
hooks:
|
| 22 |
+
- id: docker-compose-check
|
| 23 |
+
- repo: https://github.com/hadolint/hadolint
|
| 24 |
+
rev: v2.12.0
|
| 25 |
+
hooks:
|
| 26 |
+
- id: hadolint
|
| 27 |
+
- repo: https://github.com/shellcheck-py/shellcheck-py
|
| 28 |
+
rev: v0.10.0.1
|
| 29 |
+
hooks:
|
| 30 |
+
- id: shellcheck
|
| 31 |
+
# NOTE: not using https://github.com/RobertCraigie/pyright-python because it doesn't work with poetry virtual environments
|
| 32 |
+
# NOTE: not using github.com/astral-sh/ruff-pre-commit because it doesn't work on NixOS
|
| 33 |
+
- repo: local
|
| 34 |
+
hooks:
|
| 35 |
+
- id: pyright
|
| 36 |
+
name: pyright
|
| 37 |
+
entry: ./pre-commit-scripts/pyright.sh
|
| 38 |
+
language: script
|
| 39 |
+
pass_filenames: false
|
| 40 |
+
- id: ruff-lint
|
| 41 |
+
name: ruff-lint
|
| 42 |
+
entry: ./pre-commit-scripts/ruff-lint.sh
|
| 43 |
+
pass_filenames: false
|
| 44 |
+
language: script
|
| 45 |
+
- id: ruff-format
|
| 46 |
+
name: ruff-format
|
| 47 |
+
entry: ./pre-commit-scripts/ruff-format.sh
|
| 48 |
+
language: script
|
Dockerfile.cpu
CHANGED
|
@@ -1,9 +1,12 @@
|
|
| 1 |
FROM ubuntu:22.04
|
|
|
|
| 2 |
RUN apt-get update && \
|
| 3 |
-
apt-get install -y curl software-properties-common && \
|
| 4 |
add-apt-repository ppa:deadsnakes/ppa && \
|
| 5 |
apt-get update && \
|
| 6 |
-
DEBIAN_FRONTEND=noninteractive apt-get -y install python3.11 python3.11-distutils && \
|
|
|
|
|
|
|
| 7 |
curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11
|
| 8 |
RUN pip install --no-cache-dir poetry==1.8.2
|
| 9 |
WORKDIR /root/speaches
|
|
|
|
| 1 |
FROM ubuntu:22.04
|
| 2 |
+
# hadolint ignore=DL3008,DL4006
|
| 3 |
RUN apt-get update && \
|
| 4 |
+
apt-get install -y --no-install-recommends curl software-properties-common && \
|
| 5 |
add-apt-repository ppa:deadsnakes/ppa && \
|
| 6 |
apt-get update && \
|
| 7 |
+
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends python3.11 python3.11-distutils && \
|
| 8 |
+
apt-get clean && \
|
| 9 |
+
rm -rf /var/lib/apt/lists/* && \
|
| 10 |
curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11
|
| 11 |
RUN pip install --no-cache-dir poetry==1.8.2
|
| 12 |
WORKDIR /root/speaches
|
Dockerfile.cuda
CHANGED
|
@@ -1,9 +1,12 @@
|
|
| 1 |
FROM nvidia/cuda:12.2.2-cudnn8-runtime-ubuntu22.04
|
|
|
|
| 2 |
RUN apt-get update && \
|
| 3 |
-
apt-get install -y curl software-properties-common && \
|
| 4 |
add-apt-repository ppa:deadsnakes/ppa && \
|
| 5 |
apt-get update && \
|
| 6 |
-
DEBIAN_FRONTEND=noninteractive apt-get -y install python3.11 python3.11-distutils && \
|
|
|
|
|
|
|
| 7 |
curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11
|
| 8 |
RUN pip install --no-cache-dir poetry==1.8.2
|
| 9 |
WORKDIR /root/speaches
|
|
|
|
| 1 |
FROM nvidia/cuda:12.2.2-cudnn8-runtime-ubuntu22.04
|
| 2 |
+
# hadolint ignore=DL3008,DL4006
|
| 3 |
RUN apt-get update && \
|
| 4 |
+
apt-get install -y --no-install-recommends curl software-properties-common && \
|
| 5 |
add-apt-repository ppa:deadsnakes/ppa && \
|
| 6 |
apt-get update && \
|
| 7 |
+
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends python3.11 python3.11-distutils && \
|
| 8 |
+
apt-get clean && \
|
| 9 |
+
rm -rf /var/lib/apt/lists/* && \
|
| 10 |
curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11
|
| 11 |
RUN pip install --no-cache-dir poetry==1.8.2
|
| 12 |
WORKDIR /root/speaches
|
pre-commit-scripts/pyright.sh
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# shellcheck disable=SC1091
|
| 3 |
+
source "$(poetry env info --path)"/bin/activate
|
| 4 |
+
pyright
|
pre-commit-scripts/ruff-format.sh
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
ruff format
|
pre-commit-scripts/ruff-lint.sh
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
ruff check --fix
|
pyproject.toml
CHANGED
|
@@ -22,6 +22,11 @@ youtube-dl = {git = "https://github.com/ytdl-org/youtube-dl.git"}
|
|
| 22 |
[tool.ruff]
|
| 23 |
target-version = "py311"
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
[build-system]
|
| 26 |
requires = ["poetry-core"]
|
| 27 |
build-backend = "poetry.core.masonry.api"
|
|
|
|
| 22 |
[tool.ruff]
|
| 23 |
target-version = "py311"
|
| 24 |
|
| 25 |
+
[tool.pyright]
|
| 26 |
+
# typeCheckingMode = "strict"
|
| 27 |
+
pythonVersion = "3.11"
|
| 28 |
+
pythonPlatform = "Linux"
|
| 29 |
+
|
| 30 |
[build-system]
|
| 31 |
requires = ["poetry-core"]
|
| 32 |
build-backend = "poetry.core.masonry.api"
|
speaches/main.py
CHANGED
|
@@ -6,8 +6,15 @@ from contextlib import asynccontextmanager
|
|
| 6 |
from io import BytesIO
|
| 7 |
from typing import Annotated, Literal, OrderedDict
|
| 8 |
|
| 9 |
-
from fastapi import (
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
from fastapi.responses import StreamingResponse
|
| 12 |
from fastapi.websockets import WebSocketState
|
| 13 |
from faster_whisper import WhisperModel
|
|
@@ -16,11 +23,12 @@ from faster_whisper.vad import VadOptions, get_speech_timestamps
|
|
| 16 |
from speaches import utils
|
| 17 |
from speaches.asr import FasterWhisperASR
|
| 18 |
from speaches.audio import AudioStream, audio_samples_from_file
|
| 19 |
-
from speaches.config import
|
| 20 |
-
ResponseFormat, config)
|
| 21 |
from speaches.logger import logger
|
| 22 |
-
from speaches.server_models import (
|
| 23 |
-
|
|
|
|
|
|
|
| 24 |
from speaches.transcriber import audio_transcriber
|
| 25 |
|
| 26 |
models: OrderedDict[Model, WhisperModel] = OrderedDict()
|
|
|
|
| 6 |
from io import BytesIO
|
| 7 |
from typing import Annotated, Literal, OrderedDict
|
| 8 |
|
| 9 |
+
from fastapi import (
|
| 10 |
+
FastAPI,
|
| 11 |
+
Form,
|
| 12 |
+
Query,
|
| 13 |
+
Response,
|
| 14 |
+
UploadFile,
|
| 15 |
+
WebSocket,
|
| 16 |
+
WebSocketDisconnect,
|
| 17 |
+
)
|
| 18 |
from fastapi.responses import StreamingResponse
|
| 19 |
from fastapi.websockets import WebSocketState
|
| 20 |
from faster_whisper import WhisperModel
|
|
|
|
| 23 |
from speaches import utils
|
| 24 |
from speaches.asr import FasterWhisperASR
|
| 25 |
from speaches.audio import AudioStream, audio_samples_from_file
|
| 26 |
+
from speaches.config import SAMPLES_PER_SECOND, Language, Model, ResponseFormat, config
|
|
|
|
| 27 |
from speaches.logger import logger
|
| 28 |
+
from speaches.server_models import (
|
| 29 |
+
TranscriptionJsonResponse,
|
| 30 |
+
TranscriptionVerboseJsonResponse,
|
| 31 |
+
)
|
| 32 |
from speaches.transcriber import audio_transcriber
|
| 33 |
|
| 34 |
models: OrderedDict[Model, WhisperModel] = OrderedDict()
|
speaches/server_models.py
CHANGED
|
@@ -85,7 +85,7 @@ class TranscriptionVerboseJsonResponse(BaseModel):
|
|
| 85 |
text=segment.text,
|
| 86 |
words=(
|
| 87 |
[WordObject.from_word(word) for word in segment.words]
|
| 88 |
-
if
|
| 89 |
else []
|
| 90 |
),
|
| 91 |
segments=[SegmentObject.from_segment(segment)],
|
|
|
|
| 85 |
text=segment.text,
|
| 86 |
words=(
|
| 87 |
[WordObject.from_word(word) for word in segment.words]
|
| 88 |
+
if isinstance(segment.words, list)
|
| 89 |
else []
|
| 90 |
),
|
| 91 |
segments=[SegmentObject.from_segment(segment)],
|
tests/app_test.py
CHANGED
|
@@ -12,7 +12,7 @@ from starlette.testclient import WebSocketTestSession
|
|
| 12 |
|
| 13 |
from speaches.config import BYTES_PER_SECOND
|
| 14 |
from speaches.main import app
|
| 15 |
-
from speaches.server_models import
|
| 16 |
|
| 17 |
SIMILARITY_THRESHOLD = 0.97
|
| 18 |
AUDIO_FILES_LIMIT = 5
|
|
@@ -54,13 +54,13 @@ def stream_audio_data(
|
|
| 54 |
|
| 55 |
def transcribe_audio_data(
|
| 56 |
client: TestClient, data: bytes
|
| 57 |
-
) ->
|
| 58 |
response = client.post(
|
| 59 |
TRANSCRIBE_ENDPOINT,
|
| 60 |
files={"file": ("audio.raw", data, "audio/raw")},
|
| 61 |
)
|
| 62 |
data = json.loads(response.json()) # TODO: figure this out
|
| 63 |
-
return
|
| 64 |
|
| 65 |
|
| 66 |
@pytest.mark.parametrize("file_path", file_paths)
|
|
@@ -70,14 +70,16 @@ def test_ws_audio_transcriptions(
|
|
| 70 |
with open(file_path, "rb") as file:
|
| 71 |
data = file.read()
|
| 72 |
|
| 73 |
-
streaming_transcription:
|
| 74 |
thread = threading.Thread(
|
| 75 |
target=stream_audio_data, args=(ws, data), kwargs={"speed": 4.0}
|
| 76 |
)
|
| 77 |
thread.start()
|
| 78 |
while True:
|
| 79 |
try:
|
| 80 |
-
streaming_transcription =
|
|
|
|
|
|
|
| 81 |
except WebSocketDisconnect:
|
| 82 |
break
|
| 83 |
file_transcription = transcribe_audio_data(client, data)
|
|
|
|
| 12 |
|
| 13 |
from speaches.config import BYTES_PER_SECOND
|
| 14 |
from speaches.main import app
|
| 15 |
+
from speaches.server_models import TranscriptionVerboseJsonResponse
|
| 16 |
|
| 17 |
SIMILARITY_THRESHOLD = 0.97
|
| 18 |
AUDIO_FILES_LIMIT = 5
|
|
|
|
| 54 |
|
| 55 |
def transcribe_audio_data(
|
| 56 |
client: TestClient, data: bytes
|
| 57 |
+
) -> TranscriptionVerboseJsonResponse:
|
| 58 |
response = client.post(
|
| 59 |
TRANSCRIBE_ENDPOINT,
|
| 60 |
files={"file": ("audio.raw", data, "audio/raw")},
|
| 61 |
)
|
| 62 |
data = json.loads(response.json()) # TODO: figure this out
|
| 63 |
+
return TranscriptionVerboseJsonResponse(**data) # type: ignore
|
| 64 |
|
| 65 |
|
| 66 |
@pytest.mark.parametrize("file_path", file_paths)
|
|
|
|
| 70 |
with open(file_path, "rb") as file:
|
| 71 |
data = file.read()
|
| 72 |
|
| 73 |
+
streaming_transcription: TranscriptionVerboseJsonResponse = None # type: ignore
|
| 74 |
thread = threading.Thread(
|
| 75 |
target=stream_audio_data, args=(ws, data), kwargs={"speed": 4.0}
|
| 76 |
)
|
| 77 |
thread.start()
|
| 78 |
while True:
|
| 79 |
try:
|
| 80 |
+
streaming_transcription = TranscriptionVerboseJsonResponse(
|
| 81 |
+
**ws.receive_json()
|
| 82 |
+
)
|
| 83 |
except WebSocketDisconnect:
|
| 84 |
break
|
| 85 |
file_transcription = transcribe_audio_data(client, data)
|