Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
afe9246
Add telemetry package to support sinks
namrataghadi-galileo Apr 5, 2026
8b57ca7
add telemetry to server docker
namrataghadi-galileo Apr 5, 2026
92cee53
make defauteventingestor use ControlEventSink
namrataghadi-galileo Apr 5, 2026
d8b10aa
add telemetry README
namrataghadi-galileo Apr 6, 2026
efd93b0
merge from add telemetry sink
namrataghadi-galileo Apr 6, 2026
946fb5a
merge from master
namrataghadi-galileo Apr 7, 2026
2c623e5
merge from main
namrataghadi-galileo Apr 7, 2026
6d6cced
Merge branch 'main' into feature/61574-refactor-server-ingestion-to-sink
namrataghadi-galileo Apr 7, 2026
9025e45
type error
namrataghadi-galileo Apr 7, 2026
d93564b
coverage
namrataghadi-galileo Apr 7, 2026
d51558c
address comment
namrataghadi-galileo Apr 14, 2026
9a36ae5
Merge branch 'main' into feature/61574-refactor-server-ingestion-to-sink
namrataghadi-galileo Apr 16, 2026
c2e9f05
add external sink
namrataghadi-galileo Apr 16, 2026
04729cc
Merge branch 'main' into feature/62793-add-external-sink
namrataghadi-galileo Apr 16, 2026
4d8c853
adding config
namrataghadi-galileo Apr 16, 2026
3fe0694
fix some p1s
namrataghadi-galileo Apr 16, 2026
f7b75ea
fix regressions
namrataghadi-galileo Apr 16, 2026
3531e48
fix regressions
namrataghadi-galileo Apr 16, 2026
c361242
fix issues
namrataghadi-galileo Apr 16, 2026
9759fbc
mypy
namrataghadi-galileo Apr 16, 2026
2a2b976
add otel sink
namrataghadi-galileo Apr 16, 2026
9232988
add otel sink
namrataghadi-galileo Apr 16, 2026
365accb
address comments
namrataghadi-galileo Apr 19, 2026
39deee7
address merge conflict
namrataghadi-galileo May 3, 2026
7b54a8d
make sure to get traget from provider
namrataghadi-galileo May 3, 2026
d1de509
Merge branch 'main' into feature/62792-add-agent-control-brigde-for-l…
namrataghadi-galileo May 5, 2026
5d44184
Merge branch 'main' into feature/61578-add-otel-support
namrataghadi-galileo May 6, 2026
1a11ad7
Merge branch 'feature/62792-add-agent-control-brigde-for-logger' into…
namrataghadi-galileo May 6, 2026
e8be30e
fix ci adn merge from main
namrataghadi-galileo May 6, 2026
cddebb1
address comment
namrataghadi-galileo May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,61 @@ if __name__ == "__main__":

Use `agent_control.shutdown()` or `await agent_control.ashutdown()` before process exit so short-lived scripts flush pending observability events cleanly.

External integrations can register a sink for the same finalized
control-event payloads and then select it through observability config:

```python
from agent_control import (
register_control_event_sink,
unregister_control_event_sink,
)
from agent_control_telemetry import BaseControlEventSink, SinkResult


class MyControlEventSink(BaseControlEventSink):
def write_events(self, events):
for event in events:
forward_to_external_system(event.model_dump(mode="json"))
return SinkResult(accepted=len(events), dropped=0)


sink = MyControlEventSink()
register_control_event_sink(sink)

# Select registered sinks instead of the default SDK -> server sink.
agent_control.init(
agent_name="awesome_bot_3000",
observability_enabled=True,
observability_sink_name="registered",
)

# Later, when tearing down the integration:
unregister_control_event_sink(sink)
```

Registered sinks receive the same local, server, and merged control-execution
events the SDK emits through its normal event-construction flow. The default
SDK sink remains the OSS path to the Agent Control server. To use registered
or named custom sinks, set `observability_sink_name` explicitly.

The SDK also includes a built-in OpenTelemetry sink. Install the OTEL extra,
select the `otel` sink, and configure the OTLP exporter through Agent Control
settings or environment variables:

```bash
uv pip install "agent-control-sdk[otel]"
export AGENT_CONTROL_OBSERVABILITY_SINK_NAME=otel
export AGENT_CONTROL_OTEL_ENABLED=true
export AGENT_CONTROL_OTEL_ENDPOINT=http://localhost:4318/v1/traces
export AGENT_CONTROL_OTEL_HEADERS='{"authorization":"Bearer demo-token"}'
export AGENT_CONTROL_OTEL_SERVICE_NAME=awesome-bot
```

If the `otel` sink is selected without an OTLP endpoint/exporter configured,
the OTEL path stays inert and the default OSS SDK-to-server behavior still
remains unchanged unless `observability_sink_name` is explicitly switched away
from `default`.

Next, create a control in Step 4, then run the setup and agent scripts in
order to see blocking in action.

Expand Down
5 changes: 5 additions & 0 deletions sdks/python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ Repository = "https://github.com/yourusername/agent-control"
strands-agents = ["strands-agents>=1.26.0"]
google-adk = ["google-adk>=1.0.0"]
galileo = ["agent-control-evaluator-galileo>=3.0.0"]
otel = [
"opentelemetry-api>=1.24.0",
"opentelemetry-sdk>=1.24.0",
"opentelemetry-exporter-otlp-proto-http>=1.24.0",
]

[dependency-groups]
dev = [
Expand Down
22 changes: 22 additions & 0 deletions sdks/python/src/agent_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ async def handle_input(user_message: str) -> str:
EvaluationResult,
EvaluatorResult,
EvaluatorSpec,
JSONObject,
Step,
StepSchema,
TemplateControlInput,
Expand Down Expand Up @@ -98,13 +99,20 @@ async def handle_input(user_message: str) -> str:
get_event_sink,
get_log_config,
get_logger,
get_registered_control_event_sink_factory_names,
get_registered_control_event_sinks,
init_observability,
is_observability_enabled,
log_control_evaluation,
register_control_event_sink,
register_control_event_sink_factory,
shutdown_observability,
sync_shutdown_observability,
unregister_control_event_sink,
unregister_control_event_sink_factory,
write_events,
)
from .otel_sink import control_event_to_otel_span
from .tracing import (
get_current_span_id,
get_current_trace_id,
Expand Down Expand Up @@ -412,6 +420,8 @@ def init(
steps: list[StepSchemaDict] | None = None,
conflict_mode: Literal["strict", "overwrite"] = "overwrite",
observability_enabled: bool | None = None,
observability_sink_name: str | None = None,
observability_sink_config: JSONObject | None = None,
log_config: dict[str, Any] | None = None,
policy_refresh_interval_seconds: int = 60,
**kwargs: object
Expand Down Expand Up @@ -440,6 +450,9 @@ def init(
conflict_mode: Conflict handling mode for initAgent registration.
Defaults to "overwrite" in SDK flows.
observability_enabled: Optional bool to enable/disable observability (defaults to env var)
observability_sink_name: Optional sink selection name. Use "default" to preserve
SDK -> server OSS delivery or "registered" / a named sink factory for custom sinks.
observability_sink_config: Optional JSON config payload for the selected sink.
log_config: Optional logging configuration dict:
{"enabled": True, "span_start": True, "span_end": True, "control_eval": True}
policy_refresh_interval_seconds: Interval for background policy refresh loop.
Expand Down Expand Up @@ -633,6 +646,8 @@ def run_in_thread() -> None:
server_url=state.server_url,
api_key=state.api_key,
enabled=observability_enabled,
sink_name=observability_sink_name,
sink_config=observability_sink_config,
)
if batcher:
logger.info("Observability enabled")
Expand Down Expand Up @@ -1366,8 +1381,15 @@ async def main():
"write_events",
"shutdown_observability",
"is_observability_enabled",
"control_event_to_otel_span",
"get_event_batcher",
"get_event_sink",
"get_registered_control_event_sink_factory_names",
"get_registered_control_event_sinks",
"register_control_event_sink",
"register_control_event_sink_factory",
"unregister_control_event_sink",
"unregister_control_event_sink_factory",
"configure_logging",
"get_log_config",
"log_control_evaluation",
Expand Down
Loading
Loading