You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by "atiaomar1978-hub (via GitHub)" <gi...@apache.org> on 2026/08/05 06:26:01 UTC
[PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
atiaomar1978-hub opened a new pull request, #25337:
URL: https://github.com/apache/camel/pull/25337
# Description
Implements **Phase 1** of [CAMEL-23861](https://issues.apache.org/jira/browse/CAMEL-23861) — GenAI observability for Camel AI route producers, aligned with Claus Ibsen's latest JIRA guidance:
- **Ad 1:** langchain4j + openai components
- **Ad 2:** model identification headers on langchain4j producers
- **Ad 3:** global on/off toggle via `camel.ai.observability.enabled`
- **Ad 4:** OpenTelemetry spans first, then Micrometer metrics
_AI-generated PR description on behalf of atiaomar1978-hub_
## What changed
### New module: `camel-ai-observability`
Shared library providing GenAI observability for Camel AI producers:
- **OpenTelemetry:** child span per LLM call via `camel-telemetry.Tracer` + GenAI semantic convention attributes (`gen_ai.operation.name`, `gen_ai.system`, `gen_ai.request.model`, token usage, etc.)
- **Micrometer:** `gen_ai.client.operation` timer and `gen_ai.client.token.usage` counter (Spring AI–aligned metric names)
- **Global toggle:** `camel.ai.observability.enabled` (default `true`; no-op when disabled or no backend present)
- **Tracer resolution:** uses `CamelContext.hasService(Tracer.class)` so `camel-opentelemetry2` service registration is discovered correctly
- **Span kind:** `CLIENT` with exchange header propagation extractor for parent trace linking
### Producer integrations (Phase 1)
| Component | Integration |
|-----------|-------------|
| `langchain4j-chat` | Wraps each `chatModel.chat()` call |
| `langchain4j-tools` | Per tool-calling LLM iteration in `chatWithLLM()` |
| `langchain4j-agent` | Per agent invocation (chat model resolved from agent configuration) |
| `langchain4j-embeddings` | Per embedding call |
| `openai` | `createChatCompletion()` and streaming path |
### New exchange headers (langchain4j)
- `CamelLangChain4jChatRequestModel` / `CamelLangChain4jChatResponseModel`
- `CamelLangChain4jToolsRequestModel` / `CamelLangChain4jToolsResponseModel`
- `CamelLangChain4jAgentRequestModel` / `CamelLangChain4jAgentResponseModel`
- `CamelLangChain4jEmbeddingsRequestModel` / `CamelLangChain4jEmbeddingsResponseModel`
OpenAI already exposes `MODEL` / `RESPONSE_MODEL`.
### Documentation
- Component doc: `components/camel-ai/camel-ai-observability/src/main/docs/ai-observability.adoc`
- Upgrade guide entry in `docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc`
## Design notes
- **Child span per LLM call** (not route-span enrichment only) — required for multi-iteration tool-calling loops
- Stable GenAI semconv subset aligned with Spring AI
- Micrometer uses direct `MeterRegistry` binding; Micrometer Observation API deferred to follow-up
- Phase 2 (separate scope): `camel ask` / TUI observability per Claus comment
## Tests
- `GenAiModelResolverTest` — provider/model resolution
- `GenAiObservabilityTest` — global toggle, Micrometer metrics
- `GenAiObservabilitySpanTest` — OTel span attributes and error tagging
- `GenAiObservationContextTest` — span naming convention
```bash
mvn test -pl components/camel-ai/camel-ai-observability -am -DskipITs
```
## Target
- [x] Commit targets `main` (Camel 4.22.0-SNAPSHOT)
## Tracking
- [x] [CAMEL-23861](https://issues.apache.org/jira/browse/CAMEL-23861)
## AI-assisted contributions
- [x] AI-assisted (Cursor Cloud Agent)
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "davsclaus (via GitHub)" <gi...@apache.org>.
davsclaus commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3719810263
##########
components/camel-ai/camel-ai-observability/pom.xml:
##########
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>camel-ai-parent</artifactId>
+ <groupId>org.apache.camel</groupId>
+ <version>4.22.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-ai-observability</artifactId>
+ <packaging>jar</packaging>
+ <name>Camel :: AI :: Observability</name>
+ <description>GenAI observability support for Camel AI components (OpenTelemetry and Micrometer)</description>
+
+ <properties>
+ <firstVersion>4.22.0</firstVersion>
+ <label>ai</label>
+ <title>AI Observability</title>
+ <supportLevel>Preview</supportLevel>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-telemetry</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-core</artifactId>
+ </dependency>
Review Comment:
**[High]** `micrometer-core` should be marked `<optional>true</optional>`. Since `camel-ai-observability` is pulled into every AI component (langchain4j-chat, tools, agent, embeddings, openai), this forces Micrometer onto the classpath for all AI users — even those who only want OTel spans, or neither.
The code in `GenAiObservability.resolveMeterRegistry()` already handles the absent case gracefully (returns null, metrics skipped). Making the dependency optional would match that design.
```suggestion
<artifactId>micrometer-core</artifactId>
<optional>true</optional>
```
##########
components/camel-ai/camel-ai-observability/src/main/java/org/apache/camel/component/ai/observability/GenAiModelResolver.java:
##########
@@ -0,0 +1,91 @@
+/*
+ * 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.Method;
+
+/**
+ * Resolves GenAI provider and model metadata from LangChain4j model beans.
+ */
+public final class GenAiModelResolver {
+
+ private static final String UNKNOWN = "unknown";
+
+ private GenAiModelResolver() {
+ }
+
+ public static String resolveSystem(Object model) {
+ if (model == null) {
+ return UNKNOWN;
+ }
+ String className = model.getClass().getName().toLowerCase();
+ if (className.contains("openai")) {
+ return "openai";
+ }
Review Comment:
**[Medium]** The class-name substring matching is fragile. For example, a class in package `com.example.myopenai_config.SomeModel` would incorrectly match `"openai"`. Additionally, `resolveModelName` uses reflection to probe for `modelName()`, `getModelName()`, `model()` — this may break silently with langchain4j version updates.
Consider using langchain4j's own `ChatResponse.modelName()` (which exists and returns the actual model name) where the response is available, rather than reflectively probing the model bean.
##########
components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AbstractAgent.java:
##########
@@ -65,6 +66,13 @@ protected AgentConfiguration getConfiguration() {
return configuration;
}
+ /**
+ * Returns the LangChain4j chat model configured for this agent.
+ */
+ public ChatModel getChatModel() {
Review Comment:
**[Low]** Per project conventions, new public methods in API modules should include a `@since` tag.
```suggestion
public ChatModel getChatModel() {
```
Please add `@since 4.22` to the Javadoc above.
##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -614,11 +628,17 @@ private void processStreaming(Exchange exchange, ChatCompletionCreateParams para
exchange.getUnitOfWork().addSynchronization(new Synchronization() {
@Override
public void onComplete(Exchange e) {
+ observation.recordSuccess(GenAiUsage.of(null, null, null, requestModel));
Review Comment:
**[Medium]** The streaming observation records no token usage — all nulls. The OpenAI streaming API can include usage data in the final chunk when `stream_options.include_usage` is set. Recording a span with zero token information could be misleading in observability dashboards.
Consider accumulating token usage from the final streaming chunk, or at minimum documenting this limitation.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5188352652
@davsclaus @oscerd — could you please review this Phase 1 implementation for [CAMEL-23861](https://issues.apache.org/jira/browse/CAMEL-23861)?
Scope per Claus's JIRA guidance: GenAI observability (OTel CLIENT spans + Micrometer metrics) for langchain4j-chat, langchain4j-tools, langchain4j-agent, langchain4j-embeddings, and openai, with global toggle and model headers.
_AI-generated comment on behalf of atiaomar1978-hub_
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5194669536
:star2: Thank you for your contribution to the Apache Camel project! :star2:
:robot: CI automation will test this PR automatically.
:camel: Apache Camel Committers, please review the following items:
* First-time contributors **require MANUAL approval** for the GitHub Actions to run
* You can use the command `/component-test (camel-)component-name1 (camel-)component-name2..` to request a test from the test bot although they are normally detected and executed by CI.
* You can label PRs using `skip-tests` and `test-dependents` to fine-tune the checks executed by this PR.
* Build and test logs are available in the summary page. **Only** [Apache Camel committers](https://camel.apache.org/community/team/#committers) have access to the summary.
:warning: Be careful when sharing logs. Review their contents before sharing them publicly.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3722111695
##########
components/camel-ai/camel-ai-observability/pom.xml:
##########
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>camel-ai-parent</artifactId>
+ <groupId>org.apache.camel</groupId>
+ <version>4.22.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-ai-observability</artifactId>
+ <packaging>jar</packaging>
+ <name>Camel :: AI :: Observability</name>
+ <description>GenAI observability support for Camel AI components (OpenTelemetry and Micrometer)</description>
+
+ <properties>
+ <firstVersion>4.22.0</firstVersion>
+ <label>ai</label>
+ <title>AI Observability</title>
+ <supportLevel>Preview</supportLevel>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-telemetry</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-core</artifactId>
+ </dependency>
Review Comment:
Fixed in 2cbf19a16e9: marked `micrometer-core` as `<optional>true</optional>` in camel-ai-observability/pom.xml. Metrics are skipped when no MeterRegistry is present, matching the existing resolveMeterRegistry() design.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AbstractAgent.java:
##########
@@ -65,6 +66,13 @@ protected AgentConfiguration getConfiguration() {
return configuration;
}
+ /**
+ * Returns the LangChain4j chat model configured for this agent.
+ */
+ public ChatModel getChatModel() {
Review Comment:
Fixed in 2cbf19a16e9: added `@since 4.22` to the `getChatModel()` javadoc on AbstractAgent.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-ai-observability/src/main/java/org/apache/camel/component/ai/observability/GenAiModelResolver.java:
##########
@@ -0,0 +1,91 @@
+/*
+ * 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.Method;
+
+/**
+ * Resolves GenAI provider and model metadata from LangChain4j model beans.
+ */
+public final class GenAiModelResolver {
+
+ private static final String UNKNOWN = "unknown";
+
+ private GenAiModelResolver() {
+ }
+
+ public static String resolveSystem(Object model) {
+ if (model == null) {
+ return UNKNOWN;
+ }
+ String className = model.getClass().getName().toLowerCase();
+ if (className.contains("openai")) {
+ return "openai";
+ }
Review Comment:
Fixed in 2cbf19a16e9: GenAiModelResolver now uses langchain4j `ModelProvider` and `ChatModel.defaultRequestParameters().modelName()` (with package-prefix fallback). Langchain4j producers use `ChatResponse.modelName()` via `GenAiModelResolver.resolveResponseModelName()` for the response model.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -614,11 +628,17 @@ private void processStreaming(Exchange exchange, ChatCompletionCreateParams para
exchange.getUnitOfWork().addSynchronization(new Synchronization() {
@Override
public void onComplete(Exchange e) {
+ observation.recordSuccess(GenAiUsage.of(null, null, null, requestModel));
Review Comment:
Fixed in 2cbf19a16e9: streaming requests now set `stream_options.include_usage=true` and accumulate `CompletionUsage` from the final chunk when recording the GenAI observation (token counts and response model when present).
_AI-generated reply on behalf of atiaomar1978-hub_
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5207154429
_AI-generated comment on behalf of atiaomar1978-hub._
Addressed the latest round of review feedback from @davsclaus in commit `8e424d403b8`:
1. **Uncached reflection** — `GenAiObservability.start()` now caches the resolved `Method` per `CamelContext` via `ConcurrentHashMap`. `GenAiObservabilityImpl.resolveMetricsBackend()` similarly caches the Micrometer backend per context instead of creating a new instance on every LLM call.
2. **Silent exception swallowing** — Added `DEBUG` logging in both the API bridge and impl metrics-backend resolution when reflective setup fails.
3. **Google AI system name inconsistency** — `dev.langchain4j.model.google` package fallback now returns `"google"` (matching `GOOGLE_AI_GEMINI` / `GOOGLE_GENAI` provider mapping), while `dev.langchain4j.model.vertexai` continues to return `"gcp.vertex_ai"`. Added package-name tests.
4. **`long` → `int` narrowing cast** — OpenAI producer now uses `Math.toIntExact()` via a `toTokenCount()` helper for prompt/completion tokens passed to `GenAiUsage`.
**Tests:** All observability-related tests pass (GenAiObservabilityTest, GenAiObservabilitySpanTest, GenAiModelResolverTest, OpenAIObservabilityTest, LangChain4jChat/Tools/Embeddings/Agent observability tests).
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3723596061
##########
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]
** xref:attachments.adoc[Attachments]
** xref:aws-bedrock-examples.adoc[AWS Bedrock - Examples]
Review Comment:
Addressed in `f12051a92c4`: the `nav.adoc` entry is generated by `PrepareDocSymlinksMojo` during `mvn process-classes -pl docs -Pregen` (not hand-edited). The `***` prefix is intentional for pages with a `:group:` attribute such as `:group: AI`.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java:
##########
@@ -141,14 +147,49 @@ public void process(Exchange exchange) throws Exception {
AiAgentBody<?> aiAgentBody = exchange.getMessage().getMandatoryBody(AiAgentBody.class);
ToolProvider toolProvider = createComposedToolProvider(tags, exchange);
- Result<String> result = agent.chat(aiAgentBody, toolProvider);
- exchange.getMessage().setBody(result.content());
- populateResultHeaders(result, exchange);
+ Object chatModel = resolveChatModel(agent);
+ GenAiObservationContext observationContext = GenAiObservationContext.builder()
+ .operationName(GenAiOperationName.GENERATE_CONTENT)
+ .system(GenAiModelResolver.resolveSystem(chatModel))
+ .requestModel(GenAiModelResolver.resolveModelName(chatModel))
+ .componentScheme("langchain4j-agent")
+ .build();
+ GenAiObservation observation = GenAiObservability.start(exchange, observationContext);
+ try {
+ Result<String> result = agent.chat(aiAgentBody, toolProvider);
+ exchange.getMessage().setBody(result.content());
+ populateResultHeaders(result, exchange, observationContext.requestModel());
+ observation.recordSuccess(GenAiUsage.of(
+ result.tokenUsage() != null ? result.tokenUsage().inputTokenCount() : null,
+ result.tokenUsage() != null ? result.tokenUsage().outputTokenCount() : null,
+ result.finishReason(),
+ observationContext.requestModel()));
+ } catch (RuntimeException e) {
+ observation.recordError(e);
+ throw e;
+ } finally {
+ observation.close();
+ }
+ }
+
+ private Object resolveChatModel(Agent agent) {
+ if (endpoint.getConfiguration().getAgentConfiguration() != null) {
Review Comment:
Fixed in `f12051a92c4`: `LangChain4jAgentProducer` now sets only `REQUEST_MODEL` on exchange headers. The `RESPONSE_MODEL` header is omitted because `Result<String>` does not expose a response model name. GenAI span `gen_ai.response.model` is also omitted in the observation usage for agent calls.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-langchain4j-embeddings/src/main/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsProducer.java:
##########
@@ -38,8 +44,35 @@ public LangChain4jEmbeddingsEndpoint getEndpoint() {
public void process(Exchange exchange) throws Exception {
final TextSegment in = exchange.getMessage().getMandatoryBody(TextSegment.class);
final EmbeddingModel model = getEndpoint().getConfiguration().getEmbeddingModel();
- final Response<Embedding> result = model.embed(in);
- final Message message = exchange.getMessage();
+ GenAiObservationContext observationContext = GenAiObservationContext.builder()
+ .operationName(GenAiOperationName.EMBEDDINGS)
+ .system(GenAiModelResolver.resolveSystem(model))
+ .requestModel(GenAiModelResolver.resolveModelName(model))
+ .componentScheme("langchain4j-embeddings")
+ .build();
+ GenAiObservation observation = GenAiObservability.start(exchange, observationContext);
+ try {
+ final Response<Embedding> result = model.embed(in);
+ populateHeaders(exchange.getMessage(), result, in, observationContext.requestModel());
+ observation.recordSuccess(GenAiUsage.of(
+ result.tokenUsage() != null ? result.tokenUsage().inputTokenCount() : null,
+ result.tokenUsage() != null ? result.tokenUsage().outputTokenCount() : null,
+ result.finishReason(),
+ observationContext.requestModel()));
+ } catch (RuntimeException e) {
+ observation.recordError(e);
Review Comment:
Fixed in `f12051a92c4`: `LangChain4jEmbeddingsProducer` now sets only `REQUEST_MODEL`. `RESPONSE_MODEL` is omitted because `Response<Embedding>` does not carry a response model name. The observation span likewise omits `gen_ai.response.model` for embeddings.
_AI-generated reply on behalf of atiaomar1978-hub_
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "davsclaus (via GitHub)" <gi...@apache.org>.
davsclaus commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3726106086
##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -602,23 +609,66 @@ private void enforceAgenticTokenBudget(
}
private void processStreaming(Exchange exchange, ChatCompletionCreateParams params) {
+ String requestModel = params.model().toString();
+ ChatCompletionCreateParams streamingParams = params.toBuilder()
+ .streamOptions(ChatCompletionStreamOptions.builder().includeUsage(true).build())
Review Comment:
**Behavioral change — `streamOptions(includeUsage=true)` injected unconditionally**
This modifies the outgoing API payload for ALL streaming requests, causing the stream to include a final chunk with token usage data. Two concerns:
1. **Downstream consumers** iterating the stream chunks may receive an unexpected usage-only chunk they don't handle
2. **Non-OpenAI providers** accessed through camel-openai may not support this option, potentially causing 400 errors
This should either be opt-in (gated behind the `camel.ai.observability.enabled` property) or at minimum documented as a behavioral change in the upgrade guide entry. Currently the upgrade guide does not mention this streaming behavior change.
##########
components/camel-ai/camel-ai-observability-api/src/main/java/org/apache/camel/component/ai/observability/GenAiObservability.java:
##########
@@ -0,0 +1,90 @@
+/*
+ * 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.util.Optional;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+
+/**
+ * Entry point for GenAI observability in Camel AI producers.
+ * <p/>
+ * The concrete tracing and metrics implementation lives in {@code camel-ai-observability} and is loaded via reflection
+ * when that module is on the classpath. Without it, calls return a no-op observation.
+ */
+public final class GenAiObservability {
+
+ private static final String IMPL_CLASS = "org.apache.camel.component.ai.observability.GenAiObservabilityImpl";
+ 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, when
+ * {@code camel-ai-observability} is absent, or when no backend is available.
+ */
+ public static GenAiObservation start(Exchange exchange, GenAiObservationContext context) {
+ if (exchange == null || context == null || !isEnabled(exchange.getContext())) {
+ return NOOP;
+ }
+ CamelContext camelContext = exchange.getContext();
+ if (camelContext.getClassResolver().resolveClass(IMPL_CLASS) == null) {
+ return NOOP;
+ }
+ try {
+ Class<?> implClass = camelContext.getClassResolver().resolveClass(IMPL_CLASS);
+ return (GenAiObservation) implClass.getMethod("start", Exchange.class, GenAiObservationContext.class)
+ .invoke(null, exchange, context);
+ } catch (Throwable t) {
Review Comment:
**Two issues in `start()`:**
1. **Double class resolution** — `resolveClass(IMPL_CLASS)` is called twice (line 62 and line 66). Store the result in a local variable and reuse it.
2. **`catch (Throwable t)` is too broad** — This swallows fatal `Error` types (`OutOfMemoryError`, `StackOverflowError`). The impl module's `resolveMetricsBackend()` correctly catches `ReflectiveOperationException | LinkageError`. This catch should do the same.
```suggestion
Class<?> implClass = camelContext.getClassResolver().resolveClass(IMPL_CLASS);
if (implClass == null) {
return NOOP;
}
try {
return (GenAiObservation) implClass.getMethod("start", Exchange.class, GenAiObservationContext.class)
.invoke(null, exchange, context);
} catch (ReflectiveOperationException | LinkageError e) {
return NOOP;
}
```
##########
components/camel-ai/camel-ai-observability/src/main/java/org/apache/camel/component/ai/observability/GenAiObservabilityImpl.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * 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 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;
+
+/**
+ * GenAI observability implementation loaded reflectively from {@link GenAiObservability}.
+ */
+public final class GenAiObservabilityImpl {
+
+ private static final GenAiObservation NOOP = new NoopGenAiObservation();
+ 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 GenAiObservabilityImpl() {
+ }
+
+ /**
+ * Starts a GenAI observation for a single LLM client call. Returns a no-op when no backend is available.
+ */
+ public static GenAiObservation start(Exchange exchange, GenAiObservationContext context) {
+ 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);
+ return (GenAiMetricsBackend) constructor.newInstance(camelContext);
+ } catch (ReflectiveOperationException | LinkageError e) {
+ return null;
+ }
+ }
+
+ private static final class DefaultGenAiObservation implements GenAiObservation {
+
+ private final Exchange exchange;
+ private final GenAiObservationContext context;
+ private final Tracer tracer;
+ private final GenAiMetricsBackend metricsBackend;
+ private final long startNanos;
+ private Span span;
+ private GenAiUsage usage;
+ private Throwable error;
+ private boolean closed;
+
+ private DefaultGenAiObservation(
+ Exchange exchange, GenAiObservationContext context, Tracer tracer,
+ GenAiMetricsBackend metricsBackend) {
+ this.exchange = exchange;
+ this.context = context;
+ this.tracer = tracer;
+ this.metricsBackend = metricsBackend;
+ this.startNanos = System.nanoTime();
+ startSpan();
+ }
+
+ private void startSpan() {
+ if (tracer == null || tracer.getSpanLifecycleManager() == null) {
+ return;
+ }
+ SpanStorageManagerExchange storage = new SpanStorageManagerExchange();
+ Span parent = storage.peek(exchange);
+ SpanLifecycleManager lifecycleManager = tracer.getSpanLifecycleManager();
+ var extractor = new CamelHeadersSpanContextPropagationExtractor(exchange.getIn().getHeaders());
+ span = lifecycleManager.create(context.spanName(), CLIENT_SPAN_KIND, parent, extractor);
+ lifecycleManager.activate(span);
+ applyContextAttributes(span, context, null, null);
+ }
+
+ @Override
+ public void recordSuccess(GenAiUsage usage) {
+ this.usage = usage;
+ }
+
+ @Override
+ public void recordError(Throwable error) {
+ this.error = error;
+ }
+
+ @Override
+ public void close() {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ closeSpan();
+ recordMetrics();
+ }
+
+ private void closeSpan() {
+ if (span == null || tracer == null || tracer.getSpanLifecycleManager() == null) {
+ return;
+ }
+ SpanLifecycleManager lifecycleManager = tracer.getSpanLifecycleManager();
+ if (error != null) {
+ span.setError(true);
+ span.setTag(GenAiAttributes.ERROR_TYPE, error.getClass().getSimpleName());
+ }
+ if (usage != null) {
+ applyContextAttributes(span, context, usage, error == null ? usage.responseModel() : null);
+ }
+ lifecycleManager.deactivate(span);
+ lifecycleManager.close(span);
+ span = null;
+ }
+
+ private void recordMetrics() {
+ if (metricsBackend == null) {
+ return;
+ }
+ metricsBackend.recordMetrics(context, usage, error, startNanos);
+ }
+
+ private static void applyContextAttributes(
+ Span span, GenAiObservationContext context, GenAiUsage usage, String responseModel) {
+ span.setTag(GenAiAttributes.OPERATION_NAME, context.operationName().value());
+ span.setTag(GenAiAttributes.SYSTEM, nullToUnknown(context.system()));
+ span.setTag(GenAiAttributes.REQUEST_MODEL, nullToUnknown(context.requestModel()));
+ if (ObjectHelper.isNotEmpty(context.componentScheme())) {
+ span.setTag(GenAiAttributes.CAMEL_COMPONENT, context.componentScheme());
+ }
+ if (responseModel != null && !responseModel.isBlank()) {
+ span.setTag(GenAiAttributes.RESPONSE_MODEL, responseModel);
+ }
+ if (usage != null) {
+ if (usage.inputTokens() != null) {
+ span.setTag(GenAiAttributes.INPUT_TOKENS, usage.inputTokens().toString());
+ }
+ if (usage.outputTokens() != null) {
+ span.setTag(GenAiAttributes.OUTPUT_TOKENS, usage.outputTokens().toString());
+ }
+ if (usage.finishReason() != null) {
+ span.setTag(GenAiAttributes.FINISH_REASONS, usage.finishReason());
+ }
+ if (usage.responseModel() != null && !usage.responseModel().isBlank()) {
+ span.setTag(GenAiAttributes.RESPONSE_MODEL, usage.responseModel());
Review Comment:
**`RESPONSE_MODEL` set redundantly, defeating the error-path guard**
The `responseModel` parameter is set conditionally at line 156, then `usage.responseModel()` unconditionally overwrites it at line 169. On the error path, `closeSpan()` passes `null` as `responseModel` (via `error == null ? usage.responseModel() : null`) intending to suppress it, but this second block defeats that guard since `usage.responseModel()` may still be non-null.
Either remove the redundant second block, or adjust the logic so the error-path intent is preserved.
##########
components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/Headers.java:
##########
@@ -56,6 +56,12 @@ public class Headers {
@Metadata(description = "The Total Token Count.", javaType = "int")
public static final String TOTAL_TOKEN_COUNT = "CamelLangChain4jAgentTotalTokenCount";
+ @Metadata(description = "The request model name.", javaType = "String")
+ public static final String REQUEST_MODEL = "CamelLangChain4jAgentRequestModel";
+
+ @Metadata(description = "The response model name.", javaType = "String")
+ public static final String RESPONSE_MODEL = "CamelLangChain4jAgentResponseModel";
Review Comment:
**`RESPONSE_MODEL` header is declared but never populated by the agent producer.**
The `LangChain4jAgentProducer` never sets this header because the langchain4j `Result<String>` doesn't expose a response model name. The constant appears in the catalog/endpoint-dsl, creating a misleading API surface for users who see it in the documentation.
Either:
- Remove the constant until the agent producer can actually populate it, or
- Add a Javadoc note that this is reserved for future use when langchain4j exposes response model information
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "davsclaus (via GitHub)" <gi...@apache.org>.
davsclaus commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3727904650
##########
components/camel-ai/camel-ai-observability-api/src/main/java/org/apache/camel/component/ai/observability/GenAiObservability.java:
##########
@@ -0,0 +1,90 @@
+/*
+ * 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.util.Optional;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+
+/**
+ * Entry point for GenAI observability in Camel AI producers.
+ * <p/>
+ * The concrete tracing and metrics implementation lives in {@code camel-ai-observability} and is loaded via reflection
+ * when that module is on the classpath. Without it, calls return a no-op observation.
+ */
+public final class GenAiObservability {
+
+ private static final String IMPL_CLASS = "org.apache.camel.component.ai.observability.GenAiObservabilityImpl";
+ 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, when
+ * {@code camel-ai-observability} is absent, or when no backend is available.
+ */
+ public static GenAiObservation start(Exchange exchange, GenAiObservationContext context) {
+ if (exchange == null || context == null || !isEnabled(exchange.getContext())) {
+ return NOOP;
+ }
+ CamelContext camelContext = exchange.getContext();
+ Class<?> implClass = camelContext.getClassResolver().resolveClass(IMPL_CLASS);
Review Comment:
**[Medium] Uncached reflection on every LLM call**
`resolveClass(IMPL_CLASS)` + `getMethod("start", ...).invoke()` runs on every `start()` call. `GenAiObservabilityImpl.resolveMetricsBackend()` similarly does `Class.forName()` + constructor reflection per call, creating a new `GenAiMicrometerSupport` instance each time.
For agentic loops with multiple LLM iterations per exchange, this adds avoidable overhead. Consider caching the resolved class and method handle per `CamelContext` (e.g., in a `ConcurrentHashMap<CamelContext, MethodHandle>` or via `CamelContext.getExtension()`).
##########
components/camel-ai/camel-ai-observability-api/src/main/java/org/apache/camel/component/ai/observability/GenAiModelResolver.java:
##########
@@ -0,0 +1,125 @@
+/*
+ * 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 dev.langchain4j.model.ModelProvider;
+import dev.langchain4j.model.chat.ChatModel;
+import dev.langchain4j.model.chat.response.ChatResponse;
+import dev.langchain4j.model.embedding.EmbeddingModel;
+
+/**
+ * Resolves GenAI provider and model metadata from LangChain4j model beans.
+ */
+public final class GenAiModelResolver {
+
+ private static final String UNKNOWN = "unknown";
+
+ private GenAiModelResolver() {
+ }
+
+ public static String resolveSystem(Object model) {
+ if (model == null) {
+ return UNKNOWN;
+ }
+ if (model instanceof ChatModel chatModel) {
+ return mapProvider(chatModel.provider());
+ }
+ if (model instanceof EmbeddingModel embeddingModel) {
+ return mapProvider(embeddingModel.provider());
+ }
+ return resolveSystemFromPackage(model.getClass().getPackageName());
+ }
+
+ public static String resolveModelName(Object model) {
+ if (model == null) {
+ return UNKNOWN;
+ }
+ if (model instanceof ChatModel chatModel) {
+ String modelName = chatModel.defaultRequestParameters().modelName();
+ if (modelName != null && !modelName.isBlank()) {
+ return modelName;
+ }
+ }
+ if (model instanceof EmbeddingModel embeddingModel) {
+ String modelName = embeddingModel.modelName();
+ if (modelName != null && !modelName.isBlank()) {
+ return modelName;
+ }
+ }
+ return UNKNOWN;
+ }
+
+ /**
+ * Resolves the response model from a LangChain4j {@link ChatResponse}, falling back when absent.
+ */
+ public static String resolveResponseModelName(ChatResponse chatResponse, String fallback) {
+ if (chatResponse == null) {
+ return fallback;
+ }
+ String modelName = chatResponse.modelName();
+ return modelName != null && !modelName.isBlank() ? modelName : fallback;
+ }
+
+ private static String mapProvider(ModelProvider provider) {
+ if (provider == null) {
+ return UNKNOWN;
+ }
+ return switch (provider) {
+ case OPEN_AI -> "openai";
+ case ANTHROPIC -> "anthropic";
+ case OLLAMA -> "ollama";
+ case AZURE_OPEN_AI -> "azure.ai.openai";
+ case GOOGLE_VERTEX_AI_GEMINI, GOOGLE_VERTEX_AI_ANTHROPIC -> "gcp.vertex_ai";
+ case GOOGLE_AI_GEMINI, GOOGLE_GENAI -> "google";
Review Comment:
**[Medium] Google AI system name inconsistency**
`mapProvider()` maps `GOOGLE_AI_GEMINI` / `GOOGLE_GENAI` → `"google"`, but `resolveSystemFromPackage()` (line 111–112) maps `dev.langchain4j.model.google` → `"gcp.vertex_ai"`. The same Google AI Gemini model returns different system names depending on which resolution path is taken.
Should the package fallback for `dev.langchain4j.model.google` return `"google"` instead of `"gcp.vertex_ai"`?
##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -655,6 +707,32 @@ private static String getFinishReasonString(ChatCompletion.Choice choice) {
.orElse("stop");
}
+ private ChatCompletion createChatCompletion(Exchange exchange, ChatCompletionCreateParams params) {
+ String requestModel = params.model().toString();
+ GenAiObservationContext observationContext = GenAiObservationContext.builder()
+ .operationName(GenAiOperationName.CHAT)
+ .system("openai")
+ .requestModel(requestModel)
+ .componentScheme("openai")
+ .build();
+ GenAiObservation observation = GenAiObservability.start(exchange, observationContext);
+ try {
+ ChatCompletion response = getEndpoint().getClient().chat().completions().create(params);
+ CompletionUsage usage = response.usage().orElse(null);
+ observation.recordSuccess(GenAiUsage.of(
+ usage != null ? (int) usage.promptTokens() : null,
Review Comment:
**[Low] `long` → `int` narrowing cast**
`usage.promptTokens()` and `usage.completionTokens()` return `long`, but are cast to `(int)` here (and at lines 660–661 in the streaming path). While overflow is unlikely in practice, consider either:
- Typing `GenAiUsage.inputTokens` / `outputTokens` as `Long` (or `long`) to avoid the lossy cast, or
- Using `Math.toIntExact()` for a fail-fast on overflow instead of silent truncation
##########
components/camel-ai/camel-ai-observability-api/src/main/java/org/apache/camel/component/ai/observability/GenAiObservability.java:
##########
@@ -0,0 +1,90 @@
+/*
+ * 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.util.Optional;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+
+/**
+ * Entry point for GenAI observability in Camel AI producers.
+ * <p/>
+ * The concrete tracing and metrics implementation lives in {@code camel-ai-observability} and is loaded via reflection
+ * when that module is on the classpath. Without it, calls return a no-op observation.
+ */
+public final class GenAiObservability {
+
+ private static final String IMPL_CLASS = "org.apache.camel.component.ai.observability.GenAiObservabilityImpl";
+ 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, when
+ * {@code camel-ai-observability} is absent, or when no backend is available.
+ */
+ public static GenAiObservation start(Exchange exchange, GenAiObservationContext context) {
+ if (exchange == null || context == null || !isEnabled(exchange.getContext())) {
+ return NOOP;
+ }
+ CamelContext camelContext = exchange.getContext();
+ Class<?> implClass = camelContext.getClassResolver().resolveClass(IMPL_CLASS);
+ if (implClass == null) {
+ return NOOP;
+ }
+ try {
+ return (GenAiObservation) implClass.getMethod("start", Exchange.class, GenAiObservationContext.class)
+ .invoke(null, exchange, context);
+ } catch (ReflectiveOperationException | LinkageError e) {
Review Comment:
**[Medium] Silent exception swallowing**
This catch block (and the equivalent one in `GenAiObservabilityImpl.resolveMetricsBackend()`) silently discards all errors. If the impl class exists but has a method signature mismatch (e.g., after a version skew between API and impl), failures will be invisible.
A `DEBUG` or `TRACE` level log would aid troubleshooting.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3726144957
##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -602,23 +609,66 @@ private void enforceAgenticTokenBudget(
}
private void processStreaming(Exchange exchange, ChatCompletionCreateParams params) {
+ String requestModel = params.model().toString();
+ ChatCompletionCreateParams streamingParams = params.toBuilder()
+ .streamOptions(ChatCompletionStreamOptions.builder().includeUsage(true).build())
Review Comment:
Fixed in `5f196cf`: `stream_options.include_usage=true` is now set only when `GenAiObservability.isEnabled()` is true. Documented in the 4.22 upgrade guide.
##########
components/camel-ai/camel-ai-observability-api/src/main/java/org/apache/camel/component/ai/observability/GenAiObservability.java:
##########
@@ -0,0 +1,90 @@
+/*
+ * 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.util.Optional;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+
+/**
+ * Entry point for GenAI observability in Camel AI producers.
+ * <p/>
+ * The concrete tracing and metrics implementation lives in {@code camel-ai-observability} and is loaded via reflection
+ * when that module is on the classpath. Without it, calls return a no-op observation.
+ */
+public final class GenAiObservability {
+
+ private static final String IMPL_CLASS = "org.apache.camel.component.ai.observability.GenAiObservabilityImpl";
+ 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, when
+ * {@code camel-ai-observability} is absent, or when no backend is available.
+ */
+ public static GenAiObservation start(Exchange exchange, GenAiObservationContext context) {
+ if (exchange == null || context == null || !isEnabled(exchange.getContext())) {
+ return NOOP;
+ }
+ CamelContext camelContext = exchange.getContext();
+ if (camelContext.getClassResolver().resolveClass(IMPL_CLASS) == null) {
+ return NOOP;
+ }
+ try {
+ Class<?> implClass = camelContext.getClassResolver().resolveClass(IMPL_CLASS);
+ return (GenAiObservation) implClass.getMethod("start", Exchange.class, GenAiObservationContext.class)
+ .invoke(null, exchange, context);
+ } catch (Throwable t) {
Review Comment:
Fixed in `5f196cf`: single `resolveClass` lookup; catch narrowed to `ReflectiveOperationException | LinkageError`.
##########
components/camel-ai/camel-ai-observability/src/main/java/org/apache/camel/component/ai/observability/GenAiObservabilityImpl.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * 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 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;
+
+/**
+ * GenAI observability implementation loaded reflectively from {@link GenAiObservability}.
+ */
+public final class GenAiObservabilityImpl {
+
+ private static final GenAiObservation NOOP = new NoopGenAiObservation();
+ 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 GenAiObservabilityImpl() {
+ }
+
+ /**
+ * Starts a GenAI observation for a single LLM client call. Returns a no-op when no backend is available.
+ */
+ public static GenAiObservation start(Exchange exchange, GenAiObservationContext context) {
+ 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);
+ return (GenAiMetricsBackend) constructor.newInstance(camelContext);
+ } catch (ReflectiveOperationException | LinkageError e) {
+ return null;
+ }
+ }
+
+ private static final class DefaultGenAiObservation implements GenAiObservation {
+
+ private final Exchange exchange;
+ private final GenAiObservationContext context;
+ private final Tracer tracer;
+ private final GenAiMetricsBackend metricsBackend;
+ private final long startNanos;
+ private Span span;
+ private GenAiUsage usage;
+ private Throwable error;
+ private boolean closed;
+
+ private DefaultGenAiObservation(
+ Exchange exchange, GenAiObservationContext context, Tracer tracer,
+ GenAiMetricsBackend metricsBackend) {
+ this.exchange = exchange;
+ this.context = context;
+ this.tracer = tracer;
+ this.metricsBackend = metricsBackend;
+ this.startNanos = System.nanoTime();
+ startSpan();
+ }
+
+ private void startSpan() {
+ if (tracer == null || tracer.getSpanLifecycleManager() == null) {
+ return;
+ }
+ SpanStorageManagerExchange storage = new SpanStorageManagerExchange();
+ Span parent = storage.peek(exchange);
+ SpanLifecycleManager lifecycleManager = tracer.getSpanLifecycleManager();
+ var extractor = new CamelHeadersSpanContextPropagationExtractor(exchange.getIn().getHeaders());
+ span = lifecycleManager.create(context.spanName(), CLIENT_SPAN_KIND, parent, extractor);
+ lifecycleManager.activate(span);
+ applyContextAttributes(span, context, null, null);
+ }
+
+ @Override
+ public void recordSuccess(GenAiUsage usage) {
+ this.usage = usage;
+ }
+
+ @Override
+ public void recordError(Throwable error) {
+ this.error = error;
+ }
+
+ @Override
+ public void close() {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ closeSpan();
+ recordMetrics();
+ }
+
+ private void closeSpan() {
+ if (span == null || tracer == null || tracer.getSpanLifecycleManager() == null) {
+ return;
+ }
+ SpanLifecycleManager lifecycleManager = tracer.getSpanLifecycleManager();
+ if (error != null) {
+ span.setError(true);
+ span.setTag(GenAiAttributes.ERROR_TYPE, error.getClass().getSimpleName());
+ }
+ if (usage != null) {
+ applyContextAttributes(span, context, usage, error == null ? usage.responseModel() : null);
+ }
+ lifecycleManager.deactivate(span);
+ lifecycleManager.close(span);
+ span = null;
+ }
+
+ private void recordMetrics() {
+ if (metricsBackend == null) {
+ return;
+ }
+ metricsBackend.recordMetrics(context, usage, error, startNanos);
+ }
+
+ private static void applyContextAttributes(
+ Span span, GenAiObservationContext context, GenAiUsage usage, String responseModel) {
+ span.setTag(GenAiAttributes.OPERATION_NAME, context.operationName().value());
+ span.setTag(GenAiAttributes.SYSTEM, nullToUnknown(context.system()));
+ span.setTag(GenAiAttributes.REQUEST_MODEL, nullToUnknown(context.requestModel()));
+ if (ObjectHelper.isNotEmpty(context.componentScheme())) {
+ span.setTag(GenAiAttributes.CAMEL_COMPONENT, context.componentScheme());
+ }
+ if (responseModel != null && !responseModel.isBlank()) {
+ span.setTag(GenAiAttributes.RESPONSE_MODEL, responseModel);
+ }
+ if (usage != null) {
+ if (usage.inputTokens() != null) {
+ span.setTag(GenAiAttributes.INPUT_TOKENS, usage.inputTokens().toString());
+ }
+ if (usage.outputTokens() != null) {
+ span.setTag(GenAiAttributes.OUTPUT_TOKENS, usage.outputTokens().toString());
+ }
+ if (usage.finishReason() != null) {
+ span.setTag(GenAiAttributes.FINISH_REASONS, usage.finishReason());
+ }
+ if (usage.responseModel() != null && !usage.responseModel().isBlank()) {
+ span.setTag(GenAiAttributes.RESPONSE_MODEL, usage.responseModel());
Review Comment:
Fixed in `5f196cf`: removed redundant `usage.responseModel()` span tag so the error-path guard in `closeSpan()` is effective.
##########
components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/Headers.java:
##########
@@ -56,6 +56,12 @@ public class Headers {
@Metadata(description = "The Total Token Count.", javaType = "int")
public static final String TOTAL_TOKEN_COUNT = "CamelLangChain4jAgentTotalTokenCount";
+ @Metadata(description = "The request model name.", javaType = "String")
+ public static final String REQUEST_MODEL = "CamelLangChain4jAgentRequestModel";
+
+ @Metadata(description = "The response model name.", javaType = "String")
+ public static final String RESPONSE_MODEL = "CamelLangChain4jAgentResponseModel";
Review Comment:
Addressed in `5f196cf`: updated `@Metadata` description to note the agent producer does not set this header when langchain4j Result lacks a response model. Upgrade guide updated accordingly.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5256852133
## Rebased on main and updated for Camel 4.23.0
_AI-generated comment on behalf of atiaomar1978-hub via Cursor Cloud Agent._
Addressed @davsclaus's Aug 11 feedback — main is now on **4.23.0-SNAPSHOT** and this PR has been rebased and version-bumped accordingly.
**Commits:** `0b8bb332250c`, `b88c31f4933e`
### Changes
| Area | Update |
|------|--------|
| Rebase | Clean rebase onto `upstream/main` (4.23.0-SNAPSHOT) |
| `camel-ai-observability` / `-api` poms | `firstVersion` → **4.23.0** |
| Docs / `@since` tags | Updated from 4.22 → **4.23** |
| Upgrade guide | Moved GenAI observability entry to **4.22 → 4.23** guide |
| Catalog | Regenerated `ai-observability.json` metadata |
### Tests (local)
```
./mvnw -pl components/camel-ai/camel-ai-observability-api,components/camel-ai/camel-ai-observability -am test
```
BUILD SUCCESS
Branch: `atiaomar1978-hub:cursor/CAMEL-23861-genai-observability-c587`
Ready for CI and merge when green.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5196662362
Addressed round-2 review feedback and CI failures in commit `f12051a92c4`:
**CI fixes**
- Committed missing regenerated catalog files (`ai-observability` others entry, model header metadata) and endpoint DSL factories — fixes the "uncommitted changes" build failure
- Doc xref + symlinks from prior commit verified locally with Antora quick build
**Review fixes**
- **Optional Micrometer:** moved metrics recording to `GenAiMicrometerSupport`, loaded reflectively only when `MeterRegistry` is on the classpath (fixes `NoClassDefFoundError` in agent/chat tests without Micrometer)
- **RESPONSE_MODEL:** agent and embeddings producers now omit the response model header/span attribute when unavailable from the result (instead of mirroring request model)
- **Producer integration test:** added `LangChain4jChatObservabilityTest` verifying GenAI span attributes and model headers from the langchain4j-chat producer
- **nav.adoc:** entry is produced by `PrepareDocSymlinksMojo` during docs regen (not hand-edited); `***` prefix is expected for pages with `:group: AI`
**Tests run**
```
mvn test -pl components/camel-ai/camel-ai-observability,components/camel-ai/camel-langchain4j-chat,components/camel-ai/camel-langchain4j-agent -am -DskipITs \
-Dtest=GenAiObservabilityTest,GenAiObservabilitySpanTest,GenAiModelResolverTest,LangChain4jChatObservabilityTest,LangChain4jAgentResultHeadersTest
```
_AI-generated comment on behalf of atiaomar1978-hub_
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3730274882
##########
components/camel-ai/camel-ai-observability-api/src/main/java/org/apache/camel/component/ai/observability/GenAiObservability.java:
##########
@@ -0,0 +1,90 @@
+/*
+ * 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.util.Optional;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+
+/**
+ * Entry point for GenAI observability in Camel AI producers.
+ * <p/>
+ * The concrete tracing and metrics implementation lives in {@code camel-ai-observability} and is loaded via reflection
+ * when that module is on the classpath. Without it, calls return a no-op observation.
+ */
+public final class GenAiObservability {
+
+ private static final String IMPL_CLASS = "org.apache.camel.component.ai.observability.GenAiObservabilityImpl";
+ 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, when
+ * {@code camel-ai-observability} is absent, or when no backend is available.
+ */
+ public static GenAiObservation start(Exchange exchange, GenAiObservationContext context) {
+ if (exchange == null || context == null || !isEnabled(exchange.getContext())) {
+ return NOOP;
+ }
+ CamelContext camelContext = exchange.getContext();
+ Class<?> implClass = camelContext.getClassResolver().resolveClass(IMPL_CLASS);
Review Comment:
Fixed in `8e424d403b8`: `GenAiObservability.start()` now caches the resolved `Method` per `CamelContext` in a `ConcurrentHashMap` (`ImplBridge`), so agentic loops no longer pay reflection cost on every LLM call. `GenAiObservabilityImpl.resolveMetricsBackend()` likewise caches the Micrometer backend per context instead of constructing a new `GenAiMicrometerSupport` on each observation.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-ai-observability-api/src/main/java/org/apache/camel/component/ai/observability/GenAiObservability.java:
##########
@@ -0,0 +1,90 @@
+/*
+ * 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.util.Optional;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+
+/**
+ * Entry point for GenAI observability in Camel AI producers.
+ * <p/>
+ * The concrete tracing and metrics implementation lives in {@code camel-ai-observability} and is loaded via reflection
+ * when that module is on the classpath. Without it, calls return a no-op observation.
+ */
+public final class GenAiObservability {
+
+ private static final String IMPL_CLASS = "org.apache.camel.component.ai.observability.GenAiObservabilityImpl";
+ 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, when
+ * {@code camel-ai-observability} is absent, or when no backend is available.
+ */
+ public static GenAiObservation start(Exchange exchange, GenAiObservationContext context) {
+ if (exchange == null || context == null || !isEnabled(exchange.getContext())) {
+ return NOOP;
+ }
+ CamelContext camelContext = exchange.getContext();
+ Class<?> implClass = camelContext.getClassResolver().resolveClass(IMPL_CLASS);
+ if (implClass == null) {
+ return NOOP;
+ }
+ try {
+ return (GenAiObservation) implClass.getMethod("start", Exchange.class, GenAiObservationContext.class)
+ .invoke(null, exchange, context);
+ } catch (ReflectiveOperationException | LinkageError e) {
Review Comment:
Fixed in `8e424d403b8`: added `DEBUG` logging when reflective bridge resolution or invocation fails in `GenAiObservability`, and when Micrometer backend setup fails in `GenAiObservabilityImpl.createMetricsBackend()`. Failures remain non-fatal (no-op observation), but version skew or signature mismatches are now visible in logs.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-ai-observability-api/src/main/java/org/apache/camel/component/ai/observability/GenAiModelResolver.java:
##########
@@ -0,0 +1,125 @@
+/*
+ * 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 dev.langchain4j.model.ModelProvider;
+import dev.langchain4j.model.chat.ChatModel;
+import dev.langchain4j.model.chat.response.ChatResponse;
+import dev.langchain4j.model.embedding.EmbeddingModel;
+
+/**
+ * Resolves GenAI provider and model metadata from LangChain4j model beans.
+ */
+public final class GenAiModelResolver {
+
+ private static final String UNKNOWN = "unknown";
+
+ private GenAiModelResolver() {
+ }
+
+ public static String resolveSystem(Object model) {
+ if (model == null) {
+ return UNKNOWN;
+ }
+ if (model instanceof ChatModel chatModel) {
+ return mapProvider(chatModel.provider());
+ }
+ if (model instanceof EmbeddingModel embeddingModel) {
+ return mapProvider(embeddingModel.provider());
+ }
+ return resolveSystemFromPackage(model.getClass().getPackageName());
+ }
+
+ public static String resolveModelName(Object model) {
+ if (model == null) {
+ return UNKNOWN;
+ }
+ if (model instanceof ChatModel chatModel) {
+ String modelName = chatModel.defaultRequestParameters().modelName();
+ if (modelName != null && !modelName.isBlank()) {
+ return modelName;
+ }
+ }
+ if (model instanceof EmbeddingModel embeddingModel) {
+ String modelName = embeddingModel.modelName();
+ if (modelName != null && !modelName.isBlank()) {
+ return modelName;
+ }
+ }
+ return UNKNOWN;
+ }
+
+ /**
+ * Resolves the response model from a LangChain4j {@link ChatResponse}, falling back when absent.
+ */
+ public static String resolveResponseModelName(ChatResponse chatResponse, String fallback) {
+ if (chatResponse == null) {
+ return fallback;
+ }
+ String modelName = chatResponse.modelName();
+ return modelName != null && !modelName.isBlank() ? modelName : fallback;
+ }
+
+ private static String mapProvider(ModelProvider provider) {
+ if (provider == null) {
+ return UNKNOWN;
+ }
+ return switch (provider) {
+ case OPEN_AI -> "openai";
+ case ANTHROPIC -> "anthropic";
+ case OLLAMA -> "ollama";
+ case AZURE_OPEN_AI -> "azure.ai.openai";
+ case GOOGLE_VERTEX_AI_GEMINI, GOOGLE_VERTEX_AI_ANTHROPIC -> "gcp.vertex_ai";
+ case GOOGLE_AI_GEMINI, GOOGLE_GENAI -> "google";
Review Comment:
Fixed in `8e424d403b8`: split the package fallback so `dev.langchain4j.model.google` returns `"google"` (consistent with `GOOGLE_AI_GEMINI` / `GOOGLE_GENAI` in `mapProvider()`), while `dev.langchain4j.model.vertexai` continues to return `"gcp.vertex_ai"`. Added package-name tests with stub classes in those packages.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -655,6 +707,32 @@ private static String getFinishReasonString(ChatCompletion.Choice choice) {
.orElse("stop");
}
+ private ChatCompletion createChatCompletion(Exchange exchange, ChatCompletionCreateParams params) {
+ String requestModel = params.model().toString();
+ GenAiObservationContext observationContext = GenAiObservationContext.builder()
+ .operationName(GenAiOperationName.CHAT)
+ .system("openai")
+ .requestModel(requestModel)
+ .componentScheme("openai")
+ .build();
+ GenAiObservation observation = GenAiObservability.start(exchange, observationContext);
+ try {
+ ChatCompletion response = getEndpoint().getClient().chat().completions().create(params);
+ CompletionUsage usage = response.usage().orElse(null);
+ observation.recordSuccess(GenAiUsage.of(
+ usage != null ? (int) usage.promptTokens() : null,
Review Comment:
Fixed in `8e424d403b8`: OpenAI producer now uses `Math.toIntExact()` via a `toTokenCount()` helper for both streaming and non-streaming paths when passing prompt/completion tokens to `GenAiUsage`. This avoids silent truncation and fails fast on overflow.
_AI-generated reply on behalf of atiaomar1978-hub_
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5201117944
## CI fix — uncommitted endpoint DSL
_AI-generated on behalf of atiaomar1978-hub via Cursor Cloud Agent._
CI failed the **uncommitted changes** check ([run 31074465889](https://github.com/apache/camel/actions/runs/31074465889/job/92531575880)) because `Headers.RESPONSE_MODEL` `@Metadata` was updated in `5f196cf` but the generated endpoint DSL was not regenerated.
**Fixed in `4450d6b`:**
- Regenerated `LangChain4jAgentEndpointBuilderFactory.java` with the updated javadoc:
_"The response model name. Not set by the agent producer when langchain4j Result does not expose it."_
CI should pass the generated-files check on the next run.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3723603431
##########
components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AbstractAgent.java:
##########
@@ -65,6 +66,13 @@ protected AgentConfiguration getConfiguration() {
return configuration;
}
+ /**
+ * Returns the LangChain4j chat model configured for this agent.
+ */
+ public ChatModel getChatModel() {
Review Comment:
Confirmed still in place on latest commit `f12051a92c4`: `@since 4.22` remains on `AbstractAgent.getChatModel()`.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-ai-observability/pom.xml:
##########
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>camel-ai-parent</artifactId>
+ <groupId>org.apache.camel</groupId>
+ <version>4.22.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-ai-observability</artifactId>
+ <packaging>jar</packaging>
+ <name>Camel :: AI :: Observability</name>
+ <description>GenAI observability support for Camel AI components (OpenTelemetry and Micrometer)</description>
+
+ <properties>
+ <firstVersion>4.22.0</firstVersion>
+ <label>ai</label>
+ <title>AI Observability</title>
+ <supportLevel>Preview</supportLevel>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-telemetry</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-core</artifactId>
+ </dependency>
Review Comment:
Follow-up in `f12051a92c4`: beyond marking `micrometer-core` optional in the POM, metrics recording now lives in `GenAiMicrometerSupport` and is loaded reflectively only when `MeterRegistry` is on the classpath. This prevents `NoClassDefFoundError` in AI components (e.g. langchain4j-agent tests) that do not pull Micrometer transitively.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -614,11 +628,17 @@ private void processStreaming(Exchange exchange, ChatCompletionCreateParams para
exchange.getUnitOfWork().addSynchronization(new Synchronization() {
@Override
public void onComplete(Exchange e) {
+ observation.recordSuccess(GenAiUsage.of(null, null, null, requestModel));
Review Comment:
Confirmed still in place on latest commit `f12051a92c4`: OpenAI streaming sets `stream_options.include_usage=true` and accumulates token counts from the final chunk when recording the GenAI observation.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-ai-observability/src/main/java/org/apache/camel/component/ai/observability/GenAiModelResolver.java:
##########
@@ -0,0 +1,91 @@
+/*
+ * 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.Method;
+
+/**
+ * Resolves GenAI provider and model metadata from LangChain4j model beans.
+ */
+public final class GenAiModelResolver {
+
+ private static final String UNKNOWN = "unknown";
+
+ private GenAiModelResolver() {
+ }
+
+ public static String resolveSystem(Object model) {
+ if (model == null) {
+ return UNKNOWN;
+ }
+ String className = model.getClass().getName().toLowerCase();
+ if (className.contains("openai")) {
+ return "openai";
+ }
Review Comment:
Confirmed still in place on latest commit `f12051a92c4`: `GenAiModelResolver` uses langchain4j `ModelProvider`, `ChatModel.defaultRequestParameters().modelName()`, and `ChatResponse.modelName()` for response model resolution. Package-prefix fallback is retained only as a last resort.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
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]
** xref:attachments.adoc[Attachments]
** xref:aws-bedrock-examples.adoc[AWS Bedrock - Examples]
Review Comment:
Follow-up: also committed regenerated catalog artifacts in `f12051a92c4` and verified the doc xref locally with Antora quick build. CI doc validation and uncommitted-changes checks should pass on the latest push.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java:
##########
@@ -141,14 +147,49 @@ public void process(Exchange exchange) throws Exception {
AiAgentBody<?> aiAgentBody = exchange.getMessage().getMandatoryBody(AiAgentBody.class);
ToolProvider toolProvider = createComposedToolProvider(tags, exchange);
- Result<String> result = agent.chat(aiAgentBody, toolProvider);
- exchange.getMessage().setBody(result.content());
- populateResultHeaders(result, exchange);
+ Object chatModel = resolveChatModel(agent);
+ GenAiObservationContext observationContext = GenAiObservationContext.builder()
+ .operationName(GenAiOperationName.GENERATE_CONTENT)
+ .system(GenAiModelResolver.resolveSystem(chatModel))
+ .requestModel(GenAiModelResolver.resolveModelName(chatModel))
+ .componentScheme("langchain4j-agent")
+ .build();
+ GenAiObservation observation = GenAiObservability.start(exchange, observationContext);
+ try {
+ Result<String> result = agent.chat(aiAgentBody, toolProvider);
+ exchange.getMessage().setBody(result.content());
+ populateResultHeaders(result, exchange, observationContext.requestModel());
+ observation.recordSuccess(GenAiUsage.of(
+ result.tokenUsage() != null ? result.tokenUsage().inputTokenCount() : null,
+ result.tokenUsage() != null ? result.tokenUsage().outputTokenCount() : null,
+ result.finishReason(),
+ observationContext.requestModel()));
+ } catch (RuntimeException e) {
+ observation.recordError(e);
+ throw e;
+ } finally {
+ observation.close();
+ }
+ }
+
+ private Object resolveChatModel(Agent agent) {
+ if (endpoint.getConfiguration().getAgentConfiguration() != null) {
Review Comment:
Follow-up: added `LangChain4jAgentResultHeadersTest.shouldNotSetResponseModelWhenUnavailableFromAgentResult()` in `f12051a92c4` to assert the header is omitted when the agent result has no response model.
_AI-generated reply on behalf of atiaomar1978-hub_
##########
components/camel-ai/camel-langchain4j-embeddings/src/main/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsProducer.java:
##########
@@ -38,8 +44,35 @@ public LangChain4jEmbeddingsEndpoint getEndpoint() {
public void process(Exchange exchange) throws Exception {
final TextSegment in = exchange.getMessage().getMandatoryBody(TextSegment.class);
final EmbeddingModel model = getEndpoint().getConfiguration().getEmbeddingModel();
- final Response<Embedding> result = model.embed(in);
- final Message message = exchange.getMessage();
+ GenAiObservationContext observationContext = GenAiObservationContext.builder()
+ .operationName(GenAiOperationName.EMBEDDINGS)
+ .system(GenAiModelResolver.resolveSystem(model))
+ .requestModel(GenAiModelResolver.resolveModelName(model))
+ .componentScheme("langchain4j-embeddings")
+ .build();
+ GenAiObservation observation = GenAiObservability.start(exchange, observationContext);
+ try {
+ final Response<Embedding> result = model.embed(in);
+ populateHeaders(exchange.getMessage(), result, in, observationContext.requestModel());
+ observation.recordSuccess(GenAiUsage.of(
+ result.tokenUsage() != null ? result.tokenUsage().inputTokenCount() : null,
+ result.tokenUsage() != null ? result.tokenUsage().outputTokenCount() : null,
+ result.finishReason(),
+ observationContext.requestModel()));
+ } catch (RuntimeException e) {
+ observation.recordError(e);
Review Comment:
Follow-up: confirmed on latest commit — only `REQUEST_MODEL` is set on exchange headers; `gen_ai.response.model` is omitted from the observation span for embeddings calls.
_AI-generated reply on behalf of atiaomar1978-hub_
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5200776315
## Round-3 review feedback addressed (@davsclaus)
_AI-generated on behalf of atiaomar1978-hub via Cursor Cloud Agent._
Pushed commit `5f196cf` with the following fixes:
### Code fixes
| Review item | Fix |
|-------------|-----|
| **Double `resolveClass` + broad `catch(Throwable)`** in `GenAiObservability.start()` | Single lookup; catch narrowed to `ReflectiveOperationException \| LinkageError` |
| **`RESPONSE_MODEL` overwritten on error path** in `GenAiObservabilityImpl` | Removed redundant second `usage.responseModel()` span tag (parameter already guards error path) |
| **Unconditional `stream_options.include_usage`** in `OpenAIProducer` | Only set when `GenAiObservability.isEnabled()`; documented in upgrade guide |
| **`RESPONSE_MODEL` header never populated by agent** | Updated `@Metadata` description; upgrade guide clarifies agent/embeddings omit it when unavailable |
### CI / generated files
- **Regenerated** `catalog/.../docs/ai-observability.adoc` (syncs API-module paragraph; removes stale `:group: AI` from catalog copy)
- **Reverted** unrelated `camel.jbang.mcp` catalog drift (not included in this commit)
### Tests (BUILD SUCCESS)
```
./mvnw -B -pl components/camel-ai/camel-ai-observability,components/camel-ai/camel-ai-observability-api,components/camel-ai/camel-openai,components/camel-ai/camel-langchain4j-agent,components/camel-ai/camel-langchain4j-chat,components/camel-ai/camel-langchain4j-tools,components/camel-ai/camel-langchain4j-embeddings -am test \
-Dtest=GenAiObservabilityTest,GenAiObservabilitySpanTest,OpenAIObservabilityTest,LangChain4jChatObservabilityTest,LangChain4jToolsObservabilityTest,LangChain4jEmbeddingsObservabilityTest,LangChain4jAgentResultHeadersTest
```
Ready for re-review. CI should re-run on this push.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5212287682
## CI failure analysis (run 31115312949)
_AI-generated on behalf of atiaomar1978-hub_
**Root cause:** The failing job is **not** from CAMEL-23861 code — all AI observability tests passed in CI.
| Module | Test | Result |
|--------|------|--------|
| `camel-ai-observability` | 16 tests | ✅ PASS |
| `camel-openai` (incl. `OpenAIObservabilityTest`) | all | ✅ PASS |
| `camel-langchain4j-*` | all | ✅ PASS |
| `camel-lumberjack` | `LumberjackComponentTest.shouldListenToMessages` | ❌ **flake** — `ConditionTimeout` after 30s (failed all 3 reruns) |
| `camel-vertx-websocket` | `testSendWithConnectionKeyForRawParameterizedPath` | ⚠️ failed run 1, passed run 2 |
The build tested **606 modules** (broad dependent-module scope from PR labels), which increases flake exposure under CI load. Lumberjack is a known timing-sensitive integration test (`@Isolated`, already disabled on s390x for reliability — CAMEL-21438).
**Fix applied:** Rebased branch onto latest `upstream/main` (was ~10 commits behind) and verified focused test suite passes locally. Pushed to re-trigger CI.
If lumberjack flakes again, it is unrelated to this PR — consider re-running the workflow or narrowing CI scope to changed modules only.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5194191853
Addressed @davsclaus review feedback in commit `2cbf19a16e9`:
1. **Optional micrometer-core** — marked `<optional>true</optional>` in `camel-ai-observability/pom.xml` so AI users are not forced to pull Micrometer transitively.
2. **`@since 4.22`** — added to `AbstractAgent.getChatModel()` javadoc.
3. **GenAiModelResolver** — replaced fragile class-name substring matching with langchain4j `ModelProvider`, `ChatModel.defaultRequestParameters().modelName()`, and package-prefix fallback; producers now use `ChatResponse.modelName()` for response model.
4. **OpenAI streaming tokens** — enables `stream_options.include_usage` and accumulates usage from the final chunk when recording the observation.
Tests: `mvn test -pl components/camel-ai/camel-ai-observability` — 14 passed.
_AI-generated comment on behalf of atiaomar1978-hub_
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5195002670
Fixed the two CI failures (documentation validation + docs xref-check):
**Root cause:** The upgrade guide referenced `xref:components:ai-observability.adoc`, but the AI Observability doc lives under the **others** module (same pattern as `mcp-server`, `observation`).
**Changes in `5f34708496e`:**
- Corrected xref to `xref:components:others:ai-observability.adoc` in `camel-4x-upgrade-guide-4_22.adoc`
- Added missing doc symlinks under `docs/components/modules/others/` (page + JSON example)
- Regenerated `nav.adoc` entry for the new page
Verified locally with `mvn process-classes -pl docs -Pregen` (xref-check passes).
_AI-generated on behalf of atiaomar1978-hub_
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5202399965
<!-- ci-tested-modules -->
:test_tube: **CI tested the following changed modules:**
- `bom/camel-bom`
- `catalog/camel-allcomponents`
- `catalog/camel-catalog`
- `components/camel-ai`
- `components/camel-ai/camel-ai-observability`
- `components/camel-ai/camel-ai-observability-api`
- `components/camel-ai/camel-ai-observability`
- `components/camel-ai/camel-langchain4j-agent`
- `components/camel-ai/camel-langchain4j-agent-api`
- `components/camel-ai/camel-langchain4j-agent`
- `components/camel-ai/camel-langchain4j-chat`
- `components/camel-ai/camel-langchain4j-embeddings`
- `components/camel-ai/camel-langchain4j-tools`
- `components/camel-ai/camel-openai`
- `docs`
- `dsl/camel-endpointdsl`
- `parent`
- `tooling/maven/camel-package-maven-plugin`
> :information_source: Dependent modules were not tested because the total number of affected modules exceeded the threshold (50). Use the `test-dependents` label to force testing all dependents.
---
<details><summary>:microscope: Scalpel shadow comparison — Scalpel: 579 tested, 25 compile-only — current: 76 all tested</summary>
[Maveniverse Scalpel](https://github.com/maveniverse/scalpel) detected **604 affected modules** (current approach: 76).
<details><summary>:warning: Modules only in Scalpel (604)</summary>
- `apache-camel`
- `archetypes`
- `camel-a2a`
- `camel-activemq`
- `camel-activemq6`
- `camel-ai-observability`
- `camel-ai-observability-api`
- `camel-ai-parent`
- `camel-ai-tool`
- `camel-allcomponents`
- `camel-amqp`
- `camel-api`
- `camel-api-component-maven-plugin`
- `camel-arangodb`
- `camel-archetype-api-component`
- `camel-archetype-component`
- `camel-archetype-dataformat`
- `camel-archetype-java`
- `camel-archetype-main`
- `camel-archetype-spring`
- `camel-as2`
- `camel-as2-api`
- `camel-as2-parent`
- `camel-asn1`
- `camel-asterisk`
- `camel-atmosphere-websocket`
- `camel-atom`
- `camel-attachments`
- `camel-avro`
- `camel-avro-rpc`
- `camel-avro-rpc-jetty`
- `camel-avro-rpc-parent`
- `camel-avro-rpc-spi`
- `camel-aws-bedrock`
- `camel-aws-cloudtrail`
- `camel-aws-common`
- `camel-aws-config`
- `camel-aws-parameter-store`
- `camel-aws-parent`
- `camel-aws-secrets-manager`
- `camel-aws-security-hub`
- `camel-aws2-athena`
- `camel-aws2-comprehend`
- `camel-aws2-cw`
- `camel-aws2-ddb`
- `camel-aws2-ec2`
- `camel-aws2-ecs`
- `camel-aws2-eks`
- `camel-aws2-eventbridge`
- `camel-aws2-iam`
- `camel-aws2-kinesis`
- `camel-aws2-kms`
- `camel-aws2-lambda`
- `camel-aws2-mq`
- `camel-aws2-msk`
- `camel-aws2-polly`
- `camel-aws2-redshift`
- `camel-aws2-rekognition`
- `camel-aws2-s3`
- `camel-aws2-s3-vectors`
- `camel-aws2-ses`
- `camel-aws2-sns`
- `camel-aws2-sqs`
- `camel-aws2-step-functions`
- `camel-aws2-sts`
- `camel-aws2-textract`
- `camel-aws2-timestream`
- `camel-aws2-transcribe`
- `camel-aws2-translate`
- `camel-azure-common`
- `camel-azure-cosmosdb`
- `camel-azure-eventgrid`
- `camel-azure-eventhubs`
- `camel-azure-files`
- `camel-azure-functions`
- `camel-azure-key-vault`
- `camel-azure-parent`
- `camel-azure-schema-registry`
- `camel-azure-servicebus`
- `camel-azure-storage-blob`
- `camel-azure-storage-datalake`
- `camel-azure-storage-queue`
- `camel-barcode`
- `camel-base`
- `camel-base-engine`
- `camel-base64`
- `camel-bean`
- `camel-bean-validator`
- `camel-beanio`
- `camel-bindy`
- `camel-bom`
- `camel-bonita`
- `camel-box`
- `camel-box-api`
- `camel-box-parent`
- `camel-braintree`
- `camel-browse`
- `camel-caffeine`
- `camel-camunda`
- `camel-cassandraql`
- `camel-catalog`
- `camel-catalog-common`
- `camel-catalog-console`
- `camel-catalog-lucene`
- `camel-catalog-maven`
- `camel-catalog-suggest`
- `camel-cbor`
- `camel-chatscript`
- `camel-chunk`
- `camel-cli-connector`
- `camel-cli-debug`
- `camel-clickhouse`
- `camel-clickup`
- `camel-cloudevents`
- `camel-cluster`
- `camel-cm-sms`
- `camel-coap`
- `camel-cometd`
- `camel-component-maven-plugin`
- `camel-componentdsl`
- `camel-console`
- `camel-consul`
- `camel-controlbus`
- `camel-core`
- `camel-core-all`
- `camel-core-catalog`
- `camel-core-engine`
- `camel-core-languages`
- `camel-core-model`
- `camel-core-processor`
- `camel-core-reifier`
- `camel-core-xml`
- `camel-couchbase`
- `camel-couchdb`
- `camel-cron`
- `camel-crypto`
- `camel-crypto-pgp`
- `camel-csimple-joor`
- `camel-csimple-maven-plugin`
- `camel-csv`
- `camel-cxf-common`
- `camel-cxf-parent`
- `camel-cxf-rest`
- `camel-cxf-soap`
- `camel-cxf-spring-common`
- `camel-cxf-spring-rest`
- `camel-cxf-spring-soap`
- `camel-cxf-spring-transport`
- `camel-cxf-transport`
- `camel-cyberark-vault`
- `camel-dapr`
- `camel-dataformat`
- `camel-dataset`
- `camel-datasonnet`
- `camel-debezium-common`
- `camel-debezium-common-parent`
- `camel-debezium-db2`
- `camel-debezium-maven-plugin`
- `camel-debezium-mongodb`
- `camel-debezium-mysql`
- `camel-debezium-oracle`
- `camel-debezium-parent`
- `camel-debezium-postgres`
- `camel-debezium-sqlserver`
- `camel-debug`
- `camel-dependencies`
- `camel-dfdl`
- `camel-dhis2`
- `camel-dhis2-api`
- `camel-dhis2-parent`
- `camel-diagram`
- `camel-digitalocean`
- `camel-direct`
- `camel-disruptor`
- `camel-djl`
- `camel-dns`
- `camel-docker`
- `camel-docling`
- `camel-drill`
- `camel-dropbox`
- `camel-dsl-modeline`
- `camel-dsl-support`
- `camel-duckdb`
- `camel-dynamic-router`
- `camel-ehcache`
- `camel-eip-documentation-enricher-maven-plugin`
- `camel-elasticsearch`
- `camel-elasticsearch-rest-client`
- `camel-endpointdsl`
- `camel-endpointdsl-support`
- `camel-event`
- `camel-exec`
- `camel-fastjson`
- `camel-fhir`
- `camel-fhir-api`
- `camel-fhir-parent`
- `camel-file`
- `camel-file-watch`
- `camel-flatpack`
- `camel-flink`
- `camel-flowable`
- `camel-fop`
- `camel-fory`
- `camel-freemarker`
- `camel-ftp`
- `camel-ftp-common`
- `camel-geocoder`
- `camel-git`
- `camel-github2`
- `camel-google-bigquery`
- `camel-google-calendar`
- `camel-google-common`
- `camel-google-drive`
- `camel-google-firestore`
- `camel-google-functions`
- `camel-google-mail`
- `camel-google-parent`
- `camel-google-pubsub`
- `camel-google-secret-manager`
- `camel-google-sheets`
- `camel-google-speech-to-text`
- `camel-google-storage`
- `camel-google-text-to-speech`
- `camel-google-vertexai`
- `camel-google-vision`
- `camel-graphql`
- `camel-grok`
- `camel-groovy`
- `camel-grpc`
- `camel-gson`
- `camel-hashicorp-vault`
- `camel-hazelcast`
- `camel-headersmap`
- `camel-health`
- `camel-hl7`
- `camel-http`
- `camel-http-base`
- `camel-http-common`
- `camel-huawei-parent`
- `camel-huaweicloud-common`
- `camel-huaweicloud-dms`
- `camel-huaweicloud-frs`
- `camel-huaweicloud-functiongraph`
- `camel-huaweicloud-iam`
- `camel-huaweicloud-imagerecognition`
- `camel-huaweicloud-obs`
- `camel-huaweicloud-smn`
- `camel-huggingface`
- `camel-ibm-cos`
- `camel-ibm-parent`
- `camel-ibm-secrets-manager`
- `camel-ibm-watson-discovery`
- `camel-ibm-watson-language`
- `camel-ibm-watson-speech-to-text`
- `camel-ibm-watson-text-to-speech`
- `camel-ibm-watsonx-ai`
- `camel-ibm-watsonx-data`
- `camel-ical`
- `camel-iec60870`
- `camel-iggy`
- `camel-ignite`
- `camel-infinispan`
- `camel-infinispan-common`
- `camel-infinispan-embedded`
- `camel-infinispan-parent`
- `camel-influxdb`
- `camel-influxdb2`
- `camel-irc`
- `camel-ironmq`
- `camel-iso8583`
- `camel-itest`
- `camel-jackson`
- `camel-jackson-avro`
- `camel-jackson-protobuf`
- `camel-jackson3`
- `camel-jackson3-avro`
- `camel-jackson3-protobuf`
- `camel-jackson3xml`
- `camel-jacksonxml`
- `camel-jactl`
- `camel-jandex`
- `camel-jasypt`
- `camel-java-io`
- `camel-java-joor-dsl`
- `camel-javascript`
- `camel-jaxb`
- `camel-jbang-console`
- `camel-jbang-core`
- `camel-jbang-it`
- `camel-jbang-main`
- `camel-jbang-mcp`
- `camel-jbang-parent`
- `camel-jbang-plugin-edit`
- `camel-jbang-plugin-generate`
- `camel-jbang-plugin-kubernetes`
- `camel-jbang-plugin-mcp`
- `camel-jbang-plugin-route-parser`
- `camel-jbang-plugin-test`
- `camel-jbang-plugin-tui`
- `camel-jbang-plugin-validate`
- `camel-jcache`
- `camel-jcr`
- `camel-jdbc`
- `camel-jetty`
- `camel-jetty-common`
- `camel-jfr`
- `camel-jgroups`
- `camel-jgroups-raft`
- `camel-jira`
- `camel-jms`
- `camel-jmx`
- `camel-jolt`
- `camel-jooq`
- `camel-joor`
- `camel-jpa`
- `camel-jq`
- `camel-jsch`
- `camel-jslt`
- `camel-json-patch`
- `camel-json-validator`
- `camel-jsonapi`
- `camel-jsonata`
- `camel-jsonb`
- `camel-jsonpath`
- `camel-jsoup`
- `camel-jt400`
- `camel-jta`
- `camel-jte`
- `camel-kafka`
- `camel-kamelet`
- `camel-kamelet-main`
- `camel-kamelet-main-support`
- `camel-keycloak`
- `camel-knative`
- `camel-knative-api`
- `camel-knative-http`
- `camel-knative-parent`
- `camel-kserve`
- `camel-kubernetes`
- `camel-kudu`
- `camel-langchain4j-agent`
- `camel-langchain4j-agent-api`
- `camel-langchain4j-chat`
- `camel-langchain4j-core`
- `camel-langchain4j-embeddings`
- `camel-langchain4j-embeddingstore`
- `camel-langchain4j-embeddingstore-api`
- `camel-langchain4j-tokenizer`
- `camel-langchain4j-tools`
- `camel-langchain4j-web-search`
- `camel-language`
- `camel-launcher`
- `camel-launcher-container`
- `camel-ldap`
- `camel-ldif`
- `camel-leveldb`
- `camel-log`
- `camel-lra`
- `camel-lucene`
- `camel-lumberjack`
- `camel-lzf`
- `camel-mail`
- `camel-mail-microsoft-oauth`
- `camel-main`
- `camel-management`
- `camel-management-api`
- `camel-mapstruct`
- `camel-master`
- `camel-maven-plugin`
- `camel-mcp-server`
- `camel-mcp-server-api`
- `camel-mdc`
- `camel-metrics`
- `camel-micrometer`
- `camel-micrometer-observability`
- `camel-micrometer-prometheus`
- `camel-microprofile-config`
- `camel-microprofile-fault-tolerance`
- `camel-microprofile-health`
- `camel-microprofile-parent`
- `camel-milo`
- `camel-milvus`
- `camel-mina`
- `camel-mina-sftp`
- `camel-minio`
- `camel-mllp`
- `camel-mock`
- `camel-mongodb`
- `camel-mongodb-gridfs`
- `camel-mustache`
- `camel-mvel`
- `camel-mybatis`
- `camel-nats`
- `camel-neo4j`
- `camel-netty`
- `camel-netty-http`
- `camel-oaipmh`
- `camel-oauth`
- `camel-observability-services`
- `camel-observation`
- `camel-ocsf`
- `camel-ognl`
- `camel-olingo2`
- `camel-olingo2-api`
- `camel-olingo2-parent`
- `camel-olingo4`
- `camel-olingo4-api`
- `camel-olingo4-parent`
- `camel-once`
- `camel-openai`
- `camel-openapi-java`
- `camel-openapi-rest-dsl-generator`
- `camel-openapi-validator`
- `camel-opensearch`
- `camel-openstack`
- `camel-opentelemetry`
- `camel-opentelemetry-metrics`
- `camel-opentelemetry2`
- `camel-optaplanner`
- `camel-package-maven-plugin`
- `camel-paho`
- `camel-paho-mqtt5`
- `camel-parquet-avro`
- `camel-pdf`
- `camel-pg-replication-slot`
- `camel-pgevent`
- `camel-pgvector`
- `camel-pinecone`
- `camel-platform-http`
- `camel-platform-http-jolokia`
- `camel-platform-http-main`
- `camel-platform-http-vertx`
- `camel-plc4x`
- `camel-pqc`
- `camel-printer`
- `camel-protobuf`
- `camel-pubnub`
- `camel-pulsar`
- `camel-python`
- `camel-qdrant`
- `camel-quartz`
- `camel-quickfix`
- `camel-reactive-executor-tomcat`
- `camel-reactive-executor-vertx`
- `camel-reactive-streams`
- `camel-reactor`
- `camel-redis`
- `camel-ref`
- `camel-report-maven-plugin`
- `camel-resilience4j`
- `camel-resilience4j-micrometer`
- `camel-resourceresolver-github`
- `camel-rest`
- `camel-rest-openapi`
- `camel-restdsl-openapi-plugin`
- `camel-robotframework`
- `camel-rocketmq`
- `camel-route-parser`
- `camel-rss`
- `camel-rxjava`
- `camel-saga`
- `camel-salesforce`
- `camel-salesforce-codegen`
- `camel-salesforce-maven-plugin`
- `camel-salesforce-parent`
- `camel-sap-netweaver`
- `camel-saxon`
- `camel-scheduler`
- `camel-schematron`
- `camel-seda`
- `camel-servicenow`
- `camel-servicenow-maven-plugin`
- `camel-servicenow-parent`
- `camel-servlet`
- `camel-shell`
- `camel-shiro`
- `camel-sjms`
- `camel-sjms2`
- `camel-slack`
- `camel-smb`
- `camel-smooks`
- `camel-smpp`
- `camel-snakeyaml`
- `camel-snmp`
- `camel-soap`
- `camel-solr`
- `camel-splunk`
- `camel-splunk-hec`
- `camel-spring`
- `camel-spring-ai-chat`
- `camel-spring-ai-embeddings`
- `camel-spring-ai-image`
- `camel-spring-ai-parent`
- `camel-spring-ai-vector-store`
- `camel-spring-batch`
- `camel-spring-cloud-config`
- `camel-spring-jdbc`
- `camel-spring-ldap`
- `camel-spring-main`
- `camel-spring-parent`
- `camel-spring-rabbitmq`
- `camel-spring-redis`
- `camel-spring-security`
- `camel-spring-ws`
- `camel-spring-xml`
- `camel-sql`
- `camel-ssh`
- `camel-stax`
- `camel-stitch`
- `camel-stream`
- `camel-streamcaching-test`
- `camel-stringtemplate`
- `camel-stripe`
- `camel-stub`
- `camel-support`
- `camel-swift`
- `camel-syslog`
- `camel-tahu`
- `camel-tarfile`
- `camel-telegram`
- `camel-telemetry`
- `camel-telemetry-dev`
- `camel-tensorflow-serving`
- `camel-test-infra-all`
- `camel-test-infra-artemis`
- `camel-test-infra-cli`
- `camel-test-infra-core`
- `camel-test-infra-jetty`
- `camel-test-infra-qdrant`
- `camel-test-infra-smb`
- `camel-test-junit5`
- `camel-test-junit6`
- `camel-test-main-junit5`
- `camel-test-main-junit6`
- `camel-test-parent`
- `camel-test-spring-junit5`
- `camel-test-spring-junit6`
- `camel-threadpoolfactory-vertx`
- `camel-thrift`
- `camel-thymeleaf`
- `camel-tika`
- `camel-timer`
- `camel-tooling-maven`
- `camel-tracing`
- `camel-twilio`
- `camel-twitter`
- `camel-undertow`
- `camel-undertow-spring-security`
- `camel-univocity-parsers`
- `camel-util`
- `camel-validator`
- `camel-velocity`
- `camel-vertx`
- `camel-vertx-common`
- `camel-vertx-http`
- `camel-vertx-parent`
- `camel-vertx-websocket`
- `camel-wal`
- `camel-wasm`
- `camel-weather`
- `camel-weaviate`
- `camel-web3j`
- `camel-webhook`
- `camel-whatsapp`
- `camel-wordpress`
- `camel-workday`
- `camel-xchange`
- `camel-xj`
- `camel-xml-io`
- `camel-xml-io-dsl`
- `camel-xml-io-util`
- `camel-xml-jaxb`
- `camel-xml-jaxb-dsl`
- `camel-xml-jaxb-dsl-test`
- `camel-xml-jaxb-dsl-test-definition`
- `camel-xml-jaxb-dsl-test-spring`
- `camel-xml-jaxp`
- `camel-xml-jaxp-util`
- `camel-xmlsecurity`
- `camel-xmpp`
- `camel-xpath`
- `camel-xslt`
- `camel-xslt-saxon`
- `camel-yaml-dsl`
- `camel-yaml-dsl-common`
- `camel-yaml-dsl-deserializers`
- `camel-yaml-dsl-maven-plugin`
- `camel-yaml-dsl-parent`
- `camel-yaml-dsl-validator`
- `camel-yaml-dsl-validator-maven-plugin`
- `camel-yaml-io`
- `camel-zeebe`
- `camel-zendesk`
- `camel-zip-deflater`
- `camel-zipfile`
- `camel-zookeeper`
- `camel-zookeeper-master`
- `components`
- `core`
- `coverage`
- `docs`
- `dsl`
- `dummy-component`
- `sync-properties-maven-plugin`
</details>
<details><summary>Modules only in current approach (76)</summary>
- `[ERROR]`
- `[ERROR]`
- `[ERROR] Command execution failed.`
- `[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.6.3:exec (default-cli) on project camel-package-maven-plugin: Command execution failed. Process exited with an error: 127 (Exit value: 127) -> [Help 1]`
- `[ERROR] For more information about the errors and possible solutions, please read the following articles:`
- `[ERROR] Re-run Maven using the -X switch to enable full debug logging.`
- `[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.`
- `[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException`
- `at com.gradle.maven.cache.extension.c.a.a (SourceFile:46)`
- `at com.gradle.maven.cache.extension.c.a.a (SourceFile:46)`
- `at com.gradle.maven.cache.extension.c.b.a (SourceFile:62)`
- `at com.gradle.maven.cache.extension.c.b.c (SourceFile:118)`
- `at com.gradle.maven.cache.extension.c.c.a (SourceFile:26)`
- `at com.gradle.maven.cache.extension.c.d.a (SourceFile:27)`
- `at com.gradle.maven.cache.extension.c.g.a (SourceFile:27)`
- `at com.gradle.maven.cache.extension.c.h$1.run (SourceFile:35)`
- `at com.gradle.maven.cache.extension.c.h.a (SourceFile:31)`
- `at com.gradle.maven.cache.extension.c.j.a (SourceFile:28)`
- `at com.gradle.maven.cache.extension.c.m.a (SourceFile:80)`
- `at com.gradle.maven.cache.extension.c.n.a (SourceFile:53)`
- `at com.gradle.maven.cache.extension.c.o.a (SourceFile:18)`
- `at com.gradle.maven.cache.extension.c.p.a (SourceFile:27)`
- `at com.gradle.maven.cache.extension.c.q.a (SourceFile:23)`
- `at com.gradle.maven.cache.extension.g.b.lambda$createProxy$0 (SourceFile:77)`
- `at com.gradle.maven.scan.extension.internal.e.b.executeMojo (SourceFile:116)`
- `at java.lang.reflect.Method.invoke (Method.java:569)`
- `at java.lang.reflect.Method.invoke (Method.java:569)`
- `at java.lang.reflect.Method.invoke (Method.java:569)`
- `at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)`
- `at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)`
- `at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)`
- `at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:77)`
- `at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:77)`
- `at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:77)`
- `at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)`
- `at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)`
- `at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)`
- `at jdk.proxy5.$Proxy69.execute (Unknown Source)`
- `at org.apache.commons.exec.DefaultExecutor.execute (DefaultExecutor.java:306)`
- `at org.apache.commons.exec.DefaultExecutor.executeInternal (DefaultExecutor.java:408)`
- `at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:173)`
- `at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:261)`
- `at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:101)`
- `at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:285)`
- `at org.apache.maven.cli.MavenCli.execute (MavenCli.java:919)`
- `at org.apache.maven.cli.MavenCli.main (MavenCli.java:207)`
- `at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:105)`
- `at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:73)`
- `at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:118)`
- `at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:162)`
- `at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:75)`
- `at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:316)`
- `at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:328)`
- `at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:159)`
- `at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:174)`
- `at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:212)`
- `at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:53)`
- `at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)`
- `at org.apache.maven.wrapper.BootstrapMainStarter.start (BootstrapMainStarter.java:52)`
- `at org.apache.maven.wrapper.MavenWrapperMain.main (MavenWrapperMain.java:73)`
- `at org.apache.maven.wrapper.WrapperExecutor.execute (WrapperExecutor.java:161)`
- `at org.codehaus.mojo.exec.ExecMojo.execute (ExecMojo.java:464)`
- `at org.codehaus.mojo.exec.ExecMojo.executeCommandLine (ExecMojo.java:873)`
- `at org.codehaus.mojo.exec.ExecMojo.executeCommandLine (ExecMojo.java:913)`
- `at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:201)`
- `at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:255)`
- `at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:314)`
- `at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:362)`
- `at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute (SourceFile:27)`
- `at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute (SourceFile:30)`
- `at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute (SourceFile:60)`
- `at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute (SourceFile:67)`
- `at org.gradle.internal.operations.DefaultBuildOperationRunner.execute (SourceFile:167)`
- `at org.gradle.internal.operations.DefaultBuildOperationRunner.execute (SourceFile:60)`
- `at org.gradle.internal.operations.DefaultBuildOperationRunner.run (SourceFile:48)`
- `org.apache.commons.exec.ExecuteException: Process exited with an error: 127 (Exit value: 127)`
</details>
Changed managed dependencies: org.apache.camel:camel-ai-observability, org.apache.camel:camel-ai-observability-api
**Skip-tests mode would test 579 modules** (14 direct + 565 downstream), **skip tests for 25** (generated code, meta-modules)
<details><summary>Modules Scalpel would test (579)</summary>
- `archetypes`
- `camel-a2a`
- `camel-activemq`
- `camel-activemq6`
- `camel-ai-observability`
- `camel-ai-observability-api`
- `camel-ai-parent`
- `camel-ai-tool`
- `camel-allcomponents`
- `camel-amqp`
- `camel-api`
- `camel-api-component-maven-plugin`
- `camel-arangodb`
- `camel-archetype-api-component`
- `camel-archetype-component`
- `camel-archetype-dataformat`
- `camel-archetype-java`
- `camel-archetype-main`
- `camel-archetype-spring`
- `camel-as2`
- `camel-as2-api`
- `camel-as2-parent`
- `camel-asn1`
- `camel-asterisk`
- `camel-atmosphere-websocket`
- `camel-atom`
- `camel-attachments`
- `camel-avro`
- `camel-avro-rpc`
- `camel-avro-rpc-jetty`
- `camel-avro-rpc-parent`
- `camel-avro-rpc-spi`
- `camel-aws-bedrock`
- `camel-aws-cloudtrail`
- `camel-aws-common`
- `camel-aws-config`
- `camel-aws-parameter-store`
- `camel-aws-parent`
- `camel-aws-secrets-manager`
- `camel-aws-security-hub`
- `camel-aws2-athena`
- `camel-aws2-comprehend`
- `camel-aws2-cw`
- `camel-aws2-ddb`
- `camel-aws2-ec2`
- `camel-aws2-ecs`
- `camel-aws2-eks`
- `camel-aws2-eventbridge`
- `camel-aws2-iam`
- `camel-aws2-kinesis`
- `camel-aws2-kms`
- `camel-aws2-lambda`
- `camel-aws2-mq`
- `camel-aws2-msk`
- `camel-aws2-polly`
- `camel-aws2-redshift`
- `camel-aws2-rekognition`
- `camel-aws2-s3`
- `camel-aws2-s3-vectors`
- `camel-aws2-ses`
- `camel-aws2-sns`
- `camel-aws2-sqs`
- `camel-aws2-step-functions`
- `camel-aws2-sts`
- `camel-aws2-textract`
- `camel-aws2-timestream`
- `camel-aws2-transcribe`
- `camel-aws2-translate`
- `camel-azure-common`
- `camel-azure-cosmosdb`
- `camel-azure-eventgrid`
- `camel-azure-eventhubs`
- `camel-azure-files`
- `camel-azure-functions`
- `camel-azure-key-vault`
- `camel-azure-parent`
- `camel-azure-schema-registry`
- `camel-azure-servicebus`
- `camel-azure-storage-blob`
- `camel-azure-storage-datalake`
- `camel-azure-storage-queue`
- `camel-barcode`
- `camel-base`
- `camel-base-engine`
- `camel-base64`
- `camel-bean`
- `camel-bean-validator`
- `camel-beanio`
- `camel-bindy`
- `camel-bom`
- `camel-bonita`
- `camel-box`
- `camel-box-api`
- `camel-box-parent`
- `camel-braintree`
- `camel-browse`
- `camel-caffeine`
- `camel-camunda`
- `camel-cassandraql`
- `camel-catalog`
- `camel-catalog-common`
- `camel-cbor`
- `camel-chatscript`
- `camel-chunk`
- `camel-cli-connector`
- `camel-cli-debug`
- `camel-clickhouse`
- `camel-clickup`
- `camel-cloudevents`
- `camel-cluster`
- `camel-cm-sms`
- `camel-coap`
- `camel-cometd`
- `camel-component-maven-plugin`
- `camel-console`
- `camel-consul`
- `camel-controlbus`
- `camel-core`
- `camel-core-all`
- `camel-core-catalog`
- `camel-core-engine`
- `camel-core-languages`
- `camel-core-model`
- `camel-core-processor`
- `camel-core-reifier`
- `camel-core-xml`
- `camel-couchbase`
- `camel-couchdb`
- `camel-cron`
- `camel-crypto`
- `camel-crypto-pgp`
- `camel-csimple-joor`
- `camel-csv`
- `camel-cxf-common`
- `camel-cxf-parent`
- `camel-cxf-rest`
- `camel-cxf-soap`
- `camel-cxf-spring-common`
- `camel-cxf-spring-rest`
- `camel-cxf-spring-soap`
- `camel-cxf-spring-transport`
- `camel-cxf-transport`
- `camel-cyberark-vault`
- `camel-dapr`
- `camel-dataformat`
- `camel-dataset`
- `camel-datasonnet`
- `camel-debezium-common`
- `camel-debezium-common-parent`
- `camel-debezium-db2`
- `camel-debezium-maven-plugin`
- `camel-debezium-mongodb`
- `camel-debezium-mysql`
- `camel-debezium-oracle`
- `camel-debezium-parent`
- `camel-debezium-postgres`
- `camel-debezium-sqlserver`
- `camel-debug`
- `camel-dependencies`
- `camel-dfdl`
- `camel-dhis2`
- `camel-dhis2-api`
- `camel-dhis2-parent`
- `camel-diagram`
- `camel-digitalocean`
- `camel-direct`
- `camel-disruptor`
- `camel-djl`
- `camel-dns`
- `camel-docker`
- `camel-docling`
- `camel-drill`
- `camel-dropbox`
- `camel-dsl-modeline`
- `camel-dsl-support`
- `camel-duckdb`
- `camel-dynamic-router`
- `camel-ehcache`
- `camel-eip-documentation-enricher-maven-plugin`
- `camel-elasticsearch`
- `camel-elasticsearch-rest-client`
- `camel-endpointdsl`
- `camel-event`
- `camel-exec`
- `camel-fastjson`
- `camel-fhir`
- `camel-fhir-api`
- `camel-fhir-parent`
- `camel-file`
- `camel-file-watch`
- `camel-flatpack`
- `camel-flink`
- `camel-flowable`
- `camel-fop`
- `camel-fory`
- `camel-freemarker`
- `camel-ftp`
- `camel-ftp-common`
- `camel-geocoder`
- `camel-git`
- `camel-github2`
- `camel-google-bigquery`
- `camel-google-calendar`
- `camel-google-common`
- `camel-google-drive`
- `camel-google-firestore`
- `camel-google-functions`
- `camel-google-mail`
- `camel-google-parent`
- `camel-google-pubsub`
- `camel-google-secret-manager`
- `camel-google-sheets`
- `camel-google-speech-to-text`
- `camel-google-storage`
- `camel-google-text-to-speech`
- `camel-google-vertexai`
- `camel-google-vision`
- `camel-graphql`
- `camel-grok`
- `camel-groovy`
- `camel-grpc`
- `camel-gson`
- `camel-hashicorp-vault`
- `camel-hazelcast`
- `camel-headersmap`
- `camel-health`
- `camel-hl7`
- `camel-http`
- `camel-http-base`
- `camel-http-common`
- `camel-huawei-parent`
- `camel-huaweicloud-common`
- `camel-huaweicloud-dms`
- `camel-huaweicloud-frs`
- `camel-huaweicloud-functiongraph`
- `camel-huaweicloud-iam`
- `camel-huaweicloud-imagerecognition`
- `camel-huaweicloud-obs`
- `camel-huaweicloud-smn`
- `camel-huggingface`
- `camel-ibm-cos`
- `camel-ibm-parent`
- `camel-ibm-secrets-manager`
- `camel-ibm-watson-discovery`
- `camel-ibm-watson-language`
- `camel-ibm-watson-speech-to-text`
- `camel-ibm-watson-text-to-speech`
- `camel-ibm-watsonx-ai`
- `camel-ibm-watsonx-data`
- `camel-ical`
- `camel-iec60870`
- `camel-iggy`
- `camel-ignite`
- `camel-infinispan`
- `camel-infinispan-common`
- `camel-infinispan-embedded`
- `camel-infinispan-parent`
- `camel-influxdb`
- `camel-influxdb2`
- `camel-irc`
- `camel-ironmq`
- `camel-iso8583`
- `camel-jackson`
- `camel-jackson-avro`
- `camel-jackson-protobuf`
- `camel-jackson3`
- `camel-jackson3-avro`
- `camel-jackson3-protobuf`
- `camel-jackson3xml`
- `camel-jacksonxml`
- `camel-jactl`
- `camel-jandex`
- `camel-jasypt`
- `camel-java-io`
- `camel-java-joor-dsl`
- `camel-javascript`
- `camel-jaxb`
- `camel-jbang-console`
- `camel-jbang-mcp`
- `camel-jbang-parent`
- `camel-jbang-plugin-mcp`
- `camel-jbang-plugin-route-parser`
- `camel-jbang-plugin-tui`
- `camel-jbang-plugin-validate`
- `camel-jcache`
- `camel-jcr`
- `camel-jdbc`
- `camel-jetty`
- `camel-jetty-common`
- `camel-jfr`
- `camel-jgroups`
- `camel-jgroups-raft`
- `camel-jira`
- `camel-jms`
- `camel-jmx`
- `camel-jolt`
- `camel-jooq`
- `camel-joor`
- `camel-jpa`
- `camel-jq`
- `camel-jsch`
- `camel-jslt`
- `camel-json-patch`
- `camel-json-validator`
- `camel-jsonapi`
- `camel-jsonata`
- `camel-jsonb`
- `camel-jsonpath`
- `camel-jsoup`
- `camel-jt400`
- `camel-jta`
- `camel-jte`
- `camel-kafka`
- `camel-kamelet`
- `camel-kamelet-main-support`
- `camel-keycloak`
- `camel-knative`
- `camel-knative-api`
- `camel-knative-http`
- `camel-knative-parent`
- `camel-kserve`
- `camel-kubernetes`
- `camel-kudu`
- `camel-langchain4j-agent`
- `camel-langchain4j-agent-api`
- `camel-langchain4j-chat`
- `camel-langchain4j-core`
- `camel-langchain4j-embeddings`
- `camel-langchain4j-embeddingstore`
- `camel-langchain4j-embeddingstore-api`
- `camel-langchain4j-tokenizer`
- `camel-langchain4j-tools`
- `camel-langchain4j-web-search`
- `camel-language`
- `camel-launcher-container`
- `camel-ldap`
- `camel-ldif`
- `camel-leveldb`
- `camel-log`
- `camel-lra`
- `camel-lucene`
- `camel-lumberjack`
- `camel-lzf`
- `camel-mail`
- `camel-mail-microsoft-oauth`
- `camel-main`
- `camel-management`
- `camel-management-api`
- `camel-mapstruct`
- `camel-master`
- `camel-maven-plugin`
- `camel-mcp-server`
- `camel-mcp-server-api`
- `camel-mdc`
- `camel-metrics`
- `camel-micrometer`
- `camel-micrometer-observability`
- `camel-micrometer-prometheus`
- `camel-microprofile-config`
- `camel-microprofile-fault-tolerance`
- `camel-microprofile-health`
- `camel-microprofile-parent`
- `camel-milo`
- `camel-milvus`
- `camel-mina`
- `camel-mina-sftp`
- `camel-minio`
- `camel-mllp`
- `camel-mock`
- `camel-mongodb`
- `camel-mongodb-gridfs`
- `camel-mustache`
- `camel-mvel`
- `camel-mybatis`
- `camel-nats`
- `camel-neo4j`
- `camel-netty`
- `camel-netty-http`
- `camel-oaipmh`
- `camel-oauth`
- `camel-observability-services`
- `camel-observation`
- `camel-ocsf`
- `camel-ognl`
- `camel-olingo2`
- `camel-olingo2-api`
- `camel-olingo2-parent`
- `camel-olingo4`
- `camel-olingo4-api`
- `camel-olingo4-parent`
- `camel-once`
- `camel-openai`
- `camel-openapi-java`
- `camel-openapi-rest-dsl-generator`
- `camel-openapi-validator`
- `camel-opensearch`
- `camel-openstack`
- `camel-opentelemetry`
- `camel-opentelemetry-metrics`
- `camel-opentelemetry2`
- `camel-optaplanner`
- `camel-package-maven-plugin`
- `camel-paho`
- `camel-paho-mqtt5`
- `camel-parquet-avro`
- `camel-pdf`
- `camel-pg-replication-slot`
- `camel-pgevent`
- `camel-pgvector`
- `camel-pinecone`
- `camel-platform-http`
- `camel-platform-http-jolokia`
- `camel-platform-http-main`
- `camel-platform-http-vertx`
- `camel-plc4x`
- `camel-pqc`
- `camel-printer`
- `camel-protobuf`
- `camel-pubnub`
- `camel-pulsar`
- `camel-python`
- `camel-qdrant`
- `camel-quartz`
- `camel-quickfix`
- `camel-reactive-executor-tomcat`
- `camel-reactive-executor-vertx`
- `camel-reactive-streams`
- `camel-reactor`
- `camel-redis`
- `camel-ref`
- `camel-resilience4j`
- `camel-resilience4j-micrometer`
- `camel-resourceresolver-github`
- `camel-rest`
- `camel-rest-openapi`
- `camel-restdsl-openapi-plugin`
- `camel-robotframework`
- `camel-rocketmq`
- `camel-rss`
- `camel-rxjava`
- `camel-saga`
- `camel-salesforce`
- `camel-salesforce-codegen`
- `camel-salesforce-maven-plugin`
- `camel-salesforce-parent`
- `camel-sap-netweaver`
- `camel-saxon`
- `camel-scheduler`
- `camel-schematron`
- `camel-seda`
- `camel-servicenow`
- `camel-servicenow-maven-plugin`
- `camel-servicenow-parent`
- `camel-servlet`
- `camel-shell`
- `camel-shiro`
- `camel-sjms`
- `camel-sjms2`
- `camel-slack`
- `camel-smb`
- `camel-smooks`
- `camel-smpp`
- `camel-snakeyaml`
- `camel-snmp`
- `camel-soap`
- `camel-solr`
- `camel-splunk`
- `camel-splunk-hec`
- `camel-spring`
- `camel-spring-ai-chat`
- `camel-spring-ai-embeddings`
- `camel-spring-ai-image`
- `camel-spring-ai-parent`
- `camel-spring-ai-vector-store`
- `camel-spring-batch`
- `camel-spring-cloud-config`
- `camel-spring-jdbc`
- `camel-spring-ldap`
- `camel-spring-main`
- `camel-spring-parent`
- `camel-spring-rabbitmq`
- `camel-spring-redis`
- `camel-spring-security`
- `camel-spring-ws`
- `camel-spring-xml`
- `camel-sql`
- `camel-ssh`
- `camel-stax`
- `camel-stitch`
- `camel-stream`
- `camel-streamcaching-test`
- `camel-stringtemplate`
- `camel-stripe`
- `camel-stub`
- `camel-support`
- `camel-swift`
- `camel-syslog`
- `camel-tahu`
- `camel-tarfile`
- `camel-telegram`
- `camel-telemetry`
- `camel-telemetry-dev`
- `camel-tensorflow-serving`
- `camel-test-infra-all`
- `camel-test-infra-artemis`
- `camel-test-infra-cli`
- `camel-test-infra-core`
- `camel-test-infra-jetty`
- `camel-test-infra-qdrant`
- `camel-test-infra-smb`
- `camel-test-junit5`
- `camel-test-junit6`
- `camel-test-main-junit5`
- `camel-test-main-junit6`
- `camel-test-parent`
- `camel-test-spring-junit5`
- `camel-test-spring-junit6`
- `camel-threadpoolfactory-vertx`
- `camel-thrift`
- `camel-thymeleaf`
- `camel-tika`
- `camel-timer`
- `camel-tooling-maven`
- `camel-tracing`
- `camel-twilio`
- `camel-twitter`
- `camel-undertow`
- `camel-undertow-spring-security`
- `camel-univocity-parsers`
- `camel-util`
- `camel-validator`
- `camel-velocity`
- `camel-vertx`
- `camel-vertx-common`
- `camel-vertx-http`
- `camel-vertx-parent`
- `camel-vertx-websocket`
- `camel-wal`
- `camel-wasm`
- `camel-weather`
- `camel-weaviate`
- `camel-web3j`
- `camel-webhook`
- `camel-whatsapp`
- `camel-wordpress`
- `camel-workday`
- `camel-xchange`
- `camel-xj`
- `camel-xml-io`
- `camel-xml-io-dsl`
- `camel-xml-io-util`
- `camel-xml-jaxb`
- `camel-xml-jaxb-dsl`
- `camel-xml-jaxb-dsl-test`
- `camel-xml-jaxb-dsl-test-definition`
- `camel-xml-jaxb-dsl-test-spring`
- `camel-xml-jaxp`
- `camel-xml-jaxp-util`
- `camel-xmlsecurity`
- `camel-xmpp`
- `camel-xpath`
- `camel-xslt`
- `camel-xslt-saxon`
- `camel-yaml-dsl-common`
- `camel-yaml-dsl-parent`
- `camel-yaml-dsl-validator`
- `camel-yaml-dsl-validator-maven-plugin`
- `camel-yaml-io`
- `camel-zeebe`
- `camel-zendesk`
- `camel-zip-deflater`
- `camel-zipfile`
- `camel-zookeeper`
- `camel-zookeeper-master`
- `components`
- `core`
- `docs`
- `dsl`
- `sync-properties-maven-plugin`
</details>
<details><summary>Modules with tests skipped (25)</summary>
- `apache-camel`
- `camel-catalog-console`
- `camel-catalog-lucene`
- `camel-catalog-maven`
- `camel-catalog-suggest`
- `camel-componentdsl`
- `camel-csimple-maven-plugin`
- `camel-endpointdsl-support`
- `camel-itest`
- `camel-jbang-core`
- `camel-jbang-it`
- `camel-jbang-main`
- `camel-jbang-plugin-edit`
- `camel-jbang-plugin-generate`
- `camel-jbang-plugin-kubernetes`
- `camel-jbang-plugin-test`
- `camel-kamelet-main`
- `camel-launcher`
- `camel-report-maven-plugin`
- `camel-route-parser`
- `camel-yaml-dsl`
- `camel-yaml-dsl-deserializers`
- `camel-yaml-dsl-maven-plugin`
- `coverage`
- `dummy-component`
</details>
> :information_source: Shadow mode — Scalpel observes but does not affect test execution. [Learn more](https://github.com/maveniverse/scalpel)
</details>
:warning: **Some tests are disabled on GitHub Actions** (`@DisabledIfSystemProperty(named = "ci.env.name")`) and require manual verification:
- `components/camel-ai`: 39 test(s) disabled on GitHub Actions
- `components/camel-ai/camel-langchain4j-agent`: 16 test(s) disabled on GitHub Actions
- `components/camel-ai/camel-langchain4j-agent`: 16 test(s) disabled on GitHub Actions
- `components/camel-ai/camel-langchain4j-chat`: 1 test(s) disabled on GitHub Actions
- `components/camel-ai/camel-langchain4j-tools`: 6 test(s) disabled on GitHub Actions
- `components/camel-ai/camel-openai`: 7 test(s) disabled on GitHub Actions
<details><summary>Build reactor — dependencies compiled but only changed modules were tested (604 modules)</summary>
- Camel :: AI :: A2A
- Camel :: AI :: ChatScript
- Camel :: AI :: Deep Java Library
- Camel :: AI :: Docling
- Camel :: AI :: Hugging Face
- Camel :: AI :: KServe
- Camel :: AI :: LangChain4j :: Agent
- Camel :: AI :: LangChain4j :: Agent :: API
- Camel :: AI :: LangChain4j :: Chat
- Camel :: AI :: LangChain4j :: Core
- Camel :: AI :: LangChain4j :: Embedding
- Camel :: AI :: LangChain4j :: Embedding Store :: API
- Camel :: AI :: LangChain4j :: EmbeddingStore
- Camel :: AI :: LangChain4j :: Tokenizer
- Camel :: AI :: LangChain4j :: Tools (deprecated)
- Camel :: AI :: LangChain4j :: Web Search
- Camel :: AI :: MCP Server
- Camel :: AI :: MCP Server API
- Camel :: AI :: Milvus
- Camel :: AI :: Neo4j
- Camel :: AI :: Observability
- Camel :: AI :: Observability API
- Camel :: AI :: OpenAI
- Camel :: AI :: PGVector
- Camel :: AI :: Parent
- Camel :: AI :: Pinecone
- Camel :: AI :: Qdrant
- Camel :: AI :: TensorFlow Serving
- Camel :: AI :: Tool
- Camel :: AI :: Weaviate
- Camel :: AMQP
- Camel :: API
- Camel :: AS2 :: API
- Camel :: AS2 :: Component
- Camel :: AS2 :: Parent
- Camel :: ASN.1
- Camel :: AWS :: Common
- Camel :: AWS :: Parent
- Camel :: AWS CloudTrail
- Camel :: AWS Config
- Camel :: AWS Redshift Data
- Camel :: AWS Rekognition
- Camel :: AWS Security Hub
- Camel :: AWS Step Functions
- Camel :: AWS Timestream
- Camel :: AWS2 :: Transcribe
- Camel :: AWS2 Athena
- Camel :: AWS2 Bedrock
- Camel :: AWS2 CW
- Camel :: AWS2 Comprehend
- Camel :: AWS2 DDB
- Camel :: AWS2 EC2
- Camel :: AWS2 ECS
- Camel :: AWS2 EKS
- Camel :: AWS2 Eventbridge
- Camel :: AWS2 IAM
- Camel :: AWS2 KMS
- Camel :: AWS2 Kinesis
- Camel :: AWS2 Lambda
- Camel :: AWS2 MQ
- Camel :: AWS2 MSK
- Camel :: AWS2 Parameter Store
- Camel :: AWS2 Polly
- Camel :: AWS2 S3
- Camel :: AWS2 S3 Vectors
- Camel :: AWS2 SES
- Camel :: AWS2 SNS
- Camel :: AWS2 SQS
- Camel :: AWS2 STS
- Camel :: AWS2 Secrets Manager
- Camel :: AWS2 Textract
- Camel :: AWS2 Translate
- Camel :: ActiveMQ 5.x
- Camel :: ActiveMQ 6.x
- Camel :: All Components Sync point
- Camel :: All Core Sync point
- Camel :: ArangoDB
- Camel :: Archetypes
- Camel :: Archetypes :: API Component
- Camel :: Archetypes :: Component
- Camel :: Archetypes :: Data Format
- Camel :: Archetypes :: Java Router
- Camel :: Archetypes :: Main
- Camel :: Archetypes :: Spring XML Based Router (deprecated)
- Camel :: Assembly
- Camel :: Asterisk
- Camel :: Atmosphere WebSocket Servlet
- Camel :: Atom
- Camel :: Attachments
- Camel :: Avro
- Camel :: Avro RPC
- Camel :: Avro RPC :: Jetty
- Camel :: Avro RPC :: Parent
- Camel :: Avro RPC :: Spi
- Camel :: Azure :: Common
- Camel :: Azure :: CosmosDB
- Camel :: Azure :: Event Grid
- Camel :: Azure :: Event Hubs
- Camel :: Azure :: Files
- Camel :: Azure :: Functions
- Camel :: Azure :: Key Vault
- Camel :: Azure :: Parent
- Camel :: Azure :: Schema Registry
- Camel :: Azure :: ServiceBus
- Camel :: Azure :: Storage Blob
- Camel :: Azure :: Storage Datalake
- Camel :: Azure :: Storage Queue
- Camel :: BOM
- Camel :: Barcode
- Camel :: Base
- Camel :: Base Engine
- Camel :: Base64
- Camel :: Bean
- Camel :: Bean validator
- Camel :: BeanIO
- Camel :: Bindy
- Camel :: Bonita
- Camel :: Box :: API
- Camel :: Box :: Component
- Camel :: Box :: Parent
- Camel :: Braintree
- Camel :: Browse
- Camel :: CBOR
- Camel :: CM SMS
- Camel :: CSV
- Camel :: CXF :: Common
- Camel :: CXF :: Common :: Spring
- Camel :: CXF :: Parent
- Camel :: CXF :: REST
- Camel :: CXF :: REST :: Spring
- Camel :: CXF :: SOAP
- Camel :: CXF :: SOAP :: Spring
- Camel :: CXF :: Transport
- Camel :: CXF :: Transport :: Spring
- Camel :: Caffeine
- Camel :: Camunda
- Camel :: Cassandra CQL
- Camel :: Catalog :: CSimple Maven Plugin (deprecated)
- Camel :: Catalog :: Camel Catalog
- Camel :: Catalog :: Camel Report Maven Plugin
- Camel :: Catalog :: Camel Route Parser
- Camel :: Catalog :: Common
- Camel :: Catalog :: Console
- Camel :: Catalog :: Dummy Component
- Camel :: Catalog :: Lucene (deprecated)
- Camel :: Catalog :: Maven
- Camel :: Catalog :: Suggest
- Camel :: Chunk
- Camel :: ClickHouse
- Camel :: ClickUp
- Camel :: CloudEvents
- Camel :: Cluster
- Camel :: CoAP
- Camel :: Cometd
- Camel :: Common Telemetry
- Camel :: Common Tracing (deprecated)
- Camel :: Component DSL
- Camel :: Components
- Camel :: Console
- Camel :: Consul
- Camel :: Controlbus
- Camel :: Core
- Camel :: Core Catalog
- Camel :: Core Engine
- Camel :: Core Languages
- Camel :: Core Model
- Camel :: Core Modules
- Camel :: Core Processor
- Camel :: Core Reifier
- Camel :: Core XML
- Camel :: CouchDB
- Camel :: Couchbase
- Camel :: Coverage
- Camel :: Cron
- Camel :: Crypto
- Camel :: Crypto PGP
- Camel :: CyberArk Vault
- Camel :: DFDL
- Camel :: DHIS2
- Camel :: DHIS2 :: Parent
- Camel :: DHIS2 API
- Camel :: DNS
- Camel :: DSL :: CLI Connector
- Camel :: DSL :: CLI Debug
- Camel :: DSL :: Modeline
- Camel :: DSL :: Parent
- Camel :: DSL :: Support
- Camel :: Dapr
- Camel :: DataSet
- Camel :: DataSonnet
- Camel :: Dataformat
- Camel :: Debezium :: Common
- Camel :: Debezium :: Common :: Parent
- Camel :: Debezium :: DB2
- Camel :: Debezium :: Maven Plugin
- Camel :: Debezium :: MongoDB
- Camel :: Debezium :: MySQL
- Camel :: Debezium :: Oracle
- Camel :: Debezium :: Parent
- Camel :: Debezium :: PostgreSQL
- Camel :: Debezium :: SQL Server
- Camel :: Debugging
- Camel :: Dependencies
- Camel :: Diagram
- Camel :: DigitalOcean (deprecated)
- Camel :: Direct
- Camel :: Disruptor
- Camel :: Docker
- Camel :: Docs
- Camel :: Drill
- Camel :: Dropbox
- Camel :: DuckDB
- Camel :: Dynamic Router
- Camel :: Ehcache
- Camel :: ElasticSearch Rest Client
- Camel :: Elasticsearch Java API Client
- Camel :: Endpoint DSL
- Camel :: Endpoint DSL :: Support
- Camel :: Event
- Camel :: Exec
- Camel :: FHIR
- Camel :: FHIR :: API
- Camel :: FHIR :: Parent
- Camel :: FOP
- Camel :: FTP
- Camel :: FTP Common
- Camel :: Fastjson
- Camel :: File
- Camel :: File Watch
- Camel :: FlatPack
- Camel :: Flink
- Camel :: Flowable
- Camel :: Fory
- Camel :: Freemarker
- Camel :: Geocoder
- Camel :: Git
- Camel :: GitHub2
- Camel :: Google :: BigQuery
- Camel :: Google :: Calendar
- Camel :: Google :: Common
- Camel :: Google :: Drive
- Camel :: Google :: Firestore
- Camel :: Google :: Functions
- Camel :: Google :: Mail
- Camel :: Google :: Parent
- Camel :: Google :: PubSub
- Camel :: Google :: Secret Manager
- Camel :: Google :: Sheets
- Camel :: Google :: Speech To Text
- Camel :: Google :: Storage
- Camel :: Google :: Text To Speech
- Camel :: Google :: Vertex AI
- Camel :: Google :: Vision
- Camel :: GraphQL
- Camel :: Grok
- Camel :: Groovy
- Camel :: Gson
- Camel :: HL7
- Camel :: HTTP
- Camel :: HTTP :: Base
- Camel :: HTTP :: Common
- Camel :: HashiCorp :: Key Vault
- Camel :: HazelCast
- Camel :: Headers Map (deprecated)
- Camel :: Health
- Camel :: Huawei Cloud :: Common
- Camel :: Huawei Cloud :: DMS
- Camel :: Huawei Cloud :: FaceRecognition
- Camel :: Huawei Cloud :: FunctionGraph
- Camel :: Huawei Cloud :: IAM
- Camel :: Huawei Cloud :: ImageRecognition
- Camel :: Huawei Cloud :: OBS
- Camel :: Huawei Cloud :: Parent
- Camel :: Huawei Cloud :: SimpleNotification
- Camel :: IBM :: Cloud Object Storage
- Camel :: IBM :: Parent
- Camel :: IBM :: Secrets Manager
- Camel :: IBM :: Watson Discovery
- Camel :: IBM :: Watson Language
- Camel :: IBM :: Watson Speech to Text
- Camel :: IBM :: Watson Text to Speech
- Camel :: IBM :: watsonx.ai
- Camel :: IBM :: watsonx.data
- Camel :: IEC 60870 (deprecated)
- Camel :: IRC (deprecated)
- Camel :: ISO-8583
- Camel :: Iggy
- Camel :: Ignite
- Camel :: Infinispan :: Common
- Camel :: Infinispan :: Embedded
- Camel :: Infinispan :: Parent
- Camel :: Infinispan :: Remote
- Camel :: InfluxDB
- Camel :: InfluxDB2
- Camel :: Integration Tests
- Camel :: Integration Tests :: Stream Caching Tests
- Camel :: IronMQ
- Camel :: JAXB
- Camel :: JBang :: Console
- Camel :: JBang :: Core
- Camel :: JBang :: Integration tests
- Camel :: JBang :: MCP
- Camel :: JBang :: Main
- Camel :: JBang :: Parent
- Camel :: JBang :: Plugin :: Edit
- Camel :: JBang :: Plugin :: Generate
- Camel :: JBang :: Plugin :: Kubernetes
- Camel :: JBang :: Plugin :: MCP
- Camel :: JBang :: Plugin :: Route Parser
- Camel :: JBang :: Plugin :: TUI
- Camel :: JBang :: Plugin :: Testing
- Camel :: JBang :: Plugin :: Validate
- Camel :: JCR
- Camel :: JCache
- Camel :: JDBC
- Camel :: JGroups
- Camel :: JGroups Raft
- Camel :: JIRA
- Camel :: JMS
- Camel :: JMX
- Camel :: JOOQ
- Camel :: JPA
- Camel :: JQ
- Camel :: JSON validator
- Camel :: JSON-B
- Camel :: JSONATA
- Camel :: JSon Path
- Camel :: JSonApi
- Camel :: JSoup
- Camel :: JTA
- Camel :: Jackson
- Camel :: Jackson 3
- Camel :: Jackson 3 Avro
- Camel :: Jackson 3 Protobuf
- Camel :: Jackson 3 XML
- Camel :: Jackson Avro
- Camel :: Jackson Protobuf
- Camel :: Jackson XML
- Camel :: Jactl
- Camel :: Jandex
- Camel :: Jasypt
- Camel :: Java DSL IO
- Camel :: Java DSL with jOOR
- Camel :: Java Flight Recorder
- Camel :: Java Template Engine
- Camel :: Java Toolbox for IBM i
- Camel :: JavaScript
- Camel :: Jetty
- Camel :: Jetty :: Common
- Camel :: Jolt
- Camel :: Jsch
- Camel :: Jslt
- Camel :: JsonPatch (deprecated)
- Camel :: Kafka
- Camel :: Kamelet
- Camel :: Kamelet Main
- Camel :: Kamelet Main :: Support
- Camel :: Keycloak
- Camel :: Knative :: Parent
- Camel :: Knative API
- Camel :: Knative Component
- Camel :: Knative HTTP
- Camel :: Kubernetes
- Camel :: Kudu
- Camel :: LDAP
- Camel :: LDIF
- Camel :: LZF
- Camel :: Language
- Camel :: Launcher
- Camel :: Launcher :: Container
- Camel :: LevelDB (deprecated)
- Camel :: Log
- Camel :: Long-Running-Action
- Camel :: Lucene
- Camel :: Lumberjack
- Camel :: MDC
- Camel :: MINA
- Camel :: MINA SFTP
- Camel :: MLLP
- Camel :: MVEL
- Camel :: Mail
- Camel :: Mail :: Microsoft OAuth
- Camel :: Main
- Camel :: Management
- Camel :: Management API
- Camel :: MapStruct
- Camel :: Master
- Camel :: Maven Plugins :: Camel API Component Plugin
- Camel :: Maven Plugins :: Camel Maven Component
- Camel :: Maven Plugins :: Camel Maven Package
- Camel :: Maven Plugins :: Camel Maven Plugin
- Camel :: Maven Plugins :: OpenApi REST DSL Generator
- Camel :: Maven Plugins :: Sync Properties
- Camel :: Maven Plugins :: XML DSL Doc Enricher
- Camel :: Metrics
- Camel :: MicroProfile :: Config
- Camel :: MicroProfile :: Fault Tolerance
- Camel :: MicroProfile :: Health
- Camel :: MicroProfile :: Parent
- Camel :: Micrometer
- Camel :: Micrometer :: Observability 2
- Camel :: Micrometer :: Observation (deprecated)
- Camel :: Micrometer :: Prometheus
- Camel :: Milo
- Camel :: MinIO
- Camel :: Mock
- Camel :: MongoDB
- Camel :: MongoDB GridFS
- Camel :: Mustache
- Camel :: MyBatis
- Camel :: Nats
- Camel :: Netty
- Camel :: Netty HTTP
- Camel :: OAIPMH
- Camel :: OAuth
- Camel :: OCSF
- Camel :: OGNL (deprecated)
- Camel :: Observability Services
- Camel :: Olingo2 (Deprecated) :: API
- Camel :: Olingo2 (Deprecated) :: Component
- Camel :: Olingo2 (Deprecated) :: Parent
- Camel :: Olingo4 (Deprecated) :: API
- Camel :: Olingo4 (Deprecated) :: Component
- Camel :: Olingo4 (Deprecated) :: Parent
- Camel :: Once
- Camel :: OpenAPI :: Validator
- Camel :: OpenApi Java
- Camel :: OpenSearch Java API Client
- Camel :: OpenStack
- Camel :: OpenTelemetry (deprecated)
- Camel :: Opentelemetry 2
- Camel :: Opentelemetry Metrics
- Camel :: OptaPlanner
- Camel :: PDF
- Camel :: PLC4X
- Camel :: PQC
- Camel :: Paho (deprecated)
- Camel :: Paho MQTT 5
- Camel :: Parent
- Camel :: Parquet Avro
- Camel :: PgEvent
- Camel :: PgReplicationSlot
- Camel :: Platform HTTP
- Camel :: Platform HTTP :: Jolokia
- Camel :: Platform HTTP :: Main
- Camel :: Platform HTTP :: Vert.x
- Camel :: Printer
- Camel :: Protobuf
- Camel :: PubNub
- Camel :: Pulsar
- Camel :: Python
- Camel :: Quartz
- Camel :: QuickFIX/J
- Camel :: REST
- Camel :: REST OpenApi
- Camel :: RSS
- Camel :: Reactive Executor :: Tomcat (deprecated)
- Camel :: Reactive Executor :: Vert.x (deprecated)
- Camel :: Reactive Streams
- Camel :: Reactor
- Camel :: Redis
- Camel :: Ref
- Camel :: Resilience4j
- Camel :: Resilience4j :: Micrometer
- Camel :: ResourceResolver GitHub
- Camel :: RobotFramework
- Camel :: RocketMQ
- Camel :: RxJava
- Camel :: SAP NetWeaver
- Camel :: SMB
- Camel :: SMPP
- Camel :: SNMP
- Camel :: SOAP
- Camel :: SQL
- Camel :: SSH
- Camel :: SWIFT
- Camel :: Saga
- Camel :: Salesforce
- Camel :: Salesforce :: CodeGen
- Camel :: Salesforce :: Maven Plugin
- Camel :: Salesforce :: Parent
- Camel :: Saxon
- Camel :: Scheduler
- Camel :: Schematron
- Camel :: Seda
- Camel :: ServiceNow :: Component
- Camel :: ServiceNow :: Maven Plugin
- Camel :: ServiceNow :: Parent
- Camel :: Servlet
- Camel :: Shell
- Camel :: Shiro
- Camel :: Simple JMS
- Camel :: Simple JMS2
- Camel :: Slack
- Camel :: Smooks :: Parent
- Camel :: SnakeYAML
- Camel :: Solr
- Camel :: Splunk (deprecated)
- Camel :: Splunk HEC
- Camel :: Spring
- Camel :: Spring :: Parent
- Camel :: Spring AI :: Chat
- Camel :: Spring AI :: Embeddings
- Camel :: Spring AI :: Image
- Camel :: Spring AI :: Parent
- Camel :: Spring AI :: Vector Store
- Camel :: Spring Batch
- Camel :: Spring Cloud Config
- Camel :: Spring JDBC
- Camel :: Spring LDAP
- Camel :: Spring Main
- Camel :: Spring RabbitMQ
- Camel :: Spring Redis
- Camel :: Spring Security
- Camel :: Spring Web Services
- Camel :: Spring XML
- Camel :: StAX
- Camel :: Stitch
- Camel :: Stream
- Camel :: StringTemplate
- Camel :: Stripe
- Camel :: Stub
- Camel :: Support
- Camel :: Syslog
- Camel :: Tahu
- Camel :: Tar File
- Camel :: Telegram
- Camel :: Telemetry :: Dev
- Camel :: Test :: JUnit5
- Camel :: Test :: JUnit6
- Camel :: Test :: Main :: JUnit5
- Camel :: Test :: Main :: JUnit6
- Camel :: Test :: Parent
- Camel :: Test :: Spring :: JUnit5
- Camel :: Test Infra :: All test services
- Camel :: Test Infra :: Artemis
- Camel :: Test Infra :: Cli (Camel CLI)
- Camel :: Test Infra :: Core
- Camel :: Test Infra :: Jetty
- Camel :: Test Infra :: Server Message Block
- Camel :: Test Infra :: qdrant
- Camel :: Thread Pool Factory :: Vert.x (deprecated)
- Camel :: Thrift
- Camel :: Thymeleaf
- Camel :: Tika
- Camel :: Timer
- Camel :: Tooling :: Maven
- Camel :: Tooling :: OpenApi REST DSL Generator
- Camel :: Twilio
- Camel :: Twitter
- Camel :: Undertow
- Camel :: Undertow Spring Security
- Camel :: UniVocity Parsers
- Camel :: Util
- Camel :: Validator
- Camel :: Velocity
- Camel :: Vert.x :: Common
- Camel :: Vert.x :: HTTP
- Camel :: Vert.x :: Parent
- Camel :: Vert.x :: WebSocket
- Camel :: Vertx
- Camel :: WAL
- Camel :: Wasm
- Camel :: Weather
- Camel :: Web3j
- Camel :: Webhook
- Camel :: Whatsapp
- Camel :: Wordpress
- Camel :: Workday
- Camel :: XChange
- Camel :: XJ
- Camel :: XML DSL Jaxb :: Test :: Definition
- Camel :: XML DSL Jaxb :: Test :: Spring
- Camel :: XML DSL Jaxb :: Tests
- Camel :: XML DSL with camel-xml-io
- Camel :: XML DSL with camel-xml-jaxb
- Camel :: XML IO
- Camel :: XML IO Util
- Camel :: XML JAXB
- Camel :: XML JAXP
- Camel :: XML JAXP Util
- Camel :: XML Security
- Camel :: XMPP
- Camel :: XPath
- Camel :: XSLT
- Camel :: XSLT Saxon
- Camel :: YAML DSL
- Camel :: YAML DSL :: Common
- Camel :: YAML DSL :: Deserializers
- Camel :: YAML DSL :: Maven Plugins
- Camel :: YAML DSL :: Parent
- Camel :: YAML DSL :: Validator
- Camel :: YAML DSL :: Validator Maven Plugin
- Camel :: YAML IO
- Camel :: Zeebe (deprecated)
- Camel :: Zendesk
- Camel :: Zip Deflater
- Camel :: Zip File
- Camel :: Zookeeper
- Camel :: Zookeeper Master
- Camel :: csimple jOOR (deprecated)
- Camel :: gRPC
- Camel :: iCal
- Camel :: jOOR
</details>
---
:gear: [View full build and test results](https://github.com/apache/camel/actions/runs/31077024949)
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "davsclaus (via GitHub)" <gi...@apache.org>.
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "davsclaus (via GitHub)" <gi...@apache.org>.
davsclaus merged PR #25337:
URL: https://github.com/apache/camel/pull/25337
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "davsclaus (via GitHub)" <gi...@apache.org>.
davsclaus commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5226358370
This needs to be updated to be for Camel 4.23 after we have released 4.22.0 and main branch has been updated as well.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "davsclaus (via GitHub)" <gi...@apache.org>.
davsclaus commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3723053535
##########
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]
** xref:attachments.adoc[Attachments]
** xref:aws-bedrock-examples.adoc[AWS Bedrock - Examples]
Review Comment:
**[High]** This file is auto-generated (see line 1–2: _"this file is auto generated and changes to it will be overwritten"_). The entry should be generated automatically by the build from the module's doc structure. Remove this manual edit.
Additionally, the nesting level is wrong: `***` (three stars) makes this a sub-item of "A2A - Producer Guide" instead of a peer entry at the `**` level.
##########
components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java:
##########
@@ -141,14 +147,49 @@ public void process(Exchange exchange) throws Exception {
AiAgentBody<?> aiAgentBody = exchange.getMessage().getMandatoryBody(AiAgentBody.class);
ToolProvider toolProvider = createComposedToolProvider(tags, exchange);
- Result<String> result = agent.chat(aiAgentBody, toolProvider);
- exchange.getMessage().setBody(result.content());
- populateResultHeaders(result, exchange);
+ Object chatModel = resolveChatModel(agent);
+ GenAiObservationContext observationContext = GenAiObservationContext.builder()
+ .operationName(GenAiOperationName.GENERATE_CONTENT)
+ .system(GenAiModelResolver.resolveSystem(chatModel))
+ .requestModel(GenAiModelResolver.resolveModelName(chatModel))
+ .componentScheme("langchain4j-agent")
+ .build();
+ GenAiObservation observation = GenAiObservability.start(exchange, observationContext);
+ try {
+ Result<String> result = agent.chat(aiAgentBody, toolProvider);
+ exchange.getMessage().setBody(result.content());
+ populateResultHeaders(result, exchange, observationContext.requestModel());
+ observation.recordSuccess(GenAiUsage.of(
+ result.tokenUsage() != null ? result.tokenUsage().inputTokenCount() : null,
+ result.tokenUsage() != null ? result.tokenUsage().outputTokenCount() : null,
+ result.finishReason(),
+ observationContext.requestModel()));
+ } catch (RuntimeException e) {
+ observation.recordError(e);
+ throw e;
+ } finally {
+ observation.close();
+ }
+ }
+
+ private Object resolveChatModel(Agent agent) {
+ if (endpoint.getConfiguration().getAgentConfiguration() != null) {
Review Comment:
**[Medium]** `RESPONSE_MODEL` is set to `requestModel` here, making it identical to `REQUEST_MODEL`. The actual response model may differ (e.g., `gpt-4o` request resolves to `gpt-4o-2024-11-20`). Since `Result<String>` doesn't expose the response model, consider omitting the `RESPONSE_MODEL` header rather than reporting a potentially incorrect value.
Same concern applies to the `GenAiUsage` on line 168 where `observationContext.requestModel()` is passed as the response model for the span attribute.
##########
components/camel-ai/camel-langchain4j-embeddings/src/main/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsProducer.java:
##########
@@ -38,8 +44,35 @@ public LangChain4jEmbeddingsEndpoint getEndpoint() {
public void process(Exchange exchange) throws Exception {
final TextSegment in = exchange.getMessage().getMandatoryBody(TextSegment.class);
final EmbeddingModel model = getEndpoint().getConfiguration().getEmbeddingModel();
- final Response<Embedding> result = model.embed(in);
- final Message message = exchange.getMessage();
+ GenAiObservationContext observationContext = GenAiObservationContext.builder()
+ .operationName(GenAiOperationName.EMBEDDINGS)
+ .system(GenAiModelResolver.resolveSystem(model))
+ .requestModel(GenAiModelResolver.resolveModelName(model))
+ .componentScheme("langchain4j-embeddings")
+ .build();
+ GenAiObservation observation = GenAiObservability.start(exchange, observationContext);
+ try {
+ final Response<Embedding> result = model.embed(in);
+ populateHeaders(exchange.getMessage(), result, in, observationContext.requestModel());
+ observation.recordSuccess(GenAiUsage.of(
+ result.tokenUsage() != null ? result.tokenUsage().inputTokenCount() : null,
+ result.tokenUsage() != null ? result.tokenUsage().outputTokenCount() : null,
+ result.finishReason(),
+ observationContext.requestModel()));
+ } catch (RuntimeException e) {
+ observation.recordError(e);
Review Comment:
**[Medium]** Same issue as the agent producer: `RESPONSE_MODEL` is set to `requestModel`. For embeddings, the `Response<Embedding>` object also doesn't carry a response model name. Consider omitting `RESPONSE_MODEL` when the actual value is unavailable.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5188337340
## Phase 1 implementation summary
_AI-generated comment on behalf of atiaomar1978-hub_
This PR implements Claus Ibsen's Phase 1 scope from [CAMEL-23861](https://issues.apache.org/jira/browse/CAMEL-23861):
| Requirement | Implementation |
|-------------|----------------|
| langchain4j + openai | Integrated in chat, tools, agent, embeddings, openai producers |
| Model headers | Request/response model headers on all langchain4j components |
| Global toggle | `camel.ai.observability.enabled` (default `true`) |
| OTel then Micrometer | CLIENT spans via `camel-telemetry`; metrics via `MeterRegistry` |
### Review fixes already applied
- **Tracer lookup:** `CamelContext.hasService(Tracer.class)` (not registry-only lookup)
- **Agent model resolution:** `AbstractAgent.getChatModel()` so observability tags are not `unknown`
- **Span propagation:** `CamelHeadersSpanContextPropagationExtractor` to avoid NPE in OpenTelemetry create path
- **Span kind:** `CLIENT` for GenAI client calls
- **OpenAI streaming:** observability wrapper on streaming path (duration span; token counts N/A until stream completes)
### Tests
```
mvn test -pl components/camel-ai/camel-ai-observability -am -DskipITs
```
12 unit tests in `camel-ai-observability` (model resolver, toggle, Micrometer metrics, OTel span attributes).
Ready for review when CI is green.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5196674981
Replies to round-2 review summary items without inline threads:
**1. CI build failing** — Fixed across `5f34708496e` (doc xref + symlinks) and `f12051a92c4` (regenerated catalog/endpoint DSL artifacts). Verified locally: Antora quick build passes; focused test suite passes.
**4. Producer-level observability tests** — Added `LangChain4jChatObservabilityTest` in `f12051a92c4`, which exercises the langchain4j-chat producer end-to-end and asserts GenAI span attributes plus request/response model headers. Existing `GenAiObservabilitySpanTest` / `GenAiObservabilityTest` cover the observability module itself.
**5. Observability on deprecated `camel-langchain4j-tools`** — Intentional for Phase 1 parity per CAMEL-23861 scope (langchain4j-chat, tools, agent, embeddings, openai). The component remains in use until users migrate to `ai-tool` + `langchain4j-agent`; observability there avoids a telemetry gap for existing routes. Happy to drop it in a follow-up if committers prefer.
_AI-generated comment on behalf of atiaomar1978-hub_
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "atiaomar1978-hub (via GitHub)" <gi...@apache.org>.
atiaomar1978-hub commented on code in PR #25337:
URL: https://github.com/apache/camel/pull/25337#discussion_r3724512182
##########
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:
_AI-generated on behalf of atiaomar1978-hub._
Addressed in 38821e3: introduced `camel-ai-observability-api` (lightweight types + reflection bridge). AI producers now depend only on the API module; `camel-ai-observability` (with `camel-telemetry`) is opt-in for users who want spans/metrics. Same pattern as PQC/Micrometer.
##########
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:
_AI-generated on behalf of atiaomar1978-hub._
Fixed: removed `:group: AI` from `ai-observability.adoc` and regenerated `docs/components/modules/others/nav.adoc` via `prepare-doc-symlinks`. AI Observability is now a top-level `**` entry under Miscellaneous Components, not nested under A2A.
##########
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:
_AI-generated on behalf of atiaomar1978-hub._
Fixed: removed `setAccessible(true)` in `GenAiObservabilityImpl` — `GenAiMicrometerSupport` is package-private and accessible without reflection tricks.
##########
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:
_AI-generated on behalf of atiaomar1978-hub._
Confirmed removed: `camel.jbang.mcp` is not present in the regenerated catalog on this branch (was cleaned up in e870186 and verified again after rebuild).
##########
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:
_AI-generated on behalf of atiaomar1978-hub._
Restored `camel-test-spring-junit6` and kept `camel-test-junit6` as an additional test dependency (observability tests use `CamelTestSupport`). No replacement of the original dependency.
Also added producer-level observability tests for `langchain4j-tools`, `langchain4j-embeddings`, and `openai`.
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] CAMEL-23861: GenAI observability for langchain4j and OpenAI (Phase 1) [camel]
Posted by "davsclaus (via GitHub)" <gi...@apache.org>.
davsclaus commented on PR #25337:
URL: https://github.com/apache/camel/pull/25337#issuecomment-5253397137
okay so main is now ready for 4.23.0-SNAPSHOT and this PR needs to be rebased on top and update its versions
--
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: commits-unsubscribe@camel.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org