davsclaus commented on code in PR #27602:
URL: https://github.com/apache/camel/pull/27602#discussion_r4227704862


##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/language/semantic/SemanticLanguage.java:
##########
@@ -226,6 +231,66 @@ private synchronized SemanticAdapter adapter() {
         }
     }
 
+    private Object resolveAdapter() throws IOException {
+        CamelContext context = getCamelContext();
+        String configured = adapter == null ? null : 
context.resolvePropertyPlaceholders(adapter);
+        if (configured != null) {
+            if (configured.isBlank() || configured.startsWith("#")) {
+                throw new IllegalArgumentException("Semantic adapter must be a 
bean name or class name without a # prefix");
+            }
+            Object bean = context.getRegistry().lookupByName(configured);
+            if (bean != null) {
+                if (!(bean instanceof SemanticAdapter found)) {
+                    throw new IllegalArgumentException(
+                            "Semantic adapter bean does not implement 
SemanticAdapter: " + configured);
+                }
+                selectedAdapterName = configured;

Review Comment:
   Before the refactor, `selectedAdapterName` was only set together with 
`selectedAdapter`. Now `describeExpert(null)` (inspection) reaches this line 
and sets the name without selecting the adapter. It's harmless today because 
the value is the same, but it is a side effect of a read-only call. Consider 
returning the name to the caller instead.



##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/SemanticEvaluateConsole.java:
##########
@@ -0,0 +1,138 @@
+/*
+ * 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.semantic;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.Expression;
+import org.apache.camel.language.semantic.SemanticLanguage;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.json.JsonRecordSupport;
+
+@DevConsole(name = "semantic-evaluate", displayName = "Semantic Evaluation",
+            description = "Evaluate sample input through a semantic definition 
or expert operation", readOnly = false)
+public class SemanticEvaluateConsole extends AbstractDevConsole {
+    @Metadata(label = "query", description = "Name of the published evaluation 
to execute", javaType = "java.lang.String")
+    public static final String EVALUATION = "evaluation";
+    @Metadata(label = "query", description = "Sample message body", javaType = 
"java.lang.Object")
+    public static final String BODY = "body";
+    @Metadata(label = "query", description = "Sample message headers", 
javaType = "java.util.Map")
+    public static final String HEADERS = "headers";
+    @Metadata(label = "query", description = "Sample exchange variables", 
javaType = "java.util.Map")
+    public static final String VARIABLES = "variables";
+    @Metadata(label = "query", description = "Expert reference for direct 
evaluation", javaType = "java.lang.String")
+    public static final String EXPERT = "expert";
+    @Metadata(label = "query", description = "Expert operation for direct 
evaluation", javaType = "java.lang.String")
+    public static final String OPERATION = "operation";
+    @Metadata(label = "query", description = "Already-selected input for 
direct evaluation", javaType = "java.lang.Object")
+    public static final String INPUT = "input";
+    @Metadata(label = "query", description = "Expert operation parameters", 
javaType = "java.util.Map")
+    public static final String PARAMETERS = "parameters";
+
+    public record Response(
+            @Metadata(description = "Evaluation name") String evaluation,
+            @Metadata(description = "Success or failed") String status,
+            @Metadata(description = "Validated result value") Object value,
+            @Metadata(description = "Provider probability") Double probability,
+            @Metadata(description = "Probabilities by label") Map<String, 
Double> probabilities,
+            @Metadata(description = "Provider confidence") Double confidence,
+            @Metadata(description = "Provider result metadata") Map<String, 
Object> metadata,
+            @Metadata(description = "Elapsed time in milliseconds") long 
elapsedMillis,
+            @Metadata(description = "Evaluation error, if any") String error) {
+    }
+
+    public SemanticEvaluateConsole() {
+        super("camel", "semantic-evaluate", "Semantic Evaluation",
+              "Evaluate sample input through a semantic definition or expert 
operation");
+    }
+
+    @Override
+    protected String doCallText(Map<String, Object> options) {
+        return doCallJson(options).toString();
+    }
+
+    @Override
+    protected Map<String, Object> doCallJson(Map<String, Object> options) {
+        String name = optionString(options, EVALUATION);
+        long start = System.nanoTime();
+        try {
+            if (options.containsKey(OPERATION)) {
+                if (options.containsKey(EVALUATION) || 
options.containsKey(BODY)
+                        || options.containsKey(HEADERS) || 
options.containsKey(VARIABLES)) {
+                    throw new IllegalArgumentException("Choose a named 
evaluation or a direct expert operation");
+                }
+                Object supplied = options.getOrDefault(PARAMETERS, Map.of());
+                if (!(supplied instanceof Map<?, ?> map) || 
map.keySet().stream().anyMatch(k -> !(k instanceof String))) {
+                    throw new IllegalArgumentException("Expert parameters must 
be a JSON object with string keys");
+                }
+                @SuppressWarnings("unchecked")
+                Map<String, Object> parameters = (Map<String, Object>) map;
+                SemanticEvaluation evaluation = new SemanticEvaluation(
+                        optionString(options, OPERATION), 
optionString(options, EXPERT), null, parameters);
+                SemanticLanguage language = (SemanticLanguage) 
getCamelContext().resolveLanguage("semantic");
+                SemanticResult result = language.evaluate(evaluation, 
options.get(INPUT));

Review Comment:
   Related to the connector comment: this is a blocking provider call with no 
timeout or cancellation. When the TUI gives up after 60s, the call keeps 
running in the integration.



##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/language/semantic/SemanticLanguage.java:
##########
@@ -166,33 +188,16 @@ private synchronized SemanticAdapter adapter() {
             return selectedAdapter;
         }
         CamelContext context = getCamelContext();
-        String configured = adapter == null ? null : 
context.resolvePropertyPlaceholders(adapter);
-        if (configured != null) {
-            if (configured.isBlank() || configured.startsWith("#")) {
-                throw new IllegalArgumentException("Semantic adapter must be a 
bean name or class name without a # prefix");
-            }
-            Object bean = context.getRegistry().lookupByName(configured);
-            if (bean != null) {
-                if (!(bean instanceof SemanticAdapter found)) {
-                    throw new IllegalArgumentException(
-                            "Semantic adapter bean does not implement 
SemanticAdapter: " + configured);
-                }
-                selectedAdapter = found;
-                selectedAdapterName = configured;
-                return found;
-            }
-        }
         ManagedAdapter owned = null;
         try {
-            Object candidate = configured == null
-                    ? discoverAdapter(context) : 
context.getClassResolver().resolveClass(configured);
+            Object candidate = resolveAdapter();
             if (candidate instanceof SemanticAdapter registered) {
                 selectedAdapter = registered;
                 return registered;
             }
             Class<?> resolved = (Class<?>) candidate;
             if (resolved == null) {
-                throw new IllegalArgumentException("No semantic adapter bean 
or class found: " + configured);
+                throw new IllegalArgumentException("No semantic adapter bean 
or class found: " + adapter);

Review Comment:
   Small regression from the refactor: this used `configured` (the 
placeholder-resolved value) before, and now uses the raw `adapter` field, so 
`{{my.adapter}}` appears unresolved in the error. The same applies at line 286. 
Maybe have `resolveAdapter()` return the resolved name along with the 
candidate, or resolve it again here.



##########
dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java:
##########
@@ -840,6 +842,22 @@ private void doActionHeapHistogramTask() throws 
IOException {
         }
     }
 
+    private void doActionSemanticTask(JsonObject root) throws IOException {
+        String action = root.getString("action");
+        DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById(action);
+        Map<String, Object> options = new HashMap<>();
+        List<String> keys = "semantic-metadata".equals(action)
+                ? List.of("expert", "overview")
+                : List.of("evaluation", "body", "headers", "variables", 
"expert", "operation", "input", "parameters");
+        for (String key : keys) {
+            if (root.containsKey(key)) {
+                options.put(key, root.get(key));
+            }
+        }
+        writeOutput(dc == null ? new JsonObject() : (JsonObject) 
dc.call(DevConsole.MediaType.JSON, options));

Review Comment:
   `FileCliConnectorTransport` dispatches all actions on its single scheduled 
thread (`CliActionDispatcher` must be called from one thread). For 
`semantic-evaluate`, this `dc.call(...)` runs the provider inline, and the TUI 
waits up to 60s for it. During that time, status/trace updates and every other 
action are blocked, and the other screens' 5s actions time out. Consider 
handing the evaluation to a separate executor and writing the output file when 
it completes, or at least bounding the call.



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