Files
wehub-resource-sync 85742ab165
CPU Test / Lint - next (push) Waiting to run
Dashboard / Chromatic (push) Waiting to run
CPU Test / Lint - fast (push) Waiting to run
CPU Test / Build documentation (push) Waiting to run
CPU Test / Test (Store, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (Utilities, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (Weave, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (AgentOps, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (LLM proxy, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (Others, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (Store, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (Utilities, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (Weave, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (AgentOps, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (LLM proxy, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (Others, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (Store, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (Utilities, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (Weave, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (AgentOps, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (LLM proxy, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (Others, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (Store, latest, Python 3.13) (push) Waiting to run
CPU Test / Lint - slow (push) Waiting to run
CPU Test / Lint - JavaScript (push) Waiting to run
CPU Test / Test (AgentOps, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (LLM proxy, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (Others, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (Utilities, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (Weave, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (JavaScript) (push) Waiting to run
Deploy Documentation / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:44:17 +08:00

288 lines
9.3 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import functools
import logging
from contextlib import contextmanager
from typing import TYPE_CHECKING, Any, AsyncContextManager, Awaitable, Callable, ContextManager, List, Optional, TypeVar
from agentlightning.store.base import LightningStore
from agentlightning.types import Attributes, ParallelWorkerBase, Span, SpanCoreFields, SpanRecordingContext, TraceStatus
if TYPE_CHECKING:
from langchain_core.callbacks.base import BaseCallbackHandler # type: ignore
logger = logging.getLogger(__name__)
T = TypeVar("T")
_active_tracer: Optional[Tracer] = None
T_func = Callable[..., Awaitable[Any]]
class Tracer(ParallelWorkerBase):
"""
An abstract base class for tracers.
This class defines a standard interface for tracing code execution,
capturing the resulting spans, and providing them for analysis. It is
designed to be backend-agnostic, allowing for different implementations
(e.g., for AgentOps, OpenTelemetry, Docker, etc.).
The primary interaction pattern is through the [`trace_context`][agentlightning.Tracer.trace_context]
context manager, which ensures that traces are properly started and captured,
even in the case of exceptions.
A typical workflow:
```python
tracer = YourTracerImplementation()
try:
async with tracer.trace_context(name="my_traced_task"):
# ... code to be traced ...
await run_my_agent_logic()
except Exception as e:
print(f"An error occurred: {e}")
# Retrieve the trace data after the context block
spans: list[ReadableSpan] = tracer.get_last_trace()
# Process the trace data
if trace_tree:
rl_triplets = TracerTraceToTriplet().adapt(spans)
# ... do something with the triplets
```
"""
_store: Optional[LightningStore] = None
def init_worker(self, worker_id: int, store: Optional[LightningStore] = None) -> None:
"""Initialize the tracer for a worker.
Args:
worker_id: The ID of the worker.
store: The store to add the spans to. If it's provided, traces will be added to the store when tracing.
"""
super().init_worker(worker_id)
self._store = store
def trace_context(
self,
name: Optional[str] = None,
*,
store: Optional[LightningStore] = None,
rollout_id: Optional[str] = None,
attempt_id: Optional[str] = None,
) -> AsyncContextManager[Any]:
"""
Starts a new tracing context. This should be used as a context manager.
The implementation should handle the setup and teardown of the tracing
for the enclosed code block. It must ensure that any spans generated
within the `with` block are collected and made available via
[`get_last_trace`][agentlightning.Tracer.get_last_trace].
Args:
name: The name for the root span of this trace context.
store: The store to add the spans to. Deprecated in favor of passing store to init_worker().
rollout_id: The rollout ID to add the spans to.
attempt_id: The attempt ID to add the spans to.
"""
raise NotImplementedError()
def _trace_context_sync(
self,
name: Optional[str] = None,
*,
rollout_id: Optional[str] = None,
attempt_id: Optional[str] = None,
) -> ContextManager[Any]:
"""Internal API for CI backward compatibility."""
raise NotImplementedError()
def get_last_trace(self) -> List[Span]:
"""
Retrieves the raw list of captured spans from the most recent trace.
Returns:
A list of [`Span`][agentlightning.Span] objects collected during the last trace.
"""
raise NotImplementedError()
def trace_run(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
"""
A convenience wrapper to trace the execution of a single synchronous function.
Deprecated in favor of customizing Runners.
Args:
func: The synchronous function to execute and trace.
*args: Positional arguments to pass to the function.
**kwargs: Keyword arguments to pass to the function.
Returns:
The return value of the function.
"""
with self._trace_context_sync(name=func.__name__):
return func(*args, **kwargs)
def create_span(
self,
name: str,
attributes: Optional[Attributes] = None,
timestamp: Optional[float] = None,
status: Optional[TraceStatus] = None,
) -> SpanCoreFields:
"""Notify the tracer that a span should be created here.
It uses a fire-and-forget approach and doesn't wait for the span to be created.
Args:
name: The name of the span.
attributes: The attributes of the span.
timestamp: The timestamp of the span.
status: The status of the span.
Returns:
The core fields of the span.
"""
raise NotImplementedError()
def operation_context(
self,
name: str,
attributes: Optional[Attributes] = None,
start_time: Optional[float] = None,
end_time: Optional[float] = None,
) -> ContextManager[SpanRecordingContext]:
"""Start to record an operation to a span.
Args:
name: The name of the operation.
attributes: The attributes of the operation.
start_time: The start time of the operation.
end_time: The end time of the operation.
Returns:
A [`SpanRecordingContext`][agentlightning.SpanRecordingContext] for recording the operation on the span.
"""
raise NotImplementedError()
async def trace_run_async(self, func: Callable[..., Awaitable[Any]], *args: Any, **kwargs: Any) -> Any:
"""
A convenience wrapper to trace the execution of a single asynchronous function.
Deprecated in favor of customizing Runners.
Args:
func: The asynchronous function to execute and trace.
*args: Positional arguments to pass to the function.
**kwargs: Keyword arguments to pass to the function.
Returns:
The return value of the function.
"""
async with self.trace_context(name=func.__name__):
return await func(*args, **kwargs)
def get_langchain_handler(self) -> Optional[BaseCallbackHandler]: # type: ignore
"""Get a handler to install in langchain agent callback.
Agents are expected to use this handler in their agents to enable tracing.
"""
logger.warning(f"{self.__class__.__name__} does not provide a LangChain callback handler.")
return None
@contextmanager
def lifespan(self, store: Optional[LightningStore] = None):
"""A context manager to manage the lifespan of the tracer.
This can be used to set up and tear down any necessary resources
for the tracer, useful for debugging purposes.
Args:
store: The store to add the spans to. If it's provided, traces will be added to the store when tracing.
"""
has_init = False
has_init_worker = False
try:
self.init()
has_init = True
self.init_worker(0, store)
has_init_worker = True
yield
finally:
if has_init_worker:
self.teardown_worker(0)
if has_init:
self.teardown()
def set_active_tracer(tracer: Tracer):
"""Set the active tracer for the current process.
Args:
tracer: The tracer to set as active.
"""
global _active_tracer
if _active_tracer is not None:
raise ValueError("An active tracer is already set. Cannot set a new one.")
_active_tracer = tracer
def clear_active_tracer():
"""Clear the active tracer for the current process."""
global _active_tracer
_active_tracer = None
def get_active_tracer() -> Optional[Tracer]:
"""Get the active tracer for the current process.
Returns:
The active tracer, or None if no tracer is active.
"""
global _active_tracer
return _active_tracer
class _ActiveTracerAsyncCM(AsyncContextManager[T]):
def __init__(self, tracer: Tracer, inner: AsyncContextManager[T]):
self._tracer = tracer
self._inner = inner
async def __aenter__(self) -> T:
set_active_tracer(self._tracer) # will raise if nested
try:
return await self._inner.__aenter__()
except Exception:
clear_active_tracer()
raise
async def __aexit__(self, *args: Any, **kwargs: Any) -> Optional[bool]:
try:
return await self._inner.__aexit__(*args, **kwargs)
finally:
clear_active_tracer()
def with_active_tracer_context(
func: Callable[..., AsyncContextManager[T]],
) -> Callable[..., AsyncContextManager[T]]:
"""Decorate a method returning an AsyncContextManager so tracer is active for the whole `async with`."""
@functools.wraps(func)
def wrapper(self: Tracer, *args: Any, **kwargs: Any) -> AsyncContextManager[T]:
cm = func(self, *args, **kwargs)
return _ActiveTracerAsyncCM(self, cm)
return wrapper