@anyshift/mcp-proxy 0.3.5 → 0.4.0
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.
- package/dist/__tests__/unit/timeseries.test.d.ts +1 -0
- package/dist/__tests__/unit/timeseries.test.js +217 -0
- package/dist/fileWriter/writer.js +1 -1
- package/dist/index.js +75 -29
- package/dist/jq/handler.js +11 -56
- package/dist/jq/tool.d.ts +3 -9
- package/dist/jq/tool.js +4 -4
- package/dist/timeseries/algorithms/index.d.ts +8 -0
- package/dist/timeseries/algorithms/index.js +8 -0
- package/dist/timeseries/algorithms/mad.d.ts +15 -0
- package/dist/timeseries/algorithms/mad.js +44 -0
- package/dist/timeseries/algorithms/moving-average.d.ts +15 -0
- package/dist/timeseries/algorithms/moving-average.js +72 -0
- package/dist/timeseries/algorithms/rolling-quantile.d.ts +16 -0
- package/dist/timeseries/algorithms/rolling-quantile.js +78 -0
- package/dist/timeseries/algorithms/stats.d.ts +49 -0
- package/dist/timeseries/algorithms/stats.js +139 -0
- package/dist/timeseries/algorithms/threshold.d.ts +15 -0
- package/dist/timeseries/algorithms/threshold.js +49 -0
- package/dist/timeseries/handler.d.ts +10 -0
- package/dist/timeseries/handler.js +292 -0
- package/dist/timeseries/index.d.ts +68 -0
- package/dist/timeseries/index.js +26 -0
- package/dist/timeseries/tool.d.ts +71 -0
- package/dist/timeseries/tool.js +170 -0
- package/dist/timeseries/types.d.ts +147 -0
- package/dist/timeseries/types.js +4 -0
- package/dist/types/index.d.ts +0 -21
- package/dist/utils/filename.d.ts +0 -8
- package/dist/utils/filename.js +0 -10
- package/dist/utils/jq.d.ts +25 -0
- package/dist/utils/jq.js +90 -0
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moving Average based anomaly detection
|
|
3
|
+
*
|
|
4
|
+
* Detects points that deviate significantly from a rolling moving average.
|
|
5
|
+
* Good for detecting sudden changes in time series with trends.
|
|
6
|
+
*/
|
|
7
|
+
import type { DataPoint, Anomaly, MovingAverageParams } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Detect anomalies using Moving Average method
|
|
10
|
+
*
|
|
11
|
+
* @param data - Array of data points (should be in time order)
|
|
12
|
+
* @param params - Moving Average parameters
|
|
13
|
+
* @returns Array of detected anomalies
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectMovingAverageAnomalies(data: DataPoint[], params: MovingAverageParams): Anomaly[];
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moving Average based anomaly detection
|
|
3
|
+
*
|
|
4
|
+
* Detects points that deviate significantly from a rolling moving average.
|
|
5
|
+
* Good for detecting sudden changes in time series with trends.
|
|
6
|
+
*/
|
|
7
|
+
import { movingAverage, movingStandardDeviation } from './stats.js';
|
|
8
|
+
const DEFAULT_THRESHOLD = 2;
|
|
9
|
+
/**
|
|
10
|
+
* Detect anomalies using Moving Average method
|
|
11
|
+
*
|
|
12
|
+
* @param data - Array of data points (should be in time order)
|
|
13
|
+
* @param params - Moving Average parameters
|
|
14
|
+
* @returns Array of detected anomalies
|
|
15
|
+
*/
|
|
16
|
+
export function detectMovingAverageAnomalies(data, params) {
|
|
17
|
+
const { window_size: windowSize, threshold = DEFAULT_THRESHOLD } = params;
|
|
18
|
+
if (windowSize <= 0) {
|
|
19
|
+
throw new Error('window_size must be greater than 0');
|
|
20
|
+
}
|
|
21
|
+
if (windowSize > data.length) {
|
|
22
|
+
throw new Error(`window_size (${windowSize}) cannot be larger than data length (${data.length})`);
|
|
23
|
+
}
|
|
24
|
+
const values = data.map((d) => d.value);
|
|
25
|
+
const ma = movingAverage(values, windowSize);
|
|
26
|
+
const mstd = movingStandardDeviation(values, windowSize);
|
|
27
|
+
const anomalies = [];
|
|
28
|
+
for (let i = 0; i < data.length; i++) {
|
|
29
|
+
// Skip points where we don't have enough data for the window
|
|
30
|
+
if (isNaN(ma[i]) || isNaN(mstd[i])) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const point = data[i];
|
|
34
|
+
const expectedValue = ma[i];
|
|
35
|
+
const stdDev = mstd[i];
|
|
36
|
+
// If standard deviation is 0, all values in window are the same
|
|
37
|
+
if (stdDev === 0) {
|
|
38
|
+
// Only flag as anomaly if current value differs from the constant
|
|
39
|
+
if (point.value !== expectedValue) {
|
|
40
|
+
anomalies.push({
|
|
41
|
+
index: point.index,
|
|
42
|
+
timestamp: point.timestamp,
|
|
43
|
+
value: point.value,
|
|
44
|
+
reason: `Value ${point.value} differs from constant window value ${expectedValue}`,
|
|
45
|
+
metadata: {
|
|
46
|
+
moving_average: expectedValue,
|
|
47
|
+
moving_std_dev: stdDev,
|
|
48
|
+
window_size: windowSize,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const deviation = Math.abs(point.value - expectedValue);
|
|
55
|
+
const deviationScore = deviation / stdDev;
|
|
56
|
+
if (deviationScore > threshold) {
|
|
57
|
+
anomalies.push({
|
|
58
|
+
index: point.index,
|
|
59
|
+
timestamp: point.timestamp,
|
|
60
|
+
value: point.value,
|
|
61
|
+
reason: `Value deviates ${deviationScore.toFixed(2)} std devs from moving average (expected: ${expectedValue.toFixed(2)}, threshold: ${threshold})`,
|
|
62
|
+
metadata: {
|
|
63
|
+
moving_average: expectedValue,
|
|
64
|
+
moving_std_dev: stdDev,
|
|
65
|
+
deviation_score: deviationScore,
|
|
66
|
+
window_size: windowSize,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return anomalies;
|
|
72
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rolling Quantile based anomaly detection (similar to Datadog's Basic algorithm)
|
|
3
|
+
*
|
|
4
|
+
* Uses a sliding window to compute rolling percentiles, then flags points
|
|
5
|
+
* outside the expected range. This is non-parametric and works well for
|
|
6
|
+
* any distribution without assuming normality.
|
|
7
|
+
*/
|
|
8
|
+
import type { DataPoint, Anomaly, RollingQuantileParams } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Detect anomalies using Rolling Quantile method
|
|
11
|
+
*
|
|
12
|
+
* @param data - Array of data points (should be in time order)
|
|
13
|
+
* @param params - Rolling Quantile parameters
|
|
14
|
+
* @returns Array of detected anomalies
|
|
15
|
+
*/
|
|
16
|
+
export declare function detectRollingQuantileAnomalies(data: DataPoint[], params: RollingQuantileParams): Anomaly[];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rolling Quantile based anomaly detection (similar to Datadog's Basic algorithm)
|
|
3
|
+
*
|
|
4
|
+
* Uses a sliding window to compute rolling percentiles, then flags points
|
|
5
|
+
* outside the expected range. This is non-parametric and works well for
|
|
6
|
+
* any distribution without assuming normality.
|
|
7
|
+
*/
|
|
8
|
+
import { rollingQuantiles } from './stats.js';
|
|
9
|
+
const DEFAULT_LOWER_QUANTILE = 0.05;
|
|
10
|
+
const DEFAULT_UPPER_QUANTILE = 0.95;
|
|
11
|
+
/**
|
|
12
|
+
* Detect anomalies using Rolling Quantile method
|
|
13
|
+
*
|
|
14
|
+
* @param data - Array of data points (should be in time order)
|
|
15
|
+
* @param params - Rolling Quantile parameters
|
|
16
|
+
* @returns Array of detected anomalies
|
|
17
|
+
*/
|
|
18
|
+
export function detectRollingQuantileAnomalies(data, params) {
|
|
19
|
+
const { window_size: windowSize, lower_quantile: lowerQuantile = DEFAULT_LOWER_QUANTILE, upper_quantile: upperQuantile = DEFAULT_UPPER_QUANTILE, } = params;
|
|
20
|
+
if (windowSize <= 0) {
|
|
21
|
+
throw new Error('window_size must be greater than 0');
|
|
22
|
+
}
|
|
23
|
+
if (windowSize > data.length) {
|
|
24
|
+
throw new Error(`window_size (${windowSize}) cannot be larger than data length (${data.length})`);
|
|
25
|
+
}
|
|
26
|
+
if (lowerQuantile < 0 || lowerQuantile > 1) {
|
|
27
|
+
throw new Error('lower_quantile must be between 0 and 1');
|
|
28
|
+
}
|
|
29
|
+
if (upperQuantile < 0 || upperQuantile > 1) {
|
|
30
|
+
throw new Error('upper_quantile must be between 0 and 1');
|
|
31
|
+
}
|
|
32
|
+
if (lowerQuantile >= upperQuantile) {
|
|
33
|
+
throw new Error('lower_quantile must be less than upper_quantile');
|
|
34
|
+
}
|
|
35
|
+
const values = data.map((d) => d.value);
|
|
36
|
+
const { lower, upper } = rollingQuantiles(values, windowSize, lowerQuantile, upperQuantile);
|
|
37
|
+
const anomalies = [];
|
|
38
|
+
for (let i = 0; i < data.length; i++) {
|
|
39
|
+
// Skip points where we don't have enough data for the window
|
|
40
|
+
if (isNaN(lower[i]) || isNaN(upper[i])) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const point = data[i];
|
|
44
|
+
const lowerBound = lower[i];
|
|
45
|
+
const upperBound = upper[i];
|
|
46
|
+
if (point.value < lowerBound) {
|
|
47
|
+
anomalies.push({
|
|
48
|
+
index: point.index,
|
|
49
|
+
timestamp: point.timestamp,
|
|
50
|
+
value: point.value,
|
|
51
|
+
reason: `Value ${point.value.toFixed(2)} below rolling ${(lowerQuantile * 100).toFixed(0)}th percentile ${lowerBound.toFixed(2)}`,
|
|
52
|
+
metadata: {
|
|
53
|
+
lower_bound: lowerBound,
|
|
54
|
+
upper_bound: upperBound,
|
|
55
|
+
lower_quantile: lowerQuantile,
|
|
56
|
+
upper_quantile: upperQuantile,
|
|
57
|
+
window_size: windowSize,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else if (point.value > upperBound) {
|
|
62
|
+
anomalies.push({
|
|
63
|
+
index: point.index,
|
|
64
|
+
timestamp: point.timestamp,
|
|
65
|
+
value: point.value,
|
|
66
|
+
reason: `Value ${point.value.toFixed(2)} above rolling ${(upperQuantile * 100).toFixed(0)}th percentile ${upperBound.toFixed(2)}`,
|
|
67
|
+
metadata: {
|
|
68
|
+
lower_bound: lowerBound,
|
|
69
|
+
upper_bound: upperBound,
|
|
70
|
+
lower_quantile: lowerQuantile,
|
|
71
|
+
upper_quantile: upperQuantile,
|
|
72
|
+
window_size: windowSize,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return anomalies;
|
|
78
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistical utility functions for anomaly detection
|
|
3
|
+
* Implemented in pure TypeScript for zero dependencies and fast execution
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Calculate the mean (average) of an array of numbers
|
|
7
|
+
*/
|
|
8
|
+
export declare function mean(values: number[]): number;
|
|
9
|
+
/**
|
|
10
|
+
* Calculate the median of an array of numbers
|
|
11
|
+
*/
|
|
12
|
+
export declare function median(values: number[]): number;
|
|
13
|
+
/**
|
|
14
|
+
* Calculate the standard deviation of an array of numbers
|
|
15
|
+
*/
|
|
16
|
+
export declare function standardDeviation(values: number[]): number;
|
|
17
|
+
/**
|
|
18
|
+
* Calculate the Median Absolute Deviation (MAD)
|
|
19
|
+
* MAD = median(|Xi - median(X)|)
|
|
20
|
+
*/
|
|
21
|
+
export declare function medianAbsoluteDeviation(values: number[]): number;
|
|
22
|
+
/**
|
|
23
|
+
* Calculate modified Z-score using MAD
|
|
24
|
+
* Modified Z-score = 0.6745 * (Xi - median) / MAD
|
|
25
|
+
* The constant 0.6745 makes it comparable to standard Z-scores for normal distributions
|
|
26
|
+
*/
|
|
27
|
+
export declare function modifiedZScore(value: number, med: number, mad: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* Calculate a simple moving average for an array at each position
|
|
30
|
+
* Returns an array of the same length, with NaN for positions where window is incomplete
|
|
31
|
+
*/
|
|
32
|
+
export declare function movingAverage(values: number[], windowSize: number): number[];
|
|
33
|
+
/**
|
|
34
|
+
* Calculate the standard deviation of a moving window at each position
|
|
35
|
+
*/
|
|
36
|
+
export declare function movingStandardDeviation(values: number[], windowSize: number): number[];
|
|
37
|
+
/**
|
|
38
|
+
* Calculate a specific quantile (percentile) of an array
|
|
39
|
+
* Uses linear interpolation between closest ranks
|
|
40
|
+
*/
|
|
41
|
+
export declare function quantile(values: number[], q: number): number;
|
|
42
|
+
/**
|
|
43
|
+
* Calculate rolling quantiles (lower and upper bounds) at each position
|
|
44
|
+
* Returns arrays of the same length as input, with NaN for positions where window is incomplete
|
|
45
|
+
*/
|
|
46
|
+
export declare function rollingQuantiles(values: number[], windowSize: number, lowerQuantile: number, upperQuantile: number): {
|
|
47
|
+
lower: number[];
|
|
48
|
+
upper: number[];
|
|
49
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistical utility functions for anomaly detection
|
|
3
|
+
* Implemented in pure TypeScript for zero dependencies and fast execution
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Calculate the mean (average) of an array of numbers
|
|
7
|
+
*/
|
|
8
|
+
export function mean(values) {
|
|
9
|
+
if (values.length === 0)
|
|
10
|
+
return 0;
|
|
11
|
+
return values.reduce((sum, v) => sum + v, 0) / values.length;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Calculate the median of an array of numbers
|
|
15
|
+
*/
|
|
16
|
+
export function median(values) {
|
|
17
|
+
if (values.length === 0)
|
|
18
|
+
return 0;
|
|
19
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
20
|
+
const mid = Math.floor(sorted.length / 2);
|
|
21
|
+
return sorted.length % 2 !== 0
|
|
22
|
+
? sorted[mid]
|
|
23
|
+
: (sorted[mid - 1] + sorted[mid]) / 2;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Calculate the standard deviation of an array of numbers
|
|
27
|
+
*/
|
|
28
|
+
export function standardDeviation(values) {
|
|
29
|
+
if (values.length < 2)
|
|
30
|
+
return 0;
|
|
31
|
+
const avg = mean(values);
|
|
32
|
+
const squaredDiffs = values.map((v) => Math.pow(v - avg, 2));
|
|
33
|
+
return Math.sqrt(mean(squaredDiffs));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Calculate the Median Absolute Deviation (MAD)
|
|
37
|
+
* MAD = median(|Xi - median(X)|)
|
|
38
|
+
*/
|
|
39
|
+
export function medianAbsoluteDeviation(values) {
|
|
40
|
+
if (values.length === 0)
|
|
41
|
+
return 0;
|
|
42
|
+
const med = median(values);
|
|
43
|
+
const absoluteDeviations = values.map((v) => Math.abs(v - med));
|
|
44
|
+
return median(absoluteDeviations);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Calculate modified Z-score using MAD
|
|
48
|
+
* Modified Z-score = 0.6745 * (Xi - median) / MAD
|
|
49
|
+
* The constant 0.6745 makes it comparable to standard Z-scores for normal distributions
|
|
50
|
+
*/
|
|
51
|
+
export function modifiedZScore(value, med, mad) {
|
|
52
|
+
if (mad === 0)
|
|
53
|
+
return 0;
|
|
54
|
+
return (0.6745 * (value - med)) / mad;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Calculate a simple moving average for an array at each position
|
|
58
|
+
* Returns an array of the same length, with NaN for positions where window is incomplete
|
|
59
|
+
*/
|
|
60
|
+
export function movingAverage(values, windowSize) {
|
|
61
|
+
if (windowSize <= 0 || values.length === 0)
|
|
62
|
+
return [];
|
|
63
|
+
const result = [];
|
|
64
|
+
for (let i = 0; i < values.length; i++) {
|
|
65
|
+
if (i < windowSize - 1) {
|
|
66
|
+
// Not enough data points yet
|
|
67
|
+
result.push(NaN);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Calculate average of the window
|
|
71
|
+
const window = values.slice(i - windowSize + 1, i + 1);
|
|
72
|
+
result.push(mean(window));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Calculate the standard deviation of a moving window at each position
|
|
79
|
+
*/
|
|
80
|
+
export function movingStandardDeviation(values, windowSize) {
|
|
81
|
+
if (windowSize <= 0 || values.length === 0)
|
|
82
|
+
return [];
|
|
83
|
+
const result = [];
|
|
84
|
+
for (let i = 0; i < values.length; i++) {
|
|
85
|
+
if (i < windowSize - 1) {
|
|
86
|
+
result.push(NaN);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const window = values.slice(i - windowSize + 1, i + 1);
|
|
90
|
+
result.push(standardDeviation(window));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Calculate a specific quantile (percentile) of an array
|
|
97
|
+
* Uses linear interpolation between closest ranks
|
|
98
|
+
*/
|
|
99
|
+
export function quantile(values, q) {
|
|
100
|
+
if (values.length === 0)
|
|
101
|
+
return 0;
|
|
102
|
+
if (q <= 0)
|
|
103
|
+
return Math.min(...values);
|
|
104
|
+
if (q >= 1)
|
|
105
|
+
return Math.max(...values);
|
|
106
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
107
|
+
const pos = (sorted.length - 1) * q;
|
|
108
|
+
const lower = Math.floor(pos);
|
|
109
|
+
const upper = Math.ceil(pos);
|
|
110
|
+
const weight = pos - lower;
|
|
111
|
+
if (upper >= sorted.length)
|
|
112
|
+
return sorted[sorted.length - 1];
|
|
113
|
+
return sorted[lower] * (1 - weight) + sorted[upper] * weight;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Calculate rolling quantiles (lower and upper bounds) at each position
|
|
117
|
+
* Returns arrays of the same length as input, with NaN for positions where window is incomplete
|
|
118
|
+
*/
|
|
119
|
+
export function rollingQuantiles(values, windowSize, lowerQuantile, upperQuantile) {
|
|
120
|
+
if (windowSize <= 0 || values.length === 0) {
|
|
121
|
+
return { lower: [], upper: [] };
|
|
122
|
+
}
|
|
123
|
+
const lower = [];
|
|
124
|
+
const upper = [];
|
|
125
|
+
for (let i = 0; i < values.length; i++) {
|
|
126
|
+
if (i < windowSize - 1) {
|
|
127
|
+
// Not enough data points yet
|
|
128
|
+
lower.push(NaN);
|
|
129
|
+
upper.push(NaN);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// Calculate quantiles of the window
|
|
133
|
+
const window = values.slice(i - windowSize + 1, i + 1);
|
|
134
|
+
lower.push(quantile(window, lowerQuantile));
|
|
135
|
+
upper.push(quantile(window, upperQuantile));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { lower, upper };
|
|
139
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold-based anomaly detection
|
|
3
|
+
*
|
|
4
|
+
* Detects points that cross one or more named thresholds.
|
|
5
|
+
* Supports multiple thresholds with names and directions (upper/lower).
|
|
6
|
+
*/
|
|
7
|
+
import type { DataPoint, Anomaly, ThresholdParams } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Detect anomalies using threshold method
|
|
10
|
+
*
|
|
11
|
+
* @param data - Array of data points
|
|
12
|
+
* @param params - Threshold parameters with named thresholds
|
|
13
|
+
* @returns Array of detected anomalies
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectThresholdAnomalies(data: DataPoint[], params: ThresholdParams): Anomaly[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold-based anomaly detection
|
|
3
|
+
*
|
|
4
|
+
* Detects points that cross one or more named thresholds.
|
|
5
|
+
* Supports multiple thresholds with names and directions (upper/lower).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Detect anomalies using threshold method
|
|
9
|
+
*
|
|
10
|
+
* @param data - Array of data points
|
|
11
|
+
* @param params - Threshold parameters with named thresholds
|
|
12
|
+
* @returns Array of detected anomalies
|
|
13
|
+
*/
|
|
14
|
+
export function detectThresholdAnomalies(data, params) {
|
|
15
|
+
const { thresholds } = params;
|
|
16
|
+
if (!thresholds || thresholds.length === 0) {
|
|
17
|
+
throw new Error('At least one threshold must be specified');
|
|
18
|
+
}
|
|
19
|
+
const anomalies = [];
|
|
20
|
+
for (const point of data) {
|
|
21
|
+
const crossedThresholds = [];
|
|
22
|
+
for (const threshold of thresholds) {
|
|
23
|
+
if (threshold.direction === 'upper' && point.value > threshold.value) {
|
|
24
|
+
crossedThresholds.push(threshold);
|
|
25
|
+
}
|
|
26
|
+
else if (threshold.direction === 'lower' && point.value < threshold.value) {
|
|
27
|
+
crossedThresholds.push(threshold);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (crossedThresholds.length > 0) {
|
|
31
|
+
const thresholdNames = crossedThresholds.map((t) => t.name);
|
|
32
|
+
const thresholdDetails = crossedThresholds
|
|
33
|
+
.map((t) => `${t.name} (${t.direction}: ${t.value})`)
|
|
34
|
+
.join(', ');
|
|
35
|
+
anomalies.push({
|
|
36
|
+
index: point.index,
|
|
37
|
+
timestamp: point.timestamp,
|
|
38
|
+
value: point.value,
|
|
39
|
+
reason: `Value ${point.value} crossed threshold(s): ${thresholdDetails}`,
|
|
40
|
+
crossed_thresholds: thresholdNames,
|
|
41
|
+
metadata: crossedThresholds.reduce((acc, t) => {
|
|
42
|
+
acc[`threshold_${t.name}`] = t.value;
|
|
43
|
+
return acc;
|
|
44
|
+
}, {}),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return anomalies;
|
|
49
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TimeseriesConfig, DetectionType, DetectionParams } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Main handler for time series anomaly detection
|
|
4
|
+
*/
|
|
5
|
+
export declare function detectTimeseriesAnomalies(config: TimeseriesConfig, filePath: string, jqQuery: string, valueField: string, detectionType: DetectionType, detectionParams?: DetectionParams, timestampField?: string): Promise<{
|
|
6
|
+
content: Array<{
|
|
7
|
+
type: 'text';
|
|
8
|
+
text: string;
|
|
9
|
+
}>;
|
|
10
|
+
}>;
|