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

163 lines
5.2 KiB
Python

# ======== from tools/tempo_tool/ ========
"""Grafana Tempo trace query tool (single action-based entrypoint)."""
from __future__ import annotations
from typing import Any
from core.tool_framework.tool_decorator import tool
from integrations.tempo import TempoConfig, tempo_extract_params
from integrations.tempo.availability import tempo_available_or_backend
from integrations.tempo.client import TempoClient
_VALID_ACTIONS = ("search", "get_trace", "list_services", "list_span_names")
def _tempo_is_available(sources: dict[str, dict]) -> bool:
return tempo_available_or_backend(sources)
def _tempo_extract_params(sources: dict[str, dict]) -> dict[str, Any]:
tempo = sources.get("tempo", {})
return {
**tempo_extract_params(sources),
"service": tempo.get("service_name", ""),
"time_range_minutes": tempo.get("time_range_minutes", 60),
"limit": 20,
"tempo_backend": tempo.get("_backend"),
}
def _dispatch(
client: Any,
*,
action: str,
trace_id: str | None,
service: str | None,
span_name: str | None,
min_duration_ms: float | None,
max_duration_ms: float | None,
tags: dict[str, str] | None,
time_range_minutes: int,
limit: int,
) -> dict[str, Any]:
result: dict[str, Any]
if action == "get_trace":
result = client.get_trace_by_id(trace_id or "")
elif action == "list_services":
result = client.list_services(time_range_minutes=time_range_minutes)
elif action == "list_span_names":
result = client.list_span_names(time_range_minutes=time_range_minutes)
else:
result = client.search_traces(
service=service,
span_name=span_name,
min_duration_ms=min_duration_ms,
max_duration_ms=max_duration_ms,
tags=tags,
time_range_minutes=time_range_minutes,
limit=limit,
)
return result
@tool(
name="query_tempo",
display_name="Grafana Tempo",
source="tempo",
tags=("traces", "observability"),
description=(
"Query a standalone Grafana Tempo backend for distributed traces. "
"Use 'action' to pick: search traces, fetch a trace by ID, or list "
"registered services / span names."
),
use_cases=[
"Fetching a full trace by trace ID to inspect its spans",
"Searching traces by service, span name, duration, or tags",
"Listing services and span names registered in Tempo",
"Correlating slow or error spans with logs and metrics",
],
requires=[],
input_schema={
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": list(_VALID_ACTIONS),
"default": "search",
"description": "Which Tempo query to run.",
},
"trace_id": {
"type": "string",
"description": "Trace ID to fetch (required when action='get_trace').",
},
"service": {"type": "string", "description": "Service name filter for search."},
"span_name": {"type": "string", "description": "Span name filter for search."},
"min_duration_ms": {"type": "number", "description": "Minimum span duration (ms)."},
"max_duration_ms": {"type": "number", "description": "Maximum span duration (ms)."},
"tags": {
"type": "object",
"description": "Span attribute filters (key -> value), applied as span.<key>.",
},
"time_range_minutes": {"type": "integer", "default": 60},
"limit": {"type": "integer", "default": 20},
},
"required": [],
},
is_available=_tempo_is_available,
extract_params=_tempo_extract_params,
)
def query_tempo(
action: str = "search",
trace_id: str | None = None,
service: str | None = None,
span_name: str | None = None,
min_duration_ms: float | None = None,
max_duration_ms: float | None = None,
tags: dict[str, str] | None = None,
time_range_minutes: int = 60,
limit: int = 20,
tempo_backend: Any = None,
**_kwargs: Any,
) -> dict[str, Any]:
"""Query Grafana Tempo for traces, a single trace, or registered tag values."""
if action not in _VALID_ACTIONS:
action = "search"
if tempo_backend is not None:
return _dispatch(
tempo_backend,
action=action,
trace_id=trace_id,
service=service,
span_name=span_name,
min_duration_ms=min_duration_ms,
max_duration_ms=max_duration_ms,
tags=tags,
time_range_minutes=time_range_minutes,
limit=limit,
)
config = TempoConfig.model_validate(_kwargs)
if not config.is_configured:
return {
"source": "tempo",
"action": action,
"available": False,
"error": "Tempo not configured. Provide TEMPO_URL.",
}
return _dispatch(
TempoClient(config),
action=action,
trace_id=trace_id,
service=service,
span_name=span_name,
min_duration_ms=min_duration_ms,
max_duration_ms=max_duration_ms,
tags=tags,
time_range_minutes=time_range_minutes,
limit=limit,
)