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
agentloop/workflows.py
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""Framework-neutral workflow/stage tracing using the existing trace lifecycle."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
import time
|
|
7
|
+
from asyncio import CancelledError
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
|
|
10
|
+
from agentloop.events import AgentEvent, new_event_id, utc_now_iso
|
|
11
|
+
from agentloop.operations import normalize_operation_kind
|
|
12
|
+
from agentloop.tokens import EXACT_PROVENANCE, validate_provenance
|
|
13
|
+
from agentloop.tracer import (
|
|
14
|
+
AgentTrace,
|
|
15
|
+
_trace_execution,
|
|
16
|
+
bind_trace_context,
|
|
17
|
+
current_event_id,
|
|
18
|
+
current_trace,
|
|
19
|
+
)
|
|
20
|
+
from agentloop.workflow_types import (
|
|
21
|
+
STAGE_KEY,
|
|
22
|
+
WORKFLOW_KEY,
|
|
23
|
+
_set_outcome,
|
|
24
|
+
_text,
|
|
25
|
+
)
|
|
26
|
+
from agentloop.workflow_types import (
|
|
27
|
+
StageInfo as StageInfo,
|
|
28
|
+
)
|
|
29
|
+
from agentloop.workflow_types import (
|
|
30
|
+
WorkflowInfo as WorkflowInfo,
|
|
31
|
+
)
|
|
32
|
+
from agentloop.workflow_types import (
|
|
33
|
+
operation_metadata as operation_metadata,
|
|
34
|
+
)
|
|
35
|
+
from agentloop.workflow_types import (
|
|
36
|
+
set_workflow_outcome as set_workflow_outcome,
|
|
37
|
+
)
|
|
38
|
+
from agentloop.workflow_types import (
|
|
39
|
+
stage_summary as stage_summary,
|
|
40
|
+
)
|
|
41
|
+
from agentloop.workflow_types import (
|
|
42
|
+
workflow_metadata as workflow_metadata,
|
|
43
|
+
)
|
|
44
|
+
from agentloop.workflow_types import (
|
|
45
|
+
workflow_summary as workflow_summary,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
ExecutionTrace = AgentTrace
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _status(error):
|
|
52
|
+
if isinstance(error, CancelledError):
|
|
53
|
+
return "cancelled"
|
|
54
|
+
if isinstance(error, (GeneratorExit, KeyboardInterrupt)):
|
|
55
|
+
return "interrupted"
|
|
56
|
+
return "failed"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@contextmanager
|
|
60
|
+
def trace_workflow(name, *, workflow, task_id=None, example_id=None, metadata=None):
|
|
61
|
+
"""Open the same trace lifecycle/store path used by trace_agent."""
|
|
62
|
+
_text(name, "name")
|
|
63
|
+
owned = workflow_metadata(workflow, task_id=task_id, example_id=example_id, metadata=metadata)
|
|
64
|
+
with _trace_execution(name, owned) as trace:
|
|
65
|
+
info = trace.metadata[WORKFLOW_KEY]
|
|
66
|
+
info["status"] = "running"
|
|
67
|
+
try:
|
|
68
|
+
yield trace
|
|
69
|
+
except BaseException as exc:
|
|
70
|
+
info["status"] = _status(exc)
|
|
71
|
+
raise
|
|
72
|
+
else:
|
|
73
|
+
info["status"] = "completed"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class OperationSpan:
|
|
77
|
+
"""The current span's stable identity and optional payload-free result."""
|
|
78
|
+
|
|
79
|
+
def __init__(self, event_id):
|
|
80
|
+
self._event_id, self._result, self._finished = event_id, {}, False
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def event_id(self):
|
|
84
|
+
return self._event_id
|
|
85
|
+
|
|
86
|
+
def set_outcome(self, outcome, *, output_ref=None):
|
|
87
|
+
if self._finished:
|
|
88
|
+
raise RuntimeError("operation span has already finished")
|
|
89
|
+
_set_outcome(self._result, outcome, output_ref)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _record(
|
|
93
|
+
name,
|
|
94
|
+
kind,
|
|
95
|
+
*,
|
|
96
|
+
duration_ms,
|
|
97
|
+
started_at,
|
|
98
|
+
ended_at,
|
|
99
|
+
metadata,
|
|
100
|
+
model=None,
|
|
101
|
+
input_tokens=None,
|
|
102
|
+
output_tokens=None,
|
|
103
|
+
token_provenance=None,
|
|
104
|
+
status="ok",
|
|
105
|
+
error=None,
|
|
106
|
+
trace=None,
|
|
107
|
+
parent_id=None,
|
|
108
|
+
event_id=None,
|
|
109
|
+
):
|
|
110
|
+
target = trace if trace is not None else current_trace()
|
|
111
|
+
if target is None:
|
|
112
|
+
raise RuntimeError("No active AgentLoop trace")
|
|
113
|
+
parent = (
|
|
114
|
+
parent_id if parent_id is not None else None if trace is not None else current_event_id()
|
|
115
|
+
)
|
|
116
|
+
category, provenance = _usage(kind, input_tokens, output_tokens, token_provenance)
|
|
117
|
+
try:
|
|
118
|
+
valid_duration = (
|
|
119
|
+
type(duration_ms) in {int, float} and math.isfinite(duration_ms) and duration_ms >= 0
|
|
120
|
+
)
|
|
121
|
+
except OverflowError:
|
|
122
|
+
valid_duration = False
|
|
123
|
+
if not valid_duration:
|
|
124
|
+
raise ValueError("duration_ms must be finite and nonnegative")
|
|
125
|
+
_text(name, "name")
|
|
126
|
+
_text(model, "model", optional=True)
|
|
127
|
+
_text(event_id, "event_id", optional=True)
|
|
128
|
+
_text(parent, "parent_id", optional=True)
|
|
129
|
+
event = AgentEvent(
|
|
130
|
+
event_id=event_id or new_event_id(),
|
|
131
|
+
run_id=target.run_id,
|
|
132
|
+
event_type=category,
|
|
133
|
+
name=name,
|
|
134
|
+
started_at=started_at,
|
|
135
|
+
ended_at=ended_at or utc_now_iso(),
|
|
136
|
+
duration_ms=duration_ms,
|
|
137
|
+
parent_id=parent,
|
|
138
|
+
model=model,
|
|
139
|
+
input_tokens=input_tokens or 0,
|
|
140
|
+
output_tokens=output_tokens or 0,
|
|
141
|
+
token_provenance=provenance,
|
|
142
|
+
status=status,
|
|
143
|
+
error=error,
|
|
144
|
+
metadata=metadata,
|
|
145
|
+
)
|
|
146
|
+
# Validate the completed event with the same native schema as imports.
|
|
147
|
+
target.add_event(AgentEvent.from_dict(event.to_dict()))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _usage(kind, input_tokens, output_tokens, token_provenance):
|
|
151
|
+
category = {"model": "model_call", "tool": "tool_call", "retry": "retry"}.get(
|
|
152
|
+
normalize_operation_kind(kind), "operation"
|
|
153
|
+
)
|
|
154
|
+
for value in (input_tokens, output_tokens):
|
|
155
|
+
if value is not None and (type(value) is not int or value < 0):
|
|
156
|
+
raise ValueError("token counts must be nonnegative integers or unknown")
|
|
157
|
+
if category != "model_call":
|
|
158
|
+
if input_tokens is not None or output_tokens is not None or token_provenance is not None:
|
|
159
|
+
raise ValueError(
|
|
160
|
+
"token usage requires kind='model'; a stage can declare a different logical kind"
|
|
161
|
+
)
|
|
162
|
+
return category, None
|
|
163
|
+
known = input_tokens is not None and output_tokens is not None
|
|
164
|
+
provenance = (
|
|
165
|
+
("user_supplied" if known else "unavailable")
|
|
166
|
+
if token_provenance is None
|
|
167
|
+
else token_provenance
|
|
168
|
+
)
|
|
169
|
+
validate_provenance(provenance)
|
|
170
|
+
if not known and provenance in EXACT_PROVENANCE:
|
|
171
|
+
raise ValueError("exact usage requires both input and output token counts")
|
|
172
|
+
return category, provenance
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def record_operation(
|
|
176
|
+
name,
|
|
177
|
+
*,
|
|
178
|
+
kind,
|
|
179
|
+
duration_ms,
|
|
180
|
+
started_at,
|
|
181
|
+
ended_at=None,
|
|
182
|
+
stage=None,
|
|
183
|
+
depends_on=None,
|
|
184
|
+
outcome=None,
|
|
185
|
+
output_ref=None,
|
|
186
|
+
metadata=None,
|
|
187
|
+
model=None,
|
|
188
|
+
input_tokens=None,
|
|
189
|
+
output_tokens=None,
|
|
190
|
+
token_provenance=None,
|
|
191
|
+
status="ok",
|
|
192
|
+
error=None,
|
|
193
|
+
trace=None,
|
|
194
|
+
parent_id=None,
|
|
195
|
+
event_id=None,
|
|
196
|
+
):
|
|
197
|
+
"""Record completed work, including callbacks with an explicitly captured trace."""
|
|
198
|
+
owned = operation_metadata(kind, stage=stage, depends_on=depends_on, metadata=metadata)
|
|
199
|
+
if outcome is not None:
|
|
200
|
+
_set_outcome(owned[STAGE_KEY], outcome, output_ref)
|
|
201
|
+
elif output_ref is not None:
|
|
202
|
+
_text(output_ref, "output_ref")
|
|
203
|
+
owned[STAGE_KEY]["output_ref"] = output_ref
|
|
204
|
+
_record(
|
|
205
|
+
name,
|
|
206
|
+
kind,
|
|
207
|
+
duration_ms=duration_ms,
|
|
208
|
+
started_at=started_at,
|
|
209
|
+
ended_at=ended_at,
|
|
210
|
+
metadata=owned,
|
|
211
|
+
model=model,
|
|
212
|
+
input_tokens=input_tokens,
|
|
213
|
+
output_tokens=output_tokens,
|
|
214
|
+
token_provenance=token_provenance,
|
|
215
|
+
status=status,
|
|
216
|
+
error=error,
|
|
217
|
+
trace=trace,
|
|
218
|
+
parent_id=parent_id,
|
|
219
|
+
event_id=event_id,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@contextmanager
|
|
224
|
+
def trace_operation(
|
|
225
|
+
name,
|
|
226
|
+
*,
|
|
227
|
+
kind,
|
|
228
|
+
stage=None,
|
|
229
|
+
depends_on=None,
|
|
230
|
+
metadata=None,
|
|
231
|
+
model=None,
|
|
232
|
+
input_tokens=None,
|
|
233
|
+
output_tokens=None,
|
|
234
|
+
token_provenance=None,
|
|
235
|
+
capture_error_detail=False,
|
|
236
|
+
):
|
|
237
|
+
"""Measure one operation; ordinary bodies/errors are not captured by default."""
|
|
238
|
+
target = current_trace()
|
|
239
|
+
if target is None:
|
|
240
|
+
raise RuntimeError("No active AgentLoop trace")
|
|
241
|
+
if type(capture_error_detail) is not bool:
|
|
242
|
+
raise ValueError("capture_error_detail must be a boolean")
|
|
243
|
+
_text(name, "name")
|
|
244
|
+
_text(model, "model", optional=True)
|
|
245
|
+
_usage(kind, input_tokens, output_tokens, token_provenance)
|
|
246
|
+
owned = operation_metadata(kind, stage=stage, depends_on=depends_on, metadata=metadata)
|
|
247
|
+
span = OperationSpan(new_event_id())
|
|
248
|
+
parent, started = current_event_id(), utc_now_iso()
|
|
249
|
+
began = time.perf_counter()
|
|
250
|
+
status, detail, original = "ok", None, None
|
|
251
|
+
try:
|
|
252
|
+
with bind_trace_context(target, span.event_id):
|
|
253
|
+
yield span
|
|
254
|
+
except BaseException as exc:
|
|
255
|
+
original, status = exc, "error"
|
|
256
|
+
owned["error_type"] = type(exc).__name__
|
|
257
|
+
owned[STAGE_KEY]["execution_status"] = _status(exc)
|
|
258
|
+
detail = str(exc) if capture_error_detail else None
|
|
259
|
+
raise
|
|
260
|
+
finally:
|
|
261
|
+
span._finished = True
|
|
262
|
+
owned[STAGE_KEY].update(span._result)
|
|
263
|
+
owned[STAGE_KEY].setdefault("execution_status", "completed")
|
|
264
|
+
try:
|
|
265
|
+
_record(
|
|
266
|
+
name,
|
|
267
|
+
kind,
|
|
268
|
+
duration_ms=(time.perf_counter() - began) * 1000,
|
|
269
|
+
started_at=started,
|
|
270
|
+
ended_at=utc_now_iso(),
|
|
271
|
+
metadata=owned,
|
|
272
|
+
model=model,
|
|
273
|
+
input_tokens=input_tokens,
|
|
274
|
+
output_tokens=output_tokens,
|
|
275
|
+
token_provenance=token_provenance,
|
|
276
|
+
status=status,
|
|
277
|
+
error=detail,
|
|
278
|
+
trace=target,
|
|
279
|
+
parent_id=parent,
|
|
280
|
+
event_id=span.event_id,
|
|
281
|
+
)
|
|
282
|
+
except BaseException:
|
|
283
|
+
if original is None:
|
|
284
|
+
raise
|