85742ab165
Deploy Documentation / deploy (push) Has been cancelled
CPU Test / Test (Utilities, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (LLM proxy, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Others, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Store, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Utilities, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Weave, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (AgentOps, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (LLM proxy, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (Others, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (Weave, latest, Python 3.13) (push) Has been cancelled
Dashboard / Chromatic (push) Has been cancelled
CPU Test / Lint - fast (push) Has been cancelled
CPU Test / Lint - next (push) Has been cancelled
CPU Test / Lint - slow (push) Has been cancelled
CPU Test / Lint - JavaScript (push) Has been cancelled
CPU Test / Build documentation (push) Has been cancelled
CPU Test / Test (AgentOps, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (LLM proxy, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (Others, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (Store, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (Weave, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (AgentOps, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Store, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (Utilities, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (Weave, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (AgentOps, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (LLM proxy, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (Others, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (Store, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (Utilities, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (JavaScript) (push) Has been cancelled
118 lines
4.6 KiB
Python
118 lines
4.6 KiB
Python
# Copyright (c) Microsoft. All rights reserved.
|
|
|
|
import base64
|
|
import json
|
|
import logging
|
|
from typing import Any, Dict, Optional
|
|
|
|
from agentlightning.semconv import AGL_OBJECT, LightningSpanAttributes
|
|
from agentlightning.tracer.base import get_active_tracer
|
|
from agentlightning.tracer.dummy import DummyTracer
|
|
from agentlightning.types import SpanCoreFields, SpanLike, TraceStatus
|
|
from agentlightning.utils.otel import flatten_attributes, full_qualified_name, sanitize_attributes
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def emit_object(object: Any, attributes: Optional[Dict[str, Any]] = None, propagate: bool = True) -> SpanCoreFields:
|
|
"""Emit an object's serialized representation as an OpenTelemetry span.
|
|
|
|
Args:
|
|
object: Data structure to encode as JSON and attach to the span payload.
|
|
attributes: Additional attributes to attach to the object span.
|
|
propagate: Whether to propagate the span to exporters automatically.
|
|
|
|
!!! note
|
|
The payload must be JSON serializable. Non-serializable objects will lead to a RuntimeError.
|
|
"""
|
|
span_attributes = encode_object(object)
|
|
if attributes:
|
|
flattened = flatten_attributes(attributes, expand_leaf_lists=False)
|
|
span_attributes.update(sanitize_attributes(flattened))
|
|
|
|
attr_length = 0
|
|
if LightningSpanAttributes.OBJECT_JSON.value in span_attributes:
|
|
attr_length = len(span_attributes[LightningSpanAttributes.OBJECT_JSON.value])
|
|
elif LightningSpanAttributes.OBJECT_LITERAL.value in span_attributes:
|
|
attr_length = len(span_attributes[LightningSpanAttributes.OBJECT_LITERAL.value])
|
|
logger.debug("Emitting object span with payload size %d characters", attr_length)
|
|
|
|
if propagate:
|
|
tracer = get_active_tracer()
|
|
if tracer is None:
|
|
raise RuntimeError("No active tracer found. Cannot emit object span.")
|
|
else:
|
|
# Do not actually propagate to any store or tracer backend.
|
|
tracer = DummyTracer()
|
|
|
|
return tracer.create_span(
|
|
name=AGL_OBJECT,
|
|
attributes=span_attributes,
|
|
status=TraceStatus(status_code="OK"),
|
|
)
|
|
|
|
|
|
def encode_object(object: Any) -> Dict[str, Any]:
|
|
"""Encode an object as span attributes.
|
|
|
|
Args:
|
|
object: Data structure to encode as JSON.
|
|
"""
|
|
span_attributes = {}
|
|
if isinstance(object, (str, int, float, bool)):
|
|
span_attributes = {
|
|
LightningSpanAttributes.OBJECT_TYPE.value: type(object).__name__,
|
|
LightningSpanAttributes.OBJECT_LITERAL.value: str(object),
|
|
}
|
|
elif isinstance(object, bytes):
|
|
b64_encoded = base64.b64encode(object).decode("utf-8")
|
|
span_attributes = {
|
|
LightningSpanAttributes.OBJECT_TYPE.value: "bytes",
|
|
LightningSpanAttributes.OBJECT_LITERAL.value: b64_encoded,
|
|
}
|
|
else:
|
|
try:
|
|
serialized = json.dumps(object)
|
|
except (TypeError, ValueError) as exc:
|
|
raise RuntimeError(f"Object must be JSON serializable, got: {type(object)}.") from exc
|
|
|
|
span_attributes = {
|
|
LightningSpanAttributes.OBJECT_TYPE.value: full_qualified_name(type(object)), # type: ignore
|
|
LightningSpanAttributes.OBJECT_JSON.value: serialized,
|
|
}
|
|
|
|
return span_attributes
|
|
|
|
|
|
def get_object_value(span: SpanLike) -> Any:
|
|
"""Extract the object payload from an object span.
|
|
|
|
Args:
|
|
span: Span object produced by Agent Lightning emitters.
|
|
"""
|
|
attributes = span.attributes or {}
|
|
if LightningSpanAttributes.OBJECT_JSON.value in attributes:
|
|
serialized = attributes[LightningSpanAttributes.OBJECT_JSON.value]
|
|
try:
|
|
return json.loads(serialized) # type: ignore
|
|
except (TypeError, ValueError) as exc:
|
|
raise RuntimeError("Failed to deserialize object JSON from span.") from exc
|
|
elif LightningSpanAttributes.OBJECT_LITERAL.value in attributes:
|
|
literal = attributes[LightningSpanAttributes.OBJECT_LITERAL.value]
|
|
obj_type = attributes.get(LightningSpanAttributes.OBJECT_TYPE.value, "str")
|
|
if obj_type == "str":
|
|
return literal
|
|
elif obj_type == "int":
|
|
# Let it raise errors if there are any
|
|
return int(literal) # type: ignore
|
|
elif obj_type == "float":
|
|
return float(literal) # type: ignore
|
|
elif obj_type == "bool":
|
|
return literal.lower() == "true" # type: ignore
|
|
elif obj_type == "bytes":
|
|
return base64.b64decode(literal.encode("utf-8")) # type: ignore
|
|
else:
|
|
raise RuntimeError(f"Unsupported object type for literal deserialization: {obj_type}")
|
|
else:
|
|
return None
|