Files
2026-07-13 13:22:34 +08:00

117 lines
3.9 KiB
Python

import json
import random
import time
import uuid
from opentelemetry import trace as trace_api
from opentelemetry.sdk.resources import Resource as _OTelResource
from opentelemetry.sdk.trace import ReadableSpan as OTelReadableSpan
from opentelemetry.trace import SpanContext
from mlflow.entities.span import Span, SpanType, create_mlflow_span
from mlflow.entities.trace_info import TraceInfo
from mlflow.entities.trace_location import TraceLocation
from mlflow.entities.trace_state import TraceState
from mlflow.store.tracking.sqlalchemy_store import SqlAlchemyStore
from mlflow.tracing.constant import SpanAttributeKey, TraceTagKey
from mlflow.tracing.utils import TraceJSONEncoder
ENV_CHOICES = ["prod", "staging", "dev"]
NAME_PREFIXES = ["agent_run", "qa_chain", "rag_pipeline", "summarizer"]
WEEK_MS = 7 * 24 * 60 * 60 * 1000
SEED_TRACES = 1000
SEED_SPANS_PER_TRACE = 10
def generate_trace_data(
experiment_id: str,
num_spans: int,
rng: random.Random,
) -> tuple[TraceInfo, list[Span]]:
trace_id = f"tr-{uuid.uuid4().hex}"
request_time = int(time.time() * 1000) - rng.randint(0, WEEK_MS)
name_prefix = rng.choice(NAME_PREFIXES)
trace_info = TraceInfo(
trace_id=trace_id,
trace_location=TraceLocation.from_experiment_id(experiment_id),
request_time=request_time,
state=rng.choice([TraceState.OK, TraceState.OK, TraceState.OK, TraceState.ERROR]),
execution_duration=rng.randint(100, 5000),
tags={
TraceTagKey.TRACE_NAME: f"{name_prefix}_{trace_id[-4:]}",
"env": rng.choice(ENV_CHOICES),
},
)
span_types = [SpanType.LLM, SpanType.RETRIEVER, SpanType.TOOL, SpanType.CHAIN]
base_ns = 1_000_000_000_000
spans: list[Span] = []
for i in range(num_spans):
is_root = i == 0
span_type = SpanType.AGENT if is_root else rng.choice(span_types)
parent_id = None if is_root else rng.choice(range(max(0, i - 3), i))
trace_num = rng.randint(1, 2**63)
ctx = SpanContext(
trace_id=trace_num,
span_id=i + 1,
is_remote=False,
trace_flags=trace_api.TraceFlags(1),
trace_state=trace_api.TraceState(),
)
parent_ctx = None
if parent_id is not None:
parent_ctx = SpanContext(
trace_id=trace_num,
span_id=parent_id + 1,
is_remote=False,
trace_flags=trace_api.TraceFlags(1),
trace_state=trace_api.TraceState(),
)
attrs: dict[str, object] = {}
if is_root:
attrs[SpanAttributeKey.INPUTS] = json.dumps(
{"query": "What is ML?"}, cls=TraceJSONEncoder
)
attrs[SpanAttributeKey.OUTPUTS] = json.dumps(
{"response": "ML is..."}, cls=TraceJSONEncoder
)
otel_span = OTelReadableSpan(
name=f"{span_type.lower()}_{i}" if not is_root else "agent_run",
context=ctx,
parent=parent_ctx,
attributes={
"mlflow.traceRequestId": json.dumps(trace_id),
"mlflow.spanType": json.dumps(span_type, cls=TraceJSONEncoder),
**attrs,
},
start_time=base_ns + i * 10_000_000,
end_time=base_ns + i * 10_000_000 + rng.randint(5_000_000, 50_000_000),
status=trace_api.Status(trace_api.StatusCode.OK),
resource=_OTelResource.get_empty(),
)
spans.append(create_mlflow_span(otel_span, trace_id, span_type))
return trace_info, spans
def seed_traces(
store: SqlAlchemyStore,
experiment_id: str,
count: int,
spans_per_trace: int,
) -> list[str]:
rng = random.Random(123)
trace_ids: list[str] = []
for _ in range(count):
ti, sp = generate_trace_data(experiment_id, spans_per_trace, rng)
store.start_trace(ti)
store.log_spans(experiment_id, sp)
trace_ids.append(ti.trace_id)
return trace_ids