from dataclasses import dataclass from enum import StrEnum from typing import Any from pydantic import BaseModel, ConfigDict, Field from core.schemas.status import AuditSeverity class LoggingOutput(StrEnum): STDOUT = "stdout" STDERR = "stderr" class LoggingConfig(BaseModel): level: str = Field("INFO", min_length=1) output: LoggingOutput = Field(LoggingOutput.STDOUT) class PluginPackagesConfig(BaseModel): renderers: list[str] = Field(default_factory=lambda: ["reporting.exporters"]) transports: list[str] = Field(default_factory=lambda: ["reporting.transports"]) modules: list[str] = Field(default_factory=list) class PluginConfigRef(BaseModel): model_config = ConfigDict(extra="allow") type: str = Field(min_length=1) def payload(self) -> dict[str, Any]: return dict(self.model_extra or {}) class ReportOutputConfig(BaseModel): renderer: PluginConfigRef = Field() transport: PluginConfigRef = Field() class ReportsConfig(BaseModel): outputs: list[ReportOutputConfig] = Field(min_length=1) class Config(BaseModel): exclude_categories: list[str] = Field(default_factory=list) allow_failures_below: AuditSeverity = Field() strict_mode: bool = Field() fail_fast: bool = Field(False) dry_run: bool = Field(False) max_concurrency: int = Field(4, ge=1) logging: LoggingConfig = Field(default_factory=LoggingConfig) plugin_packages: PluginPackagesConfig = Field(default_factory=PluginPackagesConfig) reports: ReportsConfig = Field() modules: dict[str, dict[str, Any]] = Field(default_factory=dict) @dataclass(frozen=True) class ResolvedPluginConfig: type: str config: BaseModel @dataclass(frozen=True) class ResolvedReportOutput: renderer: ResolvedPluginConfig transport: ResolvedPluginConfig @dataclass(frozen=True) class ResolvedConfig: exclude_categories: list[str] allow_failures_below: AuditSeverity strict_mode: bool fail_fast: bool dry_run: bool max_concurrency: int logging: LoggingConfig plugin_packages: PluginPackagesConfig reports: list[ResolvedReportOutput] modules: dict[str, BaseModel]