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.
Files changed (166) hide show
  1. agentloop/__init__.py +136 -0
  2. agentloop/ablation_protocol.py +229 -0
  3. agentloop/ablations.py +564 -0
  4. agentloop/aggregate_cli.py +88 -0
  5. agentloop/aggregate_stats.py +175 -0
  6. agentloop/aggregates.py +532 -0
  7. agentloop/audit.py +39 -0
  8. agentloop/autoinstrument.py +145 -0
  9. agentloop/batch_experiments.py +295 -0
  10. agentloop/batching.py +95 -0
  11. agentloop/budget_types.py +335 -0
  12. agentloop/budgets.py +539 -0
  13. agentloop/calibration.py +872 -0
  14. agentloop/checkpoint_state.py +306 -0
  15. agentloop/checkpoint_store.py +86 -0
  16. agentloop/ci.py +202 -0
  17. agentloop/cli.py +1314 -0
  18. agentloop/client.py +228 -0
  19. agentloop/completion.py +345 -0
  20. agentloop/completion_types.py +199 -0
  21. agentloop/config.py +50 -0
  22. agentloop/context_controls.py +443 -0
  23. agentloop/context_experiments.py +337 -0
  24. agentloop/context_types.py +216 -0
  25. agentloop/costs.py +667 -0
  26. agentloop/cursor_validation.py +48 -0
  27. agentloop/decorators.py +445 -0
  28. agentloop/demo.py +300 -0
  29. agentloop/doctor.py +341 -0
  30. agentloop/drift.py +361 -0
  31. agentloop/drift_cli.py +147 -0
  32. agentloop/drift_types.py +110 -0
  33. agentloop/drift_windows.py +340 -0
  34. agentloop/entrypoint.py +201 -0
  35. agentloop/estimates.py +133 -0
  36. agentloop/events.py +75 -0
  37. agentloop/experiment_artifacts.py +80 -0
  38. agentloop/experiment_observations.py +169 -0
  39. agentloop/experiment_reports.py +545 -0
  40. agentloop/experiment_types.py +355 -0
  41. agentloop/experiments.py +392 -0
  42. agentloop/exporters.py +117 -0
  43. agentloop/finding_benchmark_types.py +131 -0
  44. agentloop/finding_benchmarks.py +424 -0
  45. agentloop/findings.py +419 -0
  46. agentloop/graph.py +464 -0
  47. agentloop/harness.py +825 -0
  48. agentloop/harness_bench/__init__.py +1 -0
  49. agentloop/harness_bench/cli.py +77 -0
  50. agentloop/harness_bench/drivers.py +96 -0
  51. agentloop/harness_bench/offline.py +507 -0
  52. agentloop/harness_evidence.py +585 -0
  53. agentloop/harness_usage.py +91 -0
  54. agentloop/html_report.py +860 -0
  55. agentloop/integrations/__init__.py +1 -0
  56. agentloop/integrations/crewai.py +144 -0
  57. agentloop/integrations/harbor/__init__.py +1 -0
  58. agentloop/integrations/harbor/atif.py +982 -0
  59. agentloop/integrations/harbor/atif_validation.py +329 -0
  60. agentloop/integrations/harbor/cli.py +207 -0
  61. agentloop/integrations/harbor/manifest.py +86 -0
  62. agentloop/integrations/harbor/trial_evidence.py +196 -0
  63. agentloop/integrations/harbor/trials.py +1109 -0
  64. agentloop/integrations/harbor/verifier.py +118 -0
  65. agentloop/integrations/langgraph.py +150 -0
  66. agentloop/integrations/langgraph_harness.py +466 -0
  67. agentloop/integrations/langgraph_recovery.py +366 -0
  68. agentloop/integrations/omnigent/__init__.py +1 -0
  69. agentloop/integrations/omnigent/cli.py +78 -0
  70. agentloop/integrations/omnigent/evidence.py +93 -0
  71. agentloop/integrations/omnigent/telemetry.py +422 -0
  72. agentloop/integrations/openai.py +415 -0
  73. agentloop/integrations/openai_agents.py +291 -0
  74. agentloop/integrations/sdk.py +82 -0
  75. agentloop/integrations/vercel_ai.py +83 -0
  76. agentloop/interoperability/__init__.py +5 -0
  77. agentloop/interoperability/artifacts.py +112 -0
  78. agentloop/interoperability/cli.py +59 -0
  79. agentloop/interoperability/cohort_evidence.py +175 -0
  80. agentloop/interoperability/cohorts.py +681 -0
  81. agentloop/interoperability/contracts.py +427 -0
  82. agentloop/interoperability/coordination.py +554 -0
  83. agentloop/interoperability/evidence.py +215 -0
  84. agentloop/interoperability/otlp_jsonl.py +775 -0
  85. agentloop/interoperability/otlp_validation.py +496 -0
  86. agentloop/interoperability/registry.py +561 -0
  87. agentloop/interoperability/validation.py +333 -0
  88. agentloop/intervention_service.py +110 -0
  89. agentloop/intervention_store.py +118 -0
  90. agentloop/interventions.py +190 -0
  91. agentloop/issues.py +90 -0
  92. agentloop/judgment_adapters.py +125 -0
  93. agentloop/judgment_benchmark_types.py +98 -0
  94. agentloop/judgment_benchmarks.py +606 -0
  95. agentloop/judgment_types.py +241 -0
  96. agentloop/judgment_views.py +38 -0
  97. agentloop/judgments.py +342 -0
  98. agentloop/loop_guards.py +274 -0
  99. agentloop/loop_types.py +66 -0
  100. agentloop/markdown.py +102 -0
  101. agentloop/metrics.py +342 -0
  102. agentloop/migrations.py +303 -0
  103. agentloop/model_routing.py +470 -0
  104. agentloop/onboarding.py +315 -0
  105. agentloop/operations.py +44 -0
  106. agentloop/optimizer.py +199 -0
  107. agentloop/otel.py +715 -0
  108. agentloop/otel_ids.py +81 -0
  109. agentloop/otel_semantics.py +134 -0
  110. agentloop/parallelism.py +141 -0
  111. agentloop/patches.py +603 -0
  112. agentloop/plan_export.py +126 -0
  113. agentloop/policy_promotion.py +325 -0
  114. agentloop/promotion_evidence.py +304 -0
  115. agentloop/promotion_types.py +209 -0
  116. agentloop/quality.py +446 -0
  117. agentloop/ranking.py +529 -0
  118. agentloop/recovery_evidence.py +96 -0
  119. agentloop/replay.py +753 -0
  120. agentloop/retention.py +375 -0
  121. agentloop/retention_types.py +113 -0
  122. agentloop/reuse_experiments.py +247 -0
  123. agentloop/routing_checks.py +116 -0
  124. agentloop/routing_types.py +241 -0
  125. agentloop/rules.py +396 -0
  126. agentloop/runtime.py +286 -0
  127. agentloop/savings.py +158 -0
  128. agentloop/scheduling_plan.py +99 -0
  129. agentloop/scheduling_types.py +169 -0
  130. agentloop/schema.py +257 -0
  131. agentloop/semantic_rules.py +200 -0
  132. agentloop/semantic_waste.py +345 -0
  133. agentloop/semantic_waste_types.py +88 -0
  134. agentloop/server.py +494 -0
  135. agentloop/store.py +1429 -0
  136. agentloop/structural_experiments.py +431 -0
  137. agentloop/structural_types.py +113 -0
  138. agentloop/structured_quality.py +1020 -0
  139. agentloop/studies.py +629 -0
  140. agentloop/study_statistics.py +58 -0
  141. agentloop/substitution_evidence.py +199 -0
  142. agentloop/substitution_export.py +190 -0
  143. agentloop/substitution_reports.py +407 -0
  144. agentloop/substitution_types.py +284 -0
  145. agentloop/substitutions.py +267 -0
  146. agentloop/timing.py +142 -0
  147. agentloop/tokens.py +188 -0
  148. agentloop/tool_scheduling.py +410 -0
  149. agentloop/trace_sources.py +31 -0
  150. agentloop/tracer.py +494 -0
  151. agentloop/value.py +306 -0
  152. agentloop/version.py +3 -0
  153. agentloop/workflow_types.py +264 -0
  154. agentloop/workflows.py +284 -0
  155. agentloop_profiler-0.1.0.dist-info/METADATA +513 -0
  156. agentloop_profiler-0.1.0.dist-info/RECORD +166 -0
  157. agentloop_profiler-0.1.0.dist-info/WHEEL +5 -0
  158. agentloop_profiler-0.1.0.dist-info/entry_points.txt +2 -0
  159. agentloop_profiler-0.1.0.dist-info/licenses/LICENSE +201 -0
  160. agentloop_profiler-0.1.0.dist-info/licenses/THIRD_PARTY_LICENSES.md +101 -0
  161. agentloop_profiler-0.1.0.dist-info/top_level.txt +2 -0
  162. dashboard/__init__.py +5 -0
  163. dashboard/app.py +922 -0
  164. dashboard/streamlit_compat.py +63 -0
  165. dashboard/trace_upload.py +57 -0
  166. dashboard/value_view.py +105 -0
agentloop/__init__.py ADDED
@@ -0,0 +1,136 @@
1
+ from agentloop.autoinstrument import (
2
+ DetectionResult,
3
+ InstrumentationResult,
4
+ auto_instrument,
5
+ detect_integrations,
6
+ instrument,
7
+ )
8
+ from agentloop.client import AgentLoopClient, AgentLoopClientError
9
+ from agentloop.decorators import trace_model, trace_tool, traceable
10
+ from agentloop.doctor import run_doctor
11
+ from agentloop.findings import build_diagnosis
12
+ from agentloop.judgment_types import (
13
+ JudgeIdentity,
14
+ JudgeUsage,
15
+ JudgmentAnswer,
16
+ JudgmentBackend,
17
+ JudgmentEvidence,
18
+ JudgmentRequest,
19
+ JudgmentSpec,
20
+ JudgmentUncertainty,
21
+ )
22
+ from agentloop.judgments import (
23
+ JudgmentSession,
24
+ LocalCallbackJudge,
25
+ attach_judgment,
26
+ judgment_request,
27
+ read_judgments,
28
+ )
29
+ from agentloop.otel import trace_from_otel, trace_to_otel, traces_from_otel
30
+ from agentloop.patches import RepositoryPathError, build_patch_plan
31
+ from agentloop.replay import ReplayGates, build_replay_report
32
+ from agentloop.retention import RetentionContext, RetentionPolicy, RetentionSession, read_retention
33
+ from agentloop.runtime import (
34
+ CLEAR,
35
+ FinalizationError,
36
+ get_last_error,
37
+ get_runtime_config,
38
+ init,
39
+ reset_runtime,
40
+ )
41
+ from agentloop.semantic_waste import evaluate_semantic_waste, read_semantic_waste
42
+ from agentloop.semantic_waste_types import SemanticInvestigation
43
+ from agentloop.structured_quality import attach_quality_report
44
+ from agentloop.tracer import (
45
+ AgentTrace,
46
+ bind_trace_context,
47
+ current_trace,
48
+ record_model_call,
49
+ record_tool_call,
50
+ trace_agent,
51
+ trace_model_call,
52
+ trace_retry,
53
+ trace_tool_call,
54
+ )
55
+ from agentloop.value import build_value_report
56
+ from agentloop.version import __version__
57
+ from agentloop.workflows import (
58
+ ExecutionTrace,
59
+ StageInfo,
60
+ WorkflowInfo,
61
+ operation_metadata,
62
+ record_operation,
63
+ set_workflow_outcome,
64
+ trace_operation,
65
+ trace_workflow,
66
+ workflow_metadata,
67
+ )
68
+
69
+ __all__ = [
70
+ "SemanticInvestigation",
71
+ "evaluate_semantic_waste",
72
+ "read_semantic_waste",
73
+ "JudgeIdentity",
74
+ "JudgeUsage",
75
+ "JudgmentAnswer",
76
+ "JudgmentBackend",
77
+ "JudgmentEvidence",
78
+ "JudgmentRequest",
79
+ "JudgmentSpec",
80
+ "JudgmentUncertainty",
81
+ "JudgmentSession",
82
+ "LocalCallbackJudge",
83
+ "attach_judgment",
84
+ "judgment_request",
85
+ "read_judgments",
86
+ "AgentLoopClient",
87
+ "AgentLoopClientError",
88
+ "AgentTrace",
89
+ "attach_quality_report",
90
+ "ExecutionTrace",
91
+ "StageInfo",
92
+ "WorkflowInfo",
93
+ "operation_metadata",
94
+ "record_operation",
95
+ "set_workflow_outcome",
96
+ "trace_operation",
97
+ "trace_workflow",
98
+ "workflow_metadata",
99
+ "CLEAR",
100
+ "FinalizationError",
101
+ "DetectionResult",
102
+ "InstrumentationResult",
103
+ "ReplayGates",
104
+ "RetentionContext",
105
+ "RetentionPolicy",
106
+ "RetentionSession",
107
+ "read_retention",
108
+ "RepositoryPathError",
109
+ "auto_instrument",
110
+ "bind_trace_context",
111
+ "detect_integrations",
112
+ "instrument",
113
+ "build_value_report",
114
+ "build_diagnosis",
115
+ "build_patch_plan",
116
+ "build_replay_report",
117
+ "current_trace",
118
+ "get_last_error",
119
+ "get_runtime_config",
120
+ "init",
121
+ "record_model_call",
122
+ "record_tool_call",
123
+ "reset_runtime",
124
+ "run_doctor",
125
+ "trace_agent",
126
+ "trace_from_otel",
127
+ "traces_from_otel",
128
+ "trace_model",
129
+ "trace_model_call",
130
+ "trace_tool",
131
+ "trace_tool_call",
132
+ "trace_retry",
133
+ "traceable",
134
+ "trace_to_otel",
135
+ "__version__",
136
+ ]
@@ -0,0 +1,229 @@
1
+ """Frozen declarations for host-run harness ablations, without executing workloads."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import math
7
+ from dataclasses import dataclass
8
+ from datetime import datetime
9
+ from hashlib import sha256
10
+
11
+ SCHEMA_VERSION = "1.0"
12
+ MODES = frozenset({"uninstrumented", "tracing", "shadow", "enforce"})
13
+ VERSION_KEYS = frozenset(
14
+ {
15
+ "source",
16
+ "model",
17
+ "provider",
18
+ "configuration",
19
+ "scorer",
20
+ "runner",
21
+ "environment",
22
+ "tools",
23
+ "reset",
24
+ }
25
+ )
26
+
27
+
28
+ class AblationValidationError(ValueError):
29
+ """A declaration or observation cannot be interpreted without guessing."""
30
+
31
+
32
+ def canonical(value):
33
+ try:
34
+ return json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False)
35
+ except (TypeError, ValueError, RecursionError):
36
+ raise AblationValidationError(
37
+ "ablation artifacts must contain finite JSON values"
38
+ ) from None
39
+
40
+
41
+ def fields(value, expected, label):
42
+ if not isinstance(value, dict) or set(value) != set(expected):
43
+ raise AblationValidationError(
44
+ f"{label} must contain exactly: {', '.join(sorted(expected))}"
45
+ )
46
+
47
+
48
+ def text_value(value, label):
49
+ if not isinstance(value, str) or not value.strip():
50
+ raise AblationValidationError(f"{label} must be nonempty text")
51
+
52
+
53
+ def timestamp(value):
54
+ try:
55
+ parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
56
+ if parsed.utcoffset() is None:
57
+ raise ValueError
58
+ return parsed
59
+ except (AttributeError, TypeError, ValueError):
60
+ raise AblationValidationError("timestamps must be ISO-8601 with a timezone") from None
61
+
62
+
63
+ def number(value, label, *, maximum=None, integer=False, nullable=False):
64
+ if value is None and nullable:
65
+ return
66
+ valid = type(value) is int if integer else type(value) in {int, float}
67
+ try:
68
+ valid = (
69
+ valid and math.isfinite(value) and value >= 0 and (maximum is None or value <= maximum)
70
+ )
71
+ except (OverflowError, TypeError):
72
+ valid = False
73
+ if not valid:
74
+ raise AblationValidationError(
75
+ f"{label} must be a finite nonnegative {'integer' if integer else 'number'}"
76
+ )
77
+
78
+
79
+ def pair_key(value):
80
+ return value["task_id"], value["repetition"], value["cache_condition"]
81
+
82
+
83
+ def _validate(spec):
84
+ fields(
85
+ spec,
86
+ {
87
+ "schema_version",
88
+ "name",
89
+ "workload_id",
90
+ "permission_ref",
91
+ "frozen_at",
92
+ "synthetic",
93
+ "versions",
94
+ "tasks",
95
+ "conditions",
96
+ "schedule",
97
+ "quality_gate",
98
+ "bootstrap",
99
+ },
100
+ "protocol",
101
+ )
102
+ if spec["schema_version"] != SCHEMA_VERSION or type(spec["synthetic"]) is not bool:
103
+ raise AblationValidationError("protocol requires schema 1.0 and a boolean synthetic marker")
104
+ for key in ("name", "workload_id", "permission_ref"):
105
+ text_value(spec[key], key)
106
+ timestamp(spec["frozen_at"])
107
+ fields(spec["versions"], VERSION_KEYS, "versions")
108
+ for key, value in spec["versions"].items():
109
+ text_value(value, key)
110
+ tasks = spec["tasks"]
111
+ if not isinstance(tasks, dict) or not tasks:
112
+ raise AblationValidationError("tasks must be a nonempty mapping")
113
+ digests = set()
114
+ for task, details in tasks.items():
115
+ text_value(task, "task_id")
116
+ fields(details, {"split", "input_sha256"}, "task")
117
+ if not isinstance(details["split"], str) or details["split"] not in {"pilot", "held_out"}:
118
+ raise AblationValidationError("task split must be pilot or held_out")
119
+ digest = details["input_sha256"]
120
+ if (
121
+ not isinstance(digest, str)
122
+ or len(digest) != 64
123
+ or any(c not in "0123456789abcdef" for c in digest)
124
+ ):
125
+ raise AblationValidationError("task input_sha256 must be a SHA-256 hex digest")
126
+ if digest in digests:
127
+ raise AblationValidationError(
128
+ "repeated task inputs must share one task ID, not inflate the task denominator"
129
+ )
130
+ digests.add(digest)
131
+ if {item["split"] for item in tasks.values()} != {"pilot", "held_out"}:
132
+ raise AblationValidationError("declare separate pilot and held_out tasks")
133
+ conditions = spec["conditions"]
134
+ if not isinstance(conditions, dict) or not conditions:
135
+ raise AblationValidationError("conditions must be a mapping")
136
+ signatures = set()
137
+ for name, condition in conditions.items():
138
+ text_value(name, "condition")
139
+ fields(condition, {"mode", "policies"}, "condition")
140
+ mode, policies = condition["mode"], condition["policies"]
141
+ if not isinstance(mode, str) or mode not in MODES or not isinstance(policies, dict):
142
+ raise AblationValidationError("invalid condition mode or policies")
143
+ if bool(policies) != (mode in {"shadow", "enforce"}):
144
+ raise AblationValidationError("only shadow/enforce conditions require policies")
145
+ for policy, snapshot in policies.items():
146
+ text_value(policy, "policy_id")
147
+ text_value(snapshot, "policy version/configuration reference")
148
+ signature = (mode, canonical(policies))
149
+ if signature in signatures:
150
+ raise AblationValidationError("duplicate mode/policy condition")
151
+ signatures.add(signature)
152
+ if {mode for mode, _ in signatures} != MODES:
153
+ raise AblationValidationError(
154
+ "declare uninstrumented, tracing, shadow and enforce conditions"
155
+ )
156
+ for condition in conditions.values():
157
+ if condition["mode"] in {"shadow", "enforce"}:
158
+ policies = condition["policies"]
159
+ for mode in ("shadow", "enforce"):
160
+ if (mode, canonical(policies)) not in signatures or any(
161
+ (mode, canonical({key: value})) not in signatures
162
+ for key, value in policies.items()
163
+ ):
164
+ raise AblationValidationError(
165
+ "each policy set needs matched shadow/enforce and single-policy conditions"
166
+ )
167
+ schedule = spec["schedule"]
168
+ if not isinstance(schedule, list) or not schedule:
169
+ raise AblationValidationError("schedule must be a nonempty list")
170
+ seen = set()
171
+ for slot in schedule:
172
+ fields(slot, {"task_id", "repetition", "cache_condition", "order"}, "schedule slot")
173
+ for key in ("task_id", "repetition", "cache_condition"):
174
+ text_value(slot[key], key)
175
+ if slot["task_id"] not in tasks or pair_key(slot) in seen:
176
+ raise AblationValidationError(
177
+ "schedule must reference declared tasks with unique pairing keys"
178
+ )
179
+ seen.add(pair_key(slot))
180
+ order = slot["order"]
181
+ if (
182
+ not isinstance(order, list)
183
+ or any(not isinstance(name, str) for name in order)
184
+ or len(order) != len(conditions)
185
+ or set(order) != set(conditions)
186
+ ):
187
+ raise AblationValidationError("every slot must order all conditions exactly once")
188
+ if {slot["task_id"] for slot in schedule} != set(tasks):
189
+ raise AblationValidationError("every declared task needs a scheduled slot")
190
+ fields(spec["quality_gate"], {"min_score", "max_regression"}, "quality_gate")
191
+ for key, value in spec["quality_gate"].items():
192
+ number(value, key, maximum=1)
193
+ fields(spec["bootstrap"], {"samples", "seed", "confidence"}, "bootstrap")
194
+ settings = spec["bootstrap"]
195
+ number(settings["samples"], "samples", maximum=10000, integer=True)
196
+ if not settings["samples"] or type(settings["seed"]) is not int:
197
+ raise AblationValidationError("bootstrap requires positive samples and an integer seed")
198
+ number(settings["confidence"], "confidence", maximum=1)
199
+ if not 0 < settings["confidence"] < 1:
200
+ raise AblationValidationError("confidence must be strictly between zero and one")
201
+
202
+
203
+ @dataclass(frozen=True)
204
+ class AblationProtocol:
205
+ """A canonical snapshot; its digest identifies declarations, not preregistration proof."""
206
+
207
+ _json: str
208
+
209
+ @classmethod
210
+ def freeze(cls, specification):
211
+ owned = json.loads(canonical(specification))
212
+ _validate(owned)
213
+ digest = sha256(canonical(owned).encode()).hexdigest()
214
+ return cls(canonical({"protocol_hash": digest, "specification": owned}))
215
+
216
+ @classmethod
217
+ def from_dict(cls, payload):
218
+ fields(payload, {"protocol_hash", "specification"}, "frozen protocol")
219
+ result = cls.freeze(payload["specification"])
220
+ if result.protocol_hash != payload["protocol_hash"]:
221
+ raise AblationValidationError("protocol hash does not match the frozen specification")
222
+ return result
223
+
224
+ @property
225
+ def protocol_hash(self):
226
+ return self.to_dict()["protocol_hash"]
227
+
228
+ def to_dict(self):
229
+ return json.loads(self._json)