loopgain 0.4.1__tar.gz → 0.4.2__tar.gz
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.
- {loopgain-0.4.1 → loopgain-0.4.2}/PKG-INFO +2 -2
- {loopgain-0.4.1 → loopgain-0.4.2}/README.md +1 -1
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/_version.py +1 -1
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/classifier.py +8 -3
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/telemetry.py +5 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain.egg-info/PKG-INFO +2 -2
- {loopgain-0.4.1 → loopgain-0.4.2}/tests/test_classifier_mock_validation.py +7 -5
- {loopgain-0.4.1 → loopgain-0.4.2}/tests/test_classifier_synthetic.py +36 -1
- {loopgain-0.4.1 → loopgain-0.4.2}/LICENSE +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/__init__.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/__main__.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/cli.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/core.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/funnel.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/integrations/__init__.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/integrations/autogen.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/integrations/claude_agent_sdk.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/integrations/crewai.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/integrations/langchain.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/integrations/langgraph.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain/integrations/openai_agents.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain.egg-info/SOURCES.txt +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain.egg-info/dependency_links.txt +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain.egg-info/entry_points.txt +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain.egg-info/requires.txt +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/loopgain.egg-info/top_level.txt +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/pyproject.toml +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/setup.cfg +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/tests/test_core.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/tests/test_funnel.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/tests/test_integrations.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/tests/test_stress.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/tests/test_telemetry.py +0 -0
- {loopgain-0.4.1 → loopgain-0.4.2}/tests/test_termination_safety.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loopgain
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: An open-source cost controller for AI agent loops. Stops a loop when it has actually converged and rolls back before it degrades — replacing the max_iterations guess with a real-time loop-gain (Aβ) monitor with five named threshold bands and best-so-far rollback.
|
|
5
5
|
Author-email: Dave Fitzsimmons <hello@loopgain.ai>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -58,7 +58,7 @@ AI agent loops waste time and money when they don't know when to stop. LoopGain
|
|
|
58
58
|
[](https://pypi.org/project/loopgain/)
|
|
59
59
|
[](https://pypi.org/project/loopgain/)
|
|
60
60
|
[](LICENSE)
|
|
61
|
-
[](tests/)
|
|
62
62
|
|
|
63
63
|
**Home:** [loopgain.ai](https://loopgain.ai)
|
|
64
64
|
|
|
@@ -9,7 +9,7 @@ AI agent loops waste time and money when they don't know when to stop. LoopGain
|
|
|
9
9
|
[](https://pypi.org/project/loopgain/)
|
|
10
10
|
[](https://pypi.org/project/loopgain/)
|
|
11
11
|
[](LICENSE)
|
|
12
|
-
[](tests/)
|
|
13
13
|
|
|
14
14
|
**Home:** [loopgain.ai](https://loopgain.ai)
|
|
15
15
|
|
|
@@ -184,9 +184,14 @@ def _two_sided_t_p(t_abs: float, df: int) -> float:
|
|
|
184
184
|
# exact: cdf_t(t,1) = 0.5 + arctan(t)/pi
|
|
185
185
|
return 2.0 * (0.5 - math.atan(t_abs) / math.pi)
|
|
186
186
|
if df == 2:
|
|
187
|
-
#
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
# Exact two-sided p-value for Student-t with df=2. The df=2 CDF is
|
|
188
|
+
# F(t) = 1/2 + t / (2·√(2 + t²)), so the one-sided survival is
|
|
189
|
+
# P(T > t) = 1/2 − t / (2·√(2 + t²)) and the two-sided p is
|
|
190
|
+
# 2·P(T > |t|) = 1 − |t| / √(2 + t²).
|
|
191
|
+
# (The previous implementation returned twice this — it required
|
|
192
|
+
# |t| > 6.21 for p<0.05 instead of the correct |t| > 4.30, making
|
|
193
|
+
# the n=4 classifier far too conservative. See test_classifier.)
|
|
194
|
+
return max(0.0, 1.0 - t_abs / math.sqrt(2.0 + t_abs * t_abs))
|
|
190
195
|
# Wilson-Hilferty: transform t² ~ F(1, df), then F → chi-square via
|
|
191
196
|
# cube-root approximation. For our purposes the simpler normal-approx
|
|
192
197
|
# to the t with the Hill / Abramowitz adjustment is enough.
|
|
@@ -178,6 +178,11 @@ def build_payload(
|
|
|
178
178
|
"savings_vs_fixed_cap": result.savings_vs_fixed_cap,
|
|
179
179
|
"convergence_profile_summary": profile_summary,
|
|
180
180
|
"rollback_triggered": result.outcome in ("oscillating", "diverged"),
|
|
181
|
+
# Index (0-based) of the lowest-error iteration. Lets the receiver
|
|
182
|
+
# derive iterations-to-best (best_index+1) and iterations-past-best
|
|
183
|
+
# (iterations_used-1-best_index) — the "Iteration Waste" view.
|
|
184
|
+
# Privacy-safe: an integer position, no output/error content.
|
|
185
|
+
"best_index": result.best_index,
|
|
181
186
|
# v2: first computable eta snapshot, for ETA calibration dashboard.
|
|
182
187
|
# Predicted total iterations = first_eta_at_iteration +
|
|
183
188
|
# first_eta_prediction; compare to iterations_used to compute the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loopgain
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: An open-source cost controller for AI agent loops. Stops a loop when it has actually converged and rolls back before it degrades — replacing the max_iterations guess with a real-time loop-gain (Aβ) monitor with five named threshold bands and best-so-far rollback.
|
|
5
5
|
Author-email: Dave Fitzsimmons <hello@loopgain.ai>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -58,7 +58,7 @@ AI agent loops waste time and money when they don't know when to stop. LoopGain
|
|
|
58
58
|
[](https://pypi.org/project/loopgain/)
|
|
59
59
|
[](https://pypi.org/project/loopgain/)
|
|
60
60
|
[](LICENSE)
|
|
61
|
-
[](tests/)
|
|
62
62
|
|
|
63
63
|
**Home:** [loopgain.ai](https://loopgain.ai)
|
|
64
64
|
|
|
@@ -223,11 +223,13 @@ def test_loop_length_robustness():
|
|
|
223
223
|
- n=8 (df=6): ≥ 90% (the default real-loop length)
|
|
224
224
|
- n=12 (df=10): ≥ 95%
|
|
225
225
|
"""
|
|
226
|
-
# n=4 is intentionally excluded
|
|
227
|
-
#
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
# min-recommended-iterations limit,
|
|
226
|
+
# n=4 is intentionally excluded from the high-accuracy thresholds below:
|
|
227
|
+
# with df=2 the t-test correctly requires |t|>4.30 for p<0.05 (see
|
|
228
|
+
# test_two_sided_t_p_df2_exact), a fundamental statistical-power floor at
|
|
229
|
+
# this length. The classifier falls back to cumulative E_ratio when the
|
|
230
|
+
# slope test is underpowered. This is a min-recommended-iterations limit,
|
|
231
|
+
# not a bug. (Historically the df=2 p-value was computed at 2x its true
|
|
232
|
+
# value, requiring |t|>6.21 and worsening this floor — now fixed.)
|
|
231
233
|
LEN_THRESHOLDS = {6: 0.80, 8: 0.90, 12: 0.95}
|
|
232
234
|
for n, threshold in LEN_THRESHOLDS.items():
|
|
233
235
|
for gen, expected in [
|
|
@@ -33,7 +33,42 @@ from loopgain import (
|
|
|
33
33
|
classify_trajectory,
|
|
34
34
|
extract_features,
|
|
35
35
|
)
|
|
36
|
-
from loopgain.classifier import _ols_slope_and_p
|
|
36
|
+
from loopgain.classifier import _ols_slope_and_p, _two_sided_t_p
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ----- Two-sided t p-value closed forms -----
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_two_sided_t_p_df1_exact():
|
|
43
|
+
"""df=1 is the Cauchy distribution: two-sided p = 1 - 2·atan(t)/pi."""
|
|
44
|
+
for t in (0.0, 0.5, 1.0, 2.0, 5.0, 12.706):
|
|
45
|
+
expected = 1.0 - 2.0 * math.atan(t) / math.pi
|
|
46
|
+
assert _two_sided_t_p(t, 1) == pytest.approx(expected, abs=1e-9)
|
|
47
|
+
# t=1 is the median of |T| for df=1 → two-sided p = 0.5.
|
|
48
|
+
assert _two_sided_t_p(1.0, 1) == pytest.approx(0.5, abs=1e-9)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_two_sided_t_p_df2_exact():
|
|
52
|
+
"""df=2 closed form: two-sided p = 1 - |t|/sqrt(2 + t^2).
|
|
53
|
+
|
|
54
|
+
Regression guard for the doubled-p bug: the critical value for p=0.05
|
|
55
|
+
at df=2 is t=4.302653. The previous implementation returned ~0.10 here
|
|
56
|
+
(2x too large), which forced |t|>6.21 for significance and made the n=4
|
|
57
|
+
classifier far too conservative.
|
|
58
|
+
"""
|
|
59
|
+
for t in (0.0, 0.5, 1.0, 2.0, 5.0):
|
|
60
|
+
expected = 1.0 - t / math.sqrt(2.0 + t * t)
|
|
61
|
+
assert _two_sided_t_p(t, 2) == pytest.approx(expected, abs=1e-9)
|
|
62
|
+
# The exact 5% two-sided critical value for df=2.
|
|
63
|
+
assert _two_sided_t_p(4.302653, 2) == pytest.approx(0.05, abs=1e-4)
|
|
64
|
+
# p is a probability: monotone non-increasing in t, bounded to [0, 1].
|
|
65
|
+
assert _two_sided_t_p(0.0, 2) == pytest.approx(1.0, abs=1e-9)
|
|
66
|
+
prev = 1.1
|
|
67
|
+
for t in (0.0, 0.5, 1.0, 2.0, 4.0, 8.0, 50.0):
|
|
68
|
+
p = _two_sided_t_p(t, 2)
|
|
69
|
+
assert 0.0 <= p <= 1.0
|
|
70
|
+
assert p <= prev + 1e-12
|
|
71
|
+
prev = p
|
|
37
72
|
|
|
38
73
|
|
|
39
74
|
# ----- OLS slope / p-value building blocks -----
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|