oscerd commented on code in PR #27714:
URL: https://github.com/apache/camel/pull/27714#discussion_r4250245358
##########
core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedEventNotifier.java:
##########
@@ -168,8 +168,8 @@ public MBeanNotificationInfo[] getNotificationInfo() {
// JmxNotificationEventNotifier uses the simple class name of the
event as the notification type
List<MBeanNotificationInfo> infos = new ArrayList<>();
for (CamelEvent.Type type : CamelEvent.Type.values()) {
- if (type == CamelEvent.Type.Custom) {
- // custom events have their own class names
+ if (type == CamelEvent.Type.Custom || type == CamelEvent.Type.Ai) {
Review Comment:
Added in 122f4ab89c04 — `ManagedEventNotifierMBean` now declares
`isIgnoreAiEvents()`/`setIgnoreAiEvents()` and `ManagedEventNotifier` delegates
them, so the flag is visible and settable over JMX (JConsole/Hawtio) like every
other ignore* flag.
_Claude Code on behalf of oscerd_
##########
docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc:
##########
@@ -27,3 +27,33 @@ A `hashicorp:` property with a field of a secret that does
not exist, such as `{
now resolves to no value, as it already did without a field, so its default
value or an optional placeholder
(`{{?hashicorp:secret:missing#field}}`) applies. Before, without a default
value, it failed with a
`NullPointerException`.
+
+=== camel-openai, camel-ai-tool - AI events now use CamelEvent.Type.Ai
+
+The events fired by the AI components now share a common contract,
`org.apache.camel.ai.AiEvent` (a
+`CamelEvent` of the new `CamelEvent.Type.Ai`), instead of each being a
component-specific
+`CamelEvent.Type.Custom` event. Events that relate to a single exchange
implement the exchange-backed subtype
+`AiEvent.ExchangeAiEvent` (the root `AiEvent` itself is not an
`ExchangeEvent`). This lets an `EventNotifier` recognise AI activity — and
route it to metrics or
+tracing — by type and by the `AiEvent` sub-interfaces, without depending on an
AI component.
+
+The events shipped in 4.23.0 are affected:
+
+* `camel-ai-tool`: `AiToolAuthorizationDeniedEvent` now implements
`AiEvent.ToolCallDeniedEvent`;
+* `camel-openai`: `OpenAIAgenticLoopStartedEvent`,
`OpenAIAgenticToolCallExecutedEvent` and
+ `OpenAIAgenticLoopCompletedEvent` now implement
`AiEvent.AgentLoopStartedEvent`, `AiEvent.ToolCallExecutedEvent`
+ and `AiEvent.AgentLoopCompletedEvent`.
+
+Their `getType()` now returns `CamelEvent.Type.Ai` rather than
`CamelEvent.Type.Custom`, and they are now
+`CamelEvent.ExchangeEvent`s (via `AiEvent.ExchangeAiEvent`). An
`EventNotifier` that recognised these events by
+`event.getType() == CamelEvent.Type.Custom` must match on `CamelEvent.Type.Ai`
instead, or — preferably —
+on `event instanceof org.apache.camel.ai.AiEvent` (or one of its
sub-interfaces), which no longer requires a
+dependency on the AI component. The concrete event classes and all their
accessors are unchanged, so code that
+references them directly is not affected.
+
+Because the shipped AI events are now `ExchangeEvent`s (via
`AiEvent.ExchangeAiEvent`), a notifier that filters on
+`instanceof ExchangeEvent` now also receives them. AI events are their own
category: a new `ignoreAiEvents` flag
+(`EventNotifier.setIgnoreAiEvents(boolean)`, default `false`) opts a notifier
out, and `ignoreExchangeEvents` does
+**not** drop AI events (so an AI-observability notifier can keep
`ignoreExchangeEvents=true` for performance). Camel's
+own exchange-oriented notifiers (the debugger, the dev-console event ring, the
micrometer exchange notifier) set
+`ignoreAiEvents=true` so AI events do not reach them; a custom
exchange-focused notifier that should not see AI events
Review Comment:
Applied your suggestion in 122f4ab89c04 — the opt-out list now reads "the
micrometer and OpenTelemetry metrics exchange notifiers".
_Claude Code on behalf of oscerd_
##########
components/camel-event/src/main/java/org/apache/camel/component/camelevent/CamelEventConsumer.java:
##########
@@ -309,6 +310,10 @@ private void configureEventNotifier() {
// Custom events are CamelContextEvents, so we need to
enable context events
eventNotifier.setIgnoreCamelContextEvents(false);
break;
+ case Ai:
Review Comment:
Done in 122f4ab89c04 — added an `=== AI Events` group listing `Ai` (with a
pointer to `org.apache.camel.ai.AiEvent`) in event-component.adoc and
regenerated the catalog copy, so `event:Ai` is discoverable. Left the optional
`event:Ai` subscription test as a follow-up.
_Claude Code on behalf of oscerd_
##########
components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolAuthorizationDeniedEventTest.java:
##########
@@ -199,6 +231,75 @@ public void notify(CamelEvent event) {
AiToolResult result = AiToolExecutor.execute(spec, Map.of(), exchange);
assertThat(result).isInstanceOf(AiToolResult.AuthorizationDenied.class);
+ // the healthy @BeforeEach notifier still received the event despite
the other one throwing
+ assertThat(events).hasSize(1);
Review Comment:
Already covered on the current head — the test was reworked: a healthy
subscriber (`later`) is registered AFTER the failing notifiers and the test
asserts IT received the event, so it proves delivery continues past a failure
(not just ordering). It also exercises isIgnoreAiEvents/isEnabled/notify
throwing, AI-scoped so teardown is unaffected.
_Claude Code on behalf of oscerd_
##########
core/camel-support/src/main/java/org/apache/camel/support/EventHelper.java:
##########
@@ -1574,6 +1576,49 @@ public static boolean
notifyExchangeAsyncProcessingStartedEvent(CamelContext con
return answer;
}
+ /**
+ * Notifies an {@link AiEvent} to the started event notifiers, following
the same path as every other event
+ * category: it loops the started notifiers, skips those that are disabled
or have {@code ignoreAiEvents} set,
+ * creates the event lazily (via {@code eventSupplier}) only when some
notifier wants it, and delivers through
+ * {@code doNotifyEvent} so one failing notifier does not stop delivery to
the others.
+ * <p>
+ * AI events are their own category: {@code ignoreExchangeEvents} does
<em>not</em> suppress them even though an
+ * {@code AiEvent} is an exchange event. A notifier opts out with {@code
setIgnoreAiEvents(true)}.
+ *
+ * @since 4.24
+ */
+ public static boolean notifyAiEvent(CamelContext context,
Supplier<AiEvent> eventSupplier) {
+ ManagementStrategy management = context != null ?
context.getManagementStrategy() : null;
+ if (management == null) {
+ return false;
+ }
+
+ List<EventNotifier> notifiers = management.getStartedEventNotifiers();
+ if (notifiers == null || notifiers.isEmpty()) {
+ return false;
+ }
+
+ boolean answer = false;
+ CamelEvent event = null;
+ // optimise for loop using index access to avoid creating iterator
object
+ for (int i = 0; i < notifiers.size(); i++) {
+ EventNotifier notifier = notifiers.get(i);
+ if (notifier.isDisabled() || notifier.isIgnoreAiEvents()) {
+ continue;
+ }
+
+ if (event == null) {
+ // only create event once, and only when a notifier wants it
+ event = eventSupplier.get();
+ if (event == null) {
+ return false;
+ }
+ }
+ answer |= doNotifyEvent(notifier, event);
Review Comment:
Went ahead and added it — `OpenTelemetryExchangeEventNotifier` (and the
micrometer one) set `setIgnoreAiEvents(true)` in their constructors, so AI
events are skipped in `notifyAiEvent` before the per-event route lookup. Thanks
for the note.
_Claude Code on behalf of oscerd_
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]