mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-05-05 09:12:39 +00:00
chore: add coding tool environment detection via telemetry events
This commit is contained in:
@@ -75,6 +75,7 @@ from crewai.utilities.agent_utils import (
|
|||||||
)
|
)
|
||||||
from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE
|
from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE
|
||||||
from crewai.utilities.converter import Converter, ConverterError
|
from crewai.utilities.converter import Converter, ConverterError
|
||||||
|
from crewai.utilities.env import get_env_context
|
||||||
from crewai.utilities.guardrail import process_guardrail
|
from crewai.utilities.guardrail import process_guardrail
|
||||||
from crewai.utilities.guardrail_types import GuardrailType
|
from crewai.utilities.guardrail_types import GuardrailType
|
||||||
from crewai.utilities.llm_utils import create_llm
|
from crewai.utilities.llm_utils import create_llm
|
||||||
@@ -364,6 +365,7 @@ class Agent(BaseAgent):
|
|||||||
ValueError: If the max execution time is not a positive integer.
|
ValueError: If the max execution time is not a positive integer.
|
||||||
RuntimeError: If the agent execution fails for other reasons.
|
RuntimeError: If the agent execution fails for other reasons.
|
||||||
"""
|
"""
|
||||||
|
get_env_context()
|
||||||
# Only call handle_reasoning for legacy CrewAgentExecutor
|
# Only call handle_reasoning for legacy CrewAgentExecutor
|
||||||
# For AgentExecutor, planning is handled in AgentExecutor.generate_plan()
|
# For AgentExecutor, planning is handled in AgentExecutor.generate_plan()
|
||||||
if self.executor_class is not AgentExecutor:
|
if self.executor_class is not AgentExecutor:
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ from crewai.types.streaming import CrewStreamingOutput
|
|||||||
from crewai.types.usage_metrics import UsageMetrics
|
from crewai.types.usage_metrics import UsageMetrics
|
||||||
from crewai.utilities.constants import NOT_SPECIFIED, TRAINING_DATA_FILE
|
from crewai.utilities.constants import NOT_SPECIFIED, TRAINING_DATA_FILE
|
||||||
from crewai.utilities.crew.models import CrewContext
|
from crewai.utilities.crew.models import CrewContext
|
||||||
|
from crewai.utilities.env import get_env_context
|
||||||
from crewai.utilities.evaluators.crew_evaluator_handler import CrewEvaluator
|
from crewai.utilities.evaluators.crew_evaluator_handler import CrewEvaluator
|
||||||
from crewai.utilities.evaluators.task_evaluator import TaskEvaluator
|
from crewai.utilities.evaluators.task_evaluator import TaskEvaluator
|
||||||
from crewai.utilities.file_handler import FileHandler
|
from crewai.utilities.file_handler import FileHandler
|
||||||
@@ -679,6 +680,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
Returns:
|
Returns:
|
||||||
CrewOutput or CrewStreamingOutput if streaming is enabled.
|
CrewOutput or CrewStreamingOutput if streaming is enabled.
|
||||||
"""
|
"""
|
||||||
|
get_env_context()
|
||||||
if self.stream:
|
if self.stream:
|
||||||
enable_agent_streaming(self.agents)
|
enable_agent_streaming(self.agents)
|
||||||
ctx = StreamingContext()
|
ctx = StreamingContext()
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ from crewai.events.types.crew_events import (
|
|||||||
CrewTrainFailedEvent,
|
CrewTrainFailedEvent,
|
||||||
CrewTrainStartedEvent,
|
CrewTrainStartedEvent,
|
||||||
)
|
)
|
||||||
|
from crewai.events.types.env_events import (
|
||||||
|
CCEnvEvent,
|
||||||
|
CodexEnvEvent,
|
||||||
|
CursorEnvEvent,
|
||||||
|
DefaultEnvEvent,
|
||||||
|
)
|
||||||
from crewai.events.types.flow_events import (
|
from crewai.events.types.flow_events import (
|
||||||
FlowCreatedEvent,
|
FlowCreatedEvent,
|
||||||
FlowFinishedEvent,
|
FlowFinishedEvent,
|
||||||
@@ -143,6 +149,23 @@ class EventListener(BaseEventListener):
|
|||||||
# ----------- CREW EVENTS -----------
|
# ----------- CREW EVENTS -----------
|
||||||
|
|
||||||
def setup_listeners(self, crewai_event_bus: CrewAIEventsBus) -> None:
|
def setup_listeners(self, crewai_event_bus: CrewAIEventsBus) -> None:
|
||||||
|
|
||||||
|
@crewai_event_bus.on(CCEnvEvent)
|
||||||
|
def on_cc_env(_: Any, event: CCEnvEvent) -> None:
|
||||||
|
self._telemetry.env_context_span(event.type)
|
||||||
|
|
||||||
|
@crewai_event_bus.on(CodexEnvEvent)
|
||||||
|
def on_codex_env(_: Any, event: CodexEnvEvent) -> None:
|
||||||
|
self._telemetry.env_context_span(event.type)
|
||||||
|
|
||||||
|
@crewai_event_bus.on(CursorEnvEvent)
|
||||||
|
def on_cursor_env(_: Any, event: CursorEnvEvent) -> None:
|
||||||
|
self._telemetry.env_context_span(event.type)
|
||||||
|
|
||||||
|
@crewai_event_bus.on(DefaultEnvEvent)
|
||||||
|
def on_default_env(_: Any, event: DefaultEnvEvent) -> None:
|
||||||
|
self._telemetry.env_context_span(event.type)
|
||||||
|
|
||||||
@crewai_event_bus.on(CrewKickoffStartedEvent)
|
@crewai_event_bus.on(CrewKickoffStartedEvent)
|
||||||
def on_crew_started(source: Any, event: CrewKickoffStartedEvent) -> None:
|
def on_crew_started(source: Any, event: CrewKickoffStartedEvent) -> None:
|
||||||
self.formatter.handle_crew_started(event.crew_name or "Crew", source.id)
|
self.formatter.handle_crew_started(event.crew_name or "Crew", source.id)
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ from crewai.events.types.crew_events import (
|
|||||||
CrewKickoffFailedEvent,
|
CrewKickoffFailedEvent,
|
||||||
CrewKickoffStartedEvent,
|
CrewKickoffStartedEvent,
|
||||||
)
|
)
|
||||||
|
from crewai.events.types.env_events import (
|
||||||
|
CCEnvEvent,
|
||||||
|
CodexEnvEvent,
|
||||||
|
CursorEnvEvent,
|
||||||
|
DefaultEnvEvent,
|
||||||
|
)
|
||||||
from crewai.events.types.flow_events import (
|
from crewai.events.types.flow_events import (
|
||||||
FlowCreatedEvent,
|
FlowCreatedEvent,
|
||||||
FlowFinishedEvent,
|
FlowFinishedEvent,
|
||||||
@@ -192,6 +198,7 @@ class TraceCollectionListener(BaseEventListener):
|
|||||||
if self._listeners_setup:
|
if self._listeners_setup:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self._register_env_event_handlers(crewai_event_bus)
|
||||||
self._register_flow_event_handlers(crewai_event_bus)
|
self._register_flow_event_handlers(crewai_event_bus)
|
||||||
self._register_context_event_handlers(crewai_event_bus)
|
self._register_context_event_handlers(crewai_event_bus)
|
||||||
self._register_action_event_handlers(crewai_event_bus)
|
self._register_action_event_handlers(crewai_event_bus)
|
||||||
@@ -200,6 +207,25 @@ class TraceCollectionListener(BaseEventListener):
|
|||||||
|
|
||||||
self._listeners_setup = True
|
self._listeners_setup = True
|
||||||
|
|
||||||
|
def _register_env_event_handlers(self, event_bus: CrewAIEventsBus) -> None:
|
||||||
|
"""Register handlers for environment context events."""
|
||||||
|
|
||||||
|
@event_bus.on(CCEnvEvent)
|
||||||
|
def on_cc_env(source: Any, event: CCEnvEvent) -> None:
|
||||||
|
self._handle_action_event("cc_env", source, event)
|
||||||
|
|
||||||
|
@event_bus.on(CodexEnvEvent)
|
||||||
|
def on_codex_env(source: Any, event: CodexEnvEvent) -> None:
|
||||||
|
self._handle_action_event("codex_env", source, event)
|
||||||
|
|
||||||
|
@event_bus.on(CursorEnvEvent)
|
||||||
|
def on_cursor_env(source: Any, event: CursorEnvEvent) -> None:
|
||||||
|
self._handle_action_event("cursor_env", source, event)
|
||||||
|
|
||||||
|
@event_bus.on(DefaultEnvEvent)
|
||||||
|
def on_default_env(source: Any, event: DefaultEnvEvent) -> None:
|
||||||
|
self._handle_action_event("default_env", source, event)
|
||||||
|
|
||||||
def _register_flow_event_handlers(self, event_bus: CrewAIEventsBus) -> None:
|
def _register_flow_event_handlers(self, event_bus: CrewAIEventsBus) -> None:
|
||||||
"""Register handlers for flow events."""
|
"""Register handlers for flow events."""
|
||||||
|
|
||||||
|
|||||||
36
lib/crewai/src/crewai/events/types/env_events.py
Normal file
36
lib/crewai/src/crewai/events/types/env_events.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
from typing import Annotated, Literal
|
||||||
|
|
||||||
|
from pydantic import Field, TypeAdapter
|
||||||
|
|
||||||
|
from crewai.events.base_events import BaseEvent
|
||||||
|
|
||||||
|
|
||||||
|
class CCEnvEvent(BaseEvent):
|
||||||
|
type: Literal["cc_env"] = "cc_env"
|
||||||
|
|
||||||
|
|
||||||
|
class CodexEnvEvent(BaseEvent):
|
||||||
|
type: Literal["codex_env"] = "codex_env"
|
||||||
|
|
||||||
|
|
||||||
|
class CursorEnvEvent(BaseEvent):
|
||||||
|
type: Literal["cursor_env"] = "cursor_env"
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultEnvEvent(BaseEvent):
|
||||||
|
type: Literal["default_env"] = "default_env"
|
||||||
|
|
||||||
|
|
||||||
|
EnvContextEvent = Annotated[
|
||||||
|
CCEnvEvent | CodexEnvEvent | CursorEnvEvent | DefaultEnvEvent,
|
||||||
|
Field(discriminator="type"),
|
||||||
|
]
|
||||||
|
|
||||||
|
env_context_event_adapter: TypeAdapter[EnvContextEvent] = TypeAdapter(EnvContextEvent)
|
||||||
|
|
||||||
|
ENV_CONTEXT_EVENT_TYPES: tuple[type[BaseEvent], ...] = (
|
||||||
|
CCEnvEvent,
|
||||||
|
CodexEnvEvent,
|
||||||
|
CursorEnvEvent,
|
||||||
|
DefaultEnvEvent,
|
||||||
|
)
|
||||||
@@ -110,6 +110,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
from crewai.flow.visualization import build_flow_structure, render_interactive
|
from crewai.flow.visualization import build_flow_structure, render_interactive
|
||||||
from crewai.types.streaming import CrewStreamingOutput, FlowStreamingOutput
|
from crewai.types.streaming import CrewStreamingOutput, FlowStreamingOutput
|
||||||
|
from crewai.utilities.env import get_env_context
|
||||||
from crewai.utilities.streaming import (
|
from crewai.utilities.streaming import (
|
||||||
TaskInfo,
|
TaskInfo,
|
||||||
create_async_chunk_generator,
|
create_async_chunk_generator,
|
||||||
@@ -1770,6 +1771,7 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
Returns:
|
Returns:
|
||||||
The final output from the flow or FlowStreamingOutput if streaming.
|
The final output from the flow or FlowStreamingOutput if streaming.
|
||||||
"""
|
"""
|
||||||
|
get_env_context()
|
||||||
if self.stream:
|
if self.stream:
|
||||||
result_holder: list[Any] = []
|
result_holder: list[Any] = []
|
||||||
current_task_info: TaskInfo = {
|
current_task_info: TaskInfo = {
|
||||||
|
|||||||
@@ -986,6 +986,22 @@ class Telemetry:
|
|||||||
|
|
||||||
self._safe_telemetry_operation(_operation)
|
self._safe_telemetry_operation(_operation)
|
||||||
|
|
||||||
|
def env_context_span(self, tool: str) -> None:
|
||||||
|
"""Records the coding tool environment context."""
|
||||||
|
|
||||||
|
def _operation() -> None:
|
||||||
|
tracer = trace.get_tracer("crewai.telemetry")
|
||||||
|
span = tracer.start_span("Environment Context")
|
||||||
|
self._add_attribute(
|
||||||
|
span,
|
||||||
|
"crewai_version",
|
||||||
|
version("crewai"),
|
||||||
|
)
|
||||||
|
self._add_attribute(span, "tool", tool)
|
||||||
|
close_span(span)
|
||||||
|
|
||||||
|
self._safe_telemetry_operation(_operation)
|
||||||
|
|
||||||
def human_feedback_span(
|
def human_feedback_span(
|
||||||
self,
|
self,
|
||||||
event_type: str,
|
event_type: str,
|
||||||
|
|||||||
@@ -8,6 +8,21 @@ TRAINED_AGENTS_DATA_FILE: Final[str] = "trained_agents_data.pkl"
|
|||||||
KNOWLEDGE_DIRECTORY: Final[str] = "knowledge"
|
KNOWLEDGE_DIRECTORY: Final[str] = "knowledge"
|
||||||
MAX_FILE_NAME_LENGTH: Final[int] = 255
|
MAX_FILE_NAME_LENGTH: Final[int] = 255
|
||||||
EMITTER_COLOR: Final[PrinterColor] = "bold_blue"
|
EMITTER_COLOR: Final[PrinterColor] = "bold_blue"
|
||||||
|
CC_ENV_VAR: Final[str] = "CLAUDECODE"
|
||||||
|
CODEX_ENV_VARS: Final[tuple[str, ...]] = (
|
||||||
|
"CODEX_CI",
|
||||||
|
"CODEX_MANAGED_BY_NPM",
|
||||||
|
"CODEX_SANDBOX",
|
||||||
|
"CODEX_SANDBOX_NETWORK_DISABLED",
|
||||||
|
"CODEX_THREAD_ID",
|
||||||
|
)
|
||||||
|
CURSOR_ENV_VARS: Final[tuple[str, ...]] = (
|
||||||
|
"CURSOR_AGENT",
|
||||||
|
"CURSOR_EXTENSION_HOST_ROLE",
|
||||||
|
"CURSOR_SANDBOX",
|
||||||
|
"CURSOR_TRACE_ID",
|
||||||
|
"CURSOR_WORKSPACE_LABEL",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class _NotSpecified:
|
class _NotSpecified:
|
||||||
|
|||||||
39
lib/crewai/src/crewai/utilities/env.py
Normal file
39
lib/crewai/src/crewai/utilities/env.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import contextvars
|
||||||
|
import os
|
||||||
|
|
||||||
|
from crewai.events.event_bus import crewai_event_bus
|
||||||
|
from crewai.events.types.env_events import (
|
||||||
|
CCEnvEvent,
|
||||||
|
CodexEnvEvent,
|
||||||
|
CursorEnvEvent,
|
||||||
|
DefaultEnvEvent,
|
||||||
|
)
|
||||||
|
from crewai.utilities.constants import CC_ENV_VAR, CODEX_ENV_VARS, CURSOR_ENV_VARS
|
||||||
|
|
||||||
|
|
||||||
|
_env_context_emitted: contextvars.ContextVar[bool] = contextvars.ContextVar(
|
||||||
|
"_env_context_emitted", default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_codex_env() -> bool:
|
||||||
|
return any(os.environ.get(var) for var in CODEX_ENV_VARS)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_cursor_env() -> bool:
|
||||||
|
return any(os.environ.get(var) for var in CURSOR_ENV_VARS)
|
||||||
|
|
||||||
|
|
||||||
|
def get_env_context() -> None:
|
||||||
|
if _env_context_emitted.get():
|
||||||
|
return
|
||||||
|
_env_context_emitted.set(True)
|
||||||
|
|
||||||
|
if os.environ.get(CC_ENV_VAR):
|
||||||
|
crewai_event_bus.emit(None, CCEnvEvent())
|
||||||
|
elif _is_codex_env():
|
||||||
|
crewai_event_bus.emit(None, CodexEnvEvent())
|
||||||
|
elif _is_cursor_env():
|
||||||
|
crewai_event_bus.emit(None, CursorEnvEvent())
|
||||||
|
else:
|
||||||
|
crewai_event_bus.emit(None, DefaultEnvEvent())
|
||||||
Reference in New Issue
Block a user