Skip to content

feat(sdk): 59787 merge events#155

Open
namrataghadi-galileo wants to merge 12 commits intomainfrom
feature/59787-merge-events
Open

feat(sdk): 59787 merge events#155
namrataghadi-galileo wants to merge 12 commits intomainfrom
feature/59787-merge-events

Conversation

@namrataghadi-galileo
Copy link
Copy Markdown
Contributor

Summary

  • add an optional SDK-side merged-event path so local and server ControlExecutionEvents can be emitted together after evaluation
  • keep event generation separate from evaluation by reconstructing events after evaluation results are available, instead of sending full event payloads over the wire
  • reduce wire transfer for the merged path by keeping server evaluation responses focused on evaluation semantics and reconstructing server events in the SDK
  • preserve event identity consistency during reconstruction by using ControlDefinition.observability_identity() for composite conditions
  • add a merged-event sink interface in the SDK and a server-side merge-mode signal so the SDK can become the final emitter when needed

Behavior

  • default behavior remains unchanged: local events are reconstructed in the SDK and enqueued through the existing SDK observability pipeline, while server-side evaluation still builds and ingests its own events on the server
  • when a control event sink is registered, the SDK switches to the merged-event path: it reconstructs local and server events after evaluation, sends X-Agent-Control-Merge-Events: true to the server, and emits one merged batch through the registered sink
  • server-side merge mode skips final server ingestion but still returns the same evaluation semantics, so the SDK can merge results and reconstruct the final event batch locally
  • trace/span correlation is preserved in both paths through the tracing resolver and reconstructed control metadata

What changed

  • added shared SDK event reconstruction helpers in evaluation_events.py
  • updated evaluation.py to support:
  • local reconstruction + default enqueue path
  • optional merged-event sink path
  • SDK reconstruction of server events from the lean server response plus cached control definitions
  • added merged-event sink registration helpers in event_sink.py
  • updated server evaluation.py so the server:
  • still ingests events in the default path
  • skips final ingestion when X-Agent-Control-Merge-Events: true is set

Testing

  • SDK and server tests were updated to cover:
  • default local enqueue behavior
  • merged-event emission behavior
  • provider-backed trace/span propagation
  • server merge-mode skip-ingest behavior

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 31, 2026

Codecov Report

❌ Patch coverage is 92.94872% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
sdks/python/src/agent_control/evaluation.py 91.66% 6 Missing ⚠️
sdks/python/src/agent_control/evaluation_events.py 89.79% 5 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@lan17 lan17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are still two blockers before we merge this. I left inline notes on the partial sink integration and the unconditional reconstruction work on the evaluation hot path.

)


async def check_evaluation(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still leaves the merged-event path only partially integrated across the public SDK surface. If a caller registers set_control_event_sink() but uses the public check_evaluation() helper, we just POST and return the parsed result without setting X-Agent-Control-Merge-Events or reconstructing/emitting any events. I think we need to either wire the sink behavior through this helper too, or explicitly scope the sink API away from this entry point.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — you’re right. check_evaluation() currently bypasses the merged-event path even when a control event sink is registered, which makes the public SDK surface inconsistent. I think the right fix is to wire the sink behavior through this helper as well: when a sink is present, resolve trace/span IDs, send X-Agent-Control-Merge-Events: true, reconstruct the server events from the lightweight response plus cached control definitions, and emit them through the sink before returning the parsed result. That keeps the default behavior unchanged while making merged-event handling consistent across both public evaluation entry points.

local_control_lookup = {
control.id: control.control for control in applicable_local_controls
}
local_events = build_control_execution_events(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid reconstructing events unless something will actually consume them? We now always build local events here, and we do the same for server events later, even when merged emission is off and even when OSS observability is disabled. Since evaluation is a latency-sensitive hot path, I don't think we should pay this parsing/allocation cost unless either the existing observability path is active or a merged-event sink is registered.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure .

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m tightening the hot path so we only reconstruct events when they’ll actually be consumed: local events only when the default SDK observability path is enabled or a sink is registered, and server events only when the merged-event path is active.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants