Initial GeoIntel V1 foundation
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
from pydantic import Field, field_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
extra="ignore",
|
||||
populate_by_name=True,
|
||||
)
|
||||
|
||||
app_env: str = Field(default="development", validation_alias="GEOINTEL_ENV")
|
||||
app_version: str = Field(default="0.1.0")
|
||||
api_prefix: str = Field(default="/api/v1", validation_alias="GEOINTEL_API_PREFIX")
|
||||
database_url: str = Field(
|
||||
default="postgresql+psycopg://geointel:geointel@localhost:5432/geointel?connect_timeout=1",
|
||||
validation_alias="DATABASE_URL",
|
||||
)
|
||||
storage_root: str = Field(default="./storage", validation_alias="STORAGE_ROOT")
|
||||
max_upload_mb: int = Field(default=500, validation_alias="MAX_UPLOAD_MB")
|
||||
redis_url: str | None = Field(default=None, validation_alias="REDIS_URL")
|
||||
log_level: str = Field(default="INFO", validation_alias="GEOINTEL_LOG_LEVEL")
|
||||
database_statement_timeout_ms: int = Field(default=5_000, validation_alias="DATABASE_STATEMENT_TIMEOUT_MS")
|
||||
yolo_enabled: bool = Field(default=False, validation_alias="YOLO_ENABLED")
|
||||
yolo_model_path: str | None = Field(default=None, validation_alias="YOLO_MODEL_PATH")
|
||||
yolo_model_id: str = Field(default="yolo-configured", validation_alias="YOLO_MODEL_ID")
|
||||
yolo_model_display_name: str = Field(default="Configured YOLO detector", validation_alias="YOLO_MODEL_DISPLAY_NAME")
|
||||
yolo_model_version: str | None = Field(default=None, validation_alias="YOLO_MODEL_VERSION")
|
||||
yolo_device: str = Field(default="cpu", validation_alias="YOLO_DEVICE")
|
||||
yolo_image_size: int = Field(default=640, validation_alias="YOLO_IMAGE_SIZE")
|
||||
yolo_max_tiles: int = Field(default=100, validation_alias="YOLO_MAX_TILES")
|
||||
yolo_batch_size: int = Field(default=1, validation_alias="YOLO_BATCH_SIZE")
|
||||
cors_origins: list[str] | str = Field(
|
||||
default=["http://localhost:5173", "http://127.0.0.1:5173"],
|
||||
validation_alias="CORS_ORIGINS",
|
||||
)
|
||||
|
||||
@field_validator("cors_origins", mode="before")
|
||||
@classmethod
|
||||
def parse_cors_origins(cls, value: object) -> list[str]:
|
||||
if isinstance(value, str):
|
||||
return [item.strip() for item in value.split(",") if item.strip()]
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
if value is None:
|
||||
return ["http://localhost:5173", "http://127.0.0.1:5173"]
|
||||
return [str(value)]
|
||||
|
||||
|
||||
def get_settings() -> Settings:
|
||||
return Settings()
|
||||
@@ -0,0 +1,15 @@
|
||||
class AppError(Exception):
|
||||
"""Domain error used by services to return canonical API errors."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
code: str,
|
||||
message: str,
|
||||
details: dict | list | None = None,
|
||||
status_code: int = 400,
|
||||
) -> None:
|
||||
super().__init__(message)
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.details = details or {}
|
||||
self.status_code = status_code
|
||||
@@ -0,0 +1,12 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
|
||||
def configure_logging(level: str = "INFO") -> None:
|
||||
logging.basicConfig(
|
||||
level=level,
|
||||
format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
|
||||
stream=sys.stdout,
|
||||
)
|
||||
for name in ["uvicorn", "uvicorn.error", "uvicorn.access", "sqlalchemy.engine"]:
|
||||
logging.getLogger(name).setLevel(level)
|
||||
Reference in New Issue
Block a user