Files
2026-07-13 13:32:05 +08:00

115 lines
3.7 KiB
Python

from contextvars import ContextVar
from contextlib import contextmanager
from dataclasses import dataclass
from typing import Optional, List, Dict, Any
from deepeval.metrics import BaseMetric
from deepeval.prompt import Prompt
from deepeval.test_case.llm_test_case import ToolCall
from deepeval.tracing.context import current_trace_context, update_current_trace
from deepeval.tracing.tracing import trace_manager
from deepeval.tracing.types import TraceWorkerStatus
from deepeval.tracing.utils import is_async_context
@dataclass
class LlmSpanContext:
prompt: Optional[Prompt] = None
metrics: Optional[List[BaseMetric]] = None
metric_collection: Optional[str] = None
expected_output: Optional[str] = None
expected_tools: Optional[List[ToolCall]] = None
context: Optional[List[str]] = None
retrieval_context: Optional[List[str]] = None
@dataclass
class AgentSpanContext:
metrics: Optional[List[BaseMetric]] = None
metric_collection: Optional[str] = None
expected_output: Optional[str] = None
expected_tools: Optional[List[ToolCall]] = None
context: Optional[List[str]] = None
retrieval_context: Optional[List[str]] = None
current_llm_context: ContextVar[Optional[LlmSpanContext]] = ContextVar(
"current_llm_context", default=LlmSpanContext()
)
current_agent_context: ContextVar[Optional[AgentSpanContext]] = ContextVar(
"current_agent_context", default=AgentSpanContext()
)
@contextmanager
def trace(
llm_span_context: Optional[LlmSpanContext] = None,
agent_span_context: Optional[AgentSpanContext] = None,
name: Optional[str] = None,
tags: Optional[List[str]] = None,
metadata: Optional[Dict[str, Any]] = None,
thread_id: Optional[str] = None,
user_id: Optional[str] = None,
input: Optional[Any] = None,
output: Optional[Any] = None,
retrieval_context: Optional[List[str]] = None,
context: Optional[List[str]] = None,
expected_output: Optional[str] = None,
tools_called: Optional[List[ToolCall]] = None,
expected_tools: Optional[List[ToolCall]] = None,
metrics: Optional[List[BaseMetric]] = None,
metric_collection: Optional[str] = None,
):
if is_async_context():
trace_manager._print_trace_status(
message="Warning: Detected use of the synchronous 'trace' context manager within an async method",
trace_worker_status=TraceWorkerStatus.WARNING,
description="Wrapping an async method with the synchronous 'trace' context manager may lead to unexpected behavior.",
)
current_trace = current_trace_context.get()
started_new_trace = False
if not current_trace:
current_trace = trace_manager.start_new_trace()
started_new_trace = True
if metrics:
current_trace.metrics = metrics
if metric_collection:
current_trace.metric_collection = metric_collection
trace_ctx_token = current_trace_context.set(current_trace)
update_current_trace(
name=name,
tags=tags,
metadata=metadata,
thread_id=thread_id,
user_id=user_id,
input=input,
output=output,
retrieval_context=retrieval_context,
context=context,
expected_output=expected_output,
tools_called=tools_called,
expected_tools=expected_tools,
)
if llm_span_context:
current_llm_context.set(llm_span_context)
if agent_span_context:
current_agent_context.set(agent_span_context)
try:
yield current_trace
finally:
if started_new_trace:
trace_manager.end_trace(current_trace.uuid)
current_trace_context.reset(trace_ctx_token)
current_llm_context.set(LlmSpanContext())
current_agent_context.set(AgentSpanContext())