davsclaus commented on code in PR #27494:
URL: https://github.com/apache/camel/pull/27494#discussion_r4206868191
##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/language/semantic/SemanticLanguage.java:
##########
@@ -495,7 +545,14 @@ private Object evaluate(Exchange exchange, boolean
asPredicate) {
current = compile(Map.of(name, single));
}
}
+ if (asPredicate) {
+ requireBoolean(current.operations.get(names.get(0)));
+ }
+ startAdapter(current.groups);
Review Comment:
`startAdapter` is `synchronized` on the language, and `activate()` is
synchronized too. Calling it on every evaluation serializes all concurrent
exchanges on one monitor and does a stream scan each time, even once the
adapter is active. Could activation happen in `compile()` only, or behind a
volatile `activated` fast path checked before taking the lock?
##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/language/semantic/SemanticLanguage.java:
##########
@@ -65,6 +68,7 @@ public class SemanticLanguage extends LanguageSupport {
public static final String ADAPTER_FACTORY = "semantic-adapter";
public static final String ADAPTER_RESOURCE = FactoryFinder.DEFAULT_PATH +
ADAPTER_FACTORY;
+ private final ThreadLocal<Boolean> validating = new ThreadLocal<>();
Review Comment:
Question: a `ThreadLocal` flag changes the behaviour of `compile`, `init`
and `startAdapter`, and the same pattern appears with
`SemanticQuestions.candidate`. Anything an adapter's `validate()` does on this
thread also runs in validation mode. Would an explicit package-private
`prepare(selected, validationOnly)` / `validate(snapshot)` path be simpler and
safer?
##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/SemanticQuestions.java:
##########
@@ -25,16 +25,30 @@
import java.util.function.Consumer;
import org.apache.camel.CamelContext;
+import org.apache.camel.language.semantic.SemanticLanguage;
import org.apache.camel.spi.Resource;
/** Context-local named questions, replaced atomically per source when a route
resource is reloaded. */
public final class SemanticQuestions {
private static final Object CREATION_LOCK = new Object();
+ private final CamelContext context;
private final Map<String, Map<String, SemanticQuestion>> sources = new
HashMap<>();
private final Map<String, Resource> resources = new HashMap<>();
private volatile Map<String, SemanticQuestion> questions = Map.of();
+ private final ThreadLocal<Map<String, SemanticQuestion>> candidate = new
ThreadLocal<>();
Review Comment:
With this, `get(name)` / `get(names)` silently return a candidate snapshot
depending on thread state, which is surprising for a public API. See the
comment on `SemanticLanguage.validating`.
##########
core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/UnaryExpression.java:
##########
@@ -102,10 +103,7 @@ public void init(CamelContext context) {
@Override
public <T> T evaluate(Exchange exchange, Class<T> type) {
- Object value = exp.evaluate(exchange, Object.class);
- // the same rule the language uses for a predicate on its own,
where ${body} is true and a missing
- // header is false, so !${body} and !${header.foo} answer the
opposite of those (CAMEL-24984)
- boolean matches = ObjectHelper.evaluateValuePredicate(value);
+ boolean matches =
ExpressionToPredicateAdapter.toPredicate(exp).matches(exchange);
Review Comment:
This creates a new `ExpressionToPredicateAdapter` on every evaluation of
`!${...}`. Can the predicate be built once in `createNotExpression` (`final
Predicate predicate = ExpressionToPredicateAdapter.toPredicate(exp);`)? Please
also keep the CAMEL-24984 comment explaining the `${body}` / missing-header
semantics. The same applies to
`SimpleFunctionExpression.createNegatedExpression`.
##########
core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java:
##########
@@ -148,9 +149,8 @@ public void init(CamelContext context) {
@Override
public <T> T evaluate(Exchange exchange, Class<T> type) {
- Object value = exp.evaluate(exchange, Object.class);
- // the same rule the language uses for a predicate on its own
(CAMEL-24984)
- boolean matches = ObjectHelper.evaluateValuePredicate(value);
+ // Preserve a function's predicate contract, including
semantic's boolean-only guard.
+ boolean matches =
ExpressionToPredicateAdapter.toPredicate(exp).matches(exchange);
Review Comment:
Same as in `UnaryExpression`: please hoist the predicate out of `evaluate()`.
##########
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:
If both `criteria` and `levels` are passed, `levels` silently overwrites
`criteria`. `SemanticQuestionBuilder.build` rejects that combination, so the
convenience constructor should probably do the same.
##########
components/camel-ai/camel-typesafe-ai/src/main/java/org/apache/camel/component/typesafeai/TypeSafeAiSemanticAdapter.java:
##########
@@ -120,10 +187,47 @@ private SemanticResult result(SemanticQuestion question,
JsonObject answer, Json
if (answer.get("probabilities") instanceof Map<?, ?> values) {
values.forEach((key, value) -> probabilities.put((String) key,
((Number) value).doubleValue()));
}
- return new SemanticResult(
- question.getType() == SemanticQuestion.Type.BOOLEAN ? null :
answer.get(type(question)),
- question.getType() == SemanticQuestion.Type.BOOLEAN ?
answer.getDouble("noul") : null,
+ Double probability = question.getType() ==
SemanticQuestion.Type.BOOLEAN ? answer.getDouble("noul") : null;
+ Object value = switch (question.getType()) {
+ case BOOLEAN -> booleanDecision(question, probability);
Review Comment:
If the response has no `noul`, `probability` is null here and unboxing it
into `booleanDecision(..., double)` throws an NPE with no message. Could we
throw an `IllegalArgumentException` such as "TypeSafe AI boolean result is
missing its probability" so a malformed response produces a clear error?
##########
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:
Naming: there is now `evaluation(name)` and `question(name)` here,
`evaluation:` and `question:` in YAML, and `<evaluation>` and `<question>` in
XML, while the classes are still `SemanticQuestion*`. Before 4.23.0 ships,
could we pick one term (the JIRA and docs use "evaluation") rather than
shipping both?
##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/SemanticQuestionBuilder.java:
##########
@@ -78,84 +96,89 @@ public SemanticQuestionBuilder criterion(String name,
String description) {
return this;
}
- /** Add an ordered score level. */
public SemanticQuestionBuilder level(String level) {
levels.add(level);
return this;
}
- /** Boolean decision threshold; defaults to 0.5. */
public SemanticQuestionBuilder threshold(double threshold) {
- return threshold(Double.toString(threshold));
+ return parameter("threshold", threshold);
}
- /** Boolean decision threshold, optionally using property placeholders. */
public SemanticQuestionBuilder threshold(String threshold) {
this.threshold = threshold;
return this;
}
- /** Boolean uncertainty band; defaults to zero. */
public SemanticQuestionBuilder uncertainty(double uncertainty) {
- return uncertainty(Double.toString(uncertainty));
+ return parameter("uncertainty", uncertainty);
}
- /** Boolean uncertainty band, optionally using property placeholders. */
public SemanticQuestionBuilder uncertainty(String uncertainty) {
this.uncertainty = uncertainty;
return this;
}
- /** Boolean uncertainty policy: fail (default) or non-match. */
- public SemanticQuestionBuilder uncertaintyPolicy(String uncertaintyPolicy)
{
- this.uncertaintyPolicy = uncertaintyPolicy;
+ public SemanticQuestionBuilder uncertaintyPolicy(String policy) {
+ parameter("uncertaintyPolicy", policy);
+ normalizeUncertaintyPolicy = true;
return this;
}
- /** Return to the group to add another question. */
public SemanticQuestionsBuilder end() {
+ if (parent == null) {
+ throw new IllegalStateException("Complete a standalone declaration
with build(context)");
+ }
return parent;
}
- /** Validate and register the entire group. */
public void register() {
- parent.register();
+ end().register();
}
- SemanticQuestion build(CamelContext context) {
- if (type == null) {
- throw new IllegalArgumentException("Question type is required");
+ /** Build an immutable declaration, resolving placeholders in parameter
values while retaining their types. */
+ public SemanticQuestion build(CamelContext context) {
+ Map<String, Object> values = new LinkedHashMap<>(parameters);
+ if (!criteria.isEmpty() || !levels.isEmpty()) {
+ if (values.containsKey("criteria") || !criteria.isEmpty() &&
!levels.isEmpty()) {
+ throw new IllegalArgumentException("Duplicate parameter
'criteria'");
+ }
+ values.put("criteria", !criteria.isEmpty() ? criteria : levels);
+ }
+ for (String numeric : List.of("threshold", "uncertainty")) {
+ String text = numeric.equals("threshold") ? threshold :
uncertainty;
+ if (text != null) {
+ if (values.containsKey(numeric)) {
+ throw new IllegalArgumentException("Duplicate parameter '"
+ numeric + "'");
+ }
+ try {
+ values.put(numeric,
Double.valueOf(context.resolvePropertyPlaceholders(text)));
+ } catch (NumberFormatException invalid) {
+ throw new IllegalArgumentException("Parameter '" + numeric
+ "' must be a valid number");
+ }
+ }
}
- SemanticQuestion.Type questionType = enumeration(type,
SemanticQuestion.Type.class);
- if (questionType != SemanticQuestion.Type.BOOLEAN
- && (threshold != null || uncertainty != null ||
uncertaintyPolicy != null)) {
- throw new IllegalArgumentException("Threshold and uncertainty
policy require a boolean question");
+ values.replaceAll((name, value) -> resolve(context, value));
Review Comment:
This resolves property placeholders in every string parameter, including
free-text `instructions` (a prompt containing `{{` will fail). A numeric value
such as `threshold: "{{x}}"` nested in `parameters` stays a String and then
fails Number validation, so experts other than TypeSafe cannot externalise
numeric policy through properties. Is that intended? If so, maybe give an
example in the docs of how to externalise a nested numeric parameter.
##########
dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java:
##########
@@ -126,6 +126,9 @@ public boolean isSupportedExtension(String extension) {
}
protected RouteBuilder builder(final YamlDeserializationContext ctx, final
Node root) {
+ // All resources have prepared their beans before declarations are
validated and routes constructed.
+ setDeserializationContext(root, ctx);
+ ctx.preParse(root);
Review Comment:
This moves the resolver `preParse` hook from the pre-parse phase into
`builder()`. Fine today, since only camel-semantic implements the hook, but
exceptions thrown here are no longer wrapped with `Error pre-parsing resource:
<location>`. It is also a generic YAML-loader change hidden in a camel-semantic
PR. Could you mention it in the PR description?
##########
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:
The javadoc says custom policies remain available in parameters, but this
getter throws `IllegalArgumentException` for any value other than
fail/non-match. Maybe return null for unknown values, or keep this getter in
the TypeSafe adapter only.
##########
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 {
Review Comment:
`SemanticQuestion.Type` has the same four values as
`SemanticExpert.ResultType`, but means "instruction-driven operation name" and
throws for custom operations. Likewise `UncertaintyPolicy` and
`getThreshold`/`getUncertainty`/`getUncertaintyPolicy` are TypeSafe concepts on
the generic declaration. Since this is the first release, could these move to
the TypeSafe adapter (or a small helper) so the generic `SemanticQuestion` is
just operation/expert/state/parameters?
--
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]