mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-01 05:08:12 +00:00
fix: keep coroutine results inside the execute flow method span
Address review feedback on the native OpenTelemetry instrumentation
This commit is contained in:
@@ -2868,13 +2868,14 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta):
|
||||
result = await asyncio.to_thread(
|
||||
ctx.run, method, *args, **kwargs
|
||||
)
|
||||
# Auto-await coroutines returned from sync methods so the
|
||||
# whole call stays inside the "execute flow method" span
|
||||
# (enables AgentExecutor pattern).
|
||||
if asyncio.iscoroutine(result):
|
||||
result = await result
|
||||
finally:
|
||||
current_flow_method_name.reset(method_name_token)
|
||||
|
||||
# Auto-await coroutines returned from sync methods (enables AgentExecutor pattern)
|
||||
if asyncio.iscoroutine(result):
|
||||
result = await result
|
||||
|
||||
method_definition = self._definition.methods[str(method_name)]
|
||||
if method_definition.human_feedback is not None:
|
||||
result = await self._run_human_feedback_step(
|
||||
|
||||
@@ -1814,8 +1814,9 @@ class LLM(BaseLLM):
|
||||
ValueError: If response format is not supported
|
||||
LLMContextLengthExceededError: If input exceeds model's context limit
|
||||
"""
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
self._emit_call_started_event(
|
||||
messages=messages,
|
||||
@@ -1955,8 +1956,9 @@ class LLM(BaseLLM):
|
||||
ValueError: If response format is not supported
|
||||
LLMContextLengthExceededError: If input exceeds model's context limit
|
||||
"""
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
self._emit_call_started_event(
|
||||
messages=messages,
|
||||
|
||||
@@ -298,8 +298,9 @@ class AnthropicCompletion(BaseLLM):
|
||||
Returns:
|
||||
Chat completion response or tool call result
|
||||
"""
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
@@ -375,8 +376,9 @@ class AnthropicCompletion(BaseLLM):
|
||||
Returns:
|
||||
Chat completion response or tool call result
|
||||
"""
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
|
||||
@@ -504,8 +504,9 @@ class AzureCompletion(BaseLLM):
|
||||
response_model=response_model,
|
||||
)
|
||||
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
@@ -585,8 +586,9 @@ class AzureCompletion(BaseLLM):
|
||||
response_model=response_model,
|
||||
)
|
||||
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
|
||||
@@ -363,8 +363,9 @@ class BedrockCompletion(BaseLLM):
|
||||
"""Call AWS Bedrock Converse API."""
|
||||
effective_response_model = response_model or self.response_format
|
||||
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
@@ -498,8 +499,9 @@ class BedrockCompletion(BaseLLM):
|
||||
'Install with: uv add "crewai[bedrock-async]"'
|
||||
)
|
||||
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
|
||||
@@ -295,8 +295,9 @@ class GeminiCompletion(BaseLLM):
|
||||
Returns:
|
||||
Chat completion response or tool call result
|
||||
"""
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
@@ -383,8 +384,9 @@ class GeminiCompletion(BaseLLM):
|
||||
Returns:
|
||||
Chat completion response or tool call result
|
||||
"""
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
|
||||
@@ -411,8 +411,9 @@ class OpenAICompletion(BaseLLM):
|
||||
Returns:
|
||||
Completion response or tool call result.
|
||||
"""
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
@@ -513,8 +514,9 @@ class OpenAICompletion(BaseLLM):
|
||||
Returns:
|
||||
Completion response or tool call result.
|
||||
"""
|
||||
with llm_call_context(), operation(
|
||||
"call llm", {"crewai.llm.model": self.model}
|
||||
with (
|
||||
llm_call_context(),
|
||||
operation("call llm", {"crewai.llm.model": self.model}),
|
||||
):
|
||||
try:
|
||||
self._emit_call_started_event(
|
||||
|
||||
@@ -83,9 +83,7 @@ def operation(
|
||||
raise
|
||||
except Exception as exc:
|
||||
span.record_exception(exc, escaped=True)
|
||||
span.set_status(
|
||||
Status(StatusCode.ERROR, f"{type(exc).__name__}: {exc}")
|
||||
)
|
||||
span.set_status(Status(StatusCode.ERROR, f"{type(exc).__name__}: {exc}"))
|
||||
raise
|
||||
|
||||
|
||||
|
||||
@@ -327,9 +327,7 @@ class CrewStructuredTool(BaseModel):
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
call = functools.partial(self.func, **parsed_args, **kwargs)
|
||||
return await asyncio.get_event_loop().run_in_executor(
|
||||
None, ctx.run, call
|
||||
)
|
||||
return await asyncio.get_event_loop().run_in_executor(None, ctx.run, call)
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
|
||||
@@ -52,6 +52,30 @@ _SHARED_EXPORTER: InMemorySpanExporter | None = None
|
||||
_SHARED_PROVIDER: TracerProvider | None = None
|
||||
|
||||
|
||||
def _reset_global_tracer_provider() -> None:
|
||||
"""Reset OTel's process-global tracer provider slot.
|
||||
|
||||
OTel's ``set_tracer_provider`` is a one-shot install: once called, the
|
||||
private ``_TRACER_PROVIDER_SET_ONCE`` latch silently no-ops every
|
||||
subsequent call. Tests that need to install their own SDK provider
|
||||
have to undo that latch, but OTel exposes no public API for it, so we
|
||||
poke the private symbols directly.
|
||||
|
||||
This helper is pinned to ``opentelemetry-api~=1.34.0`` (see the
|
||||
project's ``pyproject.toml``). If a future bump renames or removes
|
||||
either of these private attributes, the ``assert`` below will fail
|
||||
loudly and a maintainer can adjust the shim.
|
||||
"""
|
||||
assert hasattr(trace, "_TRACER_PROVIDER_SET_ONCE"), (
|
||||
"opentelemetry-api dropped _TRACER_PROVIDER_SET_ONCE; update _reset_global_tracer_provider"
|
||||
)
|
||||
assert hasattr(trace, "_TRACER_PROVIDER"), (
|
||||
"opentelemetry-api dropped _TRACER_PROVIDER; update _reset_global_tracer_provider"
|
||||
)
|
||||
trace._TRACER_PROVIDER_SET_ONCE._done = False # type: ignore[attr-defined]
|
||||
trace._TRACER_PROVIDER = None # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def span_exporter(monkeypatch: pytest.MonkeyPatch) -> Iterator[InMemorySpanExporter]:
|
||||
"""Install (once) an SDK TracerProvider and yield the in-memory exporter.
|
||||
@@ -81,8 +105,7 @@ def span_exporter(monkeypatch: pytest.MonkeyPatch) -> Iterator[InMemorySpanExpor
|
||||
_SHARED_EXPORTER = InMemorySpanExporter()
|
||||
_SHARED_PROVIDER = TracerProvider()
|
||||
_SHARED_PROVIDER.add_span_processor(SimpleSpanProcessor(_SHARED_EXPORTER))
|
||||
trace._TRACER_PROVIDER_SET_ONCE._done = False # type: ignore[attr-defined]
|
||||
trace._TRACER_PROVIDER = None # type: ignore[attr-defined]
|
||||
_reset_global_tracer_provider()
|
||||
trace.set_tracer_provider(_SHARED_PROVIDER)
|
||||
actual = trace.get_tracer_provider()
|
||||
assert actual is _SHARED_PROVIDER, (
|
||||
|
||||
Reference in New Issue
Block a user