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,422 @@
|
|
|
1
|
+
"""Omnigent source aliases, session correlation and qualified policy evidence."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from collections import Counter
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from hashlib import sha256
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from agentloop.integrations.omnigent.evidence import attach_observations
|
|
13
|
+
from agentloop.interoperability.artifacts import native_bytes, write_artifact, write_import_bundle
|
|
14
|
+
from agentloop.interoperability.contracts import ImportReceipt, external_id
|
|
15
|
+
from agentloop.interoperability.evidence import EXTERNAL_KEY
|
|
16
|
+
from agentloop.interoperability.otlp_jsonl import OtlpOptions, import_otlp
|
|
17
|
+
from agentloop.interoperability.validation import (
|
|
18
|
+
ImportLimits,
|
|
19
|
+
ImportValidationError,
|
|
20
|
+
load_json_artifact,
|
|
21
|
+
)
|
|
22
|
+
from agentloop.interventions import canonical_json
|
|
23
|
+
from agentloop.tracer import AgentTrace
|
|
24
|
+
|
|
25
|
+
CONTRACT_REVISION = "a2956be0e97bb175a60b274053d836f07d494c6c"
|
|
26
|
+
_ALIASES = {
|
|
27
|
+
"agent_name": ("gen_ai.agent.name", "agent.name"),
|
|
28
|
+
"agent_id": ("omnigent.agent.id", "agent.id"),
|
|
29
|
+
"agent_role": ("omnigent.agent.role", "agent.role"),
|
|
30
|
+
"parent_agent_id": ("omnigent.parent_agent.id", "parent.agent.id"),
|
|
31
|
+
"parent_agent_name": ("parent.agent.name",),
|
|
32
|
+
"session_id": ("session.id", "gen_ai.conversation.id"),
|
|
33
|
+
"parent_session_id": ("omnigent.parent_session.id", "parent.session.id"),
|
|
34
|
+
"request_id": ("omnigent.request.id", "request.id"),
|
|
35
|
+
"turn_id": ("omnigent.turn.id", "turn.id"),
|
|
36
|
+
"harness": ("omnigent.harness", "harness.name", "harness.type", "runtime.harness"),
|
|
37
|
+
"harness_version": ("omnigent.harness.version", "harness.version", "runtime.version"),
|
|
38
|
+
"transport": ("omnigent.integration_mode",),
|
|
39
|
+
"skill_name": ("omnigent.skill.name",),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _label(value: Any) -> str | None:
|
|
44
|
+
return (
|
|
45
|
+
value if isinstance(value, str) and value.strip() and len(value.encode()) <= 512 else None
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _attributes(event: Any) -> dict:
|
|
50
|
+
return {**event.metadata.get("otel_resource_attributes", {}), **event.metadata}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _aliases(event: Any) -> dict:
|
|
54
|
+
attrs = _attributes(event)
|
|
55
|
+
result = {}
|
|
56
|
+
for key, names in _ALIASES.items():
|
|
57
|
+
labels = {value for name in names if (value := _label(attrs.get(name))) is not None}
|
|
58
|
+
result[key] = next(iter(labels)) if len(labels) == 1 else None
|
|
59
|
+
return result
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _timings(events: list[Any], *, conflicting: bool = False) -> dict:
|
|
63
|
+
result = {}
|
|
64
|
+
for role in ("agent", "model", "tool", "guardrail", "approval", "host", "unknown"):
|
|
65
|
+
selected = [event for event in events if _role(event) == role]
|
|
66
|
+
intervals = sorted(
|
|
67
|
+
(
|
|
68
|
+
int(event.metadata["source_interval"]["start_unix_nano"]),
|
|
69
|
+
int(event.metadata["source_interval"]["end_unix_nano"]),
|
|
70
|
+
)
|
|
71
|
+
for event in selected
|
|
72
|
+
if event.metadata.get("timing_available") is True
|
|
73
|
+
)
|
|
74
|
+
union = 0
|
|
75
|
+
end = None
|
|
76
|
+
for a, b in intervals:
|
|
77
|
+
union += b - a if end is None or a >= end else max(0, b - end)
|
|
78
|
+
end = b if end is None else max(end, b)
|
|
79
|
+
complete = bool(selected) and len(intervals) == len(selected) and not conflicting
|
|
80
|
+
result[role] = {
|
|
81
|
+
"spans": len(selected),
|
|
82
|
+
"timed_spans": len(intervals),
|
|
83
|
+
"interval_union_ms": union / 1_000_000 if complete else None,
|
|
84
|
+
"known_interval_union_ms": union / 1_000_000 if intervals else None,
|
|
85
|
+
"span_cumulative_ms": sum(b - a for a, b in intervals) / 1_000_000
|
|
86
|
+
if complete
|
|
87
|
+
else None,
|
|
88
|
+
"coverage": "complete_for_recorded_spans"
|
|
89
|
+
if complete
|
|
90
|
+
else "partial"
|
|
91
|
+
if intervals
|
|
92
|
+
else "unknown",
|
|
93
|
+
}
|
|
94
|
+
return result
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _role(event: Any) -> str:
|
|
98
|
+
attrs = _attributes(event)
|
|
99
|
+
declared = attrs.get("omnigent.span.role")
|
|
100
|
+
if isinstance(declared, str) and declared in {"approval", "host"}:
|
|
101
|
+
return declared
|
|
102
|
+
return (
|
|
103
|
+
event.operation_kind
|
|
104
|
+
if event.operation_kind in {"agent", "model", "tool", "guardrail"}
|
|
105
|
+
else "unknown"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _policy(event: Any, tools: list[Any]) -> dict:
|
|
110
|
+
attrs = _attributes(event)
|
|
111
|
+
action = _label(attrs.get("policy.action"))
|
|
112
|
+
canonical = (
|
|
113
|
+
action.upper()
|
|
114
|
+
if action is not None and action.upper() in {"ALLOW", "DENY", "ASK"}
|
|
115
|
+
else "UNKNOWN"
|
|
116
|
+
)
|
|
117
|
+
call_id = _label(attrs.get("policy.tool_call_id")) or _label(attrs.get("tool_call_id"))
|
|
118
|
+
matched = [
|
|
119
|
+
tool
|
|
120
|
+
for tool in tools
|
|
121
|
+
if call_id is not None and _label(_attributes(tool).get("tool_call_id")) == call_id
|
|
122
|
+
]
|
|
123
|
+
phase = _label(attrs.get("policy.phase"))
|
|
124
|
+
return {
|
|
125
|
+
"event_id": event.event_id,
|
|
126
|
+
"source_span_id": event.metadata.get("source_span_id"),
|
|
127
|
+
"name": _label(attrs.get("policy.name")),
|
|
128
|
+
"phase": phase,
|
|
129
|
+
"action": canonical,
|
|
130
|
+
"source_action": action,
|
|
131
|
+
"tool_call_id": call_id,
|
|
132
|
+
"observed_tool_event_ids": [tool.event_id for tool in matched],
|
|
133
|
+
"dispatch_observation": "recorded_tool_span" if matched else "unknown",
|
|
134
|
+
"dispatch_prevention": "unknown",
|
|
135
|
+
"enforcement": "unverified",
|
|
136
|
+
"source_enforcement_claim": attrs.get("policy.enforced")
|
|
137
|
+
if type(attrs.get("policy.enforced")) is bool
|
|
138
|
+
else None,
|
|
139
|
+
"approval_id": _label(attrs.get("approval.id")),
|
|
140
|
+
"approval_resolution": _label(attrs.get("approval.resolution")),
|
|
141
|
+
"latency_ms": event.duration_ms if event.metadata.get("timing_available") is True else None,
|
|
142
|
+
"basis": "external_reported_policy_decision",
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass(frozen=True)
|
|
147
|
+
class OmnigentImportResult:
|
|
148
|
+
traces: tuple[AgentTrace, ...]
|
|
149
|
+
source_receipts: tuple[ImportReceipt, ...]
|
|
150
|
+
_inventory_json: str
|
|
151
|
+
_sessions_json: str
|
|
152
|
+
|
|
153
|
+
def inventory(self) -> dict:
|
|
154
|
+
return json.loads(self._inventory_json)
|
|
155
|
+
|
|
156
|
+
def sessions(self) -> dict:
|
|
157
|
+
return json.loads(self._sessions_json)
|
|
158
|
+
|
|
159
|
+
def write(self, out: str | Path) -> Path:
|
|
160
|
+
inventory = write_import_bundle(self.traces, self.source_receipts, out, self.inventory())
|
|
161
|
+
write_artifact(
|
|
162
|
+
inventory.parent, "omnigent-sessions.json", (self._sessions_json + "\n").encode()
|
|
163
|
+
)
|
|
164
|
+
from agentloop.interoperability.coordination import write_coordination_report
|
|
165
|
+
|
|
166
|
+
write_coordination_report(self.traces, self.source_receipts, inventory.parent)
|
|
167
|
+
return inventory
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def import_omnigent(
|
|
171
|
+
path: str | Path,
|
|
172
|
+
*,
|
|
173
|
+
options: OtlpOptions | None = None,
|
|
174
|
+
limits: ImportLimits = ImportLimits(),
|
|
175
|
+
jsonl: bool | None = None,
|
|
176
|
+
) -> OmnigentImportResult:
|
|
177
|
+
"""Read source files only; no runtime, vendor tool or policy executes."""
|
|
178
|
+
options = options or OtlpOptions(system="omnigent")
|
|
179
|
+
if options.system != "omnigent":
|
|
180
|
+
raise ImportValidationError(
|
|
181
|
+
"invalid_source", "system", "Omnigent adaptation requires the omnigent source system"
|
|
182
|
+
)
|
|
183
|
+
imported = import_otlp(path, options=options, limits=limits, jsonl=jsonl)
|
|
184
|
+
receipt_by_run = {
|
|
185
|
+
item["run_id"]: receipt.to_dict()
|
|
186
|
+
for receipt in imported.source_receipts
|
|
187
|
+
for item in receipt.to_dict()["traces"]
|
|
188
|
+
}
|
|
189
|
+
span_index: dict[tuple[str, str], list[dict]] = {}
|
|
190
|
+
for trace in imported.traces:
|
|
191
|
+
for event in trace.events:
|
|
192
|
+
if (
|
|
193
|
+
event.metadata.get("source_trace_id") is not None
|
|
194
|
+
and event.metadata.get("source_span_id") is not None
|
|
195
|
+
):
|
|
196
|
+
key = (event.metadata["source_trace_id"], event.metadata["source_span_id"])
|
|
197
|
+
span_index.setdefault(key, []).append(
|
|
198
|
+
{"run_id": trace.run_id, "event_id": event.event_id}
|
|
199
|
+
)
|
|
200
|
+
groups: dict[str, dict] = {}
|
|
201
|
+
frozen = []
|
|
202
|
+
for trace in imported.traces:
|
|
203
|
+
receipt = receipt_by_run[trace.run_id]
|
|
204
|
+
tools = [event for event in trace.events if _role(event) == "tool"]
|
|
205
|
+
observations = []
|
|
206
|
+
event_sessions = {}
|
|
207
|
+
policies = []
|
|
208
|
+
relations = []
|
|
209
|
+
for event in trace.events:
|
|
210
|
+
aliases = _aliases(event)
|
|
211
|
+
ambiguous_aliases = [
|
|
212
|
+
key
|
|
213
|
+
for key, names in _ALIASES.items()
|
|
214
|
+
if len(
|
|
215
|
+
{
|
|
216
|
+
_label(_attributes(event).get(name))
|
|
217
|
+
for name in names
|
|
218
|
+
if _label(_attributes(event).get(name)) is not None
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
> 1
|
|
222
|
+
]
|
|
223
|
+
event.metadata["source.omnigent"] = {
|
|
224
|
+
"schema_version": "1.0",
|
|
225
|
+
**aliases,
|
|
226
|
+
"observation_role": _role(event),
|
|
227
|
+
"ambiguous_aliases": ambiguous_aliases,
|
|
228
|
+
"capture_flags": {
|
|
229
|
+
key: value
|
|
230
|
+
for key, value in _attributes(event).items()
|
|
231
|
+
if key
|
|
232
|
+
in {
|
|
233
|
+
"content_capture_enabled",
|
|
234
|
+
"omnigent.capture_content",
|
|
235
|
+
"omnigent.tracing.content_capture",
|
|
236
|
+
}
|
|
237
|
+
and type(value) is bool
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
observations.append(
|
|
241
|
+
{
|
|
242
|
+
"event_id": event.event_id,
|
|
243
|
+
"source_span_id": event.metadata.get("source_span_id"),
|
|
244
|
+
**aliases,
|
|
245
|
+
"role": _role(event),
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
event_sessions[event.event_id] = aliases["session_id"]
|
|
249
|
+
if _role(event) == "guardrail" or "policy.action" in event.metadata:
|
|
250
|
+
policies.append(_policy(event, tools))
|
|
251
|
+
for link in event.metadata.get("otel_links", []):
|
|
252
|
+
key = (link["traceId"].lower(), link["spanId"].lower())
|
|
253
|
+
targets = span_index.get(key, [])
|
|
254
|
+
relations.append(
|
|
255
|
+
{
|
|
256
|
+
"kind": "span_link",
|
|
257
|
+
"source_event_id": event.event_id,
|
|
258
|
+
"target_trace_id": key[0],
|
|
259
|
+
"target_span_id": key[1],
|
|
260
|
+
"targets": targets,
|
|
261
|
+
"resolved": len(targets) == 1,
|
|
262
|
+
"causal_edge": False,
|
|
263
|
+
"basis": "external_reported_link",
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
parent_reference = _label(event.metadata.get("parent_span_id"))
|
|
267
|
+
if parent_reference is not None:
|
|
268
|
+
relations.append(
|
|
269
|
+
{
|
|
270
|
+
"kind": "ended_parent_reference",
|
|
271
|
+
"source_event_id": event.event_id,
|
|
272
|
+
"target_trace_id": None,
|
|
273
|
+
"target_span_id": parent_reference,
|
|
274
|
+
"targets": [],
|
|
275
|
+
"resolved": False,
|
|
276
|
+
"causal_edge": False,
|
|
277
|
+
"basis": "external_reported_attribute_without_trace_scope",
|
|
278
|
+
}
|
|
279
|
+
)
|
|
280
|
+
model_count = sum(_role(event) == "model" for event in trace.events)
|
|
281
|
+
summary = {
|
|
282
|
+
"schema_version": "1.0",
|
|
283
|
+
"adapter_contract_revision": CONTRACT_REVISION,
|
|
284
|
+
"source_receipt_id": receipt["receipt_id"],
|
|
285
|
+
"source_identity": receipt["external_identity"],
|
|
286
|
+
"observation_count": len(observations),
|
|
287
|
+
"agent_names": sorted(
|
|
288
|
+
{item["agent_name"] for item in observations if item["agent_name"] is not None}
|
|
289
|
+
),
|
|
290
|
+
"policy_decisions": policies,
|
|
291
|
+
"relationships": relations,
|
|
292
|
+
"timing_by_role": _timings(
|
|
293
|
+
trace.events,
|
|
294
|
+
conflicting=receipt["source_metadata"].get("conflicting_segments", 0) > 0,
|
|
295
|
+
),
|
|
296
|
+
"model_coverage": "observed_partial" if model_count else "unknown",
|
|
297
|
+
"task_correctness": "unavailable",
|
|
298
|
+
"enforcement": "unverified",
|
|
299
|
+
"source_execution_outcome": "unknown",
|
|
300
|
+
"gaps": sorted(
|
|
301
|
+
{
|
|
302
|
+
"complete_executor_model_coverage_unestablished",
|
|
303
|
+
"external_task_verifier_absent",
|
|
304
|
+
*(
|
|
305
|
+
["unresolved_policy_ask"]
|
|
306
|
+
if any(
|
|
307
|
+
item["action"] == "ASK" and item["approval_resolution"] is None
|
|
308
|
+
for item in policies
|
|
309
|
+
)
|
|
310
|
+
else []
|
|
311
|
+
),
|
|
312
|
+
*(
|
|
313
|
+
["cross_process_parentage_incomplete"]
|
|
314
|
+
if any(not item["resolved"] for item in relations)
|
|
315
|
+
or any(
|
|
316
|
+
"unresolved_parent_span_id" in event.metadata for event in trace.events
|
|
317
|
+
)
|
|
318
|
+
else []
|
|
319
|
+
),
|
|
320
|
+
}
|
|
321
|
+
),
|
|
322
|
+
}
|
|
323
|
+
# Policy DENY has ERROR status in Omnigent. That is a recorded policy
|
|
324
|
+
# decision, not an independently established failed task execution.
|
|
325
|
+
trace.metadata[EXTERNAL_KEY]["execution_status"] = "unknown"
|
|
326
|
+
attach_observations(trace, summary)
|
|
327
|
+
receipt["outcome"]["execution_status"] = "unknown"
|
|
328
|
+
receipt["source_metadata"]["omnigent"] = {
|
|
329
|
+
key: summary[key]
|
|
330
|
+
for key in (
|
|
331
|
+
"schema_version",
|
|
332
|
+
"adapter_contract_revision",
|
|
333
|
+
"observation_count",
|
|
334
|
+
"model_coverage",
|
|
335
|
+
"task_correctness",
|
|
336
|
+
"enforcement",
|
|
337
|
+
"source_execution_outcome",
|
|
338
|
+
"gaps",
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
for item in receipt["traces"]:
|
|
342
|
+
item["trace_sha256"] = sha256(native_bytes(trace)).hexdigest()
|
|
343
|
+
frozen.append(ImportReceipt.from_dict(receipt, limits=limits))
|
|
344
|
+
session_ids = {
|
|
345
|
+
item["session_id"] for item in observations if item["session_id"] is not None
|
|
346
|
+
}
|
|
347
|
+
for session_id in sorted(session_ids):
|
|
348
|
+
group_id = external_id(
|
|
349
|
+
"group", "omnigent", {**options.identity, "session_id": session_id}
|
|
350
|
+
)
|
|
351
|
+
group = groups.setdefault(
|
|
352
|
+
group_id,
|
|
353
|
+
{
|
|
354
|
+
"group_id": group_id,
|
|
355
|
+
"session_id": session_id,
|
|
356
|
+
"run_ids": [],
|
|
357
|
+
"source_trace_ids": [],
|
|
358
|
+
"relationships": [],
|
|
359
|
+
"parentage": "partial",
|
|
360
|
+
"interpretation": "session grouping preserves separate trace identities and establishes no causal tree",
|
|
361
|
+
},
|
|
362
|
+
)
|
|
363
|
+
group["run_ids"].append(trace.run_id)
|
|
364
|
+
group["source_trace_ids"].append(receipt["external_identity"]["trace_id"])
|
|
365
|
+
group["relationships"].extend(
|
|
366
|
+
item
|
|
367
|
+
for item in relations
|
|
368
|
+
if event_sessions.get(item["source_event_id"]) == session_id
|
|
369
|
+
)
|
|
370
|
+
frozen.extend(
|
|
371
|
+
receipt for receipt in imported.source_receipts if not receipt.to_dict()["traces"]
|
|
372
|
+
)
|
|
373
|
+
actions = Counter(
|
|
374
|
+
item["action"]
|
|
375
|
+
for trace in imported.traces
|
|
376
|
+
for item in trace.metadata["agentloop.omnigent"]["observations"]["policy_decisions"]
|
|
377
|
+
)
|
|
378
|
+
inventory = {
|
|
379
|
+
**imported.inventory(),
|
|
380
|
+
"adapter": "omnigent-otel",
|
|
381
|
+
"adapter_contract_revision": CONTRACT_REVISION,
|
|
382
|
+
"session_groups": len(groups),
|
|
383
|
+
"policy_actions": dict(sorted(actions.items())),
|
|
384
|
+
"enforcement": "unverified",
|
|
385
|
+
"model_coverage": "unknown_or_observed_partial",
|
|
386
|
+
"session_sidecar": "omnigent-sessions.json",
|
|
387
|
+
}
|
|
388
|
+
assigned_run_ids = {run_id for group in groups.values() for run_id in group["run_ids"]}
|
|
389
|
+
sessions = {
|
|
390
|
+
"schema_version": "1.0",
|
|
391
|
+
"source": "omnigent",
|
|
392
|
+
"groups": list(groups.values()),
|
|
393
|
+
"unassigned_run_ids": [
|
|
394
|
+
trace.run_id for trace in imported.traces if trace.run_id not in assigned_run_ids
|
|
395
|
+
],
|
|
396
|
+
}
|
|
397
|
+
inventory["session_sidecar_sha256"] = sha256(
|
|
398
|
+
(canonical_json(sessions) + "\n").encode()
|
|
399
|
+
).hexdigest()
|
|
400
|
+
return OmnigentImportResult(
|
|
401
|
+
imported.traces, tuple(frozen), canonical_json(inventory), canonical_json(sessions)
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def inspect_omnigent_bundle(path: str | Path, limits: ImportLimits = ImportLimits()) -> dict:
|
|
406
|
+
"""Inspect existing exported evidence without executing any source records."""
|
|
407
|
+
root = Path(path)
|
|
408
|
+
inventory = load_json_artifact(root, "inventory.json", limits=limits).payload
|
|
409
|
+
sessions_artifact = load_json_artifact(root, "omnigent-sessions.json", limits=limits)
|
|
410
|
+
sessions = sessions_artifact.payload
|
|
411
|
+
if (
|
|
412
|
+
not isinstance(inventory, dict)
|
|
413
|
+
or inventory.get("adapter") != "omnigent-otel"
|
|
414
|
+
or not isinstance(sessions, dict)
|
|
415
|
+
or sessions.get("schema_version") != "1.0"
|
|
416
|
+
or sessions.get("source") != "omnigent"
|
|
417
|
+
or inventory.get("session_sidecar_sha256") != sessions_artifact.artifact_sha256
|
|
418
|
+
):
|
|
419
|
+
raise ImportValidationError(
|
|
420
|
+
"invalid_bundle", "inventory", "not a supported Omnigent import bundle"
|
|
421
|
+
)
|
|
422
|
+
return {"inventory": inventory, "sessions": sessions}
|