Files
wehub-resource-sync 4b6817381b
Benchmark image — build + push to ECR (any adapter) / build + push (push) Waiting to run
CI / quality (ubuntu-latest) (push) Waiting to run
CI / test (tools-runtime) (push) Waiting to run
CI / test (e2e-general) (push) Waiting to run
CI / test (cli-runtime) (push) Waiting to run
CI / test (e2e-provider-and-openclaw) (push) Waiting to run
CI / test (integrations-and-misc) (push) Waiting to run
CI / coverage-report (push) Blocked by required conditions
CI / test-kubernetes (push) Waiting to run
CI / should-run-thorough (push) Waiting to run
CI / test-thorough (cloudwatch-demo) (push) Blocked by required conditions
CI / test-thorough (flink-ecs) (push) Blocked by required conditions
CI / test-thorough (upstream-lambda) (push) Blocked by required conditions
CI / test-thorough (prefect-ecs-fargate) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Blocked by required conditions
Release / publish-release (push) Blocked by required conditions
Release / publish-main-release (push) Blocked by required conditions
Release / prepare (push) Waiting to run
Release / verify (push) Blocked by required conditions
Release / build-python-dist (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Blocked by required conditions
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Waiting to run
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

139 lines
4.5 KiB
Python

"""Canonical investigation loop metrics for analytics events.
Convention: ``investigation_loop_count`` is the number of completed LLM ReAct
iterations in the gather-evidence agent (0 before the first invoke).
Seed tool calls before the loop are not counted.
"""
from __future__ import annotations
from collections.abc import Mapping
from contextvars import ContextVar, Token
from typing import Any
from config.constants.investigation import MAX_INVESTIGATION_LOOPS
from platform.analytics.provider import Properties
_loop_metrics: ContextVar[tuple[int, int] | None] = ContextVar(
"investigation_loop_metrics",
default=None,
)
def investigation_loop_count_from_state(state: Mapping[str, Any] | None) -> int:
"""Read the canonical loop counter from investigation state."""
if state is None:
return 0
raw = state.get("investigation_loop_count")
if isinstance(raw, bool):
return 0
if isinstance(raw, int | float):
return max(0, int(raw))
return 0
def investigation_iteration_cap_from_state(state: Mapping[str, Any] | None) -> int:
"""Read the configured iteration cap from state, else the global default."""
if state is None:
return MAX_INVESTIGATION_LOOPS
raw = state.get("investigation_iteration_cap")
if isinstance(raw, bool):
return MAX_INVESTIGATION_LOOPS
if isinstance(raw, int | float) and int(raw) > 0:
return int(raw)
return MAX_INVESTIGATION_LOOPS
def loop_metrics_from_state(
state: Mapping[str, Any] | None,
) -> tuple[int, int]:
"""Return ``(loop_count, iteration_cap)`` from investigation state."""
return (
investigation_loop_count_from_state(state),
investigation_iteration_cap_from_state(state),
)
def loop_properties(
*,
loop_count: int,
iteration_cap: int,
) -> Properties:
"""Build required loop metric properties for PostHog events."""
return {
"investigation_loop_count": max(0, int(loop_count)),
"investigation_iteration_cap": max(1, int(iteration_cap)),
}
def merge_loop_properties(
properties: Properties,
*,
loop_count: int,
iteration_cap: int,
) -> Properties:
"""Attach loop metrics to an existing analytics property dict."""
return {**properties, **loop_properties(loop_count=loop_count, iteration_cap=iteration_cap)}
def begin_investigation_loop_metrics_scope() -> Token[tuple[int, int] | None]:
"""Start a scoped investigation loop-metrics context."""
return _loop_metrics.set(None)
def bind_investigation_loop_metrics_from_state(state: Mapping[str, Any] | None) -> None:
"""Publish loop metrics for the active investigation tracking context."""
count, cap = loop_metrics_from_state(state)
bind_investigation_loop_metrics(loop_count=count, iteration_cap=cap)
def bind_investigation_loop_metrics(*, loop_count: int, iteration_cap: int) -> None:
"""Publish explicit loop metrics for the active investigation tracking context."""
_loop_metrics.set((max(0, int(loop_count)), max(1, int(iteration_cap))))
def publish_loop_metrics_from_stream_failure(exc: BaseException) -> BaseException:
"""Bind loop metrics on this thread when *exc* carries them, then unwrap."""
loop_count = getattr(exc, "loop_count", None)
iteration_cap = getattr(exc, "iteration_cap", None)
cause = getattr(exc, "cause", None)
if (
cause is not None
and isinstance(cause, BaseException)
and loop_count is not None
and iteration_cap is not None
and not isinstance(loop_count, bool)
and not isinstance(iteration_cap, bool)
):
bind_investigation_loop_metrics(
loop_count=int(loop_count),
iteration_cap=int(iteration_cap),
)
return cause
return exc
def reset_investigation_loop_metrics(token: Token[tuple[int, int] | None]) -> None:
"""Restore loop metrics from ``begin_investigation_loop_metrics_scope``."""
_loop_metrics.reset(token)
def bound_loop_metrics() -> tuple[int, int] | None:
"""Return bound loop metrics for the current context, if any."""
return _loop_metrics.get()
__all__ = [
"begin_investigation_loop_metrics_scope",
"bind_investigation_loop_metrics",
"bind_investigation_loop_metrics_from_state",
"bound_loop_metrics",
"investigation_iteration_cap_from_state",
"investigation_loop_count_from_state",
"loop_metrics_from_state",
"loop_properties",
"merge_loop_properties",
"publish_loop_metrics_from_stream_failure",
"reset_investigation_loop_metrics",
]