|
|
"""Application configuration using pydantic-settings.""" |
|
|
|
|
|
from __future__ import annotations |
|
|
|
|
|
import os |
|
|
from pathlib import Path |
|
|
from typing import Literal |
|
|
|
|
|
from pydantic import Field, computed_field, field_validator |
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict |
|
|
|
|
|
|
|
|
def is_running_in_hf_spaces() -> bool: |
|
|
""" |
|
|
Detect if running inside Hugging Face Spaces environment. |
|
|
|
|
|
Returns: |
|
|
True if running in HF Spaces, False otherwise |
|
|
|
|
|
Detection methods (all env-var based for reliability): |
|
|
1. HF_SPACES=1 env var (set by our Dockerfile) |
|
|
2. SPACE_ID env var (set by HF Spaces runtime) |
|
|
|
|
|
Note: |
|
|
We intentionally avoid path-based detection (like checking for |
|
|
/home/user or /app) because these paths exist on many Linux |
|
|
systems and would cause false positives. |
|
|
""" |
|
|
|
|
|
if os.environ.get("HF_SPACES") == "1": |
|
|
return True |
|
|
|
|
|
return bool(os.environ.get("SPACE_ID")) |
|
|
|
|
|
|
|
|
def is_deepisles_direct_available() -> bool: |
|
|
""" |
|
|
Check if DeepISLES can be invoked directly (without Docker). |
|
|
|
|
|
Returns: |
|
|
True if DEEPISLES_DIRECT_INVOCATION env var is set |
|
|
|
|
|
This check is intentionally simple and side-effect free. |
|
|
The env var is set by our Dockerfile when running on HF Spaces. |
|
|
Actual module path setup happens in inference/direct.py when invoked. |
|
|
|
|
|
Note: |
|
|
We don't attempt import-based detection here because it would |
|
|
require modifying sys.path, which is a side effect inappropriate |
|
|
for a simple availability check. |
|
|
""" |
|
|
return os.environ.get("DEEPISLES_DIRECT_INVOCATION") == "1" |
|
|
|
|
|
|
|
|
class Settings(BaseSettings): |
|
|
""" |
|
|
Application settings loaded from environment variables. |
|
|
|
|
|
All settings can be overridden via environment variables with |
|
|
the STROKE_DEMO_ prefix. |
|
|
|
|
|
Example: |
|
|
export STROKE_DEMO_LOG_LEVEL=DEBUG |
|
|
export STROKE_DEMO_HF_DATASET_ID=my/dataset |
|
|
""" |
|
|
|
|
|
model_config = SettingsConfigDict( |
|
|
env_prefix="STROKE_DEMO_", |
|
|
env_file=".env", |
|
|
env_file_encoding="utf-8", |
|
|
extra="ignore", |
|
|
) |
|
|
|
|
|
|
|
|
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "INFO" |
|
|
log_format: Literal["simple", "detailed", "json"] = "simple" |
|
|
|
|
|
|
|
|
hf_dataset_id: str = "YongchengYAO/ISLES24-MR-Lite" |
|
|
hf_cache_dir: Path | None = None |
|
|
hf_token: str | None = Field(default=None, repr=False) |
|
|
|
|
|
|
|
|
deepisles_docker_image: str = "isleschallenge/deepisles" |
|
|
deepisles_fast_mode: bool = True |
|
|
deepisles_timeout_seconds: int = 1800 |
|
|
deepisles_use_gpu: bool = True |
|
|
|
|
|
deepisles_repo_path: Path | None = None |
|
|
|
|
|
|
|
|
temp_dir: Path | None = None |
|
|
results_dir: Path = Path("./results") |
|
|
|
|
|
|
|
|
gradio_server_name: str = "0.0.0.0" |
|
|
gradio_server_port: int = 7860 |
|
|
gradio_share: bool = False |
|
|
|
|
|
@computed_field |
|
|
@property |
|
|
def is_hf_spaces(self) -> bool: |
|
|
"""Check if running in HF Spaces environment.""" |
|
|
return is_running_in_hf_spaces() |
|
|
|
|
|
@computed_field |
|
|
@property |
|
|
def use_direct_invocation(self) -> bool: |
|
|
""" |
|
|
Check if should use direct DeepISLES invocation (vs Docker). |
|
|
|
|
|
Direct invocation is used when: |
|
|
1. Running in HF Spaces (cannot run Docker-in-Docker) |
|
|
2. DeepISLES modules are available for import |
|
|
""" |
|
|
return self.is_hf_spaces or is_deepisles_direct_available() |
|
|
|
|
|
@field_validator("results_dir", mode="before") |
|
|
@classmethod |
|
|
def ensure_results_dir_exists(cls, v: Path | str) -> Path: |
|
|
"""Create results directory if it doesn't exist.""" |
|
|
path = Path(v) |
|
|
path.mkdir(parents=True, exist_ok=True) |
|
|
return path |
|
|
|
|
|
|
|
|
|
|
|
settings = Settings() |
|
|
|
|
|
|
|
|
def get_settings() -> Settings: |
|
|
"""Get the current settings instance.""" |
|
|
return settings |
|
|
|
|
|
|
|
|
def reload_settings() -> Settings: |
|
|
"""Reload settings from environment (useful for testing).""" |
|
|
global settings |
|
|
settings = Settings() |
|
|
return settings |
|
|
|