davsclaus commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3724092573
##########
catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/jbang/camel-jbang-configuration-metadata.json:
##########
@@ -35,6 +35,7 @@
{ "name": "camel.jbang.maven-settings", "required": false, "description":
"Optional location of Maven settings.xml file to configure servers,
repositories, mirrors, and proxies. If set to false, not even the default
\/.m2\/settings.xml will be used.", "label": "maven", "type": "string",
"javaType": "String", "secret": false },
{ "name": "camel.jbang.maven-settings-security", "required": false,
"description": "Optional location of Maven settings-security.xml file to
decrypt Maven Settings (settings.xml) file", "label": "maven", "type":
"string", "javaType": "String", "secret": false },
{ "name": "camel.jbang.mavenWrapper", "required": false, "description":
"Include Maven Wrapper files in the exported project", "type": "boolean",
"javaType": "boolean", "defaultValue": true, "secret": false },
+ { "name": "camel.jbang.mcp", "required": false, "description": "Embed
dev\/diagnostics MCP tools on the local HTTP management server (\/mcp by
default)", "type": "boolean", "javaType": "boolean", "defaultValue": false,
"secret": false, "security": "insecure:dev" },
Review Comment:
**[Medium]** This `camel.jbang.mcp` entry does not exist on `main` and is
unrelated to GenAI observability. It was likely picked up from an unclean
working tree when regenerating catalog artifacts. Please revert this file to
its `main` state.
##########
docs/components/modules/others/nav.adoc:
##########
@@ -4,6 +4,7 @@
* xref:others:index.adoc[Miscellaneous Components]
** xref:a2a-consumer.adoc[A2A - Consumer Guide]
** xref:a2a-producer.adoc[A2A - Producer Guide]
+*** xref:ai-observability.adoc[AI Observability]
Review Comment:
**[High]** Two issues here:
1. This file is auto-generated (line 1–2: _"this file is auto generated and
changes to it will be overwritten — make edits in docs/*nav.adoc.template files
instead"_). If the entry is truly being generated by `PrepareDocSymlinksMojo`,
then the issue is in the mojo or template, not here.
2. The `***` (three-star) prefix nests "AI Observability" as a **sub-item of
"A2A - Producer Guide"**, which is semantically wrong. It should be a peer
entry at the `**` level, sorted alphabetically after the A2A entries. If the
`:group: AI` doc attribute is causing this nesting, the mojo logic needs fixing.
##########
components/camel-ai/camel-ai-observability/src/main/java/org/apache/camel/component/ai/observability/GenAiObservability.java:
##########
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.ai.observability;
+
+import java.lang.reflect.Constructor;
+import java.util.Optional;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.telemetry.Span;
+import org.apache.camel.telemetry.SpanLifecycleManager;
+import org.apache.camel.telemetry.SpanStorageManagerExchange;
+import org.apache.camel.telemetry.Tracer;
+import
org.apache.camel.telemetry.propagation.CamelHeadersSpanContextPropagationExtractor;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Entry point for GenAI observability in Camel AI producers.
+ */
+public final class GenAiObservability {
+
+ private static final String CLIENT_SPAN_KIND = "CLIENT";
+ private static final String METER_REGISTRY_CLASS =
"io.micrometer.core.instrument.MeterRegistry";
+ private static final String MICROMETER_SUPPORT_CLASS
+ =
"org.apache.camel.component.ai.observability.GenAiMicrometerSupport";
+ private static final GenAiObservation NOOP = new NoopGenAiObservation();
+
+ private GenAiObservability() {
+ }
+
+ /**
+ * Whether GenAI observability is enabled for the given context.
+ */
+ public static boolean isEnabled(CamelContext camelContext) {
+ if (camelContext == null) {
+ return false;
+ }
+ Optional<String> property
+ =
camelContext.getPropertiesComponent().resolveProperty(GenAiObservabilityProperties.ENABLED);
+ if (property.isPresent()) {
+ return Boolean.parseBoolean(property.get().trim());
+ }
+ return true;
+ }
+
+ /**
+ * Starts a GenAI observation for a single LLM client call. Returns a
no-op when disabled or no backend is
+ * available.
+ */
+ public static GenAiObservation start(Exchange exchange,
GenAiObservationContext context) {
+ if (exchange == null || context == null ||
!isEnabled(exchange.getContext())) {
+ return NOOP;
+ }
+ Tracer tracer = exchange.getContext().hasService(Tracer.class);
+ GenAiMetricsBackend metricsBackend =
resolveMetricsBackend(exchange.getContext());
+ if (tracer == null && (metricsBackend == null ||
!metricsBackend.isAvailable())) {
+ return NOOP;
+ }
+ return new DefaultGenAiObservation(exchange, context, tracer,
metricsBackend);
+ }
+
+ private static GenAiMetricsBackend resolveMetricsBackend(CamelContext
camelContext) {
+ try {
+ Class.forName(METER_REGISTRY_CLASS);
+ Class<?> supportClass = Class.forName(MICROMETER_SUPPORT_CLASS);
+ Constructor<?> constructor =
supportClass.getDeclaredConstructor(CamelContext.class);
+ constructor.setAccessible(true);
Review Comment:
**[Low]** `setAccessible(true)` is unnecessary here.
`GenAiMicrometerSupport` is package-private and this code is in the same
package — `getDeclaredConstructor()` + `newInstance()` can access
package-private constructors from within the same package without
`setAccessible`. Removing it avoids potential module-system warnings.
```suggestion
return (GenAiMetricsBackend)
constructor.newInstance(camelContext);
```
##########
components/camel-ai/camel-langchain4j-chat/pom.xml:
##########
@@ -63,10 +63,21 @@
<version>${jackson2-version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-ai-observability</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
<!-- for testing -->
<dependency>
<groupId>org.apache.camel</groupId>
- <artifactId>camel-test-spring-junit6</artifactId>
+ <artifactId>camel-test-junit6</artifactId>
Review Comment:
**[Medium]** This test dependency change from `camel-test-spring-junit6` to
`camel-test-junit6` is unrelated to GenAI observability. Please revert or
submit separately.
##########
components/camel-ai/camel-langchain4j-chat/pom.xml:
##########
@@ -63,10 +63,21 @@
<version>${jackson2-version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-ai-observability</artifactId>
Review Comment:
**[High]** `camel-ai-observability` is added as a **mandatory**
(non-optional) compile dependency here and on all other AI components. This
means every user of `camel-langchain4j-chat` (and `-tools`, `-agent`,
`-embeddings`, `camel-openai`) will transitively pull in
`camel-ai-observability` + `camel-telemetry`, even if they have no interest in
observability.
In Camel's architecture, telemetry is typically injected at the framework
level (via `camel-opentelemetry2` and route-level interceptors), not coupled
into each component's producer. Having each producer call
`GenAiObservability.start()` directly deviates from this pattern and creates a
hard coupling.
Consider either:
- Making this dependency `<optional>true</optional>` and using
classpath-based discovery (check if the class is present before calling it)
- Or using an SPI/service-loader pattern so the observability hooks are
discovered when `camel-ai-observability` is on the classpath, without requiring
a compile dependency
--
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]