feat(sdk): add external sink - #175

Merged
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink
Apr 21, 2026
Merged

feat(sdk): add external sink#175
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink

Conversation

@namrataghadi-galileo

Copy link
Copy Markdown
Contributor

Summary

Added a vendor-neutral external control-event sink registration API in the Python SDK so integrations can receive finalized ControlExecutionEvent payloads without adding vendor-specific logic to OSS.

Updated observability delivery so observability_enabled is the master gate, and when enabled the SDK uses registered sinks if present, otherwise the existing default OSS batcher sink.

Added docs and test coverage for sink registration, override behavior, fallback to default delivery, and local/server/merged event delivery parity.

Scope

User-facing/API changes:

  • Added register_control_event_sink(...), unregister_control_event_sink(...), and get_registered_control_event_sinks(...) to the Python SDK public API
  • Documented how external integrations can register a sink

Internal changes:

  • Added active sink resolution in the SDK observability layer
  • Enforced observability_enabled before any sink delivery occurs
  • Preserved existing event construction flow for local, server, merged, and partial/error cases
  • Added mock-sink tests covering override semantics and delivery behavior

Out of scope:

  • Config-driven sink selection for SDK/server
  • OTEL sink implementation and configuration
  • Server-side sink resolution changes

Risk and Rollout

Risk level: medium

Rollback plan: Revert the SDK observability sink-resolution changes and public sink registration API additions in the Python SDK. Since default OSS delivery remains the fallback when no external sink is registered, rollback is isolated to the SDK observability layer.

Testing

  • Added or updated automated tests
  • Ran make check — focused validation was run instead. Full workspace checks were unnecessary for this scoped SDK change. There is also a pre-existing unrelated SDK typecheck issue around google.adk untyped imports.
  • Manually verified behavior

Checklist

  • Linked issue/spec (if applicable)
  • Updated docs/examples for user-facing changes
  • Included any required follow-up tasks

@codecov

codecovBot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
sdks/python/src/agent_control/observability.py94.59%2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@lan17lan17 left a comment

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.

I like the direction here, but I don't think this base PR is ready to merge yet. The main thing I'd fix first is the enabled= override becoming sticky process state. I also think we need an explicit lifecycle/ownership story for external sinks before we merge the base abstraction.

Comment threadsdks/python/src/agent_control/observability.py Outdated
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo

Copy link
Copy Markdown
ContributorAuthor

@lan17 Thanks, I agree those were the two biggest issues in the base abstraction.

I fixed the sticky enabled= behavior first. init_observability(enabled=...) no longer writes back into the process-global SDK settings, so it now behaves as a per-call override for that initialization path instead of poisoning later init/re-init flows for the rest of the process.

I also made the external-sink ownership model explicit. Caller-registered sinks are now documented as caller-owned: the SDK does not flush, close, or unregister them during shutdown, and shutdown_observability() only drains SDK-owned resources like the built-in batcher. I added regression coverage for both behaviors so the base API has a clear lifecycle boundary before we build on it further.

@lan17lan17 left a comment

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.

Looks good to me. The main issues from the earlier round were addressed, and the overall shape here makes sense. I left a couple of follow-up comments around fanout semantics and sink registration bookkeeping, but I don't think either one should hold this up.

Comment threadsdks/python/src/agent_control/observability.py
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo
namrataghadi-galileo merged commit 45f3645 into mainApr 21, 2026
6 checks passed
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/62793-add-external-sink branch April 21, 2026 22:29
galileo-automation pushed a commit that referenced this pull request Apr 22, 2026
## [2.4.0](ts-sdk-v2.3.0...ts-sdk-v2.4.0) (2026-04-22)
### Features
* **evaluators:** add built-in budget evaluator for per-agent cost tracking ([#144](#144)) ([d4ce113](d4ce113)), closes [#130](#130)
* **sdk:** add external sink ([#175](#175)) ([45f3645](45f3645))
* **server:** Align condition and template depth limits ([#166](#166)) ([03f402e](03f402e))
* **server:** Phase 0: add control version history and soft-delete unusable legacy controls ([#172](#172)) ([e5b2b33](e5b2b33))
### Bug Fixes
* **examples:** fix crewai examples ([#179](#179)) ([9004ea3](9004ea3))
@galileo-automation

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@namrataghadi-galileo@galileo-automation@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(sdk): add external sink - #175

Merged
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink
Apr 21, 2026
Merged

feat(sdk): add external sink#175
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink

Conversation

@namrataghadi-galileo

Copy link
Copy Markdown
Contributor

Summary

Added a vendor-neutral external control-event sink registration API in the Python SDK so integrations can receive finalized ControlExecutionEvent payloads without adding vendor-specific logic to OSS.

Updated observability delivery so observability_enabled is the master gate, and when enabled the SDK uses registered sinks if present, otherwise the existing default OSS batcher sink.

Added docs and test coverage for sink registration, override behavior, fallback to default delivery, and local/server/merged event delivery parity.

Scope

User-facing/API changes:

  • Added register_control_event_sink(...), unregister_control_event_sink(...), and get_registered_control_event_sinks(...) to the Python SDK public API
  • Documented how external integrations can register a sink

Internal changes:

  • Added active sink resolution in the SDK observability layer
  • Enforced observability_enabled before any sink delivery occurs
  • Preserved existing event construction flow for local, server, merged, and partial/error cases
  • Added mock-sink tests covering override semantics and delivery behavior

Out of scope:

  • Config-driven sink selection for SDK/server
  • OTEL sink implementation and configuration
  • Server-side sink resolution changes

Risk and Rollout

Risk level: medium

Rollback plan: Revert the SDK observability sink-resolution changes and public sink registration API additions in the Python SDK. Since default OSS delivery remains the fallback when no external sink is registered, rollback is isolated to the SDK observability layer.

Testing

  • Added or updated automated tests
  • Ran make check — focused validation was run instead. Full workspace checks were unnecessary for this scoped SDK change. There is also a pre-existing unrelated SDK typecheck issue around google.adk untyped imports.
  • Manually verified behavior

Checklist

  • Linked issue/spec (if applicable)
  • Updated docs/examples for user-facing changes
  • Included any required follow-up tasks

@codecov

codecovBot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
sdks/python/src/agent_control/observability.py94.59%2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@lan17lan17 left a comment

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.

I like the direction here, but I don't think this base PR is ready to merge yet. The main thing I'd fix first is the enabled= override becoming sticky process state. I also think we need an explicit lifecycle/ownership story for external sinks before we merge the base abstraction.

Comment threadsdks/python/src/agent_control/observability.py Outdated
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo

Copy link
Copy Markdown
ContributorAuthor

@lan17 Thanks, I agree those were the two biggest issues in the base abstraction.

I fixed the sticky enabled= behavior first. init_observability(enabled=...) no longer writes back into the process-global SDK settings, so it now behaves as a per-call override for that initialization path instead of poisoning later init/re-init flows for the rest of the process.

I also made the external-sink ownership model explicit. Caller-registered sinks are now documented as caller-owned: the SDK does not flush, close, or unregister them during shutdown, and shutdown_observability() only drains SDK-owned resources like the built-in batcher. I added regression coverage for both behaviors so the base API has a clear lifecycle boundary before we build on it further.

@lan17lan17 left a comment

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.

Looks good to me. The main issues from the earlier round were addressed, and the overall shape here makes sense. I left a couple of follow-up comments around fanout semantics and sink registration bookkeeping, but I don't think either one should hold this up.

Comment threadsdks/python/src/agent_control/observability.py
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo
namrataghadi-galileo merged commit 45f3645 into mainApr 21, 2026
6 checks passed
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/62793-add-external-sink branch April 21, 2026 22:29
galileo-automation pushed a commit that referenced this pull request Apr 22, 2026
## [2.4.0](ts-sdk-v2.3.0...ts-sdk-v2.4.0) (2026-04-22)
### Features
* **evaluators:** add built-in budget evaluator for per-agent cost tracking ([#144](#144)) ([d4ce113](d4ce113)), closes [#130](#130)
* **sdk:** add external sink ([#175](#175)) ([45f3645](45f3645))
* **server:** Align condition and template depth limits ([#166](#166)) ([03f402e](03f402e))
* **server:** Phase 0: add control version history and soft-delete unusable legacy controls ([#172](#172)) ([e5b2b33](e5b2b33))
### Bug Fixes
* **examples:** fix crewai examples ([#179](#179)) ([9004ea3](9004ea3))
@galileo-automation

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@namrataghadi-galileo@galileo-automation@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(sdk): add external sink - #175

Merged
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink
Apr 21, 2026
Merged

feat(sdk): add external sink#175
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink

Conversation

@namrataghadi-galileo

Copy link
Copy Markdown
Contributor

Summary

Added a vendor-neutral external control-event sink registration API in the Python SDK so integrations can receive finalized ControlExecutionEvent payloads without adding vendor-specific logic to OSS.

Updated observability delivery so observability_enabled is the master gate, and when enabled the SDK uses registered sinks if present, otherwise the existing default OSS batcher sink.

Added docs and test coverage for sink registration, override behavior, fallback to default delivery, and local/server/merged event delivery parity.

Scope

User-facing/API changes:

  • Added register_control_event_sink(...), unregister_control_event_sink(...), and get_registered_control_event_sinks(...) to the Python SDK public API
  • Documented how external integrations can register a sink

Internal changes:

  • Added active sink resolution in the SDK observability layer
  • Enforced observability_enabled before any sink delivery occurs
  • Preserved existing event construction flow for local, server, merged, and partial/error cases
  • Added mock-sink tests covering override semantics and delivery behavior

Out of scope:

  • Config-driven sink selection for SDK/server
  • OTEL sink implementation and configuration
  • Server-side sink resolution changes

Risk and Rollout

Risk level: medium

Rollback plan: Revert the SDK observability sink-resolution changes and public sink registration API additions in the Python SDK. Since default OSS delivery remains the fallback when no external sink is registered, rollback is isolated to the SDK observability layer.

Testing

  • Added or updated automated tests
  • Ran make check — focused validation was run instead. Full workspace checks were unnecessary for this scoped SDK change. There is also a pre-existing unrelated SDK typecheck issue around google.adk untyped imports.
  • Manually verified behavior

Checklist

  • Linked issue/spec (if applicable)
  • Updated docs/examples for user-facing changes
  • Included any required follow-up tasks

@codecov

codecovBot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
sdks/python/src/agent_control/observability.py94.59%2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@lan17lan17 left a comment

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.

I like the direction here, but I don't think this base PR is ready to merge yet. The main thing I'd fix first is the enabled= override becoming sticky process state. I also think we need an explicit lifecycle/ownership story for external sinks before we merge the base abstraction.

Comment threadsdks/python/src/agent_control/observability.py Outdated
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo

Copy link
Copy Markdown
ContributorAuthor

@lan17 Thanks, I agree those were the two biggest issues in the base abstraction.

I fixed the sticky enabled= behavior first. init_observability(enabled=...) no longer writes back into the process-global SDK settings, so it now behaves as a per-call override for that initialization path instead of poisoning later init/re-init flows for the rest of the process.

I also made the external-sink ownership model explicit. Caller-registered sinks are now documented as caller-owned: the SDK does not flush, close, or unregister them during shutdown, and shutdown_observability() only drains SDK-owned resources like the built-in batcher. I added regression coverage for both behaviors so the base API has a clear lifecycle boundary before we build on it further.

@lan17lan17 left a comment

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.

Looks good to me. The main issues from the earlier round were addressed, and the overall shape here makes sense. I left a couple of follow-up comments around fanout semantics and sink registration bookkeeping, but I don't think either one should hold this up.

Comment threadsdks/python/src/agent_control/observability.py
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo
namrataghadi-galileo merged commit 45f3645 into mainApr 21, 2026
6 checks passed
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/62793-add-external-sink branch April 21, 2026 22:29
galileo-automation pushed a commit that referenced this pull request Apr 22, 2026
## [2.4.0](ts-sdk-v2.3.0...ts-sdk-v2.4.0) (2026-04-22)
### Features
* **evaluators:** add built-in budget evaluator for per-agent cost tracking ([#144](#144)) ([d4ce113](d4ce113)), closes [#130](#130)
* **sdk:** add external sink ([#175](#175)) ([45f3645](45f3645))
* **server:** Align condition and template depth limits ([#166](#166)) ([03f402e](03f402e))
* **server:** Phase 0: add control version history and soft-delete unusable legacy controls ([#172](#172)) ([e5b2b33](e5b2b33))
### Bug Fixes
* **examples:** fix crewai examples ([#179](#179)) ([9004ea3](9004ea3))
@galileo-automation

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@namrataghadi-galileo@galileo-automation@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(sdk): add external sink - #175

Merged
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink
Apr 21, 2026
Merged

feat(sdk): add external sink#175
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink

Conversation

@namrataghadi-galileo

Copy link
Copy Markdown
Contributor

Summary

Added a vendor-neutral external control-event sink registration API in the Python SDK so integrations can receive finalized ControlExecutionEvent payloads without adding vendor-specific logic to OSS.

Updated observability delivery so observability_enabled is the master gate, and when enabled the SDK uses registered sinks if present, otherwise the existing default OSS batcher sink.

Added docs and test coverage for sink registration, override behavior, fallback to default delivery, and local/server/merged event delivery parity.

Scope

User-facing/API changes:

  • Added register_control_event_sink(...), unregister_control_event_sink(...), and get_registered_control_event_sinks(...) to the Python SDK public API
  • Documented how external integrations can register a sink

Internal changes:

  • Added active sink resolution in the SDK observability layer
  • Enforced observability_enabled before any sink delivery occurs
  • Preserved existing event construction flow for local, server, merged, and partial/error cases
  • Added mock-sink tests covering override semantics and delivery behavior

Out of scope:

  • Config-driven sink selection for SDK/server
  • OTEL sink implementation and configuration
  • Server-side sink resolution changes

Risk and Rollout

Risk level: medium

Rollback plan: Revert the SDK observability sink-resolution changes and public sink registration API additions in the Python SDK. Since default OSS delivery remains the fallback when no external sink is registered, rollback is isolated to the SDK observability layer.

Testing

  • Added or updated automated tests
  • Ran make check — focused validation was run instead. Full workspace checks were unnecessary for this scoped SDK change. There is also a pre-existing unrelated SDK typecheck issue around google.adk untyped imports.
  • Manually verified behavior

Checklist

  • Linked issue/spec (if applicable)
  • Updated docs/examples for user-facing changes
  • Included any required follow-up tasks

@codecov

codecovBot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
sdks/python/src/agent_control/observability.py94.59%2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@lan17lan17 left a comment

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.

I like the direction here, but I don't think this base PR is ready to merge yet. The main thing I'd fix first is the enabled= override becoming sticky process state. I also think we need an explicit lifecycle/ownership story for external sinks before we merge the base abstraction.

Comment threadsdks/python/src/agent_control/observability.py Outdated
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo

Copy link
Copy Markdown
ContributorAuthor

@lan17 Thanks, I agree those were the two biggest issues in the base abstraction.

I fixed the sticky enabled= behavior first. init_observability(enabled=...) no longer writes back into the process-global SDK settings, so it now behaves as a per-call override for that initialization path instead of poisoning later init/re-init flows for the rest of the process.

I also made the external-sink ownership model explicit. Caller-registered sinks are now documented as caller-owned: the SDK does not flush, close, or unregister them during shutdown, and shutdown_observability() only drains SDK-owned resources like the built-in batcher. I added regression coverage for both behaviors so the base API has a clear lifecycle boundary before we build on it further.

@lan17lan17 left a comment

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.

Looks good to me. The main issues from the earlier round were addressed, and the overall shape here makes sense. I left a couple of follow-up comments around fanout semantics and sink registration bookkeeping, but I don't think either one should hold this up.

Comment threadsdks/python/src/agent_control/observability.py
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo
namrataghadi-galileo merged commit 45f3645 into mainApr 21, 2026
6 checks passed
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/62793-add-external-sink branch April 21, 2026 22:29
galileo-automation pushed a commit that referenced this pull request Apr 22, 2026
## [2.4.0](ts-sdk-v2.3.0...ts-sdk-v2.4.0) (2026-04-22)
### Features
* **evaluators:** add built-in budget evaluator for per-agent cost tracking ([#144](#144)) ([d4ce113](d4ce113)), closes [#130](#130)
* **sdk:** add external sink ([#175](#175)) ([45f3645](45f3645))
* **server:** Align condition and template depth limits ([#166](#166)) ([03f402e](03f402e))
* **server:** Phase 0: add control version history and soft-delete unusable legacy controls ([#172](#172)) ([e5b2b33](e5b2b33))
### Bug Fixes
* **examples:** fix crewai examples ([#179](#179)) ([9004ea3](9004ea3))
@galileo-automation

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@namrataghadi-galileo@galileo-automation@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(sdk): add external sink - #175

Merged
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink
Apr 21, 2026
Merged

feat(sdk): add external sink#175
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink

Conversation

@namrataghadi-galileo

Copy link
Copy Markdown
Contributor

Summary

Added a vendor-neutral external control-event sink registration API in the Python SDK so integrations can receive finalized ControlExecutionEvent payloads without adding vendor-specific logic to OSS.

Updated observability delivery so observability_enabled is the master gate, and when enabled the SDK uses registered sinks if present, otherwise the existing default OSS batcher sink.

Added docs and test coverage for sink registration, override behavior, fallback to default delivery, and local/server/merged event delivery parity.

Scope

User-facing/API changes:

  • Added register_control_event_sink(...), unregister_control_event_sink(...), and get_registered_control_event_sinks(...) to the Python SDK public API
  • Documented how external integrations can register a sink

Internal changes:

  • Added active sink resolution in the SDK observability layer
  • Enforced observability_enabled before any sink delivery occurs
  • Preserved existing event construction flow for local, server, merged, and partial/error cases
  • Added mock-sink tests covering override semantics and delivery behavior

Out of scope:

  • Config-driven sink selection for SDK/server
  • OTEL sink implementation and configuration
  • Server-side sink resolution changes

Risk and Rollout

Risk level: medium

Rollback plan: Revert the SDK observability sink-resolution changes and public sink registration API additions in the Python SDK. Since default OSS delivery remains the fallback when no external sink is registered, rollback is isolated to the SDK observability layer.

Testing

  • Added or updated automated tests
  • Ran make check — focused validation was run instead. Full workspace checks were unnecessary for this scoped SDK change. There is also a pre-existing unrelated SDK typecheck issue around google.adk untyped imports.
  • Manually verified behavior

Checklist

  • Linked issue/spec (if applicable)
  • Updated docs/examples for user-facing changes
  • Included any required follow-up tasks

@codecov

codecovBot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
sdks/python/src/agent_control/observability.py94.59%2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@lan17lan17 left a comment

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.

I like the direction here, but I don't think this base PR is ready to merge yet. The main thing I'd fix first is the enabled= override becoming sticky process state. I also think we need an explicit lifecycle/ownership story for external sinks before we merge the base abstraction.

Comment threadsdks/python/src/agent_control/observability.py Outdated
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo

Copy link
Copy Markdown
ContributorAuthor

@lan17 Thanks, I agree those were the two biggest issues in the base abstraction.

I fixed the sticky enabled= behavior first. init_observability(enabled=...) no longer writes back into the process-global SDK settings, so it now behaves as a per-call override for that initialization path instead of poisoning later init/re-init flows for the rest of the process.

I also made the external-sink ownership model explicit. Caller-registered sinks are now documented as caller-owned: the SDK does not flush, close, or unregister them during shutdown, and shutdown_observability() only drains SDK-owned resources like the built-in batcher. I added regression coverage for both behaviors so the base API has a clear lifecycle boundary before we build on it further.

@lan17lan17 left a comment

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.

Looks good to me. The main issues from the earlier round were addressed, and the overall shape here makes sense. I left a couple of follow-up comments around fanout semantics and sink registration bookkeeping, but I don't think either one should hold this up.

Comment threadsdks/python/src/agent_control/observability.py
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo
namrataghadi-galileo merged commit 45f3645 into mainApr 21, 2026
6 checks passed
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/62793-add-external-sink branch April 21, 2026 22:29
galileo-automation pushed a commit that referenced this pull request Apr 22, 2026
## [2.4.0](ts-sdk-v2.3.0...ts-sdk-v2.4.0) (2026-04-22)
### Features
* **evaluators:** add built-in budget evaluator for per-agent cost tracking ([#144](#144)) ([d4ce113](d4ce113)), closes [#130](#130)
* **sdk:** add external sink ([#175](#175)) ([45f3645](45f3645))
* **server:** Align condition and template depth limits ([#166](#166)) ([03f402e](03f402e))
* **server:** Phase 0: add control version history and soft-delete unusable legacy controls ([#172](#172)) ([e5b2b33](e5b2b33))
### Bug Fixes
* **examples:** fix crewai examples ([#179](#179)) ([9004ea3](9004ea3))
@galileo-automation

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@namrataghadi-galileo@galileo-automation@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(sdk): add external sink - #175

Merged
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink
Apr 21, 2026
Merged

feat(sdk): add external sink#175
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink

Conversation

@namrataghadi-galileo

Copy link
Copy Markdown
Contributor

Summary

Added a vendor-neutral external control-event sink registration API in the Python SDK so integrations can receive finalized ControlExecutionEvent payloads without adding vendor-specific logic to OSS.

Updated observability delivery so observability_enabled is the master gate, and when enabled the SDK uses registered sinks if present, otherwise the existing default OSS batcher sink.

Added docs and test coverage for sink registration, override behavior, fallback to default delivery, and local/server/merged event delivery parity.

Scope

User-facing/API changes:

  • Added register_control_event_sink(...), unregister_control_event_sink(...), and get_registered_control_event_sinks(...) to the Python SDK public API
  • Documented how external integrations can register a sink

Internal changes:

  • Added active sink resolution in the SDK observability layer
  • Enforced observability_enabled before any sink delivery occurs
  • Preserved existing event construction flow for local, server, merged, and partial/error cases
  • Added mock-sink tests covering override semantics and delivery behavior

Out of scope:

  • Config-driven sink selection for SDK/server
  • OTEL sink implementation and configuration
  • Server-side sink resolution changes

Risk and Rollout

Risk level: medium

Rollback plan: Revert the SDK observability sink-resolution changes and public sink registration API additions in the Python SDK. Since default OSS delivery remains the fallback when no external sink is registered, rollback is isolated to the SDK observability layer.

Testing

  • Added or updated automated tests
  • Ran make check — focused validation was run instead. Full workspace checks were unnecessary for this scoped SDK change. There is also a pre-existing unrelated SDK typecheck issue around google.adk untyped imports.
  • Manually verified behavior

Checklist

  • Linked issue/spec (if applicable)
  • Updated docs/examples for user-facing changes
  • Included any required follow-up tasks

@codecov

codecovBot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
sdks/python/src/agent_control/observability.py94.59%2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@lan17lan17 left a comment

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.

I like the direction here, but I don't think this base PR is ready to merge yet. The main thing I'd fix first is the enabled= override becoming sticky process state. I also think we need an explicit lifecycle/ownership story for external sinks before we merge the base abstraction.

Comment threadsdks/python/src/agent_control/observability.py Outdated
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo

Copy link
Copy Markdown
ContributorAuthor

@lan17 Thanks, I agree those were the two biggest issues in the base abstraction.

I fixed the sticky enabled= behavior first. init_observability(enabled=...) no longer writes back into the process-global SDK settings, so it now behaves as a per-call override for that initialization path instead of poisoning later init/re-init flows for the rest of the process.

I also made the external-sink ownership model explicit. Caller-registered sinks are now documented as caller-owned: the SDK does not flush, close, or unregister them during shutdown, and shutdown_observability() only drains SDK-owned resources like the built-in batcher. I added regression coverage for both behaviors so the base API has a clear lifecycle boundary before we build on it further.

@lan17lan17 left a comment

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.

Looks good to me. The main issues from the earlier round were addressed, and the overall shape here makes sense. I left a couple of follow-up comments around fanout semantics and sink registration bookkeeping, but I don't think either one should hold this up.

Comment threadsdks/python/src/agent_control/observability.py
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo
namrataghadi-galileo merged commit 45f3645 into mainApr 21, 2026
6 checks passed
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/62793-add-external-sink branch April 21, 2026 22:29
galileo-automation pushed a commit that referenced this pull request Apr 22, 2026
## [2.4.0](ts-sdk-v2.3.0...ts-sdk-v2.4.0) (2026-04-22)
### Features
* **evaluators:** add built-in budget evaluator for per-agent cost tracking ([#144](#144)) ([d4ce113](d4ce113)), closes [#130](#130)
* **sdk:** add external sink ([#175](#175)) ([45f3645](45f3645))
* **server:** Align condition and template depth limits ([#166](#166)) ([03f402e](03f402e))
* **server:** Phase 0: add control version history and soft-delete unusable legacy controls ([#172](#172)) ([e5b2b33](e5b2b33))
### Bug Fixes
* **examples:** fix crewai examples ([#179](#179)) ([9004ea3](9004ea3))
@galileo-automation

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@namrataghadi-galileo@galileo-automation@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(sdk): add external sink - #175

Merged
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink
Apr 21, 2026
Merged

feat(sdk): add external sink#175
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink

Conversation

@namrataghadi-galileo

Copy link
Copy Markdown
Contributor

Summary

Added a vendor-neutral external control-event sink registration API in the Python SDK so integrations can receive finalized ControlExecutionEvent payloads without adding vendor-specific logic to OSS.

Updated observability delivery so observability_enabled is the master gate, and when enabled the SDK uses registered sinks if present, otherwise the existing default OSS batcher sink.

Added docs and test coverage for sink registration, override behavior, fallback to default delivery, and local/server/merged event delivery parity.

Scope

User-facing/API changes:

  • Added register_control_event_sink(...), unregister_control_event_sink(...), and get_registered_control_event_sinks(...) to the Python SDK public API
  • Documented how external integrations can register a sink

Internal changes:

  • Added active sink resolution in the SDK observability layer
  • Enforced observability_enabled before any sink delivery occurs
  • Preserved existing event construction flow for local, server, merged, and partial/error cases
  • Added mock-sink tests covering override semantics and delivery behavior

Out of scope:

  • Config-driven sink selection for SDK/server
  • OTEL sink implementation and configuration
  • Server-side sink resolution changes

Risk and Rollout

Risk level: medium

Rollback plan: Revert the SDK observability sink-resolution changes and public sink registration API additions in the Python SDK. Since default OSS delivery remains the fallback when no external sink is registered, rollback is isolated to the SDK observability layer.

Testing

  • Added or updated automated tests
  • Ran make check — focused validation was run instead. Full workspace checks were unnecessary for this scoped SDK change. There is also a pre-existing unrelated SDK typecheck issue around google.adk untyped imports.
  • Manually verified behavior

Checklist

  • Linked issue/spec (if applicable)
  • Updated docs/examples for user-facing changes
  • Included any required follow-up tasks

@codecov

codecovBot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
sdks/python/src/agent_control/observability.py94.59%2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@lan17lan17 left a comment

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.

I like the direction here, but I don't think this base PR is ready to merge yet. The main thing I'd fix first is the enabled= override becoming sticky process state. I also think we need an explicit lifecycle/ownership story for external sinks before we merge the base abstraction.

Comment threadsdks/python/src/agent_control/observability.py Outdated
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo

Copy link
Copy Markdown
ContributorAuthor

@lan17 Thanks, I agree those were the two biggest issues in the base abstraction.

I fixed the sticky enabled= behavior first. init_observability(enabled=...) no longer writes back into the process-global SDK settings, so it now behaves as a per-call override for that initialization path instead of poisoning later init/re-init flows for the rest of the process.

I also made the external-sink ownership model explicit. Caller-registered sinks are now documented as caller-owned: the SDK does not flush, close, or unregister them during shutdown, and shutdown_observability() only drains SDK-owned resources like the built-in batcher. I added regression coverage for both behaviors so the base API has a clear lifecycle boundary before we build on it further.

@lan17lan17 left a comment

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.

Looks good to me. The main issues from the earlier round were addressed, and the overall shape here makes sense. I left a couple of follow-up comments around fanout semantics and sink registration bookkeeping, but I don't think either one should hold this up.

Comment threadsdks/python/src/agent_control/observability.py
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo
namrataghadi-galileo merged commit 45f3645 into mainApr 21, 2026
6 checks passed
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/62793-add-external-sink branch April 21, 2026 22:29
galileo-automation pushed a commit that referenced this pull request Apr 22, 2026
## [2.4.0](ts-sdk-v2.3.0...ts-sdk-v2.4.0) (2026-04-22)
### Features
* **evaluators:** add built-in budget evaluator for per-agent cost tracking ([#144](#144)) ([d4ce113](d4ce113)), closes [#130](#130)
* **sdk:** add external sink ([#175](#175)) ([45f3645](45f3645))
* **server:** Align condition and template depth limits ([#166](#166)) ([03f402e](03f402e))
* **server:** Phase 0: add control version history and soft-delete unusable legacy controls ([#172](#172)) ([e5b2b33](e5b2b33))
### Bug Fixes
* **examples:** fix crewai examples ([#179](#179)) ([9004ea3](9004ea3))
@galileo-automation

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@namrataghadi-galileo@galileo-automation@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(sdk): add external sink - #175

Merged
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink
Apr 21, 2026
Merged

feat(sdk): add external sink#175
namrataghadi-galileo merged 17 commits into
mainfrom
feature/62793-add-external-sink

Conversation

@namrataghadi-galileo

Copy link
Copy Markdown
Contributor

Summary

Added a vendor-neutral external control-event sink registration API in the Python SDK so integrations can receive finalized ControlExecutionEvent payloads without adding vendor-specific logic to OSS.

Updated observability delivery so observability_enabled is the master gate, and when enabled the SDK uses registered sinks if present, otherwise the existing default OSS batcher sink.

Added docs and test coverage for sink registration, override behavior, fallback to default delivery, and local/server/merged event delivery parity.

Scope

User-facing/API changes:

  • Added register_control_event_sink(...), unregister_control_event_sink(...), and get_registered_control_event_sinks(...) to the Python SDK public API
  • Documented how external integrations can register a sink

Internal changes:

  • Added active sink resolution in the SDK observability layer
  • Enforced observability_enabled before any sink delivery occurs
  • Preserved existing event construction flow for local, server, merged, and partial/error cases
  • Added mock-sink tests covering override semantics and delivery behavior

Out of scope:

  • Config-driven sink selection for SDK/server
  • OTEL sink implementation and configuration
  • Server-side sink resolution changes

Risk and Rollout

Risk level: medium

Rollback plan: Revert the SDK observability sink-resolution changes and public sink registration API additions in the Python SDK. Since default OSS delivery remains the fallback when no external sink is registered, rollback is isolated to the SDK observability layer.

Testing

  • Added or updated automated tests
  • Ran make check — focused validation was run instead. Full workspace checks were unnecessary for this scoped SDK change. There is also a pre-existing unrelated SDK typecheck issue around google.adk untyped imports.
  • Manually verified behavior

Checklist

  • Linked issue/spec (if applicable)
  • Updated docs/examples for user-facing changes
  • Included any required follow-up tasks

@codecov

codecovBot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
sdks/python/src/agent_control/observability.py94.59%2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@lan17lan17 left a comment

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.

I like the direction here, but I don't think this base PR is ready to merge yet. The main thing I'd fix first is the enabled= override becoming sticky process state. I also think we need an explicit lifecycle/ownership story for external sinks before we merge the base abstraction.

Comment threadsdks/python/src/agent_control/observability.py Outdated
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo

Copy link
Copy Markdown
ContributorAuthor

@lan17 Thanks, I agree those were the two biggest issues in the base abstraction.

I fixed the sticky enabled= behavior first. init_observability(enabled=...) no longer writes back into the process-global SDK settings, so it now behaves as a per-call override for that initialization path instead of poisoning later init/re-init flows for the rest of the process.

I also made the external-sink ownership model explicit. Caller-registered sinks are now documented as caller-owned: the SDK does not flush, close, or unregister them during shutdown, and shutdown_observability() only drains SDK-owned resources like the built-in batcher. I added regression coverage for both behaviors so the base API has a clear lifecycle boundary before we build on it further.

@lan17lan17 left a comment

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.

Looks good to me. The main issues from the earlier round were addressed, and the overall shape here makes sense. I left a couple of follow-up comments around fanout semantics and sink registration bookkeeping, but I don't think either one should hold this up.

Comment threadsdks/python/src/agent_control/observability.py
Comment threadsdks/python/src/agent_control/observability.py
@namrataghadi-galileo
namrataghadi-galileo merged commit 45f3645 into mainApr 21, 2026
6 checks passed
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/62793-add-external-sink branch April 21, 2026 22:29
galileo-automation pushed a commit that referenced this pull request Apr 22, 2026
## [2.4.0](ts-sdk-v2.3.0...ts-sdk-v2.4.0) (2026-04-22)
### Features
* **evaluators:** add built-in budget evaluator for per-agent cost tracking ([#144](#144)) ([d4ce113](d4ce113)), closes [#130](#130)
* **sdk:** add external sink ([#175](#175)) ([45f3645](45f3645))
* **server:** Align condition and template depth limits ([#166](#166)) ([03f402e](03f402e))
* **server:** Phase 0: add control version history and soft-delete unusable legacy controls ([#172](#172)) ([e5b2b33](e5b2b33))
### Bug Fixes
* **examples:** fix crewai examples ([#179](#179)) ([9004ea3](9004ea3))
@galileo-automation

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@namrataghadi-galileo@galileo-automation@lan17