agentloop-profiler 0.1.0__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.
- agentloop/__init__.py +136 -0
- agentloop/ablation_protocol.py +229 -0
- agentloop/ablations.py +564 -0
- agentloop/aggregate_cli.py +88 -0
- agentloop/aggregate_stats.py +175 -0
- agentloop/aggregates.py +532 -0
- agentloop/audit.py +39 -0
- agentloop/autoinstrument.py +145 -0
- agentloop/batch_experiments.py +295 -0
- agentloop/batching.py +95 -0
- agentloop/budget_types.py +335 -0
- agentloop/budgets.py +539 -0
- agentloop/calibration.py +872 -0
- agentloop/checkpoint_state.py +306 -0
- agentloop/checkpoint_store.py +86 -0
- agentloop/ci.py +202 -0
- agentloop/cli.py +1314 -0
- agentloop/client.py +228 -0
- agentloop/completion.py +345 -0
- agentloop/completion_types.py +199 -0
- agentloop/config.py +50 -0
- agentloop/context_controls.py +443 -0
- agentloop/context_experiments.py +337 -0
- agentloop/context_types.py +216 -0
- agentloop/costs.py +667 -0
- agentloop/cursor_validation.py +48 -0
- agentloop/decorators.py +445 -0
- agentloop/demo.py +300 -0
- agentloop/doctor.py +341 -0
- agentloop/drift.py +361 -0
- agentloop/drift_cli.py +147 -0
- agentloop/drift_types.py +110 -0
- agentloop/drift_windows.py +340 -0
- agentloop/entrypoint.py +201 -0
- agentloop/estimates.py +133 -0
- agentloop/events.py +75 -0
- agentloop/experiment_artifacts.py +80 -0
- agentloop/experiment_observations.py +169 -0
- agentloop/experiment_reports.py +545 -0
- agentloop/experiment_types.py +355 -0
- agentloop/experiments.py +392 -0
- agentloop/exporters.py +117 -0
- agentloop/finding_benchmark_types.py +131 -0
- agentloop/finding_benchmarks.py +424 -0
- agentloop/findings.py +419 -0
- agentloop/graph.py +464 -0
- agentloop/harness.py +825 -0
- agentloop/harness_bench/__init__.py +1 -0
- agentloop/harness_bench/cli.py +77 -0
- agentloop/harness_bench/drivers.py +96 -0
- agentloop/harness_bench/offline.py +507 -0
- agentloop/harness_evidence.py +585 -0
- agentloop/harness_usage.py +91 -0
- agentloop/html_report.py +860 -0
- agentloop/integrations/__init__.py +1 -0
- agentloop/integrations/crewai.py +144 -0
- agentloop/integrations/harbor/__init__.py +1 -0
- agentloop/integrations/harbor/atif.py +982 -0
- agentloop/integrations/harbor/atif_validation.py +329 -0
- agentloop/integrations/harbor/cli.py +207 -0
- agentloop/integrations/harbor/manifest.py +86 -0
- agentloop/integrations/harbor/trial_evidence.py +196 -0
- agentloop/integrations/harbor/trials.py +1109 -0
- agentloop/integrations/harbor/verifier.py +118 -0
- agentloop/integrations/langgraph.py +150 -0
- agentloop/integrations/langgraph_harness.py +466 -0
- agentloop/integrations/langgraph_recovery.py +366 -0
- agentloop/integrations/omnigent/__init__.py +1 -0
- agentloop/integrations/omnigent/cli.py +78 -0
- agentloop/integrations/omnigent/evidence.py +93 -0
- agentloop/integrations/omnigent/telemetry.py +422 -0
- agentloop/integrations/openai.py +415 -0
- agentloop/integrations/openai_agents.py +291 -0
- agentloop/integrations/sdk.py +82 -0
- agentloop/integrations/vercel_ai.py +83 -0
- agentloop/interoperability/__init__.py +5 -0
- agentloop/interoperability/artifacts.py +112 -0
- agentloop/interoperability/cli.py +59 -0
- agentloop/interoperability/cohort_evidence.py +175 -0
- agentloop/interoperability/cohorts.py +681 -0
- agentloop/interoperability/contracts.py +427 -0
- agentloop/interoperability/coordination.py +554 -0
- agentloop/interoperability/evidence.py +215 -0
- agentloop/interoperability/otlp_jsonl.py +775 -0
- agentloop/interoperability/otlp_validation.py +496 -0
- agentloop/interoperability/registry.py +561 -0
- agentloop/interoperability/validation.py +333 -0
- agentloop/intervention_service.py +110 -0
- agentloop/intervention_store.py +118 -0
- agentloop/interventions.py +190 -0
- agentloop/issues.py +90 -0
- agentloop/judgment_adapters.py +125 -0
- agentloop/judgment_benchmark_types.py +98 -0
- agentloop/judgment_benchmarks.py +606 -0
- agentloop/judgment_types.py +241 -0
- agentloop/judgment_views.py +38 -0
- agentloop/judgments.py +342 -0
- agentloop/loop_guards.py +274 -0
- agentloop/loop_types.py +66 -0
- agentloop/markdown.py +102 -0
- agentloop/metrics.py +342 -0
- agentloop/migrations.py +303 -0
- agentloop/model_routing.py +470 -0
- agentloop/onboarding.py +315 -0
- agentloop/operations.py +44 -0
- agentloop/optimizer.py +199 -0
- agentloop/otel.py +715 -0
- agentloop/otel_ids.py +81 -0
- agentloop/otel_semantics.py +134 -0
- agentloop/parallelism.py +141 -0
- agentloop/patches.py +603 -0
- agentloop/plan_export.py +126 -0
- agentloop/policy_promotion.py +325 -0
- agentloop/promotion_evidence.py +304 -0
- agentloop/promotion_types.py +209 -0
- agentloop/quality.py +446 -0
- agentloop/ranking.py +529 -0
- agentloop/recovery_evidence.py +96 -0
- agentloop/replay.py +753 -0
- agentloop/retention.py +375 -0
- agentloop/retention_types.py +113 -0
- agentloop/reuse_experiments.py +247 -0
- agentloop/routing_checks.py +116 -0
- agentloop/routing_types.py +241 -0
- agentloop/rules.py +396 -0
- agentloop/runtime.py +286 -0
- agentloop/savings.py +158 -0
- agentloop/scheduling_plan.py +99 -0
- agentloop/scheduling_types.py +169 -0
- agentloop/schema.py +257 -0
- agentloop/semantic_rules.py +200 -0
- agentloop/semantic_waste.py +345 -0
- agentloop/semantic_waste_types.py +88 -0
- agentloop/server.py +494 -0
- agentloop/store.py +1429 -0
- agentloop/structural_experiments.py +431 -0
- agentloop/structural_types.py +113 -0
- agentloop/structured_quality.py +1020 -0
- agentloop/studies.py +629 -0
- agentloop/study_statistics.py +58 -0
- agentloop/substitution_evidence.py +199 -0
- agentloop/substitution_export.py +190 -0
- agentloop/substitution_reports.py +407 -0
- agentloop/substitution_types.py +284 -0
- agentloop/substitutions.py +267 -0
- agentloop/timing.py +142 -0
- agentloop/tokens.py +188 -0
- agentloop/tool_scheduling.py +410 -0
- agentloop/trace_sources.py +31 -0
- agentloop/tracer.py +494 -0
- agentloop/value.py +306 -0
- agentloop/version.py +3 -0
- agentloop/workflow_types.py +264 -0
- agentloop/workflows.py +284 -0
- agentloop_profiler-0.1.0.dist-info/METADATA +513 -0
- agentloop_profiler-0.1.0.dist-info/RECORD +166 -0
- agentloop_profiler-0.1.0.dist-info/WHEEL +5 -0
- agentloop_profiler-0.1.0.dist-info/entry_points.txt +2 -0
- agentloop_profiler-0.1.0.dist-info/licenses/LICENSE +201 -0
- agentloop_profiler-0.1.0.dist-info/licenses/THIRD_PARTY_LICENSES.md +101 -0
- agentloop_profiler-0.1.0.dist-info/top_level.txt +2 -0
- dashboard/__init__.py +5 -0
- dashboard/app.py +922 -0
- dashboard/streamlit_compat.py +63 -0
- dashboard/trace_upload.py +57 -0
- dashboard/value_view.py +105 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Bounded mergeable descriptive statistics; no inferred performance findings."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import bisect
|
|
6
|
+
import math
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def number(value):
|
|
11
|
+
if type(value) not in {int, float} or value < 0:
|
|
12
|
+
raise ValueError("aggregate values must be finite and nonnegative")
|
|
13
|
+
try:
|
|
14
|
+
if not math.isfinite(value):
|
|
15
|
+
raise ValueError("aggregate values must be finite and nonnegative")
|
|
16
|
+
except OverflowError:
|
|
17
|
+
raise ValueError("aggregate numeric overflow") from None
|
|
18
|
+
return value
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def total(left, right):
|
|
22
|
+
return number(left + right)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class AggregateConfig:
|
|
27
|
+
latency_bounds_ms: tuple[float, ...] = (1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 60000)
|
|
28
|
+
heavy_hitter_capacity: int = 32
|
|
29
|
+
|
|
30
|
+
def __post_init__(self):
|
|
31
|
+
if (
|
|
32
|
+
not isinstance(self.latency_bounds_ms, tuple)
|
|
33
|
+
or not 1 <= len(self.latency_bounds_ms) <= 256
|
|
34
|
+
):
|
|
35
|
+
raise ValueError("latency bounds must be a tuple of 1..256 increasing positive values")
|
|
36
|
+
previous = 0
|
|
37
|
+
for bound in self.latency_bounds_ms:
|
|
38
|
+
if number(bound) <= previous:
|
|
39
|
+
raise ValueError("latency bounds must be strictly increasing and positive")
|
|
40
|
+
previous = bound
|
|
41
|
+
if (
|
|
42
|
+
type(self.heavy_hitter_capacity) is not int
|
|
43
|
+
or not 1 <= self.heavy_hitter_capacity <= 1024
|
|
44
|
+
):
|
|
45
|
+
raise ValueError("heavy_hitter_capacity must be 1..1024")
|
|
46
|
+
|
|
47
|
+
def to_dict(self):
|
|
48
|
+
return {
|
|
49
|
+
"latency_bounds_ms": list(self.latency_bounds_ms),
|
|
50
|
+
"heavy_hitter_capacity": self.heavy_hitter_capacity,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Histogram:
|
|
55
|
+
def __init__(self, bounds):
|
|
56
|
+
self.bounds = tuple(bounds)
|
|
57
|
+
self.counts = [0] * (len(bounds) + 1)
|
|
58
|
+
self.count = 0
|
|
59
|
+
self.sum = 0.0
|
|
60
|
+
self.minimum = self.maximum = None
|
|
61
|
+
|
|
62
|
+
def add(self, value):
|
|
63
|
+
number(value)
|
|
64
|
+
updated = total(self.sum, value)
|
|
65
|
+
self.counts[bisect.bisect_left(self.bounds, value)] += 1
|
|
66
|
+
self.count += 1
|
|
67
|
+
self.sum = updated
|
|
68
|
+
self.minimum = value if self.minimum is None else min(self.minimum, value)
|
|
69
|
+
self.maximum = value if self.maximum is None else max(self.maximum, value)
|
|
70
|
+
|
|
71
|
+
def merge(self, other):
|
|
72
|
+
if self.bounds != other.bounds:
|
|
73
|
+
raise ValueError("incompatible histogram bounds")
|
|
74
|
+
updated = total(self.sum, other.sum)
|
|
75
|
+
self.counts = [a + b for a, b in zip(self.counts, other.counts)]
|
|
76
|
+
self.count += other.count
|
|
77
|
+
self.sum = updated
|
|
78
|
+
if other.count:
|
|
79
|
+
self.minimum = (
|
|
80
|
+
other.minimum if self.minimum is None else min(self.minimum, other.minimum)
|
|
81
|
+
)
|
|
82
|
+
self.maximum = (
|
|
83
|
+
other.maximum if self.maximum is None else max(self.maximum, other.maximum)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def quantile(self, fraction):
|
|
87
|
+
if not self.count:
|
|
88
|
+
return None
|
|
89
|
+
rank = max(1, math.ceil(self.count * fraction))
|
|
90
|
+
cumulative = 0
|
|
91
|
+
for index, count in enumerate(self.counts):
|
|
92
|
+
cumulative += count
|
|
93
|
+
if cumulative >= rank:
|
|
94
|
+
return {
|
|
95
|
+
"lower_ms": max(self.minimum, 0 if index == 0 else self.bounds[index - 1]),
|
|
96
|
+
"upper_ms": min(
|
|
97
|
+
self.maximum,
|
|
98
|
+
self.bounds[index] if index < len(self.bounds) else self.maximum,
|
|
99
|
+
),
|
|
100
|
+
"rank": rank,
|
|
101
|
+
}
|
|
102
|
+
raise ValueError("inconsistent histogram counts")
|
|
103
|
+
|
|
104
|
+
def to_dict(self):
|
|
105
|
+
return {
|
|
106
|
+
"bounds_ms": list(self.bounds),
|
|
107
|
+
"counts": self.counts[:],
|
|
108
|
+
"count": self.count,
|
|
109
|
+
"sum_ms": self.sum,
|
|
110
|
+
"min_ms": self.minimum,
|
|
111
|
+
"max_ms": self.maximum,
|
|
112
|
+
"mean_ms": self.sum / self.count if self.count else None,
|
|
113
|
+
"quantile_intervals": {str(q): self.quantile(q) for q in (0.5, 0.9, 0.95, 0.99)},
|
|
114
|
+
"algorithm": "fixed_upper_inclusive_bins_v1",
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class WeightedCandidates:
|
|
119
|
+
"""At most K candidate keys, each with a lower bound and common additive error.
|
|
120
|
+
|
|
121
|
+
When full, subtract min(incoming weight, smallest counter) from every counter
|
|
122
|
+
and the incoming weight. The accumulated subtraction bounds any key's loss.
|
|
123
|
+
Merge adds both losses and then feeds surviving counters through the same rule.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
def __init__(self, capacity):
|
|
127
|
+
self.capacity = capacity
|
|
128
|
+
self.weights = {}
|
|
129
|
+
self.error = 0.0
|
|
130
|
+
self.total = 0.0
|
|
131
|
+
|
|
132
|
+
def add(self, key, weight):
|
|
133
|
+
number(weight)
|
|
134
|
+
self.total = total(self.total, weight)
|
|
135
|
+
self._add(key, weight)
|
|
136
|
+
|
|
137
|
+
def _add(self, key, weight):
|
|
138
|
+
if not weight:
|
|
139
|
+
return
|
|
140
|
+
if key in self.weights:
|
|
141
|
+
self.weights[key] = total(self.weights[key], weight)
|
|
142
|
+
elif len(self.weights) < self.capacity:
|
|
143
|
+
self.weights[key] = weight
|
|
144
|
+
else:
|
|
145
|
+
decrement = min(weight, min(self.weights.values()))
|
|
146
|
+
self.weights = {
|
|
147
|
+
key: value - decrement for key, value in self.weights.items() if value > decrement
|
|
148
|
+
}
|
|
149
|
+
self.error = total(self.error, decrement)
|
|
150
|
+
if weight > decrement:
|
|
151
|
+
self.weights[key] = weight - decrement
|
|
152
|
+
|
|
153
|
+
def merge(self, other):
|
|
154
|
+
if self.capacity != other.capacity:
|
|
155
|
+
raise ValueError("incompatible candidate capacities")
|
|
156
|
+
self.total = total(self.total, other.total)
|
|
157
|
+
self.error = total(self.error, other.error)
|
|
158
|
+
for key, weight in sorted(other.weights.items()):
|
|
159
|
+
self._add(key, weight)
|
|
160
|
+
|
|
161
|
+
def to_dict(self):
|
|
162
|
+
return {
|
|
163
|
+
"capacity": self.capacity,
|
|
164
|
+
"total_weight": self.total,
|
|
165
|
+
"unlisted_key_upper_bound": self.error,
|
|
166
|
+
"algorithm": "bounded_weighted_decrement_v1",
|
|
167
|
+
"candidates": [
|
|
168
|
+
{
|
|
169
|
+
"key_sha256": key,
|
|
170
|
+
"lower_bound": value,
|
|
171
|
+
"upper_bound": min(self.total, total(value, self.error)),
|
|
172
|
+
}
|
|
173
|
+
for key, value in sorted(self.weights.items(), key=lambda item: (-item[1], item[0]))
|
|
174
|
+
],
|
|
175
|
+
}
|