uvicorn startup

This commit is contained in:
John Lancaster
2026-07-30 21:09:51 -05:00
parent d2b793ea69
commit 1cc2f319d5
3 changed files with 47 additions and 13 deletions
+20
View File
@@ -0,0 +1,20 @@
import uvicorn
from .config import LOGGING_CONFIG
from .config import get_settings
def main() -> None:
settings = get_settings()
uvicorn.run(
"transcription.app:create_app",
factory=True,
host=settings.server.host,
port=settings.server.port,
log_level=LOGGING_CONFIG.get("root", {}).get("level", "info").lower(),
reload=settings.server.reload,
)
if __name__ == "__main__":
main()
+9 -4
View File
@@ -16,6 +16,7 @@ from fastapi.staticfiles import StaticFiles
from .api.errors import register_error_handlers from .api.errors import register_error_handlers
from .api.health import router as health_router from .api.health import router as health_router
from .config import Settings
from .config import configure_logging from .config import configure_logging
from .config import get_settings from .config import get_settings
from .db import create_all from .db import create_all
@@ -73,14 +74,14 @@ async def _recover_stale_processing_jobs(app: FastAPI) -> None:
logger.warning("Recovered %s stale processing job(s) at startup", recovered) logger.warning("Recovered %s stale processing job(s) at startup", recovered)
def create_app() -> FastAPI: def create_app(settings: Settings | None = None) -> FastAPI:
"""Create and configure the FastAPI application.""" """Create and configure the FastAPI application."""
app = FastAPI(title="Transcription", lifespan=_lifespan) app = FastAPI(title="Transcription", lifespan=_lifespan)
settings = get_settings() active_settings = settings or get_settings()
app.state.settings = settings app.state.settings = active_settings
app.mount( app.mount(
"/uploads", "/uploads",
StaticFiles(directory=settings.upload_dir, check_dir=False), StaticFiles(directory=active_settings.upload_dir, check_dir=False),
name="uploads", name="uploads",
) )
@@ -92,6 +93,10 @@ def create_app() -> FastAPI:
async def ui_redirect() -> RedirectResponse: async def ui_redirect() -> RedirectResponse:
return RedirectResponse(url="/ui/upload", status_code=status.HTTP_307_TEMPORARY_REDIRECT) return RedirectResponse(url="/ui/upload", status_code=status.HTTP_307_TEMPORARY_REDIRECT)
@app.get("/healthz")
def health() -> dict[str, str]:
return {"status": "ok"}
register_error_handlers(app) register_error_handlers(app)
register_pages(app) register_pages(app)
app.include_router(health_router) app.include_router(health_router)
+18 -9
View File
@@ -6,11 +6,13 @@ are resolved by the provider adapters, not here.
""" """
import logging.config import logging.config
from contextvars import ContextVar
from enum import StrEnum from enum import StrEnum
from functools import cache
from pathlib import Path from pathlib import Path
from typing import Literal from typing import Literal
from pydantic import BaseModel
from pydantic import ConfigDict
from pydantic import Field from pydantic import Field
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
from pydantic_settings import SettingsConfigDict from pydantic_settings import SettingsConfigDict
@@ -22,13 +24,26 @@ class Provider(StrEnum):
OPENROUTER = "openrouter" OPENROUTER = "openrouter"
class ServerSettings(BaseModel):
model_config = ConfigDict(frozen=True)
host: str = "0.0.0.0"
port: int = 8000
log_level: Literal["critical", "error", "warning", "info", "debug", "trace"] = "info"
reload: bool = False
class Settings(BaseSettings): class Settings(BaseSettings):
model_config = SettingsConfigDict( model_config = SettingsConfigDict(
env_file=".env", env_file=".env",
env_file_encoding="utf-8", env_file_encoding="utf-8",
extra="ignore", extra="ignore",
cli_parse_args=True,
) )
# --- NiceGUI Server ---
server: ServerSettings = Field(default_factory=ServerSettings)
# --- AI provider --- # --- AI provider ---
provider: Provider = Provider.OPENROUTER provider: Provider = Provider.OPENROUTER
openrouter_api_key: str openrouter_api_key: str
@@ -64,15 +79,9 @@ class Settings(BaseSettings):
return self.environment in {"development", "test"} return self.environment in {"development", "test"}
_settings: ContextVar[Settings | None] = ContextVar("settings", default=None) @cache
def get_settings(**kwargs) -> Settings: def get_settings(**kwargs) -> Settings:
settings = _settings.get() return Settings(**kwargs)
if settings is None:
settings = Settings(**kwargs) # pyright: ignore[reportCallIssue]
_settings.set(settings)
return settings
LOGGING_CONFIG: dict[str, object] = { LOGGING_CONFIG: dict[str, object] = {