From d22b6e45ab9c9cea8b3b7778c124d2e9e7edef92 Mon Sep 17 00:00:00 2001 From: nonchris Date: Sun, 14 Dec 2025 22:47:53 +0100 Subject: [PATCH 01/12] sanity test --- test/test_import.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/test_import.py b/test/test_import.py index 036e367..d152556 100644 --- a/test/test_import.py +++ b/test/test_import.py @@ -9,4 +9,15 @@ def test_import_whisper_api(self): assert whisper_api except ImportError: - unittest.fail("Failed to import whisper_api") + unittest.fail("Failed to 'import whisper_api'") + + def test_from_import(self): + try: + from whisper_api import app + + assert app + + except ImportError: + unittest.fail("Failed to 'from whisper_api import app'") + + # TODO: write a test that attempt to import from a a mp.Process and assert that app does not exist \ No newline at end of file From d993358cc7c4dacf535b503f9a57821e2376c4b7 Mon Sep 17 00:00:00 2001 From: nonchris Date: Sun, 14 Dec 2025 22:48:17 +0100 Subject: [PATCH 02/12] change global variable to importable function --- src/whisper_api/main.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/whisper_api/main.py b/src/whisper_api/main.py index a44a72b..85d4db0 100644 --- a/src/whisper_api/main.py +++ b/src/whisper_api/main.py @@ -47,20 +47,25 @@ from whisper_api.log_setup import logger from whisper_api.log_setup import uuid_log_format -IS_MAIN_PROCESS = multiprocessing.current_process().name == "MainProcess" + +def am_i_process(name: str) -> bool: + return multiprocessing.current_process().name == name + +def am_i_main_process() -> bool: + return am_i_process("MainProcess") description = """ Whisper API transcribes audio files. """ -if IS_MAIN_PROCESS: +if am_i_main_process(): print(description) """ init global variables """ -if IS_MAIN_PROCESS: +if am_i_main_process(): # TODO: can tasks get GCed before they finish if queue is too long? task_dict: TempDict[uuid_hex_t, Task] = TempDict( expiration_time_m=DELETE_RESULTS_AFTER_M, @@ -284,7 +289,7 @@ async def lifespan(_app: FastAPI): """ Init API """ -if IS_MAIN_PROCESS: +if am_i_main_process(): app = FastAPI( title="Whisper API", description=description, From 55ae60eddd3362bb70db1db6b49cc86b52193fc3 Mon Sep 17 00:00:00 2001 From: nonchris Date: Sun, 14 Dec 2025 22:49:23 +0100 Subject: [PATCH 03/12] ensure imports are _really_ only attempted on main --- src/whisper_api/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/whisper_api/__init__.py b/src/whisper_api/__init__.py index ee2b9cd..9e18ad3 100644 --- a/src/whisper_api/__init__.py +++ b/src/whisper_api/__init__.py @@ -6,10 +6,13 @@ path = os.path.realpath(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(os.path.dirname(path))) -__version__ = "20241105" +__version__ = "20251214" -from whisper_api.main import app # isort: skip -from whisper_api.main import start + +from whisper_api.main import am_i_main_process +if am_i_main_process(): + from whisper_api.main import app # isort: skip + from whisper_api.main import start if __name__ == "__main__": start() From 3d147e2a7009024dd57d1439060517aba77e40dd Mon Sep 17 00:00:00 2001 From: nonchris Date: Sun, 14 Dec 2025 22:51:10 +0100 Subject: [PATCH 04/12] pin exact python version for now --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2717a79..c9497f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "whisper_api" dynamic = ["dependencies", "version"] description = "A simple API for whisper" readme = "README.md" -requires-python = ">=3.10" +requires-python = "==3.12" license = { text = "" } authors = [ { name = "MayNiklas", email = "info@niklas-steffen.de" }, From a1048c5be74d7b46dc9c8ebc20a5a7f6e94f53c1 Mon Sep 17 00:00:00 2001 From: MayNiklas Date: Fri, 9 Jan 2026 14:45:17 +0100 Subject: [PATCH 05/12] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/807e9154dcb16384b1b765ebe9cd2bba2ac287fd?narHash=sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU%3D' (2024-10-29) → 'github:nixos/nixpkgs/3497aa5c9457a9d88d71fa93a4a8368816fbeeba?narHash=sha256-dhhvQY67aboBk8b0/u0XB6vwHdgbROZT3fJAjyNh5Ww%3D' (2026-01-08) --- flake.lock | 6 +++--- requirements.txt | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/flake.lock b/flake.lock index 873d3e5..59b740a 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1730200266, - "narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=", + "lastModified": 1767892417, + "narHash": "sha256-dhhvQY67aboBk8b0/u0XB6vwHdgbROZT3fJAjyNh5Ww=", "owner": "nixos", "repo": "nixpkgs", - "rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd", + "rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba", "type": "github" }, "original": { diff --git a/requirements.txt b/requirements.txt index 139d147..822f1e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ -fastapi==0.112.0 +fastapi==0.121.1 ffmpeg-python==0.2.0 -openai-whisper==20240930 -pydantic==2.8.2 -python-multipart==0.0.9 -uvicorn==0.29.0 +openai-whisper==20250625 +pydantic==2.12.4 +python-multipart==0.0.20 +uvicorn==0.38.0 From a3193048264eb7a1928b2fafa9de3312b745a39f Mon Sep 17 00:00:00 2001 From: MayNiklas Date: Fri, 9 Jan 2026 15:26:48 +0100 Subject: [PATCH 06/12] fix issue regarding pkgs.python3 --- nixos/pkgs/whisper_api/default.nix | 31 ++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/nixos/pkgs/whisper_api/default.nix b/nixos/pkgs/whisper_api/default.nix index de2e887..ef1db10 100644 --- a/nixos/pkgs/whisper_api/default.nix +++ b/nixos/pkgs/whisper_api/default.nix @@ -1,9 +1,24 @@ -{ self -, lib -, python3 -, +{ + self, + lib, + buildPythonApplication, + + # build-system + setuptools, + pythonRelaxDepsHook, + + # dependencies + fastapi, + ffmpeg-python, + openai-whisper, + python-multipart, + uvicorn, + + # tests + unittestCheckHook, + httpx, }: -python3.pkgs.buildPythonApplication { +buildPythonApplication { pname = "whisper_api"; @@ -18,12 +33,12 @@ python3.pkgs.buildPythonApplication { pythonRelaxDeps = [ ]; - nativeBuildInputs = with python3.pkgs; [ + nativeBuildInputs = [ setuptools pythonRelaxDepsHook ]; - propagatedBuildInputs = with python3.pkgs; [ + propagatedBuildInputs = [ fastapi ffmpeg-python openai-whisper @@ -31,7 +46,7 @@ python3.pkgs.buildPythonApplication { uvicorn ]; - nativeCheckInputs = with python3.pkgs; [ + nativeCheckInputs = [ unittestCheckHook httpx ]; From 85a925ffb49960a76862f97a1c4fcf532b5b072f Mon Sep 17 00:00:00 2001 From: MayNiklas Date: Fri, 9 Jan 2026 15:29:40 +0100 Subject: [PATCH 07/12] set env ATTIC_KEY --- .woodpecker/build-amd64.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.woodpecker/build-amd64.yml b/.woodpecker/build-amd64.yml index b3ce7c8..816daa1 100644 --- a/.woodpecker/build-amd64.yml +++ b/.woodpecker/build-amd64.yml @@ -7,7 +7,9 @@ steps: image: bash commands: - attic login lounge-rocks https://cache.lounge.rocks $ATTIC_KEY --set-default - secrets: [attic_key] + environment: + ATTIC_KEY: + from_secret: attic_key - name: build whisper_api image: bash From ebea398b811be66997c92ab67b7f301e6108c084 Mon Sep 17 00:00:00 2001 From: MayNiklas Date: Fri, 9 Jan 2026 15:34:06 +0100 Subject: [PATCH 08/12] call package using python --- flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 072e060..ae371b1 100644 --- a/flake.nix +++ b/flake.nix @@ -33,8 +33,8 @@ (system: nixpkgsFor.${system}.nixpkgs-fmt); overlays.default = final: prev: { - devShell = final.callPackage nixos/devShell { inherit self; }; - whisper_api = final.callPackage nixos/pkgs/whisper_api { inherit self; }; + devShell = final.python3Packages.callPackage nixos/devShell { inherit self; }; + whisper_api = final.python3Packages.callPackage nixos/pkgs/whisper_api { inherit self; }; # Our code is not compatible with pydantic version 2 yet. python3 = prev.python3.override { packageOverrides = python-self: python-super: { From 440293a3c2e52ad2c075c22833a910f587ac46fc Mon Sep 17 00:00:00 2001 From: nonchris Date: Fri, 9 Jan 2026 16:16:48 +0100 Subject: [PATCH 09/12] fix python version pinning --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c9497f2..7f6abd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "whisper_api" dynamic = ["dependencies", "version"] description = "A simple API for whisper" readme = "README.md" -requires-python = "==3.12" +requires-python = "~=3.12" license = { text = "" } authors = [ { name = "MayNiklas", email = "info@niklas-steffen.de" }, From 0b718840963ce52420a2cf1f19793de104b5e7e1 Mon Sep 17 00:00:00 2001 From: nonchris Date: Fri, 9 Jan 2026 16:18:13 +0100 Subject: [PATCH 10/12] fix import error on package imports --- src/whisper_api/__init__.py | 7 +++++++ src/whisper_api/main.py | 18 +++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/whisper_api/__init__.py b/src/whisper_api/__init__.py index 9e18ad3..a3ff0f1 100644 --- a/src/whisper_api/__init__.py +++ b/src/whisper_api/__init__.py @@ -1,3 +1,4 @@ +import multiprocessing import os import sys @@ -10,6 +11,12 @@ from whisper_api.main import am_i_main_process +def start(): + """Mock Version of start() that is overwritten in the main process by the import below.""" + name = multiprocessing.current_process().name + raise ImportError(f"You're trying to run start() from another process than Main " + f"(you are: '{name}'). This is currently not supported.") + if am_i_main_process(): from whisper_api.main import app # isort: skip from whisper_api.main import start diff --git a/src/whisper_api/main.py b/src/whisper_api/main.py index 85d4db0..745269d 100644 --- a/src/whisper_api/main.py +++ b/src/whisper_api/main.py @@ -351,18 +351,18 @@ async def log_requests(req: Request, call_next): return resp -""" -Hook for uvicorn -""" + """ + Hook for uvicorn + """ -def start(): - import uvicorn + def start(): + import uvicorn - # TODO: - # forwarded_allow_ips= should be set via env var - # proxy_headers=True only when needed - uvicorn.run(app, host=API_LISTEN, port=API_PORT, proxy_headers=True, forwarded_allow_ips="*", log_level="warning") + # TODO: + # forwarded_allow_ips= should be set via env var + # proxy_headers=True only when needed + uvicorn.run(app, host=API_LISTEN, port=API_PORT, proxy_headers=True, forwarded_allow_ips="*", log_level="warning") if __name__ == "__main__": From 6c30f266100d45f0670040dd7c45390bc17a11fd Mon Sep 17 00:00:00 2001 From: nonchris Date: Fri, 9 Jan 2026 16:24:25 +0100 Subject: [PATCH 11/12] update unittests --- src/whisper_api/main.py | 1 + test/test_import.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/whisper_api/main.py b/src/whisper_api/main.py index 745269d..2df6bf6 100644 --- a/src/whisper_api/main.py +++ b/src/whisper_api/main.py @@ -357,6 +357,7 @@ async def log_requests(req: Request, call_next): def start(): + """Entrypoint to start the API (note: this version is only imported on the MainProcess (see __init.py__)""" import uvicorn # TODO: diff --git a/test/test_import.py b/test/test_import.py index d152556..2bad8cb 100644 --- a/test/test_import.py +++ b/test/test_import.py @@ -11,7 +11,7 @@ def test_import_whisper_api(self): except ImportError: unittest.fail("Failed to 'import whisper_api'") - def test_from_import(self): + def test_from_import_app(self): try: from whisper_api import app @@ -20,4 +20,18 @@ def test_from_import(self): except ImportError: unittest.fail("Failed to 'from whisper_api import app'") + + def test_from_import_start(self): + try: + from whisper_api import start + + assert start + + assert "Mock Version" not in start.__doc__ + + except ImportError: + unittest.fail("Failed to 'from whisper_api import real implementation of start()' on main process") + + + # TODO: write a test that attempt to import from a a mp.Process and assert that app does not exist \ No newline at end of file From e39c3354d1dca58a4fb62484bfe151136cfedd20 Mon Sep 17 00:00:00 2001 From: MayNiklas Date: Fri, 9 Jan 2026 21:20:00 +0100 Subject: [PATCH 12/12] upgrade Dockerfile to ubuntu 24.04 --- Dockerfile | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index fa301e5..65a520d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,8 @@ # newest version: # https://hub.docker.com/r/nvidia/cuda/tags?page=1&name=-base-ubuntu22.04&ordering=name -ARG CUDA_VERSION=12.4.1 -FROM nvidia/cuda:${CUDA_VERSION}-base-ubuntu22.04 - -ARG PYTHON_VERSION=3.10 +ARG CUDA_VERSION=12.6.2 +FROM nvidia/cuda:${CUDA_VERSION}-base-ubuntu24.04 WORKDIR /workspace @@ -12,14 +10,13 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get -qq update && \ apt-get -qq install --no-install-recommends \ ffmpeg \ - python${PYTHON_VERSION} \ - python${PYTHON_VERSION}-venv \ + python3 \ + python3-venv \ python3-pip && \ - rm -rf /var/lib/apt/lists/* && \ - pip3 install --upgrade pip + rm -rf /var/lib/apt/lists/* COPY requirements.txt requirements.txt -RUN pip3 install -r requirements.txt +RUN pip3 install -r requirements.txt --break-system-packages # disabled by default since GitHub Actions do not have enough space ARG PREFETCH_MODEL=0 @@ -32,7 +29,7 @@ RUN if [ "$PREFETCH_MODEL" != 0 ]; then \ COPY . /workspace/code RUN cd /workspace/code && \ - pip3 install . + pip3 install . --break-system-packages ENV PORT=3001 \ LISTEN=0.0.0.0 \