luigidemasi commented on code in PR #27494:
URL: https://github.com/apache/camel/pull/27494#discussion_r4217003844


##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/SemanticQuestion.java:
##########
@@ -16,118 +16,178 @@
  */
 package org.apache.camel.semantic;
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
-import java.util.Objects;
+import java.util.Set;
 
-/**
- * Immutable question definition. Probabilities and thresholds are 
provider-specific evidence, not accuracy guarantees.
- */
+/** Immutable evaluation declaration. The expert defines the operation, 
parameter vocabulary and result semantics. */
 public final class SemanticQuestion {
+    private static final Set<Class<?>> NUMBER_TYPES = Set.of(Byte.class, 
Short.class, Integer.class, Long.class,
+            Float.class, Double.class, BigInteger.class, BigDecimal.class);
+
     public enum Type {
         BOOLEAN,
         CHOICE,
-        SCORE
+        SCORE,
+        CLASSIFICATION
     }
 
     public enum UncertaintyPolicy {
         FAIL,
         NON_MATCH
     }
 
-    private final Type type;
+    private final String operation;
     private final String expert;
-    private final String instructions;
     private final String state;
-    private final Map<String, String> criteria;
-    private final List<String> levels;
-    private final double threshold;
-    private final double uncertainty;
-    private final UncertaintyPolicy uncertaintyPolicy;
+    private final Map<String, Object> parameters;
+
+    public SemanticQuestion(String operation, String expert, String state, 
Map<String, ?> parameters) {
+        if (operation == null || operation.isBlank()) {
+            throw new IllegalArgumentException("Evaluation operation is 
required");
+        }
+        if (expert != null && expert.isBlank()) {
+            throw new IllegalArgumentException("Evaluation expert must not be 
blank");
+        }
+        if (state != null && state.isBlank()) {
+            throw new IllegalArgumentException("Evaluation state selector must 
not be blank");
+        }
+        this.operation = operation;
+        this.expert = expert;
+        this.state = state;
+        this.parameters = immutableMap(parameters == null ? Map.of() : 
parameters);
+    }
 
+    /** Convenience declaration for instruction-driven operations named 
boolean, choice or score. */
     public SemanticQuestion(Type type, String instructions, String state, 
Map<String, String> criteria,
-                            List<String> levels, double threshold, double 
uncertainty, UncertaintyPolicy uncertaintyPolicy) {
-        this(type, instructions, state, criteria, levels, threshold, 
uncertainty, uncertaintyPolicy, null);
+                            List<String> levels, double threshold, double 
uncertainty, UncertaintyPolicy policy) {
+        this(type, instructions, state, criteria, levels, threshold, 
uncertainty, policy, null);
     }
 
     public SemanticQuestion(Type type, String instructions, String state, 
Map<String, String> criteria,
-                            List<String> levels, double threshold, double 
uncertainty, UncertaintyPolicy uncertaintyPolicy,
+                            List<String> levels, double threshold, double 
uncertainty, UncertaintyPolicy policy,
                             String expert) {
-        this.type = Objects.requireNonNull(type, "Question type is required");
-        if (expert != null && expert.isBlank()) {
-            throw new IllegalArgumentException("Question expert must not be 
blank");
+        this(type.name().toLowerCase(Locale.ROOT), expert, state,
+             instructionParameters(type, instructions, criteria, levels, 
threshold, uncertainty, policy));
+    }
+
+    private static Map<String, Object> instructionParameters(
+            Type type, String instructions, Map<String, String> criteria,
+            List<String> levels, double threshold, double uncertainty,
+            UncertaintyPolicy policy) {
+        Map<String, Object> values = new LinkedHashMap<>();
+        if (instructions != null) {
+            values.put("instructions", instructions);
         }
-        this.expert = expert;
-        if (instructions != null && instructions.isBlank()) {
-            throw new IllegalArgumentException("Question instructions must not 
be blank");
+        if (criteria != null && !criteria.isEmpty()) {
+            values.put("criteria", criteria);
         }
-        if (state != null && state.isBlank()) {
-            throw new IllegalArgumentException("Question state selector must 
not be blank");
+        if (levels != null && !levels.isEmpty()) {
+            values.put("criteria", levels);

Review Comment:
   Addressed in 
[2ad0b3a75251](https://github.com/apache/camel/commit/2ad0b3a75251700121823e954c49f9f975ef170d)
 by removing the TypeSafe-specific convenience constructor. 
`SemanticEvaluation` now has one generic constructor taking the parameter map. 
The builder keeps criteria and levels separately and rejects mixing them, or 
combining either with an explicit `criteria` parameter. Java and XML regression 
cases cover these conflicts.
   
   _AI-generated by Codex on behalf of @luigidemasi._



##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/SemanticQuestion.java:
##########
@@ -16,118 +16,178 @@
  */
 package org.apache.camel.semantic;
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
-import java.util.Objects;
+import java.util.Set;
 
-/**
- * Immutable question definition. Probabilities and thresholds are 
provider-specific evidence, not accuracy guarantees.
- */
+/** Immutable evaluation declaration. The expert defines the operation, 
parameter vocabulary and result semantics. */
 public final class SemanticQuestion {
+    private static final Set<Class<?>> NUMBER_TYPES = Set.of(Byte.class, 
Short.class, Integer.class, Long.class,
+            Float.class, Double.class, BigInteger.class, BigDecimal.class);
+
     public enum Type {
         BOOLEAN,
         CHOICE,
-        SCORE
+        SCORE,
+        CLASSIFICATION
     }
 
     public enum UncertaintyPolicy {
         FAIL,
         NON_MATCH
     }
 
-    private final Type type;
+    private final String operation;
     private final String expert;
-    private final String instructions;
     private final String state;
-    private final Map<String, String> criteria;
-    private final List<String> levels;
-    private final double threshold;
-    private final double uncertainty;
-    private final UncertaintyPolicy uncertaintyPolicy;
+    private final Map<String, Object> parameters;
+
+    public SemanticQuestion(String operation, String expert, String state, 
Map<String, ?> parameters) {
+        if (operation == null || operation.isBlank()) {
+            throw new IllegalArgumentException("Evaluation operation is 
required");
+        }
+        if (expert != null && expert.isBlank()) {
+            throw new IllegalArgumentException("Evaluation expert must not be 
blank");
+        }
+        if (state != null && state.isBlank()) {
+            throw new IllegalArgumentException("Evaluation state selector must 
not be blank");
+        }
+        this.operation = operation;
+        this.expert = expert;
+        this.state = state;
+        this.parameters = immutableMap(parameters == null ? Map.of() : 
parameters);
+    }
 
+    /** Convenience declaration for instruction-driven operations named 
boolean, choice or score. */
     public SemanticQuestion(Type type, String instructions, String state, 
Map<String, String> criteria,
-                            List<String> levels, double threshold, double 
uncertainty, UncertaintyPolicy uncertaintyPolicy) {
-        this(type, instructions, state, criteria, levels, threshold, 
uncertainty, uncertaintyPolicy, null);
+                            List<String> levels, double threshold, double 
uncertainty, UncertaintyPolicy policy) {
+        this(type, instructions, state, criteria, levels, threshold, 
uncertainty, policy, null);
     }
 
     public SemanticQuestion(Type type, String instructions, String state, 
Map<String, String> criteria,
-                            List<String> levels, double threshold, double 
uncertainty, UncertaintyPolicy uncertaintyPolicy,
+                            List<String> levels, double threshold, double 
uncertainty, UncertaintyPolicy policy,
                             String expert) {
-        this.type = Objects.requireNonNull(type, "Question type is required");
-        if (expert != null && expert.isBlank()) {
-            throw new IllegalArgumentException("Question expert must not be 
blank");
+        this(type.name().toLowerCase(Locale.ROOT), expert, state,
+             instructionParameters(type, instructions, criteria, levels, 
threshold, uncertainty, policy));
+    }
+
+    private static Map<String, Object> instructionParameters(
+            Type type, String instructions, Map<String, String> criteria,
+            List<String> levels, double threshold, double uncertainty,
+            UncertaintyPolicy policy) {
+        Map<String, Object> values = new LinkedHashMap<>();
+        if (instructions != null) {
+            values.put("instructions", instructions);
         }
-        this.expert = expert;
-        if (instructions != null && instructions.isBlank()) {
-            throw new IllegalArgumentException("Question instructions must not 
be blank");
+        if (criteria != null && !criteria.isEmpty()) {
+            values.put("criteria", criteria);
         }
-        if (state != null && state.isBlank()) {
-            throw new IllegalArgumentException("Question state selector must 
not be blank");
+        if (levels != null && !levels.isEmpty()) {
+            values.put("criteria", levels);
         }
-        this.instructions = instructions;
-        this.state = state;
-        this.criteria = criteria == null ? Map.of() : 
Collections.unmodifiableMap(new LinkedHashMap<>(criteria));
-        this.levels = levels == null ? List.of() : List.copyOf(levels);
-        this.criteria.forEach((key, value) -> {
-            if (key == null || key.isBlank() || value == null || 
value.isBlank()) {
-                throw new IllegalArgumentException("Question criteria require 
nonblank keys and descriptions");
+        if (type == Type.BOOLEAN) {
+            values.put("threshold", threshold);
+            values.put("uncertainty", uncertainty);
+            values.put("uncertaintyPolicy", policy == 
UncertaintyPolicy.NON_MATCH ? "non-match" : "fail");
+        }
+        return values;
+    }
+
+    static Map<String, Object> immutableMap(Map<String, ?> values) {
+        Map<String, Object> copy = new LinkedHashMap<>();
+        values.forEach((name, value) -> {
+            if (name == null || name.isBlank()) {
+                throw new IllegalArgumentException("Parameter names must not 
be blank");
             }
+            copy.put(name, immutableValue(value));
         });
-        if (this.levels.stream().anyMatch(String::isBlank)) {
-            throw new IllegalArgumentException("Question score levels must not 
be blank");
-        }
-        if (type == Type.CHOICE && this.criteria.isEmpty() || type == 
Type.SCORE && this.levels.isEmpty()
-                || type != Type.SCORE && !this.levels.isEmpty() || type == 
Type.SCORE && !this.criteria.isEmpty()) {
-            throw new IllegalArgumentException(
-                    "Choice needs criteria, score needs ordered levels, 
boolean accepts true/false criteria");
+        return Collections.unmodifiableMap(copy);
+    }
+
+    private static Object immutableValue(Object value) {
+        if (value instanceof Map<?, ?> map) {
+            Map<String, Object> copy = new LinkedHashMap<>();
+            map.forEach((key, entry) -> {
+                if (!(key instanceof String name)) {
+                    throw new IllegalArgumentException("Parameter maps require 
string keys");
+                }
+                copy.put(name, entry);
+            });
+            return immutableMap(copy);
         }
-        if (type == Type.BOOLEAN && this.criteria.keySet().stream().anyMatch(k 
-> !k.equals("true") && !k.equals("false"))) {
-            throw new IllegalArgumentException("Boolean criteria keys must be 
true or false");
+        if (value instanceof List<?> list) {
+            List<Object> copy = new ArrayList<>();
+            list.forEach(entry -> copy.add(immutableValue(entry)));
+            return Collections.unmodifiableList(copy);
         }
-        if (!Double.isFinite(threshold) || !Double.isFinite(uncertainty) || 
uncertainty < 0
-                || threshold - uncertainty < 0 || threshold + uncertainty > 1) 
{
-            throw new IllegalArgumentException("Boolean threshold and 
uncertainty band must be within [0,1]");
+        if (value == null || value instanceof String || value instanceof 
Boolean
+                || NUMBER_TYPES.contains(value.getClass())) {
+            return value;
         }
-        this.threshold = threshold;
-        this.uncertainty = uncertainty;
-        this.uncertaintyPolicy = Objects.requireNonNull(uncertaintyPolicy, 
"Uncertainty policy is required");
+        throw new IllegalArgumentException("Parameters require immutable 
scalar, map or list values");
+    }
+
+    public String getOperation() {
+        return operation;
     }
 
     public String getExpert() {
         return expert;
     }
 
-    public Type getType() {
-        return type;
+    public String getState() {
+        return state;
     }
 
-    public String getInstructions() {
-        return instructions;
+    public Map<String, Object> getParameters() {
+        return parameters;
     }
 
-    public String getState() {
-        return state;
+    /**
+     * Instruction-driven operation kind; custom operations have their result 
type in the expert contract.
+     *
+     * @throws IllegalArgumentException if the operation is not a built-in 
instruction-driven kind
+     */
+    public Type getType() {
+        return Type.valueOf(operation.toUpperCase(Locale.ROOT));
     }
 
+    public String getInstructions() {
+        return (String) parameters.get("instructions");
+    }
+
+    @SuppressWarnings("unchecked")
     public Map<String, String> getCriteria() {
-        return criteria;
+        return parameters.get("criteria") instanceof Map<?, ?> map ? 
(Map<String, String>) map : Map.of();
     }
 
+    @SuppressWarnings("unchecked")
     public List<String> getLevels() {
-        return levels;
+        return parameters.get("criteria") instanceof List<?> list ? 
(List<String>) list : List.of();
     }
 
-    public double getThreshold() {
-        return threshold;
+    /** Requested threshold, or null when omitted. Defaults belong to the 
expert. */
+    public Double getThreshold() {
+        return parameters.get("threshold") instanceof Number number ? 
number.doubleValue() : null;
     }
 
-    public double getUncertainty() {
-        return uncertainty;
+    /** Requested uncertainty band, or null when omitted. Defaults belong to 
the expert. */
+    public Double getUncertainty() {
+        return parameters.get("uncertainty") instanceof Number number ? 
number.doubleValue() : null;
     }
 
+    /** Requested instruction-driven policy, or null when omitted. Custom 
policies remain available in parameters. */
     public UncertaintyPolicy getUncertaintyPolicy() {

Review Comment:
   Addressed in 
[2ad0b3a75251](https://github.com/apache/camel/commit/2ad0b3a75251700121823e954c49f9f975ef170d).
 The generic policy getter was removed along with the TypeSafe-specific 
declaration API. Custom experts receive their policy values unchanged through 
`getParameters()` and validate their own contract; TypeSafe policy 
interpretation stays in its adapter.
   
   _AI-generated by Codex on behalf of @luigidemasi._



##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/SemanticQuestionsBuilder.java:
##########
@@ -55,6 +57,28 @@ public static SemanticQuestionsBuilder 
semanticQuestions(RouteBuilder builder) {
         return new SemanticQuestionsBuilder(builder);
     }
 
+    public SemanticQuestionsBuilder expert(String expert) {
+        this.expert = expert;
+        return this;
+    }
+
+    public SemanticQuestionsBuilder state(String state) {
+        this.state = state;
+        return this;
+    }
+
+    String getExpert() {
+        return expert;
+    }
+
+    String getState() {
+        return state;
+    }
+
+    public SemanticQuestionBuilder evaluation(String name) {

Review Comment:
   Addressed in 
[2ad0b3a75251](https://github.com/apache/camel/commit/2ad0b3a75251700121823e954c49f9f975ef170d).
 The public declaration classes, Java builder, YAML and XML consistently use 
`SemanticEvaluation` / `evaluation`; the `question` declaration aliases were 
removed. Schemas, tooling examples, documentation and generated catalog copies 
were updated. The TypeSafe wire protocol retains its existing `questions` field.
   
   _AI-generated by Codex on behalf of @luigidemasi._



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