refactor(telemetry): migrate to type-safe enum-based event routing with centralized enterprise filtering

Changes:
- Change TelemetryEvent.name from str to TraceTaskName enum for type safety
- Remove hardcoded trace_task_name_map from facade (no mapping needed)
- Add centralized enterprise-only filter in TelemetryFacade.emit()
- Rename is_telemetry_enabled() to is_enterprise_telemetry_enabled()
- Update all 11 call sites to pass TraceTaskName enum values
- Remove redundant enterprise guard from draft_trace.py
- Add unit tests for TelemetryFacade.emit() routing (6 tests)
- Add unit tests for TraceQueueManager telemetry guard (5 tests)
- Fix test fixture scoping issue for full test suite compatibility
- Fix tenant_id handling in agent tool callback handler

Benefits:
- 100% type-safe: basedpyright catches errors at compile time
- No string literals: eliminates entire class of typo bugs
- Single point of control: centralized filtering in facade
- All guards removed except facade
- Zero regressions: 4887 tests passing

Verification:
- make lint: PASS
- make type-check: PASS (0 errors, 0 warnings)
- pytest: 4887 passed, 8 skipped
This commit is contained in:
GareArc
2026-02-05 15:12:02 -08:00
parent ed222945aa
commit adadf1ec5f
16 changed files with 502 additions and 54 deletions

View File

@ -2,32 +2,27 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from core.ops.entities.trace_entity import TraceTaskName
from core.telemetry.events import TelemetryEvent
if TYPE_CHECKING:
from core.ops.ops_trace_manager import TraceQueueManager
_ENTERPRISE_ONLY_TRACES: frozenset[TraceTaskName] = frozenset(
{
TraceTaskName.DRAFT_NODE_EXECUTION_TRACE,
TraceTaskName.NODE_EXECUTION_TRACE,
TraceTaskName.PROMPT_GENERATION_TRACE,
}
)
class TelemetryFacade:
@staticmethod
def emit(event: TelemetryEvent, trace_manager: TraceQueueManager | None = None) -> None:
from core.ops.ops_trace_manager import TraceQueueManager, TraceTask, TraceTaskName
from core.ops.ops_trace_manager import TraceQueueManager, TraceTask
trace_task_name_map = {
"draft_node_execution": TraceTaskName.DRAFT_NODE_EXECUTION_TRACE,
"dataset_retrieval": TraceTaskName.DATASET_RETRIEVAL_TRACE,
"generate_name": TraceTaskName.GENERATE_NAME_TRACE,
"message": TraceTaskName.MESSAGE_TRACE,
"moderation": TraceTaskName.MODERATION_TRACE,
"node_execution": TraceTaskName.NODE_EXECUTION_TRACE,
"prompt_generation": TraceTaskName.PROMPT_GENERATION_TRACE,
"suggested_question": TraceTaskName.SUGGESTED_QUESTION_TRACE,
"tool": TraceTaskName.TOOL_TRACE,
"workflow": TraceTaskName.WORKFLOW_TRACE,
}
trace_task_name = trace_task_name_map.get(event.name)
if not trace_task_name:
if event.name not in _ENTERPRISE_ONLY_TRACES:
return
trace_queue_manager = trace_manager or TraceQueueManager(
@ -36,13 +31,13 @@ class TelemetryFacade:
)
trace_queue_manager.add_trace_task(
TraceTask(
trace_task_name,
event.name,
**event.payload,
)
)
def is_telemetry_enabled() -> bool:
def is_enterprise_telemetry_enabled() -> bool:
try:
from enterprise.telemetry.exporter import is_enterprise_telemetry_enabled
except Exception: