luigidemasi commented on code in PR #27602:
URL: https://github.com/apache/camel/pull/27602#discussion_r4228262996
##########
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:
Fixed in 7d38f08051b3. `semantic-evaluate` now runs on a bounded executor
through an asynchronous dispatch path. Ordinary actions still dispatch
serially; the evaluation worker captures its own output instead of sharing
`actionOutput`. Both transports wait for that request's completion. The file
transport continues action/status/trace polling, cancels an evaluation when its
request file disappears, and drops abandoned output. WebSocket completion keeps
the original request ID and cancels outstanding evaluations on
disconnect/shutdown. The connector also has a 55-second deadline. Regression
tests cover responsive actions/status during a blocked evaluation, concurrent
response isolation, capacity rejection, and cancellation in both transports.
_OpenAI Codex on behalf of @luigidemasi, via /oss-address-review._
##########
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:
Fixed in 7d38f08051b3. The evaluation console now bounds calls to 50 seconds
by default (the `timeout` option can shorten this), limits concurrency to two
calls with no pending queue, and returns a busy response when occupied. Timeout
or caller interruption cancels the worker with interruption; file-request
abandonment and WebSocket disconnect propagate cancellation from the connector.
This also bounds calls through the HTTP console, without changing normal route
evaluation. Providers must cooperate with interruption: remote/native inference
may continue, so the documentation also calls out provider-specific timeouts.
Tests verify timeout interruption, caller cancellation, and concurrent-call
rejection.
_OpenAI Codex on behalf of @luigidemasi, via /oss-address-review._
##########
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:
Fixed in 7d38f08051b3. Adapter resolution now returns the resolved reference
together with the bean/class candidate. Both normal evaluation and metadata
inspection use that reference in missing-adapter errors. Added regression
coverage using a placeholder whose resolved bean/class is missing.
_OpenAI Codex on behalf of @luigidemasi, via /oss-address-review._
##########
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:
Fixed in 7d38f08051b3. Resolution and discovery return the candidate and its
name without changing selection state. `selectedAdapterName` is set only when
an adapter is actually selected, alongside `selectedAdapter`. Regression tests
cover both a configured placeholder-backed bean and automatic discovery,
asserting that inspection leaves both selection fields unset.
_OpenAI Codex on behalf of @luigidemasi, via /oss-address-review._
--
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]