Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ __pycache__/
*.db
.ruff_cache
.coverage
coverage.xml
.vscode/
.hypothesis/
.hypothesis/
.venv/
39 changes: 24 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ repos:
- id: resolver-unit-tests
name: Resolver unit tests
language: system
entry: bash -lc 'python -m pytest -q'
entry: >-
bash -lc 'R="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")";
cd "$R" || exit 1;
P=""; for c in "$PWD/../.venv/bin/python" "$PWD/../venv/bin/python" "$PWD/.venv/bin/python" "$PWD/venv/bin/python" "$(command -v python3)" "$(command -v python)"; do
[[ -n "$c" && -x "$c" ]] || continue;
"$c" -c "import pytest" 2>/dev/null && { P="$c"; break; }; done;
[[ -n "$P" ]] || { echo "error: no Python with pytest; use .venv and pip install -e .[dev]" >&2; exit 1; };
"$P" -m pytest -q'
pass_filenames: false
files: \.py$
exclude: (^|/)tests/
Expand All @@ -14,13 +21,14 @@ repos:
name: Resolver mypy
language: system
entry: >-
bash -lc 'APP_ENV=development
ENVIRONMENT=development
RESOLVER_DATABASE_URL=postgresql://ci_user:ci_password_123@localhost:5432/resolver
RESOLVER_EXPECTED_SERVICE_TOKEN=ci_resolver_expected_service_token_123456789
RESOLVER_CONTEXT_VERIFY_KEY=ci_resolver_context_verify_key_12345678901234567890
PYTHONPATH=$PWD
mypy --config-file "$PWD/pyproject.toml" "$@"' --
bash -lc 'R="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")";
cd "$R" || exit 1;
P=""; for c in "$PWD/../.venv/bin/python" "$PWD/../venv/bin/python" "$PWD/.venv/bin/python" "$PWD/venv/bin/python" "$(command -v python3)" "$(command -v python)"; do
[[ -n "$c" && -x "$c" ]] || continue;
"$c" -c "import mypy, numpy, sqlalchemy, pydantic, httpx" 2>/dev/null && { P="$c"; break; }; done;
[[ -n "$P" ]] || { echo "error: no Python with mypy + resolver deps; pip install -e .[dev] in resolver/" >&2; exit 1; };
APP_ENV=development ENVIRONMENT=development RESOLVER_DATABASE_URL=postgresql://ci_user:ci_password_123@localhost:5432/resolver RESOLVER_EXPECTED_SERVICE_TOKEN=ci_resolver_expected_service_token_123456789 RESOLVER_CONTEXT_VERIFY_KEY=ci_resolver_context_verify_key_12345678901234567890 PYTHONPATH="$PWD" "$P" -m mypy --python-executable "$P" --cache-dir /tmp/resolver-mypy-cache --config-file "$PWD/pyproject.toml" .'
pass_filenames: false
files: \.py$
exclude: (^|/)tests/
types: [python]
Expand All @@ -29,13 +37,14 @@ repos:
name: Resolver pylint
language: system
entry: >-
bash -lc 'APP_ENV=development
ENVIRONMENT=development
RESOLVER_DATABASE_URL=postgresql://ci_user:ci_password_123@localhost:5432/resolver
RESOLVER_EXPECTED_SERVICE_TOKEN=ci_resolver_expected_service_token_123456789
RESOLVER_CONTEXT_VERIFY_KEY=ci_resolver_context_verify_key_12345678901234567890
PYTHONPATH=$PWD
pylint --rcfile "$PWD/pyproject.toml" "$@"' --
bash -lc 'R="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")";
cd "$R" || exit 1;
P=""; for c in "$PWD/../.venv/bin/python" "$PWD/../venv/bin/python" "$PWD/.venv/bin/python" "$PWD/venv/bin/python" "$(command -v python3)" "$(command -v python)"; do
[[ -n "$c" && -x "$c" ]] || continue;
"$c" -c "import pylint, numpy, sqlalchemy, pydantic, httpx" 2>/dev/null && { P="$c"; break; }; done;
[[ -n "$P" ]] || { echo "error: no Python with pylint + resolver deps; pip install -e .[dev] in resolver/" >&2; exit 1; };
APP_ENV=development ENVIRONMENT=development RESOLVER_DATABASE_URL=postgresql://ci_user:ci_password_123@localhost:5432/resolver RESOLVER_EXPECTED_SERVICE_TOKEN=ci_resolver_expected_service_token_123456789 RESOLVER_CONTEXT_VERIFY_KEY=ci_resolver_context_verify_key_12345678901234567890 PYTHONPATH="$PWD" "$P" -m pylint --rcfile "$PWD/pyproject.toml" .'
pass_filenames: false
files: \.py$
exclude: (^|/)tests/
types: [python]
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.

### Changed

- Pinned resolver runtime dependencies in `pyproject.toml` to explicit `==` versions for reproducible installs/builds.
- Applied a clean pylint reformat/refactor pass across resolver with safe line-wrapping/readability updates.
- Enforced strict naming consistency for module state, enum members, and internal variables/constants to align with configured pylint rules.
- Removed legacy uppercase alias usage from tests and aligned analyzer compatibility exports with strict snake_case lint policy.
Expand Down
10 changes: 5 additions & 5 deletions api/requests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
from .traces import TraceRequest

__all__ = [
"AnalyzeRequest",
"AnalyzeJobCreateRequest",
"MetricRequest",
"AnalyzeRequest",
"ChangepointRequest",
"CorrelateRequest",
"DeploymentEventRequest",
"LogRequest",
"TraceRequest",
"MetricRequest",
"SloRequest",
"CorrelateRequest",
"TopologyRequest",
"DeploymentEventRequest",
"TraceRequest",
]
10 changes: 4 additions & 6 deletions api/requests/_time_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

from __future__ import annotations

from typing import List, Optional

from pydantic import BaseModel, Field, model_validator


Expand All @@ -14,12 +12,12 @@ class TimeRangeRequest(BaseModel):
start: int
end: int
step: str = "15s"
services: List[str] = Field(default_factory=list)
log_query: Optional[str] = None
metric_queries: Optional[List[str]] = None
services: list[str] = Field(default_factory=list)
log_query: str | None = None
metric_queries: list[str] | None = None

@model_validator(mode="after")
def validate_time_range(self) -> "TimeRangeRequest":
def validate_time_range(self) -> TimeRangeRequest:
if self.start >= self.end:
raise ValueError("start must be less than end")
return self
6 changes: 2 additions & 4 deletions api/requests/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@

from __future__ import annotations

from typing import Optional

from pydantic import Field

from ._time_range import TimeRangeRequest


class AnalyzeRequest(TimeRangeRequest):
config_yaml: Optional[str] = None
sensitivity: Optional[float] = Field(default=3.0, ge=1.0, le=6.0)
config_yaml: str | None = None
sensitivity: float | None = Field(default=3.0, ge=1.0, le=6.0)
apdex_threshold_ms: float = 500.0
slo_target: float = Field(default=0.999, ge=0.0, le=1.0)
correlation_window_seconds: float = Field(default=60.0, ge=10.0, le=600.0)
Expand Down
4 changes: 1 addition & 3 deletions api/requests/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

from __future__ import annotations

from typing import Optional

from pydantic import BaseModel, Field


Expand All @@ -21,7 +19,7 @@ class MetricRequest(BaseModel):
start: int
end: int
step: str = "15s"
sensitivity: Optional[float] = Field(default=3.0, ge=1.0, le=6.0)
sensitivity: float | None = Field(default=3.0, ge=1.0, le=6.0)


class ChangepointRequest(BaseModel):
Expand Down
8 changes: 3 additions & 5 deletions api/requests/slo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

from __future__ import annotations

from typing import Optional

from pydantic import BaseModel, Field, model_validator


Expand All @@ -22,11 +20,11 @@ class SloRequest(BaseModel):
end: int
step: str = "15s"
target_availability: float = Field(default=0.999, ge=0.0, le=1.0)
error_query: Optional[str] = None
total_query: Optional[str] = None
error_query: str | None = None
total_query: str | None = None

@model_validator(mode="after")
def validate_time_range(self) -> "SloRequest":
def validate_time_range(self) -> SloRequest:
if self.start >= self.end:
raise ValueError("start must be less than end")
return self
4 changes: 1 addition & 3 deletions api/requests/traces.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@

from __future__ import annotations

from typing import Optional

from pydantic import BaseModel


class TraceRequest(BaseModel):
tenant_id: str
start: int
end: int
service: Optional[str] = None
service: str | None = None
apdex_threshold_ms: float = 500.0
26 changes: 13 additions & 13 deletions api/responses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,26 @@
}

__all__ = [
"NpModel",
"MetricAnomaly",
"LogBurst",
"LogPattern",
"ServiceLatency",
"ErrorPropagation",
"RootCause",
"SloBurnAlert",
"BudgetStatus",
"AnalysisQuality",
"AnalysisReport",
"MetricSeriesDistributionStats",
"JobStatus",
"AnalyzeConfigTemplateResponse",
"AnalyzeJobCreateResponse",
"AnalyzeJobSummary",
"AnalyzeJobListResponse",
"AnalyzeJobResultResponse",
"AnalyzeReportResponse",
"AnalyzeJobSummary",
"AnalyzeReportDeleteResponse",
"AnalyzeReportResponse",
"BudgetStatus",
"ErrorPropagation",
"JobStatus",
"LogBurst",
"LogPattern",
"MetricAnomaly",
"MetricSeriesDistributionStats",
"NpModel",
"RootCause",
"ServiceLatency",
"SloBurnAlert",
]


Expand Down
48 changes: 23 additions & 25 deletions api/responses/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

from __future__ import annotations

from typing import Dict, List, Optional

from pydantic import Field

from engine.causal.bayesian import BayesianScore
Expand All @@ -24,12 +22,12 @@
from engine.ml.clustering import AnomalyCluster
from engine.ml.ranking import RankedCause

from .base import NpModel
from .anomalies import MetricAnomaly
from .base import NpModel
from .logs import LogBurst, LogPattern
from .traces import ErrorPropagation, ServiceLatency
from .slo import SloBurnAlert
from .rca import RootCause
from .slo import SloBurnAlert
from .traces import ErrorPropagation, ServiceLatency


class MetricSeriesDistributionStats(NpModel):
Expand All @@ -51,8 +49,8 @@ class MetricSeriesDistributionStats(NpModel):


class AnalysisQuality(NpModel):
anomaly_density: Dict[str, float] = Field(default_factory=dict)
suppression_counts: Dict[str, int] = Field(default_factory=dict)
anomaly_density: dict[str, float] = Field(default_factory=dict)
suppression_counts: dict[str, int] = Field(default_factory=dict)
gating_profile: str
confidence_calibration_version: str

Expand All @@ -62,23 +60,23 @@ class AnalysisReport(NpModel):
start: int
end: int
duration_seconds: int
metric_anomalies: List[MetricAnomaly]
log_bursts: List[LogBurst]
log_patterns: List[LogPattern]
service_latency: List[ServiceLatency]
error_propagation: List[ErrorPropagation]
slo_alerts: List[SloBurnAlert] = []
root_causes: List[RootCause]
ranked_causes: List[RankedCause] = []
change_points: List[ChangePoint] = []
log_metric_links: List[LogMetricLink] = []
forecasts: List[TrajectoryForecast] = []
degradation_signals: List[DegradationSignal] = []
anomaly_clusters: List[AnomalyCluster] = []
granger_results: List[GrangerResult] = []
bayesian_scores: List[BayesianScore] = []
analysis_warnings: List[str] = []
metric_anomalies: list[MetricAnomaly]
log_bursts: list[LogBurst]
log_patterns: list[LogPattern]
service_latency: list[ServiceLatency]
error_propagation: list[ErrorPropagation]
slo_alerts: list[SloBurnAlert] = []
root_causes: list[RootCause]
ranked_causes: list[RankedCause] = []
change_points: list[ChangePoint] = []
log_metric_links: list[LogMetricLink] = []
forecasts: list[TrajectoryForecast] = []
degradation_signals: list[DegradationSignal] = []
anomaly_clusters: list[AnomalyCluster] = []
granger_results: list[GrangerResult] = []
bayesian_scores: list[BayesianScore] = []
analysis_warnings: list[str] = []
overall_severity: Severity
summary: str
quality: Optional[AnalysisQuality] = None
metric_series_statistics: List[MetricSeriesDistributionStats] = Field(default_factory=list)
quality: AnalysisQuality | None = None
metric_series_statistics: list[MetricSeriesDistributionStats] = Field(default_factory=list)
4 changes: 1 addition & 3 deletions api/responses/anomalies.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

from __future__ import annotations

from typing import Tuple

from engine.enums import ChangeType, Severity

from .base import NpModel
Expand All @@ -25,7 +23,7 @@ class MetricAnomaly(NpModel):
z_score: float
mad_score: float
isolation_score: float
expected_range: Tuple[float, float]
expected_range: tuple[float, float]
severity: Severity
description: str
iqr_score: float = 0.0
Expand Down
17 changes: 8 additions & 9 deletions api/responses/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

from datetime import datetime
from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field

Expand Down Expand Up @@ -42,18 +41,18 @@ class AnalyzeJobSummary(BaseModel):
report_id: str
status: JobStatus
created_at: datetime
started_at: Optional[datetime] = None
finished_at: Optional[datetime] = None
duration_ms: Optional[int] = None
error: Optional[str] = None
summary_preview: Optional[str] = None
started_at: datetime | None = None
finished_at: datetime | None = None
duration_ms: int | None = None
error: str | None = None
summary_preview: str | None = None
tenant_id: str
requested_by: str


class AnalyzeJobListResponse(BaseModel):
items: list[AnalyzeJobSummary]
next_cursor: Optional[str] = None
next_cursor: str | None = None


class AnalyzeJobResultResponse(BaseModel):
Expand All @@ -62,7 +61,7 @@ class AnalyzeJobResultResponse(BaseModel):
status: JobStatus
tenant_id: str
requested_by: str
result: Optional[JSONDict] = None
result: JSONDict | None = None


class AnalyzeReportResponse(BaseModel):
Expand All @@ -71,7 +70,7 @@ class AnalyzeReportResponse(BaseModel):
status: JobStatus
tenant_id: str
requested_by: str
result: Optional[JSONDict] = None
result: JSONDict | None = None


class AnalyzeReportDeleteResponse(BaseModel):
Expand Down
Loading
Loading