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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to