tsagentkit 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tsagentkit/__init__.py +126 -0
- tsagentkit/anomaly/__init__.py +130 -0
- tsagentkit/backtest/__init__.py +48 -0
- tsagentkit/backtest/engine.py +788 -0
- tsagentkit/backtest/metrics.py +244 -0
- tsagentkit/backtest/report.py +342 -0
- tsagentkit/calibration/__init__.py +136 -0
- tsagentkit/contracts/__init__.py +133 -0
- tsagentkit/contracts/errors.py +275 -0
- tsagentkit/contracts/results.py +418 -0
- tsagentkit/contracts/schema.py +44 -0
- tsagentkit/contracts/task_spec.py +300 -0
- tsagentkit/covariates/__init__.py +340 -0
- tsagentkit/eval/__init__.py +285 -0
- tsagentkit/features/__init__.py +20 -0
- tsagentkit/features/covariates.py +328 -0
- tsagentkit/features/extra/__init__.py +5 -0
- tsagentkit/features/extra/native.py +179 -0
- tsagentkit/features/factory.py +187 -0
- tsagentkit/features/matrix.py +159 -0
- tsagentkit/features/tsfeatures_adapter.py +115 -0
- tsagentkit/features/versioning.py +203 -0
- tsagentkit/hierarchy/__init__.py +39 -0
- tsagentkit/hierarchy/aggregation.py +62 -0
- tsagentkit/hierarchy/evaluator.py +400 -0
- tsagentkit/hierarchy/reconciliation.py +232 -0
- tsagentkit/hierarchy/structure.py +453 -0
- tsagentkit/models/__init__.py +182 -0
- tsagentkit/models/adapters/__init__.py +83 -0
- tsagentkit/models/adapters/base.py +321 -0
- tsagentkit/models/adapters/chronos.py +387 -0
- tsagentkit/models/adapters/moirai.py +256 -0
- tsagentkit/models/adapters/registry.py +171 -0
- tsagentkit/models/adapters/timesfm.py +440 -0
- tsagentkit/models/baselines.py +207 -0
- tsagentkit/models/sktime.py +307 -0
- tsagentkit/monitoring/__init__.py +51 -0
- tsagentkit/monitoring/alerts.py +302 -0
- tsagentkit/monitoring/coverage.py +203 -0
- tsagentkit/monitoring/drift.py +330 -0
- tsagentkit/monitoring/report.py +214 -0
- tsagentkit/monitoring/stability.py +275 -0
- tsagentkit/monitoring/triggers.py +423 -0
- tsagentkit/qa/__init__.py +347 -0
- tsagentkit/router/__init__.py +37 -0
- tsagentkit/router/bucketing.py +489 -0
- tsagentkit/router/fallback.py +132 -0
- tsagentkit/router/plan.py +23 -0
- tsagentkit/router/router.py +271 -0
- tsagentkit/series/__init__.py +26 -0
- tsagentkit/series/alignment.py +206 -0
- tsagentkit/series/dataset.py +449 -0
- tsagentkit/series/sparsity.py +261 -0
- tsagentkit/series/validation.py +393 -0
- tsagentkit/serving/__init__.py +39 -0
- tsagentkit/serving/orchestration.py +943 -0
- tsagentkit/serving/packaging.py +73 -0
- tsagentkit/serving/provenance.py +317 -0
- tsagentkit/serving/tsfm_cache.py +214 -0
- tsagentkit/skill/README.md +135 -0
- tsagentkit/skill/__init__.py +8 -0
- tsagentkit/skill/recipes.md +429 -0
- tsagentkit/skill/tool_map.md +21 -0
- tsagentkit/time/__init__.py +134 -0
- tsagentkit/utils/__init__.py +20 -0
- tsagentkit/utils/quantiles.py +83 -0
- tsagentkit/utils/signature.py +47 -0
- tsagentkit/utils/temporal.py +41 -0
- tsagentkit-1.0.2.dist-info/METADATA +371 -0
- tsagentkit-1.0.2.dist-info/RECORD +72 -0
- tsagentkit-1.0.2.dist-info/WHEEL +4 -0
- tsagentkit-1.0.2.dist-info/licenses/LICENSE +201 -0
tsagentkit/__init__.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""tsagentkit - Robust execution engine for time-series forecasting agents.
|
|
2
|
+
|
|
3
|
+
This library provides a strict, production-grade workflow skeleton for
|
|
4
|
+
external coding agents (LLMs/AI agents) performing time-series forecasting tasks.
|
|
5
|
+
|
|
6
|
+
Basic usage:
|
|
7
|
+
>>> from tsagentkit import TaskSpec, validate_contract, run_forecast
|
|
8
|
+
>>> spec = TaskSpec(h=7, freq="D")
|
|
9
|
+
>>> result = run_forecast(data, spec)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__version__ = "1.0.2"
|
|
13
|
+
|
|
14
|
+
# Import commonly used items from contracts
|
|
15
|
+
from tsagentkit.anomaly import detect_anomalies
|
|
16
|
+
from tsagentkit.backtest import (
|
|
17
|
+
BacktestReport,
|
|
18
|
+
mase,
|
|
19
|
+
rolling_backtest,
|
|
20
|
+
smape,
|
|
21
|
+
wape,
|
|
22
|
+
)
|
|
23
|
+
from tsagentkit.calibration import apply_calibrator, fit_calibrator
|
|
24
|
+
from tsagentkit.contracts import (
|
|
25
|
+
ECovariateLeakage,
|
|
26
|
+
ESplitRandomForbidden,
|
|
27
|
+
ForecastResult,
|
|
28
|
+
ModelArtifact,
|
|
29
|
+
Provenance,
|
|
30
|
+
RunArtifact,
|
|
31
|
+
TaskSpec,
|
|
32
|
+
# Errors
|
|
33
|
+
TSAgentKitError,
|
|
34
|
+
ValidationReport,
|
|
35
|
+
validate_contract,
|
|
36
|
+
)
|
|
37
|
+
from tsagentkit.eval import MetricFrame, ScoreSummary, evaluate_forecasts
|
|
38
|
+
from tsagentkit.qa import run_qa
|
|
39
|
+
from tsagentkit.router import (
|
|
40
|
+
BucketConfig,
|
|
41
|
+
BucketProfile,
|
|
42
|
+
BucketStatistics,
|
|
43
|
+
# Bucketing (v0.2)
|
|
44
|
+
DataBucketer,
|
|
45
|
+
FallbackLadder,
|
|
46
|
+
PlanSpec,
|
|
47
|
+
SeriesBucket,
|
|
48
|
+
compute_plan_signature,
|
|
49
|
+
execute_with_fallback,
|
|
50
|
+
get_candidate_models,
|
|
51
|
+
make_plan,
|
|
52
|
+
)
|
|
53
|
+
from tsagentkit.series import (
|
|
54
|
+
SparsityClass,
|
|
55
|
+
SparsityProfile,
|
|
56
|
+
TSDataset,
|
|
57
|
+
build_dataset,
|
|
58
|
+
)
|
|
59
|
+
from tsagentkit.serving import MonitoringConfig, run_forecast
|
|
60
|
+
|
|
61
|
+
# Structured logging (v1.0)
|
|
62
|
+
from tsagentkit.serving.provenance import (
|
|
63
|
+
StructuredLogger,
|
|
64
|
+
format_event_json,
|
|
65
|
+
log_event,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# v0.2 imports (optional - use directly from submodules)
|
|
69
|
+
# from tsagentkit.features import FeatureFactory, FeatureMatrix, compute_feature_hash
|
|
70
|
+
# from tsagentkit.monitoring import DriftDetector, StabilityMonitor, TriggerEvaluator
|
|
71
|
+
|
|
72
|
+
__all__ = [
|
|
73
|
+
"__version__",
|
|
74
|
+
# Core contracts
|
|
75
|
+
"TaskSpec",
|
|
76
|
+
"ValidationReport",
|
|
77
|
+
"ForecastResult",
|
|
78
|
+
"ModelArtifact",
|
|
79
|
+
"Provenance",
|
|
80
|
+
"RunArtifact",
|
|
81
|
+
"validate_contract",
|
|
82
|
+
# Key errors
|
|
83
|
+
"TSAgentKitError",
|
|
84
|
+
"ESplitRandomForbidden",
|
|
85
|
+
"ECovariateLeakage",
|
|
86
|
+
# Series
|
|
87
|
+
"TSDataset",
|
|
88
|
+
"SparsityProfile",
|
|
89
|
+
"SparsityClass",
|
|
90
|
+
"build_dataset",
|
|
91
|
+
# QA
|
|
92
|
+
"run_qa",
|
|
93
|
+
# Calibration + Anomaly + Eval
|
|
94
|
+
"fit_calibrator",
|
|
95
|
+
"apply_calibrator",
|
|
96
|
+
"detect_anomalies",
|
|
97
|
+
"evaluate_forecasts",
|
|
98
|
+
"MetricFrame",
|
|
99
|
+
"ScoreSummary",
|
|
100
|
+
# Router
|
|
101
|
+
"PlanSpec",
|
|
102
|
+
"compute_plan_signature",
|
|
103
|
+
"get_candidate_models",
|
|
104
|
+
"make_plan",
|
|
105
|
+
"FallbackLadder",
|
|
106
|
+
"execute_with_fallback",
|
|
107
|
+
# Router Bucketing (v0.2)
|
|
108
|
+
"DataBucketer",
|
|
109
|
+
"BucketConfig",
|
|
110
|
+
"BucketProfile",
|
|
111
|
+
"BucketStatistics",
|
|
112
|
+
"SeriesBucket",
|
|
113
|
+
# Backtest
|
|
114
|
+
"BacktestReport",
|
|
115
|
+
"rolling_backtest",
|
|
116
|
+
"wape",
|
|
117
|
+
"smape",
|
|
118
|
+
"mase",
|
|
119
|
+
# Serving
|
|
120
|
+
"run_forecast",
|
|
121
|
+
"MonitoringConfig",
|
|
122
|
+
# Structured Logging (v1.0)
|
|
123
|
+
"log_event",
|
|
124
|
+
"format_event_json",
|
|
125
|
+
"StructuredLogger",
|
|
126
|
+
]
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Anomaly detection based on forecast intervals/quantiles."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, Literal
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
from tsagentkit.contracts import EAnomalyFail
|
|
13
|
+
from tsagentkit.utils import parse_quantile_column
|
|
14
|
+
|
|
15
|
+
AnomalyMethod = Literal["interval_breach", "conformal", "mad_residual"]
|
|
16
|
+
AnomalyScore = Literal["margin", "normalized_margin", "zscore"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class AnomalyReport:
|
|
21
|
+
"""Anomaly detection report."""
|
|
22
|
+
|
|
23
|
+
frame: pd.DataFrame
|
|
24
|
+
method: str
|
|
25
|
+
level: int
|
|
26
|
+
score: str
|
|
27
|
+
summary: dict[str, Any]
|
|
28
|
+
|
|
29
|
+
def to_dict(self) -> dict[str, Any]:
|
|
30
|
+
return {
|
|
31
|
+
"method": self.method,
|
|
32
|
+
"level": self.level,
|
|
33
|
+
"score": self.score,
|
|
34
|
+
"summary": self.summary,
|
|
35
|
+
"frame": self.frame.to_dict("records"),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def detect_anomalies(
|
|
40
|
+
forecast_with_y: pd.DataFrame,
|
|
41
|
+
method: AnomalyMethod = "interval_breach",
|
|
42
|
+
level: int = 99,
|
|
43
|
+
score: AnomalyScore = "normalized_margin",
|
|
44
|
+
calibrator: Any | None = None,
|
|
45
|
+
strict: bool = False,
|
|
46
|
+
id_col: str = "unique_id",
|
|
47
|
+
ds_col: str = "ds",
|
|
48
|
+
actual_col: str = "y",
|
|
49
|
+
pred_col: str = "yhat",
|
|
50
|
+
) -> AnomalyReport:
|
|
51
|
+
"""Detect anomalies using forecast uncertainty."""
|
|
52
|
+
if forecast_with_y.empty:
|
|
53
|
+
raise EAnomalyFail("Forecast frame is empty.")
|
|
54
|
+
|
|
55
|
+
if strict and calibrator is None:
|
|
56
|
+
raise EAnomalyFail(
|
|
57
|
+
"Strict mode requires calibrated intervals/quantiles for anomaly detection."
|
|
58
|
+
)
|
|
59
|
+
if not strict and calibrator is None and method != "interval_breach":
|
|
60
|
+
warnings.warn(
|
|
61
|
+
"Anomaly detection requested without calibrated intervals/quantiles. "
|
|
62
|
+
"Proceeding may increase false positives.",
|
|
63
|
+
UserWarning,
|
|
64
|
+
stacklevel=2,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
df = forecast_with_y.copy()
|
|
68
|
+
|
|
69
|
+
lo_col = f"yhat_lo_{level}"
|
|
70
|
+
hi_col = f"yhat_hi_{level}"
|
|
71
|
+
if lo_col not in df.columns or hi_col not in df.columns:
|
|
72
|
+
# Try to infer from quantiles
|
|
73
|
+
lower_q = (1 - level / 100.0) / 2
|
|
74
|
+
upper_q = 1 - lower_q
|
|
75
|
+
q_cols = {parse_quantile_column(c): c for c in df.columns}
|
|
76
|
+
lo_col = q_cols.get(lower_q)
|
|
77
|
+
hi_col = q_cols.get(upper_q)
|
|
78
|
+
if lo_col is None or hi_col is None:
|
|
79
|
+
raise EAnomalyFail(
|
|
80
|
+
"No interval/quantile columns available for anomaly detection.",
|
|
81
|
+
context={"expected_level": level},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
y = df[actual_col].to_numpy(dtype=float)
|
|
85
|
+
yhat = df[pred_col].to_numpy(dtype=float) if pred_col in df.columns else None
|
|
86
|
+
lo = df[lo_col].to_numpy(dtype=float)
|
|
87
|
+
hi = df[hi_col].to_numpy(dtype=float)
|
|
88
|
+
|
|
89
|
+
margin = np.zeros_like(y, dtype=float)
|
|
90
|
+
margin = np.where(y < lo, lo - y, margin)
|
|
91
|
+
margin = np.where(y > hi, y - hi, margin)
|
|
92
|
+
|
|
93
|
+
if score == "margin":
|
|
94
|
+
anomaly_score = margin
|
|
95
|
+
elif score == "normalized_margin":
|
|
96
|
+
width = np.maximum(hi - lo, 1e-8)
|
|
97
|
+
anomaly_score = margin / width
|
|
98
|
+
else: # zscore
|
|
99
|
+
if yhat is None:
|
|
100
|
+
raise EAnomalyFail("yhat is required for zscore anomaly score.")
|
|
101
|
+
width = np.maximum(hi - lo, 1e-8)
|
|
102
|
+
anomaly_score = np.abs(y - yhat) / (0.5 * width)
|
|
103
|
+
|
|
104
|
+
anomaly_flag = margin > 0
|
|
105
|
+
|
|
106
|
+
frame = df[[id_col, ds_col, actual_col, pred_col]].copy()
|
|
107
|
+
frame["lo"] = lo
|
|
108
|
+
frame["hi"] = hi
|
|
109
|
+
frame["anomaly"] = anomaly_flag
|
|
110
|
+
frame["anomaly_score"] = anomaly_score
|
|
111
|
+
frame["threshold"] = level
|
|
112
|
+
frame["method"] = method
|
|
113
|
+
frame["score"] = score
|
|
114
|
+
|
|
115
|
+
summary = {
|
|
116
|
+
"total": int(len(frame)),
|
|
117
|
+
"anomalies": int(np.sum(anomaly_flag)),
|
|
118
|
+
"anomaly_rate": float(np.mean(anomaly_flag)) if len(frame) > 0 else 0.0,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return AnomalyReport(
|
|
122
|
+
frame=frame,
|
|
123
|
+
method=method,
|
|
124
|
+
level=level,
|
|
125
|
+
score=score,
|
|
126
|
+
summary=summary,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
__all__ = ["AnomalyReport", "detect_anomalies"]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Backtest module for tsagentkit.
|
|
2
|
+
|
|
3
|
+
Provides rolling window backtesting and report structures. Legacy metric
|
|
4
|
+
helpers are still exported for compatibility but are deprecated; prefer
|
|
5
|
+
``tsagentkit.eval.evaluate_forecasts`` for new evaluation flows.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .engine import cross_validation_split, rolling_backtest
|
|
9
|
+
from .metrics import (
|
|
10
|
+
compute_all_metrics,
|
|
11
|
+
compute_metrics_by_series,
|
|
12
|
+
mae,
|
|
13
|
+
mase,
|
|
14
|
+
pinball_loss,
|
|
15
|
+
rmse,
|
|
16
|
+
smape,
|
|
17
|
+
wape,
|
|
18
|
+
wql,
|
|
19
|
+
)
|
|
20
|
+
from .report import (
|
|
21
|
+
BacktestReport,
|
|
22
|
+
SegmentMetrics,
|
|
23
|
+
SeriesMetrics,
|
|
24
|
+
TemporalMetrics,
|
|
25
|
+
WindowResult,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
# Engine
|
|
30
|
+
"rolling_backtest",
|
|
31
|
+
"cross_validation_split",
|
|
32
|
+
# Report
|
|
33
|
+
"BacktestReport",
|
|
34
|
+
"WindowResult",
|
|
35
|
+
"SeriesMetrics",
|
|
36
|
+
"SegmentMetrics",
|
|
37
|
+
"TemporalMetrics",
|
|
38
|
+
# Metrics
|
|
39
|
+
"wape",
|
|
40
|
+
"smape",
|
|
41
|
+
"mase",
|
|
42
|
+
"mae",
|
|
43
|
+
"rmse",
|
|
44
|
+
"pinball_loss",
|
|
45
|
+
"wql",
|
|
46
|
+
"compute_all_metrics",
|
|
47
|
+
"compute_metrics_by_series",
|
|
48
|
+
]
|