generated from john/python-template
Co-authored-by: Copilot App <[email protected]>
This commit is contained in:
co-authored by
Copilot App
parent
2093eb6fb3
commit
3873810022
@@ -60,6 +60,7 @@ WORKER_PROVIDER_TIMEOUT_SECONDS=30.0
|
|||||||
WORKER_STALE_JOB_SECONDS=30.0
|
WORKER_STALE_JOB_SECONDS=30.0
|
||||||
WORKER_RETRY_BACKOFF_SECONDS=1.0
|
WORKER_RETRY_BACKOFF_SECONDS=1.0
|
||||||
WORKER_SHUTDOWN_GRACE_SECONDS=5.0
|
WORKER_SHUTDOWN_GRACE_SECONDS=5.0
|
||||||
|
WORKER_POLL_INTERVAL_SECONDS=1.0
|
||||||
WORKER_MIN_TRANSCRIPTION_CHARS=0
|
WORKER_MIN_TRANSCRIPTION_CHARS=0
|
||||||
WORKER_MIN_TRANSCRIPTION_LINES=0
|
WORKER_MIN_TRANSCRIPTION_LINES=0
|
||||||
WORKER_FAIL_ON_FINISH_REASON_LENGTH=false
|
WORKER_FAIL_ON_FINISH_REASON_LENGTH=false
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ async def _lifespan(app: FastAPI):
|
|||||||
stop_event, worker_notifier, worker_health = await stack.enter_async_context(
|
stop_event, worker_notifier, worker_health = await stack.enter_async_context(
|
||||||
worker_consumer_lifespan(
|
worker_consumer_lifespan(
|
||||||
session_factory=app.state.runtime.session_factory,
|
session_factory=app.state.runtime.session_factory,
|
||||||
poll_interval_seconds=1.0,
|
poll_interval_seconds=settings.worker_poll_interval_seconds,
|
||||||
shutdown_timeout_seconds=(
|
shutdown_timeout_seconds=(
|
||||||
settings.worker_provider_timeout_seconds + settings.worker_shutdown_grace_seconds
|
settings.worker_provider_timeout_seconds + settings.worker_shutdown_grace_seconds
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ class Settings(BaseSettings):
|
|||||||
worker_stale_job_seconds: float = Field(default=30.0, gt=0.0)
|
worker_stale_job_seconds: float = Field(default=30.0, gt=0.0)
|
||||||
worker_retry_backoff_seconds: float = Field(default=1.0, ge=0.0)
|
worker_retry_backoff_seconds: float = Field(default=1.0, ge=0.0)
|
||||||
worker_shutdown_grace_seconds: float = Field(default=5.0, ge=0.0)
|
worker_shutdown_grace_seconds: float = Field(default=5.0, ge=0.0)
|
||||||
|
worker_poll_interval_seconds: float = Field(default=1.0, gt=0.0)
|
||||||
worker_min_transcription_chars: int = Field(default=0, ge=0)
|
worker_min_transcription_chars: int = Field(default=0, ge=0)
|
||||||
worker_min_transcription_lines: int = Field(default=0, ge=0)
|
worker_min_transcription_lines: int = Field(default=0, ge=0)
|
||||||
worker_fail_on_finish_reason_length: bool = False
|
worker_fail_on_finish_reason_length: bool = False
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
@@ -398,6 +399,10 @@ async def store_source_file(
|
|||||||
)
|
)
|
||||||
return StoredSourceFile(
|
return StoredSourceFile(
|
||||||
path=stored_path,
|
path=stored_path,
|
||||||
file_hash=hashlib.sha256(file_bytes).hexdigest(),
|
file_hash=await asyncio.to_thread(_sha256_hexdigest, file_bytes),
|
||||||
file_size_bytes=len(file_bytes),
|
file_size_bytes=len(file_bytes),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _sha256_hexdigest(data: bytes) -> str:
|
||||||
|
return hashlib.sha256(data).hexdigest()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
@@ -116,7 +117,7 @@ def _render_homepage_view(*, markdown_text: str, render_image_panel: Callable[[]
|
|||||||
ui.element("div")
|
ui.element("div")
|
||||||
|
|
||||||
|
|
||||||
def _render_homepage_editor(*, render_image_panel, markdown_input, on_upload) -> None:
|
def _render_homepage_editor(*, render_image_panel, markdown_input, on_upload, initial_markdown: str) -> None:
|
||||||
with ui.grid().classes("w-full grid-cols-12 gap-4"):
|
with ui.grid().classes("w-full grid-cols-12 gap-4"):
|
||||||
with ui.column().classes("col-span-12 lg:col-span-4 gap-4"):
|
with ui.column().classes("col-span-12 lg:col-span-4 gap-4"):
|
||||||
with archival_card(title="Homepage Image"):
|
with archival_card(title="Homepage Image"):
|
||||||
@@ -132,7 +133,7 @@ def _render_homepage_editor(*, render_image_panel, markdown_input, on_upload) ->
|
|||||||
markdown_input[0] = (
|
markdown_input[0] = (
|
||||||
ui.textarea(
|
ui.textarea(
|
||||||
label="Homepage markdown",
|
label="Homepage markdown",
|
||||||
value=read_homepage_markdown(),
|
value=initial_markdown,
|
||||||
)
|
)
|
||||||
.props("outlined autogrow")
|
.props("outlined autogrow")
|
||||||
.classes("w-full")
|
.classes("w-full")
|
||||||
@@ -152,6 +153,7 @@ def register_page() -> None: # noqa: PLR0915
|
|||||||
render_navigation_header(current_path="/homepage")
|
render_navigation_header(current_path="/homepage")
|
||||||
photos = await photos_service.list_photos(person_id=None)
|
photos = await photos_service.list_photos(person_id=None)
|
||||||
active_index = [0]
|
active_index = [0]
|
||||||
|
homepage_markdown = [""]
|
||||||
|
|
||||||
@ui.refreshable
|
@ui.refreshable
|
||||||
def render_image_panel() -> None:
|
def render_image_panel() -> None:
|
||||||
@@ -172,12 +174,20 @@ def register_page() -> None: # noqa: PLR0915
|
|||||||
on_click=lambda: ui.navigate.to("/homepage/edit"),
|
on_click=lambda: ui.navigate.to("/homepage/edit"),
|
||||||
icon="edit",
|
icon="edit",
|
||||||
).classes("ui-btn-primary text-xs")
|
).classes("ui-btn-primary text-xs")
|
||||||
_render_homepage_view(
|
|
||||||
markdown_text=read_homepage_markdown().strip(),
|
|
||||||
render_image_panel=render_image_panel,
|
|
||||||
)
|
|
||||||
|
|
||||||
@ui.page("/homepage/edit", title="Edit Homepage")
|
@ui.refreshable
|
||||||
|
def render_home_content() -> None:
|
||||||
|
_render_homepage_view(
|
||||||
|
markdown_text=homepage_markdown[0].strip(),
|
||||||
|
render_image_panel=render_image_panel,
|
||||||
|
)
|
||||||
|
|
||||||
|
render_home_content()
|
||||||
|
|
||||||
|
homepage_markdown[0] = await asyncio.to_thread(read_homepage_markdown, settings)
|
||||||
|
render_home_content.refresh()
|
||||||
|
|
||||||
|
@ui.page("/homepage/edit", title="Edit Home Page")
|
||||||
async def homepage_edit_page(request: Request, session_factory: SessionFactoryDep) -> None: # noqa: PLR0915
|
async def homepage_edit_page(request: Request, session_factory: SessionFactoryDep) -> None: # noqa: PLR0915
|
||||||
photos_service = PhotosService(session_factory=session_factory)
|
photos_service = PhotosService(session_factory=session_factory)
|
||||||
settings = resolve_runtime_settings(request)
|
settings = resolve_runtime_settings(request)
|
||||||
@@ -268,7 +278,11 @@ def register_page() -> None: # noqa: PLR0915
|
|||||||
ui.navigate.to("/homepage/edit")
|
ui.navigate.to("/homepage/edit")
|
||||||
|
|
||||||
async def save_homepage() -> None:
|
async def save_homepage() -> None:
|
||||||
save_homepage_markdown((markdown_input[0].value if markdown_input[0] is not None else "") or "")
|
await asyncio.to_thread(
|
||||||
|
save_homepage_markdown,
|
||||||
|
(markdown_input[0].value if markdown_input[0] is not None else "") or "",
|
||||||
|
settings,
|
||||||
|
)
|
||||||
ui.notify("Homepage saved", type="positive")
|
ui.notify("Homepage saved", type="positive")
|
||||||
ui.navigate.to("/homepage")
|
ui.navigate.to("/homepage")
|
||||||
|
|
||||||
@@ -283,4 +297,9 @@ def register_page() -> None: # noqa: PLR0915
|
|||||||
render_image_panel=render_image_panel,
|
render_image_panel=render_image_panel,
|
||||||
markdown_input=markdown_input,
|
markdown_input=markdown_input,
|
||||||
on_upload=on_upload,
|
on_upload=on_upload,
|
||||||
|
initial_markdown="",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
loaded_markdown = await asyncio.to_thread(read_homepage_markdown, settings)
|
||||||
|
if markdown_input[0] is not None:
|
||||||
|
markdown_input[0].value = loaded_markdown
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
@@ -540,8 +541,8 @@ async def _recover_prompt(prompts: PromptStore, name: str) -> None:
|
|||||||
|
|
||||||
|
|
||||||
async def _read_home_page_text(settings: Settings) -> str:
|
async def _read_home_page_text(settings: Settings) -> str:
|
||||||
return read_homepage_markdown(settings=settings)
|
return await asyncio.to_thread(read_homepage_markdown, settings=settings)
|
||||||
|
|
||||||
|
|
||||||
async def _write_home_page_text(settings: Settings, markdown_text: str) -> None:
|
async def _write_home_page_text(settings: Settings, markdown_text: str) -> None:
|
||||||
save_homepage_markdown(markdown_text, settings=settings)
|
await asyncio.to_thread(save_homepage_markdown, markdown_text, settings=settings)
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class TestAppLifespan:
|
|||||||
openrouter_api_key="test-key",
|
openrouter_api_key="test-key",
|
||||||
environment="test",
|
environment="test",
|
||||||
bootstrap_schema_on_startup=True,
|
bootstrap_schema_on_startup=True,
|
||||||
|
worker_poll_interval_seconds=2.5,
|
||||||
upload_dir=tmp_path / "uploads",
|
upload_dir=tmp_path / "uploads",
|
||||||
prompt_dir=tmp_path / "prompts",
|
prompt_dir=tmp_path / "prompts",
|
||||||
)
|
)
|
||||||
@@ -80,6 +81,7 @@ class TestAppLifespan:
|
|||||||
assert "worker_start" in calls
|
assert "worker_start" in calls
|
||||||
assert "worker_stop" in calls
|
assert "worker_stop" in calls
|
||||||
assert "dispose_db" in calls
|
assert "dispose_db" in calls
|
||||||
|
assert worker_kwargs["poll_interval_seconds"] == pytest.approx(settings.worker_poll_interval_seconds)
|
||||||
assert worker_kwargs["shutdown_timeout_seconds"] == pytest.approx(
|
assert worker_kwargs["shutdown_timeout_seconds"] == pytest.approx(
|
||||||
settings.worker_provider_timeout_seconds + settings.worker_shutdown_grace_seconds
|
settings.worker_provider_timeout_seconds + settings.worker_shutdown_grace_seconds
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ class TestWorkerReliabilitySettings:
|
|||||||
assert settings.worker_stale_job_seconds == 30.0
|
assert settings.worker_stale_job_seconds == 30.0
|
||||||
assert settings.worker_retry_backoff_seconds == 1.0
|
assert settings.worker_retry_backoff_seconds == 1.0
|
||||||
assert settings.worker_shutdown_grace_seconds == 5.0
|
assert settings.worker_shutdown_grace_seconds == 5.0
|
||||||
|
assert settings.worker_poll_interval_seconds == 1.0
|
||||||
|
|
||||||
|
|
||||||
def test_provider_timeout_is_not_capped_at_twenty_seconds():
|
def test_provider_timeout_is_not_capped_at_twenty_seconds():
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ def test_env_example_default_values_match_settings_defaults():
|
|||||||
"WORKER_STALE_JOB_SECONDS": str(defaults.worker_stale_job_seconds),
|
"WORKER_STALE_JOB_SECONDS": str(defaults.worker_stale_job_seconds),
|
||||||
"WORKER_RETRY_BACKOFF_SECONDS": str(defaults.worker_retry_backoff_seconds),
|
"WORKER_RETRY_BACKOFF_SECONDS": str(defaults.worker_retry_backoff_seconds),
|
||||||
"WORKER_SHUTDOWN_GRACE_SECONDS": str(defaults.worker_shutdown_grace_seconds),
|
"WORKER_SHUTDOWN_GRACE_SECONDS": str(defaults.worker_shutdown_grace_seconds),
|
||||||
|
"WORKER_POLL_INTERVAL_SECONDS": str(defaults.worker_poll_interval_seconds),
|
||||||
"WORKER_MIN_TRANSCRIPTION_CHARS": str(defaults.worker_min_transcription_chars),
|
"WORKER_MIN_TRANSCRIPTION_CHARS": str(defaults.worker_min_transcription_chars),
|
||||||
"WORKER_MIN_TRANSCRIPTION_LINES": str(defaults.worker_min_transcription_lines),
|
"WORKER_MIN_TRANSCRIPTION_LINES": str(defaults.worker_min_transcription_lines),
|
||||||
"WORKER_FAIL_ON_FINISH_REASON_LENGTH": str(defaults.worker_fail_on_finish_reason_length).lower(),
|
"WORKER_FAIL_ON_FINISH_REASON_LENGTH": str(defaults.worker_fail_on_finish_reason_length).lower(),
|
||||||
|
|||||||
Reference in New Issue
Block a user