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
@@ -0,0 +1,215 @@
1
+ """Measurement qualifications for native projections of external evidence.
2
+
3
+ Native event schema requires numeric durations and two statuses. Missing source
4
+ measurements use compatibility placeholders, qualified here before analysis.
5
+ This namespace is not an authenticity guarantee or an executable adapter.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import math
11
+ from collections.abc import Mapping
12
+ from typing import Any
13
+
14
+ from agentloop.interoperability.validation import ImportValidationError, validate_json_tree
15
+
16
+ EXTERNAL_KEY = "agentloop.external"
17
+
18
+
19
+ def read_external(trace: Any) -> dict[str, Any] | None:
20
+ metadata = getattr(trace, "metadata", {})
21
+ metadata = metadata if isinstance(metadata, Mapping) else {}
22
+ if "agentloop.harbor_trial" in metadata:
23
+ from agentloop.integrations.harbor.trial_evidence import read_trial
24
+
25
+ trial = read_trial(trace)
26
+ measurement = trial["measurement"]
27
+ return {
28
+ "schema_version": "1.0",
29
+ "source": {"system": "harbor", "format": "harbor_trial"},
30
+ "receipt_id": trial["receipt_id"],
31
+ "runtime_ms": measurement["runtime_ms"],
32
+ "event_timing_complete": measurement["runtime_ms"] is not None,
33
+ "execution_status": trial["outcome"]["execution_status"],
34
+ "usage_complete": measurement["input_tokens"] is not None
35
+ and measurement["output_tokens"] is not None,
36
+ "reported_model_call_count": None,
37
+ "comparison_eligible": True,
38
+ }
39
+ if EXTERNAL_KEY not in metadata:
40
+ if metadata.get("external_evidence_schema") == "1.0" or any(
41
+ isinstance(event.metadata, Mapping)
42
+ and event.metadata.get("external_evidence_schema") == "1.0"
43
+ for event in trace.events
44
+ ):
45
+ raise ImportValidationError(
46
+ "invalid_external_evidence", EXTERNAL_KEY, "source event qualification is missing"
47
+ )
48
+ return None
49
+ value = metadata[EXTERNAL_KEY]
50
+ validate_json_tree(value)
51
+ if not isinstance(value, dict) or set(value) != {
52
+ "schema_version",
53
+ "source",
54
+ "receipt_id",
55
+ "runtime_ms",
56
+ "event_timing_complete",
57
+ "execution_status",
58
+ "usage_complete",
59
+ "reported_model_call_count",
60
+ "comparison_eligible",
61
+ }:
62
+ raise ImportValidationError(
63
+ "invalid_external_evidence", EXTERNAL_KEY, "invalid measurement qualification"
64
+ )
65
+ if value["schema_version"] != "1.0" or not isinstance(value["source"], dict):
66
+ raise ImportValidationError(
67
+ "invalid_external_evidence", EXTERNAL_KEY, "unsupported qualification schema"
68
+ )
69
+ if not isinstance(value["receipt_id"], str) or not value["receipt_id"].startswith("receipt_"):
70
+ raise ImportValidationError(
71
+ "invalid_external_evidence", EXTERNAL_KEY, "missing source receipt reference"
72
+ )
73
+ for key in ("event_timing_complete", "usage_complete", "comparison_eligible"):
74
+ if type(value[key]) is not bool:
75
+ raise ImportValidationError(
76
+ "invalid_external_evidence", EXTERNAL_KEY, "qualification flags must be boolean"
77
+ )
78
+ runtime = value["runtime_ms"]
79
+ if runtime is not None and (
80
+ type(runtime) not in {int, float} or not math.isfinite(runtime) or runtime < 0
81
+ ):
82
+ raise ImportValidationError(
83
+ "invalid_external_evidence",
84
+ EXTERNAL_KEY,
85
+ "runtime must be finite and nonnegative or null",
86
+ )
87
+ count = value["reported_model_call_count"]
88
+ if count is not None and (type(count) is not int or count < 0):
89
+ raise ImportValidationError(
90
+ "invalid_external_evidence", EXTERNAL_KEY, "invalid reported call count"
91
+ )
92
+ if not isinstance(value["execution_status"], str) or value["execution_status"] not in {
93
+ "completed",
94
+ "failed",
95
+ "timed_out",
96
+ "cancelled",
97
+ "partial",
98
+ "missing",
99
+ "unknown",
100
+ }:
101
+ raise ImportValidationError(
102
+ "invalid_external_evidence", EXTERNAL_KEY, "invalid execution status"
103
+ )
104
+ if value["event_timing_complete"] and any(
105
+ event.metadata.get("timing_available") is not True for event in trace.events
106
+ ):
107
+ raise ImportValidationError(
108
+ "invalid_external_evidence",
109
+ EXTERNAL_KEY,
110
+ "event timing qualification contradicts events",
111
+ )
112
+ return value
113
+
114
+
115
+ def require_external_comparison(trace: Any, operation: str) -> None:
116
+ if "agentloop.harbor_trial" in getattr(trace, "metadata", {}):
117
+ from agentloop.integrations.harbor.trial_evidence import read_trial
118
+
119
+ trial = read_trial(trace)
120
+ # Studies already support missing per-metric values. A captured trial
121
+ # record is complete evidence even when a measurement is unavailable.
122
+ if operation == "study":
123
+ return
124
+ if trial["measurement"]["runtime_ms"] is None:
125
+ raise ValueError(
126
+ f"{operation} requires an externally recorded agent execution interval"
127
+ )
128
+ if operation == "value estimates":
129
+ raise ValueError(
130
+ "value estimates require finding-level timing; a trial phase is not a model/tool profile"
131
+ )
132
+ return
133
+ evidence = read_external(trace)
134
+ if evidence is not None and (
135
+ not evidence["comparison_eligible"]
136
+ or evidence["runtime_ms"] is None
137
+ or evidence["execution_status"] == "unknown"
138
+ or not evidence["event_timing_complete"]
139
+ or not evidence["usage_complete"]
140
+ ):
141
+ raise ValueError(
142
+ f"{operation} requires complete external trial measurements and independent quality evidence"
143
+ )
144
+
145
+
146
+ def qualify_report(trace: Any, report: dict[str, Any]) -> None:
147
+ evidence = read_external(trace)
148
+ if evidence is None:
149
+ return
150
+ report["external_evidence"] = evidence
151
+ report["total_runtime_ms"] = evidence["runtime_ms"]
152
+ report["timing_status"] = "complete" if evidence["event_timing_complete"] else "unavailable"
153
+ report["reported_model_call_count"] = evidence["reported_model_call_count"]
154
+ report["model_call_count_basis"] = "recorded blocks; multiplicity is external reported"
155
+ report["repeated_context_tokens"] = report["repeated_context_ratio"] = None
156
+ report["retry_count"] = None
157
+ report["rule_abstentions"] = [
158
+ {
159
+ "rule_id": "cache_context",
160
+ "reason": "ATIF does not provide the actual provider input context"
161
+ if evidence["source"].get("format") == "atif"
162
+ else "Imported telemetry does not establish the complete provider input context",
163
+ },
164
+ ]
165
+ models = [event for event in trace.events if event.event_type == "model_call"]
166
+ for direction in ("input", "output"):
167
+ key = direction + "_tokens"
168
+ report["known_" + key] = report[key]
169
+ if any(event.metadata.get(key + "_available") is not True for event in models) or (
170
+ not models and not evidence["usage_complete"]
171
+ ):
172
+ report[key] = None
173
+ for item in report["events"]:
174
+ if item["metadata"].get("timing_available") is False:
175
+ item["duration_ms"] = None
176
+ if item["metadata"].get("source_status_available") is False:
177
+ item["status"] = "unknown"
178
+ if item["event_type"] == "model_call":
179
+ for key in ("input_tokens", "output_tokens"):
180
+ if item["metadata"].get(key + "_available") is False:
181
+ item[key] = None
182
+ if not evidence["event_timing_complete"]:
183
+ report["parallelism_opportunities"] = []
184
+ for key in ("cumulative_span_time_ms", "model_time_ms", "tool_time_ms", "retry_time_ms"):
185
+ report[key] = None
186
+ report["token_status"] = "external_reported" if evidence["usage_complete"] else "partial"
187
+ # Retained source cost lives in receipts. It is not provider accounting or a
188
+ # rate calculated from independently verified provider usage.
189
+ report["cost_status"] = "unknown"
190
+ report["estimated_cost_usd"] = None
191
+
192
+
193
+ def qualify_candidates(report: dict[str, Any], candidates: list[Any]) -> None:
194
+ evidence = report.get("external_evidence")
195
+ if evidence is None:
196
+ return
197
+ for candidate in candidates:
198
+ candidate.estimated_cost_savings_usd = None
199
+ if not evidence["event_timing_complete"]:
200
+ candidate.estimated_latency_savings_ms = None
201
+ if candidate.estimate:
202
+ inputs = candidate.estimate.get("inputs", {})
203
+ inputs["sum_duration_ms"] = inputs["max_duration_ms"] = None
204
+ candidate.estimate["unmodeled_metrics"] = ["latency", "cost"]
205
+ candidate.observations = {
206
+ **(candidate.observations or {}),
207
+ "external_source": evidence["source"],
208
+ "source_receipt_id": evidence["receipt_id"],
209
+ "source_execution_status": evidence["execution_status"],
210
+ }
211
+ candidate.evidence_level = "external_reported"
212
+ candidate.assumptions = [
213
+ *(candidate.assumptions or []),
214
+ "Source artifacts are externally reported; execution completeness and task correctness require independent verification.",
215
+ ]