gnodet-bot commented on code in PR #27714:
URL: https://github.com/apache/camel/pull/27714#discussion_r4246846802


##########
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:
   💡 **Suggestion (minor):** The test verifies that the `@BeforeEach` notifier 
received the event even when the throwing notifier failed — but since the 
`@BeforeEach` notifier is at index 0 and the throwing notifier is at index 1, 
delivery to index 0 happens *before* the throw, not after it. The assertion 
therefore proves ordering rather than isolation.
   
   To truly demonstrate that `doNotifyEvent` continues delivery past a failing 
notifier, a third notifier registered *after* the throwing one (index 2) would 
need to receive the event:
   
   ```suggestion
           // add a trailing notifier (registered after the thrower) to prove 
delivery continues past a failure
           List<AiEvent> trailing = new CopyOnWriteArrayList<>();
           context.getManagementStrategy().addEventNotifier(new 
EventNotifierSupport() {
               @Override
               public void notify(CamelEvent event) {
                   if (event instanceof AiEvent ai) {
                       trailing.add(ai);
                   }
               }
           });
   
           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);
           // the trailing notifier (registered after the thrower) also 
received it — proving isolation
           assertThat(trailing).hasSize(1);
       }
   ```



##########
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:
   💡 **Suggestion (nit):** `OpenTelemetryExchangeEventNotifier.isEnabled()` 
already filters to `ExchangeEvent` subclasses, so AI events won't reach its 
`notify()` regardless. Setting `ignoreAiEvents(true)` in its constructor would 
be a minor early-exit optimisation (skipping the `isEnabled()` call entirely), 
but it's not a correctness concern.
   
   Worth considering in a follow-up once this API stabilises — no need to block 
this PR on it.



-- 
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]

Reply via email to