webpeel 0.21.29 → 0.21.30

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.
@@ -44,18 +44,21 @@ declare class ProviderStatsTracker {
44
44
  private readonly windowSize;
45
45
  private readonly failThreshold;
46
46
  private readonly minSamples;
47
- constructor(windowSize?: number, failThreshold?: number, minSamples?: number);
47
+ private readonly decayMs;
48
+ constructor(windowSize?: number, failThreshold?: number, minSamples?: number, decayMs?: number);
48
49
  /** Record the outcome of a single attempt for the given source. */
49
50
  record(sourceId: string, success: boolean): void;
50
51
  /**
51
52
  * Returns the failure rate (0–1) for the given source based on
52
53
  * the sliding window of recorded attempts. Returns 0 if fewer
53
- * than minSamples have been recorded.
54
+ * than minSamples have been recorded, or if all samples are older
55
+ * than decayMs (failures expire so cold-start blips don't permanently
56
+ * lock out a provider).
54
57
  */
55
58
  getFailureRate(sourceId: string): number;
56
59
  /**
57
60
  * Returns true when the source should be skipped (failure rate >=
58
- * failThreshold with at least minSamples recorded).
61
+ * failThreshold with at least minSamples recent recorded).
59
62
  */
60
63
  shouldSkip(sourceId: string): boolean;
61
64
  /** Debug snapshot for a source. */
@@ -114,15 +114,17 @@ class ProviderStatsTracker {
114
114
  windowSize;
115
115
  failThreshold;
116
116
  minSamples;
117
- constructor(windowSize = 10, failThreshold = 0.8, minSamples = 3) {
117
+ decayMs; // failures older than this are ignored
118
+ constructor(windowSize = 10, failThreshold = 0.8, minSamples = 5, decayMs = 5 * 60 * 1000) {
118
119
  this.windowSize = windowSize;
119
120
  this.failThreshold = failThreshold;
120
121
  this.minSamples = minSamples;
122
+ this.decayMs = decayMs; // default 5 minutes: old failures don't permanently lock a provider
121
123
  }
122
124
  /** Record the outcome of a single attempt for the given source. */
123
125
  record(sourceId, success) {
124
126
  const arr = this.history.get(sourceId) ?? [];
125
- arr.push({ success });
127
+ arr.push({ success, ts: Date.now() });
126
128
  if (arr.length > this.windowSize)
127
129
  arr.splice(0, arr.length - this.windowSize);
128
130
  this.history.set(sourceId, arr);
@@ -130,18 +132,24 @@ class ProviderStatsTracker {
130
132
  /**
131
133
  * Returns the failure rate (0–1) for the given source based on
132
134
  * the sliding window of recorded attempts. Returns 0 if fewer
133
- * than minSamples have been recorded.
135
+ * than minSamples have been recorded, or if all samples are older
136
+ * than decayMs (failures expire so cold-start blips don't permanently
137
+ * lock out a provider).
134
138
  */
135
139
  getFailureRate(sourceId) {
136
140
  const arr = this.history.get(sourceId);
137
141
  if (!arr || arr.length < this.minSamples)
138
142
  return 0;
139
- const failures = arr.filter(a => !a.success).length;
140
- return failures / arr.length;
143
+ const cutoff = Date.now() - this.decayMs;
144
+ const recent = arr.filter(a => a.ts >= cutoff);
145
+ if (recent.length < this.minSamples)
146
+ return 0; // not enough recent samples
147
+ const failures = recent.filter(a => !a.success).length;
148
+ return failures / recent.length;
141
149
  }
142
150
  /**
143
151
  * Returns true when the source should be skipped (failure rate >=
144
- * failThreshold with at least minSamples recorded).
152
+ * failThreshold with at least minSamples recent recorded).
145
153
  */
146
154
  shouldSkip(sourceId) {
147
155
  return this.getFailureRate(sourceId) >= this.failThreshold;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpeel",
3
- "version": "0.21.29",
3
+ "version": "0.21.30",
4
4
  "description": "Fast web fetcher for AI agents - stealth mode, crawl mode, page actions, structured extraction, PDF parsing, smart escalation from simple HTTP to headless browser",
5
5
  "author": "Jake Liu",
6
6
  "license": "AGPL-3.0-only",