luigidemasi commented on code in PR #26813:
URL: https://github.com/apache/camel/pull/26813#discussion_r4093267417
##########
dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlCompletionMojo.java:
##########
@@ -418,7 +426,8 @@ private void enrichNodeMetadata(ObjectNode node, String
nodeName, String fqName,
}
// check for language model
- LanguageModel langModel = catalog.languageModel(nodeName);
+ LanguageModel langModel =
fqName.startsWith("org.apache.camel.model.language.")
+ ? catalog.languageModel(nodeName) : null;
Review Comment:
Addressed in `9ac532851f76`. Kept the metadata distinction and added
YamlCompletionTreeTest.beanEipMetadataIsNotOverwrittenByBeanLanguage. It pins
the Bean EIP title, description and labels and verifies that the expression's
Bean Method metadata is retained. The PR description explains both this
existing collision fix and why semantic declarations must not receive language
metadata.
_AI-generated by Codex on behalf of
[luigidemasi](https://github.com/luigidemasi)._
##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/yaml/SemanticDefinitionDeserializer.java:
##########
@@ -0,0 +1,201 @@
+/*
+ * 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.yaml;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
+import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver;
+import org.apache.camel.dsl.yaml.common.YamlDeserializerSupport;
+import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException;
+import org.apache.camel.semantic.SemanticQuestion;
+import org.apache.camel.semantic.SemanticQuestions;
+import org.apache.camel.spi.CamelContextCustomizer;
+import org.apache.camel.spi.annotations.YamlIn;
+import org.apache.camel.spi.annotations.YamlProperty;
+import org.apache.camel.spi.annotations.YamlType;
+import org.snakeyaml.engine.v2.api.ConstructNode;
+import org.snakeyaml.engine.v2.nodes.Node;
+import org.snakeyaml.engine.v2.nodes.NodeTuple;
+import org.snakeyaml.engine.v2.nodes.SequenceNode;
+
+/** Named semantic declarations are installed in a resource-wide pass before
route references are resolved. */
+@YamlIn
+@YamlType(nodes = "semantic", properties = {
+ @YamlProperty(name = "question",
+ type =
"map:org.apache.camel.semantic.yaml.SemanticDefinitionDeserializer$QuestionSchema",
+ required = true)
+})
+public class SemanticDefinitionDeserializer extends YamlDeserializerSupport
implements ConstructNode, YamlDeserializerResolver {
+ private static final Set<String> FIELDS
+ = Set.of("type", "instructions", "state", "criteria", "threshold",
"uncertainty", "uncertaintyPolicy");
+
+ @Override
+ public ConstructNode resolve(String id) {
+ return "semantic".equals(id) ? this : null;
+ }
+
+ @Override
+ public Object construct(Node node) {
+ read(node);
+ // Registration happens once for the entire resource, including
declarations after routes.
+ return (CamelContextCustomizer) context -> {
+ };
+ }
+
+ @Override
+ public void preParse(YamlDeserializationContext dc, Node root) {
+ if (!(root instanceof SequenceNode sequence)) {
+ return;
+ }
+ Map<String, SemanticQuestion> definitions = new LinkedHashMap<>();
+ for (Node node : sequence.getValue()) {
+ for (NodeTuple tuple : asMappingNode(node).getValue()) {
+ if ("semantic".equals(asText(tuple.getKeyNode()))) {
+ read(tuple.getValueNode()).forEach((name, question) -> {
+ if (definitions.putIfAbsent(name, question) != null) {
+ throw new IllegalArgumentException("Duplicate
semantic question: " + name);
+ }
+ });
+ }
+ }
+ }
+ CamelContext context = dc.getCamelContext();
+ SemanticQuestions questions = definitions.isEmpty()
+ ?
context.getCamelContextExtension().getContextPlugin(SemanticQuestions.class)
+ : SemanticQuestions.get(context);
+ if (questions != null) {
+ questions.replace(dc.getResource(), definitions);
+ }
+ }
+
+ private static Map<String, SemanticQuestion> read(Node node) {
+ Map<String, Node> semantic = fields(node);
+ if (!semantic.keySet().equals(Set.of("question"))) {
+ throw new IllegalArgumentException("Semantic declaration requires
only question");
+ }
+ Map<String, SemanticQuestion> result = new LinkedHashMap<>();
+ fields(semantic.get("question")).forEach((name, definition) -> {
+ Map<String, Node> values = fields(definition);
+ if (!FIELDS.containsAll(values.keySet())) {
+ throw new IllegalArgumentException("Unknown property in
semantic question: " + name);
+ }
+ String typeName = asText(values.get("type"));
+ if (typeName == null) {
+ throw new IllegalArgumentException("Semantic question type is
required: " + name);
+ }
+ SemanticQuestion.Type type =
SemanticQuestion.Type.valueOf(typeName.toUpperCase(Locale.ROOT));
Review Comment:
Addressed in `9ac532851f76`. The parser now delegates to
YamlDeserializerSupport.asEnum. InvalidEnumException is wrapped at the same
YAML value node to retain the question and field names along with the location
and original cause. Regression tests cover invalid and empty values.
_AI-generated by Codex on behalf of
[luigidemasi](https://github.com/luigidemasi)._
##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/yaml/SemanticDefinitionDeserializer.java:
##########
@@ -0,0 +1,201 @@
+/*
+ * 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.yaml;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
+import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver;
+import org.apache.camel.dsl.yaml.common.YamlDeserializerSupport;
+import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException;
+import org.apache.camel.semantic.SemanticQuestion;
+import org.apache.camel.semantic.SemanticQuestions;
+import org.apache.camel.spi.CamelContextCustomizer;
+import org.apache.camel.spi.annotations.YamlIn;
+import org.apache.camel.spi.annotations.YamlProperty;
+import org.apache.camel.spi.annotations.YamlType;
+import org.snakeyaml.engine.v2.api.ConstructNode;
+import org.snakeyaml.engine.v2.nodes.Node;
+import org.snakeyaml.engine.v2.nodes.NodeTuple;
+import org.snakeyaml.engine.v2.nodes.SequenceNode;
+
+/** Named semantic declarations are installed in a resource-wide pass before
route references are resolved. */
+@YamlIn
+@YamlType(nodes = "semantic", properties = {
+ @YamlProperty(name = "question",
+ type =
"map:org.apache.camel.semantic.yaml.SemanticDefinitionDeserializer$QuestionSchema",
+ required = true)
+})
+public class SemanticDefinitionDeserializer extends YamlDeserializerSupport
implements ConstructNode, YamlDeserializerResolver {
+ private static final Set<String> FIELDS
+ = Set.of("type", "instructions", "state", "criteria", "threshold",
"uncertainty", "uncertaintyPolicy");
+
+ @Override
+ public ConstructNode resolve(String id) {
+ return "semantic".equals(id) ? this : null;
+ }
+
+ @Override
+ public Object construct(Node node) {
+ read(node);
+ // Registration happens once for the entire resource, including
declarations after routes.
+ return (CamelContextCustomizer) context -> {
+ };
+ }
+
+ @Override
+ public void preParse(YamlDeserializationContext dc, Node root) {
+ if (!(root instanceof SequenceNode sequence)) {
+ return;
+ }
+ Map<String, SemanticQuestion> definitions = new LinkedHashMap<>();
+ for (Node node : sequence.getValue()) {
+ for (NodeTuple tuple : asMappingNode(node).getValue()) {
+ if ("semantic".equals(asText(tuple.getKeyNode()))) {
+ read(tuple.getValueNode()).forEach((name, question) -> {
+ if (definitions.putIfAbsent(name, question) != null) {
+ throw new IllegalArgumentException("Duplicate
semantic question: " + name);
+ }
+ });
+ }
+ }
+ }
+ CamelContext context = dc.getCamelContext();
+ SemanticQuestions questions = definitions.isEmpty()
+ ?
context.getCamelContextExtension().getContextPlugin(SemanticQuestions.class)
+ : SemanticQuestions.get(context);
+ if (questions != null) {
+ questions.replace(dc.getResource(), definitions);
+ }
+ }
+
+ private static Map<String, SemanticQuestion> read(Node node) {
+ Map<String, Node> semantic = fields(node);
+ if (!semantic.keySet().equals(Set.of("question"))) {
+ throw new IllegalArgumentException("Semantic declaration requires
only question");
+ }
+ Map<String, SemanticQuestion> result = new LinkedHashMap<>();
+ fields(semantic.get("question")).forEach((name, definition) -> {
+ Map<String, Node> values = fields(definition);
+ if (!FIELDS.containsAll(values.keySet())) {
+ throw new IllegalArgumentException("Unknown property in
semantic question: " + name);
+ }
+ String typeName = asText(values.get("type"));
+ if (typeName == null) {
+ throw new IllegalArgumentException("Semantic question type is
required: " + name);
+ }
+ SemanticQuestion.Type type =
SemanticQuestion.Type.valueOf(typeName.toUpperCase(Locale.ROOT));
+ Map<String, String> criteria = new LinkedHashMap<>();
+ List<String> levels = List.of();
+ if (values.containsKey("criteria")) {
+ if (type == SemanticQuestion.Type.SCORE) {
+ levels =
asSequenceNode(values.get("criteria")).getValue().stream().map(YamlDeserializerSupport::asText)
+ .toList();
+ } else {
+ fields(values.get("criteria")).forEach((key, value) ->
criteria.put(key, asText(value)));
+ }
+ }
+ if (type != SemanticQuestion.Type.BOOLEAN &&
(values.containsKey("threshold") || values.containsKey("uncertainty")
+ || values.containsKey("uncertaintyPolicy"))) {
+ throw new IllegalArgumentException("Threshold and uncertainty
policy require a boolean question: " + name);
+ }
+ SemanticQuestion.UncertaintyPolicy policy =
values.containsKey("uncertaintyPolicy")
+ ? SemanticQuestion.UncertaintyPolicy
+
.valueOf(asText(values.get("uncertaintyPolicy")).replace('-',
'_').toUpperCase(Locale.ROOT))
+ : SemanticQuestion.UncertaintyPolicy.FAIL;
Review Comment:
Addressed in `9ac532851f76`. Uncertainty policy now uses the common asEnum
helper, and the Locale import and custom normalization are removed. Tests
verify the standard YAML parser's dash, enum-name and camelCase spellings. The
schema continues to advertise the canonical non-match spelling.
_AI-generated by Codex on behalf of
[luigidemasi](https://github.com/luigidemasi)._
##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/yaml/SemanticDefinitionDeserializer.java:
##########
@@ -0,0 +1,201 @@
+/*
+ * 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.yaml;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
+import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver;
+import org.apache.camel.dsl.yaml.common.YamlDeserializerSupport;
+import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException;
+import org.apache.camel.semantic.SemanticQuestion;
+import org.apache.camel.semantic.SemanticQuestions;
+import org.apache.camel.spi.CamelContextCustomizer;
+import org.apache.camel.spi.annotations.YamlIn;
+import org.apache.camel.spi.annotations.YamlProperty;
+import org.apache.camel.spi.annotations.YamlType;
+import org.snakeyaml.engine.v2.api.ConstructNode;
+import org.snakeyaml.engine.v2.nodes.Node;
+import org.snakeyaml.engine.v2.nodes.NodeTuple;
+import org.snakeyaml.engine.v2.nodes.SequenceNode;
+
+/** Named semantic declarations are installed in a resource-wide pass before
route references are resolved. */
+@YamlIn
+@YamlType(nodes = "semantic", properties = {
+ @YamlProperty(name = "question",
+ type =
"map:org.apache.camel.semantic.yaml.SemanticDefinitionDeserializer$QuestionSchema",
+ required = true)
+})
+public class SemanticDefinitionDeserializer extends YamlDeserializerSupport
implements ConstructNode, YamlDeserializerResolver {
+ private static final Set<String> FIELDS
+ = Set.of("type", "instructions", "state", "criteria", "threshold",
"uncertainty", "uncertaintyPolicy");
+
+ @Override
+ public ConstructNode resolve(String id) {
+ return "semantic".equals(id) ? this : null;
+ }
+
+ @Override
+ public Object construct(Node node) {
+ read(node);
+ // Registration happens once for the entire resource, including
declarations after routes.
+ return (CamelContextCustomizer) context -> {
+ };
+ }
+
+ @Override
+ public void preParse(YamlDeserializationContext dc, Node root) {
+ if (!(root instanceof SequenceNode sequence)) {
+ return;
+ }
+ Map<String, SemanticQuestion> definitions = new LinkedHashMap<>();
+ for (Node node : sequence.getValue()) {
+ for (NodeTuple tuple : asMappingNode(node).getValue()) {
+ if ("semantic".equals(asText(tuple.getKeyNode()))) {
+ read(tuple.getValueNode()).forEach((name, question) -> {
+ if (definitions.putIfAbsent(name, question) != null) {
+ throw new IllegalArgumentException("Duplicate
semantic question: " + name);
+ }
+ });
+ }
+ }
+ }
+ CamelContext context = dc.getCamelContext();
+ SemanticQuestions questions = definitions.isEmpty()
+ ?
context.getCamelContextExtension().getContextPlugin(SemanticQuestions.class)
+ : SemanticQuestions.get(context);
+ if (questions != null) {
+ questions.replace(dc.getResource(), definitions);
+ }
+ }
+
+ private static Map<String, SemanticQuestion> read(Node node) {
+ Map<String, Node> semantic = fields(node);
+ if (!semantic.keySet().equals(Set.of("question"))) {
+ throw new IllegalArgumentException("Semantic declaration requires
only question");
+ }
+ Map<String, SemanticQuestion> result = new LinkedHashMap<>();
+ fields(semantic.get("question")).forEach((name, definition) -> {
+ Map<String, Node> values = fields(definition);
+ if (!FIELDS.containsAll(values.keySet())) {
+ throw new IllegalArgumentException("Unknown property in
semantic question: " + name);
+ }
+ String typeName = asText(values.get("type"));
+ if (typeName == null) {
+ throw new IllegalArgumentException("Semantic question type is
required: " + name);
+ }
+ SemanticQuestion.Type type =
SemanticQuestion.Type.valueOf(typeName.toUpperCase(Locale.ROOT));
+ Map<String, String> criteria = new LinkedHashMap<>();
+ List<String> levels = List.of();
+ if (values.containsKey("criteria")) {
+ if (type == SemanticQuestion.Type.SCORE) {
+ levels =
asSequenceNode(values.get("criteria")).getValue().stream().map(YamlDeserializerSupport::asText)
+ .toList();
+ } else {
+ fields(values.get("criteria")).forEach((key, value) ->
criteria.put(key, asText(value)));
+ }
+ }
+ if (type != SemanticQuestion.Type.BOOLEAN &&
(values.containsKey("threshold") || values.containsKey("uncertainty")
+ || values.containsKey("uncertaintyPolicy"))) {
+ throw new IllegalArgumentException("Threshold and uncertainty
policy require a boolean question: " + name);
+ }
+ SemanticQuestion.UncertaintyPolicy policy =
values.containsKey("uncertaintyPolicy")
+ ? SemanticQuestion.UncertaintyPolicy
+
.valueOf(asText(values.get("uncertaintyPolicy")).replace('-',
'_').toUpperCase(Locale.ROOT))
+ : SemanticQuestion.UncertaintyPolicy.FAIL;
+ result.put(name, new SemanticQuestion(
Review Comment:
Addressed in `7aa7aed20d23`. Constructor validation is now wrapped with the
question name and definition node. The diagnostic tests cover blank
instructions and assert the source location; the numeric parsing cause remains
intact as well.
_AI-generated by Codex on behalf of
[luigidemasi](https://github.com/luigidemasi)._
##########
components/camel-ai/camel-semantic/src/main/java/org/apache/camel/semantic/yaml/SemanticDefinitionDeserializer.java:
##########
@@ -0,0 +1,201 @@
+/*
+ * 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.yaml;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
+import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver;
+import org.apache.camel.dsl.yaml.common.YamlDeserializerSupport;
+import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException;
+import org.apache.camel.semantic.SemanticQuestion;
+import org.apache.camel.semantic.SemanticQuestions;
+import org.apache.camel.spi.CamelContextCustomizer;
+import org.apache.camel.spi.annotations.YamlIn;
+import org.apache.camel.spi.annotations.YamlProperty;
+import org.apache.camel.spi.annotations.YamlType;
+import org.snakeyaml.engine.v2.api.ConstructNode;
+import org.snakeyaml.engine.v2.nodes.Node;
+import org.snakeyaml.engine.v2.nodes.NodeTuple;
+import org.snakeyaml.engine.v2.nodes.SequenceNode;
+
+/** Named semantic declarations are installed in a resource-wide pass before
route references are resolved. */
+@YamlIn
+@YamlType(nodes = "semantic", properties = {
+ @YamlProperty(name = "question",
+ type =
"map:org.apache.camel.semantic.yaml.SemanticDefinitionDeserializer$QuestionSchema",
+ required = true)
+})
+public class SemanticDefinitionDeserializer extends YamlDeserializerSupport
implements ConstructNode, YamlDeserializerResolver {
+ private static final Set<String> FIELDS
+ = Set.of("type", "instructions", "state", "criteria", "threshold",
"uncertainty", "uncertaintyPolicy");
+
+ @Override
+ public ConstructNode resolve(String id) {
+ return "semantic".equals(id) ? this : null;
+ }
+
+ @Override
+ public Object construct(Node node) {
+ read(node);
+ // Registration happens once for the entire resource, including
declarations after routes.
+ return (CamelContextCustomizer) context -> {
+ };
+ }
+
+ @Override
+ public void preParse(YamlDeserializationContext dc, Node root) {
+ if (!(root instanceof SequenceNode sequence)) {
+ return;
+ }
+ Map<String, SemanticQuestion> definitions = new LinkedHashMap<>();
+ for (Node node : sequence.getValue()) {
+ for (NodeTuple tuple : asMappingNode(node).getValue()) {
Review Comment:
Addressed in `9ac532851f76`. A non-mapping top-level entry now returns
control to the normal route loader without publishing replacement questions.
Returning instead of continuing prevents a malformed reload from clearing or
changing existing definitions. Four regression cases cover scalar/sequence
entries both alone and following changed semantic declarations, and assert the
normal loader error plus retention of the original question instance.
_AI-generated by Codex on behalf of
[luigidemasi](https://github.com/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]