1919from .client import AgentControlClient
2020from .evaluation_events import build_control_execution_events , enqueue_observability_events
2121from .observability import is_observability_enabled
22+ from .telemetry .event_sink import emit_control_events
2223from .tracing import get_trace_and_span_ids
2324from .validation import ensure_agent_name
2425
@@ -116,6 +117,19 @@ def _has_applicable_prefiltered_server_controls(
116117 )
117118
118119
120+ def _has_merged_event_delivery () -> bool :
121+ """Return whether merged event delivery has an active downstream consumer.
122+
123+ Args:
124+ None.
125+
126+ Returns:
127+ ``True`` when observability is enabled, which is the prerequisite for
128+ merged event creation and final delivery.
129+ """
130+ return is_observability_enabled ()
131+
132+
119133def _is_merged_event_mode_enabled (
120134 agent_name : str ,
121135 client : AgentControlClient | None = None ,
@@ -124,8 +138,8 @@ def _is_merged_event_mode_enabled(
124138
125139 Merged event creation is a session-scoped option that depends on
126140 initialized SDK state: an initialized agent, cached server controls for the
127- same agent, and observability enabled so the merged batch will actually be
128- consumed by the built-in queue/Postgres path.
141+ same agent, observability enabled, and a valid initialized session so the
142+ merged batch can be delivered safely through the configured ingestion path.
129143
130144 Args:
131145 agent_name: Normalized agent name for the current request.
@@ -135,9 +149,9 @@ def _is_merged_event_mode_enabled(
135149
136150 Returns:
137151 ``True`` when the current SDK session has enough state to reconstruct
138- and enqueue merged events safely.
152+ and deliver merged events safely.
139153 """
140- if not state .merge_events or not is_observability_enabled ():
154+ if not state .merge_events or not _has_merged_event_delivery ():
141155 return False
142156
143157 current_agent = state .current_agent
@@ -216,8 +230,10 @@ async def check_evaluation(
216230 also participate in merged event creation when the initialized SDK session
217231 has ``merge_events`` enabled. In that mode, the SDK asks the server to skip
218232 final event ingestion, reconstructs server events from the lightweight
219- response, and enqueues them through the existing observability pipeline
220- before returning the parsed result.
233+ response, and delivers them before returning the parsed result. Final
234+ delivery uses the configured event ingestion path: the existing built-in
235+ queue by default or a registered custom sink. Event creation and delivery
236+ are both gated on observability being enabled.
221237
222238 Args:
223239 client: Configured AgentControl client.
@@ -268,7 +284,7 @@ async def check_evaluation(
268284 span_id ,
269285 normalized_name ,
270286 )
271- enqueue_observability_events (server_events )
287+ emit_control_events (server_events )
272288
273289 return cast (EvaluationResult , EvaluationResult .from_dict (evaluation_response .model_dump ()))
274290
@@ -290,10 +306,11 @@ async def check_evaluation_with_local(
290306 - default behavior: local events are reconstructed and queued immediately in
291307 the SDK, while server-side events are still emitted by the server
292308 - merged-event behavior: local and server events are reconstructed in the
293- SDK and enqueued once through the built-in observability path
309+ SDK and delivered once through the configured event ingestion path
294310
295311 In both cases, the evaluation result itself stays lightweight and event
296- reconstruction happens after evaluation completes.
312+ reconstruction happens after evaluation completes. Event creation and
313+ delivery are both gated on observability being enabled.
297314
298315 Args:
299316 client: Configured AgentControl client.
@@ -420,7 +437,7 @@ def _with_parse_errors(result: EvaluationResult) -> EvaluationResult:
420437 if not local_result .is_safe :
421438 result = _with_parse_errors (EvaluationResult .model_validate (local_result .model_dump ()))
422439 if merged_emission_enabled :
423- enqueue_observability_events (local_events )
440+ emit_control_events (local_events )
424441 return result
425442
426443 if _has_applicable_prefiltered_server_controls (server_control_payloads , request ):
@@ -455,18 +472,18 @@ def _with_parse_errors(result: EvaluationResult) -> EvaluationResult:
455472 if local_result is not None :
456473 result = _with_parse_errors (_merge_results (local_result , server_result ))
457474 if merged_emission_enabled :
458- enqueue_observability_events (local_events + server_events )
475+ emit_control_events (local_events + server_events )
459476 return result
460477
461478 result = _with_parse_errors (EvaluationResult .model_validate (server_result .model_dump ()))
462479 if merged_emission_enabled :
463- enqueue_observability_events (server_events )
480+ emit_control_events (server_events )
464481 return result
465482
466483 if local_result is not None :
467484 result = _with_parse_errors (EvaluationResult .model_validate (local_result .model_dump ()))
468485 if merged_emission_enabled :
469- enqueue_observability_events (local_events )
486+ emit_control_events (local_events )
470487 return result
471488
472489 return _with_parse_errors (EvaluationResult (is_safe = True , confidence = 1.0 ))
0 commit comments