mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-02-04 09:56:47 +00:00
build: move to uv (#488)
* build: move to uv * build: add packages declaration * build: move to python 3.12, as sentencespiece does not work on 3.13 * ci: remove pre-commit check, will be done in another branch. * ci: fix name checkout * ci: update lock and dockerfile * test: remove event_loop, not needed in python 3.12 * test: updated test due to av returning AudioFrame with 4096 samples instead of 1024 * build: prevent using fastapi cli, because there is no way to set default port I don't want to pass --port 1250 every time, so back on previous approach. I deactivated auto-reload for production. * ci: remove main.py * test: fix quirck with httpx
This commit is contained in:
@@ -1 +1 @@
|
||||
3.11.6
|
||||
3.12
|
||||
|
||||
@@ -1,30 +1,25 @@
|
||||
FROM python:3.11-slim as base
|
||||
FROM python:3.12-slim
|
||||
|
||||
ENV PIP_DEFAULT_TIMEOUT=100 \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
||||
PIP_NO_CACHE_DIR=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
POETRY_VERSION=1.3.1
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
UV_LINK_MODE=copy
|
||||
|
||||
# builder install base dependencies
|
||||
FROM base AS builder
|
||||
WORKDIR /tmp
|
||||
RUN pip install "poetry==$POETRY_VERSION"
|
||||
RUN python -m venv /venv
|
||||
RUN apt-get update && apt-get install -y curl && apt-get clean
|
||||
ADD https://astral.sh/uv/install.sh /uv-installer.sh
|
||||
RUN sh /uv-installer.sh && rm /uv-installer.sh
|
||||
ENV PATH="/root/.local/bin/:$PATH"
|
||||
|
||||
# install application dependencies
|
||||
COPY pyproject.toml poetry.lock /tmp
|
||||
RUN . /venv/bin/activate && poetry config virtualenvs.create false
|
||||
RUN . /venv/bin/activate && poetry install --only main,aws --no-root --no-interaction --no-ansi
|
||||
RUN mkdir -p /app
|
||||
WORKDIR /app
|
||||
COPY pyproject.toml uv.lock /app/
|
||||
RUN touch README.md && env uv sync --compile-bytecode --locked
|
||||
|
||||
# bootstrap
|
||||
FROM base AS final
|
||||
COPY --from=builder /venv /venv
|
||||
RUN mkdir -p /app
|
||||
COPY reflector /app/reflector
|
||||
COPY migrations /app/migrations
|
||||
COPY images /app/images
|
||||
COPY alembic.ini runserver.sh /app/
|
||||
COPY images /app/images
|
||||
COPY migrations /app/migrations
|
||||
COPY reflector /app/reflector
|
||||
WORKDIR /app
|
||||
CMD ["./runserver.sh"]
|
||||
|
||||
4607
server/poetry.lock
generated
4607
server/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,76 +1,82 @@
|
||||
[tool.poetry]
|
||||
name = "reflector-server"
|
||||
[project]
|
||||
name = "reflector"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Monadical team <ops@monadical.com>"]
|
||||
authors = [{ name = "Monadical team", email = "ops@monadical.com" }]
|
||||
requires-python = ">=3.11, <3.13"
|
||||
readme = "README.md"
|
||||
packages = []
|
||||
dependencies = [
|
||||
"aiohttp>=3.9.0",
|
||||
"aiohttp-cors>=0.7.0",
|
||||
"av>=10.0.0",
|
||||
"requests>=2.31.0",
|
||||
"aiortc>=1.5.0",
|
||||
"sortedcontainers>=2.4.0",
|
||||
"loguru>=0.7.0",
|
||||
"pydantic-settings>=2.0.2",
|
||||
"structlog>=23.1.0",
|
||||
"uvicorn[standard]>=0.23.1",
|
||||
"fastapi[standard]>=0.100.1",
|
||||
"sentry-sdk[fastapi]>=1.29.2",
|
||||
"httpx>=0.24.1",
|
||||
"fastapi-pagination>=0.12.6",
|
||||
"databases[aiosqlite, asyncpg]>=0.7.0",
|
||||
"sqlalchemy<1.5",
|
||||
"fief-client[fastapi]>=0.17.0",
|
||||
"alembic>=1.11.3",
|
||||
"nltk>=3.8.1",
|
||||
"prometheus-fastapi-instrumentator>=6.1.0",
|
||||
"sentencepiece>=0.1.99",
|
||||
"protobuf>=4.24.3",
|
||||
"profanityfilter>=2.0.6",
|
||||
"celery>=5.3.4",
|
||||
"redis>=5.0.1",
|
||||
"python-jose[cryptography]>=3.3.0",
|
||||
"python-multipart>=0.0.6",
|
||||
"faster-whisper>=0.10.0",
|
||||
"transformers>=4.36.2",
|
||||
"black==24.1.1",
|
||||
"jsonschema>=4.23.0",
|
||||
"openai>=1.59.7",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
aiohttp = "^3.9.0"
|
||||
aiohttp-cors = "^0.7.0"
|
||||
av = "^10.0.0"
|
||||
requests = "^2.31.0"
|
||||
aiortc = "^1.5.0"
|
||||
sortedcontainers = "^2.4.0"
|
||||
loguru = "^0.7.0"
|
||||
pydantic-settings = "^2.0.2"
|
||||
structlog = "^23.1.0"
|
||||
uvicorn = {extras = ["standard"], version = "^0.23.1"}
|
||||
fastapi = "^0.100.1"
|
||||
sentry-sdk = {extras = ["fastapi"], version = "^1.29.2"}
|
||||
httpx = "^0.24.1"
|
||||
fastapi-pagination = "^0.12.6"
|
||||
databases = {extras = ["aiosqlite", "asyncpg"], version = "^0.7.0"}
|
||||
sqlalchemy = "<1.5"
|
||||
fief-client = {extras = ["fastapi"], version = "^0.17.0"}
|
||||
alembic = "^1.11.3"
|
||||
nltk = "^3.8.1"
|
||||
prometheus-fastapi-instrumentator = "^6.1.0"
|
||||
sentencepiece = "^0.1.99"
|
||||
protobuf = "^4.24.3"
|
||||
profanityfilter = "^2.0.6"
|
||||
celery = "^5.3.4"
|
||||
redis = "^5.0.1"
|
||||
python-jose = {extras = ["cryptography"], version = "^3.3.0"}
|
||||
python-multipart = "^0.0.6"
|
||||
faster-whisper = "^0.10.0"
|
||||
transformers = "^4.36.2"
|
||||
black = "24.1.1"
|
||||
jsonschema = "^4.23.0"
|
||||
openai = "^1.59.7"
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"black>=24.1.1",
|
||||
"stamina>=23.1.0",
|
||||
"pyinstrument>=4.6.1",
|
||||
]
|
||||
tests = [
|
||||
"pytest-cov>=4.1.0",
|
||||
"pytest-aiohttp>=1.0.4",
|
||||
"pytest-asyncio>=0.21.1",
|
||||
"pytest>=7.4.0",
|
||||
"httpx-ws>=0.4.1",
|
||||
"pytest-httpx>=0.23.1",
|
||||
"pytest-celery>=0.0.0",
|
||||
]
|
||||
aws = ["aioboto3>=11.2.0"]
|
||||
evaluation = [
|
||||
"jiwer>=3.0.2",
|
||||
"levenshtein>=0.21.1",
|
||||
"tqdm>=4.66.0",
|
||||
"pydantic>=2.1.1",
|
||||
]
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^24.1.1"
|
||||
stamina = "^23.1.0"
|
||||
pyinstrument = "^4.6.1"
|
||||
|
||||
|
||||
[tool.poetry.group.tests.dependencies]
|
||||
pytest-cov = "^4.1.0"
|
||||
pytest-aiohttp = "^1.0.4"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
pytest = "^7.4.0"
|
||||
httpx-ws = "^0.4.1"
|
||||
pytest-httpx = "^0.23.1"
|
||||
pytest-celery = "^0.0.0"
|
||||
|
||||
|
||||
[tool.poetry.group.aws.dependencies]
|
||||
aioboto3 = "^11.2.0"
|
||||
|
||||
|
||||
[tool.poetry.group.evaluation.dependencies]
|
||||
jiwer = "^3.0.2"
|
||||
levenshtein = "^0.21.1"
|
||||
tqdm = "^4.66.0"
|
||||
pydantic = "^2.1.1"
|
||||
[tool.uv]
|
||||
default-groups = [
|
||||
"dev",
|
||||
"tests",
|
||||
"aws",
|
||||
"evaluation",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["reflector"]
|
||||
|
||||
[tool.coverage.run]
|
||||
source = ["reflector"]
|
||||
|
||||
@@ -147,6 +147,10 @@ if settings.PROFILING:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run("reflector.app:app", host="0.0.0.0", port=1250, reload=True)
|
||||
should_reload = "--reload" in sys.argv
|
||||
|
||||
uvicorn.run("reflector.app:app", host="0.0.0.0", port=1250, reload=should_reload)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
poetry run python3 -m reflector.app
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f "/venv/bin/activate" ]; then
|
||||
source /venv/bin/activate
|
||||
fi
|
||||
|
||||
if [ "${ENTRYPOINT}" = "server" ]; then
|
||||
alembic upgrade head
|
||||
python -m reflector.app
|
||||
uv run alembic upgrade head
|
||||
uv run -m reflector.app
|
||||
elif [ "${ENTRYPOINT}" = "worker" ]; then
|
||||
celery -A reflector.worker.app worker --loglevel=info
|
||||
uv run celery -A reflector.worker.app worker --loglevel=info
|
||||
elif [ "${ENTRYPOINT}" = "beat" ]; then
|
||||
celery -A reflector.worker.app beat --loglevel=info
|
||||
uv run celery -A reflector.worker.app beat --loglevel=info
|
||||
else
|
||||
echo "Unknown command"
|
||||
fi
|
||||
|
||||
@@ -84,7 +84,7 @@ from unittest import mock
|
||||
],
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
async def test_processors_audio_diarization(event_loop, name, diarization, expected):
|
||||
async def test_processors_audio_diarization(name, diarization, expected):
|
||||
from reflector.processors.audio_diarization import AudioDiarizationProcessor
|
||||
from reflector.processors.types import (
|
||||
TitleSummaryWithId,
|
||||
|
||||
@@ -3,7 +3,6 @@ import pytest
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_basic_process(
|
||||
event_loop,
|
||||
nltk,
|
||||
dummy_transcript,
|
||||
dummy_llm,
|
||||
@@ -34,8 +33,8 @@ async def test_basic_process(
|
||||
print(marks)
|
||||
|
||||
# validate the events
|
||||
assert marks["TranscriptLinerProcessor"] == 4
|
||||
assert marks["TranscriptTranslatorProcessor"] == 4
|
||||
assert marks["TranscriptLinerProcessor"] == 1
|
||||
assert marks["TranscriptTranslatorProcessor"] == 1
|
||||
assert marks["TranscriptTopicDetectorProcessor"] == 1
|
||||
assert marks["TranscriptFinalSummaryProcessor"] == 1
|
||||
assert marks["TranscriptFinalTitleProcessor"] == 1
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
import asyncio
|
||||
import pytest
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from reflector.utils.retry import (
|
||||
retry,
|
||||
RetryTimeoutException,
|
||||
RetryHTTPException,
|
||||
RetryException,
|
||||
RetryHTTPException,
|
||||
RetryTimeoutException,
|
||||
retry,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_retry_redirect(httpx_mock):
|
||||
async def custom_response(request: httpx.Request):
|
||||
if request.url.path == "/hello":
|
||||
await asyncio.sleep(1)
|
||||
return httpx.Response(
|
||||
status_code=303, headers={"location": "https://test_url/redirected"}
|
||||
)
|
||||
elif request.url.path == "/redirected":
|
||||
return httpx.Response(status_code=200, json={"hello": "world"})
|
||||
else:
|
||||
raise Exception("Unexpected path")
|
||||
httpx_mock.add_response(
|
||||
url="https://test_url/hello",
|
||||
status_code=303,
|
||||
headers={"location": "https://test_url/redirected"},
|
||||
)
|
||||
httpx_mock.add_response(
|
||||
url="https://test_url/redirected",
|
||||
status_code=200,
|
||||
json={"hello": "world"},
|
||||
)
|
||||
|
||||
httpx_mock.add_callback(custom_response)
|
||||
async with httpx.AsyncClient() as client:
|
||||
# timeout should not triggered, as it will end up ok
|
||||
# even though the first request is a 303 and took more that 0.5
|
||||
@@ -37,7 +36,7 @@ async def test_retry_redirect(httpx_mock):
|
||||
@pytest.mark.asyncio
|
||||
async def test_retry_httpx(httpx_mock):
|
||||
# this code should be force a retry
|
||||
httpx_mock.add_response(status_code=500)
|
||||
httpx_mock.add_response(status_code=500, is_reusable=True)
|
||||
async with httpx.AsyncClient() as client:
|
||||
with pytest.raises(RetryTimeoutException):
|
||||
await retry(client.get)("https://test_url", retry_timeout=0.1)
|
||||
|
||||
3005
server/uv.lock
generated
Normal file
3005
server/uv.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user