oscerd opened a new pull request, #27480:
URL: https://github.com/apache/camel/pull/27480

   CAMEL-25404: make an `ai-tool` `authorizationPolicy` denial observable.
   
   ## Background
   
   The `ai-tool` `authorizationPolicy` (CAMEL-24831, 4.23.0) wraps the route's 
**outer** processor — the guard runs in
   front of the route, agreed with @davsclaus on #27332. That placement is 
deliberate: a denial is caught *before* the
   route's unit of work, `AiToolExecutor` maps it to 
`AiToolResult.AuthorizationDenied` and relays a clean refusal to the
   model (never a tool result, never a stack trace). The side effect is an 
observability gap: because the deny never
   enters the route, it fires no `ExchangeFailed` event, no route span and no 
metric — it was only logged at `WARN`. That
   gap was explicitly deferred to this follow-up.
   
   ## What this changes
   
   Keep the deliberate outer placement and the model-facing behaviour exactly 
as they are; make the denial **observable**
   by emitting a `CamelEvent` on every deny:
   
   - new `AiToolAuthorizationDeniedEvent` — a `CamelEvent` of `Type.Custom` (so 
it does not pollute generic exchange
     lifecycle metrics) that is also a `CamelEvent.FailureEvent`. It carries 
the denied tool name, the tool exchange (for
     correlation — route id / exchange id), and the 
`CamelAuthorizationException` the policy raised;
   - `AiToolExecutor.authorizationDenied(...)` now fires it through
     `exchange.getContext().getManagementStrategy().notify(...)`, in addition 
to the existing `WARN` log and the unchanged
     `AuthorizationDenied` refusal.
   
   Operators register an `EventNotifier` to count / log / alert on denials 
(e.g. drive a micrometer counter) without
   changing what the model sees. Firing is best-effort and follows the same 
pattern as `camel-openai`'s agentic lifecycle
   events: nothing is published when no `EventNotifier` is registered, and a 
failure to notify is swallowed so it can never
   affect the refusal.
   
   This is the event+counter option from the issue. The alternative — moving 
the guard **inside** the route so a deny
   flows through `onException`/tracing — was not taken: it would reverse the 
deliberate outer placement and risk changing
   the model-facing behaviour (the route's error handler could transform or 
mask the deny), which the issue explicitly
   rules out.
   
   ## Tests & docs
   
   - `AiToolAuthorizationDeniedEventTest` — a deny (policy throws, and policy 
sets the exception) fires exactly one event
     with the right tool name / exchange / cause **and** still returns 
`AuthorizationDenied`; a normal route error and a
     successful call fire **no** event.
   - `ai-tool-component.adoc` documents the event under a new *Observing 
denials* section (with the catalog mirror
     regenerated).
   
   No public API change, no new dependency, no behaviour change for existing 
users (purely additive observability), so no
   upgrade-guide entry. `main` only.
   
   ---
   _Claude Code on behalf of oscerd_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


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