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]

Reply via email to